Skip to content

Commit d1e834b

Browse files
authored
Merge pull request #68 from mlabs-haskell/bladyjoker/add-consumer-example
Add the Consumer example
2 parents c842f1e + 245e71d commit d1e834b

File tree

8 files changed

+180
-23
lines changed

8 files changed

+180
-23
lines changed

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,3 +1100,16 @@ contains the [Fact Statement minting
11001100
policy](coop-docs/02-plutus-protocol.md#fs-policy) script which is the `Currency
11011101
Symbol` the consuming dApps use to assert the authenticity and provenance of the
11021102
referenced [Fact Statement UTxOs](coop-docs/02-plutus-protocol.md#fs-validator).
1103+
1104+
An example Consumer [validator script](coop-plutus/src/Coop/Plutus:L594-L608)
1105+
was provided to demonstrate how to authenticate [Fact Statement
1106+
UTxOs](coop-docs/02-plutus-protocol.md#fs-validator) on-chain. The script
1107+
performs a simple assertion on the
1108+
[Value](https://github.com/input-output-hk/plutus/blob/c3918d6027a9a34b6f72a6e4c7bf2e5350e6467e/plutus-ledger-api/src/PlutusLedgerApi/V1/Value.hs#L185)
1109+
of a referenced UTxO to make sure it contains a
1110+
[CurrencySymbol](https://github.com/input-output-hk/plutus/blob/c3918d6027a9a34b6f72a6e4c7bf2e5350e6467e/plutus-ledger-api/src/PlutusLedgerApi/V1/Value.hs#L79)
1111+
of the [$FS](coop-docs/02-plutus-protocol.md#fs-token) tokens it trusts
1112+
1113+
The second part of the script demonstrates how to parse a [Plutus
1114+
JSON](coop-docs/05-json-plutus.md) [Fact
1115+
Statement](coop-plutus/resources/sample.json).

coop-plutus/build.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ haskell-nix.cabalProject' (plutarch.applyPlutarchDep pkgs rec {
1818
packages = {
1919
# Enable strict builds
2020
coop-plutus.configureFlags = [ "-f-dev" ];
21-
21+
coop-plutus.package.extraSrcFiles = [ "resources/sample.json" "resources/sample.pd.cbor" ]; # TODO(bladyjoker): I would like to get rid of this as haskell-nix should pick it up from the Cabal file
2222
# Use the new-ledger-namespace
2323
coop-hs-types.configureFlags = [ "-fnew-ledger-namespace" ];
2424
};

coop-plutus/coop-plutus.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ test-suite coop-plutus-tests
130130

131131
build-depends:
132132
, base
133+
, bytestring
133134
, containers
134135
, coop-hs-types
135136
, coop-plutus
@@ -139,4 +140,5 @@ test-suite coop-plutus-tests
139140
, plutus-ledger-api
140141
, plutus-tx
141142
, QuickCheck
143+
, serialise
142144
, text

coop-plutus/resources/sample.json

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"array": [
3+
1,
4+
2,
5+
3
6+
],
7+
"boolean": true,
8+
"null": null,
9+
"integer": 123,
10+
"big_integer": 12300000000000000000000000,
11+
"real": 123.123,
12+
"big_real": 12300000000000000000000000.123,
13+
"string": "Hello World"
14+
}
133 Bytes
Binary file not shown.

coop-plutus/src/Coop/Plutus.hs

Lines changed: 98 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,27 @@ module Coop.Plutus (
88
certV,
99
mkCertMp,
1010
pmustSpendAtLeastAa,
11+
exampleConsumer,
1112
) where
1213

1314
import Coop.Plutus.Aux (pcurrencyTokenQuantity, pcurrencyValue, pdatumFromTxOut, pdjust, pdnothing, pfindMap, pfoldTxInputs, pfoldTxOutputs, pfoldTxRefs, phasCurrency, pmaybeData, pmustBeSignedBy, pmustBurnOwnSingletonValue, pmustMintCurrency, pmustPayCurrencyWithDatumTo, pmustSpendAtLeast, pmustValidateAfter, pownCurrencySymbol, ptryFromData, punit)
1415
import Coop.Plutus.Types (PAuthMpParams, PAuthMpRedeemer (PAuthMpBurn, PAuthMpMint), PAuthParams, PCertDatum, PCertMpParams, PCertMpRedeemer (PCertMpBurn, PCertMpMint), PFsDatum, PFsMpParams, PFsMpRedeemer (PFsMpBurn, PFsMpMint))
1516
import Plutarch (POpaque, pmatch, popaque)
17+
import Plutarch.Api.V1.AssocMap (plookup)
1618
import Plutarch.Api.V1.Value (passertPositive, pnormalize, pvalueOf)
1719
import Plutarch.Api.V1.Value qualified as PValue
18-
import Plutarch.Api.V2 (
19-
AmountGuarantees (NonZero, Positive),
20-
KeyGuarantees (Sorted),
21-
PCurrencySymbol,
22-
PMaybeData,
23-
PMintingPolicy,
24-
PTokenName (PTokenName),
25-
PTuple,
26-
PTxInInfo,
27-
PTxOut,
28-
PValidator,
29-
PValue,
30-
)
20+
import Plutarch.Api.V2 (AmountGuarantees (NonZero, Positive), KeyGuarantees (Sorted, Unsorted), PCurrencySymbol, PMap, PMaybeData, PMintingPolicy, PTokenName (PTokenName), PTuple, PTxInInfo, PTxOut, PValidator, PValue)
3121
import Plutarch.Api.V2.Contexts (PScriptContext)
32-
import Plutarch.Bool (pif)
22+
import Plutarch.Bool (PBool, pif)
23+
import Plutarch.Builtin (PBuiltinPair, pasConstr, pfstBuiltin, psndBuiltin)
3324
import Plutarch.Crypto (pblake2b_256)
3425
import Plutarch.Extra.Interval (pcontains)
26+
import Plutarch.List (pmap)
3527
import Plutarch.Monadic qualified as P
3628
import Plutarch.Num (PNum (pnegate, (#+)))
37-
import Plutarch.Prelude (ClosedTerm, PAsData, PBuiltinList, PByteString, PEq ((#==)), PInteger, PListLike (pcons, pnil), PPair (PPair), PPartialOrd ((#<), (#<=)), Term, pcon, pconsBS, pfield, pfoldl, pfromData, phoistAcyclic, plam, plet, pletFields, ptrace, ptraceError, (#), (#$), type (:-->))
29+
import Plutarch.Prelude (ClosedTerm, PAsData, PBuiltinList, PByteString, PData, PEq ((#==)), PInteger, PListLike (pcons, phead, pnil), PMaybe (PJust), PPair (PPair), PPartialOrd ((#<), (#<=)), Term, pcon, pconsBS, pconstant, pfield, pfoldl, pfromData, phoistAcyclic, plam, plet, pletFields, ptrace, ptraceError, (#), (#$), type (:-->))
3830
import PlutusTx.Prelude (Group (inv))
39-
import Prelude (Monoid (mempty), Semigroup ((<>)), ($))
31+
import Prelude (Monoid (mempty), Semigroup ((<>)), ($), (.))
4032

4133
{- | Validates spending from @FsV
4234
@@ -598,3 +590,93 @@ pmustSpendAtLeastAa = phoistAcyclic $
598590
(atLeastAaQ #<= aaTokensSpent)
599591
(ptrace "pmustSpendAtLeastAa: Spent at least the specified amount of AA tokens" $ pblake2b_256 # tnBytes)
600592
(ptraceError "pmustSpendAtLeastAa: Must spend at least the specified amount of AA tokens")
593+
594+
{- | Example Consumer validator that authenticates and processes a referenced
595+
FactStatement UTxO
596+
597+
- check that the reference input holds the appropriate $FS token (with the
598+
trusted COOP Oracle's CurrencySymbol),
599+
- parse the Fact Statement embedded in the UTxO datum and perform assertions.
600+
601+
To demonstrate the COOP provided Plutus JSON encoding a file was created with the COOP
602+
provided `plutus-json-cli` tool:
603+
604+
\$ plutus-json-cli from-json -i resources/sample.json -o resources/sample.pd.cbor
605+
606+
This served as an exemplary Fact Statement.
607+
-}
608+
exampleConsumer :: ClosedTerm (PCurrencySymbol :--> PValidator)
609+
exampleConsumer = phoistAcyclic $
610+
plam $ \trustedCs _ _ ctx -> ptrace "exampleConsumer" P.do
611+
ctx' <- pletFields @'["txInfo"] ctx
612+
txInfo <- pletFields @'["referenceInputs"] ctx'.txInfo
613+
614+
ptrace "exampleConsumer: Looking for a Fact Statement reference input from a trusted COOP Oracle"
615+
refInput <- pletFields @'["resolved"] $ phead # pfromData txInfo.referenceInputs
616+
refInVal <- plet $ pfield @"value" # refInput.resolved
617+
618+
ptrace "exampleConsumer: Looking for a Fact Statement reference input from a trusted COOP Oracle"
619+
pif
620+
(phasCurrency # trustedCs # refInVal)
621+
( ptrace
622+
"exampleConsumer: Found an authentic Fact Statement reference input from a trusted COOP Oracle"
623+
P.do
624+
-- Parse the FsDatum available in the referenced input
625+
fsDatum <- pletFields @'["fd'fs", "fd'submitter", "fd'gcAfter", "fd'fsId"] $ pdatumFromTxOut @PFsDatum # ctx # refInput.resolved
626+
627+
-- Take the Fact Statement payload in `fd'fs` field and try to parse it as a PlutusData Map
628+
factStatement :: Term s (PMap 'Unsorted PByteString PData) <- plet $ pfromData $ ptryFromData fsDatum.fd'fs
629+
630+
-- Take the "array" field in the Fact Statement and assert that it is [1,2,3]
631+
PJust arrayNumbers''' <- pmatch $ plookup # pconstant "array" # factStatement
632+
-- Parse it as Plutus List
633+
arrayNumbers' :: Term s (PBuiltinList (PAsData PInteger)) <- plet $ pfromData $ ptryFromData arrayNumbers'''
634+
-- Parse the elements within as Plutus Integer
635+
arrayNumbers <- plet $ pmap # plam pfromData # arrayNumbers'
636+
_ <- plet $ pif (arrayNumbers #== pconstant [1, 2, 3]) (popaque punit) (ptraceError "Expected Plutus List [1,2,3]")
637+
638+
-- Take the "boolean" field in the Fact Statement and assert that it is true
639+
PJust boolean' <- pmatch $ plookup # pconstant "boolean" # factStatement
640+
boolean :: Term s PBool <- plet $ pfromData $ ptryFromData boolean'
641+
_ <- plet $ pif boolean (popaque punit) (ptraceError "Expected a Plutus Boolean true")
642+
643+
-- Take the "null" field in the Fact Statement and assert that it is null
644+
PJust null' <- pmatch $ plookup # pconstant "null" # factStatement
645+
null :: Term s (PBuiltinPair PInteger (PBuiltinList PData)) <- plet $ pasConstr # null'
646+
_ <- plet $ pif ((pfstBuiltin # null) #== 2) (popaque punit) (ptraceError "Expected a Plutus Constr 2 []")
647+
_ <- plet $ pif ((psndBuiltin # null) #== pconstant []) (popaque punit) (ptraceError "Expected a Plutus Constr 2 []")
648+
649+
-- Take the "integer" field in the Fact Statement and assert that it is 123
650+
PJust integer' <- pmatch $ plookup # pconstant "integer" # factStatement
651+
integer :: Term s PInteger <- plet $ pfromData $ ptryFromData integer'
652+
_ <- plet $ pif (integer #== pconstant 123) (popaque punit) (ptraceError "Expected a Plutus Integer 123")
653+
654+
-- Take the "big_integer" field in the Fact Statement and assert that it is 12300000000000000000000000
655+
PJust bigInteger' <- pmatch $ plookup # pconstant "big_integer" # factStatement
656+
bigInteger'' :: Term s (PBuiltinPair PInteger (PBuiltinList PData)) <- plet $ pasConstr # bigInteger'
657+
bigInteger''' :: Term s (PBuiltinList PInteger) <- plet $ pmap # plam (pfromData . ptryFromData) # (psndBuiltin # bigInteger'')
658+
_ <- plet $ pif ((pfstBuiltin # bigInteger'') #== 3) (popaque punit) (ptraceError "Expected a Plutus Constr 3 [12300000000000000000000000, 0]")
659+
_ <- plet $ pif (bigInteger''' #== pconstant [12300000000000000000000000, 0]) (popaque punit) (ptraceError "Expected a Plutus Constr 3 [12300000000000000000000000, 0]")
660+
661+
-- Take the "real" field in the Fact Statement and assert that it is 123.123
662+
PJust real' <- pmatch $ plookup # pconstant "real" # factStatement
663+
real'' :: Term s (PBuiltinPair PInteger (PBuiltinList PData)) <- plet $ pasConstr # real'
664+
real''' :: Term s (PBuiltinList PInteger) <- plet $ pmap # plam (pfromData . ptryFromData) # (psndBuiltin # real'')
665+
_ <- plet $ pif ((pfstBuiltin # real'') #== 3) (popaque punit) (ptraceError "Expected a Plutus Constr 3 [123123, -3]")
666+
_ <- plet $ pif (real''' #== pconstant [123123, -3]) (popaque punit) (ptraceError "Expected a Plutus Constr 3 [123123, -3]")
667+
668+
-- Take the "big_real" field in the Fact Statement and assert that it is 12300000000000000000000000.123
669+
PJust big_real' <- pmatch $ plookup # pconstant "big_real" # factStatement
670+
big_real'' :: Term s (PBuiltinPair PInteger (PBuiltinList PData)) <- plet $ pasConstr # big_real'
671+
big_real''' :: Term s (PBuiltinList PInteger) <- plet $ pmap # plam (pfromData . ptryFromData) # (psndBuiltin # big_real'')
672+
_ <- plet $ pif ((pfstBuiltin # big_real'') #== 3) (popaque punit) (ptraceError "Expected a Plutus Constr 3 [12300000000000000000000000123, -3]")
673+
_ <- plet $ pif (big_real''' #== pconstant [12300000000000000000000000123, -3]) (popaque punit) (ptraceError "Expected a Plutus Constr 3 [12300000000000000000000000123, -3]")
674+
675+
-- Take the "string" field in the Fact Statement and assert that it is "Hello World"
676+
PJust string' <- pmatch $ plookup # pconstant "string" # factStatement
677+
string'' :: Term s PByteString <- plet $ pfromData $ ptryFromData string'
678+
_ <- plet $ pif (string'' #== pconstant "Hello World") (popaque punit) (ptraceError "Expected a Plutus Bytestring \"Hello World\"")
679+
680+
ptrace "exampleConsumer: Everything worked!" $ popaque punit
681+
)
682+
(ptraceError "exampleConsumer: Must have a Fact Statement reference input from a trusted COOP Oracle")

coop-plutus/test/Coop/Plutus/Test.hs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,30 @@
11
module Coop.Plutus.Test (spec) where
22

33
import Plutarch.Prelude (ClosedTerm, PBool (PTrue), PEq ((#==)), pconstant, pconstantData, (#))
4-
import Test.Hspec (Expectation, Spec, describe, shouldBe)
4+
import Test.Hspec (Expectation, Spec, describe, runIO, shouldBe)
55
import Test.Hspec.QuickCheck (prop)
66
import Test.QuickCheck (NonEmptyList (getNonEmpty), Positive (getPositive), choose, forAll, generate)
77

8-
import Coop.Plutus (certV, fsV, mkAuthMp, mkCertMp, mkFsMp, pmustSpendAtLeastAa)
8+
import Codec.Serialise (deserialiseOrFail)
9+
import Coop.Plutus (certV, exampleConsumer, fsV, mkAuthMp, mkCertMp, mkFsMp, pmustSpendAtLeastAa)
910
import Coop.Plutus.Aux (hashTxInputs, pmustBurnOwnSingletonValue)
10-
import Coop.Plutus.Test.Generators (distribute, genAaInputs, genCertRdmrAc, genCorrectAuthMpBurningCtx, genCorrectAuthMpMintingCtx, genCorrectCertMpBurningCtx, genCorrectCertMpMintingCtx, genCorrectCertVSpendingCtx, genCorrectFsMpBurningCtx, genCorrectFsMpMintingCtx, genCorrectFsVSpendingCtx, genCorrectMustBurnOwnSingletonValueCtx, genCorruptAuthMpBurningCtx, genCorruptAuthMpMintingCtx, genCorruptCertMpBurningCtx, genCorruptCertMpMintingCtx, genCorruptCertVSpendingCtx, genCorruptFsMpBurningCtx, genCorruptFsMpMintingCtx, genCorruptFsVSpendingCtx, genCorruptMustBurnOwnSingletonValueCtx, mkScriptContext)
11+
import Coop.Plutus.Test.Generators (distribute, genAaInputs, genCertRdmrAc, genCorrectAuthMpBurningCtx, genCorrectAuthMpMintingCtx, genCorrectCertMpBurningCtx, genCorrectCertMpMintingCtx, genCorrectCertVSpendingCtx, genCorrectConsumerCtx, genCorrectFsMpBurningCtx, genCorrectFsMpMintingCtx, genCorrectFsVSpendingCtx, genCorrectMustBurnOwnSingletonValueCtx, genCorruptAuthMpBurningCtx, genCorruptAuthMpMintingCtx, genCorruptCertMpBurningCtx, genCorruptCertMpMintingCtx, genCorruptCertVSpendingCtx, genCorruptFsMpBurningCtx, genCorruptFsMpMintingCtx, genCorruptFsVSpendingCtx, genCorruptMustBurnOwnSingletonValueCtx, mkScriptContext)
1112
import Coop.Plutus.Types (PAuthMpParams, PCertMpParams, PFsMpParams)
1213
import Coop.Types (AuthMpParams (AuthMpParams), AuthMpRedeemer (AuthMpBurn, AuthMpMint), AuthParams (AuthParams), CertMpParams (CertMpParams), CertMpRedeemer (CertMpBurn, CertMpMint), FsMpParams (FsMpParams), FsMpRedeemer (FsMpBurn, FsMpMint))
14+
import Data.ByteString.Lazy qualified as LB
1315
import Data.Foldable (Foldable (fold))
1416
import Data.Map qualified as Map
1517
import Data.Set qualified as Set
1618
import Data.Text (Text, unpack)
1719
import Plutarch (Config (Config, tracingMode), TracingMode (DetTracing), compile, pcon, printScript)
20+
import Plutarch.Api.V1 (PCurrencySymbol)
1821
import Plutarch.Builtin (PIsData (pdataImpl))
1922
import Plutarch.Evaluate (evalScript)
2023
import Plutarch.Test (pfails, psucceeds)
2124
import PlutusLedgerApi.V1.Address (scriptHashAddress)
2225
import PlutusLedgerApi.V1.Value (AssetClass, TokenName (TokenName), assetClass, currencySymbol)
23-
import PlutusLedgerApi.V2 (Address, CurrencySymbol, Script, ScriptPurpose (Minting), ValidatorHash (ValidatorHash), toData)
26+
import PlutusLedgerApi.V2 (Address, CurrencySymbol, Script, ScriptPurpose (Minting), ValidatorHash (ValidatorHash), dataToBuiltinData, toData)
27+
import PlutusTx (Data)
2428
import PlutusTx.Builtins.Class (stringToBuiltinByteString)
2529

2630
coopAc :: AssetClass
@@ -265,6 +269,19 @@ spec = do
265269
# pconstant (toData ())
266270
# pconstant ctx
267271
)
272+
describe "@Consumer" $ do
273+
samplePd <- runIO $ readPlutusDataCbor "resources/sample.pd.cbor"
274+
describe "should-succeed" $ do
275+
prop "reference a fact statement" $
276+
forAll (genCorrectConsumerCtx fsCs (dataToBuiltinData samplePd)) $
277+
\ctx ->
278+
psucceeds
279+
( exampleConsumer
280+
# pconstant @PCurrencySymbol fsCs
281+
# pconstant (toData ())
282+
# pconstant (toData ())
283+
# pconstant ctx
284+
)
268285

269286
_plog :: ClosedTerm a -> Expectation
270287
_plog p = _ptraces' p id []
@@ -298,3 +315,9 @@ pshouldBe x y = do
298315
pscriptShouldBe :: Script -> Script -> Expectation
299316
pscriptShouldBe x y =
300317
printScript x `shouldBe` printScript y
318+
319+
readPlutusDataCbor :: FilePath -> IO Data
320+
readPlutusDataCbor fname = do
321+
cborBytes <- LB.readFile fname
322+
let errOrDecoded = deserialiseOrFail @Data cborBytes
323+
either (\err -> error $ "File " <> fname <> " can't be parsed into PlutusData CBOR: " <> show err) return errOrDecoded

coop-plutus/test/Coop/Plutus/Test/Generators.hs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
module Coop.Plutus.Test.Generators (mkScriptContext, mkTxInfo, genCertRdmrAc, distribute, genCorruptCertMpMintingCtx, genAaInputs, genCorrectCertMpMintingCtx, genCorrectAuthMpMintingCtx, genCorruptAuthMpMintingCtx, genCorrectCertMpBurningCtx, genCorruptCertMpBurningCtx, normalizeValue, genCorrectAuthMpBurningCtx, genCorruptAuthMpBurningCtx, genCorrectCertVSpendingCtx, genCorruptCertVSpendingCtx, genCorrectMustBurnOwnSingletonValueCtx, genCorruptMustBurnOwnSingletonValueCtx, genCorrectFsMpMintingCtx, genCorruptFsMpMintingCtx, genCorrectFsMpBurningCtx, genCorruptFsMpBurningCtx, genCorrectFsVSpendingCtx, genCorruptFsVSpendingCtx) where
1+
module Coop.Plutus.Test.Generators (mkScriptContext, mkTxInfo, genCertRdmrAc, distribute, genCorruptCertMpMintingCtx, genAaInputs, genCorrectCertMpMintingCtx, genCorrectAuthMpMintingCtx, genCorruptAuthMpMintingCtx, genCorrectCertMpBurningCtx, genCorruptCertMpBurningCtx, normalizeValue, genCorrectAuthMpBurningCtx, genCorruptAuthMpBurningCtx, genCorrectCertVSpendingCtx, genCorruptCertVSpendingCtx, genCorrectMustBurnOwnSingletonValueCtx, genCorruptMustBurnOwnSingletonValueCtx, genCorrectFsMpMintingCtx, genCorruptFsMpMintingCtx, genCorrectFsMpBurningCtx, genCorruptFsMpBurningCtx, genCorrectFsVSpendingCtx, genCorruptFsVSpendingCtx, genCorrectConsumerCtx) where
22

33
import Test.QuickCheck (Arbitrary (arbitrary), Gen, choose, chooseAny, chooseEnum, chooseInt, chooseInteger, sublistOf, suchThat, vectorOf)
44

@@ -17,7 +17,7 @@ import PlutusLedgerApi.V2 (Address, BuiltinByteString, Datum (Datum), Extended (
1717
import PlutusTx.AssocMap qualified as AssocMap
1818
import PlutusTx.Builtins.Class (stringToBuiltinByteString)
1919

20-
import Coop.Types (AuthMpParams (amp'authAuthorityAc, amp'requiredAtLeastAaQ), AuthParams (ap'authTokenCs, ap'certTokenCs), CertDatum (CertDatum), CertMpParams (cmp'authAuthorityAc, cmp'certVAddress, cmp'requiredAtLeastAaQ), FsDatum (FsDatum, fs'gcAfter, fs'submitter), FsMpParams (fmp'authParams, fmp'fsVAddress))
20+
import Coop.Types (AuthMpParams (amp'authAuthorityAc, amp'requiredAtLeastAaQ), AuthParams (ap'authTokenCs, ap'certTokenCs), CertDatum (CertDatum), CertMpParams (cmp'authAuthorityAc, cmp'certVAddress, cmp'requiredAtLeastAaQ), FactStatement, FsDatum (FsDatum, fs'gcAfter, fs'submitter), FsMpParams (fmp'authParams, fmp'fsVAddress))
2121
import PlutusLedgerApi.V1.Interval (interval)
2222
import PlutusLedgerApi.V2 qualified as Value
2323
import PlutusTx.Prelude (Group (inv))
@@ -464,6 +464,29 @@ genCorruptFsVSpendingCtx = do
464464

465465
return $ corrupt ctx
466466

467+
genFsRefInput :: CurrencySymbol -> FactStatement -> Gen TxInInfo
468+
genFsRefInput fsCs fs = do
469+
addr <- genAddress
470+
fsTn <- genTokenName
471+
fsId <- genBuiltinByteString "fsid" 5
472+
submitter <- genPubKeyHash
473+
return $
474+
TxInInfo
475+
(TxOutRef (TxId fsId) 0)
476+
( TxOut
477+
addr
478+
(Value.singleton fsCs fsTn 1)
479+
(toOutputDatum $ FsDatum fs (LedgerBytes fsId) PosInf submitter)
480+
Nothing
481+
)
482+
483+
genCorrectConsumerCtx :: CurrencySymbol -> FactStatement -> Gen ScriptContext
484+
genCorrectConsumerCtx fsCs fs = do
485+
spendingIn <- genInput
486+
fsRefIn <- genFsRefInput fsCs fs
487+
488+
return $ mkScriptContext (Spending (txInInfoOutRef spendingIn)) [spendingIn] [fsRefIn] mempty [] []
489+
467490
genInput :: Gen TxInInfo
468491
genInput = (\outRef val addr -> TxInInfo outRef (TxOut addr val NoOutputDatum Nothing)) <$> genTxOutRef <*> genSingletonValue <*> genAddress
469492

0 commit comments

Comments
 (0)