correctly handle proposal time

This commit is contained in:
Hongrui Fang 2022-10-26 21:15:20 +08:00
parent 4dbccbc996
commit b19faa7cfe
No known key found for this signature in database
GPG key ID: F10AB2CCE24113DD
3 changed files with 141 additions and 131 deletions

View file

@ -339,15 +339,19 @@ unlock ps = builder
---
ProposalStartingTime s = defStartingTime
time = case ps.transactionParameters.timeRange of
WhileVoting ->
closedBoundedInterval
((def :: ProposalTimingConfig).draftTime + 1)
((def :: ProposalTimingConfig).votingTime - 1)
let lb = s + (def :: ProposalTimingConfig).draftTime
ub = lb + (def :: ProposalTimingConfig).votingTime
in closedBoundedInterval (lb + 1) (ub - 1)
AfterVoting ->
closedBoundedInterval
((def :: ProposalTimingConfig).votingTime + 1)
((def :: ProposalTimingConfig).lockingTime - 1)
let lb =
s + (def :: ProposalTimingConfig).draftTime
+ (def :: ProposalTimingConfig).votingTime
ub = lb + (def :: ProposalTimingConfig).lockingTime
in closedBoundedInterval (lb + 1) (ub - 1)
sig = case ps.transactionParameters.signedBy of
Unknown -> defUnknown

View file

@ -20,11 +20,11 @@ import Agora.Proposal (
pwinner',
)
import Agora.Proposal.Time (
PPeriod (PDraftingPeriod, PExecutingPeriod, PLockingPeriod, PVotingPeriod),
PTimingRelation (PAfter, PWithin),
currentProposalTime,
isDraftPeriod,
isExecutionPeriod,
isLockingPeriod,
isVotingPeriod,
pgetRelation,
pisWithin,
)
import Agora.Stake (
PStakeDatum,
@ -232,8 +232,6 @@ proposalValidator =
]
txInfo
currentTime <- pletC $ currentProposalTime # txInfoF.validRange
----------------------------------------------------------------------------
PSpending ((pfield @"_0" #) -> propsalInputRef) <-
@ -292,6 +290,20 @@ proposalValidator =
--------------------------------------------------------------------------
getTimingRelation' <-
pletC $
let currentTime =
passertPJust # "Current time should be resolved"
#$ currentProposalTime # txInfoF.validRange
in pgetRelation
# proposalInputDatumF.timingConfig
# proposalInputDatumF.startingTime
# currentTime
let getTimingRelation = (getTimingRelation' #) . pcon
--------------------------------------------------------------------------
-- Handle stake inputs/outputs.
-- Reslove stake datum if the given UTxO is a stake UTxO.
@ -473,11 +485,7 @@ proposalValidator =
currentStatus #== pconstant VotingReady
pguardC "Proposal time should be wthin the voting period" $
isVotingPeriod # proposalInputDatumF.timingConfig
# proposalInputDatumF.startingTime
#$ passertPJust
# "Should be able to get current time"
# currentTime
pisWithin # getTimingRelation PVotingPeriod
-- Ensure the transaction is voting to a valid 'ResultTag'(outcome).
PProposalVotes voteMap <- pmatchC proposalInputDatumF.votes
@ -557,15 +565,8 @@ proposalValidator =
# proposalInputDatumF.votes
# sctxF.inputStakes
currentTime' =
passertPJust
# "Should be able to get current time"
# currentTime
inVotingPeriod =
isVotingPeriod # proposalInputDatumF.timingConfig
# proposalInputDatumF.startingTime
# currentTime'
pisWithin # getTimingRelation PVotingPeriod
-- The votes can only change when the proposal still allows voting.
shouldUpdateVotes =
@ -599,24 +600,6 @@ proposalValidator =
----------------------------------------------------------------------
PAdvanceProposal _ -> unTermCont $ do
currentTime' <-
pletC $
passertPJust
# "Should be able to get current time"
# currentTime
applyIs <- pletC $
plam $ \f ->
f
# proposalInputDatumF.timingConfig
# proposalInputDatumF.startingTime
# currentTime'
let inDraftPeriod = applyIs # isDraftPeriod
inVotingPeriod = applyIs # isVotingPeriod
inExecutionPeriod = applyIs # isExecutionPeriod
inLockedPeriod <- pletC $ applyIs # isLockingPeriod
proposalOutputStatus <-
pletC $
pfromData $
@ -641,10 +624,8 @@ proposalValidator =
pmatch currentStatus $ \case
PDraft ->
witnessStakes $ \sctxF -> do
let notTooLate = inDraftPeriod
pmatchC notTooLate >>= \case
PTrue -> do
pmatchC (getTimingRelation PDraftingPeriod) >>= \case
PWithin -> do
pguardC "More cosigns than minimum amount" $
punsafeCoerce (pfromData thresholdsF.toVoting) #<= sctxF.totalAmount
@ -658,20 +639,15 @@ proposalValidator =
pguardC "Proposal status set to VotingReady" $
proposalOutputStatus #== pconstant VotingReady
-- Too late: failed proposal, status set to 'Finished'.
PFalse ->
PAfter ->
pguardC "Proposal should fail: not on time" $
proposalOutputStatus #== pconstant Finished
----------------------------------------------------------------
PVotingReady -> unTermCont $ do
let notTooLate = inLockedPeriod
notTooEarly = pnot # inVotingPeriod
pguardC "Cannot advance ahead of time" notTooEarly
pmatchC notTooLate >>= \case
PTrue -> do
pmatchC (getTimingRelation PLockingPeriod) >>= \case
PWithin -> do
-- 'VotingReady' -> 'Locked'
pguardC "Proposal status set to Locked" $
proposalOutputStatus #== pconstant Locked
@ -681,7 +657,7 @@ proposalValidator =
#$ punsafeCoerce
$ pfromData thresholdsF.execute
-- Too late: failed proposal, status set to 'Finished'.
PFalse ->
PAfter ->
pguardC "Proposal should fail: not on time" $
proposalOutputStatus #== pconstant Finished
@ -690,11 +666,6 @@ proposalValidator =
----------------------------------------------------------------
PLocked -> unTermCont $ do
let notTooLate = inExecutionPeriod
notTooEarly = pnot # inLockedPeriod
pguardC "Not too early" notTooEarly
pguardC "Proposal status set to Finished" $
proposalOutputStatus #== pconstant Finished
@ -710,12 +681,12 @@ proposalValidator =
# pfromData txInfoF.inputs
pguardC "GST not moved if too late, moved otherwise" $
pif
notTooLate
-- Not too late: GST should moved
pidentity
-- Not too late: GST should not moved
pnot
pmatch
(getTimingRelation PExecutingPeriod)
( \case
PWithin -> pidentity
PAfter -> pnot
)
# gstMoved
pure $ popaque $ pconstant ()

View file

@ -18,20 +18,21 @@ module Agora.Proposal.Time (
PProposalTimingConfig (..),
PProposalStartingTime (..),
PMaxTimeRangeWidth (..),
PTimingRelation (..),
PPeriod (..),
-- * Compute periods given config and starting time.
validateProposalStartingTime,
currentProposalTime,
isDraftPeriod,
isVotingPeriod,
isLockingPeriod,
isExecutionPeriod,
pisProposalTimingConfigValid,
pisMaxTimeRangeWidthValid,
pgetRelation,
pisWithin,
) where
import Agora.Utils (pcurrentTimeDuration)
import Control.Composition ((.*))
import Data.Functor ((<&>))
import Plutarch.Api.V1 (
PExtended (PFinite),
PInterval (PInterval),
@ -46,11 +47,11 @@ import Plutarch.DataRepr (
)
import Plutarch.Extra.Applicative (PApply (pliftA2))
import Plutarch.Extra.Field (pletAll, pletAllC)
import Plutarch.Extra.IsData (PlutusTypeEnumData)
import Plutarch.Extra.Maybe (pjust, pmaybe, pnothing)
import "liqwid-plutarch-extra" Plutarch.Extra.TermCont (pmatchC)
import "liqwid-plutarch-extra" Plutarch.Extra.TermCont (pletC, pmatchC)
import Plutarch.Extra.Time (
PCurrentTime (PCurrentTime),
pisCurrentTimeWithin,
pisWithinCurrentTime,
)
import Plutarch.Lift (
@ -388,74 +389,108 @@ currentProposalTime = phoistAcyclic $
mkTime = phoistAcyclic $ plam $ pcon .* PCurrentTime
pure $ pliftA2 # mkTime # lowerBound # upperBound
{- | True if the 'PProposalTime' is in the draft period.
{- | Represent relation between current time and a given period.
@since 0.1.0
Note that the "before" relation isn't present due to the fact that
it's considered as an error in the proposal script.
@since 1.0.0
-}
isDraftPeriod ::
data PTimingRelation (s :: S)
= PWithin
| PAfter
deriving stock
( -- | @since 1.0.0
Generic
, -- | @since 1.0.0
Enum
, -- | @since 1.0.0
Bounded
)
deriving anyclass
( -- | @since 1.0.0
PlutusType
)
-- | @since 1.0.0
instance DerivePlutusType PTimingRelation where
type DPTStrat _ = PlutusTypeEnumData
{- | Return truw if a relation is 'PWithin'.
@since 1.0.0
-}
pisWithin :: forall (s :: S). Term s (PTimingRelation :--> PBool)
pisWithin = phoistAcyclic $
plam $
flip pmatch $ \case
PWithin -> pconstant True
_ -> pconstant False
{- | Represent a proposal period.
@since 1.0.0
-}
data PPeriod (s :: S)
= PDraftingPeriod
| PVotingPeriod
| PLockingPeriod
| PExecutingPeriod
deriving stock
( -- | @since 1.0.0
Generic
, -- | @since 1.0.0
Enum
, -- | @since 1.0.0
Bounded
)
deriving anyclass
( -- | @since 1.0.0
PlutusType
)
-- | @since 1.0.0
instance DerivePlutusType PPeriod where
type DPTStrat _ = PlutusTypeEnumData
{- | Compute the relation between current time range and the given peroid,
providing the starting time and timing configuration of a proposal. If the
relation cannot be ddetermined, error out.
@since 1.0.0
-}
pgetRelation ::
forall (s :: S).
Term
s
( PProposalTimingConfig
:--> PProposalStartingTime
:--> PProposalTime
:--> PBool
:--> PPeriod
:--> PTimingRelation
)
isDraftPeriod = phoistAcyclic $
plam $ \config s' -> pmatch s' $ \(PProposalStartingTime s) ->
pisCurrentTimeWithin # s # (s + (pfield @"draftTime" # config))
pgetRelation = phoistAcyclic $
plam $ \config startingTime currentTime period -> unTermCont $ do
configF <- pletAllC config
{- | True if the 'PProposalTime' is in the voting period.
PProposalStartingTime s <- pmatchC startingTime
PCurrentTime lb ub <- pmatchC currentTime
@since 0.1.0
-}
isVotingPeriod ::
forall (s :: S).
Term
s
( PProposalTimingConfig
:--> PProposalStartingTime
:--> PProposalTime
:--> PBool
)
isVotingPeriod = phoistAcyclic $
plam $ \config s' -> pmatch s' $ \(PProposalStartingTime s) ->
pletFields @'["draftTime", "votingTime"] config $ \f ->
pisCurrentTimeWithin # s # (s + f.draftTime + f.votingTime)
dub <- pletC $ s + configF.draftTime
vub <- pletC $ dub + configF.votingTime
lub <- pletC $ vub + configF.lockingTime
eub <- pletC $ lub + configF.executingTime
{- | True if the 'PProposalTime' is in the locking period.
(plb, pub) <-
pmatchC period
<&> ( \case
PDraftingPeriod -> (s, dub)
PVotingPeriod -> (dub, vub)
PLockingPeriod -> (vub, lub)
PExecutingPeriod -> (lub, eub)
)
@since 0.1.0
-}
isLockingPeriod ::
forall (s :: S).
Term
s
( PProposalTimingConfig
:--> PProposalStartingTime
:--> PProposalTime
:--> PBool
)
isLockingPeriod = phoistAcyclic $
plam $ \config s' -> pmatch s' $ \(PProposalStartingTime s) ->
pletFields @'["draftTime", "votingTime", "lockingTime"] config $ \f ->
pisCurrentTimeWithin # s # (s + f.draftTime + f.votingTime + f.lockingTime)
{- | True if the 'PProposalTime' is in the execution period.
@since 0.1.0
-}
isExecutionPeriod ::
forall (s :: S).
Term
s
( PProposalTimingConfig
:--> PProposalStartingTime
:--> PProposalTime
:--> PBool
)
isExecutionPeriod = phoistAcyclic $
plam $ \config s' -> pmatch s' $ \(PProposalStartingTime s) ->
pletFields @'["draftTime", "votingTime", "lockingTime", "executingTime"] config $ \f ->
pisCurrentTimeWithin # s
# (s + f.draftTime + f.votingTime + f.lockingTime + f.executingTime)
pure $
pif (plb #<= lb #&& ub #<= pub) (pcon PWithin) $
pif (pub #< lb) (pcon PAfter) $
ptraceError "pgetRelation: too early or invalid current time"