From 01cacc9721eb7aef28c965474b7f3e85f6910add Mon Sep 17 00:00:00 2001 From: Hongrui Fang Date: Thu, 8 Sep 2022 20:13:15 +0800 Subject: [PATCH] standalone stake redeemers --- agora.cabal | 1 + agora/Agora/Stake.hs | 200 +++++++++- agora/Agora/Stake/Redeemers.hs | 303 +++++++++++++++ agora/Agora/Stake/Scripts.hs | 682 +++++++++++++++------------------ bench.csv | 280 +++++++------- 5 files changed, 948 insertions(+), 518 deletions(-) create mode 100644 agora/Agora/Stake/Redeemers.hs diff --git a/agora.cabal b/agora.cabal index db24df0..f5f6c28 100644 --- a/agora.cabal +++ b/agora.cabal @@ -159,6 +159,7 @@ library Agora.SafeMoney Agora.Scripts Agora.Stake + Agora.Stake.Redeemers Agora.Stake.Scripts Agora.Treasury Agora.Utils diff --git a/agora/Agora/Stake.hs b/agora/Agora/Stake.hs index c953e5c..81ddee8 100644 --- a/agora/Agora/Stake.hs +++ b/agora/Agora/Stake.hs @@ -19,6 +19,16 @@ module Agora.Stake ( PProposalLock (..), PStakeRole (..), + -- * Validation context + PStakeInputContext (..), + PStakeOutputContext (..), + PSigContext (..), + PStakeRedeemerContext (..), + PStakeRedeemerHandlerContext (..), + PProposalContext (..), + PStakeRedeemerHandler, + StakeRedeemerImpl (..), + -- * Utility functions pstakeLocked, pnumCreatedProposals, @@ -30,17 +40,22 @@ module Agora.Stake ( pisIrrelevant, ) where -import Agora.Proposal (PProposalId, PResultTag, ProposalId, ResultTag) +import Agora.Proposal (PProposalId, PProposalRedeemer, PResultTag, ProposalId, ResultTag) import Agora.SafeMoney (GTTag) import Data.Tagged (Tagged) import Generics.SOP qualified as SOP -import Plutarch.Api.V1 (PCredential) +import Plutarch.Api.V1 (KeyGuarantees (Sorted), PCredential) +import Plutarch.Api.V1.Value (PValue) import Plutarch.Api.V2 ( + AmountGuarantees (Positive), PMaybeData, + PTxInfo, ) import Plutarch.DataRepr ( DerivePConstantViaData (DerivePConstantViaData), + PDataFields, ) +import Plutarch.Extra.AssetClass (PAssetClass) import Plutarch.Extra.Field (pletAll) import Plutarch.Extra.IsData ( DerivePConstantViaDataList (DerivePConstantViaDataList), @@ -231,6 +246,8 @@ newtype PStakeDatum (s :: S) = PStakeDatum PIsData , -- | @since 0.1.0 PEq + , -- | @since 1.0.0 + PDataFields ) instance DerivePlutusType PStakeDatum where @@ -408,6 +425,185 @@ data PStakeRole (s :: S) instance DerivePlutusType PStakeRole where type DPTStrat _ = PlutusTypeScott +-------------------------------------------------------------------------------- + +{- | Represent the stake being spent. + + @since 1.0.0 +-} +data PStakeInputContext (s :: S) = PStakeInput + { ownInputDatum :: Term s PStakeDatum + -- ^ The stake datum of said stake. + , ownInputValue :: Term s (PValue 'Sorted 'Positive) + -- ^ The value carried by the stake UTxO. + } + deriving stock + ( -- | @since 1.0.0 + Generic + ) + deriving anyclass + ( -- | @since 1.0.0 + PlutusType + ) + +-- | @since 1.0.0 +instance DerivePlutusType PStakeInputContext where + type DPTStrat _ = PlutusTypeScott + +{- | Where the stake will go? + + @since 1.0.0 +-} +data PStakeOutputContext (s :: S) + = -- | The output stake is owned by the stake validator. + PStakeOutput + { ownOutputDatum :: Term s PStakeDatum + -- ^ The stake datum of the output stake. + , ownOutputValue :: Term s (PValue 'Sorted 'Positive) + -- ^ The value carried by the stake output UTxO. + } + | -- | The stake is burnt in the transaction. + PStakeBurnt + deriving stock + ( -- | @since 1.0.0 + Generic + ) + deriving anyclass + ( -- | @since 1.0.0 + PlutusType + ) + +-- | @since 1.0.0 +instance DerivePlutusType PStakeOutputContext where + type DPTStrat _ = PlutusTypeScott + +{- | Who authorizes the transaction? + + @since 1.0.0 +-} +data PSigContext (s :: S) + = -- | The stake owner authorized the transaction. + PSignedByOwner + | -- | The delegate authorized the transaction. + PSignedByDelegate + | -- | Both owner and delegate didn't authorize. + PUnknownSig + deriving stock + ( -- | @since 1.0.0 + Generic + ) + deriving anyclass + ( -- | @since 1.0.0 + PlutusType + ) + +-- | @since 1.0.0 +instance DerivePlutusType PSigContext where + type DPTStrat _ = PlutusTypeScott + +{- | The metadata carried by the stake redeemer. See also 'StakeRedeemer'. + + @since 1.0.0 +-} +data PStakeRedeemerContext (s :: S) + = -- | See also 'DepositWithdraw'. + PDepositWithdrawDelta (Term s (PDiscrete GTTag)) + | -- | See also 'DelegateTo'. + PSetDelegateTo (Term s PCredential) + | PNoMetadata + deriving stock + ( -- | @since 1.0.0 + Generic + ) + deriving anyclass + ( -- | @since 1.0.0 + PlutusType + ) + +-- | @since 1.0.0 +instance DerivePlutusType PStakeRedeemerContext where + type DPTStrat _ = PlutusTypeScott + +{- | The usage of proposal in the transaction. + + @since 1.0.0 +-} +data PProposalContext (s :: S) + = -- | A proposal is spent. + PWithProposalRedeemer (Term s PProposalRedeemer) + | -- | A new proposal is created. + PNewProposal + | -- | No proposal is spent or created. + PNoProposal + deriving stock + ( -- | @since 1.0.0 + Generic + ) + deriving anyclass + ( -- | @since 1.0.0 + PlutusType + ) + +-- | @since 1.0.0 +instance DerivePlutusType PProposalContext where + type DPTStrat _ = PlutusTypeScott + +{- | Context required in order for redeemer handlers to peform validation. + + @1.0.0 +-} +data PStakeRedeemerHandlerContext (s :: S) = PStakeRedeemerHandlerContext + { stakeInput :: Term s PStakeInputContext + , stakeOutput :: Term s PStakeOutputContext + , redeemerContext :: Term s PStakeRedeemerContext + , sigContext :: Term s PSigContext + , proposalContext :: Term s PProposalContext + , gtAssetClass :: Term s PAssetClass + , extraTxContext :: Term s PTxInfo + } + deriving stock + ( -- | @since 1.0.0 + Generic + ) + deriving anyclass + ( -- | @since 1.0.0 + PlutusType + ) + +-- | @since 1.0.0 +instance DerivePlutusType PStakeRedeemerHandlerContext where + type DPTStrat _ = PlutusTypeScott + +{- | The plutarch type signature of the redeemer handlers. + + A redeemer handler is a piece of validation logic that performs a unique + set of checks for its corresponding stake redeemer. + + @since 1.0.0 +-} +type PStakeRedeemerHandler = PStakeRedeemerHandlerContext :--> PUnit + +{- | A collection of stake redeemer handlers for each stake redeemers. + + @since 1.0.0 +-} +data StakeRedeemerImpl = StakeRedeemerImpl + { onDepositWithdraw :: ClosedTerm PStakeRedeemerHandler + -- ^ Handler for 'DepositWithdraw'. + , onDestroy :: ClosedTerm PStakeRedeemerHandler + -- ^ Handler for 'Destroy'. + , onPermitVote :: ClosedTerm PStakeRedeemerHandler + -- ^ Handler for 'permitVotes'. + , onRetractVote :: ClosedTerm PStakeRedeemerHandler + -- ^ Handler for 'RetractVotes'. + , onDelegateTo :: ClosedTerm PStakeRedeemerHandler + -- ^ Handler for 'DelegateTo'. + , onClearDelegate :: ClosedTerm PStakeRedeemerHandler + -- ^ handler for 'ClearDelegate'. + } + +-------------------------------------------------------------------------------- + {- | Retutn true if the stake was used to voted on the proposal. @since 0.2.0 diff --git a/agora/Agora/Stake/Redeemers.hs b/agora/Agora/Stake/Redeemers.hs new file mode 100644 index 0000000..f9ff776 --- /dev/null +++ b/agora/Agora/Stake/Redeemers.hs @@ -0,0 +1,303 @@ +{- | +Module : Agora.Stake.Redeemers +Maintainer : connor@mlabs.city +Description: Default implementation of stake redeemer handlers + +Default implementation of stake redeemer handlers. +-} +module Agora.Stake.Redeemers ( + ppermitVote, + pretractVote, + pdelegateTo, + pclearDelegate, + pdestroy, + pdepositWithdraw, +) where + +import Agora.Proposal (PProposalRedeemer (PUnlock, PVote)) +import Agora.Stake ( + PProposalContext (PNewProposal, PWithProposalRedeemer), + PSigContext (PSignedByOwner, PUnknownSig), + PStakeDatum (PStakeDatum), + PStakeInputContext (PStakeInput), + PStakeOutputContext (PStakeBurnt, PStakeOutput), + PStakeRedeemerContext (PDepositWithdrawDelta, PNoMetadata, PSetDelegateTo), + PStakeRedeemerHandler, + PStakeRedeemerHandlerContext (..), + pstakeLocked, + ) +import Plutarch.Api.V1.Address (PCredential) +import Plutarch.Api.V1.Value (AmountGuarantees (Positive), PValue) +import Plutarch.Api.V2 (PMaybeData) +import Plutarch.Extra.Field (pletAllC) +import Plutarch.Extra.Maybe (pdjust, pdnothing, pmaybeData) +import Plutarch.Extra.Record (mkRecordConstr, (.&), (.=)) +import Plutarch.Extra.TermCont (pguardC, pletC, pmatchC) +import Plutarch.Extra.Value (pgeqByClass, pgeqByClass') +import Plutarch.Numeric.Additive (AdditiveMonoid (zero), AdditiveSemigroup ((+))) +import Plutarch.SafeMoney (pdiscreteValue) +import PlutusLedgerApi.V1.Value (AssetClass (..)) +import Prelude hiding (Num ((+))) + +-- | Return true if stake input and output carries the same value. +pownOutputValueUnchanged :: + forall (s :: S). + Term s (PStakeRedeemerHandlerContext :--> PBool) +pownOutputValueUnchanged = phoistAcyclic $ + plam $ + flip pmatch $ \ctxF -> unTermCont $ do + PStakeInput _ inVal <- pmatchC ctxF.stakeInput + PStakeOutput _ outVal <- pmatchC ctxF.stakeOutput + + pure $ inVal #== outVal + +-- | Return true if only the @lockedBy@ field of the stake datum is updated. +ponlyLocksUpdated :: + forall (s :: S). + Term s (PStakeRedeemerHandlerContext :--> PBool) +ponlyLocksUpdated = phoistAcyclic $ + plam $ + flip pmatch $ \ctxF -> unTermCont $ do + PStakeInput inDat _ <- pmatchC ctxF.stakeInput + PStakeOutput outDat _ <- pmatchC ctxF.stakeOutput + + inDatF <- pletAllC inDat + + let onlyLocksUpdated = + let templateStakeDatum = + mkRecordConstr + PStakeDatum + ( #stakedAmount .= inDatF.stakedAmount + .& #owner .= inDatF.owner + .& #delegatedTo .= inDatF.delegatedTo + .& #lockedBy .= pfield @"lockedBy" # outDat + ) + in outDat #== templateStakeDatum + + pure onlyLocksUpdated + +-- | Return true if the transaction is signed by the owner of the stake. +psignedByOwner :: + forall (s :: S). + Term s (PStakeRedeemerHandlerContext :--> PBool) +psignedByOwner = phoistAcyclic $ + plam $ + flip pmatch $ \ctxF -> pmatch ctxF.sigContext $ \case + PSignedByOwner -> pconstant True + _ -> pconstant False + +-- | Validation logic shared between 'ppermitVote' and 'retractVote'. +pvoteHelper :: + forall (s :: S). + Term + s + ( (PProposalContext :--> PBool) + :--> PStakeRedeemerHandler + ) +pvoteHelper = phoistAcyclic $ + plam $ \valProposalCtx ctx -> unTermCont $ do + ctxF <- pmatchC ctx + + pguardC "Owner or delegate signs this transaction" $ + pmatch ctxF.sigContext $ \case + PUnknownSig -> pconstant False + _ -> pconstant True + + -- This puts trust into the Proposal. The Proposal must necessarily check + -- that this is not abused. + + pguardC "Proposal ST spent" $ + valProposalCtx # ctxF.proposalContext + + pguardC "A UTXO must exist with the correct output" $ + let valueCorrect = pownOutputValueUnchanged # ctx + outputDatumCorrect = ponlyLocksUpdated # ctx + in foldl1 + (#&&) + [ ptraceIfFalse "valueCorrect" valueCorrect + , ptraceIfFalse "datumCorrect" outputDatumCorrect + ] + + pure $ pconstant () + +{- | Default implementation of 'Agora.Stake.PermitVote'. + + @since 1.0.0 +-} +ppermitVote :: forall (s :: S). Term s PStakeRedeemerHandler +ppermitVote = pvoteHelper #$ phoistAcyclic $ + plam $ + flip pmatch $ \case + PWithProposalRedeemer r -> pmatch r $ \case + PVote _ -> pconstant True + _ -> ptrace "Expected Vote" $ pconstant False + PNewProposal -> pconstant True + _ -> pconstant False + +{- | Default implementation of 'Agora.Stake.RetractVotes'. + + @since 1.0.0 +-} +pretractVote :: forall (s :: S). Term s PStakeRedeemerHandler +pretractVote = pvoteHelper #$ phoistAcyclic $ + plam $ + flip pmatch $ \case + PWithProposalRedeemer r -> pmatch r $ \case + PUnlock _ -> pconstant True + _ -> ptrace "Expected Unlock" $ pconstant False + _ -> pconstant False + +-- | Validation logic shared by 'pdelegateTo' and 'pclearDelegate'. +pdelegateHelper :: + forall (s :: S). + Term + s + ( (PStakeRedeemerContext :--> PMaybeData (PAsData PCredential)) + :--> PStakeRedeemerHandler + ) +pdelegateHelper = phoistAcyclic $ + plam $ \f ctx -> unTermCont $ do + ctxF <- pmatchC ctx + + pguardC "Owner signs this transaction" $ psignedByOwner # ctx + + PStakeInput inpDat _ <- pmatchC ctxF.stakeInput + PStakeOutput outDat _ <- pmatchC ctxF.stakeOutput + + inpDatF <- pletAllC inpDat + + let maybePkh = f # ctxF.redeemerContext + + pguardC "Cannot delegate to the owner" $ + pmaybeData + # pcon PTrue + # plam (\pkh -> pnot #$ inpDatF.owner #== pkh) + # maybePkh + + pguardC "A UTXO must exist with the correct output" $ + let correctOutputDatum = + outDat + #== mkRecordConstr + PStakeDatum + ( #stakedAmount .= inpDatF.stakedAmount + .& #owner .= inpDatF.owner + .& #delegatedTo .= pdata maybePkh + .& #lockedBy .= inpDatF.lockedBy + ) + valueCorrect = pownOutputValueUnchanged # ctx + in foldl1 + (#&&) + [ ptraceIfFalse "valueCorrect" valueCorrect + , ptraceIfFalse "datumCorrect" correctOutputDatum + ] + + pure $ pconstant () + +{- | Default implementation of 'Agora.Stake.DelegateTo'. + + @since 1.0.0 +-} +pdelegateTo :: forall (s :: S). Term s PStakeRedeemerHandler +pdelegateTo = pdelegateHelper #$ phoistAcyclic $ + plam $ + flip pmatch $ \case + PSetDelegateTo c -> pdjust # pdata c + _ -> perror + +{- | Default implementation of 'Agora.Stake.ClearDelegate'. + + @since 1.0.0 +-} +pclearDelegate :: forall (s :: S). Term s PStakeRedeemerHandler +pclearDelegate = pdelegateHelper #$ phoistAcyclic $ + plam $ + flip pmatch $ \case + PNoMetadata -> pdnothing + _ -> perror + +{- | Default implementation of 'Agora.Stake.Destroy'. + + @since 1.0.0 +-} +pdestroy :: forall (s :: S). Term s PStakeRedeemerHandler +pdestroy = phoistAcyclic $ + plam $ \ctx -> unTermCont $ do + ctxF <- pmatchC ctx + + PStakeInput inpDat _ <- pmatchC ctxF.stakeInput + PStakeBurnt <- pmatchC ctxF.stakeOutput + + pguardC "Owner signs this transaction" $ + psignedByOwner # ctx + + pguardC "Stake unlocked" $ pnot #$ pstakeLocked # inpDat + + pure $ pconstant () + +{- | Default implementation of 'Agora.Stake.DepositWithdraw'. + + @since 1.0.0 +-} +pdepositWithdraw :: forall (s :: S). Term s PStakeRedeemerHandler +pdepositWithdraw = phoistAcyclic $ + plam $ \ctx -> unTermCont $ do + ctxF <- pmatchC ctx + + PStakeInput inpDat inpVal <- pmatchC ctxF.stakeInput + PStakeOutput outDat outVal <- pmatchC ctxF.stakeOutput + + pguardC "Stake unlocked" $ pnot #$ pstakeLocked # inpDat + + pguardC "Owner signs this transaction" $ psignedByOwner # ctx + + pguardC + "A UTXO must exist with the correct output" + $ unTermCont $ do + inpDatF <- pletAllC inpDat + PDepositWithdrawDelta delta <- pmatchC ctxF.redeemerContext + + let oldStakedAmount = pfromData $ inpDatF.stakedAmount + + newStakedAmount <- pletC $ oldStakedAmount + delta + + pguardC "New staked amount should be greater than or equal to 0" $ + zero #<= newStakedAmount + + let expectedDatum = + mkRecordConstr + PStakeDatum + ( #stakedAmount .= pdata newStakedAmount + .& #owner .= inpDatF.owner + .& #delegatedTo .= inpDatF.delegatedTo + .& #lockedBy .= inpDatF.lockedBy + ) + datumCorrect = outDat #== expectedDatum + + let valueDelta :: Term _ (PValue _ 'Positive) + valueDelta = pdiscreteValue # ctxF.gtAssetClass # delta + + expectedValue = + inpVal <> valueDelta + + gtAssetClassF <- pletAllC ctxF.gtAssetClass + + let valueCorrect = + foldr1 + (#&&) + [ pgeqByClass' (AssetClass ("", "")) + # outVal + # expectedValue + , pgeqByClass + # gtAssetClassF.currencySymbol + # gtAssetClassF.tokenName + # outVal + # expectedValue + ] + -- + pure $ + foldl1 + (#&&) + [ ptraceIfFalse "valueCorrect" valueCorrect + , ptraceIfFalse "datumCorrect" datumCorrect + ] + pure $ pconstant () diff --git a/agora/Agora/Stake/Scripts.hs b/agora/Agora/Stake/Scripts.hs index da8ed3a..11e9db9 100644 --- a/agora/Agora/Stake/Scripts.hs +++ b/agora/Agora/Stake/Scripts.hs @@ -5,28 +5,76 @@ Description: Plutus Scripts for Stakes. Plutus Scripts for Stakes. -} -module Agora.Stake.Scripts (stakePolicy, stakeValidator) where +module Agora.Stake.Scripts ( + stakePolicy, + stakeValidator, + mkStakeValidator, +) where import Agora.Credential (authorizationContext, pauthorizedBy) -import Agora.Proposal (PProposalRedeemer (PUnlock, PVote)) +import Agora.Proposal (PProposalRedeemer) import Agora.SafeMoney (GTTag) -import Agora.Scripts (AgoraScripts, proposalSTAssetClass, stakeSTSymbol) +import Agora.Scripts ( + AgoraScripts, + proposalSTAssetClass, + stakeSTSymbol, + ) import Agora.Stake ( - PStakeDatum (PStakeDatum), - PStakeRedeemer (..), + PProposalContext ( + PNewProposal, + PNoProposal, + PWithProposalRedeemer + ), + PSigContext ( + PSignedByDelegate, + PSignedByOwner, + PUnknownSig + ), + PStakeDatum, + PStakeInputContext (PStakeInput), + PStakeOutputContext (PStakeBurnt, PStakeOutput), + PStakeRedeemer ( + PClearDelegate, + PDelegateTo, + PDepositWithdraw, + PDestroy, + PPermitVote, + PRetractVotes + ), + PStakeRedeemerContext ( + PDepositWithdrawDelta, + PNoMetadata, + PSetDelegateTo + ), + PStakeRedeemerHandlerContext ( + PStakeRedeemerHandlerContext + ), + StakeRedeemerImpl ( + StakeRedeemerImpl, + onClearDelegate, + onDelegateTo, + onDepositWithdraw, + onDestroy, + onPermitVote, + onRetractVote + ), pstakeLocked, ) -import Data.Tagged (Tagged, untag) +import Agora.Stake.Redeemers ( + pclearDelegate, + pdelegateTo, + pdepositWithdraw, + pdestroy, + ppermitVote, + pretractVote, + ) +import Data.Tagged (Tagged (Tagged)) import Plutarch.Api.V1 ( PCredential (PPubKeyCredential, PScriptCredential), PTokenName, - PValue, ) import Plutarch.Api.V1.AssocMap (plookup) import Plutarch.Api.V2 ( - AmountGuarantees (Positive), - KeyGuarantees (Sorted), - PMaybeData, PMintingPolicy, PScriptPurpose (PMinting, PSpending), PTxInInfo, @@ -41,17 +89,14 @@ import Plutarch.Extra.AssetClass ( ) import Plutarch.Extra.Bind (PBind ((#>>=))) import Plutarch.Extra.Field (pletAllC) -import Plutarch.Extra.Functor (PFunctor (pfmap)) import Plutarch.Extra.List (pfirstJust) import Plutarch.Extra.Maybe ( passertPJust, - pdjust, - pdnothing, pjust, + pmaybe, pmaybeData, pnothing, ) -import Plutarch.Extra.Record (mkRecordConstr, (.&), (.=)) import Plutarch.Extra.ScriptContext ( pfindTxInByTxOutRef, pfromOutputDatum, @@ -65,21 +110,14 @@ import Plutarch.Extra.TermCont ( ptryFromC, ) import Plutarch.Extra.Value ( - pgeqByClass', - pgeqBySymbol, psymbolValueOf, ) -import Plutarch.Numeric.Additive ( - AdditiveMonoid (zero), - AdditiveSemigroup ((+)), - ) import Plutarch.SafeMoney ( - pdiscreteValue', pvalueDiscrete', ) import Plutarch.Unsafe (punsafeCoerce) import PlutusLedgerApi.V1.Value (AssetClass (AssetClass)) -import Prelude hiding (Num (..)) +import Prelude hiding (Num ((+))) {- | Policy for Stake state threads. @@ -197,29 +235,246 @@ stakePolicy gtClassRef = -------------------------------------------------------------------------------- -{- | Validation context for stake redeemers that allow only one stake to be - spent in the transaction. +{- | Create a stake validator, given the implementation of stake redeemers. @since 1.0.0 -} -data POnlyOneStakeContext (s :: S) = POnlyOneStakeContext - { ownOutputDatum :: Term s PStakeDatum - , ownOutputValue :: Term s (PValue 'Sorted 'Positive) - , ownOutputValueUnchanged :: Term s PBool - , onlyLocksUpdated :: Term s PBool - } - deriving stock - ( -- | @since 1.0.0 - Generic - ) - deriving anyclass - ( -- | @since 1.0.0 - PlutusType - ) +mkStakeValidator :: + StakeRedeemerImpl -> + AgoraScripts -> + Tagged GTTag AssetClass -> + ClosedTerm PValidator +mkStakeValidator + impl + as + (Tagged (AssetClass (gtSym, gtTn))) = + plam $ \datum redeemer ctx -> unTermCont $ do + gtAssetClass <- pletC $ passetClass # pconstant gtSym # pconstant gtTn --- | @since 1.0.0 -instance DerivePlutusType POnlyOneStakeContext where - type DPTStrat _ = PlutusTypeScott + -------------------------------------------------------------------------- + + ctxF <- pletFieldsC @'["txInfo", "purpose"] ctx + txInfo <- pletC $ pfromData ctxF.txInfo + txInfoF <- + pletFieldsC + @'[ "inputs" + , "referenceInputs" + , "outputs" + , "mint" + , "validRange" + , "signatories" + , "redeemers" + , "datums" + ] + txInfo + + -------------------------------------------------------------------------- + + -- Assemble the stake input context. + + stakeInputDatum <- pfromData . fst <$> ptryFromC datum + stakeInputDatumF <- pletAllC $ pto stakeInputDatum + + PSpending stakeInputRef <- pmatchC $ pfromData ctxF.purpose + + -- The UTxO we are validating, which is also the input stake. + stakeInput <- + pletC $ + pfield @"resolved" + #$ passertPJust # "Malformed script context: own input not found" + #$ pfindTxInByTxOutRef + # (pfield @"_0" # stakeInputRef) + # txInfoF.inputs + + stakeInputF <- pletFieldsC @'["address", "value"] stakeInput + + stakeInputContext <- + pletC $ + pcon $ + PStakeInput + stakeInputDatum + stakeInputF.value + + -------------------------------------------------------------------------- + + -- Assemble the signature context. + + signedBy <- pletC $ pauthorizedBy # authorizationContext txInfoF + + let ownerSignsTransaction = signedBy # stakeInputDatumF.owner + + delegateSignsTransaction = + pmaybeData + # pconstant False + # plam ((signedBy #) . pfromData) + # pfromData stakeInputDatumF.delegatedTo + + sigContext <- + pletC $ + pif ownerSignsTransaction (pcon PSignedByOwner) $ + pif delegateSignsTransaction (pcon PSignedByDelegate) $ + pcon PUnknownSig + + -------------------------------------------------------------------------- + + stCurrencySymbol <- pletC $ pconstant $ stakeSTSymbol as + mintedST <- pletC $ psymbolValueOf # stCurrencySymbol # txInfoF.mint + valueSpent <- pletC $ pvalueSpent # txInfoF.inputs + spentST <- pletC $ psymbolValueOf # stCurrencySymbol #$ valueSpent + + -- The stake validator can only handle one stake in one transaction. + + pguardC "ST at inputs must be 1" $ + spentST #== 1 + + let oneStakeBurnt = + ptraceIfFalse "Exactly one stake st burnt" $ + mintedST #== (-1) + + -------------------------------------------------------------------------- + + -- Assemble the stake output context. + + let -- Look for the output stake. + stakeOutput = + pfirstJust + # plam + ( \output -> unTermCont $ do + outputF <- + pletFieldsC @'["address", "value", "datum"] + output + + let isStakeOutput = + -- The stake should be owned by the stake validator. + outputF.address #== stakeInputF.address + #&& + -- The stake UTxO carries the state thread token. + psymbolValueOf + # stCurrencySymbol + # outputF.value #== 1 + + stakeOutputDatum = + pfromOutputDatum + # outputF.datum + # txInfoF.datums + + context = + pcon $ + PStakeOutput + (pfromData stakeOutputDatum) + outputF.value + + pure $ + pif + isStakeOutput + (pjust # context) + pnothing + ) + # pfromData txInfoF.outputs + + stakeOutputContext <- + pletC $ + pmatch stakeOutput $ \case + -- Stake output found. + PJust stakeOutput' -> stakeOutput' + -- Stake output not found, meaning the input stake should be burnt. + PNothing -> unTermCont $ do + pguardC "One stake should be burnt" oneStakeBurnt + + pure $ pcon PStakeBurnt + + -------------------------------------------------------------------------- + + -- Assemble the proposal context. + + let AssetClass (propCs, propTn) = proposalSTAssetClass as + + proposalSTClass <- + pletC $ + passetClass + # pconstant propCs + # pconstant propTn + + let pstMinted = + passetClassValueOf # txInfoF.mint # proposalSTClass #== 1 + + proposalContext <- + pletC $ + let convertRedeemer = plam $ \(pto -> dt) -> + ptryFrom @PProposalRedeemer dt fst + + findRedeemer = plam $ \ref -> + plookup + # pcon + ( PSpending $ + pdcons @_0 + # pdata ref + # pdnil + ) + # txInfoF.redeemers + + f :: Term _ (PTxInInfo :--> PMaybe PTxOutRef) + f = plam $ \inInfo -> + let value = pfield @"value" #$ pfield @"resolved" # inInfo + ref = pfield @"outRef" # inInfo + in pif + (passetClassValueOf # value # proposalSTClass #== 1) + (pjust # ref) + pnothing + + proposalRef = pfirstJust # f # txInfoF.inputs + in pif pstMinted (pcon PNewProposal) $ + pmaybe + # pcon PNoProposal + # plam + ( \((convertRedeemer #) -> proposalRedeemer) -> + pcon $ PWithProposalRedeemer proposalRedeemer + ) + #$ proposalRef #>>= findRedeemer + + -------------------------------------------------------------------------- + + -- Assemeble the redeemer handler context. + + mkRedeemerhandlerContext <- pletC $ + plam $ \redeemerContext -> + pcon $ + PStakeRedeemerHandlerContext + stakeInputContext + stakeOutputContext + redeemerContext + sigContext + proposalContext + gtAssetClass + txInfo + + noMetadataContext <- + pletC $ + mkRedeemerhandlerContext + #$ pcon + $ PNoMetadata + + -------------------------------------------------------------------------- + + -- Call the redeemer handler. + + stakeRedeemer :: Term _ PStakeRedeemer <- fst <$> ptryFromC redeemer + + pure $ + popaque $ + pmatch stakeRedeemer $ \case + PDestroy _ -> onDestroy impl # noMetadataContext + PPermitVote _ -> onPermitVote impl # noMetadataContext + PRetractVotes _ -> onRetractVote impl # noMetadataContext + PClearDelegate _ -> onClearDelegate impl # noMetadataContext + PDelegateTo ((pfield @"pkh" #) -> pkh) -> + onDelegateTo impl #$ mkRedeemerhandlerContext + #$ pcon + $ PSetDelegateTo pkh + PDepositWithdraw ((pfield @"delta" #) -> delta) -> + onDepositWithdraw impl #$ mkRedeemerhandlerContext + #$ pcon + $ PDepositWithdrawDelta delta {- | Validator intended for Stake UTXOs to be locked by. @@ -261,16 +516,6 @@ instance DerivePlutusType POnlyOneStakeContext where - The stake must not be locked. - Tx must be signed by the owner. - === 'WitnessStake' - - Allow this Stake to be included in a transaction without making - any changes to it. In the future, - this could use [CIP-31](https://cips.cardano.org/cips/cip31/) instead. - - - Tx must be signed by the owner __or__ a proposal ST token must be spent - alongside the stake. - - The datum and value must remain unchanged. - @since 0.1.0 -} stakeValidator :: @@ -279,328 +524,13 @@ stakeValidator :: -- | See 'Agora.Governor.Governor.gtClassRef'. Tagged GTTag AssetClass -> ClosedTerm PValidator -stakeValidator as gtClassRef = - plam $ \datum redeemer ctx' -> unTermCont $ do - ctx <- pletFieldsC @'["txInfo", "purpose"] ctx' - txInfo <- pletC $ pfromData ctx.txInfo - txInfoF <- - pletFieldsC - @'[ "mint" - , "inputs" - , "outputs" - , "signatories" - , "datums" - , "redeemers" - ] - txInfo - - stakeRedeemer <- fst <$> ptryFromC redeemer - - stakeDatum' <- pfromData . fst <$> ptryFromC datum - stakeDatum <- pletAllC $ pto stakeDatum' - - PSpending txOutRef <- pmatchC $ pfromData ctx.purpose - - PJust ((pfield @"resolved" #) -> resolved) <- - pmatchC $ - pfindTxInByTxOutRef - # (pfield @"_0" # txOutRef) - # txInfoF.inputs - resolvedF <- pletFieldsC @'["address", "value", "datumHash"] resolved - - -- Whether the owner signs this transaction or not. - signedBy <- pletC $ pauthorizedBy # authorizationContext txInfoF - - ownerSignsTransaction <- pletC $ signedBy # stakeDatum.owner - - delegateSignsTransaction <- - pletC $ - pmaybeData - # pconstant False - # plam ((signedBy #) . pfromData) - # pfromData stakeDatum.delegatedTo - - stCurrencySymbol <- pletC $ pconstant $ stakeSTSymbol as - mintedST <- pletC $ psymbolValueOf # stCurrencySymbol # txInfoF.mint - valueSpent <- pletC $ pvalueSpent # txInfoF.inputs - spentST <- pletC $ psymbolValueOf # stCurrencySymbol #$ valueSpent - - -- Is the stake currently locked? - stakeIsLocked <- pletC $ pstakeLocked # stakeDatum' - - pure $ - pmatch stakeRedeemer $ \case - PDestroy _ -> unTermCont $ do - pguardC "ST at inputs must be 1" $ - spentST #== 1 - - pguardC "Should burn ST" $ - mintedST #== -1 - - pguardC "Stake unlocked" $ pnot # stakeIsLocked - - pguardC "Owner signs this transaction" ownerSignsTransaction - - pure $ popaque (pconstant ()) - ------------------------------------------------------------------------ - -- Handle redeemers that require own stake output. - - _ -> unTermCont $ do - let AssetClass (propCs, propTn) = proposalSTAssetClass as - proposalSTClass = passetClass # pconstant propCs # pconstant propTn - - proposalRedeemer <- - pletC $ - let convertRedeemer = plam $ \(pto -> dt) -> - ptryFrom @PProposalRedeemer dt fst - - findRedeemer = plam $ \ref -> - plookup - # pcon - ( PSpending $ - pdcons @_0 - # pdata ref - # pdnil - ) - # txInfoF.redeemers - - f :: Term _ (PTxInInfo :--> PMaybe PTxOutRef) - f = plam $ \inInfo -> - let value = pfield @"value" #$ pfield @"resolved" # inInfo - ref = pfield @"outRef" # inInfo - in pif - (passetClassValueOf # value # proposalSTClass #== 1) - (pjust # ref) - pnothing - - proposalRef = pfirstJust # f # txInfoF.inputs - in pfmap # convertRedeemer #$ proposalRef #>>= findRedeemer - - -- Filter out own outputs using own address and ST. - ownOutputs <- - pletC $ - pfilter - # plam - ( \output -> unTermCont $ do - outputF <- pletFieldsC @'["address", "value"] output - - pure $ - outputF.address #== resolvedF.address - #&& psymbolValueOf # stCurrencySymbol # outputF.value #== 1 - ) - # pfromData txInfoF.outputs - - ---------------------------------------------------------------------- - - withSingleStake' :: - Term - s - ( (POnlyOneStakeContext :--> PUnit) - :--> POpaque - ) <- - pletC $ - plam $ \validationLogic -> unTermCont $ do - pguardC "ST at inputs must be 1" $ - spentST #== 1 - - ownOutput <- pletC $ phead # ownOutputs - - let ownOutputDatum = - pfromData $ - pfromOutputDatum @(PAsData PStakeDatum) - # (pfield @"datum" # ownOutput) - # txInfoF.datums - - ownOutputValue = - pfield @"value" # ownOutput - - ownOutputValueUnchanged = - pdata resolvedF.value #== pdata ownOutputValue - - onlyLocksUpdated = - let templateStakeDatum = - mkRecordConstr - PStakeDatum - ( #stakedAmount .= stakeDatum.stakedAmount - .& #owner .= stakeDatum.owner - .& #delegatedTo .= stakeDatum.delegatedTo - .& #lockedBy .= pfield @"lockedBy" - # pto ownOutputDatum - ) - in ownOutputDatum #== templateStakeDatum - - ctx = - pcon $ - POnlyOneStakeContext - ownOutputDatum - ownOutputValue - ownOutputValueUnchanged - onlyLocksUpdated - - pure $ popaque $ validationLogic # ctx - - let withSingleStake val = withSingleStake' #$ plam $ \ctx -> - unTermCont $ do - ctxF <- pmatchC ctx - val ctxF - pure $ pconstant () - - setDelegate :: Term s (PMaybeData (PAsData PCredential) :--> POpaque) <- - pletC $ - plam $ \maybePkh -> withSingleStake $ \ctx -> do - pguardC - "Owner signs this transaction" - ownerSignsTransaction - - pguardC "Cannot delegate to the owner" $ - pmaybeData - # pcon PTrue - # plam (\pkh -> pnot #$ stakeDatum.owner #== pkh) - # maybePkh - - pguardC "A UTXO must exist with the correct output" $ - let correctOutputDatum = - ctx.ownOutputDatum - #== mkRecordConstr - PStakeDatum - ( #stakedAmount .= stakeDatum.stakedAmount - .& #owner .= stakeDatum.owner - .& #delegatedTo .= pdata maybePkh - .& #lockedBy .= stakeDatum.lockedBy - ) - valueCorrect = ctx.ownOutputValueUnchanged - in foldl1 - (#&&) - [ ptraceIfFalse "valueCorrect" valueCorrect - , ptraceIfFalse "datumCorrect" correctOutputDatum - ] - - pure $ - pmatch stakeRedeemer $ \case - PRetractVotes _ -> withSingleStake $ \ctx -> do - pguardC - "Owner or delegate signs this transaction" - $ ownerSignsTransaction #|| delegateSignsTransaction - - -- This puts trust into the Proposal. The Proposal must necessarily check - -- that this is not abused. - - pguardC "Proposal ST spent" $ - pmatch proposalRedeemer $ \case - PJust redeemer -> pmatch redeemer $ \case - PUnlock _ -> pconstant True - _ -> - ptrace "Expected PUnlock, but got other" $ - pconstant False - PNothing -> - ptrace "Proposal redeemer not found" $ - pconstant False - - pguardC "A UTXO must exist with the correct output" $ - let valueCorrect = ctx.ownOutputValueUnchanged - outputDatumCorrect = ctx.onlyLocksUpdated - in foldl1 - (#&&) - [ ptraceIfFalse "valueCorrect" valueCorrect - , ptraceIfFalse "datumCorrect" outputDatumCorrect - ] - - ------------------------------------------------------------------ - - PPermitVote _ -> withSingleStake $ \ctx -> do - pguardC - "Owner or delegate signs this transaction" - $ ownerSignsTransaction #|| delegateSignsTransaction - - let proposalTokenMinted = - passetClassValueOf # txInfoF.mint # proposalSTClass #== 1 - - -- This puts trust into the Proposal. The Proposal must necessarily check - -- that this is not abused. - pguardC "Proposal ST spent or minted" $ - pmatch - proposalRedeemer - ( \case - PJust proposalRedeemer' -> - pmatch proposalRedeemer' $ \case - PVote _ -> pconstant True - _ -> ptrace "Expected PVote" $ pconstant False - _ -> proposalTokenMinted - ) - - pguardC "A UTXO must exist with the correct output" $ - let correctOutputDatum = ctx.onlyLocksUpdated - valueCorrect = ctx.ownOutputValueUnchanged - in foldl1 - (#&&) - [ ptraceIfFalse "valueCorrect" valueCorrect - , ptraceIfFalse "datumCorrect" correctOutputDatum - ] - - ------------------------------------------------------------------ - - PDelegateTo ((pfromData . (pfield @"pkh" #)) -> pkh) -> - setDelegate #$ pdjust # pdata pkh - ------------------------------------------------------------------ - - PClearDelegate _ -> - setDelegate # pdnothing - ------------------------------------------------------------------ - - PDepositWithdraw r -> withSingleStake $ \ctx -> do - pguardC "Stake unlocked" $ - pnot #$ stakeIsLocked - pguardC - "Owner signs this transaction" - ownerSignsTransaction - pguardC "A UTXO must exist with the correct output" $ - unTermCont $ do - let oldStakedAmount = pfromData $ stakeDatum.stakedAmount - delta = pfromData $ pfield @"delta" # r - - newStakedAmount <- pletC $ oldStakedAmount + delta - - pguardC "New staked amount should be greater than or equal to 0" $ - zero #<= newStakedAmount - - let expectedDatum = - mkRecordConstr - PStakeDatum - ( #stakedAmount .= pdata newStakedAmount - .& #owner .= stakeDatum.owner - .& #delegatedTo .= stakeDatum.delegatedTo - .& #lockedBy .= stakeDatum.lockedBy - ) - datumCorrect = ctx.ownOutputDatum #== expectedDatum - - let valueDelta :: Term _ (PValue _ 'Positive) - valueDelta = pdiscreteValue' gtClassRef # delta - - expectedValue = - resolvedF.value <> valueDelta - - valueCorrect = - foldr1 - (#&&) - [ pgeqByClass' (AssetClass ("", "")) - # ctx.ownOutputValue - # expectedValue - , pgeqByClass' (untag gtClassRef) - # ctx.ownOutputValue - # expectedValue - , pgeqBySymbol - # stCurrencySymbol - # ctx.ownOutputValue - # expectedValue - ] - -- - pure $ - foldl1 - (#&&) - [ ptraceIfFalse "valueCorrect" valueCorrect - , ptraceIfFalse "datumCorrect" datumCorrect - ] - - ------------------------------------------------------------------ - - _ -> ptraceError "unreachable" +stakeValidator = + mkStakeValidator $ + StakeRedeemerImpl + { onDepositWithdraw = pdepositWithdraw + , onDestroy = pdestroy + , onPermitVote = ppermitVote + , onRetractVote = pretractVote + , onDelegateTo = pdelegateTo + , onClearDelegate = pclearDelegate + } diff --git a/bench.csv b/bench.csv index e40ca75..1c8de04 100644 --- a/bench.csv +++ b/bench.csv @@ -5,40 +5,40 @@ Agora/Effects/Treasury Withdrawal Effect/effect/Mixed Assets,565354560,1442811,4 Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/governor validator should pass,124963454,341885,9954 Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/effect validator should pass,171725573,451393,4885 Agora/Stake/policy/stakeCreation,56178945,162035,3199 -Agora/Stake/validator/stakeDepositWithdraw deposit,242089408,642275,6224 -Agora/Stake/validator/stakeDepositWithdraw withdraw,242089408,642275,6212 -Agora/Stake/validator/set delegate/override existing delegate,166352470,438155,6255 -Agora/Stake/validator/set delegate/remove existing delegate,154745748,407927,6185 -Agora/Stake/validator/set delegate/set delegate to something,159105650,420263,6185 +Agora/Stake/validator/stakeDepositWithdraw deposit,177106262,481354,6222 +Agora/Stake/validator/stakeDepositWithdraw withdraw,177106262,481354,6210 +Agora/Stake/validator/set delegate/override existing delegate,121372846,332535,6253 +Agora/Stake/validator/set delegate/remove existing delegate,113937150,314373,6183 +Agora/Stake/validator/set delegate/set delegate to something,118943858,325435,6183 Agora/Proposal/policy (proposal creation)/legal/proposal,34052826,101718,2022 Agora/Proposal/policy (proposal creation)/legal/governor,360154125,923395,10395 -Agora/Proposal/policy (proposal creation)/legal/stake,230569597,600796,6883 +Agora/Proposal/policy (proposal creation)/legal/stake,147334017,389666,6881 Agora/Proposal/policy (proposal creation)/illegal/invalid next proposal id/proposal,34052826,101718,2022 -Agora/Proposal/policy (proposal creation)/illegal/invalid next proposal id/stake,230569597,600796,6883 +Agora/Proposal/policy (proposal creation)/illegal/invalid next proposal id/stake,147334017,389666,6881 Agora/Proposal/policy (proposal creation)/illegal/use other's stake/proposal,34052826,101718,1991 Agora/Proposal/policy (proposal creation)/illegal/use other's stake/governor,360154125,923395,10364 Agora/Proposal/policy (proposal creation)/illegal/altered stake/proposal,34052826,101718,2022 Agora/Proposal/policy (proposal creation)/illegal/invalid stake locks/proposal,34052826,101718,2030 -Agora/Proposal/policy (proposal creation)/illegal/invalid stake locks/stake,246392309,644746,6891 +Agora/Proposal/policy (proposal creation)/illegal/invalid stake locks/stake,152767677,404316,6889 Agora/Proposal/policy (proposal creation)/illegal/has reached maximum proposals limit/proposal,34052826,101718,2042 -Agora/Proposal/policy (proposal creation)/illegal/has reached maximum proposals limit/stake,255739921,673228,6913 +Agora/Proposal/policy (proposal creation)/illegal/has reached maximum proposals limit/stake,160032453,425882,6911 Agora/Proposal/policy (proposal creation)/illegal/loose time range/proposal,34052826,101718,2022 -Agora/Proposal/policy (proposal creation)/illegal/loose time range/stake,230569597,600796,6883 +Agora/Proposal/policy (proposal creation)/illegal/loose time range/stake,147334017,389666,6881 Agora/Proposal/policy (proposal creation)/illegal/open time range/proposal,34052826,101718,2018 -Agora/Proposal/policy (proposal creation)/illegal/open time range/stake,230569597,600796,6879 +Agora/Proposal/policy (proposal creation)/illegal/open time range/stake,147334017,389666,6877 Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/VotingReady/proposal,34052826,101718,2022 -Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/VotingReady/stake,230569597,600796,6883 +Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/VotingReady/stake,147334017,389666,6881 Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Locked/proposal,34052826,101718,2022 -Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Locked/stake,230569597,600796,6883 +Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Locked/stake,147334017,389666,6881 Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Finished/proposal,34052826,101718,2022 -Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Finished/stake,230569597,600796,6883 +Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Finished/stake,147334017,389666,6881 Agora/Proposal/validator/cosignature/legal/with 1 cosigners: proposal,215428122,594883,10482 Agora/Proposal/validator/cosignature/legal/with 5 cosigners: proposal,522188507,1462750,11847 Agora/Proposal/validator/cosignature/legal/with 10 cosigners: proposal,1019709027,2836183,13554 Agora/Proposal/validator/voting/legal/ordinary/proposal,275112748,731990,10840 -Agora/Proposal/validator/voting/legal/ordinary/stake,228856245,593720,6730 +Agora/Proposal/validator/voting/legal/ordinary/stake,161146408,428644,6728 Agora/Proposal/validator/voting/legal/delegate/proposal,280121756,746190,10910 -Agora/Proposal/validator/voting/legal/delegate/stake,243390029,632912,6835 +Agora/Proposal/validator/voting/legal/delegate/stake,172492573,458367,6833 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to next state/from Draft to VotingReady/proposal,248851428,686062,11291 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to next state/from VotingReady to Locked/proposal,225653650,614614,11054 Agora/Proposal/validator/advancing/with 1 cosigners and 1 effects/legal/to next state/from Locked to Finished/proposal,238191563,636388,12313 @@ -318,158 +318,158 @@ Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/wrong Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/wrong GAT datum/authority,78441861,206056,5215 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/invalid governor output datum/proposal,589961641,1580844,15318 Agora/Proposal/validator/advancing/with 10 cosigners and 5 effects/illegal/invalid governor output datum/authority,78441861,206056,5221 -Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: retract votes while voting/stake,188554311,478412,6708 +Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: retract votes while voting/stake,145781968,384448,6706 Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: retract votes while voting/proposal,259046956,685287,10819 -Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: retract votes while voting/stake,201139473,514628,6724 +Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: retract votes while voting/stake,152131186,402556,6722 Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: retract votes while voting/proposal,276546528,734728,10830 -Agora/Proposal/validator/unlocking/legal/with 1 proposals/creator: remove creator locks when finished/stake,187516034,475614,6706 +Agora/Proposal/validator/unlocking/legal/with 1 proposals/creator: remove creator locks when finished/stake,144743691,381650,6704 Agora/Proposal/validator/unlocking/legal/with 1 proposals/creator: remove creator locks when finished/proposal,244224995,653258,10816 -Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: remove all locks when finished/stake,191672283,487466,6722 +Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: remove all locks when finished/stake,148899940,393502,6720 Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: remove all locks when finished/proposal,252462464,677766,10828 -Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: unlock after voting/Locked/stake,188554311,478412,6712 +Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: unlock after voting/Locked/stake,145781968,384448,6710 Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: unlock after voting/Locked/proposal,240152103,645033,10823 -Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: unlock after voting/Finished/stake,188554311,478412,6712 +Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: unlock after voting/Finished/stake,145781968,384448,6710 Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: unlock after voting/Finished/proposal,240717592,646235,10823 -Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: remove vote locks when locked/stake,201139473,514628,6728 +Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: remove vote locks when locked/stake,152131186,402556,6726 Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: remove vote locks when locked/proposal,258217164,695676,10834 -Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: retract votes while voting/stake,373467087,900120,9009 +Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: retract votes while voting/stake,292486160,747404,9007 Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: retract votes while voting/proposal,389806176,988063,13096 -Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: retract votes while voting/stake,436392897,1081200,9085 +Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: retract votes while voting/stake,324232250,837944,9083 Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: retract votes while voting/proposal,459362076,1188816,13147 -Agora/Proposal/validator/unlocking/legal/with 5 proposals/creator: remove creator locks when finished/stake,368275702,886130,8999 +Agora/Proposal/validator/unlocking/legal/with 5 proposals/creator: remove creator locks when finished/stake,287294775,733414,8997 Agora/Proposal/validator/unlocking/legal/with 5 proposals/creator: remove creator locks when finished/proposal,361007779,929122,13089 -Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: remove all locks when finished/stake,389056947,945390,9079 +Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: remove all locks when finished/stake,308076020,792674,9077 Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: remove all locks when finished/proposal,399199972,1040478,13141 -Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: unlock after voting/Locked/stake,373467087,900120,9029 +Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: unlock after voting/Locked/stake,292486160,747404,9027 Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: unlock after voting/Locked/proposal,361087995,932089,13116 -Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: unlock after voting/Finished/stake,373467087,900120,9029 +Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: unlock after voting/Finished/stake,292486160,747404,9027 Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: unlock after voting/Finished/proposal,361653484,933291,13116 -Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: remove vote locks when locked/stake,436392897,1081200,9105 +Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: remove vote locks when locked/stake,324232250,837944,9103 Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: remove vote locks when locked/proposal,431209384,1134044,13167 -Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: retract votes while voting/stake,604608057,1427255,11885 +Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: retract votes while voting/stake,475866400,1201099,11883 Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: retract votes while voting/proposal,553255201,1366533,15942 -Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: retract votes while voting/stake,730459677,1789415,12036 +Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: retract votes while voting/stake,539358580,1382179,12034 Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: retract votes while voting/proposal,687881511,1756426,16043 -Agora/Proposal/validator/unlocking/legal/with 10 proposals/creator: remove creator locks when finished/stake,594225287,1399275,11865 +Agora/Proposal/validator/unlocking/legal/with 10 proposals/creator: remove creator locks when finished/stake,465483630,1173119,11863 Agora/Proposal/validator/unlocking/legal/with 10 proposals/creator: remove creator locks when finished/proposal,506986259,1273952,15930 -Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: remove all locks when finished/stake,635787777,1517795,12025 +Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: remove all locks when finished/stake,507046120,1291639,12023 Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: remove all locks when finished/proposal,582621857,1493868,16032 -Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: unlock after voting/Locked/stake,604608057,1427255,11925 +Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: unlock after voting/Locked/stake,475866400,1201099,11923 Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: unlock after voting/Locked/proposal,512257860,1290909,15982 -Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: unlock after voting/Finished/stake,604608057,1427255,11925 +Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: unlock after voting/Finished/stake,475866400,1201099,11923 Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: unlock after voting/Finished/proposal,512823349,1292111,15982 -Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: remove vote locks when locked/stake,730459677,1789415,12076 +Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: remove vote locks when locked/stake,539358580,1382179,12074 Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: remove vote locks when locked/proposal,647449659,1682004,16083 -Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: retract votes while voting/stake,2083910265,4800919,30407 +Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: retract votes while voting/stake,1649499936,4104747,30405 Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: retract votes while voting/proposal,1599328961,3788741,34253 -Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: retract votes while voting/stake,2612487069,6321991,31095 +Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: retract votes while voting/stake,1916167092,4865283,31093 Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: retract votes while voting/proposal,2150405895,5389130,34712 -Agora/Proposal/validator/unlocking/legal/with 42 proposals/creator: remove creator locks when finished/stake,2040302631,4683403,30323 +Agora/Proposal/validator/unlocking/legal/with 42 proposals/creator: remove creator locks when finished/stake,1605892302,3987231,30321 Agora/Proposal/validator/unlocking/legal/with 42 proposals/creator: remove creator locks when finished/proposal,1441248531,3480864,34209 -Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: remove all locks when finished/stake,2214865089,5181187,31034 +Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: remove all locks when finished/stake,1780454760,4485015,31032 Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: remove all locks when finished/proposal,1756521921,4395564,34651 -Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: unlock after voting/Locked/stake,2083910265,4800919,30576 +Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: unlock after voting/Locked/stake,1649499936,4104747,30574 Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: unlock after voting/Locked/proposal,1479744996,3587357,34422 -Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: unlock after voting/Finished/stake,2083910265,4800919,30576 +Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: unlock after voting/Finished/stake,1649499936,4104747,30574 Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: unlock after voting/Finished/proposal,1480310485,3588559,34422 -Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: remove vote locks when locked/stake,2612487069,6321991,31264 +Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: remove vote locks when locked/stake,1916167092,4865283,31262 Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: remove vote locks when locked/proposal,2031387419,5188948,34881 -"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Voter , status: Draft/stake",188554311,478412,6708 -"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Voter , status: Locked/stake",188554311,478412,6708 -"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Voter , status: Finished/stake",188554311,478412,6708 -"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Creator , status: Draft/stake",182248232,479196,6710 -"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Creator , status: Locked/stake",182248232,479196,6710 -"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Creator , status: Finished/stake",182248232,479196,6710 -"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Both , status: Draft/stake",201139473,514628,6724 -"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Both , status: Locked/stake",201139473,514628,6724 -"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Both , status: Finished/stake",201139473,514628,6724 -"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Irrelevant , status: Draft/stake",169663070,442980,6690 -"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Irrelevant , status: Locked/stake",169663070,442980,6690 -"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Irrelevant , status: Finished/stake",169663070,442980,6690 -Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Draft retract votes: True/stake,169663070,442980,6690 -Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Draft retract votes: False/stake,169663070,442980,6690 -Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: VotingReady retract votes: True/stake,169663070,442980,6690 -Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: VotingReady retract votes: False/stake,169663070,442980,6690 -Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Locked retract votes: True/stake,169663070,442980,6690 -Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Locked retract votes: False/stake,169663070,442980,6690 -Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Finished retract votes: True/stake,169663070,442980,6690 -Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Finished retract votes: False/stake,169663070,442980,6690 -Agora/Proposal/validator/unlocking/illegal/with 1 proposals/remove creator too early/status: Draft/stake,187516034,475614,6706 -Agora/Proposal/validator/unlocking/illegal/with 1 proposals/remove creator too early/status: VotingReady/stake,187516034,475614,6706 -Agora/Proposal/validator/unlocking/illegal/with 1 proposals/remove creator too early/status: Locked/stake,187516034,475614,6706 -Agora/Proposal/validator/unlocking/illegal/with 1 proposals/creator: retract votes/stake,187516034,475614,6704 -"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Voter , status: Draft/stake",373467087,900120,9009 -"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Voter , status: Locked/stake",373467087,900120,9009 -"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Voter , status: Finished/stake",373467087,900120,9009 -"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Creator , status: Draft/stake",371406676,951200,9015 -"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Creator , status: Locked/stake",371406676,951200,9015 -"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Creator , status: Finished/stake",371406676,951200,9015 -"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Both , status: Draft/stake",436392897,1081200,9085 -"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Both , status: Locked/stake",436392897,1081200,9085 -"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Both , status: Finished/stake",436392897,1081200,9085 -"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Irrelevant , status: Draft/stake",308480866,770120,8926 -"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Irrelevant , status: Locked/stake",308480866,770120,8926 -"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Irrelevant , status: Finished/stake",308480866,770120,8926 -Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Draft retract votes: True/stake,308480866,770120,8926 -Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Draft retract votes: False/stake,308480866,770120,8926 -Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: VotingReady retract votes: True/stake,308480866,770120,8926 -Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: VotingReady retract votes: False/stake,308480866,770120,8926 -Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Locked retract votes: True/stake,308480866,770120,8926 -Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Locked retract votes: False/stake,308480866,770120,8926 -Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Finished retract votes: True/stake,308480866,770120,8926 -Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Finished retract votes: False/stake,308480866,770120,8926 -Agora/Proposal/validator/unlocking/illegal/with 5 proposals/remove creator too early/status: Draft/stake,368275702,886130,8999 -Agora/Proposal/validator/unlocking/illegal/with 5 proposals/remove creator too early/status: VotingReady/stake,368275702,886130,8999 -Agora/Proposal/validator/unlocking/illegal/with 5 proposals/remove creator too early/status: Locked/stake,368275702,886130,8999 -Agora/Proposal/validator/unlocking/illegal/with 5 proposals/creator: retract votes/stake,368275702,886130,8988 -"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Voter , status: Draft/stake",604608057,1427255,11885 -"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Voter , status: Locked/stake",604608057,1427255,11885 -"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Voter , status: Finished/stake",604608057,1427255,11885 -"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Creator , status: Draft/stake",607854731,1541205,11896 -"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Creator , status: Locked/stake",607854731,1541205,11896 -"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Creator , status: Finished/stake",607854731,1541205,11896 -"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Both , status: Draft/stake",730459677,1789415,12036 -"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Both , status: Locked/stake",730459677,1789415,12036 -"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Both , status: Finished/stake",730459677,1789415,12036 -"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Irrelevant , status: Draft/stake",482003111,1179045,11722 -"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Irrelevant , status: Locked/stake",482003111,1179045,11722 -"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Irrelevant , status: Finished/stake",482003111,1179045,11722 -Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Draft retract votes: True/stake,482003111,1179045,11722 -Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Draft retract votes: False/stake,482003111,1179045,11722 -Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: VotingReady retract votes: True/stake,482003111,1179045,11722 -Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: VotingReady retract votes: False/stake,482003111,1179045,11722 -Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Locked retract votes: True/stake,482003111,1179045,11722 -Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Locked retract votes: False/stake,482003111,1179045,11722 -Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Finished retract votes: True/stake,482003111,1179045,11722 -Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Finished retract votes: False/stake,482003111,1179045,11722 -Agora/Proposal/validator/unlocking/illegal/with 10 proposals/remove creator too early/status: Draft/stake,594225287,1399275,11865 -Agora/Proposal/validator/unlocking/illegal/with 10 proposals/remove creator too early/status: VotingReady/stake,594225287,1399275,11865 -Agora/Proposal/validator/unlocking/illegal/with 10 proposals/remove creator too early/status: Locked/stake,594225287,1399275,11865 -Agora/Proposal/validator/unlocking/illegal/with 10 proposals/creator: retract votes/stake,594225287,1399275,11845 -"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Voter , status: Draft/stake",2083910265,4800919,30407 -"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Voter , status: Locked/stake",2083910265,4800919,30407 -"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Voter , status: Finished/stake",2083910265,4800919,30407 -"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Creator , status: Draft/stake",2121122283,5317237,30469 -"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Creator , status: Locked/stake",2121122283,5317237,30469 -"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Creator , status: Finished/stake",2121122283,5317237,30469 -"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Both , status: Draft/stake",2612487069,6321991,31095 -"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Both , status: Locked/stake",2612487069,6321991,31095 -"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Both , status: Finished/stake",2612487069,6321991,31095 -"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Irrelevant , status: Draft/stake",1592545479,3796165,29694 -"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Irrelevant , status: Locked/stake",1592545479,3796165,29694 -"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Irrelevant , status: Finished/stake",1592545479,3796165,29694 -Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Draft retract votes: True/stake,1592545479,3796165,29694 -Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Draft retract votes: False/stake,1592545479,3796165,29694 -Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: VotingReady retract votes: True/stake,1592545479,3796165,29694 -Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: VotingReady retract votes: False/stake,1592545479,3796165,29694 -Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Locked retract votes: True/stake,1592545479,3796165,29694 -Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Locked retract votes: False/stake,1592545479,3796165,29694 -Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Finished retract votes: True/stake,1592545479,3796165,29694 -Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Finished retract votes: False/stake,1592545479,3796165,29694 -Agora/Proposal/validator/unlocking/illegal/with 42 proposals/remove creator too early/status: Draft/stake,2040302631,4683403,30323 -Agora/Proposal/validator/unlocking/illegal/with 42 proposals/remove creator too early/status: VotingReady/stake,2040302631,4683403,30323 -Agora/Proposal/validator/unlocking/illegal/with 42 proposals/remove creator too early/status: Locked/stake,2040302631,4683403,30323 -Agora/Proposal/validator/unlocking/illegal/with 42 proposals/creator: retract votes/stake,2040302631,4683403,30239 +"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Voter , status: Draft/stake",145781968,384448,6706 +"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Voter , status: Locked/stake",145781968,384448,6706 +"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Voter , status: Finished/stake",145781968,384448,6706 +"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Creator , status: Draft/stake",143063273,382844,6708 +"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Creator , status: Locked/stake",143063273,382844,6708 +"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Creator , status: Finished/stake",143063273,382844,6708 +"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Both , status: Draft/stake",152131186,402556,6722 +"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Both , status: Locked/stake",152131186,402556,6722 +"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Both , status: Finished/stake",152131186,402556,6722 +"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Irrelevant , status: Draft/stake",136714055,364736,6688 +"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Irrelevant , status: Locked/stake",136714055,364736,6688 +"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Irrelevant , status: Finished/stake",136714055,364736,6688 +Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Draft retract votes: True/stake,136714055,364736,6688 +Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Draft retract votes: False/stake,136714055,364736,6688 +Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: VotingReady retract votes: True/stake,136714055,364736,6688 +Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: VotingReady retract votes: False/stake,136714055,364736,6688 +Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Locked retract votes: True/stake,136714055,364736,6688 +Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Locked retract votes: False/stake,136714055,364736,6688 +Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Finished retract votes: True/stake,136714055,364736,6688 +Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Finished retract votes: False/stake,136714055,364736,6688 +Agora/Proposal/validator/unlocking/illegal/with 1 proposals/remove creator too early/status: Draft/stake,144743691,381650,6704 +Agora/Proposal/validator/unlocking/illegal/with 1 proposals/remove creator too early/status: VotingReady/stake,144743691,381650,6704 +Agora/Proposal/validator/unlocking/illegal/with 1 proposals/remove creator too early/status: Locked/stake,144743691,381650,6704 +Agora/Proposal/validator/unlocking/illegal/with 1 proposals/creator: retract votes/stake,144743691,381650,6702 +"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Voter , status: Draft/stake",292486160,747404,9007 +"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Voter , status: Locked/stake",292486160,747404,9007 +"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Voter , status: Finished/stake",292486160,747404,9007 +"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Creator , status: Draft/stake",288716013,755104,9013 +"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Creator , status: Locked/stake",288716013,755104,9013 +"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Creator , status: Finished/stake",288716013,755104,9013 +"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Both , status: Draft/stake",324232250,837944,9083 +"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Both , status: Locked/stake",324232250,837944,9083 +"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Both , status: Finished/stake",324232250,837944,9083 +"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Irrelevant , status: Draft/stake",256969923,664564,8924 +"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Irrelevant , status: Locked/stake",256969923,664564,8924 +"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Irrelevant , status: Finished/stake",256969923,664564,8924 +Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Draft retract votes: True/stake,256969923,664564,8924 +Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Draft retract votes: False/stake,256969923,664564,8924 +Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: VotingReady retract votes: True/stake,256969923,664564,8924 +Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: VotingReady retract votes: False/stake,256969923,664564,8924 +Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Locked retract votes: True/stake,256969923,664564,8924 +Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Locked retract votes: False/stake,256969923,664564,8924 +Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Finished retract votes: True/stake,256969923,664564,8924 +Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Finished retract votes: False/stake,256969923,664564,8924 +Agora/Proposal/validator/unlocking/illegal/with 5 proposals/remove creator too early/status: Draft/stake,287294775,733414,8997 +Agora/Proposal/validator/unlocking/illegal/with 5 proposals/remove creator too early/status: VotingReady/stake,287294775,733414,8997 +Agora/Proposal/validator/unlocking/illegal/with 5 proposals/remove creator too early/status: Locked/stake,287294775,733414,8997 +Agora/Proposal/validator/unlocking/illegal/with 5 proposals/creator: retract votes/stake,287294775,733414,8986 +"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Voter , status: Draft/stake",475866400,1201099,11883 +"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Voter , status: Locked/stake",475866400,1201099,11883 +"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Voter , status: Finished/stake",475866400,1201099,11883 +"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Creator , status: Draft/stake",470781938,1220429,11894 +"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Creator , status: Locked/stake",470781938,1220429,11894 +"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Creator , status: Finished/stake",470781938,1220429,11894 +"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Both , status: Draft/stake",539358580,1382179,12034 +"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Both , status: Locked/stake",539358580,1382179,12034 +"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Both , status: Finished/stake",539358580,1382179,12034 +"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Irrelevant , status: Draft/stake",407289758,1039349,11720 +"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Irrelevant , status: Locked/stake",407289758,1039349,11720 +"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Irrelevant , status: Finished/stake",407289758,1039349,11720 +Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Draft retract votes: True/stake,407289758,1039349,11720 +Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Draft retract votes: False/stake,407289758,1039349,11720 +Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: VotingReady retract votes: True/stake,407289758,1039349,11720 +Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: VotingReady retract votes: False/stake,407289758,1039349,11720 +Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Locked retract votes: True/stake,407289758,1039349,11720 +Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Locked retract votes: False/stake,407289758,1039349,11720 +Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Finished retract votes: True/stake,407289758,1039349,11720 +Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Finished retract votes: False/stake,407289758,1039349,11720 +Agora/Proposal/validator/unlocking/illegal/with 10 proposals/remove creator too early/status: Draft/stake,465483630,1173119,11863 +Agora/Proposal/validator/unlocking/illegal/with 10 proposals/remove creator too early/status: VotingReady/stake,465483630,1173119,11863 +Agora/Proposal/validator/unlocking/illegal/with 10 proposals/remove creator too early/status: Locked/stake,465483630,1173119,11863 +Agora/Proposal/validator/unlocking/illegal/with 10 proposals/creator: retract votes/stake,465483630,1173119,11843 +"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Voter , status: Draft/stake",1649499936,4104747,30405 +"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Voter , status: Locked/stake",1649499936,4104747,30405 +"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Voter , status: Finished/stake",1649499936,4104747,30405 +"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Creator , status: Draft/stake",1636003858,4198509,30467 +"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Creator , status: Locked/stake",1636003858,4198509,30467 +"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Creator , status: Finished/stake",1636003858,4198509,30467 +"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Both , status: Draft/stake",1916167092,4865283,31093 +"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Both , status: Locked/stake",1916167092,4865283,31093 +"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Both , status: Finished/stake",1916167092,4865283,31093 +"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Irrelevant , status: Draft/stake",1369336702,3437973,29692 +"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Irrelevant , status: Locked/stake",1369336702,3437973,29692 +"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Irrelevant , status: Finished/stake",1369336702,3437973,29692 +Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Draft retract votes: True/stake,1369336702,3437973,29692 +Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Draft retract votes: False/stake,1369336702,3437973,29692 +Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: VotingReady retract votes: True/stake,1369336702,3437973,29692 +Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: VotingReady retract votes: False/stake,1369336702,3437973,29692 +Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Locked retract votes: True/stake,1369336702,3437973,29692 +Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Locked retract votes: False/stake,1369336702,3437973,29692 +Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Finished retract votes: True/stake,1369336702,3437973,29692 +Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Finished retract votes: False/stake,1369336702,3437973,29692 +Agora/Proposal/validator/unlocking/illegal/with 42 proposals/remove creator too early/status: Draft/stake,1605892302,3987231,30321 +Agora/Proposal/validator/unlocking/illegal/with 42 proposals/remove creator too early/status: VotingReady/stake,1605892302,3987231,30321 +Agora/Proposal/validator/unlocking/illegal/with 42 proposals/remove creator too early/status: Locked/stake,1605892302,3987231,30321 +Agora/Proposal/validator/unlocking/illegal/with 42 proposals/creator: retract votes/stake,1605892302,3987231,30237 Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,24929970,68747,726 Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,47662922,128817,826 Agora/AuthorityToken/singleAuthorityTokenBurned/Correct even though scripts don't match,24929970,68747,725