Merge pull request #93 from Liqwid-Labs/connor/proposal-timing-conf

Proposal Timings
This commit is contained in:
Emily 2022-05-26 09:48:19 +02:00 committed by GitHub
commit dbfff95bfa
13 changed files with 344 additions and 175 deletions

View file

@ -11,14 +11,22 @@ module Sample.Effect.GovernorMutation (
mkEffectDatum, mkEffectDatum,
) where ) where
--------------------------------------------------------------------------------
import Agora.Effect.GovernorMutation ( import Agora.Effect.GovernorMutation (
MutateGovernorDatum (..), MutateGovernorDatum (..),
mutateGovernorValidator, mutateGovernorValidator,
) )
import Agora.Governor (GovernorDatum (..)) import Agora.Governor (GovernorDatum (..))
import Agora.Proposal (ProposalId (..), ProposalThresholds (..)) import Agora.Proposal (ProposalId (..), ProposalThresholds (..))
--------------------------------------------------------------------------------
import Plutarch.Api.V1 (mkValidator, validatorHash) import Plutarch.Api.V1 (mkValidator, validatorHash)
import Plutarch.SafeMoney (Tagged (Tagged)) import Plutarch.SafeMoney (Tagged (Tagged))
--------------------------------------------------------------------------------
import Plutus.V1.Ledger.Address (scriptHashAddress) import Plutus.V1.Ledger.Address (scriptHashAddress)
import Plutus.V1.Ledger.Api ( import Plutus.V1.Ledger.Api (
Address, Address,
@ -35,6 +43,9 @@ import Plutus.V1.Ledger.Api (
import Plutus.V1.Ledger.Api qualified as Interval import Plutus.V1.Ledger.Api qualified as Interval
import Plutus.V1.Ledger.Value (AssetClass, assetClass) import Plutus.V1.Ledger.Value (AssetClass, assetClass)
import Plutus.V1.Ledger.Value qualified as Value import Plutus.V1.Ledger.Value qualified as Value
--------------------------------------------------------------------------------
import Sample.Shared ( import Sample.Shared (
authorityTokenSymbol, authorityTokenSymbol,
defaultProposalThresholds, defaultProposalThresholds,
@ -46,6 +57,12 @@ import Sample.Shared (
) )
import Test.Util (datumPair, toDatumHash) import Test.Util (datumPair, toDatumHash)
--------------------------------------------------------------------------------
import Data.Default.Class (Default (def))
--------------------------------------------------------------------------------
-- | The effect validator instance. -- | The effect validator instance.
effectValidator :: Validator effectValidator :: Validator
effectValidator = mkValidator $ mutateGovernorValidator governor effectValidator = mkValidator $ mutateGovernorValidator governor
@ -101,6 +118,8 @@ mkEffectTxInfo newGovDatum =
GovernorDatum GovernorDatum
{ proposalThresholds = defaultProposalThresholds { proposalThresholds = defaultProposalThresholds
, nextProposalId = ProposalId 0 , nextProposalId = ProposalId 0
, proposalTimings = def
, createProposalTimeRangeMaxWidth = def
} }
governorInputDatum :: Datum governorInputDatum :: Datum
governorInputDatum = Datum $ toBuiltinData governorInputDatum' governorInputDatum = Datum $ toBuiltinData governorInputDatum'
@ -161,6 +180,8 @@ validNewGovernorDatum =
GovernorDatum GovernorDatum
{ proposalThresholds = defaultProposalThresholds { proposalThresholds = defaultProposalThresholds
, nextProposalId = ProposalId 42 , nextProposalId = ProposalId 42
, proposalTimings = def
, createProposalTimeRangeMaxWidth = def
} }
invalidNewGovernorDatum :: GovernorDatum invalidNewGovernorDatum :: GovernorDatum
@ -171,4 +192,6 @@ invalidNewGovernorDatum =
{ countVoting = Tagged (-1) { countVoting = Tagged (-1)
} }
, nextProposalId = ProposalId 42 , nextProposalId = ProposalId 42
, proposalTimings = def
, createProposalTimeRangeMaxWidth = def
} }

View file

@ -56,6 +56,10 @@ import Agora.Proposal (
emptyVotesFor, emptyVotesFor,
) )
import Agora.Proposal qualified as P import Agora.Proposal qualified as P
import Agora.Proposal.Time (
ProposalStartingTime (ProposalStartingTime),
ProposalTimingConfig (..),
)
import Agora.Stake ( import Agora.Stake (
ProposalLock (..), ProposalLock (..),
Stake (..), Stake (..),
@ -73,16 +77,19 @@ import Sample.Shared (
gstUTXORef, gstUTXORef,
minAda, minAda,
proposalPolicySymbol, proposalPolicySymbol,
proposalTimingConfig, proposalStartingTimeFromTimeRange,
proposalValidatorAddress, proposalValidatorAddress,
signer, signer,
signer2, signer2,
stake, stake,
stakeAddress, stakeAddress,
stakeAssetClass, stakeAssetClass,
tmpProposalStartingTime,
) )
import Test.Util (datumPair, toDatumHash) import Test.Util (closedBoundedInterval, datumPair, toDatumHash)
--------------------------------------------------------------------------------
import Data.Default.Class (Default (def))
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
@ -114,6 +121,8 @@ mintGST =
GovernorDatum GovernorDatum
{ proposalThresholds = defaultProposalThresholds { proposalThresholds = defaultProposalThresholds
, nextProposalId = ProposalId 0 , nextProposalId = ProposalId 0
, proposalTimings = def
, createProposalTimeRangeMaxWidth = def
} }
governorOutputDatum :: Datum governorOutputDatum :: Datum
governorOutputDatum = Datum $ toBuiltinData governorOutputDatum' governorOutputDatum = Datum $ toBuiltinData governorOutputDatum'
@ -207,6 +216,8 @@ createProposal =
GovernorDatum GovernorDatum
{ proposalThresholds = defaultProposalThresholds { proposalThresholds = defaultProposalThresholds
, nextProposalId = thisProposalId , nextProposalId = thisProposalId
, proposalTimings = def
, createProposalTimeRangeMaxWidth = def
} }
governorInputDatum :: Datum governorInputDatum :: Datum
governorInputDatum = Datum $ toBuiltinData governorInputDatum' governorInputDatum = Datum $ toBuiltinData governorInputDatum'
@ -236,8 +247,8 @@ createProposal =
, cosigners = [signer] , cosigners = [signer]
, thresholds = defaultProposalThresholds , thresholds = defaultProposalThresholds
, votes = emptyVotesFor effects , votes = emptyVotesFor effects
, timingConfig = proposalTimingConfig , timingConfig = def
, startingTime = tmpProposalStartingTime , startingTime = proposalStartingTimeFromTimeRange validTimeRange
} }
) )
proposalOutput :: TxOut proposalOutput :: TxOut
@ -298,8 +309,13 @@ createProposal =
} }
--- ---
ownInputRef :: TxOutRef ownInputRef :: TxOutRef
ownInputRef = TxOutRef "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865" 1 ownInputRef = TxOutRef "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865" 1
---
validTimeRange = closedBoundedInterval 10 15
in ScriptContext in ScriptContext
{ scriptContextTxInfo = { scriptContextTxInfo =
TxInfo TxInfo
@ -316,7 +332,7 @@ createProposal =
, txInfoMint = pst , txInfoMint = pst
, txInfoDCert = [] , txInfoDCert = []
, txInfoWdrl = [] , txInfoWdrl = []
, txInfoValidRange = Interval.always , txInfoValidRange = validTimeRange
, txInfoSignatories = [signer] , txInfoSignatories = [signer]
, txInfoData = , txInfoData =
datumPair datumPair
@ -378,6 +394,8 @@ mintGATs =
GovernorDatum GovernorDatum
{ proposalThresholds = defaultProposalThresholds { proposalThresholds = defaultProposalThresholds
, nextProposalId = ProposalId 5 , nextProposalId = ProposalId 5
, proposalTimings = def
, createProposalTimeRangeMaxWidth = def
} }
governorInputDatum :: Datum governorInputDatum :: Datum
governorInputDatum = Datum $ toBuiltinData governorInputDatum' governorInputDatum = Datum $ toBuiltinData governorInputDatum'
@ -412,8 +430,8 @@ mintGATs =
, cosigners = [signer, signer2] , cosigners = [signer, signer2]
, thresholds = defaultProposalThresholds , thresholds = defaultProposalThresholds
, votes = proposalVotes , votes = proposalVotes
, timingConfig = proposalTimingConfig , timingConfig = def
, startingTime = tmpProposalStartingTime , startingTime = ProposalStartingTime 10
} }
proposalInputDatum :: Datum proposalInputDatum :: Datum
proposalInputDatum = Datum $ toBuiltinData proposalInputDatum' proposalInputDatum = Datum $ toBuiltinData proposalInputDatum'
@ -465,6 +483,12 @@ mintGATs =
ownInputRef :: TxOutRef ownInputRef :: TxOutRef
ownInputRef = TxOutRef "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865" 1 ownInputRef = TxOutRef "4355a46b19d348dc2f57c046f8ef63d4538ebb936000f3c9ee954a27460dd865" 1
--
validTimeRange =
closedBoundedInterval
((def :: ProposalTimingConfig).lockingTime + 11)
((def :: ProposalTimingConfig).executingTime - 11)
in ScriptContext in ScriptContext
{ scriptContextTxInfo = { scriptContextTxInfo =
TxInfo TxInfo
@ -483,7 +507,7 @@ mintGATs =
, txInfoMint = gat , txInfoMint = gat
, txInfoDCert = [] , txInfoDCert = []
, txInfoWdrl = [] , txInfoWdrl = []
, txInfoValidRange = Interval.always , txInfoValidRange = validTimeRange
, txInfoSignatories = [signer, signer2] , txInfoSignatories = [signer, signer2]
, txInfoData = , txInfoData =
datumPair datumPair
@ -565,6 +589,8 @@ mutateState =
GovernorDatum GovernorDatum
{ proposalThresholds = defaultProposalThresholds { proposalThresholds = defaultProposalThresholds
, nextProposalId = ProposalId 5 , nextProposalId = ProposalId 5
, proposalTimings = def
, createProposalTimeRangeMaxWidth = def
} }
governorInputDatum :: Datum governorInputDatum :: Datum
governorInputDatum = Datum $ toBuiltinData governorInputDatum' governorInputDatum = Datum $ toBuiltinData governorInputDatum'

View file

@ -16,9 +16,14 @@ module Sample.Proposal (
) where ) where
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
import Plutarch.Api.V1 ( import Plutarch.Api.V1 (
validatorHash, validatorHash,
) )
import Plutarch.SafeMoney (Tagged (Tagged), untag)
--------------------------------------------------------------------------------
import Plutus.V1.Ledger.Api ( import Plutus.V1.Ledger.Api (
Address (Address), Address (Address),
Credential (ScriptCredential), Credential (ScriptCredential),
@ -33,13 +38,13 @@ import Plutus.V1.Ledger.Api (
TxOut (TxOut, txOutAddress, txOutDatumHash, txOutValue), TxOut (TxOut, txOutAddress, txOutDatumHash, txOutValue),
TxOutRef (TxOutRef), TxOutRef (TxOutRef),
) )
import Plutus.V1.Ledger.Interval qualified as Interval
import Plutus.V1.Ledger.Value qualified as Value import Plutus.V1.Ledger.Value qualified as Value
import PlutusTx.AssocMap qualified as AssocMap
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
import Agora.Governor ( import Agora.Governor (
GovernorDatum (GovernorDatum, nextProposalId, proposalThresholds), GovernorDatum (..),
) )
import Agora.Proposal ( import Agora.Proposal (
Proposal (..), Proposal (..),
@ -50,15 +55,20 @@ import Agora.Proposal (
ResultTag (..), ResultTag (..),
emptyVotesFor, emptyVotesFor,
) )
import Agora.Proposal.Time (ProposalTimingConfig (..)) import Agora.Proposal.Time (ProposalStartingTime (ProposalStartingTime), ProposalTimingConfig (..))
import Agora.Stake (ProposalLock (ProposalLock), Stake (..), StakeDatum (..)) import Agora.Stake (ProposalLock (ProposalLock), Stake (..), StakeDatum (..))
import Plutarch.SafeMoney (Tagged (Tagged), untag)
import PlutusTx.AssocMap qualified as AssocMap --------------------------------------------------------------------------------
import Sample.Shared import Sample.Shared
import Test.Util (closedBoundedInterval, datumPair, toDatumHash, updateMap) import Test.Util (closedBoundedInterval, datumPair, toDatumHash, updateMap)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
import Data.Default.Class (Default (def))
--------------------------------------------------------------------------------
-- | This script context should be a valid transaction. -- | This script context should be a valid transaction.
proposalCreation :: ScriptContext proposalCreation :: ScriptContext
proposalCreation = proposalCreation =
@ -79,8 +89,8 @@ proposalCreation =
, cosigners = [signer] , cosigners = [signer]
, thresholds = defaultProposalThresholds , thresholds = defaultProposalThresholds
, votes = emptyVotesFor effects , votes = emptyVotesFor effects
, timingConfig = proposalTimingConfig , timingConfig = def
, startingTime = tmpProposalStartingTime , startingTime = proposalStartingTimeFromTimeRange validTimeRange
} }
) )
@ -91,6 +101,8 @@ proposalCreation =
GovernorDatum GovernorDatum
{ proposalThresholds = defaultProposalThresholds { proposalThresholds = defaultProposalThresholds
, nextProposalId = ProposalId 0 , nextProposalId = ProposalId 0
, proposalTimings = def
, createProposalTimeRangeMaxWidth = def
} }
) )
govAfter :: Datum govAfter :: Datum
@ -100,8 +112,12 @@ proposalCreation =
GovernorDatum GovernorDatum
{ proposalThresholds = defaultProposalThresholds { proposalThresholds = defaultProposalThresholds
, nextProposalId = ProposalId 1 , nextProposalId = ProposalId 1
, proposalTimings = def
, createProposalTimeRangeMaxWidth = def
} }
) )
validTimeRange = closedBoundedInterval 10 15
in ScriptContext in ScriptContext
{ scriptContextTxInfo = { scriptContextTxInfo =
TxInfo TxInfo
@ -138,7 +154,7 @@ proposalCreation =
, txInfoMint = st , txInfoMint = st
, txInfoDCert = [] , txInfoDCert = []
, txInfoWdrl = [] , txInfoWdrl = []
, txInfoValidRange = Interval.always , txInfoValidRange = validTimeRange
, txInfoSignatories = [signer] , txInfoSignatories = [signer]
, txInfoData = , txInfoData =
[ datumPair proposalDatum [ datumPair proposalDatum
@ -174,8 +190,8 @@ cosignProposal newSigners =
, cosigners = [signer] , cosigners = [signer]
, thresholds = defaultProposalThresholds , thresholds = defaultProposalThresholds
, votes = emptyVotesFor effects , votes = emptyVotesFor effects
, timingConfig = proposalTimingConfig , timingConfig = def
, startingTime = tmpProposalStartingTime , startingTime = ProposalStartingTime 0
} }
stakeDatum :: StakeDatum stakeDatum :: StakeDatum
stakeDatum = StakeDatum (Tagged 50_000_000) signer2 [] stakeDatum = StakeDatum (Tagged 50_000_000) signer2 []
@ -185,7 +201,7 @@ cosignProposal newSigners =
validTimeRange = validTimeRange =
closedBoundedInterval closedBoundedInterval
10 10
(proposalTimingConfig.draftTime - 10) ((def :: ProposalTimingConfig).draftTime - 10)
in TxInfo in TxInfo
{ txInfoInputs = { txInfoInputs =
[ TxInInfo [ TxInInfo
@ -295,8 +311,8 @@ voteOnProposal params =
, cosigners = [stakeOwner] , cosigners = [stakeOwner]
, thresholds = defaultProposalThresholds , thresholds = defaultProposalThresholds
, votes = ProposalVotes initialVotes , votes = ProposalVotes initialVotes
, timingConfig = proposalTimingConfig , timingConfig = def
, startingTime = tmpProposalStartingTime , startingTime = ProposalStartingTime 0
} }
proposalInputDatum :: Datum proposalInputDatum :: Datum
proposalInputDatum = Datum $ toBuiltinData proposalInputDatum' proposalInputDatum = Datum $ toBuiltinData proposalInputDatum'
@ -384,7 +400,9 @@ voteOnProposal params =
--- ---
validTimeRange = validTimeRange =
closedBoundedInterval (proposalTimingConfig.draftTime + 1) (proposalTimingConfig.votingTime - 1) closedBoundedInterval
((def :: ProposalTimingConfig).draftTime + 1)
((def :: ProposalTimingConfig).votingTime - 1)
in TxInfo in TxInfo
{ txInfoInputs = { txInfoInputs =
[ TxInInfo proposalRef proposalInput [ TxInInfo proposalRef proposalInput

View file

@ -1,3 +1,5 @@
{-# OPTIONS_GHC -Wno-orphans #-}
{- | {- |
Module : Sample.Shared Module : Sample.Shared
Maintainer : emi@haskell.fyi Maintainer : emi@haskell.fyi
@ -36,8 +38,7 @@ module Sample.Shared (
proposalPolicySymbol, proposalPolicySymbol,
proposalValidatorHash, proposalValidatorHash,
proposalValidatorAddress, proposalValidatorAddress,
proposalTimingConfig, proposalStartingTimeFromTimeRange,
tmpProposalStartingTime,
-- ** Authority -- ** Authority
authorityToken, authorityToken,
@ -77,12 +78,14 @@ import Agora.Proposal (
ProposalThresholds (..), ProposalThresholds (..),
) )
import Agora.Proposal.Time ( import Agora.Proposal.Time (
ProposalStartingTime (..), MaxTimeRangeWidth (..),
ProposalStartingTime (ProposalStartingTime),
ProposalTimingConfig (..), ProposalTimingConfig (..),
) )
import Agora.Stake (Stake (..)) import Agora.Stake (Stake (..))
import Agora.Treasury (treasuryValidator) import Agora.Treasury (treasuryValidator)
import Agora.Utils (validatorHashToTokenName) import Agora.Utils (validatorHashToTokenName)
import Data.Default.Class (Default (..))
import Plutarch.Api.V1 ( import Plutarch.Api.V1 (
mintingPolicySymbol, mintingPolicySymbol,
mkMintingPolicy, mkMintingPolicy,
@ -95,9 +98,14 @@ import Plutus.V1.Ledger.Api (
Address (Address), Address (Address),
Credential (ScriptCredential), Credential (ScriptCredential),
CurrencySymbol, CurrencySymbol,
Extended (..),
Interval (..),
LowerBound (..),
MintingPolicy (..), MintingPolicy (..),
POSIXTimeRange,
PubKeyHash, PubKeyHash,
TxOutRef (TxOutRef), TxOutRef (TxOutRef),
UpperBound (..),
Value, Value,
) )
import Plutus.V1.Ledger.Contexts ( import Plutus.V1.Ledger.Contexts (
@ -190,21 +198,32 @@ authorityToken = authorityTokenFromGovernor governor
authorityTokenSymbol :: CurrencySymbol authorityTokenSymbol :: CurrencySymbol
authorityTokenSymbol = authorityTokenSymbolFromGovernor governor authorityTokenSymbol = authorityTokenSymbolFromGovernor governor
proposalTimingConfig :: ProposalTimingConfig {- | Default value of 'Agora.Governor.GovernorDatum.proposalTimings'.
proposalTimingConfig = For testing purpose only.
ProposalTimingConfig
{ draftTime = 50
, votingTime = 1000
, lockingTime = 2000
, executingTime = 3000
}
{- | Hard coded starting time of every propoal.
This will be calculated by the governor in the future.
FIXME: Remove this.
-} -}
tmpProposalStartingTime :: ProposalStartingTime instance Default ProposalTimingConfig where
tmpProposalStartingTime = ProposalStartingTime 0 def =
ProposalTimingConfig
{ draftTime = 50
, votingTime = 1000
, lockingTime = 2000
, executingTime = 3000
}
{- | Default value of 'Agora.Governor.GovernorDatum.createProposalTimeRangeMaxWidth'.
For testing purpose only.
-}
instance Default MaxTimeRangeWidth where
def = MaxTimeRangeWidth 10
{- | Get the starting time of a proposal, given a closed finite time range.
Tightness of the time range is not checked. See 'Agora.Proposal.Time.createProposalStartingTime'.
-}
proposalStartingTimeFromTimeRange :: POSIXTimeRange -> ProposalStartingTime
proposalStartingTimeFromTimeRange
(Interval (LowerBound (Finite l) True) (UpperBound (Finite u) True)) =
ProposalStartingTime $ (l + u) `div` 2
proposalStartingTimeFromTimeRange _ = error "Given time range should be finite and closed"
------------------------------------------------------------------ ------------------------------------------------------------------

View file

@ -4,6 +4,7 @@ import Agora.Effect.GovernorMutation (mutateGovernorValidator)
import Agora.Governor (GovernorDatum (..), GovernorRedeemer (MutateGovernor)) import Agora.Governor (GovernorDatum (..), GovernorRedeemer (MutateGovernor))
import Agora.Governor.Scripts (governorValidator) import Agora.Governor.Scripts (governorValidator)
import Agora.Proposal (ProposalId (..)) import Agora.Proposal (ProposalId (..))
import Data.Default.Class (Default (def))
import Plutus.V1.Ledger.Api (ScriptContext (ScriptContext), ScriptPurpose (Spending)) import Plutus.V1.Ledger.Api (ScriptContext (ScriptContext), ScriptPurpose (Spending))
import Sample.Effect.GovernorMutation ( import Sample.Effect.GovernorMutation (
effectRef, effectRef,
@ -27,9 +28,10 @@ tests =
"governor validator should pass" "governor validator should pass"
(governorValidator Shared.governor) (governorValidator Shared.governor)
( GovernorDatum ( GovernorDatum
{ proposalThresholds = Shared.defaultProposalThresholds Shared.defaultProposalThresholds
, nextProposalId = ProposalId 0 (ProposalId 0)
} def
def
) )
MutateGovernor MutateGovernor
( ScriptContext ( ScriptContext
@ -48,9 +50,10 @@ tests =
"governor validator should fail" "governor validator should fail"
(governorValidator Shared.governor) (governorValidator Shared.governor)
( GovernorDatum ( GovernorDatum
{ proposalThresholds = Shared.defaultProposalThresholds Shared.defaultProposalThresholds
, nextProposalId = ProposalId 0 (ProposalId 0)
} def
def
) )
MutateGovernor MutateGovernor
( ScriptContext ( ScriptContext

View file

@ -16,6 +16,7 @@ module Spec.Governor (tests) where
import Agora.Governor (GovernorDatum (..), GovernorRedeemer (..)) import Agora.Governor (GovernorDatum (..), GovernorRedeemer (..))
import Agora.Governor.Scripts (governorPolicy, governorValidator) import Agora.Governor.Scripts (governorPolicy, governorValidator)
import Agora.Proposal (ProposalId (..)) import Agora.Proposal (ProposalId (..))
import Data.Default.Class (Default (def))
import Sample.Governor (createProposal, mintGATs, mintGST, mutateState) import Sample.Governor (createProposal, mintGATs, mintGST, mutateState)
import Sample.Shared qualified as Shared import Sample.Shared qualified as Shared
import Test.Tasty (TestTree, testGroup) import Test.Tasty (TestTree, testGroup)
@ -38,19 +39,34 @@ tests =
[ validatorSucceedsWith [ validatorSucceedsWith
"proposal creation" "proposal creation"
(governorValidator Shared.governor) (governorValidator Shared.governor)
(GovernorDatum Shared.defaultProposalThresholds (ProposalId 0)) ( GovernorDatum
Shared.defaultProposalThresholds
(ProposalId 0)
def
def
)
CreateProposal CreateProposal
createProposal createProposal
, validatorSucceedsWith , validatorSucceedsWith
"GATs minting" "GATs minting"
(governorValidator Shared.governor) (governorValidator Shared.governor)
(GovernorDatum Shared.defaultProposalThresholds (ProposalId 5)) ( GovernorDatum
Shared.defaultProposalThresholds
(ProposalId 5)
def
def
)
MintGATs MintGATs
mintGATs mintGATs
, validatorSucceedsWith , validatorSucceedsWith
"mutate governor state" "mutate governor state"
(governorValidator Shared.governor) (governorValidator Shared.governor)
(GovernorDatum Shared.defaultProposalThresholds (ProposalId 5)) ( GovernorDatum
Shared.defaultProposalThresholds
(ProposalId 5)
def
def
)
MutateGovernor MutateGovernor
mutateState mutateState
] ]

View file

@ -31,12 +31,14 @@ import Agora.Proposal.Scripts (
proposalPolicy, proposalPolicy,
proposalValidator, proposalValidator,
) )
import Agora.Proposal.Time (ProposalStartingTime (ProposalStartingTime))
import Agora.Stake ( import Agora.Stake (
ProposalLock (ProposalLock), ProposalLock (ProposalLock),
StakeDatum (StakeDatum), StakeDatum (StakeDatum),
StakeRedeemer (PermitVote, WitnessStake), StakeRedeemer (PermitVote, WitnessStake),
) )
import Agora.Stake.Scripts (stakeValidator) import Agora.Stake.Scripts (stakeValidator)
import Data.Default.Class (Default (def))
import Plutarch.SafeMoney (Tagged (Tagged)) import Plutarch.SafeMoney (Tagged (Tagged))
import Plutus.V1.Ledger.Api (ScriptContext (..), ScriptPurpose (..)) import Plutus.V1.Ledger.Api (ScriptContext (..), ScriptPurpose (..))
import PlutusTx.AssocMap qualified as AssocMap import PlutusTx.AssocMap qualified as AssocMap
@ -82,8 +84,8 @@ tests =
[ (ResultTag 0, AssocMap.empty) [ (ResultTag 0, AssocMap.empty)
, (ResultTag 1, AssocMap.empty) , (ResultTag 1, AssocMap.empty)
] ]
, timingConfig = Shared.proposalTimingConfig , timingConfig = def
, startingTime = Shared.tmpProposalStartingTime , startingTime = ProposalStartingTime 0
} }
) )
(Cosign [signer2]) (Cosign [signer2])
@ -117,8 +119,8 @@ tests =
, (ResultTag 1, 4242) , (ResultTag 1, 4242)
] ]
) )
, timingConfig = Shared.proposalTimingConfig , timingConfig = def
, startingTime = Shared.tmpProposalStartingTime , startingTime = ProposalStartingTime 0
} }
) )
(Vote (ResultTag 0)) (Vote (ResultTag 0))

View file

@ -114,6 +114,7 @@ common test-deps
, agora , agora
, apropos , apropos
, apropos-tx , apropos-tx
, data-default-class
, mtl , mtl
, QuickCheck , QuickCheck
, quickcheck-instances , quickcheck-instances
@ -141,22 +142,20 @@ library
Agora.Proposal.Time Agora.Proposal.Time
Agora.Record Agora.Record
Agora.SafeMoney Agora.SafeMoney
Agora.ScriptInfo
Agora.Stake Agora.Stake
Agora.Stake.Scripts Agora.Stake.Scripts
Agora.Treasury Agora.Treasury
Agora.Utils Agora.Utils
Agora.Utils.Value Agora.Utils.Value
Agora.ScriptInfo
other-modules: other-modules: Agora.Aeson.Orphans
Agora.Aeson.Orphans
hs-source-dirs: agora hs-source-dirs: agora
library pprelude library pprelude
default-language: Haskell2010 default-language: Haskell2010
exposed-modules: PPrelude exposed-modules: PPrelude
hs-source-dirs: agora hs-source-dirs: agora
build-depends: build-depends:
, base , base
, plutarch , plutarch
@ -176,8 +175,8 @@ library agora-sample
Sample.Shared Sample.Shared
Sample.Stake Sample.Stake
Sample.Treasury Sample.Treasury
hs-source-dirs: agora-sample
hs-source-dirs: agora-sample
build-depends: agora-testlib build-depends: agora-testlib
test-suite agora-test test-suite agora-test
@ -214,8 +213,7 @@ executable agora-scripts
import: lang, deps, exe-opts import: lang, deps, exe-opts
main-is: Scripts.hs main-is: Scripts.hs
hs-source-dirs: agora-scripts hs-source-dirs: agora-scripts
other-modules: other-modules: Options
Options
build-depends: build-depends:
, agora , agora
, optparse-applicative , optparse-applicative

View file

@ -37,6 +37,12 @@ import Agora.Proposal (
ProposalId (ProposalId), ProposalId (ProposalId),
ProposalThresholds, ProposalThresholds,
) )
import Agora.Proposal.Time (
MaxTimeRangeWidth,
PMaxTimeRangeWidth,
PProposalTimingConfig,
ProposalTimingConfig,
)
import Agora.SafeMoney (GTTag) import Agora.SafeMoney (GTTag)
import Agora.Utils (tclet) import Agora.Utils (tclet)
@ -66,6 +72,11 @@ data GovernorDatum = GovernorDatum
-- ^ Gets copied over upon creation of a 'Agora.Proposal.ProposalDatum'. -- ^ Gets copied over upon creation of a 'Agora.Proposal.ProposalDatum'.
, nextProposalId :: ProposalId , nextProposalId :: ProposalId
-- ^ What tag the next proposal will get upon creating. -- ^ What tag the next proposal will get upon creating.
, proposalTimings :: ProposalTimingConfig
-- ^ The timing configuration for proposals.
-- Will get copied over upon the creation of proposals.
, createProposalTimeRangeMaxWidth :: MaxTimeRangeWidth
-- ^ The maximum valid duration of a transaction that creats a proposal.
} }
deriving stock (Show, GHC.Generic) deriving stock (Show, GHC.Generic)
@ -118,6 +129,8 @@ newtype PGovernorDatum (s :: S) = PGovernorDatum
( PDataRecord ( PDataRecord
'[ "proposalThresholds" ':= PProposalThresholds '[ "proposalThresholds" ':= PProposalThresholds
, "nextProposalId" ':= PProposalId , "nextProposalId" ':= PProposalId
, "proposalTimings" ':= PProposalTimingConfig
, "createProposalTimeRangeMaxWidth" ':= PMaxTimeRangeWidth
] ]
) )
} }

