{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module Cardano.Node.Emulator.Internal.Node.TimeSlot (
SlotConfig(..)
, SlotConversionError(..)
, slotRangeToPOSIXTimeRange
, slotToPOSIXTimeRange
, slotToBeginPOSIXTime
, slotToEndPOSIXTime
, posixTimeRangeToContainedSlotRange
, posixTimeToEnclosingSlot
, currentSlot
, utcTimeToPOSIXTime
, posixTimeToUTCTime
, nominalDiffTimeToPOSIXTime
, posixTimeToNominalDiffTime
) where
import Codec.Serialise (Serialise)
import Data.Aeson (FromJSON, ToJSON)
import Data.Default (Default (def))
import Data.Time.Clock qualified as Time
import Data.Time.Clock.POSIX qualified as Time
import GHC.Generics (Generic)
import Ledger.Orphans ()
import Ledger.Slot (Slot (Slot), SlotRange)
import Plutus.V1.Ledger.Interval (Extended (Finite), Interval (Interval), LowerBound (LowerBound),
UpperBound (UpperBound), interval, member)
import Plutus.V1.Ledger.Time (POSIXTime (POSIXTime, getPOSIXTime), POSIXTimeRange)
import PlutusTx.Lift (makeLift)
import PlutusTx.Prelude (Integer, divide, fmap, ($), (*), (+), (-), (.))
import Prelude (Eq, IO, Show, (<$>))
import Prelude qualified as Haskell
import Prettyprinter (Pretty (pretty), (<+>))
data SlotConfig =
SlotConfig
{ SlotConfig -> Integer
scSlotLength :: !Integer
, SlotConfig -> POSIXTime
scSlotZeroTime :: !POSIXTime
}
deriving stock (SlotConfig -> SlotConfig -> Bool
(SlotConfig -> SlotConfig -> Bool)
-> (SlotConfig -> SlotConfig -> Bool) -> Eq SlotConfig
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: SlotConfig -> SlotConfig -> Bool
$c/= :: SlotConfig -> SlotConfig -> Bool
== :: SlotConfig -> SlotConfig -> Bool
$c== :: SlotConfig -> SlotConfig -> Bool
Eq, Int -> SlotConfig -> ShowS
[SlotConfig] -> ShowS
SlotConfig -> String
(Int -> SlotConfig -> ShowS)
-> (SlotConfig -> String)
-> ([SlotConfig] -> ShowS)
-> Show SlotConfig
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [SlotConfig] -> ShowS
$cshowList :: [SlotConfig] -> ShowS
show :: SlotConfig -> String
$cshow :: SlotConfig -> String
showsPrec :: Int -> SlotConfig -> ShowS
$cshowsPrec :: Int -> SlotConfig -> ShowS
Show, (forall x. SlotConfig -> Rep SlotConfig x)
-> (forall x. Rep SlotConfig x -> SlotConfig) -> Generic SlotConfig
forall x. Rep SlotConfig x -> SlotConfig
forall x. SlotConfig -> Rep SlotConfig x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep SlotConfig x -> SlotConfig
$cfrom :: forall x. SlotConfig -> Rep SlotConfig x
Generic)
deriving anyclass ([SlotConfig] -> Encoding
[SlotConfig] -> Value
SlotConfig -> Encoding
SlotConfig -> Value
(SlotConfig -> Value)
-> (SlotConfig -> Encoding)
-> ([SlotConfig] -> Value)
-> ([SlotConfig] -> Encoding)
-> ToJSON SlotConfig
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> ToJSON a
toEncodingList :: [SlotConfig] -> Encoding
$ctoEncodingList :: [SlotConfig] -> Encoding
toJSONList :: [SlotConfig] -> Value
$ctoJSONList :: [SlotConfig] -> Value
toEncoding :: SlotConfig -> Encoding
$ctoEncoding :: SlotConfig -> Encoding
toJSON :: SlotConfig -> Value
$ctoJSON :: SlotConfig -> Value
ToJSON, Value -> Parser [SlotConfig]
Value -> Parser SlotConfig
(Value -> Parser SlotConfig)
-> (Value -> Parser [SlotConfig]) -> FromJSON SlotConfig
forall a.
(Value -> Parser a) -> (Value -> Parser [a]) -> FromJSON a
parseJSONList :: Value -> Parser [SlotConfig]
$cparseJSONList :: Value -> Parser [SlotConfig]
parseJSON :: Value -> Parser SlotConfig
$cparseJSON :: Value -> Parser SlotConfig
FromJSON, [SlotConfig] -> Encoding
SlotConfig -> Encoding
(SlotConfig -> Encoding)
-> (forall s. Decoder s SlotConfig)
-> ([SlotConfig] -> Encoding)
-> (forall s. Decoder s [SlotConfig])
-> Serialise SlotConfig
forall s. Decoder s [SlotConfig]
forall s. Decoder s SlotConfig
forall a.
(a -> Encoding)
-> (forall s. Decoder s a)
-> ([a] -> Encoding)
-> (forall s. Decoder s [a])
-> Serialise a
decodeList :: Decoder s [SlotConfig]
$cdecodeList :: forall s. Decoder s [SlotConfig]
encodeList :: [SlotConfig] -> Encoding
$cencodeList :: [SlotConfig] -> Encoding
decode :: Decoder s SlotConfig
$cdecode :: forall s. Decoder s SlotConfig
encode :: SlotConfig -> Encoding
$cencode :: SlotConfig -> Encoding
Serialise)
makeLift ''SlotConfig
instance Default SlotConfig where
{-# INLINABLE def #-}
def :: SlotConfig
def = SlotConfig :: Integer -> POSIXTime -> SlotConfig
SlotConfig{ scSlotLength :: Integer
scSlotLength = Integer
1000, scSlotZeroTime :: POSIXTime
scSlotZeroTime = Integer -> POSIXTime
POSIXTime Integer
beginningOfTime }
instance Pretty SlotConfig where
pretty :: SlotConfig -> Doc ann
pretty SlotConfig {Integer
scSlotLength :: Integer
scSlotLength :: SlotConfig -> Integer
scSlotLength, POSIXTime
scSlotZeroTime :: POSIXTime
scSlotZeroTime :: SlotConfig -> POSIXTime
scSlotZeroTime} =
Doc ann
"Slot 0 starts at"
Doc ann -> Doc ann -> Doc ann
forall ann. Doc ann -> Doc ann -> Doc ann
<+> POSIXTime -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty POSIXTime
scSlotZeroTime
Doc ann -> Doc ann -> Doc ann
forall ann. Doc ann -> Doc ann -> Doc ann
<+> Doc ann
"and one slot has length of"
Doc ann -> Doc ann -> Doc ann
forall ann. Doc ann -> Doc ann -> Doc ann
<+> Integer -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty Integer
scSlotLength
Doc ann -> Doc ann -> Doc ann
forall ann. Doc ann -> Doc ann -> Doc ann
<+> Doc ann
"ms"
data SlotConversionError =
SlotOutOfRange
{ SlotConversionError -> Slot
requestedSlot :: !Slot
, SlotConversionError -> (Slot, POSIXTime)
horizon :: !(Slot, POSIXTime)
}
deriving stock (SlotConversionError -> SlotConversionError -> Bool
(SlotConversionError -> SlotConversionError -> Bool)
-> (SlotConversionError -> SlotConversionError -> Bool)
-> Eq SlotConversionError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: SlotConversionError -> SlotConversionError -> Bool
$c/= :: SlotConversionError -> SlotConversionError -> Bool
== :: SlotConversionError -> SlotConversionError -> Bool
$c== :: SlotConversionError -> SlotConversionError -> Bool
Eq, Int -> SlotConversionError -> ShowS
[SlotConversionError] -> ShowS
SlotConversionError -> String
(Int -> SlotConversionError -> ShowS)
-> (SlotConversionError -> String)
-> ([SlotConversionError] -> ShowS)
-> Show SlotConversionError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [SlotConversionError] -> ShowS
$cshowList :: [SlotConversionError] -> ShowS
show :: SlotConversionError -> String
$cshow :: SlotConversionError -> String
showsPrec :: Int -> SlotConversionError -> ShowS
$cshowsPrec :: Int -> SlotConversionError -> ShowS
Show, (forall x. SlotConversionError -> Rep SlotConversionError x)
-> (forall x. Rep SlotConversionError x -> SlotConversionError)
-> Generic SlotConversionError
forall x. Rep SlotConversionError x -> SlotConversionError
forall x. SlotConversionError -> Rep SlotConversionError x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep SlotConversionError x -> SlotConversionError
$cfrom :: forall x. SlotConversionError -> Rep SlotConversionError x
Generic)
deriving anyclass ([SlotConversionError] -> Encoding
[SlotConversionError] -> Value
SlotConversionError -> Encoding
SlotConversionError -> Value
(SlotConversionError -> Value)
-> (SlotConversionError -> Encoding)
-> ([SlotConversionError] -> Value)
-> ([SlotConversionError] -> Encoding)
-> ToJSON SlotConversionError
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> ToJSON a
toEncodingList :: [SlotConversionError] -> Encoding
$ctoEncodingList :: [SlotConversionError] -> Encoding
toJSONList :: [SlotConversionError] -> Value
$ctoJSONList :: [SlotConversionError] -> Value
toEncoding :: SlotConversionError -> Encoding
$ctoEncoding :: SlotConversionError -> Encoding
toJSON :: SlotConversionError -> Value
$ctoJSON :: SlotConversionError -> Value
ToJSON, Value -> Parser [SlotConversionError]
Value -> Parser SlotConversionError
(Value -> Parser SlotConversionError)
-> (Value -> Parser [SlotConversionError])
-> FromJSON SlotConversionError
forall a.
(Value -> Parser a) -> (Value -> Parser [a]) -> FromJSON a
parseJSONList :: Value -> Parser [SlotConversionError]
$cparseJSONList :: Value -> Parser [SlotConversionError]
parseJSON :: Value -> Parser SlotConversionError
$cparseJSON :: Value -> Parser SlotConversionError
FromJSON)
instance Pretty SlotConversionError where
pretty :: SlotConversionError -> Doc ann
pretty SlotOutOfRange { Slot
requestedSlot :: Slot
requestedSlot :: SlotConversionError -> Slot
requestedSlot, (Slot, POSIXTime)
horizon :: (Slot, POSIXTime)
horizon :: SlotConversionError -> (Slot, POSIXTime)
horizon } =
Doc ann
"Slot out of range:"
Doc ann -> Doc ann -> Doc ann
forall ann. Doc ann -> Doc ann -> Doc ann
<+> Slot -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty Slot
requestedSlot
Doc ann -> Doc ann -> Doc ann
forall ann. Doc ann -> Doc ann -> Doc ann
<+> Doc ann
"Horizon:"
Doc ann -> Doc ann -> Doc ann
forall ann. Doc ann -> Doc ann -> Doc ann
<+> (Slot, POSIXTime) -> Doc ann
forall a ann. Pretty a => a -> Doc ann
pretty (Slot, POSIXTime)
horizon
{-# INLINABLE beginningOfTime #-}
beginningOfTime :: Integer
beginningOfTime :: Integer
beginningOfTime = Integer
1596059091000
{-# INLINABLE slotRangeToPOSIXTimeRange #-}
slotRangeToPOSIXTimeRange :: SlotConfig -> SlotRange -> POSIXTimeRange
slotRangeToPOSIXTimeRange :: SlotConfig -> SlotRange -> POSIXTimeRange
slotRangeToPOSIXTimeRange SlotConfig
sc (Interval (LowerBound Extended Slot
start Bool
startIncl) (UpperBound Extended Slot
end Bool
endIncl)) =
let lbound :: Extended POSIXTime
lbound = (Slot -> POSIXTime) -> Extended Slot -> Extended POSIXTime
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (if Bool
startIncl then SlotConfig -> Slot -> POSIXTime
slotToBeginPOSIXTime SlotConfig
sc else SlotConfig -> Slot -> POSIXTime
slotToEndPOSIXTime SlotConfig
sc) Extended Slot
start
ubound :: Extended POSIXTime
ubound = (Slot -> POSIXTime) -> Extended Slot -> Extended POSIXTime
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (if Bool
endIncl then SlotConfig -> Slot -> POSIXTime
slotToEndPOSIXTime SlotConfig
sc else SlotConfig -> Slot -> POSIXTime
slotToBeginPOSIXTime SlotConfig
sc) Extended Slot
end
in LowerBound POSIXTime -> UpperBound POSIXTime -> POSIXTimeRange
forall a. LowerBound a -> UpperBound a -> Interval a
Interval (Extended POSIXTime -> Bool -> LowerBound POSIXTime
forall a. Extended a -> Bool -> LowerBound a
LowerBound Extended POSIXTime
lbound Bool
startIncl) (Extended POSIXTime -> Bool -> UpperBound POSIXTime
forall a. Extended a -> Bool -> UpperBound a
UpperBound Extended POSIXTime
ubound Bool
endIncl)
{-# INLINABLE slotToPOSIXTimeRange #-}
slotToPOSIXTimeRange :: SlotConfig -> Slot -> POSIXTimeRange
slotToPOSIXTimeRange :: SlotConfig -> Slot -> POSIXTimeRange
slotToPOSIXTimeRange SlotConfig
sc Slot
slot =
POSIXTime -> POSIXTime -> POSIXTimeRange
forall a. a -> a -> Interval a
interval (SlotConfig -> Slot -> POSIXTime
slotToBeginPOSIXTime SlotConfig
sc Slot
slot) (SlotConfig -> Slot -> POSIXTime
slotToEndPOSIXTime SlotConfig
sc Slot
slot)
{-# INLINABLE slotToBeginPOSIXTime #-}
slotToBeginPOSIXTime :: SlotConfig -> Slot -> POSIXTime
slotToBeginPOSIXTime :: SlotConfig -> Slot -> POSIXTime
slotToBeginPOSIXTime SlotConfig{Integer
scSlotLength :: Integer
scSlotLength :: SlotConfig -> Integer
scSlotLength, POSIXTime
scSlotZeroTime :: POSIXTime
scSlotZeroTime :: SlotConfig -> POSIXTime
scSlotZeroTime} (Slot Integer
n) =
let msAfterBegin :: Integer
msAfterBegin = Integer
n Integer -> Integer -> Integer
forall a. MultiplicativeSemigroup a => a -> a -> a
* Integer
scSlotLength
in Integer -> POSIXTime
POSIXTime (Integer -> POSIXTime) -> Integer -> POSIXTime
forall a b. (a -> b) -> a -> b
$ POSIXTime -> Integer
getPOSIXTime POSIXTime
scSlotZeroTime Integer -> Integer -> Integer
forall a. AdditiveSemigroup a => a -> a -> a
+ Integer
msAfterBegin
{-# INLINABLE slotToEndPOSIXTime #-}
slotToEndPOSIXTime :: SlotConfig -> Slot -> POSIXTime
slotToEndPOSIXTime :: SlotConfig -> Slot -> POSIXTime
slotToEndPOSIXTime sc :: SlotConfig
sc@SlotConfig{Integer
scSlotLength :: Integer
scSlotLength :: SlotConfig -> Integer
scSlotLength} Slot
slot =
SlotConfig -> Slot -> POSIXTime
slotToBeginPOSIXTime SlotConfig
sc Slot
slot POSIXTime -> POSIXTime -> POSIXTime
forall a. AdditiveSemigroup a => a -> a -> a
+ Integer -> POSIXTime
POSIXTime (Integer
scSlotLength Integer -> Integer -> Integer
forall a. AdditiveGroup a => a -> a -> a
- Integer
1)
{-# INLINABLE posixTimeRangeToContainedSlotRange #-}
posixTimeRangeToContainedSlotRange :: SlotConfig -> POSIXTimeRange -> SlotRange
posixTimeRangeToContainedSlotRange :: SlotConfig -> POSIXTimeRange -> SlotRange
posixTimeRangeToContainedSlotRange SlotConfig
sc POSIXTimeRange
ptr = case (POSIXTime -> Slot) -> POSIXTimeRange -> SlotRange
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (SlotConfig -> POSIXTime -> Slot
posixTimeToEnclosingSlot SlotConfig
sc) POSIXTimeRange
ptr of
Interval (LowerBound Extended Slot
start Bool
startIncl) (UpperBound Extended Slot
end Bool
endIncl) ->
LowerBound Slot -> UpperBound Slot -> SlotRange
forall a. LowerBound a -> UpperBound a -> Interval a
Interval
(Extended Slot -> Bool -> LowerBound Slot
forall a. Extended a -> Bool -> LowerBound a
LowerBound Extended Slot
start (case Extended Slot
start of Finite Slot
s -> SlotConfig -> Slot -> POSIXTime
slotToBeginPOSIXTime SlotConfig
sc Slot
s POSIXTime -> POSIXTimeRange -> Bool
forall a. Ord a => a -> Interval a -> Bool
`member` POSIXTimeRange
ptr; Extended Slot
_ -> Bool
startIncl))
(Extended Slot -> Bool -> UpperBound Slot
forall a. Extended a -> Bool -> UpperBound a
UpperBound Extended Slot
end (case Extended Slot
end of Finite Slot
e -> SlotConfig -> Slot -> POSIXTime
slotToEndPOSIXTime SlotConfig
sc Slot
e POSIXTime -> POSIXTimeRange -> Bool
forall a. Ord a => a -> Interval a -> Bool
`member` POSIXTimeRange
ptr; Extended Slot
_ -> Bool
endIncl))
{-# INLINABLE posixTimeToEnclosingSlot #-}
posixTimeToEnclosingSlot :: SlotConfig -> POSIXTime -> Slot
posixTimeToEnclosingSlot :: SlotConfig -> POSIXTime -> Slot
posixTimeToEnclosingSlot SlotConfig{Integer
scSlotLength :: Integer
scSlotLength :: SlotConfig -> Integer
scSlotLength, POSIXTime
scSlotZeroTime :: POSIXTime
scSlotZeroTime :: SlotConfig -> POSIXTime
scSlotZeroTime} (POSIXTime Integer
t) =
let timePassed :: Integer
timePassed = Integer
t Integer -> Integer -> Integer
forall a. AdditiveGroup a => a -> a -> a
- POSIXTime -> Integer
getPOSIXTime POSIXTime
scSlotZeroTime
slotsPassed :: Integer
slotsPassed = Integer -> Integer -> Integer
divide Integer
timePassed Integer
scSlotLength
in Integer -> Slot
Slot Integer
slotsPassed
currentSlot :: SlotConfig -> IO Slot
currentSlot :: SlotConfig -> IO Slot
currentSlot SlotConfig
sc = NominalDiffTime -> Slot
timeToSlot (NominalDiffTime -> Slot) -> IO NominalDiffTime -> IO Slot
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO NominalDiffTime
Time.getPOSIXTime
where
timeToSlot :: NominalDiffTime -> Slot
timeToSlot = SlotConfig -> POSIXTime -> Slot
posixTimeToEnclosingSlot SlotConfig
sc
(POSIXTime -> Slot)
-> (NominalDiffTime -> POSIXTime) -> NominalDiffTime -> Slot
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NominalDiffTime -> POSIXTime
nominalDiffTimeToPOSIXTime
utcTimeToPOSIXTime :: Time.UTCTime -> POSIXTime
utcTimeToPOSIXTime :: UTCTime -> POSIXTime
utcTimeToPOSIXTime = NominalDiffTime -> POSIXTime
nominalDiffTimeToPOSIXTime (NominalDiffTime -> POSIXTime)
-> (UTCTime -> NominalDiffTime) -> UTCTime -> POSIXTime
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UTCTime -> NominalDiffTime
Time.utcTimeToPOSIXSeconds
posixTimeToUTCTime :: POSIXTime -> Time.UTCTime
posixTimeToUTCTime :: POSIXTime -> UTCTime
posixTimeToUTCTime = NominalDiffTime -> UTCTime
Time.posixSecondsToUTCTime (NominalDiffTime -> UTCTime)
-> (POSIXTime -> NominalDiffTime) -> POSIXTime -> UTCTime
forall b c a. (b -> c) -> (a -> b) -> a -> c
. POSIXTime -> NominalDiffTime
posixTimeToNominalDiffTime
nominalDiffTimeToPOSIXTime :: Time.NominalDiffTime -> POSIXTime
nominalDiffTimeToPOSIXTime :: NominalDiffTime -> POSIXTime
nominalDiffTimeToPOSIXTime
= Integer -> POSIXTime
POSIXTime
(Integer -> POSIXTime)
-> (NominalDiffTime -> Integer) -> NominalDiffTime -> POSIXTime
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Pico -> Integer
forall a b. (RealFrac a, Integral b) => a -> b
Haskell.truncate
(Pico -> Integer)
-> (NominalDiffTime -> Pico) -> NominalDiffTime -> Integer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Pico -> Pico -> Pico
forall a. Num a => a -> a -> a
Haskell.* Pico
1000)
(Pico -> Pico)
-> (NominalDiffTime -> Pico) -> NominalDiffTime -> Pico
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NominalDiffTime -> Pico
Time.nominalDiffTimeToSeconds
posixTimeToNominalDiffTime :: POSIXTime -> Time.NominalDiffTime
posixTimeToNominalDiffTime :: POSIXTime -> NominalDiffTime
posixTimeToNominalDiffTime
= Pico -> NominalDiffTime
Time.secondsToNominalDiffTime
(Pico -> NominalDiffTime)
-> (POSIXTime -> Pico) -> POSIXTime -> NominalDiffTime
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Pico -> Pico -> Pico
forall a. Fractional a => a -> a -> a
Haskell./ Pico
1000)
(Pico -> Pico) -> (POSIXTime -> Pico) -> POSIXTime -> Pico
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> Pico
forall a. Num a => Integer -> a
Haskell.fromInteger
(Integer -> Pico) -> (POSIXTime -> Integer) -> POSIXTime -> Pico
forall b c a. (b -> c) -> (a -> b) -> a -> c
. POSIXTime -> Integer
getPOSIXTime