Merge pull request #142 from Liqwid-Labs/connor/fix-proposal-creation
Fix proposal creation
This commit is contained in:
commit
cd23a41c41
23 changed files with 1720 additions and 664 deletions
|
|
@ -106,7 +106,7 @@ governorDatumValidProperty =
|
||||||
thres <- genProposalThresholds c
|
thres <- genProposalThresholds c
|
||||||
|
|
||||||
let timing = ProposalTimingConfig 0 0 0 0
|
let timing = ProposalTimingConfig 0 0 0 0
|
||||||
return $ GovernorDatum thres (ProposalId 0) timing (MaxTimeRangeWidth 0)
|
return $ GovernorDatum thres (ProposalId 0) timing (MaxTimeRangeWidth 0) 3
|
||||||
where
|
where
|
||||||
taggedInteger p = Tagged <$> chooseInteger p
|
taggedInteger p = Tagged <$> chooseInteger p
|
||||||
genProposalThresholds :: GovernorDatumCases -> Gen ProposalThresholds
|
genProposalThresholds :: GovernorDatumCases -> Gen ProposalThresholds
|
||||||
|
|
@ -181,6 +181,7 @@ governorMintingProperty =
|
||||||
, nextProposalId = ProposalId 0
|
, nextProposalId = ProposalId 0
|
||||||
, proposalTimings = def
|
, proposalTimings = def
|
||||||
, createProposalTimeRangeMaxWidth = def
|
, createProposalTimeRangeMaxWidth = def
|
||||||
|
, maximumProposalsPerStake = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
gen :: GovernorPolicyCases -> Gen ScriptContext
|
gen :: GovernorPolicyCases -> Gen ScriptContext
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,7 @@ mkEffectTxInfo newGovDatum =
|
||||||
, nextProposalId = ProposalId 0
|
, nextProposalId = ProposalId 0
|
||||||
, proposalTimings = def
|
, proposalTimings = def
|
||||||
, createProposalTimeRangeMaxWidth = def
|
, createProposalTimeRangeMaxWidth = def
|
||||||
|
, maximumProposalsPerStake = 3
|
||||||
}
|
}
|
||||||
governorInputDatum :: Datum
|
governorInputDatum :: Datum
|
||||||
governorInputDatum = Datum $ toBuiltinData governorInputDatum'
|
governorInputDatum = Datum $ toBuiltinData governorInputDatum'
|
||||||
|
|
@ -168,6 +169,7 @@ validNewGovernorDatum =
|
||||||
, nextProposalId = ProposalId 42
|
, nextProposalId = ProposalId 42
|
||||||
, proposalTimings = def
|
, proposalTimings = def
|
||||||
, createProposalTimeRangeMaxWidth = def
|
, createProposalTimeRangeMaxWidth = def
|
||||||
|
, maximumProposalsPerStake = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidNewGovernorDatum :: GovernorDatum
|
invalidNewGovernorDatum :: GovernorDatum
|
||||||
|
|
@ -180,4 +182,5 @@ invalidNewGovernorDatum =
|
||||||
, nextProposalId = ProposalId 42
|
, nextProposalId = ProposalId 42
|
||||||
, proposalTimings = def
|
, proposalTimings = def
|
||||||
, createProposalTimeRangeMaxWidth = def
|
, createProposalTimeRangeMaxWidth = def
|
||||||
|
, maximumProposalsPerStake = 3
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{- |
|
{- |
|
||||||
Module : Spec.Sample.Governor
|
Module : Sample.Governor
|
||||||
Maintainer : connor@mlabs.city
|
Maintainer : connor@mlabs.city
|
||||||
Description: Sample based testing for Governor utxos
|
Description: Sample based testing for Governor utxos
|
||||||
|
|
||||||
|
|
@ -119,6 +119,7 @@ mintGST =
|
||||||
, nextProposalId = ProposalId 0
|
, nextProposalId = ProposalId 0
|
||||||
, proposalTimings = def
|
, proposalTimings = def
|
||||||
, createProposalTimeRangeMaxWidth = def
|
, createProposalTimeRangeMaxWidth = def
|
||||||
|
, maximumProposalsPerStake = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
witness :: ValidatorHash
|
witness :: ValidatorHash
|
||||||
|
|
@ -183,6 +184,7 @@ createProposal =
|
||||||
, nextProposalId = thisProposalId
|
, nextProposalId = thisProposalId
|
||||||
, proposalTimings = def
|
, proposalTimings = def
|
||||||
, createProposalTimeRangeMaxWidth = def
|
, createProposalTimeRangeMaxWidth = def
|
||||||
|
, maximumProposalsPerStake = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
effects =
|
effects =
|
||||||
|
|
@ -216,8 +218,7 @@ createProposal =
|
||||||
|
|
||||||
proposalLocks :: [ProposalLock]
|
proposalLocks :: [ProposalLock]
|
||||||
proposalLocks =
|
proposalLocks =
|
||||||
[ ProposalLock (ResultTag 0) thisProposalId
|
[ Created thisProposalId
|
||||||
, ProposalLock (ResultTag 1) thisProposalId
|
|
||||||
]
|
]
|
||||||
stakeOutputDatum :: StakeDatum
|
stakeOutputDatum :: StakeDatum
|
||||||
stakeOutputDatum = stakeInputDatum {lockedBy = proposalLocks}
|
stakeOutputDatum = stakeInputDatum {lockedBy = proposalLocks}
|
||||||
|
|
@ -304,6 +305,7 @@ mintGATs =
|
||||||
, nextProposalId = ProposalId 5
|
, nextProposalId = ProposalId 5
|
||||||
, proposalTimings = def
|
, proposalTimings = def
|
||||||
, createProposalTimeRangeMaxWidth = def
|
, createProposalTimeRangeMaxWidth = def
|
||||||
|
, maximumProposalsPerStake = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
effects =
|
effects =
|
||||||
|
|
@ -423,6 +425,7 @@ mutateState =
|
||||||
, nextProposalId = ProposalId 5
|
, nextProposalId = ProposalId 5
|
||||||
, proposalTimings = def
|
, proposalTimings = def
|
||||||
, createProposalTimeRangeMaxWidth = def
|
, createProposalTimeRangeMaxWidth = def
|
||||||
|
, maximumProposalsPerStake = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
governorOutputDatum :: GovernorDatum
|
governorOutputDatum :: GovernorDatum
|
||||||
|
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
{- |
|
|
||||||
Module : Sample.Proposal
|
|
||||||
Maintainer : emi@haskell.fyi
|
|
||||||
Description: Sample based testing for Proposal utxos
|
|
||||||
|
|
||||||
This module tests primarily the happy path for Proposal interactions
|
|
||||||
-}
|
|
||||||
module Sample.Proposal (
|
|
||||||
-- * Script contexts
|
|
||||||
proposalCreation,
|
|
||||||
) where
|
|
||||||
|
|
||||||
import Agora.Governor (GovernorDatum (..))
|
|
||||||
import Agora.Proposal (
|
|
||||||
Proposal (..),
|
|
||||||
ProposalDatum (..),
|
|
||||||
ProposalId (..),
|
|
||||||
ProposalStatus (..),
|
|
||||||
ResultTag (..),
|
|
||||||
emptyVotesFor,
|
|
||||||
)
|
|
||||||
import Data.Default.Class (Default (def))
|
|
||||||
import Plutarch.Context (
|
|
||||||
MintingBuilder,
|
|
||||||
buildMintingUnsafe,
|
|
||||||
input,
|
|
||||||
mint,
|
|
||||||
output,
|
|
||||||
script,
|
|
||||||
signedWith,
|
|
||||||
txId,
|
|
||||||
withDatum,
|
|
||||||
withTxId,
|
|
||||||
withValue,
|
|
||||||
)
|
|
||||||
import PlutusLedgerApi.V1 (
|
|
||||||
ScriptContext (..),
|
|
||||||
)
|
|
||||||
import PlutusLedgerApi.V1.Value qualified as Value (
|
|
||||||
assetClassValue,
|
|
||||||
singleton,
|
|
||||||
)
|
|
||||||
import PlutusTx.AssocMap qualified as AssocMap
|
|
||||||
import Sample.Shared (
|
|
||||||
govValidatorHash,
|
|
||||||
proposal,
|
|
||||||
proposalPolicySymbol,
|
|
||||||
proposalStartingTimeFromTimeRange,
|
|
||||||
proposalValidatorHash,
|
|
||||||
signer,
|
|
||||||
)
|
|
||||||
import Test.Util (
|
|
||||||
closedBoundedInterval,
|
|
||||||
)
|
|
||||||
|
|
||||||
proposalCreation :: ScriptContext
|
|
||||||
proposalCreation =
|
|
||||||
let st = Value.singleton proposalPolicySymbol "" 1 -- Proposal ST
|
|
||||||
effects =
|
|
||||||
AssocMap.fromList
|
|
||||||
[ (ResultTag 0, AssocMap.empty)
|
|
||||||
, (ResultTag 1, AssocMap.empty)
|
|
||||||
]
|
|
||||||
proposalDatum :: ProposalDatum
|
|
||||||
proposalDatum =
|
|
||||||
ProposalDatum
|
|
||||||
{ proposalId = ProposalId 0
|
|
||||||
, effects = effects
|
|
||||||
, status = Draft
|
|
||||||
, cosigners = [signer]
|
|
||||||
, thresholds = def
|
|
||||||
, votes = emptyVotesFor effects
|
|
||||||
, timingConfig = def
|
|
||||||
, startingTime = proposalStartingTimeFromTimeRange validTimeRange
|
|
||||||
}
|
|
||||||
|
|
||||||
govBefore :: GovernorDatum
|
|
||||||
govBefore =
|
|
||||||
GovernorDatum
|
|
||||||
{ proposalThresholds = def
|
|
||||||
, nextProposalId = ProposalId 0
|
|
||||||
, proposalTimings = def
|
|
||||||
, createProposalTimeRangeMaxWidth = def
|
|
||||||
}
|
|
||||||
|
|
||||||
govAfter :: GovernorDatum
|
|
||||||
govAfter = govBefore {nextProposalId = ProposalId 1}
|
|
||||||
|
|
||||||
validTimeRange = closedBoundedInterval 10 15
|
|
||||||
|
|
||||||
builder :: MintingBuilder
|
|
||||||
builder =
|
|
||||||
mconcat
|
|
||||||
[ txId "0b2086cbf8b6900f8cb65e012de4516cb66b5cb08a9aaba12a8b88be"
|
|
||||||
, signedWith signer
|
|
||||||
, mint st
|
|
||||||
, input $
|
|
||||||
script govValidatorHash
|
|
||||||
. withValue (Value.assetClassValue proposal.governorSTAssetClass 1)
|
|
||||||
. withDatum govBefore
|
|
||||||
. withTxId "0b2086cbf8b6900f8cb65e012de4516cb66b5cb08a9aaba12a8b88be"
|
|
||||||
, output $
|
|
||||||
script proposalValidatorHash
|
|
||||||
. withValue (st <> Value.singleton "" "" 10_000_000)
|
|
||||||
. withDatum proposalDatum
|
|
||||||
, output $
|
|
||||||
script govValidatorHash
|
|
||||||
. withValue
|
|
||||||
( Value.assetClassValue proposal.governorSTAssetClass 1
|
|
||||||
<> Value.singleton "" "" 10_000_000
|
|
||||||
)
|
|
||||||
. withDatum govAfter
|
|
||||||
]
|
|
||||||
in buildMintingUnsafe builder
|
|
||||||
|
|
@ -1,3 +1,10 @@
|
||||||
|
{- |
|
||||||
|
Module : Sample.Proposal.Advance
|
||||||
|
Maintainer : connor@mlabs.city
|
||||||
|
Description: Generate sample data for testing the functionalities of advancing proposals
|
||||||
|
|
||||||
|
Sample and utilities for testing the functionalities of advancing proposals.
|
||||||
|
-}
|
||||||
module Sample.Proposal.Advance (
|
module Sample.Proposal.Advance (
|
||||||
advanceToNextStateInTimeParameters,
|
advanceToNextStateInTimeParameters,
|
||||||
advanceToFailedStateDueToTimeoutParameters,
|
advanceToFailedStateDueToTimeoutParameters,
|
||||||
|
|
@ -31,7 +38,7 @@ import Agora.Proposal.Time (
|
||||||
)
|
)
|
||||||
import Agora.SafeMoney (GTTag)
|
import Agora.SafeMoney (GTTag)
|
||||||
import Agora.Stake (
|
import Agora.Stake (
|
||||||
ProposalLock (ProposalLock),
|
ProposalLock (..),
|
||||||
Stake (gtClassRef),
|
Stake (gtClassRef),
|
||||||
StakeDatum (..),
|
StakeDatum (..),
|
||||||
StakeRedeemer (WitnessStake),
|
StakeRedeemer (WitnessStake),
|
||||||
|
|
@ -69,7 +76,7 @@ import PlutusLedgerApi.V1 (
|
||||||
)
|
)
|
||||||
import PlutusLedgerApi.V1.Value qualified as Value
|
import PlutusLedgerApi.V1.Value qualified as Value
|
||||||
import PlutusTx.AssocMap qualified as AssocMap
|
import PlutusTx.AssocMap qualified as AssocMap
|
||||||
import Sample.Proposal.Shared (proposalTxRef, stakeTxRef, testFunc)
|
import Sample.Proposal.Shared (proposalTxRef, stakeTxRef)
|
||||||
import Sample.Shared (
|
import Sample.Shared (
|
||||||
minAda,
|
minAda,
|
||||||
proposalPolicySymbol,
|
proposalPolicySymbol,
|
||||||
|
|
@ -79,7 +86,7 @@ import Sample.Shared (
|
||||||
stakeValidatorHash,
|
stakeValidatorHash,
|
||||||
)
|
)
|
||||||
import Sample.Shared qualified as Shared
|
import Sample.Shared qualified as Shared
|
||||||
import Test.Specification (SpecificationTree, group)
|
import Test.Specification (SpecificationTree, group, testValidator)
|
||||||
import Test.Util (closedBoundedInterval, pubKeyHashes, sortValue, updateMap)
|
import Test.Util (closedBoundedInterval, pubKeyHashes, sortValue, updateMap)
|
||||||
|
|
||||||
-- | Parameters for state transition of proposals.
|
-- | Parameters for state transition of proposals.
|
||||||
|
|
@ -99,19 +106,24 @@ data Parameters = Parameters
|
||||||
, stakeCount :: Integer
|
, stakeCount :: Integer
|
||||||
-- ^ The number of stakes.
|
-- ^ The number of stakes.
|
||||||
, signByAllCosigners :: Bool
|
, signByAllCosigners :: Bool
|
||||||
|
-- ^ Whether the transaction is signed by all the cosigners.
|
||||||
, perStakeGTs :: Tagged GTTag Integer
|
, perStakeGTs :: Tagged GTTag Integer
|
||||||
|
-- ^ The staked amount of each stake.
|
||||||
}
|
}
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
-- | Reference to the proposal UTXO.
|
||||||
proposalRef :: TxOutRef
|
proposalRef :: TxOutRef
|
||||||
proposalRef = TxOutRef proposalTxRef 1
|
proposalRef = TxOutRef proposalTxRef 1
|
||||||
|
|
||||||
|
-- | Create the reference to a particular stake UTXO.
|
||||||
mkStakeRef :: Int -> TxOutRef
|
mkStakeRef :: Int -> TxOutRef
|
||||||
mkStakeRef = TxOutRef stakeTxRef . (+ 2) . fromIntegral
|
mkStakeRef = TxOutRef stakeTxRef . (+ 2) . fromIntegral
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
-- | Default effects of the propsoal.
|
||||||
defEffects :: AssocMap.Map ResultTag (AssocMap.Map ValidatorHash DatumHash)
|
defEffects :: AssocMap.Map ResultTag (AssocMap.Map ValidatorHash DatumHash)
|
||||||
defEffects =
|
defEffects =
|
||||||
AssocMap.fromList
|
AssocMap.fromList
|
||||||
|
|
@ -119,14 +131,19 @@ defEffects =
|
||||||
, (ResultTag 1, AssocMap.empty)
|
, (ResultTag 1, AssocMap.empty)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
-- | Empty votes for the default effects.
|
||||||
emptyVotes :: ProposalVotes
|
emptyVotes :: ProposalVotes
|
||||||
emptyVotes = emptyVotesFor defEffects
|
emptyVotes = emptyVotesFor defEffects
|
||||||
|
|
||||||
|
{- | The default proposal statring time, which doesn't really matter in this
|
||||||
|
case.
|
||||||
|
-}
|
||||||
proposalStartingTime :: POSIXTime
|
proposalStartingTime :: POSIXTime
|
||||||
proposalStartingTime = 0
|
proposalStartingTime = 0
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
-- | Create the input proposal datum given the parameters.
|
||||||
mkProposalInputDatum :: Parameters -> ProposalDatum
|
mkProposalInputDatum :: Parameters -> ProposalDatum
|
||||||
mkProposalInputDatum ps =
|
mkProposalInputDatum ps =
|
||||||
ProposalDatum
|
ProposalDatum
|
||||||
|
|
@ -140,6 +157,7 @@ mkProposalInputDatum ps =
|
||||||
, startingTime = ProposalStartingTime proposalStartingTime
|
, startingTime = ProposalStartingTime proposalStartingTime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- | Create the input stake datums given the parameters.
|
||||||
mkStakeInputDatums :: Parameters -> [StakeDatum]
|
mkStakeInputDatums :: Parameters -> [StakeDatum]
|
||||||
mkStakeInputDatums ps =
|
mkStakeInputDatums ps =
|
||||||
map
|
map
|
||||||
|
|
@ -154,28 +172,37 @@ mkStakeInputDatums ps =
|
||||||
where
|
where
|
||||||
existingLocks :: [ProposalLock]
|
existingLocks :: [ProposalLock]
|
||||||
existingLocks =
|
existingLocks =
|
||||||
[ ProposalLock (ResultTag 0) (ProposalId 0)
|
[ Voted (ProposalId 0) (ResultTag 0)
|
||||||
, ProposalLock (ResultTag 2) (ProposalId 1)
|
, Voted (ProposalId 1) (ResultTag 2)
|
||||||
]
|
]
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
-- | Script purpose of the proposal validator.
|
||||||
proposalScriptPurpose :: ScriptPurpose
|
proposalScriptPurpose :: ScriptPurpose
|
||||||
proposalScriptPurpose = Spending proposalRef
|
proposalScriptPurpose = Spending proposalRef
|
||||||
|
|
||||||
|
-- | Script purpose of the stake validator, given which stake we want to spend.
|
||||||
mkStakeScriptPurpose :: Int -> ScriptPurpose
|
mkStakeScriptPurpose :: Int -> ScriptPurpose
|
||||||
mkStakeScriptPurpose = Spending . mkStakeRef
|
mkStakeScriptPurpose = Spending . mkStakeRef
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{- | The propsoal redeemer used to spend the proposal UTXO, which is always
|
||||||
|
'AdvanceProposal' in this case.
|
||||||
|
-}
|
||||||
proposalRedeemer :: ProposalRedeemer
|
proposalRedeemer :: ProposalRedeemer
|
||||||
proposalRedeemer = AdvanceProposal
|
proposalRedeemer = AdvanceProposal
|
||||||
|
|
||||||
|
{- | The propsoal redeemer used to spend the stake UTXO, which is always
|
||||||
|
'WitnessStake' in this case.
|
||||||
|
-}
|
||||||
stakeRedeemer :: StakeRedeemer
|
stakeRedeemer :: StakeRedeemer
|
||||||
stakeRedeemer = WitnessStake
|
stakeRedeemer = WitnessStake
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
-- | Create some valid stake owners.
|
||||||
mkStakeOwners :: Parameters -> [PubKeyHash]
|
mkStakeOwners :: Parameters -> [PubKeyHash]
|
||||||
mkStakeOwners ps =
|
mkStakeOwners ps =
|
||||||
sort $
|
sort $
|
||||||
|
|
@ -276,6 +303,9 @@ advance ps =
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{- | Given the proposal status, create a time range that is in time for
|
||||||
|
advacing to the next state.
|
||||||
|
-}
|
||||||
mkInTimeTimeRange :: ProposalStatus -> POSIXTimeRange
|
mkInTimeTimeRange :: ProposalStatus -> POSIXTimeRange
|
||||||
mkInTimeTimeRange advanceFrom =
|
mkInTimeTimeRange advanceFrom =
|
||||||
case advanceFrom of
|
case advanceFrom of
|
||||||
|
|
@ -315,6 +345,9 @@ mkInTimeTimeRange advanceFrom =
|
||||||
)
|
)
|
||||||
Finished -> error "Cannot advance 'Finished' proposal"
|
Finished -> error "Cannot advance 'Finished' proposal"
|
||||||
|
|
||||||
|
{- | Given the proposal status, create a time range that is too time for
|
||||||
|
advacing to the next state.
|
||||||
|
-}
|
||||||
mkTooLateTimeRange :: ProposalStatus -> POSIXTimeRange
|
mkTooLateTimeRange :: ProposalStatus -> POSIXTimeRange
|
||||||
mkTooLateTimeRange advanceFrom =
|
mkTooLateTimeRange advanceFrom =
|
||||||
case advanceFrom of
|
case advanceFrom of
|
||||||
|
|
@ -363,6 +396,7 @@ mkTooLateTimeRange advanceFrom =
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
-- | Next state of the given proposal status.
|
||||||
getNextState :: ProposalStatus -> ProposalStatus
|
getNextState :: ProposalStatus -> ProposalStatus
|
||||||
getNextState = \case
|
getNextState = \case
|
||||||
Draft -> VotingReady
|
Draft -> VotingReady
|
||||||
|
|
@ -475,6 +509,9 @@ invalidOutputStakeParameters nCosigners =
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{- | Create a test tree that runs the stake validator and proposal validator to
|
||||||
|
test the advancing functionalities.
|
||||||
|
-}
|
||||||
mkTestTree :: String -> Parameters -> Bool -> SpecificationTree
|
mkTestTree :: String -> Parameters -> Bool -> SpecificationTree
|
||||||
mkTestTree name ps isValidForProposalValidator = group name [proposal, stake]
|
mkTestTree name ps isValidForProposalValidator = group name [proposal, stake]
|
||||||
where
|
where
|
||||||
|
|
@ -482,7 +519,7 @@ mkTestTree name ps isValidForProposalValidator = group name [proposal, stake]
|
||||||
|
|
||||||
proposal =
|
proposal =
|
||||||
let proposalInputDatum = mkProposalInputDatum ps
|
let proposalInputDatum = mkProposalInputDatum ps
|
||||||
in testFunc
|
in testValidator
|
||||||
isValidForProposalValidator
|
isValidForProposalValidator
|
||||||
"propsoal"
|
"propsoal"
|
||||||
(proposalValidator Shared.proposal)
|
(proposalValidator Shared.proposal)
|
||||||
|
|
@ -497,7 +534,7 @@ mkTestTree name ps isValidForProposalValidator = group name [proposal, stake]
|
||||||
let idx = 0
|
let idx = 0
|
||||||
stakeInputDatum = mkStakeInputDatums ps !! idx
|
stakeInputDatum = mkStakeInputDatums ps !! idx
|
||||||
isValid = not $ ps.alterOutputStakes
|
isValid = not $ ps.alterOutputStakes
|
||||||
in testFunc
|
in testValidator
|
||||||
isValid
|
isValid
|
||||||
"stake"
|
"stake"
|
||||||
(stakeValidator Shared.stake)
|
(stakeValidator Shared.stake)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,10 @@
|
||||||
|
{- |
|
||||||
|
Module : Sample.Proposal.Cosign
|
||||||
|
Maintainer : connor@mlabs.city
|
||||||
|
Description: Generate sample data for testing the functionalities of cosigning proposals
|
||||||
|
|
||||||
|
Sample and utilities for testing the functionalities of cosigning proposals.
|
||||||
|
-}
|
||||||
module Sample.Proposal.Cosign (
|
module Sample.Proposal.Cosign (
|
||||||
Parameters (..),
|
Parameters (..),
|
||||||
validCosignNParameters,
|
validCosignNParameters,
|
||||||
|
|
@ -57,7 +64,7 @@ import PlutusLedgerApi.V1 (
|
||||||
)
|
)
|
||||||
import PlutusLedgerApi.V1.Value qualified as Value
|
import PlutusLedgerApi.V1.Value qualified as Value
|
||||||
import PlutusTx.AssocMap qualified as AssocMap
|
import PlutusTx.AssocMap qualified as AssocMap
|
||||||
import Sample.Proposal.Shared (proposalTxRef, stakeTxRef, testFunc)
|
import Sample.Proposal.Shared (proposalTxRef, stakeTxRef)
|
||||||
import Sample.Shared (
|
import Sample.Shared (
|
||||||
minAda,
|
minAda,
|
||||||
proposalPolicySymbol,
|
proposalPolicySymbol,
|
||||||
|
|
@ -71,6 +78,7 @@ import Sample.Shared qualified as Shared
|
||||||
import Test.Specification (
|
import Test.Specification (
|
||||||
SpecificationTree,
|
SpecificationTree,
|
||||||
group,
|
group,
|
||||||
|
testValidator,
|
||||||
)
|
)
|
||||||
import Test.Util (closedBoundedInterval, pubKeyHashes, sortValue)
|
import Test.Util (closedBoundedInterval, pubKeyHashes, sortValue)
|
||||||
|
|
||||||
|
|
@ -317,7 +325,7 @@ mkTestTree name ps isValid = group name [proposal, stake]
|
||||||
|
|
||||||
proposal =
|
proposal =
|
||||||
let proposalInputDatum = mkProposalInputDatum ps
|
let proposalInputDatum = mkProposalInputDatum ps
|
||||||
in testFunc
|
in testValidator
|
||||||
isValid
|
isValid
|
||||||
"propsoal"
|
"propsoal"
|
||||||
(proposalValidator Shared.proposal)
|
(proposalValidator Shared.proposal)
|
||||||
|
|
@ -332,7 +340,7 @@ mkTestTree name ps isValid = group name [proposal, stake]
|
||||||
let idx = 0
|
let idx = 0
|
||||||
stakeInputDatum = mkStakeInputDatums ps !! idx
|
stakeInputDatum = mkStakeInputDatums ps !! idx
|
||||||
isValid = not ps.alterOutputStakes
|
isValid = not ps.alterOutputStakes
|
||||||
in testFunc
|
in testValidator
|
||||||
isValid
|
isValid
|
||||||
"stake"
|
"stake"
|
||||||
(stakeValidator Shared.stake)
|
(stakeValidator Shared.stake)
|
||||||
|
|
|
||||||
461
agora-specs/Sample/Proposal/Create.hs
Normal file
461
agora-specs/Sample/Proposal/Create.hs
Normal file
|
|
@ -0,0 +1,461 @@
|
||||||
|
{- |
|
||||||
|
Module : Sample.Proposal.Create
|
||||||
|
Maintainer : connor@mlabs.city
|
||||||
|
Description: Generate sample data for testing the functionalities of creating proposals
|
||||||
|
|
||||||
|
Sample and utilities for testing the functionalities of creating proposals.
|
||||||
|
-}
|
||||||
|
module Sample.Proposal.Create (
|
||||||
|
Parameters (..),
|
||||||
|
mkTestTree,
|
||||||
|
totallyValidParameters,
|
||||||
|
invalidOutputGovernorDatumParameters,
|
||||||
|
useStakeOwnBySomeoneElseParameters,
|
||||||
|
invalidOutputStakeParameters,
|
||||||
|
addInvalidLocksParameters,
|
||||||
|
exceedMaximumProposalsParameters,
|
||||||
|
timeRangeNotTightParameters,
|
||||||
|
timeRangeNotClosedParameters,
|
||||||
|
invalidProposalStatusParameters,
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Agora.Governor (
|
||||||
|
GovernorDatum (..),
|
||||||
|
GovernorRedeemer (CreateProposal),
|
||||||
|
)
|
||||||
|
import Agora.Governor.Scripts (governorValidator)
|
||||||
|
import Agora.Proposal (
|
||||||
|
Proposal (governorSTAssetClass),
|
||||||
|
ProposalDatum (..),
|
||||||
|
ProposalId (ProposalId),
|
||||||
|
ProposalStatus (..),
|
||||||
|
ResultTag (ResultTag),
|
||||||
|
emptyVotesFor,
|
||||||
|
)
|
||||||
|
import Agora.Proposal.Scripts (proposalPolicy)
|
||||||
|
import Agora.Proposal.Time (MaxTimeRangeWidth (MaxTimeRangeWidth), ProposalStartingTime (..))
|
||||||
|
import Agora.Stake (
|
||||||
|
ProposalLock (..),
|
||||||
|
Stake (gtClassRef),
|
||||||
|
StakeDatum (..),
|
||||||
|
StakeRedeemer (PermitVote),
|
||||||
|
)
|
||||||
|
import Agora.Stake.Scripts (stakeValidator)
|
||||||
|
import Data.Coerce (coerce)
|
||||||
|
import Data.Default (Default (def))
|
||||||
|
import Data.Tagged (Tagged, untag)
|
||||||
|
import Plutarch.Context (
|
||||||
|
BaseBuilder,
|
||||||
|
buildTxInfoUnsafe,
|
||||||
|
input,
|
||||||
|
mint,
|
||||||
|
output,
|
||||||
|
script,
|
||||||
|
signedWith,
|
||||||
|
timeRange,
|
||||||
|
txId,
|
||||||
|
withDatum,
|
||||||
|
withOutRef,
|
||||||
|
withValue,
|
||||||
|
)
|
||||||
|
import PlutusLedgerApi.V1 (
|
||||||
|
DatumHash,
|
||||||
|
POSIXTime (POSIXTime),
|
||||||
|
POSIXTimeRange,
|
||||||
|
PubKeyHash,
|
||||||
|
ScriptContext (ScriptContext),
|
||||||
|
ScriptPurpose (Minting, Spending),
|
||||||
|
TxInfo,
|
||||||
|
TxOutRef (TxOutRef),
|
||||||
|
ValidatorHash,
|
||||||
|
always,
|
||||||
|
)
|
||||||
|
import PlutusLedgerApi.V1.Value qualified as Value
|
||||||
|
import PlutusTx.AssocMap qualified as AssocMap
|
||||||
|
import Sample.Proposal.Shared (stakeTxRef)
|
||||||
|
import Sample.Shared (
|
||||||
|
govValidatorHash,
|
||||||
|
minAda,
|
||||||
|
proposal,
|
||||||
|
proposalPolicySymbol,
|
||||||
|
proposalStartingTimeFromTimeRange,
|
||||||
|
proposalValidatorHash,
|
||||||
|
signer,
|
||||||
|
signer2,
|
||||||
|
stake,
|
||||||
|
stakeAssetClass,
|
||||||
|
stakeValidatorHash,
|
||||||
|
)
|
||||||
|
import Sample.Shared qualified as Shared
|
||||||
|
import Test.Specification (SpecificationTree, group, testPolicy, testValidator)
|
||||||
|
import Test.Util (closedBoundedInterval, sortValue)
|
||||||
|
|
||||||
|
-- | Parameters for creating a proposal.
|
||||||
|
data Parameters = Parameters
|
||||||
|
{ advanceNextProposalId :: Bool
|
||||||
|
-- ^ Whether to advance 'GovernorDatum.nextProposalId'.
|
||||||
|
, createdMoreThanMaximumProposals :: Bool
|
||||||
|
-- ^ Try creating more than maximum amount of proposals.
|
||||||
|
, stakeOwnerSignsTheTransaction :: Bool
|
||||||
|
-- ^ Should the stake owner sign the transaction?
|
||||||
|
, invalidNewLocks :: Bool
|
||||||
|
-- ^ Place invalid new locks on the output stake.
|
||||||
|
, alterOutputStakeOwner :: Bool
|
||||||
|
-- ^ Whether to change the 'owner' field of the output stake datum.
|
||||||
|
, timeRangeTightEnough :: Bool
|
||||||
|
-- ^ Is 'TxInfo.validTimeRange' tight enough?
|
||||||
|
, timeRangeClosed :: Bool
|
||||||
|
-- ^ Is 'TxInfo.validTimeRange' closed?
|
||||||
|
, proposalStatus :: ProposalStatus
|
||||||
|
-- ^ The status of the newly created proposal.
|
||||||
|
}
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | See 'GovernorDatum.maximumProposalsPerStake'.
|
||||||
|
maxProposalPerStake :: Integer
|
||||||
|
maxProposalPerStake = 3
|
||||||
|
|
||||||
|
-- | The id of the proposal we are creating.
|
||||||
|
thisProposalId :: ProposalId
|
||||||
|
thisProposalId = ProposalId 25
|
||||||
|
|
||||||
|
-- | The arbitrary staked amount. Doesn;t really matter in this case.
|
||||||
|
stakedGTs :: Tagged _ Integer
|
||||||
|
stakedGTs = 5
|
||||||
|
|
||||||
|
-- | The owner of the stake.
|
||||||
|
stakeOwner :: PubKeyHash
|
||||||
|
stakeOwner = signer
|
||||||
|
|
||||||
|
{- | The invalid stake owner. If the 'alterOutputStakeOwner' is set to true,
|
||||||
|
the output stake owner will be set to this.
|
||||||
|
-}
|
||||||
|
alteredStakeOwner :: PubKeyHash
|
||||||
|
alteredStakeOwner = signer2
|
||||||
|
|
||||||
|
-- | Locks the stake that the input stake already has.
|
||||||
|
defLocks :: [ProposalLock]
|
||||||
|
defLocks = [Created (ProposalId 0)]
|
||||||
|
|
||||||
|
-- | The effect of the newly created proposal.
|
||||||
|
defEffects :: AssocMap.Map ResultTag (AssocMap.Map ValidatorHash DatumHash)
|
||||||
|
defEffects =
|
||||||
|
AssocMap.fromList
|
||||||
|
[ (ResultTag 0, AssocMap.empty)
|
||||||
|
, (ResultTag 1, AssocMap.empty)
|
||||||
|
]
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | The governor input datum.
|
||||||
|
governorInputDatum :: GovernorDatum
|
||||||
|
governorInputDatum =
|
||||||
|
GovernorDatum
|
||||||
|
{ proposalThresholds = def
|
||||||
|
, nextProposalId = thisProposalId
|
||||||
|
, proposalTimings = def
|
||||||
|
, createProposalTimeRangeMaxWidth = def
|
||||||
|
, maximumProposalsPerStake = maxProposalPerStake
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | Create governor output datum given the parameters.
|
||||||
|
mkGovernorOutputDatum :: Parameters -> GovernorDatum
|
||||||
|
mkGovernorOutputDatum ps =
|
||||||
|
let nextPid =
|
||||||
|
if ps.advanceNextProposalId
|
||||||
|
then ProposalId $ coerce thisProposalId + 1
|
||||||
|
else thisProposalId
|
||||||
|
in GovernorDatum
|
||||||
|
{ proposalThresholds = def
|
||||||
|
, nextProposalId = nextPid
|
||||||
|
, proposalTimings = def
|
||||||
|
, createProposalTimeRangeMaxWidth = def
|
||||||
|
, maximumProposalsPerStake = maxProposalPerStake
|
||||||
|
}
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | Create the stake input datum given the parameters.
|
||||||
|
mkStakeInputDatum :: Parameters -> StakeDatum
|
||||||
|
mkStakeInputDatum ps =
|
||||||
|
let locks =
|
||||||
|
if ps.createdMoreThanMaximumProposals
|
||||||
|
then
|
||||||
|
Created . ProposalId
|
||||||
|
<$> take
|
||||||
|
(fromInteger maxProposalPerStake)
|
||||||
|
[1 ..]
|
||||||
|
else defLocks
|
||||||
|
in StakeDatum
|
||||||
|
{ stakedAmount = stakedGTs
|
||||||
|
, owner = stakeOwner
|
||||||
|
, lockedBy = locks
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | Create the stake output datum given the parameters.
|
||||||
|
mkStakeOutputDatum :: Parameters -> StakeDatum
|
||||||
|
mkStakeOutputDatum ps =
|
||||||
|
let inputDatum = mkStakeInputDatum ps
|
||||||
|
newLocks =
|
||||||
|
if ps.invalidNewLocks
|
||||||
|
then
|
||||||
|
[ Voted thisProposalId (ResultTag 0)
|
||||||
|
, Voted thisProposalId (ResultTag 1)
|
||||||
|
]
|
||||||
|
else [Created thisProposalId]
|
||||||
|
locks = newLocks <> inputDatum.lockedBy
|
||||||
|
newOwner = mkOwner ps
|
||||||
|
in inputDatum
|
||||||
|
{ owner = newOwner
|
||||||
|
, lockedBy = locks
|
||||||
|
}
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
{- | Create the proposal datum for the newly created proposal, given the
|
||||||
|
parameters.
|
||||||
|
-}
|
||||||
|
mkProposalOutputDatum :: Parameters -> ProposalDatum
|
||||||
|
mkProposalOutputDatum ps =
|
||||||
|
ProposalDatum
|
||||||
|
{ proposalId = thisProposalId
|
||||||
|
, effects = defEffects
|
||||||
|
, status = ps.proposalStatus
|
||||||
|
, cosigners = [mkOwner ps]
|
||||||
|
, thresholds = def
|
||||||
|
, votes = emptyVotesFor defEffects
|
||||||
|
, timingConfig = def
|
||||||
|
, startingTime = mkProposalStartingTime ps
|
||||||
|
}
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | Create time range for 'TxInfo.validTimeRange'.
|
||||||
|
mkTimeRange :: Parameters -> POSIXTimeRange
|
||||||
|
mkTimeRange ps =
|
||||||
|
if ps.timeRangeClosed
|
||||||
|
then
|
||||||
|
let s = 0
|
||||||
|
di :: POSIXTime = coerce (def @MaxTimeRangeWidth)
|
||||||
|
o = if ps.timeRangeTightEnough then (-1) else 1
|
||||||
|
in closedBoundedInterval s $ o + di
|
||||||
|
else always
|
||||||
|
|
||||||
|
-- | Get the starting time of the propsoal.
|
||||||
|
mkProposalStartingTime :: Parameters -> ProposalStartingTime
|
||||||
|
mkProposalStartingTime ps =
|
||||||
|
if ps.timeRangeClosed
|
||||||
|
then proposalStartingTimeFromTimeRange $ mkTimeRange ps
|
||||||
|
else ProposalStartingTime 0
|
||||||
|
|
||||||
|
-- | Who should be the 'owner' of the output stake.
|
||||||
|
mkOwner :: Parameters -> PubKeyHash
|
||||||
|
mkOwner ps =
|
||||||
|
if ps.alterOutputStakeOwner
|
||||||
|
then alteredStakeOwner
|
||||||
|
else stakeOwner
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | Reference to the input stake UTXO.
|
||||||
|
stakeRef :: TxOutRef
|
||||||
|
stakeRef = TxOutRef stakeTxRef 1
|
||||||
|
|
||||||
|
-- | Reference to the input governor UTXO.
|
||||||
|
governorRef :: TxOutRef
|
||||||
|
governorRef = TxOutRef "0b2086cbf8b6900f8cb65e012de4516cb66b5cb08a9aaba12a8b88be" 3
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | Create a 'TxInfo' that spends a stake to create a new proposal.
|
||||||
|
createProposal :: Parameters -> TxInfo
|
||||||
|
createProposal ps = buildTxInfoUnsafe builder
|
||||||
|
where
|
||||||
|
pst = Value.singleton proposalPolicySymbol "" 1
|
||||||
|
sst = Value.assetClassValue stakeAssetClass 1
|
||||||
|
gst = Value.assetClassValue proposal.governorSTAssetClass 1
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
governorValue = sortValue $ gst <> minAda
|
||||||
|
stakeValue =
|
||||||
|
sortValue $
|
||||||
|
sortValue $
|
||||||
|
sst
|
||||||
|
<> Value.assetClassValue (untag stake.gtClassRef) (untag stakedGTs)
|
||||||
|
<> minAda
|
||||||
|
proposalValue = sortValue $ pst <> minAda
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
withSig =
|
||||||
|
if ps.stakeOwnerSignsTheTransaction
|
||||||
|
then signedWith stakeOwner
|
||||||
|
else mempty
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
builder :: BaseBuilder
|
||||||
|
builder =
|
||||||
|
mconcat
|
||||||
|
[ txId "0b2086cbf8b6900f8cb65e012de4516cb66b5cb08a9aaba12a8b88be"
|
||||||
|
, ---
|
||||||
|
withSig
|
||||||
|
, ---
|
||||||
|
mint pst
|
||||||
|
, ---
|
||||||
|
timeRange $ mkTimeRange ps
|
||||||
|
, input $
|
||||||
|
script govValidatorHash
|
||||||
|
. withValue governorValue
|
||||||
|
. withDatum governorInputDatum
|
||||||
|
. withOutRef governorRef
|
||||||
|
, output $
|
||||||
|
script govValidatorHash
|
||||||
|
. withValue governorValue
|
||||||
|
. withDatum (mkGovernorOutputDatum ps)
|
||||||
|
, ---
|
||||||
|
input $
|
||||||
|
script stakeValidatorHash
|
||||||
|
. withValue stakeValue
|
||||||
|
. withDatum (mkStakeInputDatum ps)
|
||||||
|
. withOutRef stakeRef
|
||||||
|
, output $
|
||||||
|
script stakeValidatorHash
|
||||||
|
. withValue stakeValue
|
||||||
|
. withDatum (mkStakeOutputDatum ps)
|
||||||
|
, ---
|
||||||
|
output $
|
||||||
|
script proposalValidatorHash
|
||||||
|
. withValue proposalValue
|
||||||
|
. withDatum (mkProposalOutputDatum ps)
|
||||||
|
]
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | Spend the stake with the 'PermitVote' redeemer.
|
||||||
|
stakeRedeemer :: StakeRedeemer
|
||||||
|
stakeRedeemer = PermitVote
|
||||||
|
|
||||||
|
-- | Spend the governor with the 'CreateProposal' redeemer.
|
||||||
|
governorRedeemer :: GovernorRedeemer
|
||||||
|
governorRedeemer = CreateProposal
|
||||||
|
|
||||||
|
-- | Mint the PST with an arbitrary redeemer. Doesn't really matter.
|
||||||
|
proposalPolicyRedeemer :: ()
|
||||||
|
proposalPolicyRedeemer = ()
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
totallyValidParameters :: Parameters
|
||||||
|
totallyValidParameters =
|
||||||
|
Parameters
|
||||||
|
{ advanceNextProposalId = True
|
||||||
|
, createdMoreThanMaximumProposals = False
|
||||||
|
, stakeOwnerSignsTheTransaction = True
|
||||||
|
, invalidNewLocks = False
|
||||||
|
, alterOutputStakeOwner = False
|
||||||
|
, timeRangeTightEnough = True
|
||||||
|
, timeRangeClosed = True
|
||||||
|
, proposalStatus = Draft
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidOutputGovernorDatumParameters :: Parameters
|
||||||
|
invalidOutputGovernorDatumParameters =
|
||||||
|
totallyValidParameters
|
||||||
|
{ advanceNextProposalId = False
|
||||||
|
}
|
||||||
|
|
||||||
|
useStakeOwnBySomeoneElseParameters :: Parameters
|
||||||
|
useStakeOwnBySomeoneElseParameters =
|
||||||
|
totallyValidParameters
|
||||||
|
{ stakeOwnerSignsTheTransaction = False
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidOutputStakeParameters :: Parameters
|
||||||
|
invalidOutputStakeParameters =
|
||||||
|
totallyValidParameters
|
||||||
|
{ alterOutputStakeOwner = True
|
||||||
|
}
|
||||||
|
|
||||||
|
addInvalidLocksParameters :: Parameters
|
||||||
|
addInvalidLocksParameters =
|
||||||
|
totallyValidParameters
|
||||||
|
{ invalidNewLocks = True
|
||||||
|
}
|
||||||
|
|
||||||
|
exceedMaximumProposalsParameters :: Parameters
|
||||||
|
exceedMaximumProposalsParameters =
|
||||||
|
totallyValidParameters
|
||||||
|
{ createdMoreThanMaximumProposals = True
|
||||||
|
}
|
||||||
|
|
||||||
|
timeRangeNotTightParameters :: Parameters
|
||||||
|
timeRangeNotTightParameters =
|
||||||
|
totallyValidParameters
|
||||||
|
{ timeRangeTightEnough = False
|
||||||
|
}
|
||||||
|
|
||||||
|
timeRangeNotClosedParameters :: Parameters
|
||||||
|
timeRangeNotClosedParameters =
|
||||||
|
totallyValidParameters
|
||||||
|
{ timeRangeClosed = False
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidProposalStatusParameters :: [Parameters]
|
||||||
|
invalidProposalStatusParameters =
|
||||||
|
map
|
||||||
|
( \st ->
|
||||||
|
totallyValidParameters {proposalStatus = st}
|
||||||
|
)
|
||||||
|
[VotingReady, Locked, Finished]
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
{- | Create a test tree that runs the propsoal minting policy, the governor
|
||||||
|
validator and the stake validator to test the functionalities of creting
|
||||||
|
proposals
|
||||||
|
-}
|
||||||
|
mkTestTree :: String -> Parameters -> Bool -> Bool -> Bool -> SpecificationTree
|
||||||
|
mkTestTree
|
||||||
|
name
|
||||||
|
ps
|
||||||
|
validForProposalPolicy
|
||||||
|
validForGovernorValidator
|
||||||
|
validForStakeValidator =
|
||||||
|
group name [proposalTest, governorTest, stakeTest]
|
||||||
|
where
|
||||||
|
txInfo = createProposal ps
|
||||||
|
|
||||||
|
proposalTest =
|
||||||
|
testPolicy
|
||||||
|
validForProposalPolicy
|
||||||
|
"proposal"
|
||||||
|
(proposalPolicy Shared.proposal.governorSTAssetClass)
|
||||||
|
proposalPolicyRedeemer
|
||||||
|
(ScriptContext txInfo (Minting proposalPolicySymbol))
|
||||||
|
|
||||||
|
governorTest =
|
||||||
|
testValidator
|
||||||
|
validForGovernorValidator
|
||||||
|
"governor"
|
||||||
|
(governorValidator Shared.governor)
|
||||||
|
governorInputDatum
|
||||||
|
governorRedeemer
|
||||||
|
( ScriptContext
|
||||||
|
txInfo
|
||||||
|
(Spending governorRef)
|
||||||
|
)
|
||||||
|
|
||||||
|
stakeTest =
|
||||||
|
testValidator
|
||||||
|
validForStakeValidator
|
||||||
|
"stake"
|
||||||
|
(stakeValidator Shared.stake)
|
||||||
|
(mkStakeInputDatum ps)
|
||||||
|
stakeRedeemer
|
||||||
|
( ScriptContext
|
||||||
|
txInfo
|
||||||
|
(Spending stakeRef)
|
||||||
|
)
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
module Sample.Proposal.Shared (proposalTxRef, stakeTxRef, testFunc) where
|
{- |
|
||||||
|
Module : Sample.Proposal.Shared
|
||||||
|
Maintainer : connor@mlabs.city
|
||||||
|
Description: Shared constants for propsoal samples
|
||||||
|
|
||||||
import Plutarch.Api.V1 (PValidator)
|
Shared constants for propsoal samples.
|
||||||
import Plutarch.Lift (PUnsafeLiftDecl (..))
|
-}
|
||||||
import PlutusLedgerApi.V1 (ScriptContext, ToData, TxId)
|
module Sample.Proposal.Shared (proposalTxRef, stakeTxRef) where
|
||||||
import Test.Specification (
|
|
||||||
SpecificationTree,
|
import PlutusLedgerApi.V1 (TxId)
|
||||||
validatorFailsWith,
|
|
||||||
validatorSucceedsWith,
|
|
||||||
)
|
|
||||||
|
|
||||||
-- | 'TxId' of all the propsoal inputs in the samples.
|
-- | 'TxId' of all the propsoal inputs in the samples.
|
||||||
proposalTxRef :: TxId
|
proposalTxRef :: TxId
|
||||||
|
|
@ -16,24 +16,3 @@ proposalTxRef = "0b2086cbf8b6900f8cb65e012de4516cb66b5cb08a9aaba12a8b88be"
|
||||||
-- | 'TxId' of all the stake inputs in the samples.
|
-- | 'TxId' of all the stake inputs in the samples.
|
||||||
stakeTxRef :: TxId
|
stakeTxRef :: TxId
|
||||||
stakeTxRef = "0ca36f3a357bc69579ab2531aecd1e7d3714d993c7820f40b864be15"
|
stakeTxRef = "0ca36f3a357bc69579ab2531aecd1e7d3714d993c7820f40b864be15"
|
||||||
|
|
||||||
-- | Get the test function given whether a test case is valid.
|
|
||||||
testFunc ::
|
|
||||||
forall {datum :: PType} {redeemer :: PType}.
|
|
||||||
( PUnsafeLiftDecl datum
|
|
||||||
, PUnsafeLiftDecl redeemer
|
|
||||||
, ToData (PLifted datum)
|
|
||||||
, ToData (PLifted redeemer)
|
|
||||||
) =>
|
|
||||||
-- | Should the validator pass?
|
|
||||||
Bool ->
|
|
||||||
String ->
|
|
||||||
ClosedTerm PValidator ->
|
|
||||||
PLifted datum ->
|
|
||||||
PLifted redeemer ->
|
|
||||||
ScriptContext ->
|
|
||||||
SpecificationTree
|
|
||||||
testFunc isValid =
|
|
||||||
if isValid
|
|
||||||
then validatorSucceedsWith
|
|
||||||
else validatorFailsWith
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,26 @@
|
||||||
|
{- |
|
||||||
|
Module : Sample.Proposal.UnlockStake
|
||||||
|
Maintainer : connor@mlabs.city
|
||||||
|
Description: Generate sample data for testing the functionalities of unlocking stake and retracting votes
|
||||||
|
|
||||||
|
Sample and utilities for testing the functionalities of unlocking stake and retracting votes
|
||||||
|
-}
|
||||||
module Sample.Proposal.UnlockStake (
|
module Sample.Proposal.UnlockStake (
|
||||||
unlockStake,
|
|
||||||
StakeRole (..),
|
StakeRole (..),
|
||||||
UnlockStakeParameters (..),
|
Parameters (..),
|
||||||
votesTemplate,
|
unlockStake,
|
||||||
emptyEffectFor,
|
mkTestTree,
|
||||||
mkProposalInputDatum,
|
mkVoterRetractVotesWhileVotingParameters,
|
||||||
mkStakeInputDatum,
|
mkVoterCreatorRetractVotesWhileVotingParameters,
|
||||||
mkProposalValidatorTestCase,
|
mkCreatorRemoveCreatorLocksWhenFinishedParameters,
|
||||||
|
mkVoterCreatorRemoveAllLocksWhenFinishedParameters,
|
||||||
|
mkVoterUnlockStakeAfterVotingParameters,
|
||||||
|
mkVoterCreatorRemoveVoteLocksWhenLockedParameters,
|
||||||
|
mkRetractVotesWhileNotVoting,
|
||||||
|
mkUnockIrrelevantStakeParameters,
|
||||||
|
mkRemoveCreatorLockBeforeFinishedParameters,
|
||||||
|
mkRetractVotesWithCreatorStakeParamaters,
|
||||||
|
mkAlterStakeParameters,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
@ -21,9 +35,8 @@ import Agora.Proposal (
|
||||||
)
|
)
|
||||||
import Agora.Proposal.Scripts (proposalValidator)
|
import Agora.Proposal.Scripts (proposalValidator)
|
||||||
import Agora.Proposal.Time (ProposalStartingTime (ProposalStartingTime))
|
import Agora.Proposal.Time (ProposalStartingTime (ProposalStartingTime))
|
||||||
import Agora.Stake (ProposalLock (ProposalLock), Stake (..), StakeDatum (..))
|
import Agora.Stake (ProposalLock (..), Stake (..), StakeDatum (..), StakeRedeemer (RetractVotes))
|
||||||
import Control.Monad (join)
|
import Agora.Stake.Scripts (stakeValidator)
|
||||||
import Data.Coerce (coerce)
|
|
||||||
import Data.Default.Class (Default (def))
|
import Data.Default.Class (Default (def))
|
||||||
import Data.Tagged (Tagged (..), untag)
|
import Data.Tagged (Tagged (..), untag)
|
||||||
import Plutarch.Context (
|
import Plutarch.Context (
|
||||||
|
|
@ -32,14 +45,15 @@ import Plutarch.Context (
|
||||||
input,
|
input,
|
||||||
output,
|
output,
|
||||||
script,
|
script,
|
||||||
|
signedWith,
|
||||||
txId,
|
txId,
|
||||||
withDatum,
|
withDatum,
|
||||||
withRefIndex,
|
withOutRef,
|
||||||
withTxId,
|
|
||||||
withValue,
|
withValue,
|
||||||
)
|
)
|
||||||
import PlutusLedgerApi.V1 (
|
import PlutusLedgerApi.V1 (
|
||||||
DatumHash,
|
DatumHash,
|
||||||
|
PubKeyHash,
|
||||||
ScriptContext (..),
|
ScriptContext (..),
|
||||||
ScriptPurpose (Spending),
|
ScriptPurpose (Spending),
|
||||||
TxInfo (..),
|
TxInfo (..),
|
||||||
|
|
@ -48,7 +62,7 @@ import PlutusLedgerApi.V1 (
|
||||||
)
|
)
|
||||||
import PlutusLedgerApi.V1.Value qualified as Value
|
import PlutusLedgerApi.V1.Value qualified as Value
|
||||||
import PlutusTx.AssocMap qualified as AssocMap
|
import PlutusTx.AssocMap qualified as AssocMap
|
||||||
import Sample.Proposal.Shared (proposalTxRef, stakeTxRef, testFunc)
|
import Sample.Proposal.Shared (stakeTxRef)
|
||||||
import Sample.Shared (
|
import Sample.Shared (
|
||||||
minAda,
|
minAda,
|
||||||
proposalPolicySymbol,
|
proposalPolicySymbol,
|
||||||
|
|
@ -59,7 +73,7 @@ import Sample.Shared (
|
||||||
stakeValidatorHash,
|
stakeValidatorHash,
|
||||||
)
|
)
|
||||||
import Sample.Shared qualified as Shared
|
import Sample.Shared qualified as Shared
|
||||||
import Test.Specification (SpecificationTree)
|
import Test.Specification (SpecificationTree, group, testValidator)
|
||||||
import Test.Util (sortValue, updateMap)
|
import Test.Util (sortValue, updateMap)
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
@ -82,106 +96,130 @@ emptyEffectFor (ProposalVotes vs) =
|
||||||
map (,AssocMap.empty) (AssocMap.keys vs)
|
map (,AssocMap.empty) (AssocMap.keys vs)
|
||||||
|
|
||||||
-- | The default vote option that will be used by functions in this module.
|
-- | The default vote option that will be used by functions in this module.
|
||||||
defaultVoteFor :: ResultTag
|
defVoteFor :: ResultTag
|
||||||
defaultVoteFor = ResultTag 0
|
defVoteFor = ResultTag 0
|
||||||
|
|
||||||
-- | The default number of GTs the stake will have.
|
-- | The default number of GTs the stake will have.
|
||||||
defaultStakedGTs :: Tagged _ Integer
|
defStakedGTs :: Tagged _ Integer
|
||||||
defaultStakedGTs = Tagged 100000
|
defStakedGTs = 100000
|
||||||
|
|
||||||
|
{- | If 'Parameters.alterOutputStake' is set to true, the
|
||||||
|
'StakeDatum.stakedAmount' will be set to this.
|
||||||
|
-}
|
||||||
|
alteredStakedGTs :: Tagged _ Integer
|
||||||
|
alteredStakedGTs = 100
|
||||||
|
|
||||||
|
-- | Default owner of the stakes.
|
||||||
|
defOwner :: PubKeyHash
|
||||||
|
defOwner = signer
|
||||||
|
|
||||||
-- | How a stake has been used on a particular proposal.
|
-- | How a stake has been used on a particular proposal.
|
||||||
data StakeRole
|
data StakeRole
|
||||||
= -- | The stake was spent to vote for a paraticular option.
|
= -- | The stake was spent to vote for a paraticular option.
|
||||||
Voter
|
Voter
|
||||||
| -- | The stake was used to created the proposal.
|
| -- | The stake was used to create the proposal.
|
||||||
Creator
|
Creator
|
||||||
|
| -- | The stake was used to both create and vote for the proposal.
|
||||||
|
Both
|
||||||
| -- | The stake has nothing to do with the proposal.
|
| -- | The stake has nothing to do with the proposal.
|
||||||
Irrelevant
|
Irrelevant
|
||||||
|
deriving stock (Bounded, Enum, Show)
|
||||||
|
|
||||||
-- | Parameters for creating a 'TxOut' that unlocks a stake.
|
-- | Parameters for creating a 'TxOut' that unlocks a stake.
|
||||||
data UnlockStakeParameters = UnlockStakeParameters
|
data Parameters = Parameters
|
||||||
{ proposalCount :: Integer
|
{ proposalCount :: Integer
|
||||||
-- ^ The number of proposals in the 'TxOut'.
|
-- ^ The number of proposals in the 'TxOut'.
|
||||||
, stakeUsage :: StakeRole
|
, stakeRole :: StakeRole
|
||||||
-- ^ The role of the stake we're unlocking.
|
-- ^ The role of the stake we're unlocking.
|
||||||
, retractVotes :: Bool
|
, retractVotes :: Bool
|
||||||
-- ^ Whether to retract votes or not.
|
-- ^ Whether to retract votes or not.
|
||||||
|
, removeVoterLock :: Bool
|
||||||
|
-- ^ Remove the voter locks from the input stake.
|
||||||
|
, removeCreatorLock :: Bool
|
||||||
|
-- ^ Remove the creator locks from the input stake.
|
||||||
, proposalStatus :: ProposalStatus
|
, proposalStatus :: ProposalStatus
|
||||||
-- ^ The state of all the proposals.
|
-- ^ The state of all the proposals.
|
||||||
|
, alterOutputStake :: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
instance Show UnlockStakeParameters where
|
-- | Iterate over the proposal id of every proposal, given the number of proposals.
|
||||||
show p =
|
forEachProposalId :: Parameters -> (ProposalId -> a) -> [a]
|
||||||
let role = case p.stakeUsage of
|
forEachProposalId ps = forEachProposalId' ps.proposalCount
|
||||||
Voter -> "voter"
|
where
|
||||||
Creator -> "creator"
|
forEachProposalId' :: Integer -> (ProposalId -> a) -> [a]
|
||||||
_ -> "irrelevant stake"
|
forEachProposalId' 0 _ = error "zero proposal"
|
||||||
|
forEachProposalId' n f = f . ProposalId <$> [0 .. n - 1]
|
||||||
|
|
||||||
action =
|
-- | Create locks for the input stake given the parameters.
|
||||||
if p.retractVotes
|
mkInputStakeLocks :: Parameters -> [ProposalLock]
|
||||||
then "unlock stake + retract votes"
|
mkInputStakeLocks ps = mconcat $ forEachProposalId ps $ mkStakeLocksFor ps.stakeRole
|
||||||
else "unlock stake"
|
where
|
||||||
|
mkStakeLocksFor :: StakeRole -> ProposalId -> [ProposalLock]
|
||||||
|
mkStakeLocksFor sr pid =
|
||||||
|
let voted = [Voted pid defVoteFor]
|
||||||
|
created = [Created pid]
|
||||||
|
in case sr of
|
||||||
|
Voter -> voted
|
||||||
|
Creator -> created
|
||||||
|
Both -> voted <> created
|
||||||
|
_ -> []
|
||||||
|
|
||||||
while = show p.proposalStatus
|
-- | Create locks for the output stake by removing locks from the input locks.
|
||||||
|
mkOutputStakeLocks :: Parameters -> [ProposalLock]
|
||||||
|
mkOutputStakeLocks ps =
|
||||||
|
filter
|
||||||
|
( \lock -> not $ case lock of
|
||||||
|
Voted _ _ -> ps.removeVoterLock
|
||||||
|
Created _ -> ps.removeCreatorLock
|
||||||
|
)
|
||||||
|
inputLocks
|
||||||
|
where
|
||||||
|
inputLocks = mkInputStakeLocks ps
|
||||||
|
|
||||||
proposalInfo = mconcat [show p.proposalCount, " proposals"]
|
-- | Create the stake input datum given the parameters.
|
||||||
in mconcat [proposalInfo, ", ", role, ", ", action, ", ", while]
|
mkStakeInputDatum :: Parameters -> StakeDatum
|
||||||
|
mkStakeInputDatum ps =
|
||||||
|
StakeDatum
|
||||||
|
{ stakedAmount = defStakedGTs
|
||||||
|
, owner = defOwner
|
||||||
|
, lockedBy = mkInputStakeLocks ps
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | Create stake output datum given the parameters.
|
||||||
|
mkStakeOutputDatum :: Parameters -> StakeDatum
|
||||||
|
mkStakeOutputDatum ps =
|
||||||
|
let template = mkStakeInputDatum ps
|
||||||
|
stakedAmount' =
|
||||||
|
if ps.alterOutputStake
|
||||||
|
then alteredStakedGTs
|
||||||
|
else defStakedGTs
|
||||||
|
in template
|
||||||
|
{ stakedAmount = stakedAmount'
|
||||||
|
, lockedBy = mkOutputStakeLocks ps
|
||||||
|
}
|
||||||
|
|
||||||
-- | Generate some input proposals and their corresponding output proposals.
|
-- | Generate some input proposals and their corresponding output proposals.
|
||||||
mkProposals :: UnlockStakeParameters -> [(ProposalDatum, ProposalDatum)]
|
mkProposals :: Parameters -> [(ProposalDatum, ProposalDatum)]
|
||||||
mkProposals p = forEachProposalId p.proposalCount $ mkProposalDatumPair p
|
mkProposals ps = forEachProposalId ps $ mkProposalDatumPair ps
|
||||||
|
|
||||||
-- | Iterate over the proposal id of every proposal, given the number of proposals.
|
|
||||||
forEachProposalId :: Integer -> (ProposalId -> a) -> [a]
|
|
||||||
forEachProposalId 0 _ = error "zero proposal"
|
|
||||||
forEachProposalId n f = f . ProposalId <$> [0 .. n - 1]
|
|
||||||
|
|
||||||
-- | Create the input stake and its corresponding output stake.
|
|
||||||
mkStakeDatumPair :: UnlockStakeParameters -> (StakeDatum, StakeDatum)
|
|
||||||
mkStakeDatumPair c =
|
|
||||||
let output =
|
|
||||||
StakeDatum
|
|
||||||
{ stakedAmount = defaultStakedGTs
|
|
||||||
, owner = signer
|
|
||||||
, lockedBy = []
|
|
||||||
}
|
|
||||||
|
|
||||||
inputLocks = join $ forEachProposalId c.proposalCount (mkStakeLocks c.stakeUsage)
|
|
||||||
|
|
||||||
input = output {lockedBy = inputLocks}
|
|
||||||
in (input, output)
|
|
||||||
where
|
|
||||||
mkStakeLocks :: StakeRole -> ProposalId -> [ProposalLock]
|
|
||||||
mkStakeLocks Voter pid = [ProposalLock defaultVoteFor pid]
|
|
||||||
mkStakeLocks Creator pid =
|
|
||||||
map (`ProposalLock` pid) $
|
|
||||||
AssocMap.keys $ getProposalVotes votesTemplate
|
|
||||||
mkStakeLocks _ _ = []
|
|
||||||
|
|
||||||
-- | Create the input proposal datum.
|
-- | Create the input proposal datum.
|
||||||
mkProposalInputDatum :: UnlockStakeParameters -> ProposalId -> ProposalDatum
|
mkProposalInputDatum :: Parameters -> ProposalId -> ProposalDatum
|
||||||
mkProposalInputDatum p pid = fst $ mkProposalDatumPair p pid
|
mkProposalInputDatum p pid = fst $ mkProposalDatumPair p pid
|
||||||
|
|
||||||
-- | Create the input stake datum.
|
|
||||||
mkStakeInputDatum :: UnlockStakeParameters -> StakeDatum
|
|
||||||
mkStakeInputDatum = fst . mkStakeDatumPair
|
|
||||||
|
|
||||||
-- | Create a input proposal and its corresponding output proposal.
|
-- | Create a input proposal and its corresponding output proposal.
|
||||||
mkProposalDatumPair ::
|
mkProposalDatumPair ::
|
||||||
UnlockStakeParameters ->
|
Parameters ->
|
||||||
ProposalId ->
|
ProposalId ->
|
||||||
(ProposalDatum, ProposalDatum)
|
(ProposalDatum, ProposalDatum)
|
||||||
mkProposalDatumPair params pid =
|
mkProposalDatumPair params pid =
|
||||||
let owner = signer
|
let inputVotes = mkInputVotes params.stakeRole $ untag defStakedGTs
|
||||||
|
|
||||||
inputVotes = mkInputVotes params.stakeUsage $ untag defaultStakedGTs
|
|
||||||
|
|
||||||
input =
|
input =
|
||||||
ProposalDatum
|
ProposalDatum
|
||||||
{ proposalId = pid
|
{ proposalId = pid
|
||||||
, effects = emptyEffectFor votesTemplate
|
, effects = emptyEffectFor votesTemplate
|
||||||
, status = params.proposalStatus
|
, status = params.proposalStatus
|
||||||
, cosigners = [owner]
|
, cosigners = [defOwner]
|
||||||
, thresholds = def
|
, thresholds = def
|
||||||
, votes = inputVotes
|
, votes = inputVotes
|
||||||
, timingConfig = def
|
, timingConfig = def
|
||||||
|
|
@ -200,62 +238,62 @@ mkProposalDatumPair params pid =
|
||||||
-- The staked amount/votes.
|
-- The staked amount/votes.
|
||||||
Integer ->
|
Integer ->
|
||||||
ProposalVotes
|
ProposalVotes
|
||||||
mkInputVotes Voter vc =
|
|
||||||
ProposalVotes $
|
|
||||||
updateMap (Just . const vc) defaultVoteFor $
|
|
||||||
getProposalVotes votesTemplate
|
|
||||||
mkInputVotes Creator _ =
|
mkInputVotes Creator _ =
|
||||||
ProposalVotes $
|
ProposalVotes $
|
||||||
updateMap (Just . const 1000) defaultVoteFor $
|
updateMap (Just . const 1000) defVoteFor $
|
||||||
|
getProposalVotes votesTemplate
|
||||||
|
mkInputVotes Irrelevant _ = votesTemplate
|
||||||
|
mkInputVotes _ vc =
|
||||||
|
ProposalVotes $
|
||||||
|
updateMap (Just . const vc) defVoteFor $
|
||||||
getProposalVotes votesTemplate
|
getProposalVotes votesTemplate
|
||||||
mkInputVotes _ _ = votesTemplate
|
|
||||||
|
|
||||||
-- | Create a 'TxInfo' that tries to unlock a stake.
|
-- | Create a 'TxInfo' that tries to unlock a stake.
|
||||||
unlockStake :: UnlockStakeParameters -> TxInfo
|
unlockStake :: Parameters -> TxInfo
|
||||||
unlockStake p =
|
unlockStake ps =
|
||||||
let pst = Value.singleton proposalPolicySymbol "" 1
|
let pst = Value.singleton proposalPolicySymbol "" 1
|
||||||
sst = Value.assetClassValue stakeAssetClass 1
|
sst = Value.assetClassValue stakeAssetClass 1
|
||||||
|
|
||||||
pIODatums = mkProposals p
|
pIODatums = mkProposals ps
|
||||||
(sInDatum, sOutDatum) = mkStakeDatumPair p
|
|
||||||
|
|
||||||
proposals =
|
proposals =
|
||||||
foldMap
|
foldMap
|
||||||
( \(i, o) ->
|
( \((i, o), idx) ->
|
||||||
mconcat
|
mconcat
|
||||||
@BaseBuilder
|
@BaseBuilder
|
||||||
[ input $
|
[ input $
|
||||||
script proposalValidatorHash
|
script proposalValidatorHash
|
||||||
. withValue pst
|
. withValue pst
|
||||||
. withDatum i
|
. withDatum i
|
||||||
. withTxId proposalTxRef
|
. withOutRef (mkProposalRef idx)
|
||||||
. withRefIndex (coerce i.proposalId + 2)
|
|
||||||
, output $
|
, output $
|
||||||
script proposalValidatorHash
|
script proposalValidatorHash
|
||||||
. withValue (sortValue $ pst <> minAda)
|
. withValue (sortValue $ pst <> minAda)
|
||||||
. withDatum o
|
. withDatum o
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
pIODatums
|
(zip pIODatums [0 ..])
|
||||||
|
|
||||||
stakeValue =
|
stakeValue =
|
||||||
sortValue $
|
sortValue $
|
||||||
mconcat
|
mconcat
|
||||||
[ Value.assetClassValue
|
[ Value.assetClassValue
|
||||||
(untag stake.gtClassRef)
|
(untag stake.gtClassRef)
|
||||||
(untag defaultStakedGTs)
|
(untag defStakedGTs)
|
||||||
, sst
|
, sst
|
||||||
, minAda
|
, minAda
|
||||||
]
|
]
|
||||||
|
|
||||||
|
sInDatum = mkStakeInputDatum ps
|
||||||
|
sOutDatum = mkStakeOutputDatum ps
|
||||||
|
|
||||||
stakes =
|
stakes =
|
||||||
mconcat @BaseBuilder
|
mconcat @BaseBuilder
|
||||||
[ input $
|
[ input $
|
||||||
script stakeValidatorHash
|
script stakeValidatorHash
|
||||||
. withValue stakeValue
|
. withValue stakeValue
|
||||||
. withDatum sInDatum
|
. withDatum sInDatum
|
||||||
. withTxId stakeTxRef
|
. withOutRef stakeRef
|
||||||
. withRefIndex 1
|
|
||||||
, output $
|
, output $
|
||||||
script stakeValidatorHash
|
script stakeValidatorHash
|
||||||
. withValue stakeValue
|
. withValue stakeValue
|
||||||
|
|
@ -267,23 +305,243 @@ unlockStake p =
|
||||||
[ txId "388bc0b897b3dadcd479da4c88291de4113a50b72ddbed001faf7fc03f11bc52"
|
[ txId "388bc0b897b3dadcd479da4c88291de4113a50b72ddbed001faf7fc03f11bc52"
|
||||||
, proposals
|
, proposals
|
||||||
, stakes
|
, stakes
|
||||||
|
, signedWith defOwner
|
||||||
]
|
]
|
||||||
in buildTxInfoUnsafe builder
|
in buildTxInfoUnsafe builder
|
||||||
|
|
||||||
-- | Create a test case that tests the proposal validator's @'Unlock' _@ redeemer.
|
-- | Reference to the stake UTXO.
|
||||||
mkProposalValidatorTestCase :: UnlockStakeParameters -> Bool -> SpecificationTree
|
stakeRef :: TxOutRef
|
||||||
mkProposalValidatorTestCase p shouldSucceed =
|
stakeRef = TxOutRef stakeTxRef 1
|
||||||
let datum = mkProposalInputDatum p $ ProposalId 0
|
|
||||||
redeemer = Unlock (ResultTag 0)
|
-- | Generate the reference to a proposal UTXOs, given the index of the proposal.
|
||||||
name = show p
|
mkProposalRef :: Int -> TxOutRef
|
||||||
scriptContext =
|
mkProposalRef offset = TxOutRef stakeTxRef $ 2 + fromIntegral offset
|
||||||
ScriptContext
|
|
||||||
(unlockStake p)
|
-- | Proposal redeemer used by 'mkTestTree', in this case it's always 'Unlock'.
|
||||||
(Spending (TxOutRef proposalTxRef 2))
|
proposalRedeemer :: ProposalRedeemer
|
||||||
in testFunc
|
proposalRedeemer = Unlock
|
||||||
shouldSucceed
|
|
||||||
name
|
-- | Stake redeemer used by 'mkTestTree', in this case it's always 'RetractVotes'.
|
||||||
(proposalValidator Shared.proposal)
|
stakeRedeemer :: StakeRedeemer
|
||||||
datum
|
stakeRedeemer = RetractVotes
|
||||||
redeemer
|
|
||||||
scriptContext
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
{- | Legal parameters that retract votes while the proposals is in 'VotingReady'
|
||||||
|
state, and also remove voter locks from the stake, which is
|
||||||
|
used to vote on the proposals.
|
||||||
|
-}
|
||||||
|
mkVoterRetractVotesWhileVotingParameters :: Integer -> Parameters
|
||||||
|
mkVoterRetractVotesWhileVotingParameters nProposals =
|
||||||
|
Parameters
|
||||||
|
{ proposalCount = nProposals
|
||||||
|
, stakeRole = Voter
|
||||||
|
, retractVotes = True
|
||||||
|
, removeVoterLock = True
|
||||||
|
, removeCreatorLock = False
|
||||||
|
, proposalStatus = VotingReady
|
||||||
|
, alterOutputStake = False
|
||||||
|
}
|
||||||
|
|
||||||
|
{- | Legal parameters that retract votes while the proposals is in 'VotingReady'
|
||||||
|
state, and also remove voter locks from the stake, which is
|
||||||
|
used to both create and vote on the proposals.
|
||||||
|
-}
|
||||||
|
mkVoterCreatorRetractVotesWhileVotingParameters :: Integer -> Parameters
|
||||||
|
mkVoterCreatorRetractVotesWhileVotingParameters nProposals =
|
||||||
|
Parameters
|
||||||
|
{ proposalCount = nProposals
|
||||||
|
, stakeRole = Both
|
||||||
|
, retractVotes = True
|
||||||
|
, removeVoterLock = True
|
||||||
|
, removeCreatorLock = False
|
||||||
|
, proposalStatus = VotingReady
|
||||||
|
, alterOutputStake = False
|
||||||
|
}
|
||||||
|
|
||||||
|
{- | Legal parameters that remove creator locks from the stake while the
|
||||||
|
proposals is in 'Finished' state. The stake was only used for creating
|
||||||
|
the proposals.
|
||||||
|
-}
|
||||||
|
mkCreatorRemoveCreatorLocksWhenFinishedParameters :: Integer -> Parameters
|
||||||
|
mkCreatorRemoveCreatorLocksWhenFinishedParameters nProposals =
|
||||||
|
Parameters
|
||||||
|
{ proposalCount = nProposals
|
||||||
|
, stakeRole = Creator
|
||||||
|
, retractVotes = False
|
||||||
|
, removeVoterLock = False
|
||||||
|
, removeCreatorLock = True
|
||||||
|
, proposalStatus = Finished
|
||||||
|
, alterOutputStake = False
|
||||||
|
}
|
||||||
|
|
||||||
|
{- | Legal parameters that remove voter and creator locks from the stake while
|
||||||
|
the proposals is in 'Finished' state. The stake was used for creating
|
||||||
|
and voting on the proposals.
|
||||||
|
-}
|
||||||
|
mkVoterCreatorRemoveAllLocksWhenFinishedParameters :: Integer -> Parameters
|
||||||
|
mkVoterCreatorRemoveAllLocksWhenFinishedParameters nProposals =
|
||||||
|
Parameters
|
||||||
|
{ proposalCount = nProposals
|
||||||
|
, stakeRole = Both
|
||||||
|
, retractVotes = False
|
||||||
|
, removeVoterLock = True
|
||||||
|
, removeCreatorLock = True
|
||||||
|
, proposalStatus = Finished
|
||||||
|
, alterOutputStake = False
|
||||||
|
}
|
||||||
|
|
||||||
|
{- Legal parameters that remove voter locks from the stake after the voting
|
||||||
|
phrase. The stake was used only for voting on the proposals.
|
||||||
|
-}
|
||||||
|
mkVoterUnlockStakeAfterVotingParameters :: Integer -> [Parameters]
|
||||||
|
mkVoterUnlockStakeAfterVotingParameters nProposals =
|
||||||
|
map
|
||||||
|
( \st ->
|
||||||
|
Parameters
|
||||||
|
{ proposalCount = nProposals
|
||||||
|
, stakeRole = Voter
|
||||||
|
, retractVotes = False
|
||||||
|
, removeVoterLock = True
|
||||||
|
, removeCreatorLock = False
|
||||||
|
, proposalStatus = st
|
||||||
|
, alterOutputStake = False
|
||||||
|
}
|
||||||
|
)
|
||||||
|
[Locked, Finished]
|
||||||
|
|
||||||
|
{- Legal parameters that remove voter locks whenproposals are in phrase.
|
||||||
|
The stake was used for crating and voting on the proposals.
|
||||||
|
-}
|
||||||
|
mkVoterCreatorRemoveVoteLocksWhenLockedParameters :: Integer -> Parameters
|
||||||
|
mkVoterCreatorRemoveVoteLocksWhenLockedParameters nProposals =
|
||||||
|
Parameters
|
||||||
|
{ proposalCount = nProposals
|
||||||
|
, stakeRole = Both
|
||||||
|
, retractVotes = False
|
||||||
|
, removeVoterLock = True
|
||||||
|
, removeCreatorLock = False
|
||||||
|
, proposalStatus = Locked
|
||||||
|
, alterOutputStake = False
|
||||||
|
}
|
||||||
|
|
||||||
|
{- | Illegal parameters that retract votes when the proposals are not in voting
|
||||||
|
phrase.
|
||||||
|
-}
|
||||||
|
mkRetractVotesWhileNotVoting :: Integer -> [Parameters]
|
||||||
|
mkRetractVotesWhileNotVoting nProposals = do
|
||||||
|
role <- enumFrom Voter
|
||||||
|
status <- [Draft, Locked, Finished]
|
||||||
|
|
||||||
|
pure $
|
||||||
|
Parameters
|
||||||
|
{ proposalCount = nProposals
|
||||||
|
, stakeRole = role
|
||||||
|
, retractVotes = True
|
||||||
|
, removeVoterLock = True
|
||||||
|
, removeCreatorLock = False
|
||||||
|
, proposalStatus = status
|
||||||
|
, alterOutputStake = False
|
||||||
|
}
|
||||||
|
|
||||||
|
{- | Illegal parameter that try to unlock a stake that has nothing to do with
|
||||||
|
the proposals.
|
||||||
|
-}
|
||||||
|
mkUnockIrrelevantStakeParameters :: Integer -> [Parameters]
|
||||||
|
mkUnockIrrelevantStakeParameters nProposals = do
|
||||||
|
status <- [Draft, VotingReady, Locked, Finished]
|
||||||
|
retractVotes <- [True, False]
|
||||||
|
|
||||||
|
pure $
|
||||||
|
Parameters
|
||||||
|
{ proposalCount = nProposals
|
||||||
|
, stakeRole = Irrelevant
|
||||||
|
, retractVotes = retractVotes
|
||||||
|
, removeVoterLock = True
|
||||||
|
, removeCreatorLock = True
|
||||||
|
, proposalStatus = status
|
||||||
|
, alterOutputStake = False
|
||||||
|
}
|
||||||
|
|
||||||
|
{- | Illegal parameters that remove the creator locks before the proposals are
|
||||||
|
'Finished'.
|
||||||
|
-}
|
||||||
|
mkRemoveCreatorLockBeforeFinishedParameters :: Integer -> [Parameters]
|
||||||
|
mkRemoveCreatorLockBeforeFinishedParameters nProposals = do
|
||||||
|
status <- [Draft, VotingReady, Locked]
|
||||||
|
|
||||||
|
pure $
|
||||||
|
Parameters
|
||||||
|
{ proposalCount = nProposals
|
||||||
|
, stakeRole = Creator
|
||||||
|
, retractVotes = False
|
||||||
|
, removeVoterLock = False
|
||||||
|
, removeCreatorLock = True
|
||||||
|
, proposalStatus = status
|
||||||
|
, alterOutputStake = False
|
||||||
|
}
|
||||||
|
|
||||||
|
{- | Illegal parameters that try to retract votes with a stake that was only used
|
||||||
|
for creating the proposals.
|
||||||
|
-}
|
||||||
|
mkRetractVotesWithCreatorStakeParamaters :: Integer -> Parameters
|
||||||
|
mkRetractVotesWithCreatorStakeParamaters nProposals =
|
||||||
|
Parameters
|
||||||
|
{ proposalCount = nProposals
|
||||||
|
, stakeRole = Creator
|
||||||
|
, retractVotes = True
|
||||||
|
, removeVoterLock = True
|
||||||
|
, removeCreatorLock = True
|
||||||
|
, proposalStatus = VotingReady
|
||||||
|
, alterOutputStake = False
|
||||||
|
}
|
||||||
|
|
||||||
|
{- | Illegal parameters that try to change the 'StakeDatum.stakedAmount' field of
|
||||||
|
the output stake datum.
|
||||||
|
-}
|
||||||
|
mkAlterStakeParameters :: Integer -> [Parameters]
|
||||||
|
mkAlterStakeParameters nProposals = do
|
||||||
|
role <- enumFrom Voter
|
||||||
|
status <- [Draft, Locked, Finished]
|
||||||
|
|
||||||
|
pure $
|
||||||
|
Parameters
|
||||||
|
{ proposalCount = nProposals
|
||||||
|
, stakeRole = role
|
||||||
|
, retractVotes = True
|
||||||
|
, removeVoterLock = True
|
||||||
|
, removeCreatorLock = False
|
||||||
|
, proposalStatus = status
|
||||||
|
, alterOutputStake = True
|
||||||
|
}
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
{- | Create a test tree that runs both the stake validator and the proposal
|
||||||
|
validator.
|
||||||
|
-}
|
||||||
|
mkTestTree :: String -> Parameters -> Bool -> SpecificationTree
|
||||||
|
mkTestTree name ps isValid = group name [stake, proposal]
|
||||||
|
where
|
||||||
|
txInfo = unlockStake ps
|
||||||
|
|
||||||
|
stake =
|
||||||
|
testValidator
|
||||||
|
(not ps.alterOutputStake)
|
||||||
|
"stake"
|
||||||
|
(stakeValidator Shared.stake)
|
||||||
|
(mkStakeInputDatum ps)
|
||||||
|
stakeRedeemer
|
||||||
|
(ScriptContext txInfo (Spending stakeRef))
|
||||||
|
|
||||||
|
proposal =
|
||||||
|
let idx = 0
|
||||||
|
pid = ProposalId $ fromIntegral idx
|
||||||
|
ref = mkProposalRef idx
|
||||||
|
in testValidator
|
||||||
|
isValid
|
||||||
|
"propsoal"
|
||||||
|
(proposalValidator Shared.proposal)
|
||||||
|
(mkProposalInputDatum ps pid)
|
||||||
|
proposalRedeemer
|
||||||
|
(ScriptContext txInfo (Spending ref))
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,10 @@
|
||||||
|
{- |
|
||||||
|
Module : Sample.Proposal.Vote
|
||||||
|
Maintainer : connor@mlabs.city
|
||||||
|
Description: Generate sample data for testing the functionalities of voting on proposals.
|
||||||
|
|
||||||
|
Sample and utilities for testing the functionalities of voting on proposals.
|
||||||
|
-}
|
||||||
module Sample.Proposal.Vote (
|
module Sample.Proposal.Vote (
|
||||||
validVoteParameters,
|
validVoteParameters,
|
||||||
mkTestTree,
|
mkTestTree,
|
||||||
|
|
@ -17,7 +24,7 @@ import Agora.Proposal.Time (
|
||||||
ProposalTimingConfig (draftTime, votingTime),
|
ProposalTimingConfig (draftTime, votingTime),
|
||||||
)
|
)
|
||||||
import Agora.Stake (
|
import Agora.Stake (
|
||||||
ProposalLock (ProposalLock),
|
ProposalLock (..),
|
||||||
Stake (gtClassRef),
|
Stake (gtClassRef),
|
||||||
StakeDatum (..),
|
StakeDatum (..),
|
||||||
StakeRedeemer (PermitVote),
|
StakeRedeemer (PermitVote),
|
||||||
|
|
@ -47,7 +54,7 @@ import PlutusLedgerApi.V1 (
|
||||||
)
|
)
|
||||||
import PlutusLedgerApi.V1.Value qualified as Value
|
import PlutusLedgerApi.V1.Value qualified as Value
|
||||||
import PlutusTx.AssocMap qualified as AssocMap
|
import PlutusTx.AssocMap qualified as AssocMap
|
||||||
import Sample.Proposal.Shared (proposalTxRef, stakeTxRef, testFunc)
|
import Sample.Proposal.Shared (proposalTxRef, stakeTxRef)
|
||||||
import Sample.Shared (
|
import Sample.Shared (
|
||||||
minAda,
|
minAda,
|
||||||
proposalPolicySymbol,
|
proposalPolicySymbol,
|
||||||
|
|
@ -61,13 +68,16 @@ import Sample.Shared qualified as Shared
|
||||||
import Test.Specification (
|
import Test.Specification (
|
||||||
SpecificationTree,
|
SpecificationTree,
|
||||||
group,
|
group,
|
||||||
|
testValidator,
|
||||||
validatorSucceedsWith,
|
validatorSucceedsWith,
|
||||||
)
|
)
|
||||||
import Test.Util (closedBoundedInterval, sortValue, updateMap)
|
import Test.Util (closedBoundedInterval, sortValue, updateMap)
|
||||||
|
|
||||||
|
-- | Reference to the proposal UTXO.
|
||||||
proposalRef :: TxOutRef
|
proposalRef :: TxOutRef
|
||||||
proposalRef = TxOutRef proposalTxRef 0
|
proposalRef = TxOutRef proposalTxRef 0
|
||||||
|
|
||||||
|
-- | Reference to the stake UTXO.
|
||||||
stakeRef :: TxOutRef
|
stakeRef :: TxOutRef
|
||||||
stakeRef = TxOutRef stakeTxRef 1
|
stakeRef = TxOutRef stakeTxRef 1
|
||||||
|
|
||||||
|
|
@ -79,9 +89,11 @@ data Parameters = Parameters
|
||||||
-- ^ The count of votes.
|
-- ^ The count of votes.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- | The public key hash of the stake owner.
|
||||||
stakeOwner :: PubKeyHash
|
stakeOwner :: PubKeyHash
|
||||||
stakeOwner = signer
|
stakeOwner = signer
|
||||||
|
|
||||||
|
-- | The votes of the input proposals.
|
||||||
initialVotes :: AssocMap.Map ResultTag Integer
|
initialVotes :: AssocMap.Map ResultTag Integer
|
||||||
initialVotes =
|
initialVotes =
|
||||||
AssocMap.fromList
|
AssocMap.fromList
|
||||||
|
|
@ -89,6 +101,7 @@ initialVotes =
|
||||||
, (ResultTag 1, 4242)
|
, (ResultTag 1, 4242)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
-- | The input proposal datum.
|
||||||
proposalInputDatum :: ProposalDatum
|
proposalInputDatum :: ProposalDatum
|
||||||
proposalInputDatum =
|
proposalInputDatum =
|
||||||
ProposalDatum
|
ProposalDatum
|
||||||
|
|
@ -106,12 +119,16 @@ proposalInputDatum =
|
||||||
, startingTime = ProposalStartingTime 0
|
, startingTime = ProposalStartingTime 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- | The locks of the input stake.
|
||||||
existingLocks :: [ProposalLock]
|
existingLocks :: [ProposalLock]
|
||||||
existingLocks =
|
existingLocks =
|
||||||
[ ProposalLock (ResultTag 0) (ProposalId 0)
|
[ Voted (ProposalId 0) (ResultTag 0)
|
||||||
, ProposalLock (ResultTag 2) (ProposalId 1)
|
, Voted (ProposalId 1) (ResultTag 2)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
{- | Set the 'StakeDatum.stakedAmount' according to the number of votes being
|
||||||
|
casted.
|
||||||
|
-}
|
||||||
mkStakeInputDatum :: Parameters -> StakeDatum
|
mkStakeInputDatum :: Parameters -> StakeDatum
|
||||||
mkStakeInputDatum params =
|
mkStakeInputDatum params =
|
||||||
StakeDatum
|
StakeDatum
|
||||||
|
|
@ -120,14 +137,19 @@ mkStakeInputDatum params =
|
||||||
, lockedBy = existingLocks
|
, lockedBy = existingLocks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- | Create the proposal redeemer. In this case @'Vote' _@ will always be used.
|
||||||
mkProposalRedeemer :: Parameters -> ProposalRedeemer
|
mkProposalRedeemer :: Parameters -> ProposalRedeemer
|
||||||
mkProposalRedeemer = Vote . voteFor
|
mkProposalRedeemer = Vote . voteFor
|
||||||
|
|
||||||
|
-- | Place new proposal locks on the stake.
|
||||||
mkNewLock :: Parameters -> ProposalLock
|
mkNewLock :: Parameters -> ProposalLock
|
||||||
mkNewLock ps = ProposalLock ps.voteFor proposalInputDatum.proposalId
|
mkNewLock = Voted proposalInputDatum.proposalId . voteFor
|
||||||
|
|
||||||
mkStakeRedeemer :: Parameters -> StakeRedeemer
|
{- | The stake redeemer that is used in 'mkTestTree'. In this case it'll always be
|
||||||
mkStakeRedeemer = PermitVote . mkNewLock
|
'PermitVote'.
|
||||||
|
-}
|
||||||
|
stakeRedeemer :: StakeRedeemer
|
||||||
|
stakeRedeemer = PermitVote
|
||||||
|
|
||||||
-- | Create a valid transaction that votes on a propsal, given the parameters.
|
-- | Create a valid transaction that votes on a propsal, given the parameters.
|
||||||
vote :: Parameters -> TxInfo
|
vote :: Parameters -> TxInfo
|
||||||
|
|
@ -210,6 +232,7 @@ vote params =
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
-- | Valida parameters that vote on the proposal.
|
||||||
validVoteParameters :: Parameters
|
validVoteParameters :: Parameters
|
||||||
validVoteParameters =
|
validVoteParameters =
|
||||||
Parameters
|
Parameters
|
||||||
|
|
@ -219,13 +242,16 @@ validVoteParameters =
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
{- | Create a test tree that runs the stake validator and proposal validator to
|
||||||
|
test the voting functionalities.
|
||||||
|
-}
|
||||||
mkTestTree :: String -> Parameters -> Bool -> SpecificationTree
|
mkTestTree :: String -> Parameters -> Bool -> SpecificationTree
|
||||||
mkTestTree name ps isValid = group name [proposal, stake]
|
mkTestTree name ps isValid = group name [proposal, stake]
|
||||||
where
|
where
|
||||||
txInfo = vote ps
|
txInfo = vote ps
|
||||||
|
|
||||||
proposal =
|
proposal =
|
||||||
testFunc
|
testValidator
|
||||||
isValid
|
isValid
|
||||||
"propsoal"
|
"propsoal"
|
||||||
(proposalValidator Shared.proposal)
|
(proposalValidator Shared.proposal)
|
||||||
|
|
@ -242,7 +268,7 @@ mkTestTree name ps isValid = group name [proposal, stake]
|
||||||
"stake"
|
"stake"
|
||||||
(stakeValidator Shared.stake)
|
(stakeValidator Shared.stake)
|
||||||
stakeInputDatum
|
stakeInputDatum
|
||||||
(mkStakeRedeemer ps)
|
stakeRedeemer
|
||||||
( ScriptContext
|
( ScriptContext
|
||||||
txInfo
|
txInfo
|
||||||
(Spending stakeRef)
|
(Spending stakeRef)
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ specs =
|
||||||
(ProposalId 0)
|
(ProposalId 0)
|
||||||
def
|
def
|
||||||
def
|
def
|
||||||
|
3
|
||||||
)
|
)
|
||||||
MutateGovernor
|
MutateGovernor
|
||||||
( ScriptContext
|
( ScriptContext
|
||||||
|
|
@ -60,6 +61,7 @@ specs =
|
||||||
(ProposalId 0)
|
(ProposalId 0)
|
||||||
def
|
def
|
||||||
def
|
def
|
||||||
|
3
|
||||||
)
|
)
|
||||||
MutateGovernor
|
MutateGovernor
|
||||||
( ScriptContext
|
( ScriptContext
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ specs =
|
||||||
(ProposalId 0)
|
(ProposalId 0)
|
||||||
def
|
def
|
||||||
def
|
def
|
||||||
|
3
|
||||||
)
|
)
|
||||||
CreateProposal
|
CreateProposal
|
||||||
createProposal
|
createProposal
|
||||||
|
|
@ -58,6 +59,7 @@ specs =
|
||||||
(ProposalId 5)
|
(ProposalId 5)
|
||||||
def
|
def
|
||||||
def
|
def
|
||||||
|
3
|
||||||
)
|
)
|
||||||
MintGATs
|
MintGATs
|
||||||
mintGATs
|
mintGATs
|
||||||
|
|
@ -69,6 +71,7 @@ specs =
|
||||||
(ProposalId 5)
|
(ProposalId 5)
|
||||||
def
|
def
|
||||||
def
|
def
|
||||||
|
3
|
||||||
)
|
)
|
||||||
MutateGovernor
|
MutateGovernor
|
||||||
mutateState
|
mutateState
|
||||||
|
|
|
||||||
|
|
@ -7,33 +7,83 @@ Tests for Proposal policy and validator
|
||||||
-}
|
-}
|
||||||
module Spec.Proposal (specs) where
|
module Spec.Proposal (specs) where
|
||||||
|
|
||||||
import Agora.Proposal (
|
|
||||||
Proposal (..),
|
|
||||||
ProposalStatus (..),
|
|
||||||
)
|
|
||||||
import Agora.Proposal.Scripts (proposalPolicy)
|
|
||||||
import Sample.Proposal qualified as Proposal
|
|
||||||
import Sample.Proposal.Advance qualified as Advance
|
import Sample.Proposal.Advance qualified as Advance
|
||||||
import Sample.Proposal.Cosign qualified as Cosign
|
import Sample.Proposal.Cosign qualified as Cosign
|
||||||
|
import Sample.Proposal.Create qualified as Create
|
||||||
import Sample.Proposal.UnlockStake qualified as UnlockStake
|
import Sample.Proposal.UnlockStake qualified as UnlockStake
|
||||||
import Sample.Proposal.Vote qualified as Vote
|
import Sample.Proposal.Vote qualified as Vote
|
||||||
import Sample.Shared qualified as Shared (proposal)
|
|
||||||
import Test.Specification (
|
import Test.Specification (
|
||||||
SpecificationTree,
|
SpecificationTree,
|
||||||
group,
|
group,
|
||||||
policySucceedsWith,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
-- | Stake specs.
|
-- | Stake specs.
|
||||||
specs :: [SpecificationTree]
|
specs :: [SpecificationTree]
|
||||||
specs =
|
specs =
|
||||||
[ group
|
[ group
|
||||||
"policy"
|
"policy (proposal creation)"
|
||||||
[ policySucceedsWith
|
[ Create.mkTestTree
|
||||||
"proposalCreation"
|
"legal"
|
||||||
(proposalPolicy Shared.proposal.governorSTAssetClass)
|
Create.totallyValidParameters
|
||||||
()
|
True
|
||||||
Proposal.proposalCreation
|
True
|
||||||
|
True
|
||||||
|
, group
|
||||||
|
"illegal"
|
||||||
|
[ Create.mkTestTree
|
||||||
|
"invalid next proposal id"
|
||||||
|
Create.invalidOutputGovernorDatumParameters
|
||||||
|
True
|
||||||
|
False
|
||||||
|
True
|
||||||
|
, Create.mkTestTree
|
||||||
|
"use other's stake"
|
||||||
|
Create.useStakeOwnBySomeoneElseParameters
|
||||||
|
True
|
||||||
|
False
|
||||||
|
False
|
||||||
|
, Create.mkTestTree
|
||||||
|
"altered stake"
|
||||||
|
Create.invalidOutputStakeParameters
|
||||||
|
True
|
||||||
|
False
|
||||||
|
False
|
||||||
|
, Create.mkTestTree
|
||||||
|
"invalid stake locks"
|
||||||
|
Create.addInvalidLocksParameters
|
||||||
|
True
|
||||||
|
False
|
||||||
|
True
|
||||||
|
, Create.mkTestTree
|
||||||
|
"has reached maximum proposals limit"
|
||||||
|
Create.exceedMaximumProposalsParameters
|
||||||
|
True
|
||||||
|
False
|
||||||
|
True
|
||||||
|
, Create.mkTestTree
|
||||||
|
"loose time range"
|
||||||
|
Create.timeRangeNotTightParameters
|
||||||
|
True
|
||||||
|
False
|
||||||
|
True
|
||||||
|
, Create.mkTestTree
|
||||||
|
"open time range"
|
||||||
|
Create.timeRangeNotClosedParameters
|
||||||
|
True
|
||||||
|
False
|
||||||
|
True
|
||||||
|
, group "invalid proposal status" $
|
||||||
|
map
|
||||||
|
( \ps ->
|
||||||
|
Create.mkTestTree
|
||||||
|
(show ps.proposalStatus)
|
||||||
|
ps
|
||||||
|
True
|
||||||
|
False
|
||||||
|
True
|
||||||
|
)
|
||||||
|
Create.invalidProposalStatusParameters
|
||||||
|
]
|
||||||
]
|
]
|
||||||
, group
|
, group
|
||||||
"validator"
|
"validator"
|
||||||
|
|
@ -166,110 +216,92 @@ specs =
|
||||||
pure $ Advance.mkTestTree name ps False
|
pure $ Advance.mkTestTree name ps False
|
||||||
]
|
]
|
||||||
in [draftGroup, legalGroup, illegalGroup]
|
in [draftGroup, legalGroup, illegalGroup]
|
||||||
, group "unlocking" $ do
|
, group "unlocking" $
|
||||||
proposalCount <- [1, 42]
|
let proposalCountCases = [1, 5, 10, 42]
|
||||||
|
|
||||||
let legalGroup = group "legal" $ do
|
mkSubgroupName nProposals = "with " <> show nProposals <> " proposals"
|
||||||
let voterRetractVotesAndUnlockStakeWhileVoting =
|
|
||||||
UnlockStake.mkProposalValidatorTestCase
|
|
||||||
UnlockStake.UnlockStakeParameters
|
|
||||||
{ UnlockStake.proposalCount = proposalCount
|
|
||||||
, UnlockStake.stakeUsage = UnlockStake.Voter
|
|
||||||
, UnlockStake.retractVotes = True
|
|
||||||
, UnlockStake.proposalStatus = VotingReady
|
|
||||||
}
|
|
||||||
True
|
|
||||||
creatorUnlockStakeWhileFinished =
|
|
||||||
UnlockStake.mkProposalValidatorTestCase
|
|
||||||
UnlockStake.UnlockStakeParameters
|
|
||||||
{ UnlockStake.proposalCount = proposalCount
|
|
||||||
, UnlockStake.stakeUsage = UnlockStake.Creator
|
|
||||||
, UnlockStake.retractVotes = False
|
|
||||||
, UnlockStake.proposalStatus = Finished
|
|
||||||
}
|
|
||||||
True
|
|
||||||
|
|
||||||
let voterUnlockStakeAfterVoting = group "voter unlocks stake after voting" $ do
|
mkLegalGroup nProposals =
|
||||||
status <- [Finished, Locked]
|
group
|
||||||
|
(mkSubgroupName nProposals)
|
||||||
pure $
|
[ UnlockStake.mkTestTree
|
||||||
UnlockStake.mkProposalValidatorTestCase
|
"voter: retract votes while voting"
|
||||||
UnlockStake.UnlockStakeParameters
|
(UnlockStake.mkVoterRetractVotesWhileVotingParameters nProposals)
|
||||||
{ UnlockStake.proposalCount = proposalCount
|
True
|
||||||
, UnlockStake.stakeUsage = UnlockStake.Voter
|
, UnlockStake.mkTestTree
|
||||||
, UnlockStake.retractVotes = False
|
"voter/creator: retract votes while voting"
|
||||||
, UnlockStake.proposalStatus = status
|
(UnlockStake.mkVoterCreatorRetractVotesWhileVotingParameters nProposals)
|
||||||
}
|
True
|
||||||
True
|
, UnlockStake.mkTestTree
|
||||||
|
"creator: remove creator locks when finished"
|
||||||
[ voterRetractVotesAndUnlockStakeWhileVoting
|
(UnlockStake.mkCreatorRemoveCreatorLocksWhenFinishedParameters nProposals)
|
||||||
, creatorUnlockStakeWhileFinished
|
True
|
||||||
, voterUnlockStakeAfterVoting
|
, UnlockStake.mkTestTree
|
||||||
|
"voter/creator: remove all locks when finished"
|
||||||
|
(UnlockStake.mkVoterCreatorRemoveAllLocksWhenFinishedParameters nProposals)
|
||||||
|
True
|
||||||
|
, group "voter: unlock after voting" $
|
||||||
|
map
|
||||||
|
( \ps ->
|
||||||
|
let name = show ps.proposalStatus
|
||||||
|
in UnlockStake.mkTestTree name ps True
|
||||||
|
)
|
||||||
|
(UnlockStake.mkVoterUnlockStakeAfterVotingParameters nProposals)
|
||||||
|
, UnlockStake.mkTestTree
|
||||||
|
"voter/creator: remove vote locks when locked"
|
||||||
|
(UnlockStake.mkVoterCreatorRemoveVoteLocksWhenLockedParameters nProposals)
|
||||||
|
True
|
||||||
]
|
]
|
||||||
|
|
||||||
let illegalGroup = group "illegal" $ do
|
mkIllegalGroup nProposals =
|
||||||
let retractsVotesWhileNotVotingReady =
|
group
|
||||||
group "voter retracts votes while not voting" $ do
|
(mkSubgroupName nProposals)
|
||||||
status <- [Draft, Locked, Finished]
|
[ group "retract votes while not voting" $
|
||||||
|
map
|
||||||
pure $
|
( \ps ->
|
||||||
UnlockStake.mkProposalValidatorTestCase
|
let name =
|
||||||
UnlockStake.UnlockStakeParameters
|
"role: " <> show ps.stakeRole
|
||||||
{ UnlockStake.proposalCount = proposalCount
|
<> ", status: "
|
||||||
, UnlockStake.stakeUsage = UnlockStake.Voter
|
<> show ps.proposalStatus
|
||||||
, UnlockStake.retractVotes = True
|
in UnlockStake.mkTestTree name ps False
|
||||||
, UnlockStake.proposalStatus = status
|
)
|
||||||
}
|
(UnlockStake.mkRetractVotesWhileNotVoting nProposals)
|
||||||
False
|
, group "unlock an irrelevant stake" $
|
||||||
|
map
|
||||||
unlockIrrelevantStake =
|
( \ps ->
|
||||||
group "unlock an irrelevant stake" $ do
|
let name =
|
||||||
status <- [Draft, VotingReady, Locked, Finished]
|
"status: " <> show ps.proposalStatus
|
||||||
shouldRetractVotes <- [True, False]
|
<> "retract votes: "
|
||||||
|
<> show ps.retractVotes
|
||||||
pure $
|
in UnlockStake.mkTestTree name ps False
|
||||||
UnlockStake.mkProposalValidatorTestCase
|
)
|
||||||
UnlockStake.UnlockStakeParameters
|
(UnlockStake.mkUnockIrrelevantStakeParameters nProposals)
|
||||||
{ UnlockStake.proposalCount = proposalCount
|
, group "remove creator too early" $
|
||||||
, UnlockStake.stakeUsage = UnlockStake.Irrelevant
|
map
|
||||||
, UnlockStake.retractVotes = shouldRetractVotes
|
( \ps ->
|
||||||
, UnlockStake.proposalStatus = status
|
let name =
|
||||||
}
|
"status: " <> show ps.proposalStatus
|
||||||
False
|
in UnlockStake.mkTestTree name ps False
|
||||||
|
)
|
||||||
unlockCreatorStakeBeforeFinished =
|
(UnlockStake.mkRemoveCreatorLockBeforeFinishedParameters nProposals)
|
||||||
group "unlock creator stake before finished" $ do
|
, UnlockStake.mkTestTree
|
||||||
status <- [Draft, VotingReady, Locked]
|
"creator: retract votes"
|
||||||
|
(UnlockStake.mkRetractVotesWithCreatorStakeParamaters nProposals)
|
||||||
pure $
|
False
|
||||||
UnlockStake.mkProposalValidatorTestCase
|
, group "alter output stake datum" $
|
||||||
UnlockStake.UnlockStakeParameters
|
map
|
||||||
{ UnlockStake.proposalCount = proposalCount
|
( \ps ->
|
||||||
, UnlockStake.stakeUsage = UnlockStake.Creator
|
let name =
|
||||||
, UnlockStake.retractVotes = False
|
"role: " <> show ps.stakeRole
|
||||||
, UnlockStake.proposalStatus = status
|
<> ", status: "
|
||||||
}
|
<> show ps.proposalStatus
|
||||||
False
|
in UnlockStake.mkTestTree name ps False
|
||||||
retractVotesWithCreatorStake =
|
)
|
||||||
group "creator stake retracts votes" $ do
|
(UnlockStake.mkAlterStakeParameters nProposals)
|
||||||
status <- [Draft, VotingReady, Locked, Finished]
|
|
||||||
|
|
||||||
pure $
|
|
||||||
UnlockStake.mkProposalValidatorTestCase
|
|
||||||
UnlockStake.UnlockStakeParameters
|
|
||||||
{ UnlockStake.proposalCount = proposalCount
|
|
||||||
, UnlockStake.stakeUsage = UnlockStake.Creator
|
|
||||||
, UnlockStake.retractVotes = True
|
|
||||||
, UnlockStake.proposalStatus = status
|
|
||||||
}
|
|
||||||
False
|
|
||||||
|
|
||||||
[ retractsVotesWhileNotVotingReady
|
|
||||||
, unlockIrrelevantStake
|
|
||||||
, unlockCreatorStakeBeforeFinished
|
|
||||||
, retractVotesWithCreatorStake
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[legalGroup, illegalGroup]
|
legalGroup = group "legal" $ map mkLegalGroup proposalCountCases
|
||||||
|
illegalGroup = group "illegal" $ map mkIllegalGroup proposalCountCases
|
||||||
|
in [legalGroup, illegalGroup]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,8 @@ module Test.Specification (
|
||||||
validatorFailsWith,
|
validatorFailsWith,
|
||||||
effectSucceedsWith,
|
effectSucceedsWith,
|
||||||
effectFailsWith,
|
effectFailsWith,
|
||||||
|
testValidator,
|
||||||
|
testPolicy,
|
||||||
|
|
||||||
-- * Converters
|
-- * Converters
|
||||||
toTestTree,
|
toTestTree,
|
||||||
|
|
@ -253,3 +255,37 @@ effectFailsWith ::
|
||||||
ScriptContext ->
|
ScriptContext ->
|
||||||
SpecificationTree
|
SpecificationTree
|
||||||
effectFailsWith tag eff datum = validatorFailsWith tag eff datum ()
|
effectFailsWith tag eff datum = validatorFailsWith tag eff datum ()
|
||||||
|
|
||||||
|
testValidator ::
|
||||||
|
( PLift datum
|
||||||
|
, PlutusTx.ToData (PLifted datum)
|
||||||
|
, PLift redeemer
|
||||||
|
, PlutusTx.ToData (PLifted redeemer)
|
||||||
|
) =>
|
||||||
|
-- | Should the validator pass?
|
||||||
|
Bool ->
|
||||||
|
String ->
|
||||||
|
ClosedTerm PValidator ->
|
||||||
|
PLifted datum ->
|
||||||
|
PLifted redeemer ->
|
||||||
|
ScriptContext ->
|
||||||
|
SpecificationTree
|
||||||
|
testValidator isValid =
|
||||||
|
if isValid
|
||||||
|
then validatorSucceedsWith
|
||||||
|
else validatorFailsWith
|
||||||
|
|
||||||
|
testPolicy ::
|
||||||
|
( PLift redeemer
|
||||||
|
, PlutusTx.ToData (PLifted redeemer)
|
||||||
|
) =>
|
||||||
|
Bool ->
|
||||||
|
String ->
|
||||||
|
ClosedTerm PMintingPolicy ->
|
||||||
|
PLifted redeemer ->
|
||||||
|
ScriptContext ->
|
||||||
|
SpecificationTree
|
||||||
|
testPolicy isValid =
|
||||||
|
if isValid
|
||||||
|
then policySucceedsWith
|
||||||
|
else policyFailsWith
|
||||||
|
|
|
||||||
|
|
@ -95,12 +95,16 @@ updateMap f k =
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- | Sort the given 'AssocMap.Map' by keys in ascending order.
|
||||||
sortMap :: forall k v. Ord k => AssocMap.Map k v -> AssocMap.Map k v
|
sortMap :: forall k v. Ord k => AssocMap.Map k v -> AssocMap.Map k v
|
||||||
sortMap =
|
sortMap =
|
||||||
AssocMap.fromList
|
AssocMap.fromList
|
||||||
. sortOn fst
|
. sortOn fst
|
||||||
. AssocMap.toList
|
. AssocMap.toList
|
||||||
|
|
||||||
|
{- | Sort the given 'Value' in ascending order. Some plutarch functions that
|
||||||
|
work with plutarch's 'Sorted' 'PMap' require this to work correctly.
|
||||||
|
-}
|
||||||
sortValue :: Value -> Value
|
sortValue :: Value -> Value
|
||||||
sortValue =
|
sortValue =
|
||||||
Value
|
Value
|
||||||
|
|
|
||||||
|
|
@ -188,9 +188,9 @@ library agora-specs
|
||||||
Sample.Effect.GovernorMutation
|
Sample.Effect.GovernorMutation
|
||||||
Sample.Effect.TreasuryWithdrawal
|
Sample.Effect.TreasuryWithdrawal
|
||||||
Sample.Governor
|
Sample.Governor
|
||||||
Sample.Proposal
|
|
||||||
Sample.Proposal.Advance
|
Sample.Proposal.Advance
|
||||||
Sample.Proposal.Cosign
|
Sample.Proposal.Cosign
|
||||||
|
Sample.Proposal.Create
|
||||||
Sample.Proposal.Shared
|
Sample.Proposal.Shared
|
||||||
Sample.Proposal.UnlockStake
|
Sample.Proposal.UnlockStake
|
||||||
Sample.Proposal.Vote
|
Sample.Proposal.Vote
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,9 @@ data GovernorDatum = GovernorDatum
|
||||||
-- Will get copied over upon the creation of proposals.
|
-- Will get copied over upon the creation of proposals.
|
||||||
, createProposalTimeRangeMaxWidth :: MaxTimeRangeWidth
|
, createProposalTimeRangeMaxWidth :: MaxTimeRangeWidth
|
||||||
-- ^ The maximum valid duration of a transaction that creats a proposal.
|
-- ^ The maximum valid duration of a transaction that creats a proposal.
|
||||||
|
, maximumProposalsPerStake :: Integer
|
||||||
|
-- ^ The maximum number of unfinished proposals that a stake is allowed to be
|
||||||
|
-- associated to.
|
||||||
}
|
}
|
||||||
deriving stock (Show, GHC.Generic)
|
deriving stock (Show, GHC.Generic)
|
||||||
|
|
||||||
|
|
@ -149,6 +152,7 @@ newtype PGovernorDatum (s :: S) = PGovernorDatum
|
||||||
, "nextProposalId" ':= PProposalId
|
, "nextProposalId" ':= PProposalId
|
||||||
, "proposalTimings" ':= PProposalTimingConfig
|
, "proposalTimings" ':= PProposalTimingConfig
|
||||||
, "createProposalTimeRangeMaxWidth" ':= PMaxTimeRangeWidth
|
, "createProposalTimeRangeMaxWidth" ':= PMaxTimeRangeWidth
|
||||||
|
, "maximumProposalsPerStake" ':= PInteger
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,6 @@ import Agora.Governor (
|
||||||
)
|
)
|
||||||
import Agora.Proposal (
|
import Agora.Proposal (
|
||||||
PProposalDatum (..),
|
PProposalDatum (..),
|
||||||
PProposalId (..),
|
|
||||||
PResultTag,
|
|
||||||
Proposal (..),
|
Proposal (..),
|
||||||
ProposalStatus (Draft, Finished, Locked),
|
ProposalStatus (Draft, Finished, Locked),
|
||||||
pemptyVotesFor,
|
pemptyVotesFor,
|
||||||
|
|
@ -65,6 +63,7 @@ import Agora.Stake (
|
||||||
PProposalLock (..),
|
PProposalLock (..),
|
||||||
PStakeDatum (..),
|
PStakeDatum (..),
|
||||||
Stake (..),
|
Stake (..),
|
||||||
|
pnumCreatedProposals,
|
||||||
)
|
)
|
||||||
import Agora.Stake.Scripts (
|
import Agora.Stake.Scripts (
|
||||||
stakePolicy,
|
stakePolicy,
|
||||||
|
|
@ -108,7 +107,6 @@ import Plutarch.Api.V1.ScriptContext (pfindTxInByTxOutRef, pisUTXOSpent, ptryFin
|
||||||
import "liqwid-plutarch-extra" Plutarch.Api.V1.Value (psymbolValueOf)
|
import "liqwid-plutarch-extra" Plutarch.Api.V1.Value (psymbolValueOf)
|
||||||
import Plutarch.Extra.IsData (pmatchEnumFromData)
|
import Plutarch.Extra.IsData (pmatchEnumFromData)
|
||||||
import Plutarch.Extra.Map (
|
import Plutarch.Extra.Map (
|
||||||
pkeys,
|
|
||||||
plookup,
|
plookup,
|
||||||
plookup',
|
plookup',
|
||||||
)
|
)
|
||||||
|
|
@ -300,6 +298,7 @@ governorValidator gov =
|
||||||
, "nextProposalId"
|
, "nextProposalId"
|
||||||
, "proposalTimings"
|
, "proposalTimings"
|
||||||
, "createProposalTimeRangeMaxWidth"
|
, "createProposalTimeRangeMaxWidth"
|
||||||
|
, "maximumProposalsPerStake"
|
||||||
]
|
]
|
||||||
oldGovernorDatum
|
oldGovernorDatum
|
||||||
|
|
||||||
|
|
@ -341,6 +340,8 @@ governorValidator gov =
|
||||||
.& #proposalTimings .= oldGovernorDatumF.proposalTimings
|
.& #proposalTimings .= oldGovernorDatumF.proposalTimings
|
||||||
.& #createProposalTimeRangeMaxWidth
|
.& #createProposalTimeRangeMaxWidth
|
||||||
.= oldGovernorDatumF.createProposalTimeRangeMaxWidth
|
.= oldGovernorDatumF.createProposalTimeRangeMaxWidth
|
||||||
|
.& #maximumProposalsPerStake
|
||||||
|
.= oldGovernorDatumF.maximumProposalsPerStake
|
||||||
)
|
)
|
||||||
pguardC "Unexpected governor state datum" $
|
pguardC "Unexpected governor state datum" $
|
||||||
newGovernorDatum #== expectedNewDatum
|
newGovernorDatum #== expectedNewDatum
|
||||||
|
|
@ -377,6 +378,10 @@ governorValidator gov =
|
||||||
stakeInputDatumF <-
|
stakeInputDatumF <-
|
||||||
pletFieldsC @["stakedAmount", "owner", "lockedBy"] stakeInputDatum
|
pletFieldsC @["stakedAmount", "owner", "lockedBy"] stakeInputDatum
|
||||||
|
|
||||||
|
pguardC "Proposals created by the stake must not exceed the number stored in the governor." $
|
||||||
|
pnumCreatedProposals # stakeInputDatumF.lockedBy
|
||||||
|
#< oldGovernorDatumF.maximumProposalsPerStake
|
||||||
|
|
||||||
pguardC "Required amount of stake GTs should be presented" $
|
pguardC "Required amount of stake GTs should be presented" $
|
||||||
stakeInputDatumF.stakedAmount #== (pgtValueOf # stakeInputF.value)
|
stakeInputDatumF.stakedAmount #== (pgtValueOf # stakeInputF.value)
|
||||||
|
|
||||||
|
|
@ -479,25 +484,14 @@ governorValidator gov =
|
||||||
mustBePJust # "Stake output not found" #$ ptryFindDatum # stakeOutputDatumHash # txInfoF.datums
|
mustBePJust # "Stake output not found" #$ ptryFindDatum # stakeOutputDatumHash # txInfoF.datums
|
||||||
|
|
||||||
-- The stake should be locked by the newly created proposal.
|
-- The stake should be locked by the newly created proposal.
|
||||||
|
let newLock =
|
||||||
let possibleVoteResults = pkeys #$ pto $ pfromData proposalOutputDatum.votes
|
mkRecordConstr
|
||||||
|
PCreated
|
||||||
mkProposalLock :: Term _ (PProposalId :--> PAsData PResultTag :--> PAsData PProposalLock)
|
( #created .= oldGovernorDatumF.nextProposalId
|
||||||
mkProposalLock =
|
)
|
||||||
phoistAcyclic $
|
|
||||||
plam
|
|
||||||
( \pid rt' ->
|
|
||||||
pdata $
|
|
||||||
mkRecordConstr
|
|
||||||
PProposalLock
|
|
||||||
( #vote .= rt' .& #proposalTag .= pdata pid
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
-- Append new locks to existing locks
|
-- Append new locks to existing locks
|
||||||
expectedProposalLocks =
|
expectedProposalLocks = pcons # pdata newLock # stakeInputDatumF.lockedBy
|
||||||
pconcat # stakeInputDatumF.lockedBy
|
|
||||||
#$ pmap # (mkProposalLock # proposalOutputDatum.proposalId) # possibleVoteResults
|
|
||||||
|
|
||||||
expectedStakeOutputDatum =
|
expectedStakeOutputDatum =
|
||||||
pdata $
|
pdata $
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ import Plutarch.Lift (
|
||||||
PUnsafeLiftDecl (..),
|
PUnsafeLiftDecl (..),
|
||||||
)
|
)
|
||||||
import Plutarch.SafeMoney (PDiscrete)
|
import Plutarch.SafeMoney (PDiscrete)
|
||||||
|
import Plutarch.Show (PShow (..))
|
||||||
import PlutusLedgerApi.V1 (DatumHash, PubKeyHash, ValidatorHash)
|
import PlutusLedgerApi.V1 (DatumHash, PubKeyHash, ValidatorHash)
|
||||||
import PlutusLedgerApi.V1.Value (AssetClass)
|
import PlutusLedgerApi.V1.Value (AssetClass)
|
||||||
import PlutusTx qualified
|
import PlutusTx qualified
|
||||||
|
|
@ -328,7 +329,7 @@ data ProposalRedeemer
|
||||||
-- This list should be sorted in ascending order.
|
-- This list should be sorted in ascending order.
|
||||||
Cosign [PubKeyHash]
|
Cosign [PubKeyHash]
|
||||||
| -- | Allow unlocking one or more stakes with votes towards particular 'ResultTag'.
|
| -- | Allow unlocking one or more stakes with votes towards particular 'ResultTag'.
|
||||||
Unlock ResultTag
|
Unlock
|
||||||
| -- | Advance the proposal, performing the required checks for whether that is legal.
|
| -- | Advance the proposal, performing the required checks for whether that is legal.
|
||||||
--
|
--
|
||||||
-- These are roughly the checks for each possible transition:
|
-- These are roughly the checks for each possible transition:
|
||||||
|
|
@ -408,6 +409,8 @@ newtype PResultTag (s :: S) = PResultTag (Term s PInteger)
|
||||||
PEq
|
PEq
|
||||||
, -- | @since 0.1.0
|
, -- | @since 0.1.0
|
||||||
POrd
|
POrd
|
||||||
|
, -- | @since 0.2.0
|
||||||
|
PShow
|
||||||
)
|
)
|
||||||
via (DerivePNewtype PResultTag PInteger)
|
via (DerivePNewtype PResultTag PInteger)
|
||||||
|
|
||||||
|
|
@ -440,6 +443,8 @@ newtype PProposalId (s :: S) = PProposalId (Term s PInteger)
|
||||||
PEq
|
PEq
|
||||||
, -- | @since 0.1.0
|
, -- | @since 0.1.0
|
||||||
POrd
|
POrd
|
||||||
|
, -- | @since 0.2.0
|
||||||
|
PShow
|
||||||
)
|
)
|
||||||
via (DerivePNewtype PProposalId PInteger)
|
via (DerivePNewtype PProposalId PInteger)
|
||||||
|
|
||||||
|
|
@ -665,7 +670,7 @@ deriving via (DerivePConstantViaDataList ProposalDatum PProposalDatum) instance
|
||||||
data PProposalRedeemer (s :: S)
|
data PProposalRedeemer (s :: S)
|
||||||
= PVote (Term s (PDataRecord '["resultTag" ':= PResultTag]))
|
= PVote (Term s (PDataRecord '["resultTag" ':= PResultTag]))
|
||||||
| PCosign (Term s (PDataRecord '["newCosigners" ':= PBuiltinList (PAsData PPubKeyHash)]))
|
| PCosign (Term s (PDataRecord '["newCosigners" ':= PBuiltinList (PAsData PPubKeyHash)]))
|
||||||
| PUnlock (Term s (PDataRecord '["resultTag" ':= PResultTag]))
|
| PUnlock (Term s (PDataRecord '[]))
|
||||||
| PAdvanceProposal (Term s (PDataRecord '[]))
|
| PAdvanceProposal (Term s (PDataRecord '[]))
|
||||||
deriving stock
|
deriving stock
|
||||||
( -- | @since 0.1.0
|
( -- | @since 0.1.0
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,12 @@ import Agora.Proposal.Time (
|
||||||
import Agora.Stake (
|
import Agora.Stake (
|
||||||
PProposalLock (..),
|
PProposalLock (..),
|
||||||
PStakeDatum (..),
|
PStakeDatum (..),
|
||||||
PStakeUsage (..),
|
pextractVoteOption,
|
||||||
pgetStakeUsage,
|
pgetStakeRole,
|
||||||
|
pisCreator,
|
||||||
|
pisIrrelevant,
|
||||||
|
pisPureCreator,
|
||||||
|
pisVoter,
|
||||||
)
|
)
|
||||||
import Agora.Utils (
|
import Agora.Utils (
|
||||||
getMintingPolicySymbol,
|
getMintingPolicySymbol,
|
||||||
|
|
@ -469,12 +473,7 @@ proposalValidator proposal =
|
||||||
|
|
||||||
-- Ensure that no lock with the current proposal id has been put on the stake.
|
-- Ensure that no lock with the current proposal id has been put on the stake.
|
||||||
pguardC "Same stake shouldn't vote on the same proposal twice" $
|
pguardC "Same stake shouldn't vote on the same proposal twice" $
|
||||||
pnot #$ pany
|
pnot #$ pisVoter #$ pgetStakeRole # proposalF.proposalId # pfromData stakeInF.lockedBy
|
||||||
# plam
|
|
||||||
( \((pfield @"proposalTag" #) . pfromData -> pid) ->
|
|
||||||
pid #== proposalF.proposalId
|
|
||||||
)
|
|
||||||
# pfromData stakeInF.lockedBy
|
|
||||||
|
|
||||||
let -- The amount of new votes should be the 'stakedAmount'.
|
let -- The amount of new votes should be the 'stakedAmount'.
|
||||||
-- Update the vote counter of the proposal, and leave other stuff as is.
|
-- Update the vote counter of the proposal, and leave other stuff as is.
|
||||||
|
|
@ -510,9 +509,9 @@ proposalValidator proposal =
|
||||||
|
|
||||||
let newProposalLock =
|
let newProposalLock =
|
||||||
mkRecordConstr
|
mkRecordConstr
|
||||||
PProposalLock
|
PVoted
|
||||||
( #vote .= pdata voteFor
|
( #votedOn .= proposalF.proposalId
|
||||||
.& #proposalTag .= proposalF.proposalId
|
.& #votedFor .= pdata voteFor
|
||||||
)
|
)
|
||||||
-- Prepend the new lock to existing locks
|
-- Prepend the new lock to existing locks
|
||||||
expectedProposalLocks =
|
expectedProposalLocks =
|
||||||
|
|
@ -533,30 +532,12 @@ proposalValidator proposal =
|
||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
PUnlock r -> withSingleStake $ \stakeInF stakeOut _ -> do
|
PUnlock _ -> withSingleStake $ \stakeInF stakeOut _ -> do
|
||||||
-- At draft stage, the votes should be empty.
|
stakeRole <- pletC $ pgetStakeRole # proposalF.proposalId # stakeInF.lockedBy
|
||||||
pguardC "Shouldn't retract votes from a draft proposal" $
|
|
||||||
pnot #$ currentStatus #== pconstant Draft
|
|
||||||
|
|
||||||
-- This is the vote option we're retracting from.
|
pguardC "Stake input should be relevant" $
|
||||||
retractFrom <- pletC $ pfield @"resultTag" # r
|
pnot #$ pisIrrelevant # stakeRole
|
||||||
|
|
||||||
-- Determine if the input stake is actually locked by this proposal.
|
|
||||||
stakeUsage <- pletC $ pgetStakeUsage # stakeInF.lockedBy # proposalF.proposalId
|
|
||||||
|
|
||||||
pguardC "Stake input relevant" $
|
|
||||||
pmatch stakeUsage $ \case
|
|
||||||
PDidNothing ->
|
|
||||||
ptraceIfFalse "Stake should be relevant" $
|
|
||||||
pconstant False
|
|
||||||
PCreated ->
|
|
||||||
ptraceIfFalse "Removing creator's locks means status is Finished" $
|
|
||||||
currentStatus #== pconstant Finished
|
|
||||||
PVotedFor rt ->
|
|
||||||
ptraceIfFalse "Result tag should match the one given in the redeemer" $
|
|
||||||
rt #== retractFrom
|
|
||||||
|
|
||||||
-- The count of removing votes is equal to the 'stakeAmount' of input stake.
|
|
||||||
retractCount <-
|
retractCount <-
|
||||||
pletC $
|
pletC $
|
||||||
pmatch stakeInF.stakedAmount $ \(PDiscrete v) -> pextract # v
|
pmatch stakeInF.stakedAmount $ \(PDiscrete v) -> pextract # v
|
||||||
|
|
@ -564,13 +545,34 @@ proposalValidator proposal =
|
||||||
-- The votes can only change when the proposal still allows voting.
|
-- The votes can only change when the proposal still allows voting.
|
||||||
let shouldUpdateVotes =
|
let shouldUpdateVotes =
|
||||||
currentStatus #== pconstant VotingReady
|
currentStatus #== pconstant VotingReady
|
||||||
#&& pnot # (pcon PCreated #== stakeUsage)
|
#&& pnot # (pisPureCreator # stakeRole)
|
||||||
|
|
||||||
|
allowRemovingCreatorLock =
|
||||||
|
currentStatus #== pconstant Finished
|
||||||
|
|
||||||
|
isCreator = pisCreator # stakeRole
|
||||||
|
|
||||||
|
-- If the stake has been used for creating the proposal,
|
||||||
|
-- the creator lock can only be removed when the proposal
|
||||||
|
-- is finished.
|
||||||
|
--
|
||||||
|
-- In other cases, all the locks related to this
|
||||||
|
-- proposal should be removed.
|
||||||
|
validateOutputLocks = plam $ \locks ->
|
||||||
|
plet
|
||||||
|
( pgetStakeRole # proposalF.proposalId # locks
|
||||||
|
)
|
||||||
|
$ \newStakeRole ->
|
||||||
|
pif
|
||||||
|
(isCreator #&& pnot # allowRemovingCreatorLock)
|
||||||
|
(pisPureCreator # newStakeRole)
|
||||||
|
(pisIrrelevant # newStakeRole)
|
||||||
|
|
||||||
pguardC "Proposal output correct" $
|
pguardC "Proposal output correct" $
|
||||||
pif
|
pif
|
||||||
shouldUpdateVotes
|
shouldUpdateVotes
|
||||||
( let -- Remove votes and leave other parts of the proposal as it.
|
( let -- Remove votes and leave other parts of the proposal as it.
|
||||||
expectedVotes = pretractVotes # retractFrom # retractCount # proposalF.votes
|
expectedVotes = pretractVotes # (pextractVoteOption # stakeRole) # retractCount # proposalF.votes
|
||||||
|
|
||||||
expectedProposalOut =
|
expectedProposalOut =
|
||||||
mkRecordConstr
|
mkRecordConstr
|
||||||
|
|
@ -598,15 +600,14 @@ proposalValidator proposal =
|
||||||
PStakeDatum
|
PStakeDatum
|
||||||
( #stakedAmount .= stakeInF.stakedAmount
|
( #stakedAmount .= stakeInF.stakedAmount
|
||||||
.& #owner .= stakeInF.owner
|
.& #owner .= stakeInF.owner
|
||||||
.& #lockedBy .= stakeOutputLocks
|
.& #lockedBy .= pdata stakeOutputLocks
|
||||||
)
|
)
|
||||||
|
|
||||||
pguardC "Only locks updated in the output stake" $
|
pguardC "Only locks updated in the output stake" $
|
||||||
templateStakeOut #== stakeOut
|
templateStakeOut #== stakeOut
|
||||||
|
|
||||||
pguardC "All relevant locks removed from the stake" $
|
pguardC "All relevant locks removed from the stake" $
|
||||||
pgetStakeUsage # pfromData stakeOutputLocks
|
validateOutputLocks # stakeOutputLocks
|
||||||
# proposalF.proposalId #== pcon PDidNothing
|
|
||||||
|
|
||||||
pure $ pconstant ()
|
pure $ pconstant ()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,17 @@ module Agora.Stake (
|
||||||
PStakeDatum (..),
|
PStakeDatum (..),
|
||||||
PStakeRedeemer (..),
|
PStakeRedeemer (..),
|
||||||
PProposalLock (..),
|
PProposalLock (..),
|
||||||
PStakeUsage (..),
|
PStakeRole (..),
|
||||||
|
|
||||||
-- * Utility functions
|
-- * Utility functions
|
||||||
stakeLocked,
|
pstakeLocked,
|
||||||
pgetStakeUsage,
|
pnumCreatedProposals,
|
||||||
|
pextractVoteOption,
|
||||||
|
pgetStakeRole,
|
||||||
|
pisVoter,
|
||||||
|
pisCreator,
|
||||||
|
pisPureCreator,
|
||||||
|
pisIrrelevant,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Agora.Plutarch.Orphans ()
|
import Agora.Plutarch.Orphans ()
|
||||||
|
|
@ -43,14 +49,15 @@ import Plutarch.Extra.IsData (
|
||||||
DerivePConstantViaDataList (..),
|
DerivePConstantViaDataList (..),
|
||||||
ProductIsData (ProductIsData),
|
ProductIsData (ProductIsData),
|
||||||
)
|
)
|
||||||
import Plutarch.Extra.List (pmapMaybe, pnotNull)
|
import Plutarch.Extra.List (pnotNull)
|
||||||
import Plutarch.Extra.Other (DerivePNewtype' (..))
|
import Plutarch.Extra.Other (DerivePNewtype' (..))
|
||||||
import Plutarch.Extra.TermCont (pletFieldsC)
|
|
||||||
import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (..))
|
import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (..))
|
||||||
import Plutarch.SafeMoney (PDiscrete)
|
import Plutarch.SafeMoney (PDiscrete)
|
||||||
|
import Plutarch.Show (PShow (..))
|
||||||
import PlutusLedgerApi.V1 (PubKeyHash)
|
import PlutusLedgerApi.V1 (PubKeyHash)
|
||||||
import PlutusLedgerApi.V1.Value (AssetClass)
|
import PlutusLedgerApi.V1.Value (AssetClass)
|
||||||
import PlutusTx qualified
|
import PlutusTx qualified
|
||||||
|
import Prelude ((+))
|
||||||
import Prelude hiding (Num (..))
|
import Prelude hiding (Num (..))
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
@ -69,8 +76,7 @@ data Stake = Stake
|
||||||
GHC.Generic
|
GHC.Generic
|
||||||
)
|
)
|
||||||
|
|
||||||
{- | A lock placed on a Stake datum in order to prevent
|
{- | Locks that are stored in the stake datums for various purposes.
|
||||||
depositing and withdrawing when votes are in place.
|
|
||||||
|
|
||||||
NOTE: Due to retracting votes always being possible,
|
NOTE: Due to retracting votes always being possible,
|
||||||
this lock will only lock with contention on the proposal.
|
this lock will only lock with contention on the proposal.
|
||||||
|
|
@ -97,30 +103,45 @@ data Stake = Stake
|
||||||
|
|
||||||
@since 0.1.0
|
@since 0.1.0
|
||||||
-}
|
-}
|
||||||
data ProposalLock = ProposalLock
|
data ProposalLock
|
||||||
{ vote :: ResultTag
|
= -- | The stake was used to create a proposal.
|
||||||
-- ^ What was voted on. This allows retracting votes to
|
--
|
||||||
-- undo their vote.
|
-- This kind of lock is placed upon the creation of a proposal, in order
|
||||||
, proposalId :: ProposalId
|
-- to limit creation of proposals per stake.
|
||||||
-- ^ Identifies the proposal. See 'ProposalId' for further
|
--
|
||||||
-- comments on its significance.
|
-- See also: https://github.com/Liqwid-Labs/agora/issues/68
|
||||||
}
|
--
|
||||||
|
-- @since 0.2.0
|
||||||
|
Created
|
||||||
|
ProposalId
|
||||||
|
-- ^ The identifier of the proposal.
|
||||||
|
| -- | The stake was used to vote on a proposal.
|
||||||
|
--
|
||||||
|
-- This kind of lock is placed while voting on a propsoal, in order to
|
||||||
|
-- prevent depositing and withdrawing when votes are in place.
|
||||||
|
--
|
||||||
|
-- @since 0.2.0
|
||||||
|
Voted
|
||||||
|
ProposalId
|
||||||
|
-- ^ The identifier of the proposal.
|
||||||
|
ResultTag
|
||||||
|
-- ^ The option which was voted on. This allows votes to be retracted.
|
||||||
deriving stock
|
deriving stock
|
||||||
( -- | @since 0.1.0
|
( -- | @since 0.1.0
|
||||||
Show
|
Show
|
||||||
, -- | @since 0.1.0
|
, -- | @since 0.1.0
|
||||||
GHC.Generic
|
GHC.Generic
|
||||||
)
|
)
|
||||||
deriving anyclass (Generic)
|
deriving anyclass
|
||||||
deriving
|
|
||||||
( -- | @since 0.1.0
|
( -- | @since 0.1.0
|
||||||
PlutusTx.ToData
|
Generic
|
||||||
, -- | @since 0.1.0
|
|
||||||
PlutusTx.FromData
|
|
||||||
, -- | @since 0.1.0
|
|
||||||
PlutusTx.UnsafeFromData
|
|
||||||
)
|
)
|
||||||
via (ProductIsData ProposalLock)
|
|
||||||
|
PlutusTx.makeIsDataIndexed
|
||||||
|
''ProposalLock
|
||||||
|
[ ('Created, 0)
|
||||||
|
, ('Voted, 1)
|
||||||
|
]
|
||||||
|
|
||||||
{- | Haskell-level redeemer for Stake scripts.
|
{- | Haskell-level redeemer for Stake scripts.
|
||||||
|
|
||||||
|
|
@ -138,12 +159,12 @@ data StakeRedeemer
|
||||||
-- This needs to be done in sync with casting a vote, otherwise
|
-- This needs to be done in sync with casting a vote, otherwise
|
||||||
-- it's possible for a lock to be permanently placed on the stake,
|
-- it's possible for a lock to be permanently placed on the stake,
|
||||||
-- and then the funds are lost.
|
-- and then the funds are lost.
|
||||||
PermitVote ProposalLock
|
PermitVote
|
||||||
| -- | Retract a vote, removing it from the 'lockedBy' field. See 'ProposalLock'.
|
| -- | Retract a vote, removing it from the 'lockedBy' field. See 'ProposalLock'.
|
||||||
-- This action checks for permission of the 'Agora.Proposal.Proposal'. Finished proposals are
|
-- This action checks for permission of the 'Agora.Proposal.Proposal'. Finished proposals are
|
||||||
-- always allowed to have votes retracted and won't affect the Proposal datum,
|
-- always allowed to have votes retracted and won't affect the Proposal datum,
|
||||||
-- allowing 'Stake's to be unlocked.
|
-- allowing 'Stake's to be unlocked.
|
||||||
RetractVotes [ProposalLock]
|
RetractVotes
|
||||||
| -- | The owner can consume stake if nothing is changed about it.
|
| -- | The owner can consume stake if nothing is changed about it.
|
||||||
-- If the proposal token moves, this is equivalent to the owner consuming it.
|
-- If the proposal token moves, this is equivalent to the owner consuming it.
|
||||||
WitnessStake
|
WitnessStake
|
||||||
|
|
@ -165,7 +186,7 @@ PlutusTx.makeIsDataIndexed
|
||||||
data StakeDatum = StakeDatum
|
data StakeDatum = StakeDatum
|
||||||
{ stakedAmount :: Tagged GTTag Integer
|
{ stakedAmount :: Tagged GTTag Integer
|
||||||
-- ^ Tracks the amount of governance token staked in the datum.
|
-- ^ Tracks the amount of governance token staked in the datum.
|
||||||
-- This also acts as the voting weight for 'Agora.Proposal.Proposal's.
|
-- This also acts as the voting weight for 'Agora.Proposal.Proposal's.
|
||||||
, owner :: PubKeyHash
|
, owner :: PubKeyHash
|
||||||
-- ^ The hash of the public key this stake belongs to.
|
-- ^ The hash of the public key this stake belongs to.
|
||||||
--
|
--
|
||||||
|
|
@ -173,7 +194,7 @@ data StakeDatum = StakeDatum
|
||||||
-- https://github.com/Liqwid-Labs/agora/issues/45
|
-- https://github.com/Liqwid-Labs/agora/issues/45
|
||||||
, lockedBy :: [ProposalLock]
|
, lockedBy :: [ProposalLock]
|
||||||
-- ^ The current proposals locking this stake. This field must be empty
|
-- ^ The current proposals locking this stake. This field must be empty
|
||||||
-- for the stake to be usable for deposits and withdrawals.
|
-- for the stake to be usable for deposits and withdrawals.
|
||||||
}
|
}
|
||||||
deriving stock (Show, GHC.Generic)
|
deriving stock (Show, GHC.Generic)
|
||||||
deriving anyclass (Generic)
|
deriving anyclass (Generic)
|
||||||
|
|
@ -227,13 +248,20 @@ newtype PStakeDatum (s :: S) = PStakeDatum
|
||||||
via (DerivePNewtype' PStakeDatum)
|
via (DerivePNewtype' PStakeDatum)
|
||||||
|
|
||||||
-- | @since 0.1.0
|
-- | @since 0.1.0
|
||||||
instance Plutarch.Lift.PUnsafeLiftDecl PStakeDatum where type PLifted PStakeDatum = StakeDatum
|
instance Plutarch.Lift.PUnsafeLiftDecl PStakeDatum where
|
||||||
|
type PLifted PStakeDatum = StakeDatum
|
||||||
|
|
||||||
-- | @since 0.1.0
|
-- | @since 0.1.0
|
||||||
deriving via (DerivePConstantViaDataList StakeDatum PStakeDatum) instance (Plutarch.Lift.PConstantDecl StakeDatum)
|
deriving via
|
||||||
|
(DerivePConstantViaDataList StakeDatum PStakeDatum)
|
||||||
|
instance
|
||||||
|
(Plutarch.Lift.PConstantDecl StakeDatum)
|
||||||
|
|
||||||
-- | @since 0.1.0
|
-- | @since 0.1.0
|
||||||
deriving via PAsData (DerivePNewtype' PStakeDatum) instance PTryFrom PData (PAsData PStakeDatum)
|
deriving via
|
||||||
|
PAsData (DerivePNewtype' PStakeDatum)
|
||||||
|
instance
|
||||||
|
PTryFrom PData (PAsData PStakeDatum)
|
||||||
|
|
||||||
{- | Plutarch-level redeemer for Stake scripts.
|
{- | Plutarch-level redeemer for Stake scripts.
|
||||||
|
|
||||||
|
|
@ -244,8 +272,8 @@ data PStakeRedeemer (s :: S)
|
||||||
PDepositWithdraw (Term s (PDataRecord '["delta" ':= PDiscrete GTTag]))
|
PDepositWithdraw (Term s (PDataRecord '["delta" ':= PDiscrete GTTag]))
|
||||||
| -- | Destroy a stake, retrieving its LQ, the minimum ADA and any other assets.
|
| -- | Destroy a stake, retrieving its LQ, the minimum ADA and any other assets.
|
||||||
PDestroy (Term s (PDataRecord '[]))
|
PDestroy (Term s (PDataRecord '[]))
|
||||||
| PPermitVote (Term s (PDataRecord '["lock" ':= PProposalLock]))
|
| PPermitVote (Term s (PDataRecord '[]))
|
||||||
| PRetractVotes (Term s (PDataRecord '["locks" ':= PBuiltinList (PAsData PProposalLock)]))
|
| PRetractVotes (Term s (PDataRecord '[]))
|
||||||
| PWitnessStake (Term s (PDataRecord '[]))
|
| PWitnessStake (Term s (PDataRecord '[]))
|
||||||
deriving stock
|
deriving stock
|
||||||
( -- | @since 0.1.0
|
( -- | @since 0.1.0
|
||||||
|
|
@ -267,65 +295,37 @@ data PStakeRedeemer (s :: S)
|
||||||
)
|
)
|
||||||
via PIsDataReprInstances PStakeRedeemer
|
via PIsDataReprInstances PStakeRedeemer
|
||||||
|
|
||||||
|
-- | @since 0.1.0
|
||||||
deriving via
|
deriving via
|
||||||
PAsData (PIsDataReprInstances PStakeRedeemer)
|
PAsData (PIsDataReprInstances PStakeRedeemer)
|
||||||
instance
|
instance
|
||||||
PTryFrom PData (PAsData PStakeRedeemer)
|
PTryFrom PData (PAsData PStakeRedeemer)
|
||||||
|
|
||||||
instance Plutarch.Lift.PUnsafeLiftDecl PStakeRedeemer where type PLifted PStakeRedeemer = StakeRedeemer
|
-- | @since 0.1.0
|
||||||
deriving via (DerivePConstantViaData StakeRedeemer PStakeRedeemer) instance (Plutarch.Lift.PConstantDecl StakeRedeemer)
|
instance Plutarch.Lift.PUnsafeLiftDecl PStakeRedeemer where
|
||||||
|
type PLifted PStakeRedeemer = StakeRedeemer
|
||||||
|
|
||||||
|
-- | @since 0.1.0
|
||||||
|
deriving via
|
||||||
|
(DerivePConstantViaData StakeRedeemer PStakeRedeemer)
|
||||||
|
instance
|
||||||
|
(Plutarch.Lift.PConstantDecl StakeRedeemer)
|
||||||
|
|
||||||
{- | Plutarch-level version of 'ProposalLock'.
|
{- | Plutarch-level version of 'ProposalLock'.
|
||||||
|
|
||||||
@since 0.1.0
|
@since 0.2.0
|
||||||
-}
|
-}
|
||||||
newtype PProposalLock (s :: S) = PProposalLock
|
data PProposalLock (s :: S)
|
||||||
{ getProposalLock ::
|
= PCreated (Term s (PDataRecord '["created" ':= PProposalId]))
|
||||||
Term
|
| PVoted
|
||||||
s
|
( Term
|
||||||
( PDataRecord
|
s
|
||||||
'[ "vote" ':= PResultTag
|
( PDataRecord
|
||||||
, "proposalTag" ':= PProposalId
|
'[ "votedOn" ':= PProposalId
|
||||||
]
|
, "votedFor" ':= PResultTag
|
||||||
)
|
]
|
||||||
}
|
)
|
||||||
deriving stock (GHC.Generic)
|
)
|
||||||
deriving anyclass (Generic)
|
|
||||||
deriving anyclass (PIsDataRepr)
|
|
||||||
deriving
|
|
||||||
(PlutusType, PIsData, PDataFields, PEq)
|
|
||||||
via (DerivePNewtype' PProposalLock)
|
|
||||||
|
|
||||||
deriving via
|
|
||||||
PAsData (DerivePNewtype' PProposalLock)
|
|
||||||
instance
|
|
||||||
PTryFrom PData (PAsData PProposalLock)
|
|
||||||
|
|
||||||
instance Plutarch.Lift.PUnsafeLiftDecl PProposalLock where type PLifted PProposalLock = ProposalLock
|
|
||||||
deriving via (DerivePConstantViaDataList ProposalLock PProposalLock) instance (Plutarch.Lift.PConstantDecl ProposalLock)
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
{- | Check whether a Stake is locked. If it is locked, various actions are unavailable.
|
|
||||||
|
|
||||||
@since 0.1.0
|
|
||||||
-}
|
|
||||||
stakeLocked :: forall (s :: S). Term s (PStakeDatum :--> PBool)
|
|
||||||
stakeLocked = phoistAcyclic $
|
|
||||||
plam $ \stakeDatum ->
|
|
||||||
let locks :: Term _ (PBuiltinList (PAsData PProposalLock))
|
|
||||||
locks = pfield @"lockedBy" # stakeDatum
|
|
||||||
in pnotNull # locks
|
|
||||||
|
|
||||||
{- | Represent the usage of a stake on a particular proposal.
|
|
||||||
A stake can be used to either create or vote on a proposal.
|
|
||||||
|
|
||||||
@since 0.1.0
|
|
||||||
-}
|
|
||||||
data PStakeUsage (s :: S)
|
|
||||||
= PVotedFor (Term s PResultTag)
|
|
||||||
| PCreated
|
|
||||||
| PDidNothing
|
|
||||||
deriving stock
|
deriving stock
|
||||||
( -- | @since 0.1.0
|
( -- | @since 0.1.0
|
||||||
GHC.Generic
|
GHC.Generic
|
||||||
|
|
@ -334,51 +334,212 @@ data PStakeUsage (s :: S)
|
||||||
( -- | @since 0.1.0
|
( -- | @since 0.1.0
|
||||||
Generic
|
Generic
|
||||||
, -- | @since 0.1.0
|
, -- | @since 0.1.0
|
||||||
|
HasDatatypeInfo
|
||||||
|
)
|
||||||
|
deriving anyclass
|
||||||
|
( -- | @since 0.1.0
|
||||||
|
PIsDataRepr
|
||||||
|
)
|
||||||
|
deriving
|
||||||
|
( -- | @since 0.1.0
|
||||||
PlutusType
|
PlutusType
|
||||||
, -- | @since 0.1.0
|
, -- | @since 0.1.0
|
||||||
HasDatatypeInfo
|
PIsData
|
||||||
, -- | @since 0.1.0
|
, -- | @since 0.1.0
|
||||||
PEq
|
PEq
|
||||||
)
|
)
|
||||||
|
via (PIsDataReprInstances PProposalLock)
|
||||||
|
|
||||||
{- | / O(n) /.Return the usage of a stake on a particular proposal,
|
-- | @since 0.1.0
|
||||||
given the 'lockedBy' field of a stake and the target proposal.
|
deriving via
|
||||||
|
PAsData (PIsDataReprInstances PProposalLock)
|
||||||
|
instance
|
||||||
|
PTryFrom PData (PAsData PProposalLock)
|
||||||
|
|
||||||
@since 0.1.0
|
-- | @since 0.1.0
|
||||||
|
instance Plutarch.Lift.PUnsafeLiftDecl PProposalLock where
|
||||||
|
type PLifted PProposalLock = ProposalLock
|
||||||
|
|
||||||
|
-- | @since 0.1.0
|
||||||
|
deriving via
|
||||||
|
(DerivePConstantViaData ProposalLock PProposalLock)
|
||||||
|
instance
|
||||||
|
(Plutarch.Lift.PConstantDecl ProposalLock)
|
||||||
|
|
||||||
|
-- | @since 0.2.0
|
||||||
|
instance PShow PProposalLock where
|
||||||
|
pshow' :: Bool -> Term s PProposalLock -> Term s PString
|
||||||
|
pshow' True x = "(" <> pshow' False x <> ")"
|
||||||
|
pshow' False lock = pmatch lock $ \case
|
||||||
|
PCreated ((pfield @"created" #) -> pid) -> "PCreated " <> pshow' True pid
|
||||||
|
PVoted x -> pletFields @'["votedOn", "votedFor"] x $ \xF ->
|
||||||
|
"PVoted " <> pshow' True xF.votedOn <> " " <> pshow' True xF.votedFor
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
{- | Check whether a Stake is locked. If it is locked, various actions are unavailable.
|
||||||
|
|
||||||
|
@since 0.2.0
|
||||||
-}
|
-}
|
||||||
pgetStakeUsage ::
|
pstakeLocked :: forall (s :: S). Term s (PStakeDatum :--> PBool)
|
||||||
Term
|
pstakeLocked = phoistAcyclic $
|
||||||
_
|
plam $ \stakeDatum ->
|
||||||
( PBuiltinList (PAsData PProposalLock)
|
let locks :: Term _ (PBuiltinList (PAsData PProposalLock))
|
||||||
:--> PProposalId
|
locks = pfield @"lockedBy" # stakeDatum
|
||||||
:--> PStakeUsage
|
in pnotNull # locks
|
||||||
|
|
||||||
|
{- | Get the number of *alive* proposals that were created by the given stake.
|
||||||
|
|
||||||
|
@since 0.2.0
|
||||||
|
-}
|
||||||
|
pnumCreatedProposals :: Term s (PBuiltinList (PAsData PProposalLock) :--> PInteger)
|
||||||
|
pnumCreatedProposals =
|
||||||
|
phoistAcyclic $
|
||||||
|
plam $ \l ->
|
||||||
|
pfoldl
|
||||||
|
# phoistAcyclic
|
||||||
|
( plam
|
||||||
|
( \c (pfromData -> lock) ->
|
||||||
|
c
|
||||||
|
+ pmatch
|
||||||
|
lock
|
||||||
|
( \case
|
||||||
|
PCreated _ -> 1
|
||||||
|
_ -> 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# 0
|
||||||
|
# l
|
||||||
|
|
||||||
|
{- | The role of a stake for a particular proposal. Scott-encoded.
|
||||||
|
|
||||||
|
@since 0.2.0
|
||||||
|
-}
|
||||||
|
data PStakeRole (s :: S)
|
||||||
|
= -- | The stake was used to vote on the proposal.
|
||||||
|
PVoter
|
||||||
|
(Term s PResultTag)
|
||||||
|
-- ^ The option which was voted for.
|
||||||
|
| -- | The stake was used to create the propsoal.
|
||||||
|
PCreator
|
||||||
|
| -- | The stake was used to both create and vote on the proposal.
|
||||||
|
PBoth
|
||||||
|
(Term s PResultTag)
|
||||||
|
-- ^ The option which was voted for.
|
||||||
|
| -- | The stake has nothing to do with the given propsoal.
|
||||||
|
PIrrelevant
|
||||||
|
deriving stock
|
||||||
|
( -- | @since 0.2.0
|
||||||
|
GHC.Generic
|
||||||
|
)
|
||||||
|
deriving anyclass
|
||||||
|
( -- | @since 0.2.0
|
||||||
|
Generic
|
||||||
|
, -- | @since 0.2.0
|
||||||
|
PlutusType
|
||||||
|
, -- | @since 0.2.0
|
||||||
|
HasDatatypeInfo
|
||||||
|
, -- | @since 0.2.0
|
||||||
|
PEq
|
||||||
)
|
)
|
||||||
pgetStakeUsage = phoistAcyclic $
|
|
||||||
plam $ \locks pid ->
|
|
||||||
let -- All locks from the given proposal.
|
|
||||||
filteredLocks =
|
|
||||||
pmapMaybe
|
|
||||||
# plam
|
|
||||||
( \lock'@(pfromData -> lock) -> unTermCont $ do
|
|
||||||
lockF <- pletFieldsC @'["proposalTag"] lock
|
|
||||||
|
|
||||||
pure $
|
{- | Retutn true if the stake was used to voted on the proposal.
|
||||||
|
|
||||||
|
@since 0.2.0
|
||||||
|
-}
|
||||||
|
pisVoter :: Term s (PStakeRole :--> PBool)
|
||||||
|
pisVoter = phoistAcyclic $
|
||||||
|
plam $ \sr -> pmatch sr $ \case
|
||||||
|
PVoter _ -> pconstant True
|
||||||
|
PBoth _ -> pconstant True
|
||||||
|
_ -> pconstant False
|
||||||
|
|
||||||
|
{- | Retutn true if the stake was used to create the proposal.
|
||||||
|
|
||||||
|
@since 0.2.0
|
||||||
|
-}
|
||||||
|
pisCreator :: Term s (PStakeRole :--> PBool)
|
||||||
|
pisCreator = phoistAcyclic $
|
||||||
|
plam $ \sr -> pmatch sr $ \case
|
||||||
|
PCreator -> pconstant True
|
||||||
|
PBoth _ -> pconstant True
|
||||||
|
_ -> pconstant False
|
||||||
|
|
||||||
|
{- | Retutn true if the stake was used to create the proposal, but not vote on
|
||||||
|
the proposal.
|
||||||
|
|
||||||
|
@since 0.2.0
|
||||||
|
-}
|
||||||
|
pisPureCreator :: Term s (PStakeRole :--> PBool)
|
||||||
|
pisPureCreator = phoistAcyclic $
|
||||||
|
plam $ \sr -> pmatch sr $ \case
|
||||||
|
PCreator -> pconstant True
|
||||||
|
_ -> pconstant False
|
||||||
|
|
||||||
|
{- | Return true if the stake isn't related to the proposal.
|
||||||
|
|
||||||
|
@since 0.2.0
|
||||||
|
-}
|
||||||
|
pisIrrelevant :: Term s (PStakeRole :--> PBool)
|
||||||
|
pisIrrelevant = phoistAcyclic $
|
||||||
|
plam $ \sr -> pmatch sr $ \case
|
||||||
|
PIrrelevant -> pconstant True
|
||||||
|
_ -> pconstant False
|
||||||
|
|
||||||
|
{- | Get the role of a stake for the proposal specified by the poroposal id,
|
||||||
|
given the 'StakeDatum.lockedBy' field of the stake.
|
||||||
|
|
||||||
|
Note that the list of locks is cosidered valid only if it contains at most
|
||||||
|
two locks from the given proposal: one voter lock and one creator lock.
|
||||||
|
|
||||||
|
@since 0.2.0
|
||||||
|
-}
|
||||||
|
pgetStakeRole :: Term s (PProposalId :--> PBuiltinList (PAsData PProposalLock) :--> PStakeRole)
|
||||||
|
pgetStakeRole = phoistAcyclic $
|
||||||
|
plam $ \pid locks ->
|
||||||
|
pfoldl
|
||||||
|
# plam
|
||||||
|
( \role (pfromData -> lock) ->
|
||||||
|
let thisRole = pmatch lock $ \case
|
||||||
|
PCreated ((pfield @"created" #) -> pid') ->
|
||||||
pif
|
pif
|
||||||
(lockF.proposalTag #== pid)
|
(pid' #== pid)
|
||||||
(pcon $ PJust lock')
|
(pcon PCreator)
|
||||||
(pcon PNothing)
|
(pcon PIrrelevant)
|
||||||
)
|
PVoted lock' -> pletFields @'["votedOn", "votedFor"] lock' $ \lockF ->
|
||||||
# locks
|
pif
|
||||||
|
(lockF.votedOn #== pid)
|
||||||
|
(pcon $ PVoter lockF.votedFor)
|
||||||
|
(pcon PIrrelevant)
|
||||||
|
in pcombineStakeRole # thisRole # role
|
||||||
|
)
|
||||||
|
# pcon PIrrelevant
|
||||||
|
# locks
|
||||||
|
where
|
||||||
|
pcombineStakeRole :: Term s (PStakeRole :--> PStakeRole :--> PStakeRole)
|
||||||
|
pcombineStakeRole = phoistAcyclic $
|
||||||
|
plam $ \x y ->
|
||||||
|
let cannotCombine = ptraceError "duplicate roles"
|
||||||
|
in pmatch x $ \case
|
||||||
|
PVoter r -> pmatch y $ \case
|
||||||
|
PCreator -> pcon $ PBoth r
|
||||||
|
PIrrelevant -> x
|
||||||
|
_ -> cannotCombine
|
||||||
|
PCreator -> pmatch y $ \case
|
||||||
|
PVoter r -> pcon $ PBoth r
|
||||||
|
PIrrelevant -> x
|
||||||
|
_ -> cannotCombine
|
||||||
|
PBoth _ -> cannotCombine
|
||||||
|
PIrrelevant -> y
|
||||||
|
|
||||||
lockCount' = plength # filteredLocks
|
{- | Get the outcome that was voted for.
|
||||||
in plet lockCount' $ \lockCount ->
|
|
||||||
pif (lockCount #== 0) (pcon PDidNothing) $
|
@since 0.2.0
|
||||||
pif
|
-}
|
||||||
(lockCount #== 1)
|
pextractVoteOption :: Term s (PStakeRole :--> PResultTag)
|
||||||
( pcon $
|
pextractVoteOption = phoistAcyclic $
|
||||||
PVotedFor $
|
plam $ \sr -> pmatch sr $ \case
|
||||||
pfromData $
|
PVoter r -> r
|
||||||
pfield @"vote" #$ phead # filteredLocks
|
PBoth r -> r
|
||||||
)
|
_ -> ptraceError "not voter"
|
||||||
-- Note: see the implementation of the governor.
|
|
||||||
(pcon PCreated)
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import Agora.Stake (
|
||||||
),
|
),
|
||||||
Stake (gtClassRef, proposalSTClass),
|
Stake (gtClassRef, proposalSTClass),
|
||||||
StakeRedeemer (WitnessStake),
|
StakeRedeemer (WitnessStake),
|
||||||
stakeLocked,
|
pstakeLocked,
|
||||||
)
|
)
|
||||||
import Agora.Utils (
|
import Agora.Utils (
|
||||||
mustBePJust,
|
mustBePJust,
|
||||||
|
|
@ -109,7 +109,7 @@ stakePolicy gtClassRef =
|
||||||
pif
|
pif
|
||||||
(psymbolValueOf # ownSymbol # txOutF.value #== 1)
|
(psymbolValueOf # ownSymbol # txOutF.value #== 1)
|
||||||
( let datum = mustFindDatum' @PStakeDatum # txOutF.datumHash # txInfoF.datums
|
( let datum = mustFindDatum' @PStakeDatum # txOutF.datumHash # txInfoF.datums
|
||||||
in pnot # (stakeLocked # datum)
|
in pnot # (pstakeLocked # datum)
|
||||||
)
|
)
|
||||||
(pconstant False)
|
(pconstant False)
|
||||||
)
|
)
|
||||||
|
|
@ -263,7 +263,7 @@ stakeValidator stake =
|
||||||
spentST <- pletC $ psymbolValueOf # stCurrencySymbol #$ valueSpent
|
spentST <- pletC $ psymbolValueOf # stCurrencySymbol #$ valueSpent
|
||||||
|
|
||||||
-- Is the stake currently locked?
|
-- Is the stake currently locked?
|
||||||
stakeIsLocked <- pletC $ stakeLocked # stakeDatum'
|
stakeIsLocked <- pletC $ pstakeLocked # stakeDatum'
|
||||||
|
|
||||||
pure $
|
pure $
|
||||||
pmatch stakeRedeemer $ \case
|
pmatch stakeRedeemer $ \case
|
||||||
|
|
@ -287,7 +287,7 @@ stakeValidator stake =
|
||||||
proposalSTClass = passetClass # pconstant propCs # pconstant propTn
|
proposalSTClass = passetClass # pconstant propCs # pconstant propTn
|
||||||
spentProposalST = passetClassValueOf # valueSpent # proposalSTClass
|
spentProposalST = passetClassValueOf # valueSpent # proposalSTClass
|
||||||
|
|
||||||
proposalTokenMoved <- pletC $ spentProposalST #== 1
|
proposalTokenMoved <- pletC $ 1 #<= spentProposalST
|
||||||
|
|
||||||
-- Filter out own outputs using own address and ST.
|
-- Filter out own outputs using own address and ST.
|
||||||
ownOutputs <-
|
ownOutputs <-
|
||||||
|
|
@ -371,9 +371,20 @@ stakeValidator stake =
|
||||||
pletC $
|
pletC $
|
||||||
pdata resolvedF.value #== pdata ownOutputValue
|
pdata resolvedF.value #== pdata ownOutputValue
|
||||||
|
|
||||||
|
onlyLocksUpdated <-
|
||||||
|
pletC $
|
||||||
|
let templateStakeDatum =
|
||||||
|
mkRecordConstr
|
||||||
|
PStakeDatum
|
||||||
|
( #stakedAmount .= stakeDatum.stakedAmount
|
||||||
|
.& #owner .= stakeDatum.owner
|
||||||
|
.& #lockedBy .= pfield @"lockedBy" # stakeOut
|
||||||
|
)
|
||||||
|
in stakeOut #== templateStakeDatum
|
||||||
|
|
||||||
pure $
|
pure $
|
||||||
pmatch stakeRedeemer $ \case
|
pmatch stakeRedeemer $ \case
|
||||||
PRetractVotes l -> unTermCont $ do
|
PRetractVotes _ -> unTermCont $ do
|
||||||
pguardC
|
pguardC
|
||||||
"Owner signs this transaction"
|
"Owner signs this transaction"
|
||||||
ownerSignsTransaction
|
ownerSignsTransaction
|
||||||
|
|
@ -383,18 +394,8 @@ stakeValidator stake =
|
||||||
pguardC "Proposal ST spent" proposalTokenMoved
|
pguardC "Proposal ST spent" proposalTokenMoved
|
||||||
|
|
||||||
pguardC "A UTXO must exist with the correct output" $
|
pguardC "A UTXO must exist with the correct output" $
|
||||||
let expectedLocks = pfield @"locks" # l
|
let valueCorrect = ownOutputValueUnchanged
|
||||||
|
outputDatumCorrect = onlyLocksUpdated
|
||||||
expectedDatum =
|
|
||||||
mkRecordConstr
|
|
||||||
PStakeDatum
|
|
||||||
( #stakedAmount .= stakeDatum.stakedAmount
|
|
||||||
.& #owner .= stakeDatum.owner
|
|
||||||
.& #lockedBy .= expectedLocks
|
|
||||||
)
|
|
||||||
|
|
||||||
valueCorrect = ownOutputValueUnchanged
|
|
||||||
outputDatumCorrect = stakeOut #== expectedDatum
|
|
||||||
in foldl1
|
in foldl1
|
||||||
(#&&)
|
(#&&)
|
||||||
[ ptraceIfFalse "valueCorrect" valueCorrect
|
[ ptraceIfFalse "valueCorrect" valueCorrect
|
||||||
|
|
@ -405,34 +406,21 @@ stakeValidator stake =
|
||||||
|
|
||||||
------------------------------------------------------------
|
------------------------------------------------------------
|
||||||
|
|
||||||
PPermitVote l -> unTermCont $ do
|
PPermitVote _ -> unTermCont $ do
|
||||||
pguardC
|
pguardC
|
||||||
"Owner signs this transaction"
|
"Owner signs this transaction"
|
||||||
ownerSignsTransaction
|
ownerSignsTransaction
|
||||||
|
|
||||||
|
let proposalTokenMinted =
|
||||||
|
passetClassValueOf # txInfoF.mint # proposalSTClass #== 1
|
||||||
|
|
||||||
-- This puts trust into the Proposal. The Proposal must necessarily check
|
-- This puts trust into the Proposal. The Proposal must necessarily check
|
||||||
-- that this is not abused.
|
-- that this is not abused.
|
||||||
pguardC "Proposal ST spent" proposalTokenMoved
|
pguardC "Proposal ST spent or minted" $
|
||||||
|
proposalTokenMoved #|| proposalTokenMinted
|
||||||
-- Update the stake datum, but only the 'lockedBy' field.
|
|
||||||
|
|
||||||
let -- We actually don't know whether the given lock is valid or not.
|
|
||||||
-- This is checked in the proposal validator.
|
|
||||||
newLock = pfield @"lock" # l
|
|
||||||
-- Prepend the new lock to the existing locks.
|
|
||||||
expectedLocks = pcons # newLock # stakeDatum.lockedBy
|
|
||||||
|
|
||||||
expectedDatum <-
|
|
||||||
pletC $
|
|
||||||
mkRecordConstr
|
|
||||||
PStakeDatum
|
|
||||||
( #stakedAmount .= stakeDatum.stakedAmount
|
|
||||||
.& #owner .= stakeDatum.owner
|
|
||||||
.& #lockedBy .= pdata expectedLocks
|
|
||||||
)
|
|
||||||
|
|
||||||
pguardC "A UTXO must exist with the correct output" $
|
pguardC "A UTXO must exist with the correct output" $
|
||||||
let correctOutputDatum = stakeOut #== expectedDatum
|
let correctOutputDatum = onlyLocksUpdated
|
||||||
valueCorrect = ownOutputValueUnchanged
|
valueCorrect = ownOutputValueUnchanged
|
||||||
in foldl1
|
in foldl1
|
||||||
(#&&)
|
(#&&)
|
||||||
|
|
|
||||||
286
bench.csv
286
bench.csv
|
|
@ -2,69 +2,233 @@ name,cpu,mem,size
|
||||||
Agora/Effects/Treasury Withdrawal Effect/effect/Simple,333327612,830203,3674
|
Agora/Effects/Treasury Withdrawal Effect/effect/Simple,333327612,830203,3674
|
||||||
Agora/Effects/Treasury Withdrawal Effect/effect/Simple with multiple treasuries ,492387542,1197315,3986
|
Agora/Effects/Treasury Withdrawal Effect/effect/Simple with multiple treasuries ,492387542,1197315,3986
|
||||||
Agora/Effects/Treasury Withdrawal Effect/effect/Mixed Assets,456007605,1104500,3859
|
Agora/Effects/Treasury Withdrawal Effect/effect/Mixed Assets,456007605,1104500,3859
|
||||||
Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/governor validator should pass,87839169,243032,8561
|
Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/governor validator should pass,88940927,246756,8891
|
||||||
Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/effect validator should pass,106082031,292993,3609
|
Agora/Effects/Governor Mutation Effect/validator/valid new governor datum/effect validator should pass,107090537,296185,3627
|
||||||
Agora/Stake/policy/stakeCreation,50939580,148729,2387
|
Agora/Stake/policy/stakeCreation,51008580,149029,2522
|
||||||
Agora/Stake/validator/stakeDepositWithdraw deposit,150745141,416137,4995
|
Agora/Stake/validator/stakeDepositWithdraw deposit,183506412,498838,4745
|
||||||
Agora/Stake/validator/stakeDepositWithdraw withdraw,150745141,416137,4983
|
Agora/Stake/validator/stakeDepositWithdraw withdraw,183506412,498838,4733
|
||||||
Agora/Proposal/policy/proposalCreation,23140177,69194,1515
|
Agora/Proposal/policy (proposal creation)/legal/proposal,34975627,103548,2117
|
||||||
Agora/Proposal/validator/cosignature/legal/with 1 cosigners/propsoal,235408912,657765,8097
|
Agora/Proposal/policy (proposal creation)/legal/governor,327971301,871386,9370
|
||||||
Agora/Proposal/validator/cosignature/legal/with 1 cosigners/stake,125665131,316762,5462
|
Agora/Proposal/policy (proposal creation)/legal/stake,152415805,398403,5404
|
||||||
Agora/Proposal/validator/cosignature/legal/with 5 cosigners/propsoal,680441047,1897008,10727
|
Agora/Proposal/policy (proposal creation)/illegal/invalid next proposal id/proposal,34975627,103548,2117
|
||||||
Agora/Proposal/validator/cosignature/legal/with 5 cosigners/stake,576106975,1490610,7972
|
Agora/Proposal/policy (proposal creation)/illegal/invalid next proposal id/stake,152415805,398403,5404
|
||||||
Agora/Proposal/validator/cosignature/legal/with 10 cosigners/propsoal,1351073436,3706315,14015
|
Agora/Proposal/policy (proposal creation)/illegal/use other's stake/proposal,34975627,103548,2086
|
||||||
Agora/Proposal/validator/cosignature/legal/with 10 cosigners/stake,1148637636,2982695,11109
|
Agora/Proposal/policy (proposal creation)/illegal/altered stake/proposal,34975627,103548,2117
|
||||||
Agora/Proposal/validator/cosignature/illegal/duplicate cosigners/stake,125665131,316762,5462
|
Agora/Proposal/policy (proposal creation)/illegal/invalid stake locks/proposal,34975627,103548,2125
|
||||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 1 cosigners/status: VotingReady/stake,125665131,316762,5462
|
Agora/Proposal/policy (proposal creation)/illegal/invalid stake locks/stake,157849465,413053,5412
|
||||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 1 cosigners/status: Locked/stake,125665131,316762,5462
|
Agora/Proposal/policy (proposal creation)/illegal/has reached maximum proposals limit/proposal,34975627,103548,2137
|
||||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 1 cosigners/status: Finished/stake,125665131,316762,5462
|
Agora/Proposal/policy (proposal creation)/illegal/has reached maximum proposals limit/stake,158878297,416511,5434
|
||||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 5 cosigners/status: VotingReady/stake,576106975,1490610,7972
|
Agora/Proposal/policy (proposal creation)/illegal/loose time range/proposal,34975627,103548,2117
|
||||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 5 cosigners/status: Locked/stake,576106975,1490610,7972
|
Agora/Proposal/policy (proposal creation)/illegal/loose time range/stake,152415805,398403,5404
|
||||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 5 cosigners/status: Finished/stake,576106975,1490610,7972
|
Agora/Proposal/policy (proposal creation)/illegal/open time range/proposal,34975627,103548,2113
|
||||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 10 cosigners/status: VotingReady/stake,1148637636,2982695,11109
|
Agora/Proposal/policy (proposal creation)/illegal/open time range/stake,152415805,398403,5400
|
||||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 10 cosigners/status: Locked/stake,1148637636,2982695,11109
|
Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/VotingReady/proposal,34975627,103548,2117
|
||||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 10 cosigners/status: Finished/stake,1148637636,2982695,11109
|
Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/VotingReady/stake,152415805,398403,5404
|
||||||
Agora/Proposal/validator/voting/legal/propsoal,246896882,688919,8069
|
Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Locked/proposal,34975627,103548,2117
|
||||||
Agora/Proposal/validator/voting/legal/stake,141234659,368136,5481
|
Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Locked/stake,152415805,398403,5404
|
||||||
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/legal/to next state/propsoal,222376736,631090,8052
|
Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Finished/proposal,34975627,103548,2117
|
||||||
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/legal/to next state/stake,125665131,316762,5459
|
Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Finished/stake,152415805,398403,5404
|
||||||
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/legal/to failed state/propsoal,217322044,620369,8054
|
Agora/Proposal/validator/cosignature/legal/with 1 cosigners/propsoal,232230876,649441,8097
|
||||||
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/legal/to failed state/stake,125665131,316762,5461
|
Agora/Proposal/validator/cosignature/legal/with 1 cosigners/stake,122255811,317464,5213
|
||||||
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/illegal/insufficient cosigns/stake,118020743,304972,5389
|
Agora/Proposal/validator/cosignature/legal/with 5 cosigners/propsoal,662894867,1848188,10727
|
||||||
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/legal/to next state/propsoal,614587307,1766683,10875
|
Agora/Proposal/validator/cosignature/legal/with 5 cosigners/stake,553050999,1459872,7723
|
||||||
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/legal/to next state/stake,548619697,1410656,8162
|
Agora/Proposal/validator/cosignature/legal/with 10 cosigners/propsoal,1315567076,3606875,14015
|
||||||
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/legal/to failed state/propsoal,239691392,682433,8415
|
Agora/Proposal/validator/cosignature/legal/with 10 cosigners/stake,1101023340,2912657,10860
|
||||||
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/legal/to failed state/stake,125665131,316762,5702
|
Agora/Proposal/validator/cosignature/illegal/duplicate cosigners/stake,122255811,317464,5213
|
||||||
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/illegal/insufficient cosigns/stake,446252055,1166812,7811
|
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 1 cosigners/status: VotingReady/stake,122255811,317464,5213
|
||||||
Agora/Proposal/validator/advancing/from draft/with 10 cosigner(s)/legal/to next state/propsoal,1196289192,3454898,14404
|
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 1 cosigners/status: Locked/stake,122255811,317464,5213
|
||||||
Agora/Proposal/validator/advancing/from draft/with 10 cosigner(s)/legal/to next state/stake,1201388310,3136405,11540
|
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 1 cosigners/status: Finished/stake,122255811,317464,5213
|
||||||
Agora/Proposal/validator/advancing/from draft/with 10 cosigner(s)/legal/to failed state/propsoal,267653077,760013,8868
|
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 5 cosigners/status: VotingReady/stake,553050999,1459872,7723
|
||||||
Agora/Proposal/validator/advancing/from draft/with 10 cosigner(s)/legal/to failed state/stake,125665131,316762,6004
|
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 5 cosigners/status: Locked/stake,553050999,1459872,7723
|
||||||
Agora/Proposal/validator/advancing/from draft/with 10 cosigner(s)/illegal/insufficient cosigns/stake,1025012867,2732083,10837
|
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 5 cosigners/status: Finished/stake,553050999,1459872,7723
|
||||||
Agora/Proposal/validator/advancing/legal/advance to next state/from: VotingReady/propsoal,250229153,709227,8061
|
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 10 cosigners/status: VotingReady/stake,1101023340,2912657,10860
|
||||||
Agora/Proposal/validator/advancing/legal/advance to next state/from: VotingReady/stake,125665131,316762,5466
|
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 10 cosigners/status: Locked/stake,1101023340,2912657,10860
|
||||||
Agora/Proposal/validator/advancing/legal/advance to next state/from: Locked/propsoal,238990044,676396,8061
|
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 10 cosigners/status: Finished/stake,1101023340,2912657,10860
|
||||||
Agora/Proposal/validator/advancing/legal/advance to next state/from: Locked/stake,125665131,316762,5466
|
Agora/Proposal/validator/voting/legal/propsoal,253541830,711367,8079
|
||||||
Agora/Proposal/validator/advancing/legal/advance to failed state/from: VotingReady/propsoal,236162599,670386,8055
|
Agora/Proposal/validator/voting/legal/stake,139493011,366217,5239
|
||||||
Agora/Proposal/validator/advancing/legal/advance to failed state/from: VotingReady/stake,125665131,316762,5462
|
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/legal/to next state/propsoal,222392288,630302,8060
|
||||||
Agora/Proposal/validator/advancing/legal/advance to failed state/from: Locked/propsoal,237293577,672790,8055
|
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/legal/to next state/stake,122255811,317464,5222
|
||||||
Agora/Proposal/validator/advancing/legal/advance to failed state/from: Locked/stake,125665131,316762,5462
|
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/legal/to failed state/propsoal,217337596,619581,8062
|
||||||
Agora/Proposal/validator/advancing/illegal/insufficient votes/stake,125665131,316762,5462
|
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/legal/to failed state/stake,122255811,317464,5224
|
||||||
Agora/Proposal/validator/advancing/illegal/initial state is Finished/stake,125665131,316762,5454
|
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/illegal/insufficient cosigns/stake,117978393,308804,5152
|
||||||
"Agora/Proposal/validator/unlocking/legal/1 proposals, voter, unlock stake + retract votes, VotingReady",245855872,689807,8029
|
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/legal/to next state/propsoal,602596705,1722841,10915
|
||||||
"Agora/Proposal/validator/unlocking/legal/1 proposals, creator, unlock stake, Finished",215131610,613807,8031
|
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/legal/to next state/stake,499559031,1304096,7957
|
||||||
"Agora/Proposal/validator/unlocking/legal/voter unlocks stake after voting/1 proposals, voter, unlock stake, Finished",212428891,605718,8033
|
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/legal/to failed state/propsoal,239706944,681645,8423
|
||||||
"Agora/Proposal/validator/unlocking/legal/voter unlocks stake after voting/1 proposals, voter, unlock stake, Locked",212428891,605718,8033
|
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/legal/to failed state/stake,122255811,317464,5465
|
||||||
"Agora/Proposal/validator/unlocking/legal/42 proposals, voter, unlock stake + retract votes, VotingReady",1775520444,5200586,29137
|
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/illegal/insufficient cosigns/stake,446950999,1172710,7606
|
||||||
"Agora/Proposal/validator/unlocking/legal/42 proposals, creator, unlock stake, Finished",1448162043,4319059,29321
|
Agora/Proposal/validator/advancing/from draft/with 10 cosigner(s)/legal/to next state/propsoal,1183788938,3402248,14484
|
||||||
"Agora/Proposal/validator/unlocking/legal/voter unlocks stake after voting/42 proposals, voter, unlock stake, Finished",1340521669,3979526,29305
|
Agora/Proposal/validator/advancing/from draft/with 10 cosigner(s)/legal/to next state/stake,1129251912,2994677,11375
|
||||||
"Agora/Proposal/validator/unlocking/legal/voter unlocks stake after voting/42 proposals, voter, unlock stake, Locked",1340521669,3979526,29305
|
Agora/Proposal/validator/advancing/from draft/with 10 cosigner(s)/legal/to failed state/propsoal,267668629,759225,8876
|
||||||
|
Agora/Proposal/validator/advancing/from draft/with 10 cosigner(s)/legal/to failed state/stake,122255811,317464,5767
|
||||||
|
Agora/Proposal/validator/advancing/from draft/with 10 cosigner(s)/illegal/insufficient cosigns/stake,1013080113,2701103,10673
|
||||||
|
Agora/Proposal/validator/advancing/legal/advance to next state/from: VotingReady/propsoal,253438293,715975,8069
|
||||||
|
Agora/Proposal/validator/advancing/legal/advance to next state/from: VotingReady/stake,122255811,317464,5229
|
||||||
|
Agora/Proposal/validator/advancing/legal/advance to next state/from: Locked/propsoal,242199184,683144,8069
|
||||||
|
Agora/Proposal/validator/advancing/legal/advance to next state/from: Locked/stake,122255811,317464,5229
|
||||||
|
Agora/Proposal/validator/advancing/legal/advance to failed state/from: VotingReady/propsoal,239371739,677134,8063
|
||||||
|
Agora/Proposal/validator/advancing/legal/advance to failed state/from: VotingReady/stake,122255811,317464,5225
|
||||||
|
Agora/Proposal/validator/advancing/legal/advance to failed state/from: Locked/propsoal,240502717,679538,8063
|
||||||
|
Agora/Proposal/validator/advancing/legal/advance to failed state/from: Locked/stake,122255811,317464,5225
|
||||||
|
Agora/Proposal/validator/advancing/illegal/insufficient votes/stake,122255811,317464,5225
|
||||||
|
Agora/Proposal/validator/advancing/illegal/initial state is Finished/stake,122255811,317464,5217
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: retract votes while voting/stake,128424052,334368,5219
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: retract votes while voting/propsoal,236436652,664524,8060
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: retract votes while voting/stake,131655298,343422,5235
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: retract votes while voting/propsoal,249908859,704466,8071
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 1 proposals/creator: remove creator locks when finished/stake,128424052,334368,5217
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 1 proposals/creator: remove creator locks when finished/propsoal,204228787,584379,8057
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: remove all locks when finished/stake,128424052,334368,5233
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: remove all locks when finished/propsoal,212663044,609283,8069
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: unlock after voting/Locked/stake,128424052,334368,5223
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: unlock after voting/Locked/propsoal,205544939,588263,8064
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: unlock after voting/Finished/stake,128424052,334368,5223
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter: unlock after voting/Finished/propsoal,205544939,588263,8064
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: remove vote locks when locked/stake,131655298,343422,5239
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 1 proposals/voter/creator: remove vote locks when locked/propsoal,219548722,629597,8075
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: retract votes while voting/stake,279213296,711004,7303
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: retract votes while voting/propsoal,379077900,1077188,10120
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: retract votes while voting/stake,295369526,756274,7380
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: retract votes while voting/propsoal,444606435,1268442,10172
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 5 proposals/creator: remove creator locks when finished/stake,279213296,711004,7293
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 5 proposals/creator: remove creator locks when finished/propsoal,312309599,902235,10113
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: remove all locks when finished/stake,279213296,711004,7374
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: remove all locks when finished/propsoal,350698580,1013987,10166
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: unlock after voting/Locked/stake,279213296,711004,7324
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: unlock after voting/Locked/propsoal,317778859,917311,10141
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: unlock after voting/Finished/stake,279213296,711004,7324
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter: unlock after voting/Finished/propsoal,317778859,917311,10141
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: remove vote locks when locked/stake,295369526,756274,7400
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 5 proposals/voter/creator: remove vote locks when locked/propsoal,383838970,1109957,10192
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: retract votes while voting/stake,467699851,1181799,9909
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: retract votes while voting/propsoal,557379460,1593018,12696
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: retract votes while voting/stake,500012311,1272339,10060
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: retract votes while voting/propsoal,687978405,1973412,12797
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 10 proposals/creator: remove creator locks when finished/stake,467699851,1181799,9888
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 10 proposals/creator: remove creator locks when finished/propsoal,447410614,1299555,12683
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: remove all locks when finished/stake,467699851,1181799,10049
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: remove all locks when finished/propsoal,523243000,1519867,12786
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: unlock after voting/Locked/stake,467699851,1181799,9949
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: unlock after voting/Locked/propsoal,458071259,1328621,12736
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: unlock after voting/Finished/stake,467699851,1181799,9949
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter: unlock after voting/Finished/propsoal,458071259,1328621,12736
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: remove vote locks when locked/stake,500012311,1272339,10100
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 10 proposals/voter/creator: remove vote locks when locked/propsoal,589201780,1710407,12837
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: retract votes while voting/stake,1674013803,4194887,26674
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: retract votes while voting/propsoal,1698509444,4894330,29250
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: retract votes while voting/stake,1809726135,4575155,27362
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: retract votes while voting/propsoal,2245559013,6485220,29709
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 42 proposals/creator: remove creator locks when finished/stake,1674013803,4194887,26590
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 42 proposals/creator: remove creator locks when finished/propsoal,1312057110,3842403,29206
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: remove all locks when finished/stake,1674013803,4194887,27301
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: remove all locks when finished/propsoal,1627527288,4757499,29648
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: unlock after voting/Locked/stake,1674013803,4194887,26843
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: unlock after voting/Locked/propsoal,1355942619,3961005,29419
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: unlock after voting/Finished/stake,1674013803,4194887,26843
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter: unlock after voting/Finished/propsoal,1355942619,3961005,29419
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: remove vote locks when locked/stake,1809726135,4575155,27531
|
||||||
|
Agora/Proposal/validator/unlocking/legal/with 42 proposals/voter/creator: remove vote locks when locked/propsoal,1903523764,5553287,29878
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Voter, status: Draft/stake",128424052,334368,5219
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Voter, status: Locked/stake",128424052,334368,5219
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Voter, status: Finished/stake",128424052,334368,5219
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Creator, status: Draft/stake",124053466,322518,5221
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Creator, status: Locked/stake",124053466,322518,5221
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Creator, status: Finished/stake",124053466,322518,5221
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Both, status: Draft/stake",131655298,343422,5235
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Both, status: Locked/stake",131655298,343422,5235
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Both, status: Finished/stake",131655298,343422,5235
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Irrelevant, status: Draft/stake",120822220,313464,5201
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Irrelevant, status: Locked/stake",120822220,313464,5201
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 1 proposals/retract votes while not voting/role: Irrelevant, status: Finished/stake",120822220,313464,5201
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Draftretract votes: True/stake,120822220,313464,5201
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Draftretract votes: False/stake,120822220,313464,5201
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: VotingReadyretract votes: True/stake,120822220,313464,5201
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: VotingReadyretract votes: False/stake,120822220,313464,5201
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Lockedretract votes: True/stake,120822220,313464,5201
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Lockedretract votes: False/stake,120822220,313464,5201
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Finishedretract votes: True/stake,120822220,313464,5201
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/unlock an irrelevant stake/status: Finishedretract votes: False/stake,120822220,313464,5201
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/remove creator too early/status: Draft/stake,128424052,334368,5217
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/remove creator too early/status: VotingReady/stake,128424052,334368,5217
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/remove creator too early/status: Locked/stake,128424052,334368,5217
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 1 proposals/creator: retract votes/stake,128424052,334368,5215
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Voter, status: Draft/stake",279213296,711004,7303
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Voter, status: Locked/stake",279213296,711004,7303
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Voter, status: Finished/stake",279213296,711004,7303
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Creator, status: Draft/stake",272564030,693562,7309
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Creator, status: Locked/stake",272564030,693562,7309
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Creator, status: Finished/stake",272564030,693562,7309
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Both, status: Draft/stake",295369526,756274,7380
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Both, status: Locked/stake",295369526,756274,7380
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Both, status: Finished/stake",295369526,756274,7380
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Irrelevant, status: Draft/stake",256407800,648292,7221
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Irrelevant, status: Locked/stake",256407800,648292,7221
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 5 proposals/retract votes while not voting/role: Irrelevant, status: Finished/stake",256407800,648292,7221
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Draftretract votes: True/stake,256407800,648292,7221
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Draftretract votes: False/stake,256407800,648292,7221
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: VotingReadyretract votes: True/stake,256407800,648292,7221
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: VotingReadyretract votes: False/stake,256407800,648292,7221
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Lockedretract votes: True/stake,256407800,648292,7221
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Lockedretract votes: False/stake,256407800,648292,7221
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Finishedretract votes: True/stake,256407800,648292,7221
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/unlock an irrelevant stake/status: Finishedretract votes: False/stake,256407800,648292,7221
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/remove creator too early/status: Draft/stake,279213296,711004,7293
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/remove creator too early/status: VotingReady/stake,279213296,711004,7293
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/remove creator too early/status: Locked/stake,279213296,711004,7293
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 5 proposals/creator: retract votes/stake,279213296,711004,7283
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Voter, status: Draft/stake",467699851,1181799,9909
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Voter, status: Locked/stake",467699851,1181799,9909
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Voter, status: Finished/stake",467699851,1181799,9909
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Creator, status: Draft/stake",458202235,1157367,9920
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Creator, status: Locked/stake",458202235,1157367,9920
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Creator, status: Finished/stake",458202235,1157367,9920
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Both, status: Draft/stake",500012311,1272339,10060
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Both, status: Locked/stake",500012311,1272339,10060
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Both, status: Finished/stake",500012311,1272339,10060
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Irrelevant, status: Draft/stake",425889775,1066827,9746
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Irrelevant, status: Locked/stake",425889775,1066827,9746
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 10 proposals/retract votes while not voting/role: Irrelevant, status: Finished/stake",425889775,1066827,9746
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Draftretract votes: True/stake,425889775,1066827,9746
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Draftretract votes: False/stake,425889775,1066827,9746
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: VotingReadyretract votes: True/stake,425889775,1066827,9746
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: VotingReadyretract votes: False/stake,425889775,1066827,9746
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Lockedretract votes: True/stake,425889775,1066827,9746
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Lockedretract votes: False/stake,425889775,1066827,9746
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Finishedretract votes: True/stake,425889775,1066827,9746
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/unlock an irrelevant stake/status: Finishedretract votes: False/stake,425889775,1066827,9746
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/remove creator too early/status: Draft/stake,467699851,1181799,9888
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/remove creator too early/status: VotingReady/stake,467699851,1181799,9888
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/remove creator too early/status: Locked/stake,467699851,1181799,9888
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 10 proposals/creator: retract votes/stake,467699851,1181799,9868
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Voter, status: Draft/stake",1674013803,4194887,26674
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Voter, status: Locked/stake",1674013803,4194887,26674
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Voter, status: Finished/stake",1674013803,4194887,26674
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Creator, status: Draft/stake",1646286747,4125719,26736
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Creator, status: Locked/stake",1646286747,4125719,26736
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Creator, status: Finished/stake",1646286747,4125719,26736
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Both, status: Draft/stake",1809726135,4575155,27362
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Both, status: Locked/stake",1809726135,4575155,27362
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Both, status: Finished/stake",1809726135,4575155,27362
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Irrelevant, status: Draft/stake",1510574415,3745451,25961
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Irrelevant, status: Locked/stake",1510574415,3745451,25961
|
||||||
|
"Agora/Proposal/validator/unlocking/illegal/with 42 proposals/retract votes while not voting/role: Irrelevant, status: Finished/stake",1510574415,3745451,25961
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Draftretract votes: True/stake,1510574415,3745451,25961
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Draftretract votes: False/stake,1510574415,3745451,25961
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: VotingReadyretract votes: True/stake,1510574415,3745451,25961
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: VotingReadyretract votes: False/stake,1510574415,3745451,25961
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Lockedretract votes: True/stake,1510574415,3745451,25961
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Lockedretract votes: False/stake,1510574415,3745451,25961
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Finishedretract votes: True/stake,1510574415,3745451,25961
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/unlock an irrelevant stake/status: Finishedretract votes: False/stake,1510574415,3745451,25961
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/remove creator too early/status: Draft/stake,1674013803,4194887,26590
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/remove creator too early/status: VotingReady/stake,1674013803,4194887,26590
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/remove creator too early/status: Locked/stake,1674013803,4194887,26590
|
||||||
|
Agora/Proposal/validator/unlocking/illegal/with 42 proposals/creator: retract votes/stake,1674013803,4194887,26506
|
||||||
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,21017788,55883,806
|
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,21017788,55883,806
|
||||||
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,33204186,88241,900
|
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,33204186,88241,900
|
||||||
Agora/Treasury/Validator/Positive/Allows for effect changes,31556709,81546,1452
|
Agora/Treasury/Validator/Positive/Allows for effect changes,31556709,81546,1452
|
||||||
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,21017788,55883,806
|
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,21017788,55883,806
|
||||||
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,33204186,88241,900
|
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,33204186,88241,900
|
||||||
Agora/Governor/policy/GST minting,51007235,144191,2034
|
Agora/Governor/policy/GST minting,51480023,145787,2048
|
||||||
Agora/Governor/validator/proposal creation,309689999,834675,9064
|
Agora/Governor/validator/proposal creation,303114849,813451,9390
|
||||||
Agora/Governor/validator/GATs minting,418560845,1137908,9187
|
Agora/Governor/validator/GATs minting,422654153,1147158,9516
|
||||||
Agora/Governor/validator/mutate governor state,88986020,248491,8662
|
Agora/Governor/validator/mutate governor state,90087778,252215,8991
|
||||||
|
|
|
||||||
|
Loading…
Add table
Add a link
Reference in a new issue