improve efficiency of treasury withdrawal effect script

This commit is contained in:
Hongrui Fang 2022-10-07 21:39:39 +08:00
parent 1741d8bbd5
commit 777c55cd88
3 changed files with 81 additions and 73 deletions

View file

@ -15,24 +15,27 @@ module Agora.Effect.TreasuryWithdrawal (
import Agora.Effect (makeEffect) import Agora.Effect (makeEffect)
import Agora.Plutarch.Orphans () import Agora.Plutarch.Orphans ()
import Agora.Utils (pdelete)
import Plutarch.Api.V1 ( import Plutarch.Api.V1 (
PCredential, PCredential,
PValue, PValue,
ptuple, ptuple,
) )
import Plutarch.Api.V1.Value (pnormalize)
import Plutarch.Api.V2 ( import Plutarch.Api.V2 (
AmountGuarantees (Positive), AmountGuarantees (Positive),
KeyGuarantees (Sorted), KeyGuarantees (Sorted),
PTuple, PTuple,
PTxInInfo,
PTxOut,
PValidator, PValidator,
) )
import Plutarch.DataRepr ( import Plutarch.DataRepr (
DerivePConstantViaData (DerivePConstantViaData), DerivePConstantViaData (DerivePConstantViaData),
PDataFields, PDataFields,
) )
import Plutarch.Extra.ScriptContext (pfindTxInByTxOutRef, pisPubKey) import Plutarch.Extra.Field (pletAllC)
import "liqwid-plutarch-extra" Plutarch.Extra.TermCont (pguardC, pletC, pletFieldsC, pmatchC) import Plutarch.Extra.ScriptContext (pisPubKey)
import "liqwid-plutarch-extra" Plutarch.Extra.TermCont (pguardC, pletC, pletFieldsC)
import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (PLifted)) import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (PLifted))
import PlutusLedgerApi.V1.Credential (Credential) import PlutusLedgerApi.V1.Credential (Credential)
import PlutusLedgerApi.V1.Value (CurrencySymbol, Value) import PlutusLedgerApi.V1.Value (CurrencySymbol, Value)
@ -129,69 +132,57 @@ instance PTryFrom PData PTreasuryWithdrawalDatum
-} -}
treasuryWithdrawalValidator :: forall {s :: S}. CurrencySymbol -> Term s PValidator treasuryWithdrawalValidator :: forall {s :: S}. CurrencySymbol -> Term s PValidator
treasuryWithdrawalValidator currSymbol = makeEffect currSymbol $ treasuryWithdrawalValidator currSymbol = makeEffect currSymbol $
\_cs (datum' :: Term _ PTreasuryWithdrawalDatum) txOutRef' txInfo' -> unTermCont $ do \_cs (datum :: Term _ PTreasuryWithdrawalDatum) effectInputRef txInfo -> unTermCont $ do
datum <- pletFieldsC @'["receivers", "treasuries"] datum' datumF <- pletAllC datum
txInfo <- pletFieldsC @'["outputs", "inputs"] txInfo' txInfoF <- pletFieldsC @'["outputs", "inputs"] txInfo
PJust ((pfield @"resolved" #) -> txOut) <- pmatchC $ pfindTxInByTxOutRef # txOutRef' # pfromData txInfo.inputs
effInput <- pletFieldsC @'["address", "value"] $ txOut let validateInput :: Term _ (PTxInInfo :--> PBool)
outputValues <- validateInput = plam $ \input -> unTermCont $ do
pletC $ inputF <- pletAllC input
pmap
# plam cred <-
( \txOut' -> unTermCont $ do pletC $
txOut <- pletFieldsC @'["address", "value"] $ txOut' pfield @"credential"
let cred = pfield @"credential" # pfromData txOut.address #$ pfield @"address" # inputF.resolved
pure . pdata $ ptuple # cred # txOut.value
) pure $
# pfromData txInfo.outputs foldl1
inputValues <- (#||)
pletC $ [ ptraceIfTrue "Effect input" $ inputF.outRef #== effectInputRef
pmap , ptraceIfTrue "Treasury input" $ pelem # cred # datumF.treasuries
# plam , ptraceIfTrue "Collateral input" $ pisPubKey # pfromData cred
( \((pfield @"resolved" #) -> txOut') -> unTermCont $ do ]
txOut <- pletFieldsC @'["address", "value"] $ txOut'
let cred = pfield @"credential" # pfromData txOut.address validateOutput ::
pure . pdata $ ptuple # cred # txOut.value Term
) _
# txInfo.inputs ( PBuiltinList (PAsData (PTuple PCredential (PValue 'Sorted 'Positive)))
let ofTreasury = :--> PTxOut
pfilter :--> PBuiltinList (PAsData (PTuple PCredential (PValue 'Sorted 'Positive)))
# plam (\((pfield @"_0" #) . pfromData -> cred) -> pelem # cred # datum.treasuries) )
sumValues = phoistAcyclic $ validateOutput = plam $ \receivers output -> unTermCont $ do
plam $ \v -> outputF <- pletFieldsC @'["address", "value"] output
pnormalize cred <- pletC $ pfield @"credential" # pfromData outputF.address
#$ pfoldr
# plam (\(pfromData . (pfield @"_1" #) -> x) y -> x <> y) let credValue = pdata $ ptuple # cred # outputF.value
# mempty
# v shouldSendToTreasury =
treasuryInputValuesSum = sumValues #$ ofTreasury # inputValues pif
treasuryOutputValuesSum = sumValues #$ ofTreasury # outputValues (pelem # cred # datumF.treasuries)
receiverValuesSum = sumValues # datum.receivers receivers
-- Constraints (ptraceError "Invalid receiver")
outputContentMatchesRecivers =
pall # plam (\out -> pelem # out # outputValues) pure $
#$ datum.receivers pmatch (pdelete # credValue # receivers) $ \case
excessShouldBePaidToInputs = PJust updatedReceivers ->
treasuryOutputValuesSum <> receiverValuesSum #== treasuryInputValuesSum ptrace "Receiver output" updatedReceivers
shouldNotPayToEffect = PNothing ->
pnot #$ pany ptrace "Treasury output" shouldSendToTreasury
# plam
( \x -> pguardC "All input are valid" $
effInput.address #== pfield @"address" # x pall # validateInput # txInfoF.inputs
)
# pfromData txInfo.outputs pguardC "All receiver get correct output" $
inputsAreOnlyTreasuriesOrCollateral = pnull #$ pfoldl # validateOutput # datumF.receivers # txInfoF.outputs
pall
# plam
( \((pfield @"_0" #) . pfromData -> cred) ->
cred #== pfield @"credential" # effInput.address
#|| pelem # cred # datum.treasuries
#|| pisPubKey # pfromData cred
)
# inputValues
pguardC "Transaction should not pay to effects" shouldNotPayToEffect
pguardC "Transaction output does not match receivers" outputContentMatchesRecivers
pguardC "Remainders should be returned to the treasury" excessShouldBePaidToInputs
pguardC "Transaction should only have treasuries specified in the datum as input" inputsAreOnlyTreasuriesOrCollateral
pure . popaque $ pconstant () pure . popaque $ pconstant ()

View file

@ -48,7 +48,7 @@ import Agora.Stake (
), ),
pstakeLocked, pstakeLocked,
) )
import Agora.Utils (pdeleteBy, pfromSingleton, pisSingleton) import Agora.Utils (pfromSingleton, pisSingleton, pmustDeleteBy)
import Plutarch.Api.V1.Address (PCredential) import Plutarch.Api.V1.Address (PCredential)
import Plutarch.Api.V2 (PMaybeData) import Plutarch.Api.V2 (PMaybeData)
import Plutarch.Extra.Field (pletAll, pletAllC) import Plutarch.Extra.Field (pletAll, pletAllC)
@ -88,7 +88,7 @@ pbatchUpdateInputs ::
pbatchUpdateInputs = phoistAcyclic $ pbatchUpdateInputs = phoistAcyclic $
plam $ \f -> flip pmatch $ \ctxF -> plam $ \f -> flip pmatch $ \ctxF ->
pnull #$ pfoldr pnull #$ pfoldr
# (pdeleteBy # f) # (pmustDeleteBy # f)
# ctxF.stakeOutputDatums # ctxF.stakeOutputDatums
# ctxF.stakeInputDatums # ctxF.stakeInputDatums

View file

@ -24,6 +24,7 @@ module Agora.Utils (
pcurrentTimeDuration, pcurrentTimeDuration,
pdelete, pdelete,
pdeleteBy, pdeleteBy,
pmustDeleteBy,
pisSingleton, pisSingleton,
pfromSingleton, pfromSingleton,
pmapMaybe, pmapMaybe,
@ -40,7 +41,7 @@ import Plutarch.Api.V2 (PScriptHash, PScriptPurpose)
import Plutarch.Extra.Applicative (PApplicative (ppure)) import Plutarch.Extra.Applicative (PApplicative (ppure))
import Plutarch.Extra.Category (PCategory (pidentity)) import Plutarch.Extra.Category (PCategory (pidentity))
import Plutarch.Extra.Functor (PFunctor (PSubcategory, pfmap)) import Plutarch.Extra.Functor (PFunctor (PSubcategory, pfmap))
import Plutarch.Extra.Maybe (pnothing) import Plutarch.Extra.Maybe (pjust, pnothing)
import Plutarch.Extra.Ord (PComparator, POrdering (PLT), pcompareBy, pequateBy) import Plutarch.Extra.Ord (PComparator, POrdering (PLT), pcompareBy, pequateBy)
import Plutarch.Extra.Time (PCurrentTime (PCurrentTime)) import Plutarch.Extra.Time (PCurrentTime (PCurrentTime))
import Plutarch.Unsafe (punsafeCoerce) import Plutarch.Unsafe (punsafeCoerce)
@ -214,15 +215,31 @@ pcurrentTimeDuration = phoistAcyclic $
pdelete :: pdelete ::
forall (a :: PType) (list :: PType -> PType) (s :: S). forall (a :: PType) (list :: PType -> PType) (s :: S).
(PEq a, PIsListLike list a) => (PEq a, PIsListLike list a) =>
Term s (a :--> list a :--> list a) Term s (a :--> list a :--> PMaybe (list a))
pdelete = phoistAcyclic $ pdeleteBy # plam (#==) pdelete = phoistAcyclic $ pdeleteBy # plam (#==)
-- | @since 1.0.0 -- | @since 1.0.0
pdeleteBy :: pdeleteBy ::
forall (a :: PType) (list :: PType -> PType) (s :: S). forall (a :: PType) (list :: PType -> PType) (s :: S).
(PIsListLike list a) => (PIsListLike list a) =>
Term s ((a :--> a :--> PBool) :--> a :--> list a :--> list a) Term s ((a :--> a :--> PBool) :--> a :--> list a :--> PMaybe (list a))
pdeleteBy = phoistAcyclic $ pdeleteBy = phoistAcyclic $
plam $ \f' x -> plet (f' # x) $ \f ->
precList
( \self h t ->
pif
(f # h)
(pjust # t)
(pfmap # (pcons # h) # (self # t))
)
(const pnothing)
-- | @since 1.0.0
pmustDeleteBy ::
forall (a :: PType) (list :: PType -> PType) (s :: S).
(PIsListLike list a) =>
Term s ((a :--> a :--> PBool) :--> a :--> list a :--> list a)
pmustDeleteBy = phoistAcyclic $
plam $ \f' x -> plet (f' # x) $ \f -> plam $ \f' x -> plet (f' # x) $ \f ->
precList precList
( \self h t -> ( \self h t ->
@ -231,7 +248,7 @@ pdeleteBy = phoistAcyclic $
t t
(pcons # h #$ self # t) (pcons # h #$ self # t)
) )
(const pnil) (const $ ptraceError "Cannot delete element")
{- | / O(1) /.Return true if the given list has only one element. {- | / O(1) /.Return true if the given list has only one element.