Added some annotations to work on multisig apropos testing

This commit is contained in:
Jack Hodgkinson 2022-03-18 14:46:47 +00:00
parent 93acd81e79
commit 745e576d33

View file

@ -1,7 +1,16 @@
module Model.MultiSig (plutarchTests, genTests) where module Model.MultiSig (plutarchTests, genTests) where
import Agora.MultiSig (MultiSig (..), validatedByMultisig) import Agora.MultiSig (MultiSig (..), validatedByMultisig)
import Apropos (Apropos (Apropos), Formula (ExactlyOne, Var, Yes), HasLogicalModel (..), HasParameterisedGenerator, LogicalModel (logic), parameterisedGenerator, runGeneratorTestsWhere, (:+)) import Apropos (
Apropos (Apropos),
Formula (ExactlyOne, Var, Yes),
HasLogicalModel (..),
HasParameterisedGenerator,
LogicalModel (logic),
parameterisedGenerator,
runGeneratorTestsWhere,
(:+),
)
import Apropos.Gen (Gen, choice, int, linear, list) import Apropos.Gen (Gen, choice, int, linear, list)
import Apropos.LogicalModel (Enumerable) import Apropos.LogicalModel (Enumerable)
import Apropos.LogicalModel.Enumerable (Enumerable (enumerated)) import Apropos.LogicalModel.Enumerable (Enumerable (enumerated))
@ -12,7 +21,17 @@ import Plutus.V1.Ledger.Api (
Script, Script,
ScriptContext (scriptContextPurpose), ScriptContext (scriptContextPurpose),
ScriptPurpose (Spending), ScriptPurpose (Spending),
TxInfo (txInfoDCert, txInfoData, txInfoFee, txInfoId, txInfoInputs, txInfoMint, txInfoOutputs, txInfoValidRange, txInfoWdrl), TxInfo (
txInfoDCert,
txInfoData,
txInfoFee,
txInfoId,
txInfoInputs,
txInfoMint,
txInfoOutputs,
txInfoValidRange,
txInfoWdrl
),
TxOutRef (TxOutRef), TxOutRef (TxOutRef),
scriptContextTxInfo, scriptContextTxInfo,
txInfoSignatories, txInfoSignatories,
@ -24,45 +43,50 @@ import Plutus.V1.Ledger.Value qualified as Value
import Test.Tasty (TestTree, testGroup) import Test.Tasty (TestTree, testGroup)
import Test.Tasty.Hedgehog (fromGroup) import Test.Tasty.Hedgehog (fromGroup)
{- {- Function definitions for reference:
1. Create proposition sum type. {- | A MultiSig represents a proof that a particular set of
2. Create logical model defining relationships between propositions. signatures are present on a transaction.
3. Associating propositions with the "concrete" type i.e. MultiSig. -}
4. Create Generators. data MultiSig = MultiSig
5. Run tests (with magic). { keys :: [PubKeyHash]
-- ^ List of PubKeyHashes that must be present in the list of signatories.
-} , minSigs :: Integer
}
{- deriving stock (GHC.Generic, Eq, Show)
deriving anyclass (Generic)
1. Create a
-- | Check if a Haskell-level MultiSig signs this transaction.
Define a prop, as if it is the way a script can pass. validatedByMultisig :: MultiSig -> Term s (PTxInfo :--> PBool)
1. keys signed exceeds `minSigs` validatedByMultisig params =
2. `minSigs` is lte zero. phoistAcyclic $
pvalidatedByMultisig # pconstant params
Props not passing:
1. No signatures present.
2. Signatures present is less than `minSigs`.
-} -}
-- | apropos model for testing multisigs.
data MultiSigModel = MultiSigModel data MultiSigModel = MultiSigModel
{ ms :: MultiSig { ms :: MultiSig
-- ^ `MultiSig` value to be tested.
, ctx :: ScriptContext , ctx :: ScriptContext
-- ^ The `ScriptContext` of the transaction.
} }
deriving stock (Eq, Show) deriving stock (Eq, Show)
-- | Propositions that may hold true of a `MultiSigModel`.
data MultiSigProp data MultiSigProp
= MeetsMinSigs = -- | Sufficient number of signatories in the script context.
| DoesNotMeetMinSigs MeetsMinSigs
| -- | Insufficient number of signatories in the script context.
DoesNotMeetMinSigs
deriving stock (Eq, Show, Ord) deriving stock (Eq, Show, Ord)
instance Enumerable MultiSigProp where instance Enumerable MultiSigProp where
enumerated = [MeetsMinSigs, DoesNotMeetMinSigs] enumerated = [MeetsMinSigs, DoesNotMeetMinSigs]
instance LogicalModel MultiSigProp where instance LogicalModel MultiSigProp where
-- Only logical relationship between the two propositions is
-- that exactly one of them holds for a given model.
logic = ExactlyOne [Var MeetsMinSigs, Var DoesNotMeetMinSigs] logic = ExactlyOne [Var MeetsMinSigs, Var DoesNotMeetMinSigs]
instance HasLogicalModel MultiSigProp MultiSigModel where instance HasLogicalModel MultiSigProp MultiSigModel where
@ -75,6 +99,9 @@ instance HasLogicalModel MultiSigProp MultiSigModel where
MeetsMinSigs -> length matchingSigs >= fromInteger minSigs MeetsMinSigs -> length matchingSigs >= fromInteger minSigs
DoesNotMeetMinSigs -> length matchingSigs < fromInteger minSigs DoesNotMeetMinSigs -> length matchingSigs < fromInteger minSigs
{- | Given a list of key hashes, returns a dummy `ScriptContext`,
with those hashes as signatories.
-}
contextWithSignatures :: [PubKeyHash] -> ScriptContext contextWithSignatures :: [PubKeyHash] -> ScriptContext
contextWithSignatures sigs = contextWithSignatures sigs =
ScriptContext ScriptContext
@ -94,6 +121,7 @@ contextWithSignatures sigs =
, scriptContextPurpose = Spending (TxOutRef "" 0) , scriptContextPurpose = Spending (TxOutRef "" 0)
} }
-- | Generator returning one of four dummy public key hashes.
genPK :: Gen PubKeyHash genPK :: Gen PubKeyHash
genPK = genPK =
choice choice
@ -105,23 +133,37 @@ genPK =
instance HasParameterisedGenerator MultiSigProp MultiSigModel where instance HasParameterisedGenerator MultiSigProp MultiSigModel where
parameterisedGenerator s = do parameterisedGenerator s = do
-- Gen between one and four signatures for the `MultiSig`.
expectedSignatures <- list (linear 1 4) genPK expectedSignatures <- list (linear 1 4) genPK
-- Gen the value of `MultiSig.minSigs`.
minSigs <- toInteger <$> int (linear 1 (length expectedSignatures)) minSigs <- toInteger <$> int (linear 1 (length expectedSignatures))
-- Assign values to msig.
let msig = MultiSig expectedSignatures minSigs let msig = MultiSig expectedSignatures minSigs
actualSignaturesLength <- actualSignaturesLength <-
-- If we would like to generate a MultiSig model which passes...
if MeetsMinSigs `elem` s if MeetsMinSigs `elem` s
then int (linear (fromInteger minSigs) (length expectedSignatures)) then -- ... have a sufficient number of signatories.
else pure 0 int (linear (fromInteger minSigs) (length expectedSignatures))
else -- ... have zero signatories.
pure 0
-- Get a list of signatories for the script context.
let actualSignatures = take actualSignaturesLength expectedSignatures let actualSignatures = take actualSignaturesLength expectedSignatures
let ctx = contextWithSignatures actualSignatures let ctx = contextWithSignatures actualSignatures
-- Return the generated model.
pure (MultiSigModel msig ctx) pure (MultiSigModel msig ctx)
instance HasScriptRunner MultiSigProp MultiSigModel where instance HasScriptRunner MultiSigProp MultiSigModel where
-- When the script runs, we want the model to meet the minimum signatures.
expect :: (MultiSigModel :+ MultiSigProp) -> Formula MultiSigProp expect :: (MultiSigModel :+ MultiSigProp) -> Formula MultiSigProp
expect Apropos = Var MeetsMinSigs expect Apropos = Var MeetsMinSigs
-- Function making a valid script from the model and propositions.
script :: (MultiSigModel :+ MultiSigProp) -> MultiSigModel -> Script script :: (MultiSigModel :+ MultiSigProp) -> MultiSigModel -> Script
script Apropos msm = script Apropos msm =
compile $ compile $
@ -134,12 +176,18 @@ genTests :: TestTree
genTests = genTests =
testGroup "genTests" $ testGroup "genTests" $
fromGroup fromGroup
<$> [ runGeneratorTestsWhere (Apropos :: MultiSigModel :+ MultiSigProp) "Generator" Yes <$> [ runGeneratorTestsWhere
(Apropos :: MultiSigModel :+ MultiSigProp)
"Generator"
Yes
] ]
plutarchTests :: TestTree plutarchTests :: TestTree
plutarchTests = plutarchTests =
testGroup "plutarchTests" $ testGroup "plutarchTests" $
fromGroup fromGroup
<$> [ runScriptTestsWhere (Apropos :: MultiSigModel :+ MultiSigProp) "ScriptValid" Yes <$> [ runScriptTestsWhere
(Apropos :: MultiSigModel :+ MultiSigProp)
"ScriptValid"
Yes
] ]