From 189973f30fef0c18d373a2e5eb765f4b4ddbb1b9 Mon Sep 17 00:00:00 2001 From: Emily Martins Date: Tue, 26 Apr 2022 15:37:54 +0200 Subject: [PATCH] reduce 'PTxInfo' passing to functions --- agora/Agora/Effect/TreasuryWithdrawal.hs | 8 +- agora/Agora/Proposal.hs | 3 + agora/Agora/Proposal/Scripts.hs | 95 ++++++++++-------------- agora/Agora/Stake.hs | 75 +++++++++++++++++-- agora/Agora/Utils.hs | 56 ++++++-------- 5 files changed, 140 insertions(+), 97 deletions(-) diff --git a/agora/Agora/Effect/TreasuryWithdrawal.hs b/agora/Agora/Effect/TreasuryWithdrawal.hs index 312efbf..8693cdf 100644 --- a/agora/Agora/Effect/TreasuryWithdrawal.hs +++ b/agora/Agora/Effect/TreasuryWithdrawal.hs @@ -40,9 +40,12 @@ import Plutus.V1.Ledger.Credential (Credential) import Plutus.V1.Ledger.Value (CurrencySymbol, Value) import PlutusTx qualified +-- | Datum that encodes behavior of Treasury Withdrawal effect. data TreasuryWithdrawalDatum = TreasuryWithdrawalDatum { receivers :: [(Credential, Value)] + -- ^ AssocMap for Value sent to each receiver from the treasury. , treasuries :: [Credential] + -- ^ What Credentials is spending from legal. } deriving stock (Show, GHC.Generic) deriving anyclass (Generic) @@ -77,7 +80,8 @@ deriving via instance PTryFrom PData PTreasuryWithdrawalDatum where type PTryFromExcess PData PTreasuryWithdrawalDatum = Const () ptryFrom' opq cont = - -- this will need to not use punsafeCoerce... + -- TODO: This should not use 'punsafeCoerce'. + -- Blocked by 'PCredential', and 'PTuple'. cont (punsafeCoerce opq, ()) {- | Withdraws given list of values to specific target addresses. @@ -90,7 +94,7 @@ instance PTryFrom PData PTreasuryWithdrawalDatum where Note: It should check... 1. Transaction outputs should contain all of what Datum specified - 2. Left over assests should be redirected back to Treasury + 2. Left over assets should be redirected back to Treasury It can be more flexiable over... - The number of outputs themselves -} diff --git a/agora/Agora/Proposal.hs b/agora/Agora/Proposal.hs index 6c26a3f..bf88bf8 100644 --- a/agora/Agora/Proposal.hs +++ b/agora/Agora/Proposal.hs @@ -128,6 +128,9 @@ data ProposalThresholds = ProposalThresholds -- ^ How much GT minimum must a particular 'ResultTag' accumulate for it to pass. , create :: Tagged GTTag Integer -- ^ How much GT required to "create" a proposal. + -- + -- It is recommended this be a high enough amount, in order to prevent DOS from bad + -- actors. , vote :: Tagged GTTag Integer -- ^ How much GT required to allow voting to happen. -- (i.e. to move into 'VotingReady') diff --git a/agora/Agora/Proposal/Scripts.hs b/agora/Agora/Proposal/Scripts.hs index ce21cea..09ae5c4 100644 --- a/agora/Agora/Proposal/Scripts.hs +++ b/agora/Agora/Proposal/Scripts.hs @@ -4,14 +4,18 @@ module Agora.Proposal.Scripts ( proposalDatumValid, ) where -import Agora.Proposal +import Agora.Proposal ( + PProposalDatum (PProposalDatum), + PProposalRedeemer (..), + PResultTag, + Proposal (governorSTAssetClass, stakeSTAssetClass), + ) import Agora.Record (mkRecordConstr, (.&), (.=)) -import Agora.Stake (PStakeDatum) +import Agora.Stake (findStakeOwnedBy) import Agora.Utils ( anyOutput, findTxOutByTxOutRef, passert, - pfindDatum', pnotNull, psymbolValueOf, ptokenSpent, @@ -20,14 +24,10 @@ import Agora.Utils ( ) import Plutarch.Api.V1 ( PDatumHash, - PMaybeData (PDJust, PDNothing), PMintingPolicy, - PPubKeyHash, PScriptContext (PScriptContext), PScriptPurpose (PMinting, PSpending), - PTxInInfo (PTxInInfo), PTxInfo (PTxInfo), - PTxOut (PTxOut), PValidator, PValidatorHash, mintingPolicySymbol, @@ -47,20 +47,20 @@ import Plutus.V1.Ledger.Value (AssetClass (AssetClass)) NOTE: The governor needs to check that the datum is correct and sent to the right address. -} -proposalPolicy :: Proposal -> ClosedTerm PMintingPolicy +proposalPolicy :: Agora.Proposal.Proposal -> ClosedTerm Plutarch.Api.V1.PMintingPolicy proposalPolicy proposal = plam $ \_redeemer ctx' -> P.do - PScriptContext ctx' <- pmatch ctx' + Plutarch.Api.V1.PScriptContext ctx' <- pmatch ctx' ctx <- pletFields @'["txInfo", "purpose"] ctx' - PTxInfo txInfo' <- pmatch $ pfromData ctx.txInfo + Plutarch.Api.V1.PTxInfo txInfo' <- pmatch $ pfromData ctx.txInfo txInfo <- pletFields @'["inputs", "mint"] txInfo' - PMinting _ownSymbol <- pmatch $ pfromData ctx.purpose + Plutarch.Api.V1.PMinting _ownSymbol <- pmatch $ pfromData ctx.purpose let inputs = txInfo.inputs mintedValue = pfromData txInfo.mint AssetClass (govCs, govTn) = proposal.governorSTAssetClass - PMinting ownSymbol' <- pmatch $ pfromData ctx.purpose + Plutarch.Api.V1.PMinting ownSymbol' <- pmatch $ pfromData ctx.purpose let mintedProposalST = passetClassValueOf # mintedValue # (passetClass # (pfield @"_0" # ownSymbol') # pconstant "") passert "Governance state-thread token must move" $ @@ -74,22 +74,22 @@ proposalPolicy proposal = popaque (pconstant ()) -- | Validator for Proposals. -proposalValidator :: Proposal -> ClosedTerm PValidator +proposalValidator :: Agora.Proposal.Proposal -> ClosedTerm Plutarch.Api.V1.PValidator proposalValidator proposal = plam $ \datum redeemer ctx' -> P.do - PScriptContext ctx' <- pmatch ctx' + Plutarch.Api.V1.PScriptContext ctx' <- pmatch ctx' ctx <- pletFields @'["txInfo", "purpose"] ctx' txInfo <- plet $ pfromData ctx.txInfo - PTxInfo txInfo' <- pmatch txInfo - txInfoF <- pletFields @'["inputs", "mint"] txInfo' - PSpending ((pfield @"_0" #) -> txOutRef) <- pmatch $ pfromData ctx.purpose + Plutarch.Api.V1.PTxInfo txInfo' <- pmatch txInfo + txInfoF <- pletFields @'["inputs", "mint", "datums", "signatories"] txInfo' + Plutarch.Api.V1.PSpending ((pfield @"_0" #) -> txOutRef) <- pmatch $ pfromData ctx.purpose PJust txOut <- pmatch $ findTxOutByTxOutRef # txOutRef # txInfoF.inputs txOutF <- pletFields @'["address", "value"] $ txOut - let proposalDatum :: Term _ PProposalDatum + let proposalDatum :: Term _ Agora.Proposal.PProposalDatum proposalDatum = pfromData $ punsafeCoerce datum - proposalRedeemer :: Term _ PProposalRedeemer + proposalRedeemer :: Term _ Agora.Proposal.PProposalRedeemer proposalRedeemer = pfromData $ punsafeCoerce redeemer proposalF <- @@ -105,73 +105,53 @@ proposalValidator proposal = ownAddress <- plet $ txOutF.address - stCurrencySymbol <- plet $ pconstant $ mintingPolicySymbol $ mkMintingPolicy (proposalPolicy proposal) + stCurrencySymbol <- plet $ pconstant $ Plutarch.Api.V1.mintingPolicySymbol $ Plutarch.Api.V1.mkMintingPolicy (proposalPolicy proposal) valueSpent <- plet $ pvalueSpent # txInfoF.inputs spentST <- plet $ psymbolValueOf # stCurrencySymbol #$ valueSpent let AssetClass (stakeSym, stakeTn) = proposal.stakeSTAssetClass stakeSTAssetClass <- plet $ passetClass # pconstant stakeSym # pconstant stakeTn spentStakeST <- plet $ passetClassValueOf # valueSpent # stakeSTAssetClass + signedBy <- plet $ ptxSignedBy # txInfoF.signatories + pmatch proposalRedeemer $ \case - PVote _r -> P.do + Agora.Proposal.PVote _r -> P.do passert "ST at inputs must be 1" $ spentST #== 1 popaque (pconstant ()) -------------------------------------------------------------------------- - PCosign r -> P.do + Agora.Proposal.PCosign r -> P.do newSigs <- plet $ pfield @"newCosigners" # r passert "ST at inputs must be 1" $ spentST #== 1 passert "Signed by all new cosigners" $ - pall # plam (\sig -> ptxSignedBy # ctx.txInfo # sig) # newSigs + pall # signedBy # newSigs passert "As many new cosigners as Stake datums" $ spentStakeST #== plength # newSigs - let stakeDatumOwnedBy :: Term _ (PPubKeyHash :--> PStakeDatum :--> PBool) - stakeDatumOwnedBy = - phoistAcyclic $ - plam $ \pk stakeDatum -> P.do - stakeDatumF <- pletFields @'["owner"] $ pto stakeDatum - stakeDatumF.owner #== pdata pk - - -- Does the input have a `Stake` owned by a particular PK? - let isInputStakeOwnedBy :: Term _ (PAsData PPubKeyHash :--> PAsData PTxInInfo :--> PBool) - isInputStakeOwnedBy = - plam $ \ss txInInfo' -> P.do - PTxInInfo ((pfield @"resolved" #) -> txOut) <- pmatch $ pfromData txInInfo' - PTxOut txOut' <- pmatch txOut - txOutF <- pletFields @'["value", "datumHash"] txOut' - outStakeST <- plet $ passetClassValueOf # txOutF.value # stakeSTAssetClass - pmatch txOutF.datumHash $ \case - PDNothing _ -> pcon PFalse - PDJust ((pfield @"_0" #) -> datumHash) -> - pif - (outStakeST #== 1) - -- TODO: use 'ptryFindDatum' instead in the future - ( pmatch (pfindDatum' # datumHash # txInfo) $ \case - PNothing -> pcon PFalse - PJust v -> stakeDatumOwnedBy # pfromData ss # pfromData v - ) - (pcon PFalse) - passert "All new cosigners are witnessed by their Stake datums" $ pall - # plam (\sig -> pany # (isInputStakeOwnedBy # sig) # txInfoF.inputs) + # plam + ( \sig -> + pmatch (findStakeOwnedBy # stakeSTAssetClass # pfromData sig # txInfoF.datums # txInfoF.inputs) $ \case + PNothing -> pcon PFalse + PJust _ -> pcon PTrue + ) # newSigs passert "Signatures are correctly added to cosignature list" $ - anyOutput @PProposalDatum # ctx.txInfo + anyOutput @Agora.Proposal.PProposalDatum # ctx.txInfo #$ plam $ \newValue address newProposalDatum -> P.do let correctDatum = pdata newProposalDatum #== pdata ( mkRecordConstr - PProposalDatum + Agora.Proposal.PProposalDatum ( #proposalId .= proposalF.proposalId .& #effects .= proposalF.effects .& #status .= proposalF.status @@ -191,13 +171,13 @@ proposalValidator proposal = popaque (pconstant ()) -------------------------------------------------------------------------- - PUnlock _r -> P.do + Agora.Proposal.PUnlock _r -> P.do passert "ST at inputs must be 1" $ spentST #== 1 popaque (pconstant ()) -------------------------------------------------------------------------- - PAdvanceProposal _r -> P.do + Agora.Proposal.PAdvanceProposal _r -> P.do passert "ST at inputs must be 1" $ spentST #== 1 @@ -207,13 +187,13 @@ proposalValidator proposal = This can be used to check both upopn creation and upon any following state transitions in the proposal. -} -proposalDatumValid :: Term s (PProposalDatum :--> PBool) +proposalDatumValid :: Term s (Agora.Proposal.PProposalDatum :--> PBool) proposalDatumValid = phoistAcyclic $ plam $ \datum' -> P.do datum <- pletFields @'["effects", "cosigners"] $ datum' - let effects :: Term _ (PBuiltinMap PResultTag (PBuiltinMap PValidatorHash PDatumHash)) + let effects :: Term _ (PBuiltinMap Agora.Proposal.PResultTag (PBuiltinMap Plutarch.Api.V1.PValidatorHash Plutarch.Api.V1.PDatumHash)) effects = punsafeCoerce datum.effects atLeastOneNegativeResult :: Term _ PBool @@ -224,4 +204,5 @@ proposalDatumValid = (#&&) [ ptraceIfFalse "Proposal has at least one ResultTag has no effects" atLeastOneNegativeResult , ptraceIfFalse "Proposal has at least one cosigner" $ pnotNull # pfromData datum.cosigners + , ptraceIfFalse "Proposal has at most five cosigners" $ plength # (pfromData datum.cosigners) #< 6 ] diff --git a/agora/Agora/Stake.hs b/agora/Agora/Stake.hs index f484f9d..d5d872f 100644 --- a/agora/Agora/Stake.hs +++ b/agora/Agora/Stake.hs @@ -18,6 +18,7 @@ module Agora.Stake ( stakePolicy, stakeValidator, stakeLocked, + findStakeOwnedBy, ) where -------------------------------------------------------------------------------- @@ -35,11 +36,17 @@ import PlutusTx qualified import Plutarch.Api.V1 ( PCredential (PPubKeyCredential, PScriptCredential), + PDatum, + PDatumHash, + PMaybeData (PDJust, PDNothing), PMintingPolicy, PPubKeyHash, PScriptPurpose (PMinting, PSpending), PTokenName, + PTuple, + PTxInInfo (PTxInInfo), PTxInfo, + PTxOut (PTxOut), PValidator, mintingPolicySymbol, mkMintingPolicy, @@ -63,6 +70,7 @@ import Agora.Utils ( anyOutput, paddValue, passert, + pfindDatum, pfindTxInByTxOutRef, pgeqByClass, pgeqByClass', @@ -74,7 +82,7 @@ import Agora.Utils ( ptxSignedBy, pvalueSpent, ) -import Plutarch.Api.V1.Extra (passetClass) +import Plutarch.Api.V1.Extra (PAssetClass, passetClass, passetClassValueOf) import Plutarch.Numeric import Plutarch.SafeMoney ( PDiscrete, @@ -278,7 +286,7 @@ stakePolicy gtClassRef = txInfo <- plet $ ctx.txInfo let _a :: Term _ PTxInfo _a = txInfo - txInfoF <- pletFields @'["mint", "inputs", "outputs"] txInfo + txInfoF <- pletFields @'["mint", "inputs", "outputs", "signatories"] txInfo PMinting ownSymbol' <- pmatch $ pfromData ctx.purpose ownSymbol <- plet $ pfield @"_0" # ownSymbol' @@ -337,7 +345,7 @@ stakePolicy gtClassRef = # stValue let ownerSignsTransaction = ptxSignedBy - # ctx.txInfo + # txInfoF.signatories # stakeDatum.owner -- TODO: This is quite inefficient now, as it does two lookups @@ -371,7 +379,7 @@ stakeValidator stake = plam $ \datum redeemer ctx' -> P.do ctx <- pletFields @'["txInfo", "purpose"] ctx' txInfo <- plet $ pfromData ctx.txInfo - txInfoF <- pletFields @'["mint", "inputs", "outputs"] txInfo + txInfoF <- pletFields @'["mint", "inputs", "outputs", "signatories"] txInfo (pfromData -> stakeRedeemer, _) <- ptryFrom redeemer @@ -387,7 +395,7 @@ stakeValidator stake = let continuingValue = pfield @"value" #$ pfield @"resolved" # txInInfo -- Whether the owner signs this transaction or not. - ownerSignsTransaction <- plet $ ptxSignedBy # ctx.txInfo # stakeDatum.owner + ownerSignsTransaction <- plet $ ptxSignedBy # txInfoF.signatories # stakeDatum.owner stCurrencySymbol <- plet $ pconstant $ mintingPolicySymbol $ mkMintingPolicy (stakePolicy stake.gtClassRef) mintedST <- plet $ psymbolValueOf # stCurrencySymbol # txInfoF.mint @@ -514,3 +522,60 @@ stakeLocked = phoistAcyclic $ let locks :: Term _ (PBuiltinList (PAsData PProposalLock)) locks = pfield @"lockedBy" # stakeDatum in pnotNull # locks + +-- | Find a stake owned by a particular PK. +findStakeOwnedBy :: + Term + s + ( PAssetClass + :--> PPubKeyHash + :--> PBuiltinList (PAsData (PTuple PDatumHash PDatum)) + :--> PBuiltinList (PAsData PTxInInfo) + :--> PMaybe PTxOut + ) +findStakeOwnedBy = phoistAcyclic $ + plam $ \ac pk datums inputs -> + pmatch (pfind # (isInputStakeOwnedBy # ac # pk # datums) # inputs) $ \case + PNothing -> pcon PNothing + PJust (pfromData -> v) -> P.do + let txOut = pfield @"resolved" # pto v + txOutF <- pletFields @'["datumHash"] $ txOut + pmatch txOutF.datumHash $ \case + PDNothing _ -> pcon PNothing + PDJust ((pfield @"_0" #) -> dh) -> + -- TODO: PTryFrom here + punsafeCoerce $ pfindDatum # dh # datums + +stakeDatumOwnedBy :: Term _ (PPubKeyHash :--> PStakeDatum :--> PBool) +stakeDatumOwnedBy = + phoistAcyclic $ + plam $ \pk stakeDatum -> P.do + stakeDatumF <- pletFields @'["owner"] $ pto stakeDatum + stakeDatumF.owner #== pdata pk + +-- Does the input have a `Stake` owned by a particular PK? +isInputStakeOwnedBy :: + Term + _ + ( PAssetClass :--> PPubKeyHash + :--> PBuiltinList (PAsData (PTuple PDatumHash PDatum)) + :--> PAsData PTxInInfo + :--> PBool + ) +isInputStakeOwnedBy = + plam $ \ac ss datums txInInfo' -> P.do + PTxInInfo ((pfield @"resolved" #) -> txOut) <- pmatch $ pfromData txInInfo' + PTxOut txOut' <- pmatch txOut + txOutF <- pletFields @'["value", "datumHash"] txOut' + outStakeST <- plet $ passetClassValueOf # txOutF.value # ac + pmatch txOutF.datumHash $ \case + PDNothing _ -> pcon PFalse + PDJust ((pfield @"_0" #) -> datumHash) -> + pif + (outStakeST #== 1) + -- TODO: use 'ptryFindDatum' instead in the future + ( pmatch (pfindDatum # datumHash # datums) $ \case + PNothing -> pcon PFalse + PJust v -> stakeDatumOwnedBy # ss # pfromData (punsafeCoerce v) + ) + (pcon PFalse) diff --git a/agora/Agora/Utils.hs b/agora/Agora/Utils.hs index 7a2ee01..e1b80e1 100644 --- a/agora/Agora/Utils.hs +++ b/agora/Agora/Utils.hs @@ -56,7 +56,7 @@ import Plutarch.Api.V1 ( PTokenName, PTuple, PTxInInfo (PTxInInfo), - PTxInfo (PTxInfo), + PTxInfo, PTxOut (PTxOut), PTxOutRef, PValidatorHash, @@ -78,35 +78,30 @@ passert :: Term s PString -> Term s PBool -> Term s k -> Term s k passert errorMessage check k = pif check k (ptraceError errorMessage) -- | Find a datum with the given hash. -pfindDatum :: Term s (PDatumHash :--> PTxInfo :--> PMaybe PDatum) +pfindDatum :: Term s (PDatumHash :--> PBuiltinList (PAsData (PTuple PDatumHash PDatum)) :--> PMaybe PDatum) pfindDatum = phoistAcyclic $ - plam $ \datumHash txInfo'' -> P.do - PTxInfo txInfo' <- pmatch txInfo'' - plookupTuple # datumHash #$ pfield @"datums" # txInfo' + plam $ \datumHash datums -> plookupTuple # datumHash # datums -- | Find a datum with the given hash, and `ptryFrom` it. -ptryFindDatum :: PTryFrom PData a => Term s (PDatumHash :--> PTxInfo :--> PMaybe a) +ptryFindDatum :: PTryFrom PData a => Term s (PDatumHash :--> PBuiltinList (PAsData (PTuple PDatumHash PDatum)) :--> PMaybe a) ptryFindDatum = phoistAcyclic $ - plam $ \datumHash txInfo'' -> P.do - PTxInfo txInfo' <- pmatch txInfo'' - pmatch (plookupTuple # datumHash #$ pfield @"datums" # txInfo') $ \case + plam $ \datumHash inputs -> P.do + pmatch (pfindDatum # datumHash # inputs) $ \case PNothing -> pcon PNothing PJust datum -> P.do - (datum', _) <- ptryFrom $ pto datum + (datum', _) <- ptryFrom (pto datum) pcon (PJust datum') {- | Find a datum with the given hash. NOTE: this is unsafe in the sense that, if the data layout is wrong, this is UB. -} -pfindDatum' :: PIsData a => Term s (PDatumHash :--> PTxInfo :--> PMaybe (PAsData a)) +pfindDatum' :: PIsData a => Term s (PDatumHash :--> PBuiltinList (PAsData (PTuple PDatumHash PDatum)) :--> PMaybe (PAsData a)) pfindDatum' = phoistAcyclic $ plam $ \dh x -> punsafeCoerce $ pfindDatum # dh # x -- | Check if a PubKeyHash signs this transaction. -ptxSignedBy :: Term s (PTxInfo :--> PAsData PPubKeyHash :--> PBool) +ptxSignedBy :: Term s (PBuiltinList (PAsData PPubKeyHash) :--> PAsData PPubKeyHash :--> PBool) ptxSignedBy = phoistAcyclic $ - plam $ \txInfo' pkh -> P.do - txInfo <- pletFields @'["signatories"] txInfo' - pelem @PBuiltinList # pkh # txInfo.signatories + plam $ \sigs sig -> pelem # sig # sigs -- | Get the first element that matches a predicate or return Nothing. pfind' :: @@ -334,14 +329,14 @@ anyOutput :: Term s (PTxInfo :--> (PValue :--> PAddress :--> datum :--> PBool) :--> PBool) anyOutput = phoistAcyclic $ plam $ \txInfo' predicate -> P.do - txInfo <- pletFields @'["outputs"] txInfo' + txInfo <- pletFields @'["outputs", "datums"] txInfo' pany # plam ( \txOut'' -> P.do PTxOut txOut' <- pmatch (pfromData txOut'') txOut <- pletFields @'["value", "datumHash", "address"] txOut' PDJust dh <- pmatch txOut.datumHash - pmatch (pfindDatum' @datum # (pfield @"_0" # dh) # txInfo') $ \case + pmatch (pfindDatum' @datum # (pfield @"_0" # dh) # txInfo.datums) $ \case PJust datum -> P.do predicate # txOut.value # txOut.address # pfromData datum PNothing -> pcon PFalse @@ -356,14 +351,14 @@ allOutputs :: Term s (PTxInfo :--> (PTxOut :--> PValue :--> PAddress :--> datum :--> PBool) :--> PBool) allOutputs = phoistAcyclic $ plam $ \txInfo' predicate -> P.do - txInfo <- pletFields @'["outputs"] txInfo' + txInfo <- pletFields @'["outputs", "datums"] txInfo' pall # plam ( \txOut'' -> P.do PTxOut txOut' <- pmatch (pfromData txOut'') txOut <- pletFields @'["value", "datumHash", "address"] txOut' PDJust dh <- pmatch txOut.datumHash - pmatch (pfindDatum' @datum # (pfield @"_0" # dh) # txInfo') $ \case + pmatch (pfindDatum' @datum # (pfield @"_0" # dh) # txInfo.datums) $ \case PJust datum -> P.do predicate # pfromData txOut'' # txOut.value # txOut.address # pfromData datum PNothing -> pcon PFalse @@ -378,7 +373,7 @@ anyInput :: Term s (PTxInfo :--> (PValue :--> PAddress :--> datum :--> PBool) :--> PBool) anyInput = phoistAcyclic $ plam $ \txInfo' predicate -> P.do - txInfo <- pletFields @'["inputs"] txInfo' + txInfo <- pletFields @'["inputs", "datums"] txInfo' pany # plam ( \txInInfo'' -> P.do @@ -387,7 +382,7 @@ anyInput = phoistAcyclic $ PTxOut txOut' <- pmatch (pfromData txOut'') txOut <- pletFields @'["value", "datumHash", "address"] txOut' PDJust dh <- pmatch txOut.datumHash - pmatch (pfindDatum' @datum # (pfield @"_0" # dh) # txInfo') $ \case + pmatch (pfindDatum' @datum # (pfield @"_0" # dh) # txInfo.datums) $ \case PJust datum -> P.do predicate # txOut.value # txOut.address # pfromData datum PNothing -> pcon PFalse @@ -420,23 +415,18 @@ scriptHashFromAddress = phoistAcyclic $ _ -> pcon PNothing -- | Find all TxOuts sent to an Address -findOutputsToAddress :: Term s (PTxInfo :--> PAddress :--> PBuiltinList (PAsData PTxOut)) +findOutputsToAddress :: Term s (PBuiltinList (PAsData PTxOut) :--> PAddress :--> PBuiltinList (PAsData PTxOut)) findOutputsToAddress = phoistAcyclic $ - plam $ \info address' -> P.do + plam $ \outputs address' -> P.do address <- plet $ pdata address' - let outputs = pfromData $ pfield @"outputs" # info - filteredOutputs = - pfilter - # plam - (\(pfromData -> txOut) -> pfield @"address" # txOut #== address) - # outputs - filteredOutputs + pfilter # plam (\(pfromData -> txOut) -> pfield @"address" # txOut #== address) + # outputs -- | Find the data corresponding to a TxOut, if there is one -findTxOutDatum :: Term s (PTxInfo :--> PTxOut :--> PMaybe PDatum) +findTxOutDatum :: Term s (PBuiltinList (PAsData (PTuple PDatumHash PDatum)) :--> PTxOut :--> PMaybe PDatum) findTxOutDatum = phoistAcyclic $ - plam $ \info out -> P.do + plam $ \datums out -> P.do datumHash' <- pmatch $ pfromData $ pfield @"datumHash" # out case datumHash' of - PDJust ((pfield @"_0" #) -> datumHash) -> pfindDatum # datumHash # info + PDJust ((pfield @"_0" #) -> datumHash) -> pfindDatum # datumHash # datums _ -> pcon PNothing