Merge pull request #142 from Liqwid-Labs/connor/fix-proposal-creation
Fix proposal creation
This commit is contained in:
commit
9f2066d138
23 changed files with 1720 additions and 664 deletions
|
|
@ -106,7 +106,7 @@ governorDatumValidProperty =
|
|||
thres <- genProposalThresholds c
|
||||
|
||||
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
|
||||
taggedInteger p = Tagged <$> chooseInteger p
|
||||
genProposalThresholds :: GovernorDatumCases -> Gen ProposalThresholds
|
||||
|
|
@ -181,6 +181,7 @@ governorMintingProperty =
|
|||
, nextProposalId = ProposalId 0
|
||||
, proposalTimings = def
|
||||
, createProposalTimeRangeMaxWidth = def
|
||||
, maximumProposalsPerStake = 3
|
||||
}
|
||||
|
||||
gen :: GovernorPolicyCases -> Gen ScriptContext
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ mkEffectTxInfo newGovDatum =
|
|||
, nextProposalId = ProposalId 0
|
||||
, proposalTimings = def
|
||||
, createProposalTimeRangeMaxWidth = def
|
||||
, maximumProposalsPerStake = 3
|
||||
}
|
||||
governorInputDatum :: Datum
|
||||
governorInputDatum = Datum $ toBuiltinData governorInputDatum'
|
||||
|
|
@ -168,6 +169,7 @@ validNewGovernorDatum =
|
|||
, nextProposalId = ProposalId 42
|
||||
, proposalTimings = def
|
||||
, createProposalTimeRangeMaxWidth = def
|
||||
, maximumProposalsPerStake = 3
|
||||
}
|
||||
|
||||
invalidNewGovernorDatum :: GovernorDatum
|
||||
|
|
@ -180,4 +182,5 @@ invalidNewGovernorDatum =
|
|||
, nextProposalId = ProposalId 42
|
||||
, proposalTimings = def
|
||||
, createProposalTimeRangeMaxWidth = def
|
||||
, maximumProposalsPerStake = 3
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{- |
|
||||
Module : Spec.Sample.Governor
|
||||
Module : Sample.Governor
|
||||
Maintainer : connor@mlabs.city
|
||||
Description: Sample based testing for Governor utxos
|
||||
|
||||
|
|
@ -119,6 +119,7 @@ mintGST =
|
|||
, nextProposalId = ProposalId 0
|
||||
, proposalTimings = def
|
||||
, createProposalTimeRangeMaxWidth = def
|
||||
, maximumProposalsPerStake = 3
|
||||
}
|
||||
|
||||
witness :: ValidatorHash
|
||||
|
|
@ -183,6 +184,7 @@ createProposal =
|
|||
, nextProposalId = thisProposalId
|
||||
, proposalTimings = def
|
||||
, createProposalTimeRangeMaxWidth = def
|
||||
, maximumProposalsPerStake = 3
|
||||
}
|
||||
|
||||
effects =
|
||||
|
|
@ -216,8 +218,7 @@ createProposal =
|
|||
|
||||
proposalLocks :: [ProposalLock]
|
||||
proposalLocks =
|
||||
[ ProposalLock (ResultTag 0) thisProposalId
|
||||
, ProposalLock (ResultTag 1) thisProposalId
|
||||
[ Created thisProposalId
|
||||
]
|
||||
stakeOutputDatum :: StakeDatum
|
||||
stakeOutputDatum = stakeInputDatum {lockedBy = proposalLocks}
|
||||
|
|
@ -304,6 +305,7 @@ mintGATs =
|
|||
, nextProposalId = ProposalId 5
|
||||
, proposalTimings = def
|
||||
, createProposalTimeRangeMaxWidth = def
|
||||
, maximumProposalsPerStake = 3
|
||||
}
|
||||
|
||||
effects =
|
||||
|
|
@ -423,6 +425,7 @@ mutateState =
|
|||
, nextProposalId = ProposalId 5
|
||||
, proposalTimings = def
|
||||
, createProposalTimeRangeMaxWidth = def
|
||||
, maximumProposalsPerStake = 3
|
||||
}
|
||||
|
||||
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 (
|
||||
advanceToNextStateInTimeParameters,
|
||||
advanceToFailedStateDueToTimeoutParameters,
|
||||
|
|
@ -31,7 +38,7 @@ import Agora.Proposal.Time (
|
|||
)
|
||||
import Agora.SafeMoney (GTTag)
|
||||
import Agora.Stake (
|
||||
ProposalLock (ProposalLock),
|
||||
ProposalLock (..),
|
||||
Stake (gtClassRef),
|
||||
StakeDatum (..),
|
||||
StakeRedeemer (WitnessStake),
|
||||
|
|
@ -69,7 +76,7 @@ import PlutusLedgerApi.V1 (
|
|||
)
|
||||
import PlutusLedgerApi.V1.Value qualified as Value
|
||||
import PlutusTx.AssocMap qualified as AssocMap
|
||||
import Sample.Proposal.Shared (proposalTxRef, stakeTxRef, testFunc)
|
||||
import Sample.Proposal.Shared (proposalTxRef, stakeTxRef)
|
||||
import Sample.Shared (
|
||||
minAda,
|
||||
proposalPolicySymbol,
|
||||
|
|
@ -79,7 +86,7 @@ import Sample.Shared (
|
|||
stakeValidatorHash,
|
||||
)
|
||||
import Sample.Shared qualified as Shared
|
||||
import Test.Specification (SpecificationTree, group)
|
||||
import Test.Specification (SpecificationTree, group, testValidator)
|
||||
import Test.Util (closedBoundedInterval, pubKeyHashes, sortValue, updateMap)
|
||||
|
||||
-- | Parameters for state transition of proposals.
|
||||
|
|
@ -99,19 +106,24 @@ data Parameters = Parameters
|
|||
, stakeCount :: Integer
|
||||
-- ^ The number of stakes.
|
||||
, signByAllCosigners :: Bool
|
||||
-- ^ Whether the transaction is signed by all the cosigners.
|
||||
, perStakeGTs :: Tagged GTTag Integer
|
||||
-- ^ The staked amount of each stake.
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
-- | Reference to the proposal UTXO.
|
||||
proposalRef :: TxOutRef
|
||||
proposalRef = TxOutRef proposalTxRef 1
|
||||
|
||||
-- | Create the reference to a particular stake UTXO.
|
||||
mkStakeRef :: Int -> TxOutRef
|
||||
mkStakeRef = TxOutRef stakeTxRef . (+ 2) . fromIntegral
|
||||
|
||||
---
|
||||
|
||||
-- | Default effects of the propsoal.
|
||||
defEffects :: AssocMap.Map ResultTag (AssocMap.Map ValidatorHash DatumHash)
|
||||
defEffects =
|
||||
AssocMap.fromList
|
||||
|
|
@ -119,14 +131,19 @@ defEffects =
|
|||
, (ResultTag 1, AssocMap.empty)
|
||||
]
|
||||
|
||||
-- | Empty votes for the default effects.
|
||||
emptyVotes :: ProposalVotes
|
||||
emptyVotes = emptyVotesFor defEffects
|
||||
|
||||
{- | The default proposal statring time, which doesn't really matter in this
|
||||
case.
|
||||
-}
|
||||
proposalStartingTime :: POSIXTime
|
||||
proposalStartingTime = 0
|
||||
|
||||
---
|
||||
|
||||
-- | Create the input proposal datum given the parameters.
|
||||
mkProposalInputDatum :: Parameters -> ProposalDatum
|
||||
mkProposalInputDatum ps =
|
||||
ProposalDatum
|
||||
|
|
@ -140,6 +157,7 @@ mkProposalInputDatum ps =
|
|||
, startingTime = ProposalStartingTime proposalStartingTime
|
||||
}
|
||||
|
||||
-- | Create the input stake datums given the parameters.
|
||||
mkStakeInputDatums :: Parameters -> [StakeDatum]
|
||||
mkStakeInputDatums ps =
|
||||
map
|
||||
|
|
@ -154,28 +172,37 @@ mkStakeInputDatums ps =
|
|||
where
|
||||
existingLocks :: [ProposalLock]
|
||||
existingLocks =
|
||||
[ ProposalLock (ResultTag 0) (ProposalId 0)
|
||||
, ProposalLock (ResultTag 2) (ProposalId 1)
|
||||
[ Voted (ProposalId 0) (ResultTag 0)
|
||||
, Voted (ProposalId 1) (ResultTag 2)
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
-- | Script purpose of the proposal validator.
|
||||
proposalScriptPurpose :: ScriptPurpose
|
||||
proposalScriptPurpose = Spending proposalRef
|
||||
|
||||
-- | Script purpose of the stake validator, given which stake we want to spend.
|
||||
mkStakeScriptPurpose :: Int -> ScriptPurpose
|
||||
mkStakeScriptPurpose = Spending . mkStakeRef
|
||||
|
||||
---
|
||||
|
||||
{- | The propsoal redeemer used to spend the proposal UTXO, which is always
|
||||
'AdvanceProposal' in this case.
|
||||
-}
|
||||
proposalRedeemer :: ProposalRedeemer
|
||||
proposalRedeemer = AdvanceProposal
|
||||
|
||||
{- | The propsoal redeemer used to spend the stake UTXO, which is always
|
||||
'WitnessStake' in this case.
|
||||
-}
|
||||
stakeRedeemer :: StakeRedeemer
|
||||
stakeRedeemer = WitnessStake
|
||||
|
||||
---
|
||||
|
||||
-- | Create some valid stake owners.
|
||||
mkStakeOwners :: Parameters -> [PubKeyHash]
|
||||
mkStakeOwners ps =
|
||||
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 advanceFrom =
|
||||
case advanceFrom of
|
||||
|
|
@ -315,6 +345,9 @@ mkInTimeTimeRange advanceFrom =
|
|||
)
|
||||
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 advanceFrom =
|
||||
case advanceFrom of
|
||||
|
|
@ -363,6 +396,7 @@ mkTooLateTimeRange advanceFrom =
|
|||
|
||||
---
|
||||
|
||||
-- | Next state of the given proposal status.
|
||||
getNextState :: ProposalStatus -> ProposalStatus
|
||||
getNextState = \case
|
||||
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 name ps isValidForProposalValidator = group name [proposal, stake]
|
||||
where
|
||||
|
|
@ -482,7 +519,7 @@ mkTestTree name ps isValidForProposalValidator = group name [proposal, stake]
|
|||
|
||||
proposal =
|
||||
let proposalInputDatum = mkProposalInputDatum ps
|
||||
in testFunc
|
||||
in testValidator
|
||||
isValidForProposalValidator
|
||||
"propsoal"
|
||||
(proposalValidator Shared.proposal)
|
||||
|
|
@ -497,7 +534,7 @@ mkTestTree name ps isValidForProposalValidator = group name [proposal, stake]
|
|||
let idx = 0
|
||||
stakeInputDatum = mkStakeInputDatums ps !! idx
|
||||
isValid = not $ ps.alterOutputStakes
|
||||
in testFunc
|
||||
in testValidator
|
||||
isValid
|
||||
"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 (
|
||||
Parameters (..),
|
||||
validCosignNParameters,
|
||||
|
|
@ -57,7 +64,7 @@ import PlutusLedgerApi.V1 (
|
|||
)
|
||||
import PlutusLedgerApi.V1.Value qualified as Value
|
||||
import PlutusTx.AssocMap qualified as AssocMap
|
||||
import Sample.Proposal.Shared (proposalTxRef, stakeTxRef, testFunc)
|
||||
import Sample.Proposal.Shared (proposalTxRef, stakeTxRef)
|
||||
import Sample.Shared (
|
||||
minAda,
|
||||
proposalPolicySymbol,
|
||||
|
|
@ -71,6 +78,7 @@ import Sample.Shared qualified as Shared
|
|||
import Test.Specification (
|
||||
SpecificationTree,
|
||||
group,
|
||||
testValidator,
|
||||
)
|
||||
import Test.Util (closedBoundedInterval, pubKeyHashes, sortValue)
|
||||
|
||||
|
|
@ -317,7 +325,7 @@ mkTestTree name ps isValid = group name [proposal, stake]
|
|||
|
||||
proposal =
|
||||
let proposalInputDatum = mkProposalInputDatum ps
|
||||
in testFunc
|
||||
in testValidator
|
||||
isValid
|
||||
"propsoal"
|
||||
(proposalValidator Shared.proposal)
|
||||
|
|
@ -332,7 +340,7 @@ mkTestTree name ps isValid = group name [proposal, stake]
|
|||
let idx = 0
|
||||
stakeInputDatum = mkStakeInputDatums ps !! idx
|
||||
isValid = not ps.alterOutputStakes
|
||||
in testFunc
|
||||
in testValidator
|
||||
isValid
|
||||
"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)
|
||||
import Plutarch.Lift (PUnsafeLiftDecl (..))
|
||||
import PlutusLedgerApi.V1 (ScriptContext, ToData, TxId)
|
||||
import Test.Specification (
|
||||
SpecificationTree,
|
||||
validatorFailsWith,
|
||||
validatorSucceedsWith,
|
||||
)
|
||||
Shared constants for propsoal samples.
|
||||
-}
|
||||
module Sample.Proposal.Shared (proposalTxRef, stakeTxRef) where
|
||||
|
||||
import PlutusLedgerApi.V1 (TxId)
|
||||
|
||||
-- | 'TxId' of all the propsoal inputs in the samples.
|
||||
proposalTxRef :: TxId
|
||||
|
|
@ -16,24 +16,3 @@ proposalTxRef = "0b2086cbf8b6900f8cb65e012de4516cb66b5cb08a9aaba12a8b88be"
|
|||
-- | 'TxId' of all the stake inputs in the samples.
|
||||
stakeTxRef :: TxId
|
||||
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 (
|
||||
unlockStake,
|
||||
StakeRole (..),
|
||||
UnlockStakeParameters (..),
|
||||
votesTemplate,
|
||||
emptyEffectFor,
|
||||
mkProposalInputDatum,
|
||||
mkStakeInputDatum,
|
||||
mkProposalValidatorTestCase,
|
||||
Parameters (..),
|
||||
unlockStake,
|
||||
mkTestTree,
|
||||
mkVoterRetractVotesWhileVotingParameters,
|
||||
mkVoterCreatorRetractVotesWhileVotingParameters,
|
||||
mkCreatorRemoveCreatorLocksWhenFinishedParameters,
|
||||
mkVoterCreatorRemoveAllLocksWhenFinishedParameters,
|
||||
mkVoterUnlockStakeAfterVotingParameters,
|
||||
mkVoterCreatorRemoveVoteLocksWhenLockedParameters,
|
||||
mkRetractVotesWhileNotVoting,
|
||||
mkUnockIrrelevantStakeParameters,
|
||||
mkRemoveCreatorLockBeforeFinishedParameters,
|
||||
mkRetractVotesWithCreatorStakeParamaters,
|
||||
mkAlterStakeParameters,
|
||||
) where
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
|
@ -21,9 +35,8 @@ import Agora.Proposal (
|
|||
)
|
||||
import Agora.Proposal.Scripts (proposalValidator)
|
||||
import Agora.Proposal.Time (ProposalStartingTime (ProposalStartingTime))
|
||||
import Agora.Stake (ProposalLock (ProposalLock), Stake (..), StakeDatum (..))
|
||||
import Control.Monad (join)
|
||||
import Data.Coerce (coerce)
|
||||
import Agora.Stake (ProposalLock (..), Stake (..), StakeDatum (..), StakeRedeemer (RetractVotes))
|
||||
import Agora.Stake.Scripts (stakeValidator)
|
||||
import Data.Default.Class (Default (def))
|
||||
import Data.Tagged (Tagged (..), untag)
|
||||
import Plutarch.Context (
|
||||
|
|
@ -32,14 +45,15 @@ import Plutarch.Context (
|
|||
input,
|
||||
output,
|
||||
script,
|
||||
signedWith,
|
||||
txId,
|
||||
withDatum,
|
||||
withRefIndex,
|
||||
withTxId,
|
||||
withOutRef,
|
||||
withValue,
|
||||
)
|
||||
import PlutusLedgerApi.V1 (
|
||||
DatumHash,
|
||||
PubKeyHash,
|
||||
ScriptContext (..),
|
||||
ScriptPurpose (Spending),
|
||||
TxInfo (..),
|
||||
|
|
@ -48,7 +62,7 @@ import PlutusLedgerApi.V1 (
|
|||
)
|
||||
import PlutusLedgerApi.V1.Value qualified as Value
|
||||
import PlutusTx.AssocMap qualified as AssocMap
|
||||
import Sample.Proposal.Shared (proposalTxRef, stakeTxRef, testFunc)
|
||||
import Sample.Proposal.Shared (stakeTxRef)
|
||||
import Sample.Shared (
|
||||
minAda,
|
||||
proposalPolicySymbol,
|
||||
|
|
@ -59,7 +73,7 @@ import Sample.Shared (
|
|||
stakeValidatorHash,
|
||||
)
|
||||
import Sample.Shared qualified as Shared
|
||||
import Test.Specification (SpecificationTree)
|
||||
import Test.Specification (SpecificationTree, group, testValidator)
|
||||
import Test.Util (sortValue, updateMap)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
|
@ -82,106 +96,130 @@ emptyEffectFor (ProposalVotes vs) =
|
|||
map (,AssocMap.empty) (AssocMap.keys vs)
|
||||
|
||||
-- | The default vote option that will be used by functions in this module.
|
||||
defaultVoteFor :: ResultTag
|
||||
defaultVoteFor = ResultTag 0
|
||||
defVoteFor :: ResultTag
|
||||
defVoteFor = ResultTag 0
|
||||
|
||||
-- | The default number of GTs the stake will have.
|
||||
defaultStakedGTs :: Tagged _ Integer
|
||||
defaultStakedGTs = Tagged 100000
|
||||
defStakedGTs :: Tagged _ Integer
|
||||
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.
|
||||
data StakeRole
|
||||
= -- | The stake was spent to vote for a paraticular option.
|
||||
Voter
|
||||
| -- | The stake was used to created the proposal.
|
||||
| -- | The stake was used to create the proposal.
|
||||
Creator
|
||||
| -- | The stake was used to both create and vote for the proposal.
|
||||
Both
|
||||
| -- | The stake has nothing to do with the proposal.
|
||||
Irrelevant
|
||||
deriving stock (Bounded, Enum, Show)
|
||||
|
||||
-- | Parameters for creating a 'TxOut' that unlocks a stake.
|
||||
data UnlockStakeParameters = UnlockStakeParameters
|
||||
data Parameters = Parameters
|
||||
{ proposalCount :: Integer
|
||||
-- ^ The number of proposals in the 'TxOut'.
|
||||
, stakeUsage :: StakeRole
|
||||
, stakeRole :: StakeRole
|
||||
-- ^ The role of the stake we're unlocking.
|
||||
, retractVotes :: Bool
|
||||
-- ^ 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
|
||||
-- ^ The state of all the proposals.
|
||||
, alterOutputStake :: Bool
|
||||
}
|
||||
|
||||
instance Show UnlockStakeParameters where
|
||||
show p =
|
||||
let role = case p.stakeUsage of
|
||||
Voter -> "voter"
|
||||
Creator -> "creator"
|
||||
_ -> "irrelevant stake"
|
||||
-- | Iterate over the proposal id of every proposal, given the number of proposals.
|
||||
forEachProposalId :: Parameters -> (ProposalId -> a) -> [a]
|
||||
forEachProposalId ps = forEachProposalId' ps.proposalCount
|
||||
where
|
||||
forEachProposalId' :: Integer -> (ProposalId -> a) -> [a]
|
||||
forEachProposalId' 0 _ = error "zero proposal"
|
||||
forEachProposalId' n f = f . ProposalId <$> [0 .. n - 1]
|
||||
|
||||
action =
|
||||
if p.retractVotes
|
||||
then "unlock stake + retract votes"
|
||||
else "unlock stake"
|
||||
-- | Create locks for the input stake given the parameters.
|
||||
mkInputStakeLocks :: Parameters -> [ProposalLock]
|
||||
mkInputStakeLocks ps = mconcat $ forEachProposalId ps $ mkStakeLocksFor ps.stakeRole
|
||||
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"]
|
||||
in mconcat [proposalInfo, ", ", role, ", ", action, ", ", while]
|
||||
-- | Create the stake input datum given the parameters.
|
||||
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.
|
||||
mkProposals :: UnlockStakeParameters -> [(ProposalDatum, ProposalDatum)]
|
||||
mkProposals p = forEachProposalId p.proposalCount $ mkProposalDatumPair p
|
||||
|
||||
-- | 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 _ _ = []
|
||||
mkProposals :: Parameters -> [(ProposalDatum, ProposalDatum)]
|
||||
mkProposals ps = forEachProposalId ps $ mkProposalDatumPair ps
|
||||
|
||||
-- | Create the input proposal datum.
|
||||
mkProposalInputDatum :: UnlockStakeParameters -> ProposalId -> ProposalDatum
|
||||
mkProposalInputDatum :: Parameters -> ProposalId -> ProposalDatum
|
||||
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.
|
||||
mkProposalDatumPair ::
|
||||
UnlockStakeParameters ->
|
||||
Parameters ->
|
||||
ProposalId ->
|
||||
(ProposalDatum, ProposalDatum)
|
||||
mkProposalDatumPair params pid =
|
||||
let owner = signer
|
||||
|
||||
inputVotes = mkInputVotes params.stakeUsage $ untag defaultStakedGTs
|
||||
let inputVotes = mkInputVotes params.stakeRole $ untag defStakedGTs
|
||||
|
||||
input =
|
||||
ProposalDatum
|
||||
{ proposalId = pid
|
||||
, effects = emptyEffectFor votesTemplate
|
||||
, status = params.proposalStatus
|
||||
, cosigners = [owner]
|
||||
, cosigners = [defOwner]
|
||||
, thresholds = def
|
||||
, votes = inputVotes
|
||||
, timingConfig = def
|
||||
|
|
@ -200,62 +238,62 @@ mkProposalDatumPair params pid =
|
|||
-- The staked amount/votes.
|
||||
Integer ->
|
||||
ProposalVotes
|
||||
mkInputVotes Voter vc =
|
||||
ProposalVotes $
|
||||
updateMap (Just . const vc) defaultVoteFor $
|
||||
getProposalVotes votesTemplate
|
||||
mkInputVotes Creator _ =
|
||||
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
|
||||
mkInputVotes _ _ = votesTemplate
|
||||
|
||||
-- | Create a 'TxInfo' that tries to unlock a stake.
|
||||
unlockStake :: UnlockStakeParameters -> TxInfo
|
||||
unlockStake p =
|
||||
unlockStake :: Parameters -> TxInfo
|
||||
unlockStake ps =
|
||||
let pst = Value.singleton proposalPolicySymbol "" 1
|
||||
sst = Value.assetClassValue stakeAssetClass 1
|
||||
|
||||
pIODatums = mkProposals p
|
||||
(sInDatum, sOutDatum) = mkStakeDatumPair p
|
||||
pIODatums = mkProposals ps
|
||||
|
||||
proposals =
|
||||
foldMap
|
||||
( \(i, o) ->
|
||||
( \((i, o), idx) ->
|
||||
mconcat
|
||||
@BaseBuilder
|
||||
[ input $
|
||||
script proposalValidatorHash
|
||||
. withValue pst
|
||||
. withDatum i
|
||||
. withTxId proposalTxRef
|
||||
. withRefIndex (coerce i.proposalId + 2)
|
||||
. withOutRef (mkProposalRef idx)
|
||||
, output $
|
||||
script proposalValidatorHash
|
||||
. withValue (sortValue $ pst <> minAda)
|
||||
. withDatum o
|
||||
]
|
||||
)
|
||||
pIODatums
|
||||
(zip pIODatums [0 ..])
|
||||
|
||||
stakeValue =
|
||||
sortValue $
|
||||
mconcat
|
||||
[ Value.assetClassValue
|
||||
(untag stake.gtClassRef)
|
||||
(untag defaultStakedGTs)
|
||||
(untag defStakedGTs)
|
||||
, sst
|
||||
, minAda
|
||||
]
|
||||
|
||||
sInDatum = mkStakeInputDatum ps
|
||||
sOutDatum = mkStakeOutputDatum ps
|
||||
|
||||
stakes =
|
||||
mconcat @BaseBuilder
|
||||
[ input $
|
||||
script stakeValidatorHash
|
||||
. withValue stakeValue
|
||||
. withDatum sInDatum
|
||||
. withTxId stakeTxRef
|
||||
. withRefIndex 1
|
||||
. withOutRef stakeRef
|
||||
, output $
|
||||
script stakeValidatorHash
|
||||
. withValue stakeValue
|
||||
|
|
@ -267,23 +305,243 @@ unlockStake p =
|
|||
[ txId "388bc0b897b3dadcd479da4c88291de4113a50b72ddbed001faf7fc03f11bc52"
|
||||
, proposals
|
||||
, stakes
|
||||
, signedWith defOwner
|
||||
]
|
||||
in buildTxInfoUnsafe builder
|
||||
|
||||
-- | Create a test case that tests the proposal validator's @'Unlock' _@ redeemer.
|
||||
mkProposalValidatorTestCase :: UnlockStakeParameters -> Bool -> SpecificationTree
|
||||
mkProposalValidatorTestCase p shouldSucceed =
|
||||
let datum = mkProposalInputDatum p $ ProposalId 0
|
||||
redeemer = Unlock (ResultTag 0)
|
||||
name = show p
|
||||
scriptContext =
|
||||
ScriptContext
|
||||
(unlockStake p)
|
||||
(Spending (TxOutRef proposalTxRef 2))
|
||||
in testFunc
|
||||
shouldSucceed
|
||||
name
|
||||
(proposalValidator Shared.proposal)
|
||||
datum
|
||||
redeemer
|
||||
scriptContext
|
||||
-- | Reference to the stake UTXO.
|
||||
stakeRef :: TxOutRef
|
||||
stakeRef = TxOutRef stakeTxRef 1
|
||||
|
||||
-- | Generate the reference to a proposal UTXOs, given the index of the proposal.
|
||||
mkProposalRef :: Int -> TxOutRef
|
||||
mkProposalRef offset = TxOutRef stakeTxRef $ 2 + fromIntegral offset
|
||||
|
||||
-- | Proposal redeemer used by 'mkTestTree', in this case it's always 'Unlock'.
|
||||
proposalRedeemer :: ProposalRedeemer
|
||||
proposalRedeemer = Unlock
|
||||
|
||||
-- | Stake redeemer used by 'mkTestTree', in this case it's always 'RetractVotes'.
|
||||
stakeRedeemer :: StakeRedeemer
|
||||
stakeRedeemer = RetractVotes
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
{- | 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 (
|
||||
validVoteParameters,
|
||||
mkTestTree,
|
||||
|
|
@ -17,7 +24,7 @@ import Agora.Proposal.Time (
|
|||
ProposalTimingConfig (draftTime, votingTime),
|
||||
)
|
||||
import Agora.Stake (
|
||||
ProposalLock (ProposalLock),
|
||||
ProposalLock (..),
|
||||
Stake (gtClassRef),
|
||||
StakeDatum (..),
|
||||
StakeRedeemer (PermitVote),
|
||||
|
|
@ -47,7 +54,7 @@ import PlutusLedgerApi.V1 (
|
|||
)
|
||||
import PlutusLedgerApi.V1.Value qualified as Value
|
||||
import PlutusTx.AssocMap qualified as AssocMap
|
||||
import Sample.Proposal.Shared (proposalTxRef, stakeTxRef, testFunc)
|
||||
import Sample.Proposal.Shared (proposalTxRef, stakeTxRef)
|
||||
import Sample.Shared (
|
||||
minAda,
|
||||
proposalPolicySymbol,
|
||||
|
|
@ -61,13 +68,16 @@ import Sample.Shared qualified as Shared
|
|||
import Test.Specification (
|
||||
SpecificationTree,
|
||||
group,
|
||||
testValidator,
|
||||
validatorSucceedsWith,
|
||||
)
|
||||
import Test.Util (closedBoundedInterval, sortValue, updateMap)
|
||||
|
||||
-- | Reference to the proposal UTXO.
|
||||
proposalRef :: TxOutRef
|
||||
proposalRef = TxOutRef proposalTxRef 0
|
||||
|
||||
-- | Reference to the stake UTXO.
|
||||
stakeRef :: TxOutRef
|
||||
stakeRef = TxOutRef stakeTxRef 1
|
||||
|
||||
|
|
@ -79,9 +89,11 @@ data Parameters = Parameters
|
|||
-- ^ The count of votes.
|
||||
}
|
||||
|
||||
-- | The public key hash of the stake owner.
|
||||
stakeOwner :: PubKeyHash
|
||||
stakeOwner = signer
|
||||
|
||||
-- | The votes of the input proposals.
|
||||
initialVotes :: AssocMap.Map ResultTag Integer
|
||||
initialVotes =
|
||||
AssocMap.fromList
|
||||
|
|
@ -89,6 +101,7 @@ initialVotes =
|
|||
, (ResultTag 1, 4242)
|
||||
]
|
||||
|
||||
-- | The input proposal datum.
|
||||
proposalInputDatum :: ProposalDatum
|
||||
proposalInputDatum =
|
||||
ProposalDatum
|
||||
|
|
@ -106,12 +119,16 @@ proposalInputDatum =
|
|||
, startingTime = ProposalStartingTime 0
|
||||
}
|
||||
|
||||
-- | The locks of the input stake.
|
||||
existingLocks :: [ProposalLock]
|
||||
existingLocks =
|
||||
[ ProposalLock (ResultTag 0) (ProposalId 0)
|
||||
, ProposalLock (ResultTag 2) (ProposalId 1)
|
||||
[ Voted (ProposalId 0) (ResultTag 0)
|
||||
, Voted (ProposalId 1) (ResultTag 2)
|
||||
]
|
||||
|
||||
{- | Set the 'StakeDatum.stakedAmount' according to the number of votes being
|
||||
casted.
|
||||
-}
|
||||
mkStakeInputDatum :: Parameters -> StakeDatum
|
||||
mkStakeInputDatum params =
|
||||
StakeDatum
|
||||
|
|
@ -120,14 +137,19 @@ mkStakeInputDatum params =
|
|||
, lockedBy = existingLocks
|
||||
}
|
||||
|
||||
-- | Create the proposal redeemer. In this case @'Vote' _@ will always be used.
|
||||
mkProposalRedeemer :: Parameters -> ProposalRedeemer
|
||||
mkProposalRedeemer = Vote . voteFor
|
||||
|
||||
-- | Place new proposal locks on the stake.
|
||||
mkNewLock :: Parameters -> ProposalLock
|
||||
mkNewLock ps = ProposalLock ps.voteFor proposalInputDatum.proposalId
|
||||
mkNewLock = Voted proposalInputDatum.proposalId . voteFor
|
||||
|
||||
mkStakeRedeemer :: Parameters -> StakeRedeemer
|
||||
mkStakeRedeemer = PermitVote . mkNewLock
|
||||
{- | The stake redeemer that is used in 'mkTestTree'. In this case it'll always be
|
||||
'PermitVote'.
|
||||
-}
|
||||
stakeRedeemer :: StakeRedeemer
|
||||
stakeRedeemer = PermitVote
|
||||
|
||||
-- | Create a valid transaction that votes on a propsal, given the parameters.
|
||||
vote :: Parameters -> TxInfo
|
||||
|
|
@ -210,6 +232,7 @@ vote params =
|
|||
|
||||
---
|
||||
|
||||
-- | Valida parameters that vote on the proposal.
|
||||
validVoteParameters :: Parameters
|
||||
validVoteParameters =
|
||||
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 name ps isValid = group name [proposal, stake]
|
||||
where
|
||||
txInfo = vote ps
|
||||
|
||||
proposal =
|
||||
testFunc
|
||||
testValidator
|
||||
isValid
|
||||
"propsoal"
|
||||
(proposalValidator Shared.proposal)
|
||||
|
|
@ -242,7 +268,7 @@ mkTestTree name ps isValid = group name [proposal, stake]
|
|||
"stake"
|
||||
(stakeValidator Shared.stake)
|
||||
stakeInputDatum
|
||||
(mkStakeRedeemer ps)
|
||||
stakeRedeemer
|
||||
( ScriptContext
|
||||
txInfo
|
||||
(Spending stakeRef)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ specs =
|
|||
(ProposalId 0)
|
||||
def
|
||||
def
|
||||
3
|
||||
)
|
||||
MutateGovernor
|
||||
( ScriptContext
|
||||
|
|
@ -60,6 +61,7 @@ specs =
|
|||
(ProposalId 0)
|
||||
def
|
||||
def
|
||||
3
|
||||
)
|
||||
MutateGovernor
|
||||
( ScriptContext
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ specs =
|
|||
(ProposalId 0)
|
||||
def
|
||||
def
|
||||
3
|
||||
)
|
||||
CreateProposal
|
||||
createProposal
|
||||
|
|
@ -58,6 +59,7 @@ specs =
|
|||
(ProposalId 5)
|
||||
def
|
||||
def
|
||||
3
|
||||
)
|
||||
MintGATs
|
||||
mintGATs
|
||||
|
|
@ -69,6 +71,7 @@ specs =
|
|||
(ProposalId 5)
|
||||
def
|
||||
def
|
||||
3
|
||||
)
|
||||
MutateGovernor
|
||||
mutateState
|
||||
|
|
|
|||
|
|
@ -7,33 +7,83 @@ Tests for Proposal policy and validator
|
|||
-}
|
||||
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.Cosign qualified as Cosign
|
||||
import Sample.Proposal.Create qualified as Create
|
||||
import Sample.Proposal.UnlockStake qualified as UnlockStake
|
||||
import Sample.Proposal.Vote qualified as Vote
|
||||
import Sample.Shared qualified as Shared (proposal)
|
||||
import Test.Specification (
|
||||
SpecificationTree,
|
||||
group,
|
||||
policySucceedsWith,
|
||||
)
|
||||
|
||||
-- | Stake specs.
|
||||
specs :: [SpecificationTree]
|
||||
specs =
|
||||
[ group
|
||||
"policy"
|
||||
[ policySucceedsWith
|
||||
"proposalCreation"
|
||||
(proposalPolicy Shared.proposal.governorSTAssetClass)
|
||||
()
|
||||
Proposal.proposalCreation
|
||||
"policy (proposal creation)"
|
||||
[ Create.mkTestTree
|
||||
"legal"
|
||||
Create.totallyValidParameters
|
||||
True
|
||||
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
|
||||
"validator"
|
||||
|
|
@ -166,110 +216,92 @@ specs =
|
|||
pure $ Advance.mkTestTree name ps False
|
||||
]
|
||||
in [draftGroup, legalGroup, illegalGroup]
|
||||
, group "unlocking" $ do
|
||||
proposalCount <- [1, 42]
|
||||
, group "unlocking" $
|
||||
let proposalCountCases = [1, 5, 10, 42]
|
||||
|
||||
let legalGroup = group "legal" $ do
|
||||
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
|
||||
mkSubgroupName nProposals = "with " <> show nProposals <> " proposals"
|
||||
|
||||
let voterUnlockStakeAfterVoting = group "voter unlocks stake after voting" $ do
|
||||
status <- [Finished, Locked]
|
||||
|
||||
pure $
|
||||
UnlockStake.mkProposalValidatorTestCase
|
||||
UnlockStake.UnlockStakeParameters
|
||||
{ UnlockStake.proposalCount = proposalCount
|
||||
, UnlockStake.stakeUsage = UnlockStake.Voter
|
||||
, UnlockStake.retractVotes = False
|
||||
, UnlockStake.proposalStatus = status
|
||||
}
|
||||
True
|
||||
|
||||
[ voterRetractVotesAndUnlockStakeWhileVoting
|
||||
, creatorUnlockStakeWhileFinished
|
||||
, voterUnlockStakeAfterVoting
|
||||
mkLegalGroup nProposals =
|
||||
group
|
||||
(mkSubgroupName nProposals)
|
||||
[ UnlockStake.mkTestTree
|
||||
"voter: retract votes while voting"
|
||||
(UnlockStake.mkVoterRetractVotesWhileVotingParameters nProposals)
|
||||
True
|
||||
, UnlockStake.mkTestTree
|
||||
"voter/creator: retract votes while voting"
|
||||
(UnlockStake.mkVoterCreatorRetractVotesWhileVotingParameters nProposals)
|
||||
True
|
||||
, UnlockStake.mkTestTree
|
||||
"creator: remove creator locks when finished"
|
||||
(UnlockStake.mkCreatorRemoveCreatorLocksWhenFinishedParameters nProposals)
|
||||
True
|
||||
, 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
|
||||
let retractsVotesWhileNotVotingReady =
|
||||
group "voter retracts votes while not voting" $ do
|
||||
status <- [Draft, Locked, Finished]
|
||||
|
||||
pure $
|
||||
UnlockStake.mkProposalValidatorTestCase
|
||||
UnlockStake.UnlockStakeParameters
|
||||
{ UnlockStake.proposalCount = proposalCount
|
||||
, UnlockStake.stakeUsage = UnlockStake.Voter
|
||||
, UnlockStake.retractVotes = True
|
||||
, UnlockStake.proposalStatus = status
|
||||
}
|
||||
False
|
||||
|
||||
unlockIrrelevantStake =
|
||||
group "unlock an irrelevant stake" $ do
|
||||
status <- [Draft, VotingReady, Locked, Finished]
|
||||
shouldRetractVotes <- [True, False]
|
||||
|
||||
pure $
|
||||
UnlockStake.mkProposalValidatorTestCase
|
||||
UnlockStake.UnlockStakeParameters
|
||||
{ UnlockStake.proposalCount = proposalCount
|
||||
, UnlockStake.stakeUsage = UnlockStake.Irrelevant
|
||||
, UnlockStake.retractVotes = shouldRetractVotes
|
||||
, UnlockStake.proposalStatus = status
|
||||
}
|
||||
False
|
||||
|
||||
unlockCreatorStakeBeforeFinished =
|
||||
group "unlock creator stake before finished" $ do
|
||||
status <- [Draft, VotingReady, Locked]
|
||||
|
||||
pure $
|
||||
UnlockStake.mkProposalValidatorTestCase
|
||||
UnlockStake.UnlockStakeParameters
|
||||
{ UnlockStake.proposalCount = proposalCount
|
||||
, UnlockStake.stakeUsage = UnlockStake.Creator
|
||||
, UnlockStake.retractVotes = False
|
||||
, UnlockStake.proposalStatus = status
|
||||
}
|
||||
False
|
||||
retractVotesWithCreatorStake =
|
||||
group "creator stake retracts votes" $ do
|
||||
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
|
||||
mkIllegalGroup nProposals =
|
||||
group
|
||||
(mkSubgroupName nProposals)
|
||||
[ group "retract votes while not voting" $
|
||||
map
|
||||
( \ps ->
|
||||
let name =
|
||||
"role: " <> show ps.stakeRole
|
||||
<> ", status: "
|
||||
<> show ps.proposalStatus
|
||||
in UnlockStake.mkTestTree name ps False
|
||||
)
|
||||
(UnlockStake.mkRetractVotesWhileNotVoting nProposals)
|
||||
, group "unlock an irrelevant stake" $
|
||||
map
|
||||
( \ps ->
|
||||
let name =
|
||||
"status: " <> show ps.proposalStatus
|
||||
<> "retract votes: "
|
||||
<> show ps.retractVotes
|
||||
in UnlockStake.mkTestTree name ps False
|
||||
)
|
||||
(UnlockStake.mkUnockIrrelevantStakeParameters nProposals)
|
||||
, group "remove creator too early" $
|
||||
map
|
||||
( \ps ->
|
||||
let name =
|
||||
"status: " <> show ps.proposalStatus
|
||||
in UnlockStake.mkTestTree name ps False
|
||||
)
|
||||
(UnlockStake.mkRemoveCreatorLockBeforeFinishedParameters nProposals)
|
||||
, UnlockStake.mkTestTree
|
||||
"creator: retract votes"
|
||||
(UnlockStake.mkRetractVotesWithCreatorStakeParamaters nProposals)
|
||||
False
|
||||
, group "alter output stake datum" $
|
||||
map
|
||||
( \ps ->
|
||||
let name =
|
||||
"role: " <> show ps.stakeRole
|
||||
<> ", status: "
|
||||
<> show ps.proposalStatus
|
||||
in UnlockStake.mkTestTree name ps False
|
||||
)
|
||||
(UnlockStake.mkAlterStakeParameters nProposals)
|
||||
]
|
||||
|
||||
[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,
|
||||
effectSucceedsWith,
|
||||
effectFailsWith,
|
||||
testValidator,
|
||||
testPolicy,
|
||||
|
||||
-- * Converters
|
||||
toTestTree,
|
||||
|
|
@ -253,3 +255,37 @@ effectFailsWith ::
|
|||
ScriptContext ->
|
||||
SpecificationTree
|
||||
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 =
|
||||
AssocMap.fromList
|
||||
. sortOn fst
|
||||
. 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
|
||||
|
|
|
|||
|
|
@ -188,9 +188,9 @@ library agora-specs
|
|||
Sample.Effect.GovernorMutation
|
||||
Sample.Effect.TreasuryWithdrawal
|
||||
Sample.Governor
|
||||
Sample.Proposal
|
||||
Sample.Proposal.Advance
|
||||
Sample.Proposal.Cosign
|
||||
Sample.Proposal.Create
|
||||
Sample.Proposal.Shared
|
||||
Sample.Proposal.UnlockStake
|
||||
Sample.Proposal.Vote
|
||||
|
|
|
|||
|
|
@ -73,6 +73,9 @@ data GovernorDatum = GovernorDatum
|
|||
-- Will get copied over upon the creation of proposals.
|
||||
, createProposalTimeRangeMaxWidth :: MaxTimeRangeWidth
|
||||
-- ^ 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)
|
||||
|
||||
|
|
@ -149,6 +152,7 @@ newtype PGovernorDatum (s :: S) = PGovernorDatum
|
|||
, "nextProposalId" ':= PProposalId
|
||||
, "proposalTimings" ':= PProposalTimingConfig
|
||||
, "createProposalTimeRangeMaxWidth" ':= PMaxTimeRangeWidth
|
||||
, "maximumProposalsPerStake" ':= PInteger
|
||||
]
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,8 +46,6 @@ import Agora.Governor (
|
|||
)
|
||||
import Agora.Proposal (
|
||||
PProposalDatum (..),
|
||||
PProposalId (..),
|
||||
PResultTag,
|
||||
Proposal (..),
|
||||
ProposalStatus (Draft, Finished, Locked),
|
||||
pemptyVotesFor,
|
||||
|
|
@ -65,6 +63,7 @@ import Agora.Stake (
|
|||
PProposalLock (..),
|
||||
PStakeDatum (..),
|
||||
Stake (..),
|
||||
pnumCreatedProposals,
|
||||
)
|
||||
import Agora.Stake.Scripts (
|
||||
stakePolicy,
|
||||
|
|
@ -108,7 +107,6 @@ import Plutarch.Api.V1.ScriptContext (pfindTxInByTxOutRef, pisUTXOSpent, ptryFin
|
|||
import "liqwid-plutarch-extra" Plutarch.Api.V1.Value (psymbolValueOf)
|
||||
import Plutarch.Extra.IsData (pmatchEnumFromData)
|
||||
import Plutarch.Extra.Map (
|
||||
pkeys,
|
||||
plookup,
|
||||
plookup',
|
||||
)
|
||||
|
|
@ -300,6 +298,7 @@ governorValidator gov =
|
|||
, "nextProposalId"
|
||||
, "proposalTimings"
|
||||
, "createProposalTimeRangeMaxWidth"
|
||||
, "maximumProposalsPerStake"
|
||||
]
|
||||
oldGovernorDatum
|
||||
|
||||
|
|
@ -341,6 +340,8 @@ governorValidator gov =
|
|||
.& #proposalTimings .= oldGovernorDatumF.proposalTimings
|
||||
.& #createProposalTimeRangeMaxWidth
|
||||
.= oldGovernorDatumF.createProposalTimeRangeMaxWidth
|
||||
.& #maximumProposalsPerStake
|
||||
.= oldGovernorDatumF.maximumProposalsPerStake
|
||||
)
|
||||
pguardC "Unexpected governor state datum" $
|
||||
newGovernorDatum #== expectedNewDatum
|
||||
|
|
@ -377,6 +378,10 @@ governorValidator gov =
|
|||
stakeInputDatumF <-
|
||||
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" $
|
||||
stakeInputDatumF.stakedAmount #== (pgtValueOf # stakeInputF.value)
|
||||
|
||||
|
|
@ -479,25 +484,14 @@ governorValidator gov =
|
|||
mustBePJust # "Stake output not found" #$ ptryFindDatum # stakeOutputDatumHash # txInfoF.datums
|
||||
|
||||
-- The stake should be locked by the newly created proposal.
|
||||
|
||||
let possibleVoteResults = pkeys #$ pto $ pfromData proposalOutputDatum.votes
|
||||
|
||||
mkProposalLock :: Term _ (PProposalId :--> PAsData PResultTag :--> PAsData PProposalLock)
|
||||
mkProposalLock =
|
||||
phoistAcyclic $
|
||||
plam
|
||||
( \pid rt' ->
|
||||
pdata $
|
||||
mkRecordConstr
|
||||
PProposalLock
|
||||
( #vote .= rt' .& #proposalTag .= pdata pid
|
||||
)
|
||||
)
|
||||
let newLock =
|
||||
mkRecordConstr
|
||||
PCreated
|
||||
( #created .= oldGovernorDatumF.nextProposalId
|
||||
)
|
||||
|
||||
-- Append new locks to existing locks
|
||||
expectedProposalLocks =
|
||||
pconcat # stakeInputDatumF.lockedBy
|
||||
#$ pmap # (mkProposalLock # proposalOutputDatum.proposalId) # possibleVoteResults
|
||||
expectedProposalLocks = pcons # pdata newLock # stakeInputDatumF.lockedBy
|
||||
|
||||
expectedStakeOutputDatum =
|
||||
pdata $
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ import Plutarch.Lift (
|
|||
PUnsafeLiftDecl (..),
|
||||
)
|
||||
import Plutarch.SafeMoney (PDiscrete)
|
||||
import Plutarch.Show (PShow (..))
|
||||
import PlutusLedgerApi.V1 (DatumHash, PubKeyHash, ValidatorHash)
|
||||
import PlutusLedgerApi.V1.Value (AssetClass)
|
||||
import PlutusTx qualified
|
||||
|
|
@ -328,7 +329,7 @@ data ProposalRedeemer
|
|||
-- This list should be sorted in ascending order.
|
||||
Cosign [PubKeyHash]
|
||||
| -- | 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.
|
||||
--
|
||||
-- These are roughly the checks for each possible transition:
|
||||
|
|
@ -408,6 +409,8 @@ newtype PResultTag (s :: S) = PResultTag (Term s PInteger)
|
|||
PEq
|
||||
, -- | @since 0.1.0
|
||||
POrd
|
||||
, -- | @since 0.2.0
|
||||
PShow
|
||||
)
|
||||
via (DerivePNewtype PResultTag PInteger)
|
||||
|
||||
|
|
@ -440,6 +443,8 @@ newtype PProposalId (s :: S) = PProposalId (Term s PInteger)
|
|||
PEq
|
||||
, -- | @since 0.1.0
|
||||
POrd
|
||||
, -- | @since 0.2.0
|
||||
PShow
|
||||
)
|
||||
via (DerivePNewtype PProposalId PInteger)
|
||||
|
||||
|
|
@ -665,7 +670,7 @@ deriving via (DerivePConstantViaDataList ProposalDatum PProposalDatum) instance
|
|||
data PProposalRedeemer (s :: S)
|
||||
= PVote (Term s (PDataRecord '["resultTag" ':= PResultTag]))
|
||||
| PCosign (Term s (PDataRecord '["newCosigners" ':= PBuiltinList (PAsData PPubKeyHash)]))
|
||||
| PUnlock (Term s (PDataRecord '["resultTag" ':= PResultTag]))
|
||||
| PUnlock (Term s (PDataRecord '[]))
|
||||
| PAdvanceProposal (Term s (PDataRecord '[]))
|
||||
deriving stock
|
||||
( -- | @since 0.1.0
|
||||
|
|
|
|||
|
|
@ -29,8 +29,12 @@ import Agora.Proposal.Time (
|
|||
import Agora.Stake (
|
||||
PProposalLock (..),
|
||||
PStakeDatum (..),
|
||||
PStakeUsage (..),
|
||||
pgetStakeUsage,
|
||||
pextractVoteOption,
|
||||
pgetStakeRole,
|
||||
pisCreator,
|
||||
pisIrrelevant,
|
||||
pisPureCreator,
|
||||
pisVoter,
|
||||
)
|
||||
import Agora.Utils (
|
||||
getMintingPolicySymbol,
|
||||
|
|
@ -469,12 +473,7 @@ proposalValidator proposal =
|
|||
|
||||
-- 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" $
|
||||
pnot #$ pany
|
||||
# plam
|
||||
( \((pfield @"proposalTag" #) . pfromData -> pid) ->
|
||||
pid #== proposalF.proposalId
|
||||
)
|
||||
# pfromData stakeInF.lockedBy
|
||||
pnot #$ pisVoter #$ pgetStakeRole # proposalF.proposalId # pfromData stakeInF.lockedBy
|
||||
|
||||
let -- The amount of new votes should be the 'stakedAmount'.
|
||||
-- Update the vote counter of the proposal, and leave other stuff as is.
|
||||
|
|
@ -510,9 +509,9 @@ proposalValidator proposal =
|
|||
|
||||
let newProposalLock =
|
||||
mkRecordConstr
|
||||
PProposalLock
|
||||
( #vote .= pdata voteFor
|
||||
.& #proposalTag .= proposalF.proposalId
|
||||
PVoted
|
||||
( #votedOn .= proposalF.proposalId
|
||||
.& #votedFor .= pdata voteFor
|
||||
)
|
||||
-- Prepend the new lock to existing locks
|
||||
expectedProposalLocks =
|
||||
|
|
@ -533,30 +532,12 @@ proposalValidator proposal =
|
|||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
PUnlock r -> withSingleStake $ \stakeInF stakeOut _ -> do
|
||||
-- At draft stage, the votes should be empty.
|
||||
pguardC "Shouldn't retract votes from a draft proposal" $
|
||||
pnot #$ currentStatus #== pconstant Draft
|
||||
PUnlock _ -> withSingleStake $ \stakeInF stakeOut _ -> do
|
||||
stakeRole <- pletC $ pgetStakeRole # proposalF.proposalId # stakeInF.lockedBy
|
||||
|
||||
-- This is the vote option we're retracting from.
|
||||
retractFrom <- pletC $ pfield @"resultTag" # r
|
||||
pguardC "Stake input should be relevant" $
|
||||
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 <-
|
||||
pletC $
|
||||
pmatch stakeInF.stakedAmount $ \(PDiscrete v) -> pextract # v
|
||||
|
|
@ -564,13 +545,34 @@ proposalValidator proposal =
|
|||
-- The votes can only change when the proposal still allows voting.
|
||||
let shouldUpdateVotes =
|
||||
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" $
|
||||
pif
|
||||
shouldUpdateVotes
|
||||
( 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 =
|
||||
mkRecordConstr
|
||||
|
|
@ -598,15 +600,14 @@ proposalValidator proposal =
|
|||
PStakeDatum
|
||||
( #stakedAmount .= stakeInF.stakedAmount
|
||||
.& #owner .= stakeInF.owner
|
||||
.& #lockedBy .= stakeOutputLocks
|
||||
.& #lockedBy .= pdata stakeOutputLocks
|
||||
)
|
||||
|
||||
pguardC "Only locks updated in the output stake" $
|
||||
templateStakeOut #== stakeOut
|
||||
|
||||
pguardC "All relevant locks removed from the stake" $
|
||||
pgetStakeUsage # pfromData stakeOutputLocks
|
||||
# proposalF.proposalId #== pcon PDidNothing
|
||||
validateOutputLocks # stakeOutputLocks
|
||||
|
||||
pure $ pconstant ()
|
||||
|
||||
|
|
|
|||
|
|
@ -18,11 +18,17 @@ module Agora.Stake (
|
|||
PStakeDatum (..),
|
||||
PStakeRedeemer (..),
|
||||
PProposalLock (..),
|
||||
PStakeUsage (..),
|
||||
PStakeRole (..),
|
||||
|
||||
-- * Utility functions
|
||||
stakeLocked,
|
||||
pgetStakeUsage,
|
||||
pstakeLocked,
|
||||
pnumCreatedProposals,
|
||||
pextractVoteOption,
|
||||
pgetStakeRole,
|
||||
pisVoter,
|
||||
pisCreator,
|
||||
pisPureCreator,
|
||||
pisIrrelevant,
|
||||
) where
|
||||
|
||||
import Agora.Plutarch.Orphans ()
|
||||
|
|
@ -43,14 +49,15 @@ import Plutarch.Extra.IsData (
|
|||
DerivePConstantViaDataList (..),
|
||||
ProductIsData (ProductIsData),
|
||||
)
|
||||
import Plutarch.Extra.List (pmapMaybe, pnotNull)
|
||||
import Plutarch.Extra.List (pnotNull)
|
||||
import Plutarch.Extra.Other (DerivePNewtype' (..))
|
||||
import Plutarch.Extra.TermCont (pletFieldsC)
|
||||
import Plutarch.Lift (PConstantDecl, PUnsafeLiftDecl (..))
|
||||
import Plutarch.SafeMoney (PDiscrete)
|
||||
import Plutarch.Show (PShow (..))
|
||||
import PlutusLedgerApi.V1 (PubKeyHash)
|
||||
import PlutusLedgerApi.V1.Value (AssetClass)
|
||||
import PlutusTx qualified
|
||||
import Prelude ((+))
|
||||
import Prelude hiding (Num (..))
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
|
@ -69,8 +76,7 @@ data Stake = Stake
|
|||
GHC.Generic
|
||||
)
|
||||
|
||||
{- | A lock placed on a Stake datum in order to prevent
|
||||
depositing and withdrawing when votes are in place.
|
||||
{- | Locks that are stored in the stake datums for various purposes.
|
||||
|
||||
NOTE: Due to retracting votes always being possible,
|
||||
this lock will only lock with contention on the proposal.
|
||||
|
|
@ -97,30 +103,45 @@ data Stake = Stake
|
|||
|
||||
@since 0.1.0
|
||||
-}
|
||||
data ProposalLock = ProposalLock
|
||||
{ vote :: ResultTag
|
||||
-- ^ What was voted on. This allows retracting votes to
|
||||
-- undo their vote.
|
||||
, proposalId :: ProposalId
|
||||
-- ^ Identifies the proposal. See 'ProposalId' for further
|
||||
-- comments on its significance.
|
||||
}
|
||||
data ProposalLock
|
||||
= -- | The stake was used to create a proposal.
|
||||
--
|
||||
-- This kind of lock is placed upon the creation of a proposal, in order
|
||||
-- to limit creation of proposals per stake.
|
||||
--
|
||||
-- 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
|
||||
( -- | @since 0.1.0
|
||||
Show
|
||||
, -- | @since 0.1.0
|
||||
GHC.Generic
|
||||
)
|
||||
deriving anyclass (Generic)
|
||||
deriving
|
||||
deriving anyclass
|
||||
( -- | @since 0.1.0
|
||||
PlutusTx.ToData
|
||||
, -- | @since 0.1.0
|
||||
PlutusTx.FromData
|
||||
, -- | @since 0.1.0
|
||||
PlutusTx.UnsafeFromData
|
||||
Generic
|
||||
)
|
||||
via (ProductIsData ProposalLock)
|
||||
|
||||
PlutusTx.makeIsDataIndexed
|
||||
''ProposalLock
|
||||
[ ('Created, 0)
|
||||
, ('Voted, 1)
|
||||
]
|
||||
|
||||
{- | Haskell-level redeemer for Stake scripts.
|
||||
|
||||
|
|
@ -138,12 +159,12 @@ data StakeRedeemer
|
|||
-- 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,
|
||||
-- and then the funds are lost.
|
||||
PermitVote ProposalLock
|
||||
PermitVote
|
||||
| -- | Retract a vote, removing it from the 'lockedBy' field. See 'ProposalLock'.
|
||||
-- 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,
|
||||
-- allowing 'Stake's to be unlocked.
|
||||
RetractVotes [ProposalLock]
|
||||
RetractVotes
|
||||
| -- | The owner can consume stake if nothing is changed about it.
|
||||
-- If the proposal token moves, this is equivalent to the owner consuming it.
|
||||
WitnessStake
|
||||
|
|
@ -165,7 +186,7 @@ PlutusTx.makeIsDataIndexed
|
|||
data StakeDatum = StakeDatum
|
||||
{ stakedAmount :: Tagged GTTag Integer
|
||||
-- ^ 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
|
||||
-- ^ 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
|
||||
, lockedBy :: [ProposalLock]
|
||||
-- ^ 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 anyclass (Generic)
|
||||
|
|
@ -227,13 +248,20 @@ newtype PStakeDatum (s :: S) = PStakeDatum
|
|||
via (DerivePNewtype' PStakeDatum)
|
||||
|
||||
-- | @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
|
||||
deriving via (DerivePConstantViaDataList StakeDatum PStakeDatum) instance (Plutarch.Lift.PConstantDecl StakeDatum)
|
||||
deriving via
|
||||
(DerivePConstantViaDataList StakeDatum PStakeDatum)
|
||||
instance
|
||||
(Plutarch.Lift.PConstantDecl StakeDatum)
|
||||
|
||||
-- | @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.
|
||||
|
||||
|
|
@ -244,8 +272,8 @@ data PStakeRedeemer (s :: S)
|
|||
PDepositWithdraw (Term s (PDataRecord '["delta" ':= PDiscrete GTTag]))
|
||||
| -- | Destroy a stake, retrieving its LQ, the minimum ADA and any other assets.
|
||||
PDestroy (Term s (PDataRecord '[]))
|
||||
| PPermitVote (Term s (PDataRecord '["lock" ':= PProposalLock]))
|
||||
| PRetractVotes (Term s (PDataRecord '["locks" ':= PBuiltinList (PAsData PProposalLock)]))
|
||||
| PPermitVote (Term s (PDataRecord '[]))
|
||||
| PRetractVotes (Term s (PDataRecord '[]))
|
||||
| PWitnessStake (Term s (PDataRecord '[]))
|
||||
deriving stock
|
||||
( -- | @since 0.1.0
|
||||
|
|
@ -267,65 +295,37 @@ data PStakeRedeemer (s :: S)
|
|||
)
|
||||
via PIsDataReprInstances PStakeRedeemer
|
||||
|
||||
-- | @since 0.1.0
|
||||
deriving via
|
||||
PAsData (PIsDataReprInstances PStakeRedeemer)
|
||||
instance
|
||||
PTryFrom PData (PAsData PStakeRedeemer)
|
||||
|
||||
instance Plutarch.Lift.PUnsafeLiftDecl PStakeRedeemer where type PLifted PStakeRedeemer = StakeRedeemer
|
||||
deriving via (DerivePConstantViaData StakeRedeemer PStakeRedeemer) instance (Plutarch.Lift.PConstantDecl StakeRedeemer)
|
||||
-- | @since 0.1.0
|
||||
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'.
|
||||
|
||||
@since 0.1.0
|
||||
@since 0.2.0
|
||||
-}
|
||||
newtype PProposalLock (s :: S) = PProposalLock
|
||||
{ getProposalLock ::
|
||||
Term
|
||||
s
|
||||
( PDataRecord
|
||||
'[ "vote" ':= PResultTag
|
||||
, "proposalTag" ':= PProposalId
|
||||
]
|
||||
)
|
||||
}
|
||||
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
|
||||
data PProposalLock (s :: S)
|
||||
= PCreated (Term s (PDataRecord '["created" ':= PProposalId]))
|
||||
| PVoted
|
||||
( Term
|
||||
s
|
||||
( PDataRecord
|
||||
'[ "votedOn" ':= PProposalId
|
||||
, "votedFor" ':= PResultTag
|
||||
]
|
||||
)
|
||||
)
|
||||
deriving stock
|
||||
( -- | @since 0.1.0
|
||||
GHC.Generic
|
||||
|
|
@ -334,51 +334,212 @@ data PStakeUsage (s :: S)
|
|||
( -- | @since 0.1.0
|
||||
Generic
|
||||
, -- | @since 0.1.0
|
||||
HasDatatypeInfo
|
||||
)
|
||||
deriving anyclass
|
||||
( -- | @since 0.1.0
|
||||
PIsDataRepr
|
||||
)
|
||||
deriving
|
||||
( -- | @since 0.1.0
|
||||
PlutusType
|
||||
, -- | @since 0.1.0
|
||||
HasDatatypeInfo
|
||||
PIsData
|
||||
, -- | @since 0.1.0
|
||||
PEq
|
||||
)
|
||||
via (PIsDataReprInstances PProposalLock)
|
||||
|
||||
{- | / O(n) /.Return the usage of a stake on a particular proposal,
|
||||
given the 'lockedBy' field of a stake and the target proposal.
|
||||
-- | @since 0.1.0
|
||||
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 ::
|
||||
Term
|
||||
_
|
||||
( PBuiltinList (PAsData PProposalLock)
|
||||
:--> PProposalId
|
||||
:--> PStakeUsage
|
||||
pstakeLocked :: forall (s :: S). Term s (PStakeDatum :--> PBool)
|
||||
pstakeLocked = phoistAcyclic $
|
||||
plam $ \stakeDatum ->
|
||||
let locks :: Term _ (PBuiltinList (PAsData PProposalLock))
|
||||
locks = pfield @"lockedBy" # stakeDatum
|
||||
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
|
||||
(lockF.proposalTag #== pid)
|
||||
(pcon $ PJust lock')
|
||||
(pcon PNothing)
|
||||
)
|
||||
# locks
|
||||
(pid' #== pid)
|
||||
(pcon PCreator)
|
||||
(pcon PIrrelevant)
|
||||
PVoted lock' -> pletFields @'["votedOn", "votedFor"] lock' $ \lockF ->
|
||||
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
|
||||
in plet lockCount' $ \lockCount ->
|
||||
pif (lockCount #== 0) (pcon PDidNothing) $
|
||||
pif
|
||||
(lockCount #== 1)
|
||||
( pcon $
|
||||
PVotedFor $
|
||||
pfromData $
|
||||
pfield @"vote" #$ phead # filteredLocks
|
||||
)
|
||||
-- Note: see the implementation of the governor.
|
||||
(pcon PCreated)
|
||||
{- | Get the outcome that was voted for.
|
||||
|
||||
@since 0.2.0
|
||||
-}
|
||||
pextractVoteOption :: Term s (PStakeRole :--> PResultTag)
|
||||
pextractVoteOption = phoistAcyclic $
|
||||
plam $ \sr -> pmatch sr $ \case
|
||||
PVoter r -> r
|
||||
PBoth r -> r
|
||||
_ -> ptraceError "not voter"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import Agora.Stake (
|
|||
),
|
||||
Stake (gtClassRef, proposalSTClass),
|
||||
StakeRedeemer (WitnessStake),
|
||||
stakeLocked,
|
||||
pstakeLocked,
|
||||
)
|
||||
import Agora.Utils (
|
||||
mustBePJust,
|
||||
|
|
@ -109,7 +109,7 @@ stakePolicy gtClassRef =
|
|||
pif
|
||||
(psymbolValueOf # ownSymbol # txOutF.value #== 1)
|
||||
( let datum = mustFindDatum' @PStakeDatum # txOutF.datumHash # txInfoF.datums
|
||||
in pnot # (stakeLocked # datum)
|
||||
in pnot # (pstakeLocked # datum)
|
||||
)
|
||||
(pconstant False)
|
||||
)
|
||||
|
|
@ -263,7 +263,7 @@ stakeValidator stake =
|
|||
spentST <- pletC $ psymbolValueOf # stCurrencySymbol #$ valueSpent
|
||||
|
||||
-- Is the stake currently locked?
|
||||
stakeIsLocked <- pletC $ stakeLocked # stakeDatum'
|
||||
stakeIsLocked <- pletC $ pstakeLocked # stakeDatum'
|
||||
|
||||
pure $
|
||||
pmatch stakeRedeemer $ \case
|
||||
|
|
@ -287,7 +287,7 @@ stakeValidator stake =
|
|||
proposalSTClass = passetClass # pconstant propCs # pconstant propTn
|
||||
spentProposalST = passetClassValueOf # valueSpent # proposalSTClass
|
||||
|
||||
proposalTokenMoved <- pletC $ spentProposalST #== 1
|
||||
proposalTokenMoved <- pletC $ 1 #<= spentProposalST
|
||||
|
||||
-- Filter out own outputs using own address and ST.
|
||||
ownOutputs <-
|
||||
|
|
@ -371,9 +371,20 @@ stakeValidator stake =
|
|||
pletC $
|
||||
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 $
|
||||
pmatch stakeRedeemer $ \case
|
||||
PRetractVotes l -> unTermCont $ do
|
||||
PRetractVotes _ -> unTermCont $ do
|
||||
pguardC
|
||||
"Owner signs this transaction"
|
||||
ownerSignsTransaction
|
||||
|
|
@ -383,18 +394,8 @@ stakeValidator stake =
|
|||
pguardC "Proposal ST spent" proposalTokenMoved
|
||||
|
||||
pguardC "A UTXO must exist with the correct output" $
|
||||
let expectedLocks = pfield @"locks" # l
|
||||
|
||||
expectedDatum =
|
||||
mkRecordConstr
|
||||
PStakeDatum
|
||||
( #stakedAmount .= stakeDatum.stakedAmount
|
||||
.& #owner .= stakeDatum.owner
|
||||
.& #lockedBy .= expectedLocks
|
||||
)
|
||||
|
||||
valueCorrect = ownOutputValueUnchanged
|
||||
outputDatumCorrect = stakeOut #== expectedDatum
|
||||
let valueCorrect = ownOutputValueUnchanged
|
||||
outputDatumCorrect = onlyLocksUpdated
|
||||
in foldl1
|
||||
(#&&)
|
||||
[ ptraceIfFalse "valueCorrect" valueCorrect
|
||||
|
|
@ -405,34 +406,21 @@ stakeValidator stake =
|
|||
|
||||
------------------------------------------------------------
|
||||
|
||||
PPermitVote l -> unTermCont $ do
|
||||
PPermitVote _ -> unTermCont $ do
|
||||
pguardC
|
||||
"Owner signs this transaction"
|
||||
ownerSignsTransaction
|
||||
|
||||
let proposalTokenMinted =
|
||||
passetClassValueOf # txInfoF.mint # proposalSTClass #== 1
|
||||
|
||||
-- This puts trust into the Proposal. The Proposal must necessarily check
|
||||
-- that this is not abused.
|
||||
pguardC "Proposal ST spent" proposalTokenMoved
|
||||
|
||||
-- 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 "Proposal ST spent or minted" $
|
||||
proposalTokenMoved #|| proposalTokenMinted
|
||||
|
||||
pguardC "A UTXO must exist with the correct output" $
|
||||
let correctOutputDatum = stakeOut #== expectedDatum
|
||||
let correctOutputDatum = onlyLocksUpdated
|
||||
valueCorrect = ownOutputValueUnchanged
|
||||
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 with multiple treasuries ,492387542,1197315,3986
|
||||
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/effect validator should pass,106082031,292993,3609
|
||||
Agora/Stake/policy/stakeCreation,50939580,148729,2387
|
||||
Agora/Stake/validator/stakeDepositWithdraw deposit,150745141,416137,4995
|
||||
Agora/Stake/validator/stakeDepositWithdraw withdraw,150745141,416137,4983
|
||||
Agora/Proposal/policy/proposalCreation,23140177,69194,1515
|
||||
Agora/Proposal/validator/cosignature/legal/with 1 cosigners/propsoal,235408912,657765,8097
|
||||
Agora/Proposal/validator/cosignature/legal/with 1 cosigners/stake,125665131,316762,5462
|
||||
Agora/Proposal/validator/cosignature/legal/with 5 cosigners/propsoal,680441047,1897008,10727
|
||||
Agora/Proposal/validator/cosignature/legal/with 5 cosigners/stake,576106975,1490610,7972
|
||||
Agora/Proposal/validator/cosignature/legal/with 10 cosigners/propsoal,1351073436,3706315,14015
|
||||
Agora/Proposal/validator/cosignature/legal/with 10 cosigners/stake,1148637636,2982695,11109
|
||||
Agora/Proposal/validator/cosignature/illegal/duplicate cosigners/stake,125665131,316762,5462
|
||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 1 cosigners/status: VotingReady/stake,125665131,316762,5462
|
||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 1 cosigners/status: Locked/stake,125665131,316762,5462
|
||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 1 cosigners/status: Finished/stake,125665131,316762,5462
|
||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 5 cosigners/status: VotingReady/stake,576106975,1490610,7972
|
||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 5 cosigners/status: Locked/stake,576106975,1490610,7972
|
||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 5 cosigners/status: Finished/stake,576106975,1490610,7972
|
||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 10 cosigners/status: VotingReady/stake,1148637636,2982695,11109
|
||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 10 cosigners/status: Locked/stake,1148637636,2982695,11109
|
||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 10 cosigners/status: Finished/stake,1148637636,2982695,11109
|
||||
Agora/Proposal/validator/voting/legal/propsoal,246896882,688919,8069
|
||||
Agora/Proposal/validator/voting/legal/stake,141234659,368136,5481
|
||||
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/legal/to next state/propsoal,222376736,631090,8052
|
||||
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/legal/to next state/stake,125665131,316762,5459
|
||||
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/legal/to failed state/propsoal,217322044,620369,8054
|
||||
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/legal/to failed state/stake,125665131,316762,5461
|
||||
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/illegal/insufficient cosigns/stake,118020743,304972,5389
|
||||
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/legal/to next state/propsoal,614587307,1766683,10875
|
||||
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/legal/to next state/stake,548619697,1410656,8162
|
||||
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/legal/to failed state/propsoal,239691392,682433,8415
|
||||
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/legal/to failed state/stake,125665131,316762,5702
|
||||
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/illegal/insufficient cosigns/stake,446252055,1166812,7811
|
||||
Agora/Proposal/validator/advancing/from draft/with 10 cosigner(s)/legal/to next state/propsoal,1196289192,3454898,14404
|
||||
Agora/Proposal/validator/advancing/from draft/with 10 cosigner(s)/legal/to next state/stake,1201388310,3136405,11540
|
||||
Agora/Proposal/validator/advancing/from draft/with 10 cosigner(s)/legal/to failed state/propsoal,267653077,760013,8868
|
||||
Agora/Proposal/validator/advancing/from draft/with 10 cosigner(s)/legal/to failed state/stake,125665131,316762,6004
|
||||
Agora/Proposal/validator/advancing/from draft/with 10 cosigner(s)/illegal/insufficient cosigns/stake,1025012867,2732083,10837
|
||||
Agora/Proposal/validator/advancing/legal/advance to next state/from: VotingReady/propsoal,250229153,709227,8061
|
||||
Agora/Proposal/validator/advancing/legal/advance to next state/from: VotingReady/stake,125665131,316762,5466
|
||||
Agora/Proposal/validator/advancing/legal/advance to next state/from: Locked/propsoal,238990044,676396,8061
|
||||
Agora/Proposal/validator/advancing/legal/advance to next state/from: Locked/stake,125665131,316762,5466
|
||||
Agora/Proposal/validator/advancing/legal/advance to failed state/from: VotingReady/propsoal,236162599,670386,8055
|
||||
Agora/Proposal/validator/advancing/legal/advance to failed state/from: VotingReady/stake,125665131,316762,5462
|
||||
Agora/Proposal/validator/advancing/legal/advance to failed state/from: Locked/propsoal,237293577,672790,8055
|
||||
Agora/Proposal/validator/advancing/legal/advance to failed state/from: Locked/stake,125665131,316762,5462
|
||||
Agora/Proposal/validator/advancing/illegal/insufficient votes/stake,125665131,316762,5462
|
||||
Agora/Proposal/validator/advancing/illegal/initial state is Finished/stake,125665131,316762,5454
|
||||
"Agora/Proposal/validator/unlocking/legal/1 proposals, voter, unlock stake + retract votes, VotingReady",245855872,689807,8029
|
||||
"Agora/Proposal/validator/unlocking/legal/1 proposals, creator, unlock stake, Finished",215131610,613807,8031
|
||||
"Agora/Proposal/validator/unlocking/legal/voter unlocks stake after voting/1 proposals, voter, unlock stake, Finished",212428891,605718,8033
|
||||
"Agora/Proposal/validator/unlocking/legal/voter unlocks stake after voting/1 proposals, voter, unlock stake, Locked",212428891,605718,8033
|
||||
"Agora/Proposal/validator/unlocking/legal/42 proposals, voter, unlock stake + retract votes, VotingReady",1775520444,5200586,29137
|
||||
"Agora/Proposal/validator/unlocking/legal/42 proposals, creator, unlock stake, Finished",1448162043,4319059,29321
|
||||
"Agora/Proposal/validator/unlocking/legal/voter unlocks stake after voting/42 proposals, voter, unlock stake, Finished",1340521669,3979526,29305
|
||||
"Agora/Proposal/validator/unlocking/legal/voter unlocks stake after voting/42 proposals, voter, unlock stake, Locked",1340521669,3979526,29305
|
||||
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,107090537,296185,3627
|
||||
Agora/Stake/policy/stakeCreation,51008580,149029,2522
|
||||
Agora/Stake/validator/stakeDepositWithdraw deposit,183506412,498838,4745
|
||||
Agora/Stake/validator/stakeDepositWithdraw withdraw,183506412,498838,4733
|
||||
Agora/Proposal/policy (proposal creation)/legal/proposal,34975627,103548,2117
|
||||
Agora/Proposal/policy (proposal creation)/legal/governor,327971301,871386,9370
|
||||
Agora/Proposal/policy (proposal creation)/legal/stake,152415805,398403,5404
|
||||
Agora/Proposal/policy (proposal creation)/illegal/invalid next proposal id/proposal,34975627,103548,2117
|
||||
Agora/Proposal/policy (proposal creation)/illegal/invalid next proposal id/stake,152415805,398403,5404
|
||||
Agora/Proposal/policy (proposal creation)/illegal/use other's stake/proposal,34975627,103548,2086
|
||||
Agora/Proposal/policy (proposal creation)/illegal/altered stake/proposal,34975627,103548,2117
|
||||
Agora/Proposal/policy (proposal creation)/illegal/invalid stake locks/proposal,34975627,103548,2125
|
||||
Agora/Proposal/policy (proposal creation)/illegal/invalid stake locks/stake,157849465,413053,5412
|
||||
Agora/Proposal/policy (proposal creation)/illegal/has reached maximum proposals limit/proposal,34975627,103548,2137
|
||||
Agora/Proposal/policy (proposal creation)/illegal/has reached maximum proposals limit/stake,158878297,416511,5434
|
||||
Agora/Proposal/policy (proposal creation)/illegal/loose time range/proposal,34975627,103548,2117
|
||||
Agora/Proposal/policy (proposal creation)/illegal/loose time range/stake,152415805,398403,5404
|
||||
Agora/Proposal/policy (proposal creation)/illegal/open time range/proposal,34975627,103548,2113
|
||||
Agora/Proposal/policy (proposal creation)/illegal/open time range/stake,152415805,398403,5400
|
||||
Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/VotingReady/proposal,34975627,103548,2117
|
||||
Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/VotingReady/stake,152415805,398403,5404
|
||||
Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Locked/proposal,34975627,103548,2117
|
||||
Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Locked/stake,152415805,398403,5404
|
||||
Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Finished/proposal,34975627,103548,2117
|
||||
Agora/Proposal/policy (proposal creation)/illegal/invalid proposal status/Finished/stake,152415805,398403,5404
|
||||
Agora/Proposal/validator/cosignature/legal/with 1 cosigners/propsoal,232230876,649441,8097
|
||||
Agora/Proposal/validator/cosignature/legal/with 1 cosigners/stake,122255811,317464,5213
|
||||
Agora/Proposal/validator/cosignature/legal/with 5 cosigners/propsoal,662894867,1848188,10727
|
||||
Agora/Proposal/validator/cosignature/legal/with 5 cosigners/stake,553050999,1459872,7723
|
||||
Agora/Proposal/validator/cosignature/legal/with 10 cosigners/propsoal,1315567076,3606875,14015
|
||||
Agora/Proposal/validator/cosignature/legal/with 10 cosigners/stake,1101023340,2912657,10860
|
||||
Agora/Proposal/validator/cosignature/illegal/duplicate cosigners/stake,122255811,317464,5213
|
||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 1 cosigners/status: VotingReady/stake,122255811,317464,5213
|
||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 1 cosigners/status: Locked/stake,122255811,317464,5213
|
||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 1 cosigners/status: Finished/stake,122255811,317464,5213
|
||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 5 cosigners/status: VotingReady/stake,553050999,1459872,7723
|
||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 5 cosigners/status: Locked/stake,553050999,1459872,7723
|
||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 5 cosigners/status: Finished/stake,553050999,1459872,7723
|
||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 10 cosigners/status: VotingReady/stake,1101023340,2912657,10860
|
||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 10 cosigners/status: Locked/stake,1101023340,2912657,10860
|
||||
Agora/Proposal/validator/cosignature/illegal/proposal status not Draft/with 10 cosigners/status: Finished/stake,1101023340,2912657,10860
|
||||
Agora/Proposal/validator/voting/legal/propsoal,253541830,711367,8079
|
||||
Agora/Proposal/validator/voting/legal/stake,139493011,366217,5239
|
||||
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/legal/to next state/propsoal,222392288,630302,8060
|
||||
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/legal/to next state/stake,122255811,317464,5222
|
||||
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/legal/to failed state/propsoal,217337596,619581,8062
|
||||
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/legal/to failed state/stake,122255811,317464,5224
|
||||
Agora/Proposal/validator/advancing/from draft/with 1 cosigner(s)/illegal/insufficient cosigns/stake,117978393,308804,5152
|
||||
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/legal/to next state/propsoal,602596705,1722841,10915
|
||||
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/legal/to next state/stake,499559031,1304096,7957
|
||||
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/legal/to failed state/propsoal,239706944,681645,8423
|
||||
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/legal/to failed state/stake,122255811,317464,5465
|
||||
Agora/Proposal/validator/advancing/from draft/with 5 cosigner(s)/illegal/insufficient cosigns/stake,446950999,1172710,7606
|
||||
Agora/Proposal/validator/advancing/from draft/with 10 cosigner(s)/legal/to next state/propsoal,1183788938,3402248,14484
|
||||
Agora/Proposal/validator/advancing/from draft/with 10 cosigner(s)/legal/to next state/stake,1129251912,2994677,11375
|
||||
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 many inputs,33204186,88241,900
|
||||
Agora/Treasury/Validator/Positive/Allows for effect changes,31556709,81546,1452
|
||||
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct simple,21017788,55883,806
|
||||
Agora/AuthorityToken/singleAuthorityTokenBurned/Correct many inputs,33204186,88241,900
|
||||
Agora/Governor/policy/GST minting,51007235,144191,2034
|
||||
Agora/Governor/validator/proposal creation,309689999,834675,9064
|
||||
Agora/Governor/validator/GATs minting,418560845,1137908,9187
|
||||
Agora/Governor/validator/mutate governor state,88986020,248491,8662
|
||||
Agora/Governor/policy/GST minting,51480023,145787,2048
|
||||
Agora/Governor/validator/proposal creation,303114849,813451,9390
|
||||
Agora/Governor/validator/GATs minting,422654153,1147158,9516
|
||||
Agora/Governor/validator/mutate governor state,90087778,252215,8991
|
||||
|
|
|
|||
|
Loading…
Add table
Add a link
Reference in a new issue