add validation logic for unlocking stakes

This commit is contained in:
fanghr 2022-05-31 18:15:59 +08:00 committed by Hongrui Fang
parent 13dff0db43
commit 9b88d5c099
5 changed files with 192 additions and 34 deletions

View file

@ -33,6 +33,7 @@ module Agora.Proposal (
pemptyVotesFor, pemptyVotesFor,
pwinner, pwinner,
pneutralOption, pneutralOption,
pretractVotes,
) where ) where
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -63,7 +64,7 @@ import Plutarch.DataRepr (DerivePConstantViaData (..), PDataFields, PIsDataReprI
import Plutarch.Extra.List (pnotNull) import Plutarch.Extra.List (pnotNull)
import Plutarch.Extra.Map qualified as PM import Plutarch.Extra.Map qualified as PM
import Plutarch.Extra.Map.Unsorted qualified as PUM import Plutarch.Extra.Map.Unsorted qualified as PUM
import Plutarch.Extra.TermCont (pletC) import Plutarch.Extra.TermCont (pguardC, pletC)
import Plutarch.Lift ( import Plutarch.Lift (
DerivePConstantViaNewtype (..), DerivePConstantViaNewtype (..),
PConstantDecl, PConstantDecl,
@ -367,6 +368,24 @@ newtype PProposalVotes (s :: S)
= PProposalVotes (Term s (PMap 'Unsorted PResultTag PInteger)) = PProposalVotes (Term s (PMap 'Unsorted PResultTag PInteger))
deriving (PlutusType, PIsData) via (DerivePNewtype PProposalVotes (PMap 'Unsorted PResultTag PInteger)) deriving (PlutusType, PIsData) via (DerivePNewtype PProposalVotes (PMap 'Unsorted PResultTag PInteger))
-- | Retract votes given the option and the amount of votes.
pretractVotes :: Term s (PProposalVotes :--> PResultTag :--> PInteger :--> PProposalVotes)
pretractVotes = phoistAcyclic $
plam $ \votes rt count ->
let voteMap :: Term _ (PMap 'Unsorted PResultTag PInteger)
voteMap = pto votes
in pcon $
PProposalVotes $
PM.pupdate
# plam
( \oldCount -> unTermCont $ do
newCount <- pletC $ oldCount - count
pguardC "Resulting vote count greater or equal to 0" $ 0 #<= newCount
pure $ pcon $ PJust newCount
)
# rt
# voteMap
instance PUnsafeLiftDecl PProposalVotes where type PLifted PProposalVotes = ProposalVotes instance PUnsafeLiftDecl PProposalVotes where type PLifted PProposalVotes = ProposalVotes
deriving via deriving via
(DerivePConstantViaNewtype ProposalVotes PProposalVotes (PMap 'Unsorted PResultTag PInteger)) (DerivePConstantViaNewtype ProposalVotes PProposalVotes (PMap 'Unsorted PResultTag PInteger))

View file

@ -17,6 +17,7 @@ import Agora.Proposal (
PProposalVotes (PProposalVotes), PProposalVotes (PProposalVotes),
Proposal (governorSTAssetClass, stakeSTAssetClass), Proposal (governorSTAssetClass, stakeSTAssetClass),
ProposalStatus (..), ProposalStatus (..),
pretractVotes,
) )
import Agora.Proposal.Time ( import Agora.Proposal.Time (
currentProposalTime, currentProposalTime,
@ -25,7 +26,13 @@ import Agora.Proposal.Time (
isLockingPeriod, isLockingPeriod,
isVotingPeriod, isVotingPeriod,
) )
import Agora.Stake (PProposalLock (..), PStakeDatum (..), findStakeOwnedBy) import Agora.Stake (
PProposalLock (..),
PStakeDatum (..),
PStakeUsage (..),
findStakeOwnedBy,
pgetStakeUsage,
)
import Agora.Utils ( import Agora.Utils (
findTxOutByTxOutRef, findTxOutByTxOutRef,
getMintingPolicySymbol, getMintingPolicySymbol,
@ -183,7 +190,6 @@ proposalValidator proposal =
let stCurrencySymbol = let stCurrencySymbol =
pconstant $ getMintingPolicySymbol (proposalPolicy proposal.governorSTAssetClass) pconstant $ getMintingPolicySymbol (proposalPolicy proposal.governorSTAssetClass)
valueSpent <- pletC $ pvalueSpent # txInfoF.inputs valueSpent <- pletC $ pvalueSpent # txInfoF.inputs
spentST <- pletC $ psymbolValueOf # stCurrencySymbol #$ valueSpent
signedBy <- pletC $ ptxSignedBy # txInfoF.signatories signedBy <- pletC $ ptxSignedBy # txInfoF.signatories
@ -225,6 +231,7 @@ proposalValidator proposal =
# (pfield @"datumHash" # ownOutput) # (pfield @"datumHash" # ownOutput)
# txInfoF.datums # txInfoF.datums
proposalUnchanged <- pletC $ proposalOut #== proposalDatum
-------------------------------------------------------------------------- --------------------------------------------------------------------------
-- Find the stake input and stake output by SST. -- Find the stake input and stake output by SST.
@ -234,8 +241,6 @@ proposalValidator proposal =
spentStakeST <- spentStakeST <-
pletC $ passetClassValueOf # valueSpent # stakeSTAssetClass pletC $ passetClassValueOf # valueSpent # stakeSTAssetClass
pguardC "ST at inputs must be 1" (spentST #== 1)
let stakeInput = let stakeInput =
pfield @"resolved" pfield @"resolved"
#$ mustBePJust #$ mustBePJust
@ -397,7 +402,82 @@ proposalValidator proposal =
pure $ popaque (pconstant ()) pure $ popaque (pconstant ())
-------------------------------------------------------------------------- --------------------------------------------------------------------------
PUnlock _r -> popaque (pconstant ()) PUnlock r -> unTermCont $ do
-- At draft stage, the votes should be empty.
pguardC "Shouldn't retract votes from a draft propsoal" $
pnot #$ proposalF.status #== pconstantData Draft
-- This is the vote option we're retracting from.
retractFrom <- pletC $ pfield @"resultTag" # r
-- Determine if the input stake is actually locked by this proposal.
stakeUsage <- pletC $ pgetStakeUsage # stakeInF.lockedBy # proposalF.proposalId
pguardC "Stake input relevant" $
pmatch stakeUsage $ \case
PDidNothing ->
ptrace "Not relevant" $
pconstant False
PCreated ->
ptraceIfFalse "Too early" $
proposalF.status #== pconstantData Finished
PVotedFor rt ->
ptraceIfFalse "Result tag not match" $
rt #== retractFrom
-- The count of removing votes is equal to the 'stakeAmount' of input stake.
retractCount <-
pletC $
pmatch stakeInF.stakedAmount $ (\(PDiscrete v) -> pextract # v)
-- The votes can only change when the proposal still allows voting.
let shouldUpdateVotes =
proposalF.status #== pconstantData VotingReady
#&& pnot # (pcon PCreated #== stakeUsage)
pguardC "Proposal output correct" $
pif
shouldUpdateVotes
( let -- Remove votes and leave other parts of the proposal as it.
expectedVotes = pretractVotes # proposalF.votes # retractFrom # retractCount
expectedProposalOut =
mkRecordConstr
PProposalDatum
( #proposalId .= proposalF.proposalId
.& #effects .= proposalF.effects
.& #status .= proposalF.status
.& #cosigners .= proposalF.cosigners
.& #thresholds .= proposalF.thresholds
.& #votes .= pdata expectedVotes
.& #timingConfig .= proposalF.timingConfig
.& #startingTime .= proposalF.startingTime
)
in ptraceIfFalse "Update votes" $
expectedProposalOut #== proposalOut
)
-- No change to the proposal is allowed.
$ ptraceIfFalse "Proposal unchanged" proposalUnchanged
-- At last, we ensure that all locks belong to this proposal will be removed.
stakeOutputLocks <- pletC $ pfield @"lockedBy" # stakeOut
let templateStakeOut =
mkRecordConstr
PStakeDatum
( #stakedAmount .= stakeInF.stakedAmount
.& #owner .= stakeInF.owner
.& #lockedBy .= stakeOutputLocks
)
pguardC "Only locks updated in the output stake" $
templateStakeOut #== stakeOut
pguardC "All relevant locks removed from the stake" $
pgetStakeUsage # pfromData stakeOutputLocks
# proposalF.proposalId #== pcon PDidNothing
pure $ popaque (pconstant ())
-------------------------------------------------------------------------- --------------------------------------------------------------------------
PAdvanceProposal _r -> unTermCont $ do PAdvanceProposal _r -> unTermCont $ do
pguardC "Stake should not change" stakeUnchanged pguardC "Stake should not change" stakeUnchanged

View file

@ -18,10 +18,12 @@ module Agora.Stake (
PStakeDatum (..), PStakeDatum (..),
PStakeRedeemer (..), PStakeRedeemer (..),
PProposalLock (..), PProposalLock (..),
PStakeUsage (..),
-- * Utility functions -- * Utility functions
stakeLocked, stakeLocked,
findStakeOwnedBy, findStakeOwnedBy,
pgetStakeUsage,
) where ) where
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -29,7 +31,7 @@ module Agora.Stake (
import Control.Applicative (Const) import Control.Applicative (Const)
import Data.Tagged (Tagged (..)) import Data.Tagged (Tagged (..))
import GHC.Generics qualified as GHC import GHC.Generics qualified as GHC
import Generics.SOP (Generic, I (I)) import Generics.SOP (Generic, HasDatatypeInfo, I (I))
import Prelude hiding (Num (..)) import Prelude hiding (Num (..))
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -56,8 +58,8 @@ import Plutarch.DataRepr (
PDataFields, PDataFields,
PIsDataReprInstances (PIsDataReprInstances), PIsDataReprInstances (PIsDataReprInstances),
) )
import Plutarch.Extra.List (pnotNull) import Plutarch.Extra.List (pmapMaybe, pnotNull)
import Plutarch.Extra.TermCont (pletC, pmatchC) import Plutarch.Extra.TermCont (pletC, pletFieldsC, pmatchC)
import Plutarch.Internal (punsafeCoerce) import Plutarch.Internal (punsafeCoerce)
import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (..)) import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (..))
import Plutarch.SafeMoney (PDiscrete) import Plutarch.SafeMoney (PDiscrete)
@ -286,7 +288,7 @@ stakeDatumOwnedBy =
pletFields @'["owner"] (pto stakeDatum) $ \stakeDatumF -> pletFields @'["owner"] (pto stakeDatum) $ \stakeDatumF ->
stakeDatumF.owner #== pdata pk stakeDatumF.owner #== pdata pk
-- Does the input have a `Stake` owned by a particular PK? -- | Does the input have a `Stake` owned by a particular PK?
isInputStakeOwnedBy :: isInputStakeOwnedBy ::
Term Term
_ _
@ -299,7 +301,7 @@ isInputStakeOwnedBy =
plam $ \ac ss datums txInInfo' -> unTermCont $ do plam $ \ac ss datums txInInfo' -> unTermCont $ do
PTxInInfo ((pfield @"resolved" #) -> txOut) <- pmatchC $ pfromData txInInfo' PTxInInfo ((pfield @"resolved" #) -> txOut) <- pmatchC $ pfromData txInInfo'
PTxOut txOut' <- pmatchC txOut PTxOut txOut' <- pmatchC txOut
txOutF <- tcont $ pletFields @'["value", "datumHash"] txOut' txOutF <- pletFieldsC @'["value", "datumHash"] txOut'
outStakeST <- pletC $ passetClassValueOf # txOutF.value # ac outStakeST <- pletC $ passetClassValueOf # txOutF.value # ac
pure $ pure $
pmatch txOutF.datumHash $ \case pmatch txOutF.datumHash $ \case
@ -312,3 +314,53 @@ isInputStakeOwnedBy =
PJust v -> stakeDatumOwnedBy # ss # pfromData (punsafeCoerce v) PJust v -> stakeDatumOwnedBy # ss # pfromData (punsafeCoerce v)
) )
(pcon PFalse) (pcon PFalse)
{- | Represent the usage of a stake on a particular proposal.
A stake can be used to either create or vote on a proposal.
-}
data PStakeUsage (s :: S)
= PVotedFor (Term s PResultTag)
| PCreated
| PDidNothing
deriving stock (GHC.Generic)
deriving anyclass (Generic, PlutusType, HasDatatypeInfo, PEq)
{- | / O(n) /.Return the usage of a stake on a particular proposal,
given the 'lockedBy' field of a stake and the target proposal.
-}
pgetStakeUsage ::
Term
_
( PBuiltinList (PAsData PProposalLock)
:--> PProposalId
:--> PStakeUsage
)
pgetStakeUsage = phoistAcyclic $
plam $ \locks pid ->
let -- All locks from the given proposal.
filteredLocks =
pmapMaybe
# plam
( \lock'@(pfromData -> lock) -> unTermCont $ do
lockF <- pletFieldsC @'["proposalTag"] lock
pure $
pif
(lockF.proposalTag #== pid)
(pcon $ PJust lock')
(pcon PNothing)
)
# locks
lockCount' = plength # filteredLocks
in plet lockCount' $ \lockCount ->
pif (lockCount #== 0) (pcon PDidNothing) $
pif
(lockCount #== 1)
( pcon $
PVotedFor $
pfromData $
pfield @"vote" #$ phead # filteredLocks
)
-- Note: see the implementation of the governor.
(pcon PCreated)

View file

@ -287,7 +287,7 @@ stakeValidator stake =
pure $ pure $
pmatch stakeRedeemer $ \case pmatch stakeRedeemer $ \case
PRetractVotes _ -> unTermCont $ do PRetractVotes l -> unTermCont $ do
pguardC pguardC
"Owner signs this transaction" "Owner signs this transaction"
ownerSignsTransaction ownerSignsTransaction
@ -301,15 +301,22 @@ stakeValidator stake =
spentProposalST #== 1 spentProposalST #== 1
pguardC "A UTXO must exist with the correct output" $ pguardC "A UTXO must exist with the correct output" $
unTermCont $ do let expectedLocks = pfield @"locks" # l
let valueCorrect = ownOutputValueUnchanged
-- TODO: check output datum is expected. expectedDatum =
mkRecordConstr
PStakeDatum
( #stakedAmount .= stakeDatum.stakedAmount
.& #owner .= stakeDatum.owner
.& #lockedBy .= expectedLocks
)
pure $ valueCorrect = ownOutputValueUnchanged
foldl1 outputDatumCorrect = stakeOut #== expectedDatum
in foldl1
(#&&) (#&&)
[ ptraceIfFalse "valueCorrect" valueCorrect [ ptraceIfFalse "valueCorrect" valueCorrect
, ptraceIfFalse "datumCorrect" outputDatumCorrect
] ]
pure $ popaque (pconstant ()) pure $ popaque (pconstant ())

View file

@ -2,28 +2,28 @@ name,cpu,mem,size
Agora/Effects/Treasury Withdrawal Effect/effect/Simple,289461528,703055,3191 Agora/Effects/Treasury Withdrawal Effect/effect/Simple,289461528,703055,3191
Agora/Effects/Treasury Withdrawal Effect/effect/Simple with multiple treasuries ,448521458,1070167,3518 Agora/Effects/Treasury Withdrawal Effect/effect/Simple with multiple treasuries ,448521458,1070167,3518
Agora/Effects/Treasury Withdrawal Effect/effect/Mixed Assets,408085321,966048,3383 Agora/Effects/Treasury Withdrawal Effect/effect/Mixed Assets,408085321,966048,3383
Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/governor validator should pass,83758582,229228,7664 Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/governor validator should pass,83758582,229228,7665
Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/effect validator should pass,97345575,266935,3358 Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/effect validator should pass,97345575,266935,3358
Agora/Stake/policy/stakeCreation,43114795,124549,2156 Agora/Stake/policy/stakeCreation,43114795,124549,2156
Agora/Stake/validator/stakeDepositWithdraw deposit,171823342,464745,4144 Agora/Stake/validator/stakeDepositWithdraw deposit,171823342,464745,4189
Agora/Stake/validator/stakeDepositWithdraw withdraw,171823342,464745,4132 Agora/Stake/validator/stakeDepositWithdraw withdraw,171823342,464745,4177
Agora/Proposal/policy/proposalCreation,23140177,69194,1517 Agora/Proposal/policy/proposalCreation,23140177,69194,1518
Agora/Proposal/validator/cosignature/proposal,213692648,591151,5767 Agora/Proposal/validator/cosignature/proposal,204675349,564476,6565
Agora/Proposal/validator/cosignature/stake,115369581,282557,4681 Agora/Proposal/validator/cosignature/stake,114125937,284821,4726
Agora/Proposal/validator/voting/proposal,167847632,446101,5696 Agora/Proposal/validator/voting/proposal,166129664,437310,6494
Agora/Proposal/validator/voting/stake,99545453,256941,4655 Agora/Proposal/validator/voting/stake,107127768,275725,4700
Agora/Proposal/validator/advancing/successfully advance to next state/Draft -> VotringReady,167492034,450393,5594 Agora/Proposal/validator/advancing/successfully advance to next state/Draft -> VotringReady,162018766,433842,6392
Agora/Proposal/validator/advancing/successfully advance to next state/VotingReady -> Locked,166648612,448890,5597 Agora/Proposal/validator/advancing/successfully advance to next state/VotingReady -> Locked,161175344,432339,6395
Agora/Proposal/validator/advancing/successfully advance to next state/Locked -> Finished,168345079,452496,5597 Agora/Proposal/validator/advancing/successfully advance to next state/Locked -> Finished,162871811,435945,6395
Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/Draft -> Finished,166362233,447663,5596 Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/Draft -> Finished,160888965,431112,6394
Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/VotingReady -> Finished,164953322,444958,5597 Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/VotingReady -> Finished,159480054,428407,6395
Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/Locked -> Finished,166084300,447362,5597 Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/Locked -> Finished,160611032,430811,6395
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,21017788,55883,806 Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,21017788,55883,806
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,33204186,88241,900 Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,33204186,88241,900
Agora/Treasury/Validator/Positive/Allows for effect changes,29938856,79744,1390 Agora/Treasury/Validator/Positive/Allows for effect changes,29938856,79744,1390
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,21017788,55883,806 Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,21017788,55883,806
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,33204186,88241,900 Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,33204186,88241,900
Agora/Governor/policy/GST minting,43087287,120125,1829 Agora/Governor/policy/GST minting,43087287,120125,1829
Agora/Governor/validator/proposal creation,262494214,690689,8180 Agora/Governor/validator/proposal creation,261928725,689487,8181
Agora/Governor/validator/GATs minting,349283864,932132,8301 Agora/Governor/validator/GATs minting,352305185,937264,8302
Agora/Governor/validator/mutate governor state,84905433,234687,7765 Agora/Governor/validator/mutate governor state,84905433,234687,7766

1 name cpu mem size
2 Agora/Effects/Treasury Withdrawal Effect/effect/Simple 289461528 703055 3191
3 Agora/Effects/Treasury Withdrawal Effect/effect/Simple with multiple treasuries 448521458 1070167 3518
4 Agora/Effects/Treasury Withdrawal Effect/effect/Mixed Assets 408085321 966048 3383
5 Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/governor validator should pass 83758582 229228 7664 7665
6 Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/effect validator should pass 97345575 266935 3358
7 Agora/Stake/policy/stakeCreation 43114795 124549 2156
8 Agora/Stake/validator/stakeDepositWithdraw deposit 171823342 464745 4144 4189
9 Agora/Stake/validator/stakeDepositWithdraw withdraw 171823342 464745 4132 4177
10 Agora/Proposal/policy/proposalCreation 23140177 69194 1517 1518
11 Agora/Proposal/validator/cosignature/proposal 213692648 204675349 591151 564476 5767 6565
12 Agora/Proposal/validator/cosignature/stake 115369581 114125937 282557 284821 4681 4726
13 Agora/Proposal/validator/voting/proposal 167847632 166129664 446101 437310 5696 6494
14 Agora/Proposal/validator/voting/stake 99545453 107127768 256941 275725 4655 4700
15 Agora/Proposal/validator/advancing/successfully advance to next state/Draft -> VotringReady 167492034 162018766 450393 433842 5594 6392
16 Agora/Proposal/validator/advancing/successfully advance to next state/VotingReady -> Locked 166648612 161175344 448890 432339 5597 6395
17 Agora/Proposal/validator/advancing/successfully advance to next state/Locked -> Finished 168345079 162871811 452496 435945 5597 6395
18 Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/Draft -> Finished 166362233 160888965 447663 431112 5596 6394
19 Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/VotingReady -> Finished 164953322 159480054 444958 428407 5597 6395
20 Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/Locked -> Finished 166084300 160611032 447362 430811 5597 6395
21 Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple 21017788 55883 806
22 Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs 33204186 88241 900
23 Agora/Treasury/Validator/Positive/Allows for effect changes 29938856 79744 1390
24 Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple 21017788 55883 806
25 Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs 33204186 88241 900
26 Agora/Governor/policy/GST minting 43087287 120125 1829
27 Agora/Governor/validator/proposal creation 262494214 261928725 690689 689487 8180 8181
28 Agora/Governor/validator/GATs minting 349283864 352305185 932132 937264 8301 8302
29 Agora/Governor/validator/mutate governor state 84905433 234687 7765 7766