26. Stateless transaction observation & construction
Status
Proposed
Context
-
ADR 18 merged both
headStateandchainStateinto one single state in the Hydra node, giving the chain layer a way to fetch and update thechainStatewhen observing a chain event. -
ADR 23 outlined the need for a local chain state in the chain layer again to correctly handle correct observation of multiple relevant transactions and the resulting
chainStateupdates. -
The
ChainStateType txfor our "actual" Cardano chain layer is currently:data ChainStateAt = ChainStateAt
{ chainState :: ChainState
, recordedAt :: Maybe ChainPoint
}
data ChainState
= Idle
| Initial InitialState
| Open OpenState
| Closed ClosedStatewhere
InitialState,OpenStateandClosedStatehold elaborate information about the currently tracked Hydra head. -
We face difficulties to provide sufficient user feedback when an
initTxwas observed but (for example) keys do not match our expectation.- Core problem is, that
observeInitis required to take a decision whether it wants to "adopt" the Head by returning anInitialStateor not. - This makes it impossible to provide user feedback through the
HeadLogicandAPIlayers.
- Core problem is, that
-
We want to build a Hydra head explorer, which should be able to keep track and discover Hydra heads and their state changes even when the heads were initialized before starting the explorer.
Decision
- We supersede ADR 18 with the current ADR.
Changes internal to Direct chain layer
-
Introduce a
ResolvedTxtype that has its inputs resolved. Where a normalTxwill only containTxIninformation of its inputs, aResolvedTxalso includes theTxOutfor each input. -
Change
ChainSyncHandlersignature toonRollForward :: BlockHeader -> [ResolvedTx] -> m () -
Change observing function signature to
observeSomeTx :: ChainContext -> ResolvedTx -> Maybe (OnChainTx Tx). Notably there is noChainStateinvolved. -
Do not guard observation by
HeadIdin the chain layer and instead do it in theHeadLogiclayer. -
Define a
SpendableUTxOtype that is aUTxOwith potentially needed datums included.- TBD: instead we could decide to use inline datums and rely on
UTxOcontaining them
- TBD: instead we could decide to use inline datums and rely on
-
Change transaction creation functions
initialize,commit,abort,collect,close,contestandfanoutinHydra.Direct.Chain.Stateto takeSpendableUTxOandHeadId/HeadParametersas needed. -
Extend
IsChainStatetype class to enforce that it can be updated by concurrent transactionsupdate :: ChainStateType tx -> [tx] -> ChainStateType tx.- While this is not strictly needed "outside" of the chain layer, it will have us not fall into the same pit again.
-
Change
ChainStateAtto only hold aspendableUTxOand therecordedAt. -
Update the
LocalChainStateinonRollForwardby usingupdateand pushing a newChainStateAtgenerically.
TBD:
- Impact on generators
Chain interface changes
-
Add
HeadIdandHeadParameterstoPostChainTx. -
Add
HeadIdto allOnChainTxconstructors. -
Extend
OnInitTxwith observed chain participants.- TBD: How are cardano verification keys generically represented in
HeadLogic?
- TBD: How are cardano verification keys generically represented in
-
Extend
OnContestTxwith new deadline and a list of contesters. -
Move off-chain checks for what makes a "proper head" to
HeadLogic
TBD:
- Merge
HeadSeedandHeadId? How to abstract?
Consequences
-
All logic is kept in the logic layer and no protocol decisions (i.e. whether to adopt or ignore a head initialization) are taken in the chain layer.
- The
HeadLogicgets informed of any properinitTxand can log that it is ignored and for what reason.
- The
-
The transaction observation and construction functions can be moved into a dedicated package that is cardano-specific, but not requires special state knowledge of the "direct chain following" and can be re-used as a library.
-
All transaction observation functions used by
observeSomeTxwill need to be able to identify a Hydra Head transaction from only theResolvedTxand theChainContext -
Any
Chain Tximplementation wanting to re-use existing transaction observation functions must be able to resolve transaction inputs (against some ledger state) and produceResolvedTx.- A chain-following implementation (as
Hydra.Chain.Direct) can keep previous transactions around. - A chain indexer on "interesting" protocol addresses can be used to efficiently query most inputs.
- A chain-following implementation (as
-
We can get rid of the
Hydra.Chain.Direct.Stateglue code altogether. -
While this does not directly supersede ADR23, it paves the way to remove
LocalChainStateagain as theChainStateAtis now combinable from multiple transactions (seeupdateabove) and we can keep the state (again) only in theHeadStateaggregate. Note that this would shift the rollback handling back into the logic layer.


