check minimum stake amount while voting
This commit is contained in:
parent
1b3ec260a3
commit
270c984f8d
10 changed files with 88 additions and 49 deletions
|
|
@ -63,6 +63,7 @@ import Test.Tasty.QuickCheck (
|
||||||
data GovernorDatumCases
|
data GovernorDatumCases
|
||||||
= ExecuteLE0
|
= ExecuteLE0
|
||||||
| CreateLE0
|
| CreateLE0
|
||||||
|
| ToVotingLE0
|
||||||
| VoteLE0
|
| VoteLE0
|
||||||
| Correct
|
| Correct
|
||||||
deriving stock (Eq, Show)
|
deriving stock (Eq, Show)
|
||||||
|
|
@ -88,9 +89,10 @@ governorDatumValidProperty =
|
||||||
classifiedPropertyNative gen (const []) expected classifier pisGovernorDatumValid
|
classifiedPropertyNative gen (const []) expected classifier pisGovernorDatumValid
|
||||||
where
|
where
|
||||||
classifier :: GovernorDatum -> GovernorDatumCases
|
classifier :: GovernorDatum -> GovernorDatumCases
|
||||||
classifier ((.proposalThresholds) -> ProposalThresholds e c v)
|
classifier ((.proposalThresholds) -> ProposalThresholds e c tv v)
|
||||||
| e < 0 = ExecuteLE0
|
| e < 0 = ExecuteLE0
|
||||||
| c < 0 = CreateLE0
|
| c < 0 = CreateLE0
|
||||||
|
| tv < 0 = ToVotingLE0
|
||||||
| v < 0 = VoteLE0
|
| v < 0 = VoteLE0
|
||||||
| otherwise = Correct
|
| otherwise = Correct
|
||||||
|
|
||||||
|
|
@ -110,24 +112,27 @@ governorDatumValidProperty =
|
||||||
let validGT = taggedInteger (0, 1000000000)
|
let validGT = taggedInteger (0, 1000000000)
|
||||||
execute <- validGT
|
execute <- validGT
|
||||||
create <- validGT
|
create <- validGT
|
||||||
|
toVoting <- validGT
|
||||||
vote <- validGT
|
vote <- validGT
|
||||||
le0 <- taggedInteger (-1000, -1)
|
le0 <- taggedInteger (-1000, -1)
|
||||||
|
|
||||||
case c of
|
case c of
|
||||||
ExecuteLE0 ->
|
ExecuteLE0 ->
|
||||||
-- execute < 0
|
-- execute < 0
|
||||||
return $ ProposalThresholds le0 create vote
|
return $ ProposalThresholds le0 create toVoting vote
|
||||||
CreateLE0 ->
|
CreateLE0 ->
|
||||||
-- c < 0
|
-- c < 0
|
||||||
return $ ProposalThresholds execute le0 vote
|
return $ ProposalThresholds execute le0 toVoting vote
|
||||||
|
ToVotingLE0 ->
|
||||||
|
return $ ProposalThresholds execute create le0 vote
|
||||||
VoteLE0 ->
|
VoteLE0 ->
|
||||||
-- vote < 0
|
-- vote < 0
|
||||||
return $ ProposalThresholds execute create le0
|
return $ ProposalThresholds execute create toVoting le0
|
||||||
Correct -> do
|
Correct -> do
|
||||||
-- c <= vote < execute
|
-- c <= vote < execute
|
||||||
nv <- taggedInteger (0, untag execute - 1)
|
nv <- taggedInteger (0, untag execute - 1)
|
||||||
nc <- taggedInteger (0, untag nv)
|
nc <- taggedInteger (0, untag nv)
|
||||||
return $ ProposalThresholds execute nc nv
|
return $ ProposalThresholds execute nc toVoting nv
|
||||||
|
|
||||||
data GovernorPolicyCases
|
data GovernorPolicyCases
|
||||||
= ReferenceUTXONotSpent
|
= ReferenceUTXONotSpent
|
||||||
|
|
|
||||||
|
|
@ -195,7 +195,7 @@ invalidNewGovernorDatum =
|
||||||
GovernorDatum
|
GovernorDatum
|
||||||
{ proposalThresholds =
|
{ proposalThresholds =
|
||||||
def
|
def
|
||||||
{ vote = Tagged (-1)
|
{ toVoting = Tagged (-1)
|
||||||
}
|
}
|
||||||
, nextProposalId = ProposalId 42
|
, nextProposalId = ProposalId 42
|
||||||
, proposalTimings = def
|
, proposalTimings = def
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ validGovernorOutputDatum =
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidProposalThresholds :: ProposalThresholds
|
invalidProposalThresholds :: ProposalThresholds
|
||||||
invalidProposalThresholds = ProposalThresholds (-1) (-1) (-1)
|
invalidProposalThresholds = ProposalThresholds (-1) (-1) (-1) (-1)
|
||||||
|
|
||||||
invalidMaxTimeRangeWidth :: MaxTimeRangeWidth
|
invalidMaxTimeRangeWidth :: MaxTimeRangeWidth
|
||||||
invalidMaxTimeRangeWidth = MaxTimeRangeWidth 0
|
invalidMaxTimeRangeWidth = MaxTimeRangeWidth 0
|
||||||
|
|
|
||||||
|
|
@ -120,6 +120,7 @@ mkGovernorOutputDatum ValueInvalid =
|
||||||
ProposalThresholds
|
ProposalThresholds
|
||||||
{ execute = -1
|
{ execute = -1
|
||||||
, create = -1
|
, create = -1
|
||||||
|
, toVoting = -1
|
||||||
, vote = -1
|
, vote = -1
|
||||||
}
|
}
|
||||||
in Just $
|
in Just $
|
||||||
|
|
|
||||||
|
|
@ -679,7 +679,7 @@ getNextState = \case
|
||||||
-- | Calculate the number of GTs per stake in order to exceed the minimum limit.
|
-- | Calculate the number of GTs per stake in order to exceed the minimum limit.
|
||||||
compPerStakeGTsForDraft :: NumStake -> Integer
|
compPerStakeGTsForDraft :: NumStake -> Integer
|
||||||
compPerStakeGTsForDraft nCosigners =
|
compPerStakeGTsForDraft nCosigners =
|
||||||
untag (def :: ProposalThresholds).vote
|
untag (def :: ProposalThresholds).toVoting
|
||||||
`div` fromIntegral nCosigners + 1
|
`div` fromIntegral nCosigners + 1
|
||||||
|
|
||||||
dummyDatum :: ()
|
dummyDatum :: ()
|
||||||
|
|
@ -944,7 +944,7 @@ mkInsufficientCosignsBundle nCosigners nEffects =
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
insuffcientPerStakeGTs =
|
insuffcientPerStakeGTs =
|
||||||
untag (def :: ProposalThresholds).vote
|
untag (def :: ProposalThresholds).toVoting
|
||||||
`div` fromIntegral nCosigners - 1
|
`div` fromIntegral nCosigners - 1
|
||||||
template = mkValidToNextStateBundle nCosigners nEffects False Draft
|
template = mkValidToNextStateBundle nCosigners nEffects False Draft
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ module Sample.Proposal.Vote (
|
||||||
moreThanOneProposals,
|
moreThanOneProposals,
|
||||||
invalidLocks,
|
invalidLocks,
|
||||||
destroyStakes,
|
destroyStakes,
|
||||||
|
insufficientAmount,
|
||||||
|
insufficientAmount1,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Agora.Governor (Governor (..))
|
import Agora.Governor (Governor (..))
|
||||||
|
|
@ -526,3 +528,27 @@ destroyStakes =
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
insufficientAmount :: ParameterBundle
|
||||||
|
insufficientAmount =
|
||||||
|
ownerVoteWithSignleStake
|
||||||
|
{ stakeParameters =
|
||||||
|
ownerVoteWithSignleStake.stakeParameters
|
||||||
|
{ stakeInputParameters =
|
||||||
|
ownerVoteWithSignleStake.stakeParameters.stakeInputParameters
|
||||||
|
{ perStakeGTs = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
insufficientAmount1 :: ParameterBundle
|
||||||
|
insufficientAmount1 =
|
||||||
|
ownerVoteWithMultipleStakes
|
||||||
|
{ stakeParameters =
|
||||||
|
ownerVoteWithMultipleStakes.stakeParameters
|
||||||
|
{ stakeInputParameters =
|
||||||
|
ownerVoteWithMultipleStakes.stakeParameters.stakeInputParameters
|
||||||
|
{ perStakeGTs = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,7 @@ instance Default ProposalThresholds where
|
||||||
ProposalThresholds
|
ProposalThresholds
|
||||||
{ execute = Tagged 1000
|
{ execute = Tagged 1000
|
||||||
, create = Tagged 1
|
, create = Tagged 1
|
||||||
|
, toVoting = Tagged 100
|
||||||
, vote = Tagged 100
|
, vote = Tagged 100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,14 @@ specs =
|
||||||
"attempt to burn stakes"
|
"attempt to burn stakes"
|
||||||
Vote.destroyStakes
|
Vote.destroyStakes
|
||||||
(Vote.Validity True False)
|
(Vote.Validity True False)
|
||||||
|
, Vote.mkTestTree
|
||||||
|
"insufficient staked amount"
|
||||||
|
Vote.insufficientAmount
|
||||||
|
(Vote.Validity False True)
|
||||||
|
, Vote.mkTestTree
|
||||||
|
"insufficient staked amount"
|
||||||
|
Vote.insufficientAmount1
|
||||||
|
(Vote.Validity False True)
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
, group
|
, group
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,7 @@ import Plutarch.DataRepr (
|
||||||
),
|
),
|
||||||
PDataFields,
|
PDataFields,
|
||||||
)
|
)
|
||||||
import Plutarch.Extra.Comonad (pextract)
|
import Plutarch.Extra.Field (pletAll)
|
||||||
import Plutarch.Extra.Field (pletAllC)
|
|
||||||
import Plutarch.Extra.Function (pbuiltinUncurry)
|
import Plutarch.Extra.Function (pbuiltinUncurry)
|
||||||
import Plutarch.Extra.IsData (
|
import Plutarch.Extra.IsData (
|
||||||
DerivePConstantViaDataList (DerivePConstantViaDataList),
|
DerivePConstantViaDataList (DerivePConstantViaDataList),
|
||||||
|
|
@ -81,14 +80,14 @@ import Plutarch.Extra.IsData (
|
||||||
import "liqwid-plutarch-extra" Plutarch.Extra.List (pfindJust)
|
import "liqwid-plutarch-extra" Plutarch.Extra.List (pfindJust)
|
||||||
import Plutarch.Extra.Map qualified as PM
|
import Plutarch.Extra.Map qualified as PM
|
||||||
import Plutarch.Extra.Maybe (pfromJust)
|
import Plutarch.Extra.Maybe (pfromJust)
|
||||||
import "liqwid-plutarch-extra" Plutarch.Extra.TermCont (pguardC, pletC, pmatchC)
|
import "liqwid-plutarch-extra" Plutarch.Extra.TermCont (pguardC, pletC)
|
||||||
import Plutarch.Lift (
|
import Plutarch.Lift (
|
||||||
DerivePConstantViaNewtype (DerivePConstantViaNewtype),
|
DerivePConstantViaNewtype (DerivePConstantViaNewtype),
|
||||||
PConstantDecl,
|
PConstantDecl,
|
||||||
PUnsafeLiftDecl (type PLifted),
|
PUnsafeLiftDecl (type PLifted),
|
||||||
)
|
)
|
||||||
import Plutarch.Orphans ()
|
import Plutarch.Orphans ()
|
||||||
import Plutarch.SafeMoney (PDiscrete (PDiscrete))
|
import Plutarch.SafeMoney (PDiscrete)
|
||||||
import PlutusLedgerApi.V2 (Credential, DatumHash, ScriptHash, ValidatorHash)
|
import PlutusLedgerApi.V2 (Credential, DatumHash, ScriptHash, ValidatorHash)
|
||||||
import PlutusTx qualified
|
import PlutusTx qualified
|
||||||
|
|
||||||
|
|
@ -222,7 +221,7 @@ data ProposalStatus
|
||||||
This data is stored centrally (in the 'Agora.Governor.Governor') and copied over
|
This data is stored centrally (in the 'Agora.Governor.Governor') and copied over
|
||||||
to 'Proposal's when they are created.
|
to 'Proposal's when they are created.
|
||||||
|
|
||||||
@since 0.1.0
|
@since 1.0.0
|
||||||
-}
|
-}
|
||||||
data ProposalThresholds = ProposalThresholds
|
data ProposalThresholds = ProposalThresholds
|
||||||
{ execute :: Tagged GTTag Integer
|
{ execute :: Tagged GTTag Integer
|
||||||
|
|
@ -232,9 +231,10 @@ data ProposalThresholds = ProposalThresholds
|
||||||
--
|
--
|
||||||
-- It is recommended this be a high enough amount, in order to prevent DOS from bad
|
-- It is recommended this be a high enough amount, in order to prevent DOS from bad
|
||||||
-- actors.
|
-- actors.
|
||||||
|
, toVoting :: Tagged GTTag Integer
|
||||||
|
-- ^ How much GT required to to move into 'Locked'.
|
||||||
, vote :: Tagged GTTag Integer
|
, vote :: Tagged GTTag Integer
|
||||||
-- ^ How much GT required to allow voting to happen.
|
-- ^ How much GT required to vote on a outcome.
|
||||||
-- (i.e. to move into 'VotingReady')
|
|
||||||
}
|
}
|
||||||
deriving stock
|
deriving stock
|
||||||
( -- | @since 0.1.0
|
( -- | @since 0.1.0
|
||||||
|
|
@ -553,7 +553,7 @@ deriving via (DerivePConstantViaEnum ProposalStatus PProposalStatus) instance (P
|
||||||
|
|
||||||
{- | Plutarch-level version of 'ProposalThresholds'.
|
{- | Plutarch-level version of 'ProposalThresholds'.
|
||||||
|
|
||||||
@since 0.1.0
|
@since 1.0.0
|
||||||
-}
|
-}
|
||||||
newtype PProposalThresholds (s :: S) = PProposalThresholds
|
newtype PProposalThresholds (s :: S) = PProposalThresholds
|
||||||
{ getProposalThresholds ::
|
{ getProposalThresholds ::
|
||||||
|
|
@ -562,6 +562,7 @@ newtype PProposalThresholds (s :: S) = PProposalThresholds
|
||||||
( PDataRecord
|
( PDataRecord
|
||||||
'[ "execute" ':= PDiscrete GTTag
|
'[ "execute" ':= PDiscrete GTTag
|
||||||
, "create" ':= PDiscrete GTTag
|
, "create" ':= PDiscrete GTTag
|
||||||
|
, "toVoting" ':= PDiscrete GTTag
|
||||||
, "vote" ':= PDiscrete GTTag
|
, "vote" ':= PDiscrete GTTag
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
@ -951,23 +952,18 @@ pneutralOption = phoistAcyclic $
|
||||||
-}
|
-}
|
||||||
pisProposalThresholdsValid :: forall (s :: S). Term s (PProposalThresholds :--> PBool)
|
pisProposalThresholdsValid :: forall (s :: S). Term s (PProposalThresholds :--> PBool)
|
||||||
pisProposalThresholdsValid = phoistAcyclic $
|
pisProposalThresholdsValid = phoistAcyclic $
|
||||||
plam $ \thresholds -> unTermCont $ do
|
plam $
|
||||||
thresholdsF <- pletAllC thresholds
|
flip pletAll $ \thresholdsF ->
|
||||||
|
|
||||||
PDiscrete execute' <- pmatchC thresholdsF.execute
|
|
||||||
PDiscrete draft' <- pmatchC thresholdsF.create
|
|
||||||
PDiscrete vote' <- pmatchC thresholdsF.vote
|
|
||||||
|
|
||||||
execute <- pletC $ pextract # execute'
|
|
||||||
draft <- pletC $ pextract # draft'
|
|
||||||
vote <- pletC $ pextract # vote'
|
|
||||||
|
|
||||||
pure $
|
|
||||||
foldr1
|
foldr1
|
||||||
(#&&)
|
(#&&)
|
||||||
[ ptraceIfFalse "Execute threshold is less than or equal to 0" $ 0 #<= execute
|
[ ptraceIfFalse "Execute threshold is less than or equal to 0" $
|
||||||
, ptraceIfFalse "Draft threshold is less than or equal to 0" $ 0 #<= draft
|
0 #<= pfromData thresholdsF.execute
|
||||||
, ptraceIfFalse "Vote threshold is less than or equal to 0" $ 0 #<= vote
|
, ptraceIfFalse "Create threshold is less than or equal to 0" $
|
||||||
|
0 #<= pfromData thresholdsF.create
|
||||||
|
, ptraceIfFalse "toVoting threshold is less than or equal to 0" $
|
||||||
|
0 #<= pfromData thresholdsF.toVoting
|
||||||
|
, ptraceIfFalse "Vote threshold is less than or equal to 0" $
|
||||||
|
0 #<= pfromData thresholdsF.vote
|
||||||
]
|
]
|
||||||
|
|
||||||
{- | Retract votes given the option and the amount of votes.
|
{- | Retract votes given the option and the amount of votes.
|
||||||
|
|
|
||||||
|
|
@ -458,25 +458,27 @@ proposalValidator as maximumCosigners =
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
PVote r -> spendStakes $ \sctxF -> do
|
PVote r -> spendStakes $ \sctxF -> do
|
||||||
let totalStakeAmount =
|
totalStakeAmount <-
|
||||||
pto $
|
pletC $
|
||||||
pfoldMap
|
pto $
|
||||||
# plam
|
pfoldMap
|
||||||
( \stake -> unTermCont $ do
|
# plam
|
||||||
stakeF <- pletFieldsC @'["stakedAmount", "lockedBy"] stake
|
( \stake -> unTermCont $ do
|
||||||
|
stakeF <- pletFieldsC @'["stakedAmount", "lockedBy"] stake
|
||||||
|
|
||||||
pguardC "Same stake shouldn't vote on the same proposal twice" $
|
pguardC "Same stake shouldn't vote on the same proposal twice" $
|
||||||
pnot
|
pnot
|
||||||
#$ pisVoter
|
#$ pisVoter
|
||||||
#$ pgetStakeRole
|
#$ pgetStakeRole
|
||||||
# proposalInputDatumF.proposalId
|
# proposalInputDatumF.proposalId
|
||||||
# stakeF.lockedBy
|
# stakeF.lockedBy
|
||||||
|
|
||||||
pure $ pcon $ PSum $ pfromData stakeF.stakedAmount
|
pure $ pcon $ PSum $ pfromData stakeF.stakedAmount
|
||||||
)
|
)
|
||||||
# sctxF.inputStakes
|
# sctxF.inputStakes
|
||||||
|
|
||||||
-- TODO(Connor): check minimum stake amount?
|
pguardC "Exceed minimum amount" $
|
||||||
|
thresholdsF.vote #< totalStakeAmount
|
||||||
|
|
||||||
pguardC "Input proposal must be in VotingReady state" $
|
pguardC "Input proposal must be in VotingReady state" $
|
||||||
currentStatus #== pconstant VotingReady
|
currentStatus #== pconstant VotingReady
|
||||||
|
|
@ -657,7 +659,7 @@ proposalValidator as maximumCosigners =
|
||||||
pmatchC notTooLate >>= \case
|
pmatchC notTooLate >>= \case
|
||||||
PTrue -> do
|
PTrue -> do
|
||||||
pguardC "More cosigns than minimum amount" $
|
pguardC "More cosigns than minimum amount" $
|
||||||
punsafeCoerce (pfromData thresholdsF.vote) #< sctxF.totalAmount
|
punsafeCoerce (pfromData thresholdsF.toVoting) #< sctxF.totalAmount
|
||||||
|
|
||||||
pguardC "All new cosigners are witnessed by their Stake datums" $
|
pguardC "All new cosigners are witnessed by their Stake datums" $
|
||||||
plistEqualsBy
|
plistEqualsBy
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue