ADR 12: Time conversion semantic change
Currently, PAB users need to provide the
SlotConfig in the configuration file, which is passed
through to the
Contract API, which users can use to convert between a
Slot and a
However, the current
SlotConfig representation supposes that the slot length is the same for all
epochs in the Cardano blockchain, which is not the case.
For example, during the Byron era, the slot length was 20s, while from Shelley era and onwards, the
slot length is 1s.
Therefore, the functions from the
Ledger.TimeSlot module in
plutus-ledger do not compute the
POSIXTime the right way.
The current easiest way to compute the time conversions is to query the local Cardano node on the
consensus layer, which requires the
We will deprecate the
Ledger.TimeSlot.SlotConfigtype and all functions in the
Ledger.TimeSlotmodule using the
SlotConfig. The only viable functions are the ones that convert between
Data.Timetypes and plutus types (types related to
We will copy the
Ledger.TimeSlotmodule in the emulator (ideally rename it) and keep it as an internal module. Any functions not used by the emulator will be removed.
We will move the
Ledger.Paramsmodule inside the emulator as an internal module and modify the
Paramsdatatype name to
We will modify the
Plutus.Contract.Request.getProtocolParameters. This implies modifying the name of Contract effect
We will create two pairs of effects in
data PABReq = ... | SlotToUTCTimeIntervalReq SlotNo | UTCTimeToSlotReq UTCTime ... data PABResp = ... | SlotToUTCTimeIntervalResp (UTCTime, SlotLength) -- An alternative can be (UTCTime, UTCTime) | UTCTimeToSlotResp SlotNo ...
We will implement the emulator effect interpreter by simply using the
SlotConfigfor the conversions.
We will implement the PAB effect interpreter by using the local node. There are multiple steps to implement this:
At startup, the PAB will query the
EraHistoryfrom the local node and store it in its local environment.
We will implement the PAB interpreter by using the
EraHistoryalongside the consensus functions
slotToWallclock. Here’s an example function of how to use them:
-- Calculate slot number which contains a given timestamp utcTimeToSlotNo :: SystemStart -> EraHistory CardanoMode -> Time.UTCTime -> Either PastHorizonException SlotNo utcTimeToSlotNo systemStart (EraHistory _ interpreter) time = do let relativeTime = toRelativeTime systemStart time (slotNo, _, _) <- interpretQuery interpreter $ wallclockToSlot relativeTime pure slotNo slotStart :: SystemStart -> EraHistory CardanoMode -> SlotNo -> Either PastHorizonException Time.UTCTime slotStart systemStart (EraHistory _ interpreter) slotNo = do (relativeTime, _) <- interpretQuery interpreter $ slotToWallclock slotNo pure $ fromRelativeTime systemStart relativeTime
However, we will also add an additional step. If the conversion returns
PastHorizonException, then there is a good probability that the
EraHistoryis out of date. The reason is that
EraHistoryonly encodes era information from the moment the user ran the query and it cannot predict the future. In that case, if the
PastHorizonExceptionis returned, we will re-query the
EraHistoryof the local node, replace the old value in the PAB environment, and retry the conversion. If it fails again, we return the error message.
The solution to create new effects in
Plutus.Contract.Effects has the nice property that the
implementation can differ depending on the environment.
It allows us to keep using
SlotConfig in the emulator, while using the existing implementation
ouroboros-consensus when using a real Cardano node.
We will have to update the
plutus-use-casesexamples to use those new conversion functions. The user will not use
SlotConfigto convert between slots and UTC time. He will instead need to use the new effects defined by the
Changing the representation of
SlotConfig to the correct one
A good solution would be to reuse the
Summary datatype from
ouroboros-consensus which has
the correct representation.
However, the emulator does not depend on
ouroboros-consensus and adding it would incur a large
dependency footprint for such a simple need.
Additionnaly, consensus doesn’t (and doesn’t want to) expose the
Summary, which is internal to
Interpreter datatype, which in turn is returned by
cardano-api when querying the local
node for the
Thus, even if we copy-pasted the
Summary datatype in the emulator, we would still need to find
a way to query the
ouroboros-consensus and convert it to our own
The ideal solution would be coordinate with the maintainers of
ouroboros-consensus to move out
Summary datatype in a (small) consensus core module and find a way to reconstruct the
Summary when querying the local node.
This should be done in the long term, but it is not our current focus.
Directly use EpochInfo in the emulator and Contract API
Another thought of solution would be to replace the use of
However, we need
EpochInfo to be an instance of
FromJSON/ToJSON, which is not possible
because its data contructor parameters are functions.