222 lines
6.6 KiB
Haskell
222 lines
6.6 KiB
Haskell
{- |
|
|
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 (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), untag)
|
|
import Data.Universe (Finite (..), Universe (..))
|
|
import Plutarch.Api.V1 (PScriptContext)
|
|
import Plutarch.Builtin (pforgetData)
|
|
import Plutarch.Context (
|
|
MintingBuilder,
|
|
buildMintingUnsafe,
|
|
input,
|
|
mint,
|
|
output,
|
|
script,
|
|
withDatum,
|
|
withMinting,
|
|
withOutRef,
|
|
withValue,
|
|
)
|
|
import PlutusLedgerApi.V1 (
|
|
ScriptContext (scriptContextTxInfo),
|
|
TxInInfo (txInInfoOutRef),
|
|
TxInfo (txInfoInputs, txInfoMint, txInfoOutputs),
|
|
TxOut (txOutValue),
|
|
)
|
|
import PlutusLedgerApi.V1.Value (assetClassValue)
|
|
import Property.Generator (genInput, genOutput)
|
|
import Sample.Shared (
|
|
govAssetClass,
|
|
govSymbol,
|
|
govValidatorHash,
|
|
governor,
|
|
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
|
|
| VoteLE0
|
|
| Correct
|
|
deriving stock (Eq, Show)
|
|
|
|
instance Universe GovernorDatumCases where
|
|
universe =
|
|
[ ExecuteLE0
|
|
, CreateLE0
|
|
, VoteLE0
|
|
, 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 e c v)
|
|
| e < 0 = ExecuteLE0
|
|
| c < 0 = CreateLE0
|
|
| v < 0 = VoteLE0
|
|
| 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
|
|
vote <- validGT
|
|
le0 <- taggedInteger (-1000, -1)
|
|
|
|
case c of
|
|
ExecuteLE0 ->
|
|
-- execute < 0
|
|
return $ ProposalThresholds le0 create vote
|
|
CreateLE0 ->
|
|
-- c < 0
|
|
return $ ProposalThresholds execute le0 vote
|
|
VoteLE0 ->
|
|
-- vote < 0
|
|
return $ ProposalThresholds execute create le0
|
|
Correct -> do
|
|
-- c <= vote < execute
|
|
nv <- taggedInteger (0, untag execute - 1)
|
|
nc <- taggedInteger (0, untag nv)
|
|
return $ ProposalThresholds execute nc nv
|
|
|
|
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 govAssetClass 1
|
|
mintAmount x = mint . mconcat $ replicate x gst
|
|
outputToGov =
|
|
output $
|
|
mconcat
|
|
[ script govValidatorHash
|
|
, withValue gst
|
|
, withDatum govDatum
|
|
]
|
|
referencedInput = input $ withOutRef 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 . buildMintingUnsafe $ inputs <> outputs <> comp <> withMinting govSymbol
|
|
|
|
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 governor # 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
|
|
]
|