View file

@ -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 (..), ProposalTimingConfig (..))
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"] oldGovernorDatum oldGovernorDatumF <-
tcont $
pletFields
@'[ "proposalThresholds"
, "nextProposalId"
, "proposalTimings"
, "createProposalTimeRangeMaxWidth"
]
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
@ -337,6 +346,9 @@ governorValidator gov =
PGovernorDatum PGovernorDatum
( #proposalThresholds .= oldGovernorDatumF.proposalThresholds ( #proposalThresholds .= oldGovernorDatumF.proposalThresholds
.& #nextProposalId .= pdata expectedNextProposalId .& #nextProposalId .= pdata expectedNextProposalId
.& #proposalTimings .= oldGovernorDatumF.proposalTimings
.& #createProposalTimeRangeMaxWidth
.= oldGovernorDatumF.createProposalTimeRangeMaxWidth
) )
tcassert "Unexpected governor state datum" $ tcassert "Unexpected governor state datum" $
newGovernorDatum #== expectedNewDatum newGovernorDatum #== expectedNewDatum
@ -402,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
@ -417,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.createProposalTimeRangeMaxWidth
# 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" $
@ -560,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.
@ -577,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 .= pdata (pconstant tmpTimingConfig) .& #startingTime .= proposalInputDatumF.startingTime
-- FIXME: calculate from 'txInfoValidRange'
.& #startingTime .= pdata (pconstant tmpProposalStartingTime)
) )
tcassert "Unexpected output proposal datum" $ tcassert "Unexpected output proposal datum" $
@ -732,20 +750,6 @@ governorValidator gov =
let sym = governorSTSymbolFromGovernor gov let sym = governorSTSymbolFromGovernor gov
in phoistAcyclic $ pconstant sym in phoistAcyclic $ pconstant sym
-- TODO: remove this. This is temperary.
tmpTimingConfig :: ProposalTimingConfig
tmpTimingConfig =
ProposalTimingConfig
{ draftTime = 50
, votingTime = 1000
, lockingTime = 2000
, executingTime = 3000
}
-- TODO: remove this.
tmpProposalStartingTime :: ProposalStartingTime
tmpProposalStartingTime = ProposalStartingTime 0
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- | Get the 'CurrencySymbol' of GST. -- | Get the 'CurrencySymbol' of GST.

