calculate the starting time of a proposal upon creation
This commit is contained in:
parent
2820831dad
commit
9bc2acb9ca
5 changed files with 77 additions and 31 deletions
|
|
@ -43,6 +43,7 @@ import Agora.Utils (tclet)
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
import Plutarch.Api.V1 (PPOSIXTime)
|
||||||
import Plutarch.DataRepr (
|
import Plutarch.DataRepr (
|
||||||
DerivePConstantViaData (..),
|
DerivePConstantViaData (..),
|
||||||
PDataFields,
|
PDataFields,
|
||||||
|
|
@ -55,7 +56,7 @@ import Plutarch.Unsafe (punsafeCoerce)
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
import Plutus.V1.Ledger.Api (TxOutRef)
|
import Plutus.V1.Ledger.Api (POSIXTime, TxOutRef)
|
||||||
import Plutus.V1.Ledger.Value (AssetClass (..))
|
import Plutus.V1.Ledger.Value (AssetClass (..))
|
||||||
import PlutusTx qualified
|
import PlutusTx qualified
|
||||||
|
|
||||||
|
|
@ -70,6 +71,8 @@ data GovernorDatum = GovernorDatum
|
||||||
, proposalTimings :: ProposalTimingConfig
|
, proposalTimings :: ProposalTimingConfig
|
||||||
-- ^ The timing configuration for proposals.
|
-- ^ The timing configuration for proposals.
|
||||||
-- Will get copied over upon the creation of proposals.
|
-- Will get copied over upon the creation of proposals.
|
||||||
|
, createProposalTimeRangeMaxDuration :: POSIXTime
|
||||||
|
-- ^ The maximum valid duration of a transaction that creats a proposal.
|
||||||
}
|
}
|
||||||
deriving stock (Show, GHC.Generic)
|
deriving stock (Show, GHC.Generic)
|
||||||
|
|
||||||
|
|
@ -123,6 +126,7 @@ newtype PGovernorDatum (s :: S) = PGovernorDatum
|
||||||
'[ "proposalThresholds" ':= PProposalThresholds
|
'[ "proposalThresholds" ':= PProposalThresholds
|
||||||
, "nextProposalId" ':= PProposalId
|
, "nextProposalId" ':= PProposalId
|
||||||
, "proposalTimings" ':= PProposalTimingConfig
|
, "proposalTimings" ':= PProposalTimingConfig
|
||||||
|
, "createProposalTimeRangeMaxDuration" ':= PPOSIXTime
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,12 +51,14 @@ import Agora.Proposal (
|
||||||
PResultTag,
|
PResultTag,
|
||||||
Proposal (..),
|
Proposal (..),
|
||||||
ProposalStatus (Draft, Locked),
|
ProposalStatus (Draft, Locked),
|
||||||
|
pemptyVotesFor,
|
||||||
proposalDatumValid,
|
proposalDatumValid,
|
||||||
)
|
)
|
||||||
import Agora.Proposal.Scripts (
|
import Agora.Proposal.Scripts (
|
||||||
proposalPolicy,
|
proposalPolicy,
|
||||||
proposalValidator,
|
proposalValidator,
|
||||||
)
|
)
|
||||||
|
import Agora.Proposal.Time (createProposalStartingTime)
|
||||||
import Agora.Record
|
import Agora.Record
|
||||||
import Agora.SafeMoney (GTTag)
|
import Agora.SafeMoney (GTTag)
|
||||||
import Agora.Stake (
|
import Agora.Stake (
|
||||||
|
|
@ -126,7 +128,6 @@ import Plutarch.TryFrom (ptryFrom)
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
import Agora.Proposal.Time (ProposalStartingTime (..))
|
|
||||||
import Plutus.V1.Ledger.Api (
|
import Plutus.V1.Ledger.Api (
|
||||||
CurrencySymbol (..),
|
CurrencySymbol (..),
|
||||||
MintingPolicy,
|
MintingPolicy,
|
||||||
|
|
@ -172,7 +173,7 @@ governorPolicy gov =
|
||||||
let ownAssetClass = passetClass # ownSymbol # pconstant ""
|
let ownAssetClass = passetClass # ownSymbol # pconstant ""
|
||||||
txInfo = pfromData $ pfield @"txInfo" # ctx'
|
txInfo = pfromData $ pfield @"txInfo" # ctx'
|
||||||
|
|
||||||
txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "datums"] txInfo
|
txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "datums", "validRange"] txInfo
|
||||||
|
|
||||||
tcassert "Referenced utxo should be spent" $
|
tcassert "Referenced utxo should be spent" $
|
||||||
pisUTXOSpent # oref # txInfoF.inputs
|
pisUTXOSpent # oref # txInfoF.inputs
|
||||||
|
|
@ -288,7 +289,7 @@ governorValidator gov =
|
||||||
ctxF <- tcont $ pletFields @'["txInfo", "purpose"] ctx'
|
ctxF <- tcont $ pletFields @'["txInfo", "purpose"] ctx'
|
||||||
|
|
||||||
txInfo' <- tclet $ pfromData $ ctxF.txInfo
|
txInfo' <- tclet $ pfromData $ ctxF.txInfo
|
||||||
txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "datums", "signatories"] txInfo'
|
txInfoF <- tcont $ pletFields @'["mint", "inputs", "outputs", "datums", "signatories", "validRange"] txInfo'
|
||||||
|
|
||||||
PSpending (pfromData . (pfield @"_0" #) -> ownInputRef) <- tcmatch $ pfromData ctxF.purpose
|
PSpending (pfromData . (pfield @"_0" #) -> ownInputRef) <- tcmatch $ pfromData ctxF.purpose
|
||||||
|
|
||||||
|
|
@ -300,7 +301,15 @@ governorValidator gov =
|
||||||
let ownAddress = pfromData $ ownInputF.address
|
let ownAddress = pfromData $ ownInputF.address
|
||||||
|
|
||||||
(pfromData -> (oldGovernorDatum :: Term _ PGovernorDatum), _) <- tcont $ ptryFrom datum'
|
(pfromData -> (oldGovernorDatum :: Term _ PGovernorDatum), _) <- tcont $ ptryFrom datum'
|
||||||
oldGovernorDatumF <- tcont $ pletFields @'["proposalThresholds", "nextProposalId", "proposalTimings"] oldGovernorDatum
|
oldGovernorDatumF <-
|
||||||
|
tcont $
|
||||||
|
pletFields
|
||||||
|
@'[ "proposalThresholds"
|
||||||
|
, "nextProposalId"
|
||||||
|
, "proposalTimings"
|
||||||
|
, "createProposalTimeRangeMaxDuration"
|
||||||
|
]
|
||||||
|
oldGovernorDatum
|
||||||
|
|
||||||
-- Check that GST will be returned to the governor.
|
-- Check that GST will be returned to the governor.
|
||||||
let ownInputGSTAmount = psymbolValueOf # pgstSymbol # ownInputF.value
|
let ownInputGSTAmount = psymbolValueOf # pgstSymbol # ownInputF.value
|
||||||
|
|
@ -338,6 +347,8 @@ governorValidator gov =
|
||||||
( #proposalThresholds .= oldGovernorDatumF.proposalThresholds
|
( #proposalThresholds .= oldGovernorDatumF.proposalThresholds
|
||||||
.& #nextProposalId .= pdata expectedNextProposalId
|
.& #nextProposalId .= pdata expectedNextProposalId
|
||||||
.& #proposalTimings .= oldGovernorDatumF.proposalTimings
|
.& #proposalTimings .= oldGovernorDatumF.proposalTimings
|
||||||
|
.& #createProposalTimeRangeMaxDuration
|
||||||
|
.= oldGovernorDatumF.createProposalTimeRangeMaxDuration
|
||||||
)
|
)
|
||||||
tcassert "Unexpected governor state datum" $
|
tcassert "Unexpected governor state datum" $
|
||||||
newGovernorDatum #== expectedNewDatum
|
newGovernorDatum #== expectedNewDatum
|
||||||
|
|
@ -403,9 +414,6 @@ governorValidator gov =
|
||||||
|
|
||||||
outputDatumHash <- tclet $ pfield @"datumHash" #$ phead # outputsToProposalValidatorWithStateToken
|
outputDatumHash <- tclet $ pfield @"datumHash" #$ phead # outputsToProposalValidatorWithStateToken
|
||||||
|
|
||||||
tcassert "The utxo paid to the proposal validator must have datum" $
|
|
||||||
pisDJust # outputDatumHash
|
|
||||||
|
|
||||||
proposalOutputDatum' <-
|
proposalOutputDatum' <-
|
||||||
tclet $
|
tclet $
|
||||||
mustFindDatum' @PProposalDatum
|
mustFindDatum' @PProposalDatum
|
||||||
|
|
@ -418,23 +426,34 @@ governorValidator gov =
|
||||||
proposalOutputDatum <-
|
proposalOutputDatum <-
|
||||||
tcont $
|
tcont $
|
||||||
pletFields
|
pletFields
|
||||||
@'["proposalId", "status", "cosigners", "thresholds", "votes"]
|
@'["effects", "cosigners", "proposalId", "votes"]
|
||||||
proposalOutputDatum'
|
proposalOutputDatum'
|
||||||
|
|
||||||
-- Id and thresholds should be copied from the old governor state datum.
|
|
||||||
tcassert "Invalid proposal id in proposal datum" $
|
|
||||||
proposalOutputDatum.proposalId #== oldGovernorDatumF.nextProposalId
|
|
||||||
|
|
||||||
tcassert "Invalid thresholds in proposal datum" $
|
|
||||||
proposalOutputDatum.thresholds #== oldGovernorDatumF.proposalThresholds
|
|
||||||
|
|
||||||
-- The proposal at this point should be in draft state.
|
|
||||||
tcassert "Proposal state should be draft" $
|
|
||||||
proposalOutputDatum.status #== pconstantData Draft
|
|
||||||
|
|
||||||
tcassert "Proposal should have only one cosigner" $
|
tcassert "Proposal should have only one cosigner" $
|
||||||
plength # pfromData proposalOutputDatum.cosigners #== 1
|
plength # pfromData proposalOutputDatum.cosigners #== 1
|
||||||
|
|
||||||
|
let -- Votes should be empty at this point
|
||||||
|
expectedVotes = pemptyVotesFor # pfromData proposalOutputDatum.effects
|
||||||
|
expectedStartingTime =
|
||||||
|
createProposalStartingTime
|
||||||
|
# oldGovernorDatumF.createProposalTimeRangeMaxDuration
|
||||||
|
# txInfoF.validRange
|
||||||
|
-- Id, thresholds and timings should be copied from the old governor state datum.
|
||||||
|
expectedProposalOut =
|
||||||
|
mkRecordConstr
|
||||||
|
PProposalDatum
|
||||||
|
( #proposalId .= oldGovernorDatumF.nextProposalId
|
||||||
|
.& #effects .= proposalOutputDatum.effects
|
||||||
|
.& #status .= pconstantData Draft
|
||||||
|
.& #cosigners .= proposalOutputDatum.cosigners
|
||||||
|
.& #thresholds .= oldGovernorDatumF.proposalThresholds
|
||||||
|
.& #votes .= pdata expectedVotes
|
||||||
|
.& #timingConfig .= oldGovernorDatumF.proposalTimings
|
||||||
|
.& #startingTime .= pdata expectedStartingTime
|
||||||
|
)
|
||||||
|
|
||||||
|
tcassert "Datum correct" $ expectedProposalOut #== proposalOutputDatum'
|
||||||
|
|
||||||
let cosigner = phead # pfromData proposalOutputDatum.cosigners
|
let cosigner = phead # pfromData proposalOutputDatum.cosigners
|
||||||
|
|
||||||
tcassert "Cosigner should be the stake owner" $
|
tcassert "Cosigner should be the stake owner" $
|
||||||
|
|
@ -561,7 +580,7 @@ governorValidator gov =
|
||||||
|
|
||||||
proposalInputDatumF <-
|
proposalInputDatumF <-
|
||||||
tcont $
|
tcont $
|
||||||
pletFields @'["proposalId", "effects", "status", "cosigners", "thresholds", "votes"]
|
pletFields @'["proposalId", "effects", "status", "cosigners", "thresholds", "votes", "timingConfig", "startingTime"]
|
||||||
proposalInputDatum
|
proposalInputDatum
|
||||||
|
|
||||||
-- Check that the proposal state is advanced so that a proposal cannot be executed twice.
|
-- Check that the proposal state is advanced so that a proposal cannot be executed twice.
|
||||||
|
|
@ -578,10 +597,8 @@ governorValidator gov =
|
||||||
.& #cosigners .= proposalInputDatumF.cosigners
|
.& #cosigners .= proposalInputDatumF.cosigners
|
||||||
.& #thresholds .= proposalInputDatumF.thresholds
|
.& #thresholds .= proposalInputDatumF.thresholds
|
||||||
.& #votes .= proposalInputDatumF.votes
|
.& #votes .= proposalInputDatumF.votes
|
||||||
-- FIXME: copy from the governor datum
|
.& #timingConfig .= proposalInputDatumF.timingConfig
|
||||||
.& #timingConfig .= oldGovernorDatumF.proposalTimings
|
.& #startingTime .= proposalInputDatumF.startingTime
|
||||||
-- FIXME: calculate from 'txInfoValidRange'
|
|
||||||
.& #startingTime .= pdata (pconstant tmpProposalStartingTime)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
tcassert "Unexpected output proposal datum" $
|
tcassert "Unexpected output proposal datum" $
|
||||||
|
|
@ -733,10 +750,6 @@ governorValidator gov =
|
||||||
let sym = governorSTSymbolFromGovernor gov
|
let sym = governorSTSymbolFromGovernor gov
|
||||||
in phoistAcyclic $ pconstant sym
|
in phoistAcyclic $ pconstant sym
|
||||||
|
|
||||||
-- TODO: remove this.
|
|
||||||
tmpProposalStartingTime :: ProposalStartingTime
|
|
||||||
tmpProposalStartingTime = ProposalStartingTime 0
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- | Get the 'CurrencySymbol' of GST.
|
-- | Get the 'CurrencySymbol' of GST.
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ module Agora.Proposal (
|
||||||
|
|
||||||
-- * Plutarch helpers
|
-- * Plutarch helpers
|
||||||
proposalDatumValid,
|
proposalDatumValid,
|
||||||
|
pemptyVotesFor,
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import GHC.Generics qualified as GHC
|
import GHC.Generics qualified as GHC
|
||||||
|
|
@ -47,7 +48,7 @@ import PlutusTx.AssocMap qualified as AssocMap
|
||||||
|
|
||||||
import Agora.Proposal.Time (PProposalStartingTime, PProposalTimingConfig, ProposalStartingTime, ProposalTimingConfig)
|
import Agora.Proposal.Time (PProposalStartingTime, PProposalTimingConfig, ProposalStartingTime, ProposalTimingConfig)
|
||||||
import Agora.SafeMoney (GTTag)
|
import Agora.SafeMoney (GTTag)
|
||||||
import Agora.Utils (pkeysEqual, pnotNull)
|
import Agora.Utils (pkeysEqual, pmapMap, pnotNull)
|
||||||
import Control.Applicative (Const)
|
import Control.Applicative (Const)
|
||||||
import Control.Arrow (first)
|
import Control.Arrow (first)
|
||||||
import Plutarch.DataRepr (DerivePConstantViaData (..), PDataFields, PIsDataReprInstances (..))
|
import Plutarch.DataRepr (DerivePConstantViaData (..), PDataFields, PIsDataReprInstances (..))
|
||||||
|
|
@ -348,6 +349,16 @@ deriving via
|
||||||
instance
|
instance
|
||||||
(PConstantDecl ProposalVotes)
|
(PConstantDecl ProposalVotes)
|
||||||
|
|
||||||
|
-- Plutarch version of 'pemptyVotesFor'.
|
||||||
|
pemptyVotesFor :: forall s a. (PIsData a) => Term s (PMap PResultTag a :--> PProposalVotes)
|
||||||
|
pemptyVotesFor =
|
||||||
|
phoistAcyclic $
|
||||||
|
plam
|
||||||
|
( \m ->
|
||||||
|
pcon $
|
||||||
|
PProposalVotes $ pmapMap # plam (const $ pconstant 0) # m
|
||||||
|
)
|
||||||
|
|
||||||
-- | Plutarch-level version of 'ProposalDatum'.
|
-- | Plutarch-level version of 'ProposalDatum'.
|
||||||
newtype PProposalDatum (s :: S) = PProposalDatum
|
newtype PProposalDatum (s :: S) = PProposalDatum
|
||||||
{ getProposalDatum ::
|
{ getProposalDatum ::
|
||||||
|
|
|
||||||
|
|
@ -198,7 +198,7 @@ createProposalStartingTime = phoistAcyclic $
|
||||||
# (currentTimeF.lowerBound + currentTimeF.upperBound)
|
# (currentTimeF.lowerBound + currentTimeF.upperBound)
|
||||||
# 2
|
# 2
|
||||||
|
|
||||||
tcassert "Given time range should be tight enough" $
|
tcassert "createProposalStartingTime: given time range should be tight enough" $
|
||||||
duration #<= maxDuration
|
duration #<= maxDuration
|
||||||
|
|
||||||
pure $ pcon $ PProposalStartingTime startingTime
|
pure $ pcon $ PProposalStartingTime startingTime
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ module Agora.Utils (
|
||||||
pmsort,
|
pmsort,
|
||||||
pnubSort,
|
pnubSort,
|
||||||
pupdate,
|
pupdate,
|
||||||
|
pmapMap,
|
||||||
pmapMaybe,
|
pmapMaybe,
|
||||||
|
|
||||||
-- * Functions which should (probably) not be upstreamed
|
-- * Functions which should (probably) not be upstreamed
|
||||||
|
|
@ -322,6 +323,23 @@ pupdate = phoistAcyclic $
|
||||||
)
|
)
|
||||||
# ps
|
# ps
|
||||||
|
|
||||||
|
-- | / O(n) /. Map a function over all values in a 'PMap'.
|
||||||
|
pmapMap :: forall s k a b. (PIsData k, PIsData a, PIsData b) => Term s ((a :--> b) :--> PMap k a :--> PMap k b)
|
||||||
|
pmapMap = phoistAcyclic $
|
||||||
|
plam $ \f (pto -> (ps :: Term _ (PBuiltinList _))) ->
|
||||||
|
pcon $
|
||||||
|
PMap $
|
||||||
|
pmap
|
||||||
|
# plam
|
||||||
|
( \kv ->
|
||||||
|
let k = pfstBuiltin # kv
|
||||||
|
v = psndBuiltin # kv
|
||||||
|
|
||||||
|
nv = pdata $ f # pfromData v
|
||||||
|
in ppairDataBuiltin # k # nv
|
||||||
|
)
|
||||||
|
# ps
|
||||||
|
|
||||||
-- | Add two 'PValue's together.
|
-- | Add two 'PValue's together.
|
||||||
paddValue :: forall s. Term s (PValue :--> PValue :--> PValue)
|
paddValue :: forall s. Term s (PValue :--> PValue :--> PValue)
|
||||||
paddValue = phoistAcyclic $
|
paddValue = phoistAcyclic $
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue