Merge pull request #100 from Liqwid-Labs/connor/advance-proposals
Advance proposals
This commit is contained in:
commit
55b0669d41
11 changed files with 831 additions and 78 deletions
|
|
@ -45,7 +45,6 @@ import Plutus.V1.Ledger.Value qualified as Value
|
|||
|
||||
import Sample.Shared (
|
||||
authorityTokenSymbol,
|
||||
defaultProposalThresholds,
|
||||
govAssetClass,
|
||||
govValidatorAddress,
|
||||
governor,
|
||||
|
|
@ -113,7 +112,7 @@ mkEffectTxInfo newGovDatum =
|
|||
governorInputDatum' :: GovernorDatum
|
||||
governorInputDatum' =
|
||||
GovernorDatum
|
||||
{ proposalThresholds = defaultProposalThresholds
|
||||
{ proposalThresholds = def
|
||||
, nextProposalId = ProposalId 0
|
||||
, proposalTimings = def
|
||||
, createProposalTimeRangeMaxWidth = def
|
||||
|
|
@ -175,7 +174,7 @@ mkEffectTxInfo newGovDatum =
|
|||
validNewGovernorDatum :: GovernorDatum
|
||||
validNewGovernorDatum =
|
||||
GovernorDatum
|
||||
{ proposalThresholds = defaultProposalThresholds
|
||||
{ proposalThresholds = def
|
||||
, nextProposalId = ProposalId 42
|
||||
, proposalTimings = def
|
||||
, createProposalTimeRangeMaxWidth = def
|
||||
|
|
@ -185,7 +184,7 @@ invalidNewGovernorDatum :: GovernorDatum
|
|||
invalidNewGovernorDatum =
|
||||
GovernorDatum
|
||||
{ proposalThresholds =
|
||||
defaultProposalThresholds
|
||||
def
|
||||
{ countVoting = Tagged (-1)
|
||||
}
|
||||
, nextProposalId = ProposalId 42
|
||||
|
|
|
|||
|
|
@ -70,7 +70,6 @@ import Agora.Stake (
|
|||
|
||||
import Sample.Shared (
|
||||
authorityTokenSymbol,
|
||||
defaultProposalThresholds,
|
||||
govAssetClass,
|
||||
govSymbol,
|
||||
govValidatorAddress,
|
||||
|
|
@ -119,7 +118,7 @@ mintGST =
|
|||
governorOutputDatum' :: GovernorDatum
|
||||
governorOutputDatum' =
|
||||
GovernorDatum
|
||||
{ proposalThresholds = defaultProposalThresholds
|
||||
{ proposalThresholds = def
|
||||
, nextProposalId = ProposalId 0
|
||||
, proposalTimings = def
|
||||
, createProposalTimeRangeMaxWidth = def
|
||||
|
|
@ -214,7 +213,7 @@ createProposal =
|
|||
governorInputDatum' :: GovernorDatum
|
||||
governorInputDatum' =
|
||||
GovernorDatum
|
||||
{ proposalThresholds = defaultProposalThresholds
|
||||
{ proposalThresholds = def
|
||||
, nextProposalId = thisProposalId
|
||||
, proposalTimings = def
|
||||
, createProposalTimeRangeMaxWidth = def
|
||||
|
|
@ -245,7 +244,7 @@ createProposal =
|
|||
, effects = effects
|
||||
, status = Draft
|
||||
, cosigners = [signer]
|
||||
, thresholds = defaultProposalThresholds
|
||||
, thresholds = def
|
||||
, votes = emptyVotesFor effects
|
||||
, timingConfig = def
|
||||
, startingTime = proposalStartingTimeFromTimeRange validTimeRange
|
||||
|
|
@ -392,7 +391,7 @@ mintGATs =
|
|||
governorInputDatum' :: GovernorDatum
|
||||
governorInputDatum' =
|
||||
GovernorDatum
|
||||
{ proposalThresholds = defaultProposalThresholds
|
||||
{ proposalThresholds = def
|
||||
, nextProposalId = ProposalId 5
|
||||
, proposalTimings = def
|
||||
, createProposalTimeRangeMaxWidth = def
|
||||
|
|
@ -428,7 +427,7 @@ mintGATs =
|
|||
, effects = effects
|
||||
, status = Locked
|
||||
, cosigners = [signer, signer2]
|
||||
, thresholds = defaultProposalThresholds
|
||||
, thresholds = def
|
||||
, votes = proposalVotes
|
||||
, timingConfig = def
|
||||
, startingTime = ProposalStartingTime 10
|
||||
|
|
@ -587,7 +586,7 @@ mutateState =
|
|||
governorInputDatum' :: GovernorDatum
|
||||
governorInputDatum' =
|
||||
GovernorDatum
|
||||
{ proposalThresholds = defaultProposalThresholds
|
||||
{ proposalThresholds = def
|
||||
, nextProposalId = ProposalId 5
|
||||
, proposalTimings = def
|
||||
, createProposalTimeRangeMaxWidth = def
|
||||
|
|
|
|||
|
|
@ -13,6 +13,12 @@ module Sample.Proposal (
|
|||
stakeRef,
|
||||
voteOnProposal,
|
||||
VotingParameters (..),
|
||||
advanceProposalSuccess,
|
||||
advanceProposalFailureTimeout,
|
||||
TransitionParameters (..),
|
||||
advanceFinishedPropsoal,
|
||||
advanceProposalInsufficientVotes,
|
||||
advancePropsoalWithsStake,
|
||||
) where
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
|
@ -27,6 +33,8 @@ import Plutus.V1.Ledger.Api (
|
|||
Address (Address),
|
||||
Credential (ScriptCredential),
|
||||
Datum (Datum),
|
||||
DatumHash,
|
||||
POSIXTime,
|
||||
POSIXTimeRange,
|
||||
PubKeyHash,
|
||||
ScriptContext (..),
|
||||
|
|
@ -36,6 +44,7 @@ import Plutus.V1.Ledger.Api (
|
|||
TxInfo (..),
|
||||
TxOut (TxOut, txOutAddress, txOutDatumHash, txOutValue),
|
||||
TxOutRef (TxOutRef),
|
||||
ValidatorHash,
|
||||
)
|
||||
import Plutus.V1.Ledger.Value qualified as Value
|
||||
import PlutusTx.AssocMap qualified as AssocMap
|
||||
|
|
@ -50,6 +59,7 @@ import Agora.Proposal (
|
|||
ProposalDatum (..),
|
||||
ProposalId (..),
|
||||
ProposalStatus (..),
|
||||
ProposalThresholds (..),
|
||||
ProposalVotes (..),
|
||||
ResultTag (..),
|
||||
emptyVotesFor,
|
||||
|
|
@ -84,7 +94,7 @@ proposalCreation =
|
|||
, effects = effects
|
||||
, status = Draft
|
||||
, cosigners = [signer]
|
||||
, thresholds = defaultProposalThresholds
|
||||
, thresholds = def
|
||||
, votes = emptyVotesFor effects
|
||||
, timingConfig = def
|
||||
, startingTime = proposalStartingTimeFromTimeRange validTimeRange
|
||||
|
|
@ -96,7 +106,7 @@ proposalCreation =
|
|||
Datum
|
||||
( toBuiltinData $
|
||||
GovernorDatum
|
||||
{ proposalThresholds = defaultProposalThresholds
|
||||
{ proposalThresholds = def
|
||||
, nextProposalId = ProposalId 0
|
||||
, proposalTimings = def
|
||||
, createProposalTimeRangeMaxWidth = def
|
||||
|
|
@ -107,7 +117,7 @@ proposalCreation =
|
|||
Datum
|
||||
( toBuiltinData $
|
||||
GovernorDatum
|
||||
{ proposalThresholds = defaultProposalThresholds
|
||||
{ proposalThresholds = def
|
||||
, nextProposalId = ProposalId 1
|
||||
, proposalTimings = def
|
||||
, createProposalTimeRangeMaxWidth = def
|
||||
|
|
@ -185,7 +195,7 @@ cosignProposal newSigners =
|
|||
, effects = effects
|
||||
, status = Draft
|
||||
, cosigners = [signer]
|
||||
, thresholds = defaultProposalThresholds
|
||||
, thresholds = def
|
||||
, votes = emptyVotesFor effects
|
||||
, timingConfig = def
|
||||
, startingTime = ProposalStartingTime 0
|
||||
|
|
@ -306,7 +316,7 @@ voteOnProposal params =
|
|||
, effects = effects
|
||||
, status = VotingReady
|
||||
, cosigners = [stakeOwner]
|
||||
, thresholds = defaultProposalThresholds
|
||||
, thresholds = def
|
||||
, votes = ProposalVotes initialVotes
|
||||
, timingConfig = def
|
||||
, startingTime = ProposalStartingTime 0
|
||||
|
|
@ -375,7 +385,7 @@ voteOnProposal params =
|
|||
|
||||
---
|
||||
|
||||
-- Off-chain code should do exactly like this: prepend new lock to the list.
|
||||
-- Off-chain code should do exactly like this: prepend new lock toStatus the list.
|
||||
updatedLocks :: [ProposalLock]
|
||||
updatedLocks = ProposalLock params.voteFor proposalInputDatum'.proposalId : existingLocks
|
||||
|
||||
|
|
@ -415,3 +425,398 @@ voteOnProposal params =
|
|||
, txInfoData = datumPair <$> [proposalInputDatum, proposalOutputDatum, stakeInputDatum, stakeOutputDatum]
|
||||
, txInfoId = "827598fb2d69a896bbd9e645bb14c307df907f422b39eecbe4d6329bc30b428c"
|
||||
}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- | Parameters for state transition of proposals.
|
||||
data TransitionParameters = TransitionParameters
|
||||
{ -- The initial status of the propsoal.
|
||||
initialProposalStatus :: ProposalStatus
|
||||
, -- The starting time of the propsoal.
|
||||
proposalStartingTime :: ProposalStartingTime
|
||||
}
|
||||
|
||||
-- | Create a 'TxInfo' that update the status of a proposal.
|
||||
mkTransitionTxInfo ::
|
||||
-- | Initial state of the proposal.
|
||||
ProposalStatus ->
|
||||
-- | Next state of the proposal.
|
||||
ProposalStatus ->
|
||||
-- | Effects.
|
||||
AssocMap.Map ResultTag (AssocMap.Map ValidatorHash DatumHash) ->
|
||||
-- | Votes.
|
||||
ProposalVotes ->
|
||||
-- | Starting time of the proposal.
|
||||
ProposalStartingTime ->
|
||||
-- | Valid time range of the transaction.
|
||||
POSIXTimeRange ->
|
||||
TxInfo
|
||||
mkTransitionTxInfo from to effects votes startingTime timeRange =
|
||||
let pst = Value.singleton proposalPolicySymbol "" 1
|
||||
|
||||
---
|
||||
|
||||
proposalInputDatum' :: ProposalDatum
|
||||
proposalInputDatum' =
|
||||
ProposalDatum
|
||||
{ proposalId = ProposalId 0
|
||||
, effects = effects
|
||||
, status = from
|
||||
, cosigners = [signer]
|
||||
, thresholds = def
|
||||
, votes = votes
|
||||
, timingConfig = def
|
||||
, startingTime = startingTime
|
||||
}
|
||||
proposalInputDatum :: Datum
|
||||
proposalInputDatum = Datum $ toBuiltinData proposalInputDatum'
|
||||
proposalInput :: TxOut
|
||||
proposalInput =
|
||||
TxOut
|
||||
{ txOutAddress = proposalValidatorAddress
|
||||
, txOutValue = pst
|
||||
, txOutDatumHash = Just $ toDatumHash proposalInputDatum
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
proposalOutputDatum' :: ProposalDatum
|
||||
proposalOutputDatum' =
|
||||
proposalInputDatum'
|
||||
{ status = to
|
||||
}
|
||||
proposalOutputDatum :: Datum
|
||||
proposalOutputDatum = Datum $ toBuiltinData proposalOutputDatum'
|
||||
proposalOutput :: TxOut
|
||||
proposalOutput =
|
||||
proposalInput
|
||||
{ txOutValue = proposalInput.txOutValue <> minAda
|
||||
, txOutDatumHash = Just $ toDatumHash proposalOutputDatum
|
||||
}
|
||||
in TxInfo
|
||||
{ txInfoInputs = [TxInInfo proposalRef proposalInput]
|
||||
, txInfoOutputs = [proposalOutput]
|
||||
, txInfoFee = Value.singleton "" "" 2
|
||||
, txInfoMint = mempty
|
||||
, txInfoDCert = []
|
||||
, txInfoWdrl = []
|
||||
, txInfoValidRange = timeRange
|
||||
, txInfoSignatories = [signer]
|
||||
, txInfoData = datumPair <$> [proposalInputDatum, proposalOutputDatum]
|
||||
, txInfoId = "95ba4015e30aef16a3461ea97a779f814aeea6b8009d99a94add4b8293be737a"
|
||||
}
|
||||
|
||||
{- | Create a valid 'TxInfo' that advances a proposal, given the parameters.
|
||||
Note that 'TransitionParameters.initialProposalStatus' should not be 'Finished'.
|
||||
-}
|
||||
advanceProposalSuccess :: TransitionParameters -> TxInfo
|
||||
advanceProposalSuccess params =
|
||||
let -- Status of the output proposal.
|
||||
toStatus :: ProposalStatus
|
||||
toStatus = case params.initialProposalStatus of
|
||||
Draft -> VotingReady
|
||||
VotingReady -> Locked
|
||||
Locked -> Finished
|
||||
Finished -> error "Cannot advance 'Finished' proposal"
|
||||
|
||||
effects =
|
||||
AssocMap.fromList
|
||||
[ (ResultTag 0, AssocMap.empty)
|
||||
, (ResultTag 1, AssocMap.empty)
|
||||
]
|
||||
|
||||
emptyVotes@(ProposalVotes emptyVotes') = emptyVotesFor effects
|
||||
|
||||
-- Set the vote count of outcome 0 to @def.countingVoting + 1@,
|
||||
-- meaning that outcome 0 will be the winner.
|
||||
outcome0WinningVotes =
|
||||
ProposalVotes $
|
||||
updateMap
|
||||
(\_ -> Just $ untag (def :: ProposalThresholds).countVoting + 1)
|
||||
(ResultTag 0)
|
||||
emptyVotes'
|
||||
|
||||
votes :: ProposalVotes
|
||||
votes = case params.initialProposalStatus of
|
||||
Draft -> emptyVotes
|
||||
-- With sufficient votes
|
||||
_ -> outcome0WinningVotes
|
||||
|
||||
proposalStartingTime :: POSIXTime
|
||||
proposalStartingTime =
|
||||
let (ProposalStartingTime startingTime) = params.proposalStartingTime
|
||||
in startingTime
|
||||
|
||||
timeRange :: POSIXTimeRange
|
||||
timeRange = case params.initialProposalStatus of
|
||||
-- [S + 1, S + D - 1]
|
||||
Draft ->
|
||||
closedBoundedInterval
|
||||
(proposalStartingTime + 1)
|
||||
(proposalStartingTime + (def :: ProposalTimingConfig).draftTime - 1)
|
||||
-- [S + D + V + 1, S + D + V + L - 1]
|
||||
VotingReady ->
|
||||
closedBoundedInterval
|
||||
( proposalStartingTime
|
||||
+ (def :: ProposalTimingConfig).draftTime
|
||||
+ (def :: ProposalTimingConfig).votingTime
|
||||
+ 1
|
||||
)
|
||||
( proposalStartingTime
|
||||
+ (def :: ProposalTimingConfig).draftTime
|
||||
+ (def :: ProposalTimingConfig).votingTime
|
||||
+ (def :: ProposalTimingConfig).lockingTime
|
||||
- 1
|
||||
)
|
||||
-- [S + D + V + L + 1, S + + D + V + L + E - 1]
|
||||
Locked ->
|
||||
closedBoundedInterval
|
||||
( proposalStartingTime
|
||||
+ (def :: ProposalTimingConfig).draftTime
|
||||
+ (def :: ProposalTimingConfig).votingTime
|
||||
+ (def :: ProposalTimingConfig).lockingTime
|
||||
+ 1
|
||||
)
|
||||
( proposalStartingTime
|
||||
+ (def :: ProposalTimingConfig).draftTime
|
||||
+ (def :: ProposalTimingConfig).votingTime
|
||||
+ (def :: ProposalTimingConfig).lockingTime
|
||||
+ (def :: ProposalTimingConfig).executingTime - 1
|
||||
)
|
||||
Finished -> error "Cannot advance 'Finished' proposal"
|
||||
in mkTransitionTxInfo
|
||||
params.initialProposalStatus
|
||||
toStatus
|
||||
effects
|
||||
votes
|
||||
params.proposalStartingTime
|
||||
timeRange
|
||||
|
||||
{- | Create a valid 'TxInfo' that advances a proposal to failed state, given the parameters.
|
||||
The reason why the proposal fails is the proposal has ran out of time.
|
||||
Note that 'TransitionParameters.initialProposalStatus' should not be 'Finished'.
|
||||
-}
|
||||
advanceProposalFailureTimeout :: TransitionParameters -> TxInfo
|
||||
advanceProposalFailureTimeout params =
|
||||
let effects =
|
||||
AssocMap.fromList
|
||||
[ (ResultTag 0, AssocMap.empty)
|
||||
, (ResultTag 1, AssocMap.empty)
|
||||
]
|
||||
|
||||
emptyVotes@(ProposalVotes emptyVotes') = emptyVotesFor effects
|
||||
|
||||
-- Set the vote count of outcome 0 to @def.countingVoting + 1@,
|
||||
-- meaning that outcome 0 will be the winner.
|
||||
outcome0WinningVotes =
|
||||
ProposalVotes $
|
||||
updateMap
|
||||
(\_ -> Just $ untag (def :: ProposalThresholds).countVoting + 1)
|
||||
(ResultTag 0)
|
||||
emptyVotes'
|
||||
|
||||
votes :: ProposalVotes
|
||||
votes = case params.initialProposalStatus of
|
||||
Draft -> emptyVotes
|
||||
-- With sufficient votes
|
||||
_ -> outcome0WinningVotes
|
||||
|
||||
proposalStartingTime :: POSIXTime
|
||||
proposalStartingTime =
|
||||
let (ProposalStartingTime startingTime) = params.proposalStartingTime
|
||||
in startingTime
|
||||
|
||||
timeRange :: POSIXTimeRange
|
||||
timeRange = case params.initialProposalStatus of
|
||||
-- [S + D + 1, S + D + V - 1]
|
||||
Draft ->
|
||||
closedBoundedInterval
|
||||
(proposalStartingTime + (def :: ProposalTimingConfig).draftTime + 1)
|
||||
( proposalStartingTime
|
||||
+ (def :: ProposalTimingConfig).draftTime
|
||||
+ (def :: ProposalTimingConfig).votingTime - 1
|
||||
)
|
||||
-- [S + D + V + L + 1, S + D + V + L + E -1]
|
||||
VotingReady ->
|
||||
closedBoundedInterval
|
||||
( proposalStartingTime
|
||||
+ (def :: ProposalTimingConfig).draftTime
|
||||
+ (def :: ProposalTimingConfig).votingTime
|
||||
+ (def :: ProposalTimingConfig).lockingTime
|
||||
+ 1
|
||||
)
|
||||
( proposalStartingTime
|
||||
+ (def :: ProposalTimingConfig).draftTime
|
||||
+ (def :: ProposalTimingConfig).votingTime
|
||||
+ (def :: ProposalTimingConfig).lockingTime
|
||||
+ (def :: ProposalTimingConfig).executingTime
|
||||
- 1
|
||||
)
|
||||
-- [S + D + V + L + E + 1, S + D + V + L + E + 100]
|
||||
Locked ->
|
||||
closedBoundedInterval
|
||||
( proposalStartingTime
|
||||
+ (def :: ProposalTimingConfig).draftTime
|
||||
+ (def :: ProposalTimingConfig).votingTime
|
||||
+ (def :: ProposalTimingConfig).lockingTime
|
||||
+ (def :: ProposalTimingConfig).executingTime
|
||||
+ 1
|
||||
)
|
||||
( proposalStartingTime
|
||||
+ (def :: ProposalTimingConfig).draftTime
|
||||
+ (def :: ProposalTimingConfig).votingTime
|
||||
+ (def :: ProposalTimingConfig).lockingTime
|
||||
+ (def :: ProposalTimingConfig).executingTime
|
||||
+ 100
|
||||
)
|
||||
Finished -> error "Cannot advance 'Finished' proposal"
|
||||
in mkTransitionTxInfo
|
||||
params.initialProposalStatus
|
||||
Finished
|
||||
effects
|
||||
votes
|
||||
params.proposalStartingTime
|
||||
timeRange
|
||||
|
||||
-- | An invalid 'TxInfo' that tries to advance a 'VotingReady' proposal without sufficient votes.
|
||||
advanceProposalInsufficientVotes :: TxInfo
|
||||
advanceProposalInsufficientVotes =
|
||||
let effects =
|
||||
AssocMap.fromList
|
||||
[ (ResultTag 0, AssocMap.empty)
|
||||
, (ResultTag 1, AssocMap.empty)
|
||||
]
|
||||
|
||||
-- Insufficient votes.
|
||||
votes = emptyVotesFor effects
|
||||
|
||||
proposalStartingTime = 0
|
||||
|
||||
-- Valid time range.
|
||||
-- [S + D + 1, S + V - 1]
|
||||
timeRange =
|
||||
closedBoundedInterval
|
||||
(proposalStartingTime + (def :: ProposalTimingConfig).draftTime + 1)
|
||||
(proposalStartingTime + (def :: ProposalTimingConfig).votingTime - 1)
|
||||
in mkTransitionTxInfo
|
||||
VotingReady
|
||||
Locked
|
||||
effects
|
||||
votes
|
||||
(ProposalStartingTime proposalStartingTime)
|
||||
timeRange
|
||||
|
||||
-- | An invalid 'TxInfo' that tries to advance a 'Finished' proposal.
|
||||
advanceFinishedPropsoal :: TxInfo
|
||||
advanceFinishedPropsoal =
|
||||
let effects =
|
||||
AssocMap.fromList
|
||||
[ (ResultTag 0, AssocMap.empty)
|
||||
, (ResultTag 1, AssocMap.empty)
|
||||
]
|
||||
|
||||
-- Set the vote count of outcome 0 to @def.countingVoting + 1@,
|
||||
-- meaning that outcome 0 will be the winner.
|
||||
outcome0WinningVotes =
|
||||
ProposalVotes $
|
||||
AssocMap.fromList
|
||||
[ (ResultTag 0, untag (def :: ProposalThresholds).countVoting + 1)
|
||||
, (ResultTag 1, 0)
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
timeRange =
|
||||
closedBoundedInterval
|
||||
((def :: ProposalTimingConfig).lockingTime + 1)
|
||||
((def :: ProposalTimingConfig).executingTime - 1)
|
||||
in mkTransitionTxInfo
|
||||
Finished
|
||||
Finished
|
||||
effects
|
||||
outcome0WinningVotes
|
||||
(ProposalStartingTime 0)
|
||||
timeRange
|
||||
|
||||
{- | An illegal 'TxInfo' that tries to use 'AdvanceProposal' with a stake.
|
||||
From the perspective of stake validator, the transition is valid,
|
||||
so the proposal validator should reject this.
|
||||
-}
|
||||
advancePropsoalWithsStake :: TxInfo
|
||||
advancePropsoalWithsStake =
|
||||
let templateTxInfo =
|
||||
advanceProposalSuccess
|
||||
TransitionParameters
|
||||
{ initialProposalStatus = VotingReady
|
||||
, proposalStartingTime = ProposalStartingTime 0
|
||||
}
|
||||
|
||||
---
|
||||
-- Now we create a new lock on an arbitrary stake
|
||||
|
||||
sst = Value.assetClassValue stakeAssetClass 1
|
||||
|
||||
---
|
||||
|
||||
stakeOwner = signer
|
||||
stakedAmount = 200
|
||||
|
||||
---
|
||||
|
||||
existingLocks :: [ProposalLock]
|
||||
existingLocks =
|
||||
[ ProposalLock (ResultTag 0) (ProposalId 0)
|
||||
, ProposalLock (ResultTag 2) (ProposalId 1)
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
stakeInputDatum' :: StakeDatum
|
||||
stakeInputDatum' =
|
||||
StakeDatum
|
||||
{ stakedAmount = Tagged stakedAmount
|
||||
, owner = stakeOwner
|
||||
, lockedBy = existingLocks
|
||||
}
|
||||
stakeInputDatum :: Datum
|
||||
stakeInputDatum = Datum $ toBuiltinData stakeInputDatum'
|
||||
stakeInput :: TxOut
|
||||
stakeInput =
|
||||
TxOut
|
||||
{ txOutAddress = stakeAddress
|
||||
, txOutValue =
|
||||
mconcat
|
||||
[ sst
|
||||
, Value.assetClassValue (untag stake.gtClassRef) stakedAmount
|
||||
, minAda
|
||||
]
|
||||
, txOutDatumHash = Just $ toDatumHash stakeInputDatum
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
updatedLocks :: [ProposalLock]
|
||||
updatedLocks = ProposalLock (ResultTag 42) (ProposalId 27) : existingLocks
|
||||
|
||||
---
|
||||
|
||||
stakeOutputDatum' :: StakeDatum
|
||||
stakeOutputDatum' =
|
||||
stakeInputDatum'
|
||||
{ lockedBy = updatedLocks
|
||||
}
|
||||
stakeOutputDatum :: Datum
|
||||
stakeOutputDatum = Datum $ toBuiltinData stakeOutputDatum'
|
||||
stakeOutput :: TxOut
|
||||
stakeOutput =
|
||||
stakeInput
|
||||
{ txOutDatumHash = Just $ toDatumHash stakeOutputDatum
|
||||
}
|
||||
in templateTxInfo
|
||||
{ txInfoInputs = TxInInfo stakeRef stakeInput : templateTxInfo.txInfoInputs
|
||||
, txInfoOutputs = stakeOutput : templateTxInfo.txInfoOutputs
|
||||
, txInfoData =
|
||||
(datumPair <$> [stakeInputDatum, stakeOutputDatum])
|
||||
<> templateTxInfo.txInfoData
|
||||
, txInfoSignatories = [stakeOwner]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ module Sample.Shared (
|
|||
gstUTXORef,
|
||||
|
||||
-- ** Proposal
|
||||
defaultProposalThresholds,
|
||||
proposal,
|
||||
proposalPolicySymbol,
|
||||
proposalValidatorHash,
|
||||
|
|
@ -184,13 +183,16 @@ proposalValidatorHash = proposalValidatorHashFromGovernor governor
|
|||
proposalValidatorAddress :: Address
|
||||
proposalValidatorAddress = scriptHashAddress proposalValidatorHash
|
||||
|
||||
defaultProposalThresholds :: ProposalThresholds
|
||||
defaultProposalThresholds =
|
||||
ProposalThresholds
|
||||
{ countVoting = Tagged 1000
|
||||
, create = Tagged 1
|
||||
, startVoting = Tagged 10
|
||||
}
|
||||
{- | Default value of 'Agora.Proposal.ProposalThresholds'.
|
||||
For testing purpose only.
|
||||
-}
|
||||
instance Default ProposalThresholds where
|
||||
def =
|
||||
ProposalThresholds
|
||||
{ countVoting = Tagged 1000
|
||||
, create = Tagged 1
|
||||
, startVoting = Tagged 10
|
||||
}
|
||||
|
||||
authorityToken :: AuthorityToken
|
||||
authorityToken = authorityTokenFromGovernor governor
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ specs =
|
|||
"governor validator should pass"
|
||||
(governorValidator Shared.governor)
|
||||
( GovernorDatum
|
||||
Shared.defaultProposalThresholds
|
||||
def
|
||||
(ProposalId 0)
|
||||
def
|
||||
def
|
||||
|
|
@ -56,7 +56,7 @@ specs =
|
|||
"governor validator should fail"
|
||||
(governorValidator Shared.governor)
|
||||
( GovernorDatum
|
||||
Shared.defaultProposalThresholds
|
||||
def
|
||||
(ProposalId 0)
|
||||
def
|
||||
def
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ specs =
|
|||
"proposal creation"
|
||||
(governorValidator Shared.governor)
|
||||
( GovernorDatum
|
||||
Shared.defaultProposalThresholds
|
||||
def
|
||||
(ProposalId 0)
|
||||
def
|
||||
def
|
||||
|
|
@ -55,7 +55,7 @@ specs =
|
|||
"GATs minting"
|
||||
(governorValidator Shared.governor)
|
||||
( GovernorDatum
|
||||
Shared.defaultProposalThresholds
|
||||
def
|
||||
(ProposalId 5)
|
||||
def
|
||||
def
|
||||
|
|
@ -66,7 +66,7 @@ specs =
|
|||
"mutate governor state"
|
||||
(governorValidator Shared.governor)
|
||||
( GovernorDatum
|
||||
Shared.defaultProposalThresholds
|
||||
def
|
||||
(ProposalId 5)
|
||||
def
|
||||
def
|
||||
|
|
|
|||
|
|
@ -15,8 +15,9 @@ import Agora.Proposal (
|
|||
Proposal (..),
|
||||
ProposalDatum (..),
|
||||
ProposalId (ProposalId),
|
||||
ProposalRedeemer (Cosign, Vote),
|
||||
ProposalStatus (Draft, VotingReady),
|
||||
ProposalRedeemer (..),
|
||||
ProposalStatus (..),
|
||||
ProposalThresholds (..),
|
||||
ProposalVotes (ProposalVotes),
|
||||
ResultTag (ResultTag),
|
||||
cosigners,
|
||||
|
|
@ -39,7 +40,7 @@ import Agora.Stake (
|
|||
)
|
||||
import Agora.Stake.Scripts (stakeValidator)
|
||||
import Data.Default.Class (Default (def))
|
||||
import Data.Tagged (Tagged (Tagged))
|
||||
import Data.Tagged (Tagged (Tagged), untag)
|
||||
import Plutus.V1.Ledger.Api (ScriptContext (..), ScriptPurpose (..))
|
||||
import PlutusTx.AssocMap qualified as AssocMap
|
||||
import Sample.Proposal qualified as Proposal
|
||||
|
|
@ -49,6 +50,7 @@ import Spec.Specification (
|
|||
SpecificationTree,
|
||||
group,
|
||||
policySucceedsWith,
|
||||
validatorFailsWith,
|
||||
validatorSucceedsWith,
|
||||
)
|
||||
|
||||
|
|
@ -81,7 +83,7 @@ specs =
|
|||
]
|
||||
, status = Draft
|
||||
, cosigners = [signer]
|
||||
, thresholds = Shared.defaultProposalThresholds
|
||||
, thresholds = def
|
||||
, votes =
|
||||
emptyVotesFor $
|
||||
AssocMap.fromList
|
||||
|
|
@ -115,7 +117,7 @@ specs =
|
|||
]
|
||||
, status = VotingReady
|
||||
, cosigners = [signer]
|
||||
, thresholds = Shared.defaultProposalThresholds
|
||||
, thresholds = def
|
||||
, votes =
|
||||
ProposalVotes
|
||||
( AssocMap.fromList
|
||||
|
|
@ -158,5 +160,189 @@ specs =
|
|||
(Spending Proposal.stakeRef)
|
||||
)
|
||||
]
|
||||
, group
|
||||
"advancing"
|
||||
[ group "successfully advance to next state" $
|
||||
map
|
||||
( \(name, initialState) ->
|
||||
validatorSucceedsWith
|
||||
name
|
||||
(proposalValidator Shared.proposal)
|
||||
( ProposalDatum
|
||||
{ proposalId = ProposalId 0
|
||||
, effects =
|
||||
AssocMap.fromList
|
||||
[ (ResultTag 0, AssocMap.empty)
|
||||
, (ResultTag 1, AssocMap.empty)
|
||||
]
|
||||
, status = initialState
|
||||
, cosigners = [signer]
|
||||
, thresholds = def
|
||||
, votes =
|
||||
ProposalVotes
|
||||
( AssocMap.fromList
|
||||
[
|
||||
( ResultTag 0
|
||||
, case initialState of
|
||||
Draft -> 0
|
||||
_ -> untag (def :: ProposalThresholds).countVoting + 1
|
||||
)
|
||||
, (ResultTag 1, 0)
|
||||
]
|
||||
)
|
||||
, timingConfig = def
|
||||
, startingTime = ProposalStartingTime 0
|
||||
}
|
||||
)
|
||||
AdvanceProposal
|
||||
( ScriptContext
|
||||
( Proposal.advanceProposalSuccess
|
||||
Proposal.TransitionParameters
|
||||
{ Proposal.initialProposalStatus = initialState
|
||||
, Proposal.proposalStartingTime = ProposalStartingTime 0
|
||||
}
|
||||
)
|
||||
(Spending Proposal.proposalRef)
|
||||
)
|
||||
)
|
||||
[ ("Draft -> VotringReady", Draft)
|
||||
, ("VotingReady -> Locked", VotingReady)
|
||||
, ("Locked -> Finished", Locked)
|
||||
]
|
||||
, group "successfully advance to failed state: timeout" $
|
||||
map
|
||||
( \(name, initialState) ->
|
||||
validatorSucceedsWith
|
||||
name
|
||||
(proposalValidator Shared.proposal)
|
||||
( ProposalDatum
|
||||
{ proposalId = ProposalId 0
|
||||
, effects =
|
||||
AssocMap.fromList
|
||||
[ (ResultTag 0, AssocMap.empty)
|
||||
, (ResultTag 1, AssocMap.empty)
|
||||
]
|
||||
, status = initialState
|
||||
, cosigners = [signer]
|
||||
, thresholds = def
|
||||
, votes =
|
||||
ProposalVotes
|
||||
( AssocMap.fromList
|
||||
[
|
||||
( ResultTag 0
|
||||
, case initialState of
|
||||
Draft -> 0
|
||||
_ -> untag (def :: ProposalThresholds).countVoting + 1
|
||||
)
|
||||
, (ResultTag 1, 0)
|
||||
]
|
||||
)
|
||||
, timingConfig = def
|
||||
, startingTime = ProposalStartingTime 0
|
||||
}
|
||||
)
|
||||
AdvanceProposal
|
||||
( ScriptContext
|
||||
( Proposal.advanceProposalFailureTimeout
|
||||
Proposal.TransitionParameters
|
||||
{ Proposal.initialProposalStatus = initialState
|
||||
, Proposal.proposalStartingTime = ProposalStartingTime 0
|
||||
}
|
||||
)
|
||||
(Spending Proposal.proposalRef)
|
||||
)
|
||||
)
|
||||
[ ("Draft -> Finished", Draft)
|
||||
, ("VotingReady -> Finished", VotingReady)
|
||||
, ("Locked -> Finished", Locked)
|
||||
]
|
||||
, validatorFailsWith
|
||||
"illegal: insufficient votes"
|
||||
(proposalValidator Shared.proposal)
|
||||
( ProposalDatum
|
||||
{ proposalId = ProposalId 0
|
||||
, effects =
|
||||
AssocMap.fromList
|
||||
[ (ResultTag 0, AssocMap.empty)
|
||||
, (ResultTag 1, AssocMap.empty)
|
||||
]
|
||||
, status = VotingReady
|
||||
, cosigners = [signer]
|
||||
, thresholds = def
|
||||
, votes =
|
||||
ProposalVotes
|
||||
( AssocMap.fromList
|
||||
[ (ResultTag 0, 0)
|
||||
, (ResultTag 1, 0)
|
||||
]
|
||||
)
|
||||
, timingConfig = def
|
||||
, startingTime = ProposalStartingTime 0
|
||||
}
|
||||
)
|
||||
AdvanceProposal
|
||||
( ScriptContext
|
||||
Proposal.advanceProposalInsufficientVotes
|
||||
(Spending Proposal.proposalRef)
|
||||
)
|
||||
, validatorFailsWith
|
||||
"illegal: initial state is Finished"
|
||||
(proposalValidator Shared.proposal)
|
||||
( ProposalDatum
|
||||
{ proposalId = ProposalId 0
|
||||
, effects =
|
||||
AssocMap.fromList
|
||||
[ (ResultTag 0, AssocMap.empty)
|
||||
, (ResultTag 1, AssocMap.empty)
|
||||
]
|
||||
, status = Finished
|
||||
, cosigners = [signer]
|
||||
, thresholds = def
|
||||
, votes =
|
||||
ProposalVotes
|
||||
( AssocMap.fromList
|
||||
[ (ResultTag 0, untag (def :: ProposalThresholds).countVoting + 1)
|
||||
, (ResultTag 1, 0)
|
||||
]
|
||||
)
|
||||
, timingConfig = def
|
||||
, startingTime = ProposalStartingTime 0
|
||||
}
|
||||
)
|
||||
AdvanceProposal
|
||||
( ScriptContext
|
||||
Proposal.advanceFinishedPropsoal
|
||||
(Spending Proposal.proposalRef)
|
||||
)
|
||||
, validatorFailsWith
|
||||
"illegal: with stake input"
|
||||
(proposalValidator Shared.proposal)
|
||||
( ProposalDatum
|
||||
{ proposalId = ProposalId 0
|
||||
, effects =
|
||||
AssocMap.fromList
|
||||
[ (ResultTag 0, AssocMap.empty)
|
||||
, (ResultTag 1, AssocMap.empty)
|
||||
]
|
||||
, status = VotingReady
|
||||
, cosigners = [signer]
|
||||
, thresholds = def
|
||||
, votes =
|
||||
ProposalVotes
|
||||
( AssocMap.fromList
|
||||
[ (ResultTag 0, 0)
|
||||
, (ResultTag 1, 0)
|
||||
]
|
||||
)
|
||||
, timingConfig = def
|
||||
, startingTime = ProposalStartingTime 0
|
||||
}
|
||||
)
|
||||
AdvanceProposal
|
||||
( ScriptContext
|
||||
Proposal.advancePropsoalWithsStake
|
||||
(Spending Proposal.proposalRef)
|
||||
)
|
||||
]
|
||||
]
|
||||
]
|
||||
|
|
|
|||
|
|
@ -52,7 +52,9 @@ import Agora.Proposal (
|
|||
Proposal (..),
|
||||
ProposalStatus (Draft, Locked),
|
||||
pemptyVotesFor,
|
||||
pneutralOption,
|
||||
proposalDatumValid,
|
||||
pwinner,
|
||||
)
|
||||
import Agora.Proposal.Scripts (
|
||||
proposalPolicy,
|
||||
|
|
@ -77,7 +79,6 @@ import Agora.Utils (
|
|||
mustFindDatum',
|
||||
pfindTxInByTxOutRef,
|
||||
pisDJust,
|
||||
pisJust,
|
||||
pisUTXOSpent,
|
||||
psymbolValueOf,
|
||||
ptryFindDatum,
|
||||
|
|
@ -114,13 +115,11 @@ import Plutarch.Api.V1.AssetClass (
|
|||
passetClass,
|
||||
passetClassValueOf,
|
||||
)
|
||||
import Plutarch.Extra.Comonad (pextract)
|
||||
import Plutarch.Extra.Map (
|
||||
pkeys,
|
||||
plookup,
|
||||
plookup',
|
||||
)
|
||||
import Plutarch.Extra.TermCont (pmatchC)
|
||||
import Plutarch.SafeMoney (PDiscrete (..), pvalueDiscrete')
|
||||
import Plutarch.TryFrom ()
|
||||
|
||||
|
|
@ -605,35 +604,9 @@ governorValidator gov =
|
|||
-- TODO: anything else to check here?
|
||||
|
||||
-- Find the highest votes and the corresponding tag.
|
||||
let highestVoteFolder =
|
||||
phoistAcyclic $
|
||||
plam
|
||||
( \pair last' ->
|
||||
pif
|
||||
(pisJust # last')
|
||||
( unTermCont $ do
|
||||
PJust last <- tcmatch last'
|
||||
let lastHighestVote = pfromData $ psndBuiltin # last
|
||||
thisVote = pfromData $ psndBuiltin # pair
|
||||
pure $ pif (lastHighestVote #< thisVote) (pcon $ PJust pair) last'
|
||||
)
|
||||
(pcon $ PJust pair)
|
||||
)
|
||||
|
||||
votesList = pto $ pto $ pfromData proposalInputDatumF.votes
|
||||
|
||||
maybeWinner =
|
||||
pfoldr # highestVoteFolder # pcon PNothing # votesList
|
||||
|
||||
winner <- tclet $ mustBePJust # "No winning outcome" # maybeWinner
|
||||
|
||||
PDiscrete minimumVotes' <- pmatchC $ pfromData $ pfield @"execute" # proposalInputDatumF.thresholds
|
||||
let highestVote = pfromData $ psndBuiltin # winner
|
||||
minimumVotes = pextract # minimumVotes'
|
||||
|
||||
tcassert "Higgest vote doesn't meet the minimum requirement" $ minimumVotes #<= highestVote
|
||||
|
||||
let finalResultTag = pfromData $ pfstBuiltin # winner
|
||||
let quorum = pto $ pto $ pfromData $ pfield @"execute" # proposalInputDatumF.thresholds
|
||||
neutralOption = pneutralOption # proposalInputDatumF.effects
|
||||
finalResultTag = pwinner # proposalInputDatumF.votes # quorum # neutralOption
|
||||
|
||||
-- The effects of the winner outcome.
|
||||
effectGroup <- tclet $ plookup' # finalResultTag #$ proposalInputDatumF.effects
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ module Agora.Proposal (
|
|||
-- * Plutarch helpers
|
||||
proposalDatumValid,
|
||||
pemptyVotesFor,
|
||||
pwinner,
|
||||
pneutralOption,
|
||||
) where
|
||||
|
||||
import GHC.Generics qualified as GHC
|
||||
|
|
@ -48,7 +50,7 @@ import PlutusTx.AssocMap qualified as AssocMap
|
|||
|
||||
import Agora.Proposal.Time (PProposalStartingTime, PProposalTimingConfig, ProposalStartingTime, ProposalTimingConfig)
|
||||
import Agora.SafeMoney (GTTag)
|
||||
import Agora.Utils (pkeysEqual, pmapMap, pnotNull)
|
||||
import Agora.Utils (mustBePJust, pkeysEqual, pmapMap, pnotNull, tclet)
|
||||
import Control.Applicative (Const)
|
||||
import Control.Arrow (first)
|
||||
import Data.Tagged (Tagged)
|
||||
|
|
@ -448,3 +450,98 @@ proposalDatumValid proposal =
|
|||
, ptraceIfFalse "Proposal has fewer cosigners than the limit" $ plength # (pfromData datum.cosigners) #<= pconstant proposal.maximumCosigners
|
||||
, ptraceIfFalse "Proposal votes and effects are compatible with each other" $ pkeysEqual # datum.effects # pto (pfromData datum.votes)
|
||||
]
|
||||
|
||||
{- Find the winner result tag, given the votes, the quorum the "neutral" result tag.
|
||||
|
||||
The winner should be unambiguous, meaning that if two options have the same highest votes,
|
||||
the "neutral" option will be the winner.
|
||||
-}
|
||||
pwinner ::
|
||||
Term
|
||||
s
|
||||
( PProposalVotes
|
||||
:--> PInteger
|
||||
:--> PResultTag
|
||||
:--> PResultTag
|
||||
)
|
||||
pwinner = phoistAcyclic $
|
||||
plam $ \votes quorum neutral -> unTermCont $ do
|
||||
winner <- tclet $ phighestVotes # votes
|
||||
winnerResultTag <- tclet $ pfromData $ pfstBuiltin # winner
|
||||
highestVotes <- tclet $ pfromData $ psndBuiltin # winner
|
||||
|
||||
let l :: Term _ (PBuiltinList _)
|
||||
l = pto $ pto votes
|
||||
|
||||
f ::
|
||||
Term
|
||||
_
|
||||
( PBuiltinPair (PAsData PResultTag) (PAsData PInteger)
|
||||
:--> PInteger
|
||||
:--> PInteger
|
||||
)
|
||||
f = plam $ \(pfromData . (psndBuiltin #) -> thisVotes) i ->
|
||||
pif
|
||||
(thisVotes #== highestVotes)
|
||||
(i + 1)
|
||||
i
|
||||
|
||||
noDuplicateHighestVotes =
|
||||
ptraceIfFalse "Ambiguous winner" $
|
||||
pfoldr # f # 0 # l #== 1
|
||||
|
||||
exceedQuorum =
|
||||
ptraceIfFalse "Highest vote count should exceed the minimum threshold" $
|
||||
quorum #< highestVotes
|
||||
|
||||
pure $
|
||||
pif
|
||||
(noDuplicateHighestVotes #&& exceedQuorum)
|
||||
winnerResultTag
|
||||
neutral
|
||||
|
||||
-- | Find the winning outcome (and the corresponding vote count) given the votes.
|
||||
phighestVotes ::
|
||||
Term
|
||||
s
|
||||
( PProposalVotes
|
||||
:--> PBuiltinPair (PAsData PResultTag) (PAsData PInteger)
|
||||
)
|
||||
phighestVotes = phoistAcyclic $
|
||||
plam $ \votes ->
|
||||
let l :: Term _ (PBuiltinList _)
|
||||
l = pto $ pto votes
|
||||
|
||||
f ::
|
||||
Term
|
||||
_
|
||||
( PBuiltinPair (PAsData PResultTag) (PAsData PInteger)
|
||||
:--> PBuiltinPair (PAsData PResultTag) (PAsData PInteger)
|
||||
:--> PBuiltinPair (PAsData PResultTag) (PAsData PInteger)
|
||||
)
|
||||
f = phoistAcyclic $
|
||||
plam $ \this last ->
|
||||
let lastVotes = pfromData $ psndBuiltin # last
|
||||
thisVotes = pfromData $ psndBuiltin # this
|
||||
in pif (lastVotes #< thisVotes) this last
|
||||
in pfoldr # f # (phead # l) # l
|
||||
|
||||
-- | Find the "neutral" option (a dummy outcome with no effect) given the effects.
|
||||
pneutralOption ::
|
||||
Term
|
||||
s
|
||||
( PMap PResultTag (PMap PValidatorHash PDatumHash)
|
||||
:--> PResultTag
|
||||
)
|
||||
pneutralOption = phoistAcyclic $
|
||||
plam $ \effects ->
|
||||
let l :: Term _ (PBuiltinList (PBuiltinPair (PAsData PResultTag) _))
|
||||
l = pto effects
|
||||
|
||||
f :: Term _ (PBuiltinPair (PAsData PResultTag) (PAsData (PMap _ _)) :--> PBool)
|
||||
f = phoistAcyclic $
|
||||
plam $ \((pfromData . (psndBuiltin #) -> el)) ->
|
||||
let el' :: Term _ (PBuiltinList _)
|
||||
el' = pto el
|
||||
in pnull # el'
|
||||
in pfromData $ pfstBuiltin #$ mustBePJust # "No neutral option" #$ pfind # f # l
|
||||
|
|
|
|||
|
|
@ -13,11 +13,18 @@ module Agora.Proposal.Scripts (
|
|||
import Agora.Proposal (
|
||||
PProposalDatum (PProposalDatum),
|
||||
PProposalRedeemer (..),
|
||||
PProposalStatus (..),
|
||||
PProposalVotes (PProposalVotes),
|
||||
Proposal (governorSTAssetClass, stakeSTAssetClass),
|
||||
ProposalStatus (VotingReady),
|
||||
ProposalStatus (..),
|
||||
)
|
||||
import Agora.Proposal.Time (
|
||||
currentProposalTime,
|
||||
isDraftPeriod,
|
||||
isExecutionPeriod,
|
||||
isLockingPeriod,
|
||||
isVotingPeriod,
|
||||
)
|
||||
import Agora.Proposal.Time (currentProposalTime, isVotingPeriod)
|
||||
import Agora.Stake (PProposalLock (..), PStakeDatum (..), findStakeOwnedBy)
|
||||
import Agora.Utils (
|
||||
findTxOutByTxOutRef,
|
||||
|
|
@ -368,5 +375,84 @@ proposalValidator proposal =
|
|||
PUnlock _r ->
|
||||
popaque (pconstant ())
|
||||
--------------------------------------------------------------------------
|
||||
PAdvanceProposal _r ->
|
||||
popaque (pconstant ())
|
||||
PAdvanceProposal _r -> unTermCont $ do
|
||||
tcassert "No stake input is allowed" $ spentStakeST #== 0
|
||||
|
||||
currentTime <- tclet $ currentProposalTime # txInfoF.validRange
|
||||
proposalOutStatus <- tclet $ pfield @"status" # proposalOut
|
||||
|
||||
let -- Only the status of proposals should be updated in this case.
|
||||
templateProposalOut =
|
||||
mkRecordConstr
|
||||
PProposalDatum
|
||||
( #proposalId .= proposalF.proposalId
|
||||
.& #effects .= proposalF.effects
|
||||
.& #status .= proposalOutStatus
|
||||
.& #cosigners .= proposalF.cosigners
|
||||
.& #thresholds .= proposalF.thresholds
|
||||
.& #votes .= proposalF.votes
|
||||
.& #timingConfig .= proposalF.timingConfig
|
||||
.& #startingTime .= proposalF.startingTime
|
||||
)
|
||||
|
||||
tcassert "Only status changes in the output proposal" $
|
||||
templateProposalOut #== proposalOut
|
||||
|
||||
inDraftPeriod <- tclet $ isDraftPeriod # proposalF.timingConfig # proposalF.startingTime # currentTime
|
||||
inVotingPeriod <- tclet $ isVotingPeriod # proposalF.timingConfig # proposalF.startingTime # currentTime
|
||||
inLockedPeriod <- tclet $ isLockingPeriod # proposalF.timingConfig # proposalF.startingTime # currentTime
|
||||
inExecutionPeriod <- tclet $ isExecutionPeriod # proposalF.timingConfig # proposalF.startingTime # currentTime
|
||||
|
||||
-- Check the timings.
|
||||
let isFinished = proposalF.status #== pconstantData Finished
|
||||
|
||||
notTooLate = pmatch (pfromData proposalF.status) $ \case
|
||||
PDraft _ -> inDraftPeriod
|
||||
-- Can only advance after the voting period is over.
|
||||
PVotingReady _ -> inLockedPeriod
|
||||
PLocked _ -> inExecutionPeriod
|
||||
_ -> pconstant False
|
||||
|
||||
notTooEarly = pmatch (pfromData proposalF.status) $ \case
|
||||
PVotingReady _ -> pnot # inVotingPeriod
|
||||
PLocked _ -> pnot # inLockedPeriod
|
||||
_ -> pconstant True
|
||||
|
||||
tcassert "Cannot advance ahead of time" notTooEarly
|
||||
tcassert "Finished proposals cannot be advanced" $ pnot # isFinished
|
||||
|
||||
pure $
|
||||
pif
|
||||
notTooLate
|
||||
-- On time: advance to next status.
|
||||
( pmatch (pfromData proposalF.status) $ \case
|
||||
PDraft _ -> unTermCont $ do
|
||||
-- TODO: Perform other necessary checks.
|
||||
|
||||
-- 'Draft' -> 'VotingReady'
|
||||
tcassert "Proposal status set to VotingReady" $
|
||||
proposalOutStatus #== pconstantData VotingReady
|
||||
|
||||
pure $ popaque (pconstant ())
|
||||
PVotingReady _ -> unTermCont $ do
|
||||
-- 'VotingReady' -> 'Locked'
|
||||
tcassert "Proposal status set to Locked" $
|
||||
proposalOutStatus #== pconstantData Locked
|
||||
|
||||
pure $ popaque (pconstant ())
|
||||
PLocked _ -> unTermCont $ do
|
||||
-- 'Locked' -> 'Finished'
|
||||
tcassert "Proposal status set to Finished" $
|
||||
proposalOutStatus #== pconstantData Finished
|
||||
|
||||
-- TODO: Perform other necessary checks.
|
||||
pure $ popaque (pconstant ())
|
||||
_ -> popaque (pconstant ())
|
||||
)
|
||||
-- Too late: failed proposal, status set to 'Finished'.
|
||||
( popaque $
|
||||
ptraceIfFalse "Proposal should fail: not on time" $
|
||||
proposalOutStatus #== pconstantData Finished
|
||||
-- TODO: Should check that the GST is not moved
|
||||
-- if the proposal is in 'Locked' state.
|
||||
)
|
||||
|
|
|
|||
18
bench.csv
18
bench.csv
|
|
@ -2,22 +2,28 @@ name,cpu,mem,size
|
|||
Agora/Effects/Treasury Withdrawal Effect/effect/Simple,340268715,724428,3050
|
||||
Agora/Effects/Treasury Withdrawal Effect/effect/Simple with multiple treasuries ,570029812,1211300,3377
|
||||
Agora/Effects/Treasury Withdrawal Effect/effect/Mixed Assets,502351827,1071087,3242
|
||||
Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/governor validator should pass,103830462,228928,7628
|
||||
Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/governor validator should pass,103830462,228928,7629
|
||||
Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/effect validator should pass,127968605,266935,3358
|
||||
Agora/Stake/policy/stakeCreation,59776675,126049,2116
|
||||
Agora/Stake/validator/stakeDepositWithdraw deposit,276249331,599197,4024
|
||||
Agora/Stake/validator/stakeDepositWithdraw withdraw,276249331,599197,4016
|
||||
Agora/Proposal/policy/proposalCreation,34784356,68894,1523
|
||||
Agora/Proposal/validator/cosignature/proposal,241204796,510319,4812
|
||||
Agora/Proposal/validator/cosignature/proposal,241651391,511819,5644
|
||||
Agora/Proposal/validator/cosignature/stake,186332635,402961,4561
|
||||
Agora/Proposal/validator/voting/proposal,239645722,489368,4820
|
||||
Agora/Proposal/validator/voting/proposal,240181636,491168,5652
|
||||
Agora/Proposal/validator/voting/stake,154223940,328703,4614
|
||||
Agora/Proposal/validator/advancing/successfully advance to next state/Draft -> VotringReady,131365724,260351,5030
|
||||
Agora/Proposal/validator/advancing/successfully advance to next state/VotingReady -> Locked,130643392,258848,5039
|
||||
Agora/Proposal/validator/advancing/successfully advance to next state/Locked -> Finished,132128827,262454,5039
|
||||
Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/Draft -> Finished,129853757,257621,5032
|
||||
Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/VotingReady -> Finished,128636280,254916,5039
|
||||
Agora/Proposal/validator/advancing/successfully advance to failed state: timeout/Locked -> Finished,129626570,257320,5039
|
||||
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,25177457,55883,806
|
||||
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,40266637,88241,900
|
||||
Agora/Treasury/Validator/Positive/Allows for effect changes,37343572,79744,1841
|
||||
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,25177457,55883,806
|
||||
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,40266637,88241,900
|
||||
Agora/Governor/policy/GST minting,57978053,120125,1833
|
||||
Agora/Governor/validator/proposal creation,330344593,681815,8143
|
||||
Agora/Governor/validator/GATs minting,431952116,934409,8266
|
||||
Agora/Governor/validator/mutate governor state,101019422,223202,7685
|
||||
Agora/Governor/validator/proposal creation,330344593,681815,8145
|
||||
Agora/Governor/validator/GATs minting,442720585,955552,8268
|
||||
Agora/Governor/validator/mutate governor state,101019422,223202,7686
|
||||
|
|
|
|||
|
Loading…
Add table
Add a link
Reference in a new issue