ADR 12: Time conversion semantic change
Date: 2022-11-19
Status
Draft
Context
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 POSIXTime
.
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
conversion between Slot
and 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 ouroboros-consensus
dependency.
Decision
We will deprecate the
Ledger.TimeSlot.SlotConfig
type and all functions in theLedger.TimeSlot
module using theSlotConfig
. The only viable functions are the ones that convert betweenData.Time
types and plutus types (types related toTxInfo
).We will copy the
Ledger.TimeSlot
module 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.Params
module inside the emulator as an internal module and modify theParams
datatype name toEmulatorParams
.We will modify the
Plutus.Contract.Request.getParams
function toPlutus.Contract.Request.getProtocolParameters
. This implies modifying the name of Contract effectGetParamsReq/GetParamsResp
.We will create two pairs of effects in
Plutus.Contract.Effect
: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
SlotConfig
for 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
EraHistory
from the local node and store it in its local environment.We will implement the PAB interpreter by using the
EraHistory
alongside the consensus functionswallclockToSlot
andslotToWallclock
. 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 theEraHistory
is out of date. The reason is thatEraHistory
only encodes era information from the moment the user ran the query and it cannot predict the future. In that case, if thePastHorizonException
is returned, we will re-query theEraHistory
of the local node, replace the old value in the PAB environment, and retry the conversion. If it fails again, we return the error message.
Argument
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
provided by ouroboros-consensus
when using a real Cardano node.
Implications
We will have to update the
plutus-use-cases
examples to use those new conversion functions. The user will not useSlotConfig
to convert between slots and UTC time. He will instead need to use the new effects defined by theContract
API.
Alternatives
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
the Interpreter
datatype, which in turn is returned by cardano-api
when querying the local
node for the EraHistory
.
Thus, even if we copy-pasted the Summary
datatype in the emulator, we would still need to find
a way to query the Summary
of ouroboros-consensus
and convert it to our own Summary
.
The ideal solution would be coordinate with the maintainers of ouroboros-consensus
to move out
the 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 SlotConfig
which EpochInfo
.
However, we need EpochInfo
to be an instance of FromJSON/ToJSON
, which is not possible
because its data contructor parameters are functions.