make 'PProposalTime' scott-encoded

This commit is contained in:
fanghr 2022-05-25 21:36:56 +08:00
parent 6f9de51541
commit abe1d950bf

View file

@ -10,7 +10,6 @@ Time functions for proposal phases.
-} -}
module Agora.Proposal.Time ( module Agora.Proposal.Time (
-- * Haskell-land -- * Haskell-land
ProposalTime (..),
ProposalTimingConfig (..), ProposalTimingConfig (..),
ProposalStartingTime (..), ProposalStartingTime (..),
MaxTimeRangeWidth (..), MaxTimeRangeWidth (..),
@ -30,10 +29,9 @@ module Agora.Proposal.Time (
isExecutionPeriod, isExecutionPeriod,
) where ) where
import Agora.Record (mkRecordConstr, (.&), (.=))
import Agora.Utils (tcassert, tcmatch) import Agora.Utils (tcassert, 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),
@ -42,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,
@ -56,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
@ -117,30 +90,33 @@ newtype MaxTimeRangeWidth = MaxTimeRangeWidth {getMaxWidth :: POSIXTime}
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- | 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)
@ -204,10 +180,7 @@ instance AdditiveSemigroup (Term s PPOSIXTime) where
createProposalStartingTime :: forall (s :: S). Term s (PMaxTimeRangeWidth :--> PPOSIXTimeRange :--> PProposalStartingTime) createProposalStartingTime :: forall (s :: S). Term s (PMaxTimeRangeWidth :--> PPOSIXTimeRange :--> PProposalStartingTime)
createProposalStartingTime = phoistAcyclic $ createProposalStartingTime = phoistAcyclic $
plam $ \(pto -> maxDuration) iv -> unTermCont $ do plam $ \(pto -> maxDuration) iv -> unTermCont $ do
currentTimeF <- currentTimeF <- tcmatch $ currentProposalTime # iv
tcont $
pletFields @'["lowerBound", "upperBound"] $
currentProposalTime # iv
-- Use the middle of the current time range as the starting time. -- Use the middle of the current time range as the starting time.
let duration = currentTimeF.upperBound - currentTimeF.lowerBound let duration = currentTimeF.upperBound - currentTimeF.lowerBound
@ -237,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 ::
@ -264,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.