agora/agora-specs/Sample/Proposal/Advance.hs
2022-08-12 04:56:19 +08:00

1089 lines
31 KiB
Haskell

{- |
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 (
-- * Parameters
ParameterBundle (..),
GovernorParameters (..),
AuthorityTokenParameters (..),
ProposalParameters (..),
StakeParameters (..),
Winner (..),
-- * Testing Utilities
Validity (..),
advance,
mkTestTree,
mkTestTree',
-- * Parameter Bundles
mkValidToNextStateBundle,
mkValidToNextStateBundles,
mkValidToFailedStateBundles,
mkInsufficientVotesBundle,
mkAmbiguousWinnerBundle,
mkFromFinishedBundles,
mkInsufficientCosignsBundle,
mkToNextStateTooLateBundles,
mkInvalidOutputStakeBundles,
mkMintGATsForWrongEffectsBundle,
mkNoGATMintedBundle,
mkGATsWithWrongDatumBundle,
mkMintGATsWithoutTagBundle,
mkBadGovernorOutputDatumBundle,
) where
import Agora.AuthorityToken (
AuthorityToken (AuthorityToken),
authorityTokenPolicy,
)
import Agora.Governor (
GovernorDatum (..),
GovernorRedeemer (MintGATs),
)
import Agora.Governor.Scripts (governorValidator)
import Agora.Proposal (
ProposalDatum (..),
ProposalId (ProposalId),
ProposalRedeemer (AdvanceProposal),
ProposalStatus (..),
ProposalThresholds (..),
ProposalVotes (ProposalVotes),
ResultTag (ResultTag),
emptyVotesFor,
)
import Agora.Proposal.Scripts (proposalValidator)
import Agora.Proposal.Time (
ProposalStartingTime (ProposalStartingTime),
ProposalTimingConfig (
draftTime,
executingTime,
lockingTime,
votingTime
),
)
import Agora.Stake (
Stake (gtClassRef),
StakeDatum (..),
StakeRedeemer (WitnessStake),
)
import Agora.Stake.Scripts (stakeValidator)
import Agora.Utils (validatorHashToTokenName)
import Control.Monad.State (execState, modify, when)
import Data.Default (def)
import Data.List (sort)
import Data.Maybe (catMaybes, fromJust)
import Data.Tagged (Tagged (..), untag)
import Plutarch.Context (
input,
mint,
output,
script,
signedWith,
timeRange,
withDatum,
withOutRef,
withValue,
)
import Plutarch.Lift (PLifted, PUnsafeLiftDecl)
import PlutusLedgerApi.V1 (
DatumHash,
POSIXTime,
POSIXTimeRange,
PubKeyHash,
TxOutRef (TxOutRef),
ValidatorHash,
)
import PlutusLedgerApi.V1.Value (AssetClass (..))
import PlutusLedgerApi.V1.Value qualified as Value
import PlutusTx.AssocMap qualified as AssocMap
import Sample.Proposal.Shared (
governorTxRef,
proposalTxRef,
stakeTxRef,
)
import Sample.Shared (
authorityTokenSymbol,
govAssetClass,
govValidatorHash,
minAda,
proposalPolicySymbol,
proposalValidatorHash,
signer,
stake,
stakeAssetClass,
stakeValidatorHash,
)
import Sample.Shared qualified as Shared
import Test.Specification (
SpecificationTree,
group,
testPolicy,
testValidator,
)
import Test.Util (
CombinableBuilder,
closedBoundedInterval,
datumHash,
groupsOfN,
mkMinting,
mkSpending,
pubKeyHashes,
sortValue,
toDatum,
updateMap,
validatorHashes,
)
{- | A bunch of parameters that control the generation of the transaction
context.
-}
data ParameterBundle = ParameterBundle
{ proposalParameters :: ProposalParameters
-- ^ Parameters related to the the advancing proposal.
, stakeParameters :: StakeParameters
-- ^ Parameters related to stakes.
, governorParameters :: Maybe GovernorParameters
-- ^ Parameters related to GST moving. If set to 'Nothing', the GST won't
-- be moved, thus the governor validator won't be run in 'mkTestTree'.
, authorityTokenParameters :: Maybe AuthorityTokenParameters
-- ^ Parameters related to GAT minting. If set to 'Nothing', no GAT will
-- be minted, thus the GAT minting policy won't be run in 'mkTestTree'.
, transactionTimeRange :: POSIXTimeRange
-- ^ The value of 'TxInfo.txInfoValidRange', valid range of the generated
-- transaction.
, extraSignature :: Maybe PubKeyHash
-- ^ An extra signator. Intended to be used when
-- 'StakeParametersstakeParameters.transactionSignedByOwners' is set to
-- false.
}
-- | Everything about the generated governor stuff.
newtype GovernorParameters = GovernorParameters
{ invalidGovernorOutputDatum :: Bool
-- ^ The output governor datum will be changed.
}
-- | Everything about the generated authority token stuff.
data AuthorityTokenParameters = forall
(datum :: Type)
(pdatum :: S -> Type).
( PUnsafeLiftDecl pdatum
, PLifted pdatum ~ datum
, PIsData pdatum
) =>
AuthorityTokenParameters
{ mintGATsFor :: [ValidatorHash]
-- ^ GATs will be minted and sent to the given group of effects.
, carryDatum :: Maybe datum
-- ^ The datum that GAT UTxOs will be carrying.
, invalidTokenName :: Bool
-- ^ If set to true, GATs won't be tagged by their corresponding effect
-- hashes.
}
-- | Represent the winning effect group(s).
data Winner
= -- | Only one effect at the given index has the highest votes.
EffectAt Index
| -- | All the effects have the same highest votes.
All
-- | Everything about the generated proposal stuff.
data ProposalParameters = ProposalParameters
{ fromStatus :: ProposalStatus
-- ^ What status is the proposal advancing from
, toStatus :: ProposalStatus
-- ^ What status is the proposal advancing to
, effectList :: [AssocMap.Map ValidatorHash DatumHash]
-- ^ The effect groups of the proposal. A neutral effect group is not
-- required here.
, winnerAndVotes :: Maybe (Winner, Integer)
-- ^ Specify the effect group(s) that have the highest votes, and the value
-- of the highest votes.
, numCosigners :: NumStake
-- ^ The number of cosigners.
, invalidProposalOutputDatum :: Bool
-- ^ Whether to make the proposal output datum invalid or not.
}
-- | Everything about the generated stake stuff.
data StakeParameters = StakeParameters
{ numStake :: NumStake
, perStakeGTs :: Integer
, transactionSignedByOwners :: Bool
, invalidStakeOutputDatum :: Bool
}
-- | Represent the number of stakes or the number of the cosigners.
type NumStake = Int
-- | Represent an index.
type Index = Int
{- | The validity of the generated transacrion for variuos componets.
'True' means valid, 'False' means invalid.
-}
data Validity = Validity
{ forProposalValidator :: Bool
, forStakeValidator :: Bool
, forGovernorValidator :: Maybe Bool
, forAuthorityTokenPolicy :: Maybe Bool
}
--------------------------------------------------------------------------------
-- * Proposal
-- | Mock cosigners.
mkCosigners :: NumStake -> [PubKeyHash]
mkCosigners = sort . flip take pubKeyHashes
-- | Allocate the result tag for the effect at the given index.
outcomeIdxToResultTag :: Index -> ResultTag
outcomeIdxToResultTag = ResultTag . fromIntegral
-- | Add a neutral effect group and allocate result tags for the effect groups.
mkEffects ::
ProposalParameters ->
AssocMap.Map ResultTag (AssocMap.Map ValidatorHash DatumHash)
mkEffects ps =
let resultTags = map ResultTag [0 ..]
neutralEffect = AssocMap.empty
finalEffects = ps.effectList <> [neutralEffect]
in AssocMap.fromList $ zip resultTags finalEffects
-- | Set the votes of the winning group(s).
setWinner :: (Winner, Integer) -> ProposalVotes -> ProposalVotes
setWinner (All, votes) (ProposalVotes m) =
ProposalVotes $ AssocMap.mapMaybe (const $ Just votes) m
setWinner (EffectAt winnerIdx, votes) (ProposalVotes m) =
let winnerResultTag = outcomeIdxToResultTag winnerIdx
in ProposalVotes $ updateMap (const $ Just votes) winnerResultTag m
-- | Mock votes for the proposal, given the parameters.
mkVotes ::
ProposalParameters ->
ProposalVotes
mkVotes ps =
let effects = mkEffects ps
emptyVotes = emptyVotesFor effects
in maybe emptyVotes (`setWinner` emptyVotes) (ps.winnerAndVotes)
-- | The starting time of every generated proposal.
proposalStartingTime :: POSIXTime
proposalStartingTime = 0
-- | Create the input proposal datum given the parameters.
mkProposalInputDatum :: ProposalParameters -> ProposalDatum
mkProposalInputDatum ps =
let effects = mkEffects ps
votes = mkVotes ps
st = ProposalStartingTime proposalStartingTime
in ProposalDatum
{ proposalId = ProposalId 0
, effects = effects
, status = ps.fromStatus
, cosigners = mkCosigners ps.numCosigners
, thresholds = def
, votes = votes
, timingConfig = def
, startingTime = st
}
-- | Create the output proposal datum given the parameters.
mkProposalOutputDatum :: ProposalParameters -> ProposalDatum
mkProposalOutputDatum ps =
let inputDatum = mkProposalInputDatum ps
outputCosigners =
if ps.invalidProposalOutputDatum
then []
else inputDatum.cosigners
in inputDatum
{ status = ps.toStatus
, cosigners = outputCosigners
}
-- | Reference to the proposal UTXO.
proposalRef :: TxOutRef
proposalRef = TxOutRef proposalTxRef 1
{- | Create a context builder that contains all the information about the
input/output of the proposal validator, given the paramters.
-}
mkProposalBuilder :: forall b. CombinableBuilder b => ProposalParameters -> b
mkProposalBuilder ps =
let pst = Value.singleton proposalPolicySymbol "" 1
value = sortValue $ minAda <> pst
in mconcat
[ input $
mconcat
[ script proposalValidatorHash
, withOutRef proposalRef
, withDatum (mkProposalInputDatum ps)
, withValue value
]
, output $
mconcat
[ script proposalValidatorHash
, withDatum (mkProposalOutputDatum ps)
, withValue value
]
]
{- | The proposal redeemer used to spend the proposal UTXO, which is always
'AdvanceProposal' in this case.
-}
proposalRedeemer :: ProposalRedeemer
proposalRedeemer = AdvanceProposal
--------------------------------------------------------------------------------
-- * Stake
-- Mock owners of the stakes.
mkStakeOwners :: NumStake -> [PubKeyHash]
mkStakeOwners = mkCosigners
-- | Create the input stake datums given the parameters.
mkStakeInputDatums :: StakeParameters -> [StakeDatum]
mkStakeInputDatums ps =
let template =
StakeDatum
{ stakedAmount = Tagged ps.perStakeGTs
, owner = ""
, delegatedTo = Nothing
, lockedBy = []
}
in (\owner -> template {owner = owner})
<$> mkStakeOwners ps.numStake
-- | Create the output stake datums given the parameters.
mkStakeOutputDatums :: StakeParameters -> [StakeDatum]
mkStakeOutputDatums ps =
let inputDatums = mkStakeInputDatums ps
outputStakedAmount =
Tagged $
if ps.invalidStakeOutputDatum
then ps.perStakeGTs * 10
else ps.perStakeGTs
modify inp = inp {stakedAmount = outputStakedAmount}
in modify <$> inputDatums
{- | Get the input stake datum given the index. The range of the index is
@[0, 'StakeParameters.numStake - 1']@
-}
getStakeInputDatumAt :: StakeParameters -> Index -> StakeDatum
getStakeInputDatumAt ps = (!!) (mkStakeInputDatums ps)
-- | Create the reference to a particular stake UTXO.
mkStakeRef :: Index -> TxOutRef
mkStakeRef = TxOutRef stakeTxRef . (+ 3) . fromIntegral
{- | Create a context builder that contains all the inputs/outputs of the
stake validator.
-}
mkStakeBuilder :: forall b. CombinableBuilder b => StakeParameters -> b
mkStakeBuilder ps =
let perStakeValue =
sortValue $
minAda
<> Value.assetClassValue stakeAssetClass 1
<> Value.assetClassValue
(untag stake.gtClassRef)
ps.perStakeGTs
perStake idx i o =
let withSig =
if ps.transactionSignedByOwners
then signedWith i.owner
else mempty
in mconcat
[ withSig
, input $
mconcat
[ script stakeValidatorHash
, withOutRef (mkStakeRef idx)
, withValue perStakeValue
, withDatum i
]
, output $
mconcat
[ script stakeValidatorHash
, withValue perStakeValue
, withDatum o
]
]
in mconcat $
zipWith3
perStake
[0 :: Index ..]
(mkStakeInputDatums ps)
(mkStakeOutputDatums ps)
{- | The proposal redeemer used to spend the stake UTXO, which is always
'WitnessStake' in this case.
-}
stakeRedeemer :: StakeRedeemer
stakeRedeemer = WitnessStake
--------------------------------------------------------------------------------
-- * Governor
-- | The input governor datum.
governorInputDatum :: GovernorDatum
governorInputDatum =
GovernorDatum
{ proposalThresholds = def
, nextProposalId = ProposalId 42
, proposalTimings = def
, createProposalTimeRangeMaxWidth = def
, maximumProposalsPerStake = 3
}
-- | Create the output governor datum given the parameters.
mkGovernorOutputDatum :: GovernorParameters -> GovernorDatum
mkGovernorOutputDatum ps =
if ps.invalidGovernorOutputDatum
then governorInputDatum {maximumProposalsPerStake = 15}
else governorInputDatum
-- | Reference to the governor UTXO.
governorRef :: TxOutRef
governorRef = TxOutRef governorTxRef 2
{- | Create a context builder that contains the input and the output of the
governor validator.
-}
mkGovernorBuilder :: forall b. CombinableBuilder b => GovernorParameters -> b
mkGovernorBuilder ps =
let gst = Value.assetClassValue govAssetClass 1
value = sortValue $ gst <> minAda
in mconcat
[ input $
mconcat
[ script govValidatorHash
, withValue value
, withOutRef governorRef
, withDatum governorInputDatum
]
, output $
mconcat
[ script govValidatorHash
, withValue value
, withOutRef governorRef
, withDatum (mkGovernorOutputDatum ps)
]
]
{- | The proposal redeemer used to spend the governor UTXO, which is always
'MintGATs' in this case.
-}
governorRedeemer :: GovernorRedeemer
governorRedeemer = MintGATs
--------------------------------------------------------------------------------
-- * Authority Token
{- | Create a context builder that contains the infomation about the minted
authority tokens and where they're sent to.
-}
mkAuthorityTokenBuilder ::
forall b.
CombinableBuilder b =>
AuthorityTokenParameters ->
b
mkAuthorityTokenBuilder (AuthorityTokenParameters es mdt invalidTokenName) =
foldMap perEffect es
where
perEffect :: ValidatorHash -> b
perEffect vh =
let tn =
if invalidTokenName
then ""
else validatorHashToTokenName vh
ac = AssetClass (authorityTokenSymbol, tn)
minted = Value.assetClassValue ac 1
value = sortValue $ minAda <> minted
in mconcat
[ mint minted
, output $
mconcat
[ script vh
, maybe mempty withDatum mdt
, withValue value
]
]
-- | The redeemer used while running the authority token policy.
authorityTokenRedeemer :: ()
authorityTokenRedeemer = ()
--------------------------------------------------------------------------------
-- | Create a 'TxInfo' that update the status of a proposal.
advance ::
forall b.
CombinableBuilder b =>
ParameterBundle ->
b
advance pb =
let mkBuilderMaybe = maybe mempty
in mconcat
[ mkProposalBuilder pb.proposalParameters
, mkStakeBuilder pb.stakeParameters
, mkBuilderMaybe mkGovernorBuilder pb.governorParameters
, mkBuilderMaybe mkAuthorityTokenBuilder pb.authorityTokenParameters
, timeRange pb.transactionTimeRange
, maybe mempty signedWith pb.extraSignature
]
--------------------------------------------------------------------------------
{- | Create a test tree that runs the relavant componets to test the advancing
functionalities.
-}
mkTestTree ::
String ->
ParameterBundle ->
Validity ->
SpecificationTree
mkTestTree name pb val =
group name $ catMaybes [proposal, stake, governor, authority]
where
spend = mkSpending advance pb
mint = mkMinting advance pb
proposal =
let proposalInputDatum = mkProposalInputDatum pb.proposalParameters
in Just $
testValidator
val.forProposalValidator
"proposal"
(proposalValidator Shared.proposal)
proposalInputDatum
proposalRedeemer
(spend proposalRef)
stake =
let idx = 0
in Just $
testValidator
val.forStakeValidator
"stake"
(stakeValidator Shared.stake)
(getStakeInputDatumAt pb.stakeParameters idx)
stakeRedeemer
( spend (mkStakeRef idx)
)
governor =
testValidator
(fromJust val.forGovernorValidator)
"governor"
(governorValidator Shared.governor)
governorInputDatum
governorRedeemer
(spend governorRef)
<$ pb.governorParameters
authority =
testPolicy
(fromJust val.forAuthorityTokenPolicy)
"authority"
(authorityTokenPolicy $ AuthorityToken Shared.govAssetClass)
authorityTokenRedeemer
(mint authorityTokenSymbol)
<$ (pb.authorityTokenParameters)
{- | Create a test tree that runs a bunch of parameter bundles. These bundles
should have the same validity.
-}
mkTestTree' ::
String ->
(ParameterBundle -> String) ->
[ParameterBundle] ->
Validity ->
SpecificationTree
mkTestTree' groupName mkCaseName bundles val =
group groupName $
(\b -> mkTestTree (mkCaseName b) b val)
<$> bundles
--------------------------------------------------------------------------------
-- Utilities for creating parameter bundles
{- | 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
-- [S + 1, S + D - 1]
Draft ->
closedBoundedInterval
(proposalStartingTime + 1)
(proposalStartingTime + (def :: ProposalTimingConfig).draftTime - 1)
-- [S + D + V + 1, S + D + V + L - 1]
VotingReady ->
closedBoundedInterval
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime
+ 1
)
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime
+ (def :: ProposalTimingConfig).lockingTime
- 1
)
-- [S + D + V + L + 1, S + + D + V + L + E - 1]
Locked ->
closedBoundedInterval
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime
+ (def :: ProposalTimingConfig).lockingTime
+ 1
)
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime
+ (def :: ProposalTimingConfig).lockingTime
+ (def :: ProposalTimingConfig).executingTime - 1
)
Finished -> error "Cannot advance 'Finished' proposal"
{- | 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
-- [S + D + 1, S + D + V - 1]
Draft ->
closedBoundedInterval
(proposalStartingTime + (def :: ProposalTimingConfig).draftTime + 1)
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime - 1
)
-- [S + D + V + L + 1, S + D + V + L + E -1]
VotingReady ->
closedBoundedInterval
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime
+ (def :: ProposalTimingConfig).lockingTime
+ 1
)
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime
+ (def :: ProposalTimingConfig).lockingTime
+ (def :: ProposalTimingConfig).executingTime
- 1
)
-- [S + D + V + L + E + 1, S + D + V + L + E + 100]
Locked ->
closedBoundedInterval
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime
+ (def :: ProposalTimingConfig).lockingTime
+ (def :: ProposalTimingConfig).executingTime
+ 1
)
( proposalStartingTime
+ (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime
+ (def :: ProposalTimingConfig).lockingTime
+ (def :: ProposalTimingConfig).executingTime
+ 100
)
Finished -> error "Cannot advance 'Finished' proposal"
-- | Next state of the given proposal status.
getNextState :: ProposalStatus -> ProposalStatus
getNextState = \case
Draft -> VotingReady
VotingReady -> Locked
Locked -> Finished
Finished -> error "Cannot advance 'Finished' proposal"
-- | Calculate the number of GTs per stake in order to exceed the minimum limit.
compPerStakeGTsForDraft :: NumStake -> Integer
compPerStakeGTsForDraft nCosigners =
untag (def :: ProposalThresholds).vote
`div` fromIntegral nCosigners + 1
dummyDatum :: ()
dummyDatum = ()
dummyDatumHash :: DatumHash
dummyDatumHash = datumHash $ toDatum dummyDatum
-- | Create given number of effect groups. Each group will have 3 effects.
mkMockEffects :: Int -> [AssocMap.Map ValidatorHash DatumHash]
mkMockEffects =
flip
take
( AssocMap.fromList
. flip zip (repeat dummyDatumHash)
<$> groupsOfN 3 validatorHashes
)
numberOfVotesThatExceedsTheMinimumRequirement :: Integer
numberOfVotesThatExceedsTheMinimumRequirement =
untag (def @ProposalThresholds).execute + 1
mkWinnerVotes :: Index -> (Winner, Integer)
mkWinnerVotes idx =
( EffectAt idx
, numberOfVotesThatExceedsTheMinimumRequirement
)
ambiguousWinnerVotes :: (Winner, Integer)
ambiguousWinnerVotes =
( All
, numberOfVotesThatExceedsTheMinimumRequirement
)
--------------------------------------------------------------------------------
-- * Parameter Bundles
---
-- * Legal
defaultWinnerIdx :: Index
defaultWinnerIdx = 0
{- | Advance a proposal to the next state, perfectly valid for all the
componets.
-}
mkValidToNextStateBundle ::
-- | Number of cosigners.
Word ->
-- | Number of effects
Word ->
-- | The initial proposal state, should not be 'Finished'.
ProposalStatus ->
ParameterBundle
mkValidToNextStateBundle _ _ Finished =
error "Cannot advance from Finished"
mkValidToNextStateBundle nCosigners nEffects from =
let next = getNextState from
effects = mkMockEffects $ fromIntegral nEffects
winner = defaultWinnerIdx
template =
ParameterBundle
{ proposalParameters =
ProposalParameters
{ fromStatus = from
, toStatus = next
, effectList = effects
, winnerAndVotes = Nothing
, numCosigners = fromIntegral nCosigners
, invalidProposalOutputDatum = False
}
, stakeParameters =
StakeParameters
{ numStake = 1
, perStakeGTs =
compPerStakeGTsForDraft $
fromIntegral nCosigners
, transactionSignedByOwners = False
, invalidStakeOutputDatum = False
}
, governorParameters = Nothing
, authorityTokenParameters = Nothing
, transactionTimeRange = mkInTimeTimeRange from
, extraSignature = Just signer
}
-- This is my favourite part of the test suite, lol.
modifyTemplate = do
when (from == Draft) $
modify $ \b ->
b
{ stakeParameters =
b.stakeParameters
{ transactionSignedByOwners = True
, numStake = fromIntegral nCosigners
}
, extraSignature = Nothing
}
when (from == VotingReady || from == Locked) $
modify $ \b ->
b
{ proposalParameters =
b.proposalParameters
{ winnerAndVotes = Just $ mkWinnerVotes winner
}
}
when (from == Locked) $
modify $ \b ->
let aut =
AuthorityTokenParameters
{ mintGATsFor = AssocMap.keys $ effects !! winner
, carryDatum = Just dummyDatum
, invalidTokenName = False
}
gov =
GovernorParameters
{ invalidGovernorOutputDatum = False
}
in b
{ governorParameters = Just gov
, authorityTokenParameters = Just aut
}
in execState modifyTemplate template
mkValidToNextStateBundles ::
-- | Number of cosigners
Word ->
-- | Number of effects
Word ->
[ParameterBundle]
mkValidToNextStateBundles nCosigners nEffects =
mkValidToNextStateBundle nCosigners nEffects
<$> [ Draft
, VotingReady
, Locked
]
mkValidToFailedStateBundles ::
-- | Number of cosigners
Word ->
-- | Number of effects
Word ->
[ParameterBundle]
mkValidToFailedStateBundles nCosigners nEffects =
mkBundle
<$> [ Draft
, VotingReady
, Locked
]
where
mkBundle from =
let next = Finished
effects = mkMockEffects $ fromIntegral nEffects
in ParameterBundle
{ proposalParameters =
ProposalParameters
{ fromStatus = from
, toStatus = next
, effectList = effects
, winnerAndVotes = Nothing
, numCosigners = fromIntegral nCosigners
, invalidProposalOutputDatum = False
}
, stakeParameters =
StakeParameters
{ numStake = 1
, perStakeGTs =
compPerStakeGTsForDraft $
fromIntegral nCosigners
, transactionSignedByOwners = False
, invalidStakeOutputDatum = False
}
, governorParameters = Nothing
, authorityTokenParameters = Nothing
, transactionTimeRange = mkTooLateTimeRange from
, extraSignature = Just signer
}
-- * Illegal
mkFromFinishedBundles ::
-- | Number of cosigners
Word ->
-- | Number of effects
Word ->
[ParameterBundle]
mkFromFinishedBundles nCosigners nEffects =
mkBundle
<$> [ Draft
, VotingReady
, Locked
]
where
mkBundle from =
let template = mkValidToNextStateBundle nCosigners nEffects from
in template
{ proposalParameters =
template.proposalParameters
{ fromStatus = Finished
, toStatus = Finished
}
}
mkToNextStateTooLateBundles :: Word -> Word -> [ParameterBundle]
mkToNextStateTooLateBundles nCosigners nEffects =
mkBundle
<$> [ Draft
, VotingReady
, Locked
]
where
mkBundle from =
let template = mkValidToNextStateBundle nCosigners nEffects from
in template
{ transactionTimeRange = mkTooLateTimeRange from
}
mkInvalidOutputStakeBundles :: Word -> Word -> [ParameterBundle]
mkInvalidOutputStakeBundles nCosigners nEffects =
mkBundle <$> [Draft, VotingReady, Locked]
where
mkBundle from =
let template = mkValidToNextStateBundle nCosigners nEffects from
in template
{ stakeParameters =
template.stakeParameters
{ invalidStakeOutputDatum = True
}
}
-- * From Draft
mkInsufficientCosignsBundle :: Word -> Word -> ParameterBundle
mkInsufficientCosignsBundle nCosigners nEffects =
template
{ stakeParameters =
template.stakeParameters
{ perStakeGTs = insuffcientPerStakeGTs
}
}
where
insuffcientPerStakeGTs =
untag (def :: ProposalThresholds).vote
`div` fromIntegral nCosigners - 1
template = mkValidToNextStateBundle nCosigners nEffects Draft
-- * From VotingReady
setWinnerAndVotes ::
ParameterBundle ->
Maybe (Winner, Integer) ->
ParameterBundle
setWinnerAndVotes pb wv =
pb
{ proposalParameters =
pb.proposalParameters
{ winnerAndVotes = wv
}
}
mkInsufficientVotesBundle ::
Word ->
Word ->
ParameterBundle
mkInsufficientVotesBundle nCosigners nEffects =
mkValidToNextStateBundle nCosigners nEffects VotingReady
`setWinnerAndVotes` Nothing
mkAmbiguousWinnerBundle ::
Word ->
Word ->
ParameterBundle
mkAmbiguousWinnerBundle nCosigners nEffects =
mkValidToNextStateBundle nCosigners nEffects VotingReady
`setWinnerAndVotes` Just ambiguousWinnerVotes
-- * From Locked
mkValidFromLockedBundle :: Word -> Word -> ParameterBundle
mkValidFromLockedBundle nCosigners nEffects =
mkValidToNextStateBundle nCosigners nEffects Locked
mkMintGATsForWrongEffectsBundle ::
Word ->
Word ->
ParameterBundle
mkMintGATsForWrongEffectsBundle nCosigners nEffects =
template
{ authorityTokenParameters =
( \aut ->
aut
{ mintGATsFor =
[ validatorHashes !! 1
, validatorHashes !! 3
, validatorHashes !! 5
, validatorHashes !! 7
]
}
)
<$> template.authorityTokenParameters
}
where
template = mkValidFromLockedBundle nCosigners nEffects
mkNoGATMintedBundle ::
Word ->
Word ->
ParameterBundle
mkNoGATMintedBundle nCosigners nEffects =
template
{ authorityTokenParameters = Nothing
}
where
template = mkValidFromLockedBundle nCosigners nEffects
mkMintGATsWithoutTagBundle ::
Word ->
Word ->
ParameterBundle
mkMintGATsWithoutTagBundle nCosigners nEffects =
template
{ authorityTokenParameters =
( \aut ->
aut
{ invalidTokenName = True
}
)
<$> template.authorityTokenParameters
}
where
template = mkValidFromLockedBundle nCosigners nEffects
mkGATsWithWrongDatumBundle ::
Word ->
Word ->
ParameterBundle
mkGATsWithWrongDatumBundle nCosigners nEffects =
template
{ authorityTokenParameters = Just newAut
}
where
template = mkValidFromLockedBundle nCosigners nEffects
aut = fromJust template.authorityTokenParameters
newAut =
AuthorityTokenParameters
aut.mintGATsFor
(Just (1 :: Integer))
False
mkBadGovernorOutputDatumBundle ::
Word ->
Word ->
ParameterBundle
mkBadGovernorOutputDatumBundle nCosigners nEffects =
template
{ governorParameters = Just gov
}
where
template = mkValidFromLockedBundle nCosigners nEffects
gov = GovernorParameters True