View file

@ -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-level version of 'emptyVotesFor'.
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 ::

View file

@ -10,16 +10,18 @@ Time functions for proposal phases.
-} -}
module Agora.Proposal.Time ( module Agora.Proposal.Time (
-- * Haskell-land -- * Haskell-land
ProposalTime (..),
ProposalTimingConfig (..), ProposalTimingConfig (..),
ProposalStartingTime (..), ProposalStartingTime (..),
MaxTimeRangeWidth (..),
-- * Plutarch-land -- * Plutarch-land
PProposalTime (..), PProposalTime (..),
PProposalTimingConfig (..), PProposalTimingConfig (..),
PProposalStartingTime (..), PProposalStartingTime (..),
PMaxTimeRangeWidth (..),
-- * Compute periods given config and starting time. -- * Compute periods given config and starting time.
createProposalStartingTime,
currentProposalTime, currentProposalTime,
isDraftPeriod, isDraftPeriod,
isVotingPeriod, isVotingPeriod,
@ -27,10 +29,9 @@ module Agora.Proposal.Time (
isExecutionPeriod, isExecutionPeriod,
) where ) where
import Agora.Record (mkRecordConstr, (.&), (.=)) import Agora.Utils (tcassert, tcmatch)
import Agora.Utils (tcmatch)
import GHC.Generics qualified as GHC import GHC.Generics qualified as GHC
import Generics.SOP (Generic, I (I)) import Generics.SOP (Generic, HasDatatypeInfo, I (I))
import Plutarch.Api.V1 ( import Plutarch.Api.V1 (
PExtended (PFinite), PExtended (PFinite),
PInterval (PInterval), PInterval (PInterval),
@ -39,7 +40,11 @@ import Plutarch.Api.V1 (
PPOSIXTimeRange, PPOSIXTimeRange,
PUpperBound (PUpperBound), PUpperBound (PUpperBound),
) )
import Plutarch.DataRepr (DerivePConstantViaData (..), PDataFields, PIsDataReprInstances (..)) import Plutarch.DataRepr (
DerivePConstantViaData (..),
PDataFields,
PIsDataReprInstances (..),
)
import Plutarch.Lift ( import Plutarch.Lift (
DerivePConstantViaNewtype (..), DerivePConstantViaNewtype (..),
PConstantDecl, PConstantDecl,
@ -53,35 +58,6 @@ import Prelude hiding ((+))
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
{- | == Establishing timing in Proposal interactions.
In Plutus, it's impossible to determine time exactly. It's also impossible
to get a single point in time, yet often we need to check
various constraints on time.
For the purposes of proposals, there's a single most important feature:
The ability to determine if we can perform an action. In order to correctly
determine if we are able to perform certain actions, we need to know what
time it roughly is, compared to when the proposal was created.
'ProposalTime' represents "the time according to the proposal".
Its representation is opaque, and doesn't matter.
Various functions work simply on 'ProposalTime' and 'ProposalTimingConfig'.
In particular, 'currentProposalTime' is useful for extracting the time
from the 'Plutus.V1.Ledger.Api.txInfoValidPeriod' field
of 'Plutus.V1.Ledger.Api.TxInfo'.
We avoid 'PPOSIXTimeRange' where we can in order to save on operations.
-}
data ProposalTime = ProposalTime
{ lowerBound :: POSIXTime
, upperBound :: POSIXTime
}
deriving stock (Eq, Show, GHC.Generic)
PlutusTx.makeIsDataIndexed ''ProposalTime [('ProposalTime, 0)]
-- | Represents the starting time of the proposal. -- | Represents the starting time of the proposal.
newtype ProposalStartingTime = ProposalStartingTime newtype ProposalStartingTime = ProposalStartingTime
{ getProposalStartingTime :: POSIXTime { getProposalStartingTime :: POSIXTime
@ -107,32 +83,40 @@ data ProposalTimingConfig = ProposalTimingConfig
PlutusTx.makeIsDataIndexed ''ProposalTimingConfig [('ProposalTimingConfig, 0)] PlutusTx.makeIsDataIndexed ''ProposalTimingConfig [('ProposalTimingConfig, 0)]
-- | Represents the maximum width of a 'POSIXTimeRange'.
newtype MaxTimeRangeWidth = MaxTimeRangeWidth {getMaxWidth :: POSIXTime}
deriving stock (Eq, Show, Ord, GHC.Generic)
deriving newtype (PlutusTx.ToData, PlutusTx.FromData, PlutusTx.UnsafeFromData)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- | Plutarch-level version of 'ProposalTime'. {- | == Establishing timing in Proposal interactions.
newtype PProposalTime (s :: S)
= PProposalTime
( Term
s
( PDataRecord
'[ "lowerBound" ':= PPOSIXTime
, "upperBound" ':= PPOSIXTime
]
)
)
deriving stock (GHC.Generic)
deriving anyclass (Generic)
deriving anyclass (PIsDataRepr)
deriving
(PlutusType, PIsData, PDataFields)
via (PIsDataReprInstances PProposalTime)
instance PUnsafeLiftDecl PProposalTime where In Plutus, it's impossible to determine time exactly. It's also impossible
type PLifted PProposalTime = ProposalTime to get a single point in time, yet often we need to check
deriving via various constraints on time.
(DerivePConstantViaData ProposalTime PProposalTime)
instance For the purposes of proposals, there's a single most important feature:
(PConstantDecl ProposalTime) The ability to determine if we can perform an action. In order to correctly
determine if we are able to perform certain actions, we need to know what
time it roughly is, compared to when the proposal was created.
'ProposalTime' represents "the time according to the proposal".
Its representation is opaque, and doesn't matter.
Various functions work simply on 'ProposalTime' and 'ProposalTimingConfig'.
In particular, 'currentProposalTime' is useful for extracting the time
from the 'Plutus.V1.Ledger.Api.txInfoValidPeriod' field
of 'Plutus.V1.Ledger.Api.TxInfo'.
We avoid 'PPOSIXTimeRange' where we can in order to save on operations.
-}
data PProposalTime (s :: S) = PProposalTime
{ lowerBound :: Term s PPOSIXTime
, upperBound :: Term s PPOSIXTime
}
deriving stock (GHC.Generic)
deriving anyclass (Generic, PlutusType, HasDatatypeInfo, PEq)
-- | Plutarch-level version of 'ProposalStartingTime'. -- | Plutarch-level version of 'ProposalStartingTime'.
newtype PProposalStartingTime (s :: S) = PProposalStartingTime (Term s PPOSIXTime) newtype PProposalStartingTime (s :: S) = PProposalStartingTime (Term s PPOSIXTime)
@ -172,12 +156,45 @@ deriving via
instance instance
(PConstantDecl ProposalTimingConfig) (PConstantDecl ProposalTimingConfig)
-- | Plutarch-level version of 'MaxTimeRangeWidth'.
newtype PMaxTimeRangeWidth (s :: S)
= PMaxTimeRangeWidth (Term s PPOSIXTime)
deriving (PlutusType, PIsData, PEq, POrd) via (DerivePNewtype PMaxTimeRangeWidth PPOSIXTime)
instance PUnsafeLiftDecl PMaxTimeRangeWidth where type PLifted PMaxTimeRangeWidth = MaxTimeRangeWidth
deriving via
(DerivePConstantViaNewtype MaxTimeRangeWidth PMaxTimeRangeWidth PPOSIXTime)
instance
(PConstantDecl MaxTimeRangeWidth)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- FIXME: Orphan instance, move this to plutarch-extra. -- FIXME: Orphan instance, move this to plutarch-extra.
instance AdditiveSemigroup (Term s PPOSIXTime) where instance AdditiveSemigroup (Term s PPOSIXTime) where
(punsafeCoerce @_ @_ @PInteger -> x) + (punsafeCoerce @_ @_ @PInteger -> y) = punsafeCoerce $ x + y (punsafeCoerce @_ @_ @PInteger -> x) + (punsafeCoerce @_ @_ @PInteger -> y) = punsafeCoerce $ x + y
{- | Get the starting time of a proposal, from the 'Plutus.V1.Ledger.Api.txInfoValidPeriod' field.
For every proposal, this is only meant to run once upon creation. Given time range should be
tight enough, meaning that the width of the time range should be less than the maximum value.
-}
createProposalStartingTime :: forall (s :: S). Term s (PMaxTimeRangeWidth :--> PPOSIXTimeRange :--> PProposalStartingTime)
createProposalStartingTime = phoistAcyclic $
plam $ \(pto -> maxDuration) iv -> unTermCont $ do
currentTimeF <- tcmatch $ currentProposalTime # iv
-- Use the middle of the current time range as the starting time.
let duration = currentTimeF.upperBound - currentTimeF.lowerBound
startingTime =
pdiv
# (currentTimeF.lowerBound + currentTimeF.upperBound)
# 2
tcassert "createProposalStartingTime: given time range should be tight enough" $
duration #<= maxDuration
pure $ pcon $ PProposalStartingTime startingTime
{- | Get the current proposal time, from the 'Plutus.V1.Ledger.Api.txInfoValidPeriod' field. {- | Get the current proposal time, from the 'Plutus.V1.Ledger.Api.txInfoValidPeriod' field.
If it's impossible to get a fully-bounded time, (e.g. either end of the 'PPOSIXTimeRange' is If it's impossible to get a fully-bounded time, (e.g. either end of the 'PPOSIXTimeRange' is
@ -193,21 +210,23 @@ currentProposalTime = phoistAcyclic $
lbf <- tcont $ pletFields @'["_0", "_1"] lb lbf <- tcont $ pletFields @'["_0", "_1"] lb
ubf <- tcont $ pletFields @'["_0", "_1"] ub ubf <- tcont $ pletFields @'["_0", "_1"] ub
pure $ pure $
mkRecordConstr PProposalTime $ pcon $
#lowerBound PProposalTime
.= pmatch { lowerBound =
lbf._0 pmatch
( \case lbf._0
PFinite ((pfield @"_0" #) -> d) -> d ( \case
_ -> ptraceError "currentProposalTime: Can't get fully-bounded proposal time." PFinite ((pfield @"_0" #) -> d) -> d
) _ -> ptraceError "currentProposalTime: Can't get fully-bounded proposal time."
.& #upperBound )
.= pmatch , upperBound =
ubf._0 pmatch
( \case ubf._0
PFinite ((pfield @"_0" #) -> d) -> d ( \case
_ -> ptraceError "currentProposalTime: Can't get fully-bounded proposal time." PFinite ((pfield @"_0" #) -> d) -> d
) _ -> ptraceError "currentProposalTime: Can't get fully-bounded proposal time."
)
}
-- | Check if 'PProposalTime' is within two 'PPOSIXTime'. Inclusive. -- | Check if 'PProposalTime' is within two 'PPOSIXTime'. Inclusive.
proposalTimeWithin :: proposalTimeWithin ::
@ -220,13 +239,12 @@ proposalTimeWithin ::
) )
proposalTimeWithin = phoistAcyclic $ proposalTimeWithin = phoistAcyclic $
plam $ \l h proposalTime' -> unTermCont $ do plam $ \l h proposalTime' -> unTermCont $ do
PProposalTime proposalTime <- tcmatch proposalTime' PProposalTime ut lt <- tcmatch proposalTime'
ptf <- tcont $ pletFields @'["lowerBound", "upperBound"] proposalTime
pure $ pure $
foldr1 foldr1
(#&&) (#&&)
[ l #<= pfromData ptf.lowerBound [ l #<= lt
, pfromData ptf.upperBound #<= h , ut #<= h
] ]
-- | True if the 'PProposalTime' is in the draft period. -- | True if the 'PProposalTime' is in the draft period.

View file

@ -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 $