{- | Module : Property.Governor Maintainer : seungheon.ooh@gmail.com Description: Property tests for 'Governor' related functions. Property model and tests for 'Governor' related functions -} module Property.Governor (props) where import Agora.Governor (Governor (gstOutRef), GovernorDatum (..), pisGovernorDatumValid) import Agora.Governor.Scripts (governorPolicy) import Agora.Proposal ( ProposalId (ProposalId), ProposalThresholds (ProposalThresholds), ) import Agora.Proposal.Time ( MaxTimeRangeWidth (MaxTimeRangeWidth), ProposalTimingConfig (ProposalTimingConfig), ) import Data.Default.Class (Default (def)) import Data.Tagged (Tagged (Tagged)) import Data.Universe (Finite (..), Universe (..)) import Plutarch.Api.V2 (PScriptContext) import Plutarch.Builtin (pforgetData) import Plutarch.Context ( MintingBuilder, buildMinting', input, mint, output, script, withDatum, withMinting, withRef, withValue, ) import Plutarch.Extra.AssetClass (assetClassValue) import PlutusLedgerApi.V2 ( ScriptContext (scriptContextTxInfo), TxInInfo (txInInfoOutRef), TxInfo (txInfoInputs, txInfoMint, txInfoOutputs), TxOut (txOutValue), ) import Property.Generator (genInput, genOutput) import Sample.Shared ( governor, governorAssetClass, governorSymbol, governorValidatorHash, gstUTXORef, ) import Test.Tasty (TestTree) import Test.Tasty.Plutarch.Property (classifiedPropertyNative) import Test.Tasty.QuickCheck ( Gen, Property, choose, chooseInteger, listOf1, testProperty, ) data GovernorDatumCases = ExecuteLE0 | CreateLE0 | ToVotingLE0 | VoteLE0 | CosignLE0 | Correct deriving stock (Eq, Show) instance Universe GovernorDatumCases where universe = [ ExecuteLE0 , CreateLE0 , VoteLE0 , CosignLE0 , Correct ] instance Finite GovernorDatumCases where universeF = universe cardinality = Tagged 6 {- | Property that checks `governorDatumValid`. `governorDatumValid` determines if given governor datum is valid or not. This property ensures `governorDatumValid` is checking the datum correctly and ruling out improper datum. -} governorDatumValidProperty :: Property governorDatumValidProperty = classifiedPropertyNative gen (const []) expected classifier pisGovernorDatumValid where classifier :: GovernorDatum -> GovernorDatumCases classifier ( (.proposalThresholds) -> ProposalThresholds execute create toVoting vote cosign ) | execute < 0 = ExecuteLE0 | create < 0 = CreateLE0 | toVoting < 0 = ToVotingLE0 | vote < 0 = VoteLE0 | cosign < 0 = CosignLE0 | otherwise = Correct expected :: GovernorDatum -> Maybe Bool expected c = Just $ classifier c == Correct gen :: GovernorDatumCases -> Gen GovernorDatum gen c = do thres <- genProposalThresholds c let timing = ProposalTimingConfig 0 0 0 0 return $ GovernorDatum thres (ProposalId 0) timing (MaxTimeRangeWidth 1) 3 where taggedInteger p = Tagged <$> chooseInteger p genProposalThresholds :: GovernorDatumCases -> Gen ProposalThresholds genProposalThresholds c = do let validGT = taggedInteger (0, 1000000000) execute <- validGT create <- validGT toVoting <- validGT vote <- validGT cosign <- validGT le0 <- taggedInteger (-1000, -1) case c of ExecuteLE0 -> -- execute < 0 return $ ProposalThresholds le0 create toVoting vote cosign CreateLE0 -> -- c < 0 return $ ProposalThresholds execute le0 toVoting vote cosign ToVotingLE0 -> return $ ProposalThresholds execute create le0 vote cosign VoteLE0 -> -- vote < 0 return $ ProposalThresholds execute create toVoting le0 cosign CosignLE0 -> return $ ProposalThresholds execute create toVoting vote le0 Correct -> return $ ProposalThresholds execute create toVoting vote cosign data GovernorPolicyCases = ReferenceUTXONotSpent | IncorrectAmountOfTokenMinted | GovernorOutputNotFound | GovernorPolicyCorrect deriving stock (Eq, Show) instance Universe GovernorPolicyCases where universe = [ ReferenceUTXONotSpent , IncorrectAmountOfTokenMinted , GovernorOutputNotFound , GovernorPolicyCorrect ] instance Finite GovernorPolicyCases where universeF = universe cardinality = Tagged 4 governorMintingProperty :: Property governorMintingProperty = classifiedPropertyNative gen (const []) expected classifier actual where {- Note: I don't think it's easily possible to randomize orefs. We can't really pass pass `Governor` type to `actual` function. -} gst = assetClassValue governorAssetClass 1 mintAmount x = mint . mconcat $ replicate x gst outputToGov = output $ mconcat [ script governorValidatorHash , withValue gst , withDatum govDatum ] referencedInput = input $ withRef gstUTXORef govDatum :: GovernorDatum govDatum = GovernorDatum { proposalThresholds = def , nextProposalId = ProposalId 0 , proposalTimings = def , createProposalTimeRangeMaxWidth = def , maximumProposalsPerStake = 3 } gen :: GovernorPolicyCases -> Gen ScriptContext gen c = do inputs <- fmap mconcat . listOf1 $ genInput @MintingBuilder outputs <- fmap mconcat . listOf1 $ genOutput @MintingBuilder toks <- choose (2, 100) let comp = case c of ReferenceUTXONotSpent -> outputToGov <> mintAmount 1 IncorrectAmountOfTokenMinted -> referencedInput <> outputToGov <> mintAmount toks GovernorOutputNotFound -> referencedInput <> mintAmount 1 GovernorPolicyCorrect -> referencedInput <> outputToGov <> mintAmount 1 return . buildMinting' $ inputs <> outputs <> comp <> withMinting governorSymbol expected :: ScriptContext -> Maybe () expected sc = case classifier sc of GovernorPolicyCorrect -> Just () _ -> Nothing opaqueToUnit :: Term s (POpaque :--> PUnit) opaqueToUnit = plam $ \_ -> pconstant () actual :: Term s (PScriptContext :--> PUnit) actual = plam $ \sc -> opaqueToUnit #$ governorPolicy # pconstant governor.gstOutRef # pforgetData (pconstantData ()) # sc classifier :: ScriptContext -> GovernorPolicyCases classifier sc | minted /= gst = IncorrectAmountOfTokenMinted | refInputNotExists = ReferenceUTXONotSpent | govOutputNotExists = GovernorOutputNotFound | otherwise = GovernorPolicyCorrect where txinfo = scriptContextTxInfo sc minted = txInfoMint txinfo refInputNotExists = gstUTXORef `notElem` (txInInfoOutRef <$> txInfoInputs txinfo) govOutputNotExists = gst `notElem` (txOutValue <$> txInfoOutputs txinfo) props :: [TestTree] props = [ testProperty "governorDatumValid" governorDatumValidProperty , testProperty "governorPolicy" governorMintingProperty ]