Merge pull request #238 from Liqwid-Labs/df/governor-mutation-fix

Apply governor mutation based on existing datum value
This commit is contained in:
emiflake 2023-04-24 16:49:23 +02:00 committed by GitHub
commit 47793cd0e2
7 changed files with 188 additions and 64 deletions

View file

@ -181,6 +181,11 @@ the stake validator easily. The behaviour of the default stake validator remains
Included by [#156](https://github.com/Liqwid-Labs/agora/pull/156). Included by [#156](https://github.com/Liqwid-Labs/agora/pull/156).
- Expected input datum value is pinned instead of out ref for governor mutation
effect.
Included by [#238](https://github.com/Liqwid-Labs/agora/pull/238).
## 0.2.0 -- 2022-08-13 ## 0.2.0 -- 2022-08-13
### Added ### Added

View file

@ -86,10 +86,10 @@ effectRef =
1 1
-- | The input effect datum in 'mkEffectTransaction'. -- | The input effect datum in 'mkEffectTransaction'.
mkEffectDatum :: GovernorDatum -> MutateGovernorDatum mkEffectDatum :: GovernorDatum -> GovernorDatum -> MutateGovernorDatum
mkEffectDatum newGovDatum = mkEffectDatum oldGovDatum newGovDatum =
MutateGovernorDatum MutateGovernorDatum
{ governorRef = govRef { oldDatum = oldGovDatum
, newDatum = newGovDatum , newDatum = newGovDatum
} }
@ -131,7 +131,7 @@ mkEffectTxInfo newGovDatum =
-- The effect should update 'nextProposalId' -- The effect should update 'nextProposalId'
effectInputDatum' :: MutateGovernorDatum effectInputDatum' :: MutateGovernorDatum
effectInputDatum' = mkEffectDatum newGovDatum effectInputDatum' = mkEffectDatum governorInputDatum' newGovDatum
effectInputDatum :: Datum effectInputDatum :: Datum
effectInputDatum = Datum $ toBuiltinData effectInputDatum' effectInputDatum = Datum $ toBuiltinData effectInputDatum'
effectInput :: TxOut effectInput :: TxOut

View file

@ -34,21 +34,30 @@ specs =
governorValidator governorValidator
( GovernorDatum ( GovernorDatum
def def
(ProposalId 0) nextProposalId
def def
def def
3 3
) )
MutateGovernor MutateGovernor
( ScriptContext ( ScriptContext
(mkEffectTxInfo validNewGovernorDatum) (mkEffectTxInfo validNewGovernorDatum')
(Spending govRef) (Spending govRef)
) )
, effectSucceedsWith , effectSucceedsWith
"effect validator should pass" "effect validator should pass"
effectValidator effectValidator
(mkEffectDatum validNewGovernorDatum) ( mkEffectDatum
(ScriptContext (mkEffectTxInfo validNewGovernorDatum) (Spending effectRef)) ( GovernorDatum
def
nextProposalId
def
def
3
)
validNewGovernorDatum
)
(ScriptContext (mkEffectTxInfo validNewGovernorDatum') (Spending effectRef))
] ]
, group , group
"invalid new governor datum" "invalid new governor datum"
@ -57,7 +66,7 @@ specs =
governorValidator governorValidator
( GovernorDatum ( GovernorDatum
def def
(ProposalId 0) nextProposalId
def def
def def
3 3
@ -70,8 +79,25 @@ specs =
, effectFailsWith , effectFailsWith
"effect validator should fail" "effect validator should fail"
effectValidator effectValidator
(mkEffectDatum validNewGovernorDatum) ( mkEffectDatum
( GovernorDatum
def
nextProposalId
def
def
3
)
validNewGovernorDatum
)
(ScriptContext (mkEffectTxInfo invalidNewGovernorDatum) (Spending effectRef)) (ScriptContext (mkEffectTxInfo invalidNewGovernorDatum) (Spending effectRef))
] ]
] ]
] ]
where
validNewGovernorDatum' :: GovernorDatum
validNewGovernorDatum' = validNewGovernorDatum {nextProposalId}
-- \^ The datum value pinned by the effect, disregarding the proposal ID and
-- taking this field from the governor input instead
nextProposalId :: ProposalId
nextProposalId = ProposalId 0

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -22,17 +22,17 @@ import Agora.Effect (makeEffect)
import Agora.Governor ( import Agora.Governor (
GovernorDatum, GovernorDatum,
GovernorRedeemer (MutateGovernor), GovernorRedeemer (MutateGovernor),
PGovernorDatum, PGovernorDatum (PGovernorDatum),
PGovernorRedeemer, PGovernorRedeemer,
) )
import Agora.Proposal (PProposalId)
import Agora.SafeMoney (AuthorityTokenTag, GovernorSTTag) import Agora.SafeMoney (AuthorityTokenTag, GovernorSTTag)
import Agora.Utils (ptaggedSymbolValueOf) import Agora.Utils (pfindInputWithStateThreadToken, pfindOutputWithStateThreadToken)
import Generics.SOP qualified as SOP import Generics.SOP qualified as SOP
import Plutarch.Api.V1 (PCurrencySymbol) import Plutarch.Api.V1 (PCurrencySymbol)
import Plutarch.Api.V2 ( import Plutarch.Api.V2 (
PScriptHash, PScriptHash,
PScriptPurpose (PSpending), PScriptPurpose (PSpending),
PTxOutRef,
PValidator, PValidator,
) )
import Plutarch.DataRepr ( import Plutarch.DataRepr (
@ -45,7 +45,7 @@ import Plutarch.Extra.IsData (
ProductIsData (ProductIsData), ProductIsData (ProductIsData),
) )
import Plutarch.Extra.Maybe (passertPJust, pfromJust) import Plutarch.Extra.Maybe (passertPJust, pfromJust)
import Plutarch.Extra.Record (mkRecordConstr, (.=)) import Plutarch.Extra.Record (mkRecordConstr, (.&), (.=))
import Plutarch.Extra.ScriptContext ( import Plutarch.Extra.ScriptContext (
pisScriptAddress, pisScriptAddress,
pscriptHashFromAddress, pscriptHashFromAddress,
@ -54,9 +54,8 @@ import Plutarch.Extra.ScriptContext (
) )
import Plutarch.Extra.Tagged (PTagged) import Plutarch.Extra.Tagged (PTagged)
import Plutarch.Lift (PConstantDecl, PLifted, PUnsafeLiftDecl) import Plutarch.Lift (PConstantDecl, PLifted, PUnsafeLiftDecl)
import PlutusLedgerApi.V1 (TxOutRef)
import PlutusTx qualified import PlutusTx qualified
import "liqwid-plutarch-extra" Plutarch.Extra.List (ptryFromSingleton)
import "liqwid-plutarch-extra" Plutarch.Extra.TermCont (pguardC, pletC, pletFieldsC) import "liqwid-plutarch-extra" Plutarch.Extra.TermCont (pguardC, pletC, pletFieldsC)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -66,8 +65,8 @@ import "liqwid-plutarch-extra" Plutarch.Extra.TermCont (pguardC, pletC, pletFiel
@since 0.1.0 @since 0.1.0
-} -}
data MutateGovernorDatum = MutateGovernorDatum data MutateGovernorDatum = MutateGovernorDatum
{ governorRef :: TxOutRef { oldDatum :: GovernorDatum
-- ^ Referenced governor state UTXO should be updated by the effect. -- ^ The governor datum value on which this effect is valid
, newDatum :: GovernorDatum , newDatum :: GovernorDatum
-- ^ The new settings for the governor. -- ^ The new settings for the governor.
} }
@ -100,7 +99,7 @@ newtype PMutateGovernorDatum (s :: S)
( Term ( Term
s s
( PDataRecord ( PDataRecord
'[ "governorRef" ':= PTxOutRef '[ "oldDatum" ':= PGovernorDatum
, "newDatum" ':= PGovernorDatum , "newDatum" ':= PGovernorDatum
] ]
) )
@ -194,57 +193,104 @@ mutateGovernorValidator =
pguardC "Only self and governor script inputs are allowed" $ pguardC "Only self and governor script inputs are allowed" $
plength # scriptInputs #== 2 plength # scriptInputs #== 2
pguardC "Governor input should present" $ let
pany governorInput =
# plam passertPJust
( flip pletAll $ \inputF -> # "Governor UTXO should carry GST"
let isGovernorInput = # ( pfindInputWithStateThreadToken
foldl1 # pfromData gstSymbol
(#&&) # scriptInputs
[ ptraceIfFalse "Governor UTxO should carry GST" $ )
ptaggedSymbolValueOf
# pfromData gstSymbol
# (pfield @"value" # inputF.resolved)
#== 1
, ptraceIfFalse "Can only modify the pinned governor" $
inputF.outRef #== effectDatumF.governorRef
, ptraceIfFalse "Governor validator run" $
let inputScriptHash =
pfromJust
#$ pscriptHashFromAddress
#$ pfield @"address"
# inputF.resolved
in inputScriptHash #== pfromData govValidatorHash
]
in isGovernorInput
)
# scriptInputs
let governorRedeemer = governorRef = pfield @"outRef" # governorInput
governorInputDatum =
ptrace "Resolve governor input datum" $
pfromData $ pfromData $
passertPJust ptryFromOutputDatum @(PAsData PGovernorDatum)
# "Govenor redeemer should be resolved" # (pfield @"datum" #$ pfield @"resolved" # governorInput)
#$ ptryFromRedeemer @(PAsData PGovernorRedeemer) # txInfoF.datums
# mkRecordConstr PSpending (#_0 .= effectDatumF.governorRef)
# txInfoF.redeemers inputProposalId = pfield @"nextProposalId" # governorInputDatum
expectedInputDatum =
replaceProposalId # effectDatumF.oldDatum # inputProposalId
pguardC "Governor input should be valid" $
( pletAll governorInput $ \inputF ->
let
isGovernorInput =
foldl1
(#&&)
[ ptraceIfFalse "Can only modify the pinned governor datum" $
governorInputDatum #== expectedInputDatum
, ptraceIfFalse "Governor validator run" $
let inputScriptHash =
pfromJust
#$ pscriptHashFromAddress
#$ pfield @"address"
# inputF.resolved
in inputScriptHash #== pfromData govValidatorHash
]
in
isGovernorInput
)
let
governorRedeemer =
pfromData $
passertPJust
# "Governor redeemer should be resolved"
#$ ptryFromRedeemer @(PAsData PGovernorRedeemer)
# mkRecordConstr PSpending (#_0 .= governorRef)
# txInfoF.redeemers
pguardC "Spend governor with redeemer MutateGovernor" $ pguardC "Spend governor with redeemer MutateGovernor" $
governorRedeemer #== pconstant MutateGovernor governorRedeemer #== pconstant MutateGovernor
---------------------------------------------------------------------------- ----------------------------------------------------------------------------
let governorOutput = let
ptrace "Only governor output is allowed" $ governorOutput =
ptryFromSingleton # pfromData txInfoF.outputs passertPJust
# "No governor output found"
#$ pfindOutputWithStateThreadToken
# pfromData gstSymbol
# pfromData txInfoF.outputs
governorOutputDatum = governorOutputDatum =
ptrace "Resolve governor outoput datum" $ ptrace "Resolve governor outoput datum" $
pfromData $ pfromData $
ptryFromOutputDatum @(PAsData PGovernorDatum) ptryFromOutputDatum @(PAsData PGovernorDatum)
# (pfield @"datum" # governorOutput) # (pfield @"datum" # governorOutput)
# txInfoF.datums # txInfoF.datums
expectedOutputDatum =
replaceProposalId # effectDatumF.newDatum # inputProposalId
pguardC "New governor datum correct" $ pguardC "New governor datum correct" $
governorOutputDatum #== effectDatumF.newDatum governorOutputDatum #== expectedOutputDatum
return $ popaque $ pconstant () return $ popaque $ pconstant ()
where
replaceProposalId ::
ClosedTerm
( PGovernorDatum
:--> PAsData PProposalId
:--> PGovernorDatum
)
replaceProposalId = plam $ \datum proposalId ->
pletAll datum $ \datumF ->
mkRecordConstr
PGovernorDatum
( #proposalThresholds
.= datumF.proposalThresholds
.& #nextProposalId
.= proposalId
.& #proposalTimings
.= datumF.proposalTimings
.& #createProposalTimeRangeMaxWidth
.= datumF.createProposalTimeRangeMaxWidth
.& #maximumCreatedProposalsPerStake
.= datumF.maximumCreatedProposalsPerStake
)

View file

@ -20,6 +20,8 @@ module Agora.Utils (
phashDatum, phashDatum,
puncurryTuple, puncurryTuple,
psubtractSortedValue, psubtractSortedValue,
pfindInputWithStateThreadToken,
pfindOutputWithStateThreadToken,
pisSubValueOf, pisSubValueOf,
) where ) where
@ -32,6 +34,8 @@ import Plutarch.Api.V2 (
PCurrencySymbol, PCurrencySymbol,
PMaybeData (PDNothing), PMaybeData (PDNothing),
PTuple, PTuple,
PTxInInfo,
PTxOut,
PValue, PValue,
) )
import Plutarch.Builtin (pforgetData, pserialiseData) import Plutarch.Builtin (pforgetData, pserialiseData)
@ -176,6 +180,49 @@ psubtractSortedValue = phoistAcyclic $ plam $ \a b ->
# (pfmap # pnegate) # (pfmap # pnegate)
# pto b # pto b
{- | Find an input containing exactly one token with the given currency symbol
@since 1.0.0
-}
pfindInputWithStateThreadToken ::
forall tag.
ClosedTerm
( PTagged tag PCurrencySymbol
:--> PBuiltinList PTxInInfo
:--> PMaybe PTxInInfo
)
pfindInputWithStateThreadToken = plam $ \tokenSymbol inputs ->
pfind
# ( plam $ \input ->
ptaggedSymbolValueOf
# tokenSymbol
# (pfield @"value" # (pfield @"resolved" # input))
#== 1
)
# inputs
{- | Find an output containing exactly one token with the given currency symbol,
@since 1.0.0
-}
pfindOutputWithStateThreadToken ::
forall tag.
ClosedTerm
( PTagged tag PCurrencySymbol
:--> PBuiltinList PTxOut
:--> PMaybe PTxOut
)
pfindOutputWithStateThreadToken = plam $ \tokenSymbol outputs ->
pfind
# ( plam $ \output ->
( ptaggedSymbolValueOf
# tokenSymbol
# (pfield @"value" # output)
#== 1
)
)
# outputs
pisNonNegativeValue :: pisNonNegativeValue ::
forall (kg :: KeyGuarantees) (am :: AmountGuarantees) (s :: S). forall (kg :: KeyGuarantees) (am :: AmountGuarantees) (s :: S).
Term s (PValue kg am :--> PBool) Term s (PValue kg am :--> PBool)