| Safe Haskell | Safe-Inferred |
|---|---|
| Language | GHC2021 |
Hydra.HeadLogic
Description
Implements the Head Protocol's state machine as pure functions in an event sourced manner.
More specifically, the update will handle Inputs (or rather "commands" in
event sourcing speak) and convert that into a list of side-Effects and
StateChanged events, which in turn are applied via aggregateNodeState into
a single NodeState.
As the specification is using a more imperative way of specifying the protocol
behavior, one would find the decision logic in update while state updates
can be found in the corresponding applyEvent branch.
Synopsis
- data NextFanoutStep tx
- = FinalStep {
- stepDistribute :: UTxOType tx
- | FullFanoutStep
- | PartialStep {
- stepDistribute :: UTxOType tx
- stepProof :: UTxOType tx
- = FinalStep {
- data OnChainFanoutDatum
- update :: IsChainState tx => Environment -> Ledger tx -> UTCTime -> NodeState tx -> Input tx -> Outcome tx
- aggregateNodeState :: IsChainState tx => NodeState tx -> StateChanged tx -> NodeState tx
- maxTxsPerSnapshot :: Int
- aggregateChainStateHistory :: IsChainState tx => ChainStateHistory tx -> StateChanged tx -> ChainStateHistory tx
- aggregateState :: IsChainState tx => NodeState tx -> Outcome tx -> NodeState tx
- isLeader :: HeadParameters -> Party -> SnapshotNumber -> Bool
- onIdleClientInit :: Environment -> Outcome tx
- onIdleChainInitTx :: Environment -> ChainStateType tx -> HeadId -> HeadSeed -> HeadParameters -> [OnChainId] -> Outcome tx
- onOpenClientNewTx :: tx -> Outcome tx
- onOpenNetworkReqTx :: IsTx tx => Environment -> Ledger tx -> ChainSlot -> OpenState tx -> TTL -> PendingDeposits tx -> tx -> Outcome tx
- selectNextIncrementalAction :: IsTx tx => PendingDeposits tx -> Maybe (TxIdType tx) -> Maybe tx -> Maybe (UTxOType tx) -> (Maybe tx, Maybe (TxIdType tx))
- onOpenNetworkReqSn :: IsTx tx => Environment -> Ledger tx -> PendingDeposits tx -> ChainSlot -> OpenState tx -> TTL -> Party -> SnapshotVersion -> SnapshotNumber -> [TxIdType tx] -> Maybe tx -> Maybe (TxIdType tx) -> Outcome tx
- finalizedDepositTxId :: IsTx tx => Maybe (FinalizedSnapshot tx) -> Maybe (TxIdType tx)
- onOpenNetworkAckSn :: IsTx tx => Environment -> PendingDeposits tx -> OpenState tx -> Party -> Signature (Snapshot tx) -> SnapshotNumber -> Outcome tx
- onClientRecover :: IsTx tx => ChainSlot -> PendingDeposits tx -> Maybe (TxIdType tx) -> TxIdType tx -> Outcome tx
- onOpenClientDecommit :: IsTx tx => HeadId -> Ledger tx -> ChainSlot -> CoordinatedHeadState tx -> tx -> Outcome tx
- requireDecommitOutputs :: IsTx tx => HeadId -> UTxOType tx -> tx -> Outcome tx -> Outcome tx
- onOpenNetworkReqDec :: IsTx tx => Environment -> Ledger tx -> TTL -> ChainSlot -> PendingDeposits tx -> OpenState tx -> tx -> Outcome tx
- existingDeposit :: IsTx tx => PendingDeposits tx -> Maybe (TxIdType tx) -> Maybe (TxIdType tx, Deposit tx)
- determineNextDepositStatus :: Environment -> PendingDeposits tx -> UTCTime -> PendingDeposits tx
- onChainTick :: IsTx tx => Environment -> PendingDeposits tx -> UTCTime -> Outcome tx
- onOpenChainTick :: IsTx tx => Environment -> UTCTime -> PendingDeposits tx -> OpenState tx -> Outcome tx
- maybeRequestSnapshotAfterVersionBump :: IsTx tx => HeadParameters -> Party -> SnapshotNumber -> Seq tx -> SnapshotVersion -> SnapshotVersion -> SeenSnapshot tx -> Maybe (TxIdType tx) -> Outcome tx
- onOpenChainIncrementTx :: IsTx tx => Environment -> OpenState tx -> ChainStateType tx -> SnapshotVersion -> TxIdType tx -> Outcome tx
- onOpenChainDecrementTx :: IsTx tx => Environment -> PendingDeposits tx -> OpenState tx -> ChainStateType tx -> SnapshotVersion -> UTxOType tx -> Outcome tx
- setExistingDeposit :: IsTx tx => PendingDeposits tx -> Maybe (TxIdType tx) -> Maybe (TxIdType tx)
- maybeRepostIncrementTx :: IsTx tx => HeadSeed -> HeadId -> HeadParameters -> PendingDeposits tx -> ConfirmedSnapshot tx -> Maybe (FinalizedSnapshot tx) -> ChainSlot -> Outcome tx
- maybeRepostDecrementTx :: HeadSeed -> HeadId -> HeadParameters -> Maybe tx -> ConfirmedSnapshot tx -> Maybe (FinalizedSnapshot tx) -> ChainSlot -> Outcome tx
- onOpenClientClose :: OpenState tx -> Outcome tx
- onOpenChainCloseTx :: IsTx tx => OpenState tx -> ChainStateType tx -> SnapshotNumber -> UTCTime -> Outcome tx
- onOpenClientSideLoadSnapshot :: IsTx tx => OpenState tx -> ConfirmedSnapshot tx -> Outcome tx
- onClosedChainContestTx :: IsTx tx => ClosedState tx -> ChainStateType tx -> SnapshotNumber -> UTCTime -> Outcome tx
- onClosedClientFanout :: IsTx tx => ClosedState tx -> Outcome tx
- fanoutStepStateChange :: HeadId -> NextFanoutStep tx -> UTxOType tx -> Outcome tx
- emitFanoutStep :: IsTx tx => NextFanoutStep tx -> ConfirmedSnapshot tx -> SnapshotVersion -> HeadSeed -> UTCTime -> Outcome tx
- computeFullFanoutUTxO :: IsTx tx => ClosedState tx -> UTxOType tx
- nextFanoutStep :: IsTx tx => ConfirmedSnapshot tx -> SnapshotVersion -> UTxOType tx -> UTxOType tx -> OnChainFanoutDatum -> NextFanoutStep tx
- sameOutputs :: IsTx tx => UTxOType tx -> UTxOType tx -> Bool
- fanoutUTxOFromSnapshot :: IsTx tx => ConfirmedSnapshot tx -> SnapshotVersion -> UTxOType tx
- effectiveCommitDecommit :: SnapshotVersion -> SnapshotVersion -> Maybe (UTxOType tx) -> Maybe (UTxOType tx) -> (Maybe (UTxOType tx), Maybe (UTxOType tx))
- mkFullFanoutTx :: IsTx tx => ConfirmedSnapshot tx -> SnapshotVersion -> HeadSeed -> UTCTime -> PostChainTx tx
- onClosedClientPartialFanout :: IsTx tx => ClosedState tx -> UTxOType tx -> Outcome tx
- nullOutputs :: IsTx tx => UTxOType tx -> Bool
- isSubMultisetOf :: IsTx tx => UTxOType tx -> UTxOType tx -> Bool
- onPartialFanoutClientPartialFanout :: IsTx tx => PartialFanoutState tx -> UTxOType tx -> Outcome tx
- onChainFanoutDatum :: IsTx tx => UTxOType tx -> OnChainFanoutDatum
- onClosedChainFanoutTx :: ClosedState tx -> ChainStateType tx -> UTxOType tx -> Outcome tx
- onClosedChainPartialFanoutTx :: IsTx tx => ClosedState tx -> ChainStateType tx -> UTxOType tx -> Outcome tx
- removeDistributedOutputs :: IsTx tx => [TxOutType tx] -> UTxOType tx -> UTxOType tx
- onPartialFanoutChainPartialFanoutTx :: IsTx tx => PartialFanoutState tx -> ChainStateType tx -> UTxOType tx -> Outcome tx
- onPartialFanoutChainFanoutTx :: IsTx tx => PartialFanoutState tx -> ChainStateType tx -> UTxOType tx -> Outcome tx
- closedToFanoutProgress :: ClosedState tx -> ChainStateType tx -> UTxOType tx -> UTxOType tx -> FanoutMode tx -> HeadState tx
- fanoutProgressToClosed :: PartialFanoutState tx -> ClosedState tx
- currentFanoutStep :: IsTx tx => PartialFanoutState tx -> Maybe (NextFanoutStep tx)
- matchesFanoutStep :: IsTx tx => NextFanoutStep tx -> PostChainTx tx -> Bool
- recordedSelectionMode :: IsTx tx => UTxOType tx -> UTxOType tx -> UTxOType tx -> FanoutMode tx
- repostFanoutStep :: IsTx tx => PartialFanoutState tx -> Outcome tx
- handleOutOfSync :: IsChainState tx => Environment -> UTCTime -> ChainPointType tx -> UTCTime -> SyncedStatus -> Outcome tx
- nextActiveDepositId :: IsTx tx => PendingDeposits tx -> Maybe (TxIdType tx)
- selectNextDeposit :: IsTx tx => PendingDeposits tx -> Maybe (TxIdType tx) -> Maybe tx -> Maybe (UTxOType tx) -> Maybe (TxIdType tx)
- eligibleDeposits :: IsTx tx => OpenState tx -> PendingDeposits tx -> PendingDeposits tx
- openFinalizedDeposit :: IsTx tx => HeadState tx -> Maybe (TxIdType tx)
- retainFinalizedCommit :: IsTx tx => ChainSlot -> TxIdType tx -> ConfirmedSnapshot tx -> Maybe (FinalizedSnapshot tx) -> Maybe (FinalizedSnapshot tx)
- retainFinalizedDecommit :: IsTx tx => ChainSlot -> SnapshotVersion -> ConfirmedSnapshot tx -> Maybe (FinalizedSnapshot tx) -> Maybe (FinalizedSnapshot tx)
- updateCatchingUpHead :: IsChainState tx => Environment -> Ledger tx -> UTCTime -> ChainPointTime -> PendingDeposits tx -> HeadState tx -> Input tx -> SyncedStatus -> Outcome tx
- updateInSyncHead :: IsChainState tx => Environment -> Ledger tx -> UTCTime -> ChainPointTime -> PendingDeposits tx -> HeadState tx -> Input tx -> SyncedStatus -> Outcome tx
- handleChainInput :: IsChainState tx => Environment -> Ledger tx -> UTCTime -> ChainPointTime -> PendingDeposits tx -> HeadState tx -> Input tx -> SyncedStatus -> Outcome tx
- handleClientInput :: IsChainState tx => Environment -> Ledger tx -> ChainPointTime -> PendingDeposits tx -> HeadState tx -> Input tx -> Outcome tx
- handleNetworkInput :: IsChainState tx => Environment -> Ledger tx -> ChainPointTime -> PendingDeposits tx -> HeadState tx -> Input tx -> Outcome tx
- onConnectionEvent :: Text -> Connectivity -> Outcome tx
- headIdOf :: HeadState tx -> Maybe HeadId
- eventHeadId :: StateChanged tx -> Maybe HeadId
- applyEvent :: IsChainState tx => HeadState tx -> StateChanged tx -> HeadState tx
- data Input tx
- = ClientInput {
- clientInput :: ClientInput tx
- | NetworkInput {
- ttl :: TTL
- networkEvent :: NetworkEvent (Message tx)
- | ChainInput {
- chainEvent :: ChainEvent tx
- = ClientInput {
- type TTL = Natural
- data SideLoadRequirementFailure tx
- = SideLoadInitialSnapshotMismatch
- | SideLoadSnNumberInvalid {
- requestedSn :: SnapshotNumber
- lastSeenSn :: SnapshotNumber
- | SideLoadSvNumberInvalid {
- requestedSv :: SnapshotVersion
- lastSeenSv :: SnapshotVersion
- | SideLoadUTxOToCommitInvalid {
- requestedSc :: Maybe (UTxOType tx)
- lastSeenSc :: Maybe (UTxOType tx)
- | SideLoadDepositTxIdInvalid {
- requestedDeposit :: Maybe (TxIdType tx)
- lastSeenDeposit :: Maybe (TxIdType tx)
- | SideLoadUTxOToDecommitInvalid {
- requestedSd :: Maybe (UTxOType tx)
- lastSeenSd :: Maybe (UTxOType tx)
- | SideLoadInvalidMultisignature { }
- | SideLoadUTxOSetTooLarge {
- utxoCount :: Int
- maxAllowed :: Int
- data LogicError tx
- = UnhandledInput {
- input :: Input tx
- currentHeadState :: HeadState tx
- | RequireFailed { }
- | AssertionFailed {
- message :: Text
- | NotOurHead {
- ourHeadId :: HeadId
- otherHeadId :: HeadId
- | SideLoadSnapshotFailed { }
- = UnhandledInput {
- data RequirementFailure tx
- = ReqSnNumberInvalid {
- requestedSn :: SnapshotNumber
- lastSeenSn :: SnapshotNumber
- | ReqSvNumberInvalid {
- requestedSv :: SnapshotVersion
- lastSeenSv :: SnapshotVersion
- | ReqSnNotLeader {
- requestedSn :: SnapshotNumber
- leader :: Party
- | ReqSnDecommitNotSettled
- | ReqSnCommitNotSettled
- | ReqSnBothCommitAndDecommit {
- depositTxId :: TxIdType tx
- decommitTxId :: TxIdType tx
- | ReqSnDecommitNoOutputs {
- decommitTxId :: TxIdType tx
- | InvalidMultisignature { }
- | SnapshotAlreadySigned {
- knownSignatures :: [Party]
- receivedSignature :: Party
- | AckSnNumberInvalid {
- requestedSn :: SnapshotNumber
- lastSeenSn :: SnapshotNumber
- | SnapshotDoesNotApply {
- requestedSn :: SnapshotNumber
- txid :: TxIdType tx
- error :: ValidationError
- | NoMatchingDeposit
- | RecoverBlockedByFinalizedCommit {
- depositTxId :: TxIdType tx
- | ReqSnDepositBlockedByFinalizedCommit {
- depositTxId :: TxIdType tx
- | RequestedDepositExpired {
- depositTxId :: TxIdType tx
- | RequestedDepositNotFoundLocally {
- depositTxId :: TxIdType tx
- | ReqSnUTxOSetTooLarge {
- utxoCount :: Int
- maxAllowed :: Int
- = ReqSnNumberInvalid {
- data CoordinatedHeadState tx = CoordinatedHeadState {
- localUTxO :: UTxOType tx
- localTxs :: Seq tx
- allTxs :: !(Map (TxIdType tx) tx)
- confirmedSnapshot :: ConfirmedSnapshot tx
- seenSnapshot :: SeenSnapshot tx
- currentDepositTxId :: Maybe (TxIdType tx)
- decommitTx :: Maybe tx
- version :: SnapshotVersion
- finalizedCommit :: Maybe (FinalizedSnapshot tx)
- finalizedDecommit :: Maybe (FinalizedSnapshot tx)
- data HeadState tx
- = Idle (IdleState tx)
- | Open (OpenState tx)
- | Closed (ClosedState tx)
- | FanoutProgress (PartialFanoutState tx)
- data OpenState tx = OpenState {
- parameters :: HeadParameters
- coordinatedHeadState :: CoordinatedHeadState tx
- chainState :: ChainStateType tx
- headId :: HeadId
- headSeed :: HeadSeed
- data SeenSnapshot tx
- = NoSeenSnapshot
- | LastSeenSnapshot {
- lastSeen :: SnapshotNumber
- | RequestedSnapshot { }
- | SeenSnapshot {
- snapshot :: Snapshot tx
- signatories :: Map Party (Signature (Snapshot tx))
- signableBytes :: ~ByteString
- data ClosedState tx = ClosedState {
- parameters :: HeadParameters
- confirmedSnapshot :: ConfirmedSnapshot tx
- contestationDeadline :: UTCTime
- readyToFanoutSent :: Bool
- chainState :: ChainStateType tx
- headId :: HeadId
- headSeed :: HeadSeed
- version :: SnapshotVersion
- data FanoutMode tx
- = AutoDrain
- | DistributingSelection (UTxOType tx)
- | AwaitingSelection
- data PartialFanoutState tx = PartialFanoutState {
- parameters :: HeadParameters
- confirmedSnapshot :: ConfirmedSnapshot tx
- contestationDeadline :: UTCTime
- chainState :: ChainStateType tx
- headId :: HeadId
- headSeed :: HeadSeed
- version :: SnapshotVersion
- remainingOutputs :: UTxOType tx
- distributedOutputs :: UTxOType tx
- mode :: FanoutMode tx
- data FinalizedSnapshot tx = FinalizedSnapshot {
- snapshot :: ConfirmedSnapshot tx
- observedAtSlot :: ChainSlot
- newtype IdleState tx = IdleState {
- chainState :: ChainStateType tx
- getChainState :: HeadState tx -> ChainStateType tx
- isCollectingAcks :: SeenSnapshot tx -> Bool
- mkSeenSnapshot :: IsTx tx => Snapshot tx -> Map Party (Signature (Snapshot tx)) -> SeenSnapshot tx
- seenSnapshotNumber :: SeenSnapshot tx -> SnapshotNumber
- setChainState :: ChainStateType tx -> HeadState tx -> HeadState tx
- snapshotInFlight :: SeenSnapshot tx -> Bool
- data Effect tx
- = ClientEffect {
- clientMessage :: ClientMessage tx
- | NetworkEffect { }
- | OnChainEffect {
- postChainTx :: PostChainTx tx
- = ClientEffect {
- data Outcome tx
- = Continue {
- stateChanges :: [StateChanged tx]
- effects :: [Effect tx]
- | Wait {
- reason :: WaitReason tx
- stateChanges :: [StateChanged tx]
- | Error {
- error :: LogicError tx
- = Continue {
- data StateChanged tx
- = NetworkConnected
- | NetworkDisconnected
- | PeerConnected { }
- | PeerDisconnected { }
- | NetworkVersionMismatch { }
- | NetworkClusterIDMismatch {
- clusterPeers :: Text
- misconfiguredPeers :: Text
- | HeadOpened {
- parameters :: HeadParameters
- chainState :: ChainStateType tx
- headId :: HeadId
- headSeed :: HeadSeed
- parties :: [Party]
- | TransactionReceived {
- tx :: tx
- | TransactionAppliedToLocalUTxO { }
- | SnapshotRequestDecided {
- snapshotNumber :: SnapshotNumber
- | SnapshotRequested {
- requestedSnapshot :: Snapshot tx
- newLocalTxs :: Seq tx
- newCurrentDepositTxId :: Maybe (TxIdType tx)
- | PartySignedSnapshot {
- snapshotNumber :: SnapshotNumber
- party :: Party
- signature :: Signature (Snapshot tx)
- | SnapshotConfirmed {
- headId :: HeadId
- snapshot :: Maybe (Snapshot tx)
- signatures :: MultiSignature (Snapshot tx)
- | DepositRecorded {
- chainState :: ChainStateType tx
- headId :: HeadId
- depositTxId :: TxIdType tx
- deposited :: UTxOType tx
- created :: UTCTime
- deadline :: UTCTime
- | DepositActivated {
- depositTxId :: TxIdType tx
- chainTime :: UTCTime
- deposit :: Deposit tx
- | DepositExpired {
- depositTxId :: TxIdType tx
- chainTime :: UTCTime
- deposit :: Deposit tx
- | DepositRecovered {
- chainState :: ChainStateType tx
- headId :: HeadId
- depositTxId :: TxIdType tx
- recovered :: UTxOType tx
- | CommitApproved {
- headId :: HeadId
- utxoToCommit :: UTxOType tx
- | CommitFinalized {
- chainState :: ChainStateType tx
- headId :: HeadId
- newVersion :: SnapshotVersion
- depositTxId :: TxIdType tx
- | DecommitRecorded {
- headId :: HeadId
- decommitTx :: tx
- | DecommitApproved {
- headId :: HeadId
- decommitTxId :: TxIdType tx
- utxoToDecommit :: UTxOType tx
- | DecommitInvalid {
- headId :: HeadId
- decommitTx :: tx
- decommitInvalidReason :: DecommitInvalidReason tx
- | DecommitFinalized {
- chainState :: ChainStateType tx
- headId :: HeadId
- distributedUTxO :: UTxOType tx
- newVersion :: SnapshotVersion
- | HeadClosed {
- headId :: HeadId
- snapshotNumber :: SnapshotNumber
- chainState :: ChainStateType tx
- contestationDeadline :: UTCTime
- | HeadContested {
- headId :: HeadId
- chainState :: ChainStateType tx
- contestationDeadline :: UTCTime
- snapshotNumber :: SnapshotNumber
- | HeadIsReadyToFanout {
- headId :: HeadId
- | HeadFanoutInitiated {
- headId :: HeadId
- remainingOutputs :: UTxOType tx
- | HeadPartialFanoutSelected {
- headId :: HeadId
- remainingOutputs :: UTxOType tx
- selection :: UTxOType tx
- | HeadFanoutReverted {
- headId :: HeadId
- | HeadFannedOut {
- headId :: HeadId
- finalizedOutputs :: UTxOType tx
- chainState :: ChainStateType tx
- | HeadPartialFannedOut {
- headId :: HeadId
- distributedOutputs :: UTxOType tx
- remainingOutputs :: UTxOType tx
- chainState :: ChainStateType tx
- mode :: FanoutMode tx
- | ChainRolledBack {
- chainState :: ChainStateType tx
- | TickObserved {
- chainPoint :: ChainPointType tx
- chainTime :: UTCTime
- | IgnoredHeadInitializing {
- headId :: HeadId
- contestationPeriod :: ContestationPeriod
- parties :: [Party]
- participants :: [OnChainId]
- | TxInvalid {
- headId :: HeadId
- utxo :: UTxOType tx
- transaction :: tx
- validationError :: ValidationError
- | LocalStateCleared {
- headId :: HeadId
- snapshotNumber :: SnapshotNumber
- | Checkpoint { }
- | NodeUnsynced {
- chainSlot :: ChainSlot
- chainTime :: UTCTime
- drift :: NominalDiffTime
- | NodeSynced {
- chainSlot :: ChainSlot
- chainTime :: UTCTime
- drift :: NominalDiffTime
- data WaitReason tx
- = WaitOnNotApplicableTx { }
- | WaitOnSnapshotNumber {
- waitingForNumber :: SnapshotNumber
- | WaitOnSnapshotVersion {
- waitingForVersion :: SnapshotVersion
- | WaitOnSeenSnapshot
- | WaitOnTxs {
- waitingForTxIds :: [TxIdType tx]
- | WaitOnContestationDeadline
- | WaitOnNotApplicableDecommitTx { }
- | WaitOnUnresolvedCommit {
- commitUTxO :: UTxOType tx
- | WaitOnUnresolvedDecommit {
- decommitTx :: tx
- | WaitOnDepositObserved {
- depositTxId :: TxIdType tx
- | WaitOnDepositActivation {
- depositTxId :: TxIdType tx
- | WaitOnNodeInSync {
- currentSlot :: ChainSlot
- cause :: Effect tx -> Outcome tx
- causes :: [Effect tx] -> Outcome tx
- changes :: [StateChanged tx] -> Outcome tx
- newState :: StateChanged tx -> Outcome tx
- noop :: Outcome tx
- wait :: WaitReason tx -> Outcome tx
Documentation
data NextFanoutStep tx Source #
Which transaction the next fanout step has to be, carrying the sets that
transaction needs. Decided once by nextFanoutStep so that callers deciding
what else to record and emitFanoutStep deciding what to post cannot
disagree, and so that no caller can pair a step with sets it does not go with.
Constructors
| FinalStep | The target is the whole remainder and the head is already in
|
Fields
| |
| FullFanoutStep | The target is the whole remainder but the head output still carries the
|
| PartialStep | The target is a strict subset: a non-final chunk distributing it, proved against the set the head's current datum commits to. |
Fields
| |
data OnChainFanoutDatum Source #
Which on-chain head datum the next fanout step will be posted against:
still Closed (no partial fanout has landed yet) or already FanoutProgress.
A FinalPartialFanoutTx (which burns the head tokens) is only valid once the
datum is DatumFanoutProgress.
Constructors
| DatumClosed | |
| DatumFanoutProgress |
Arguments
| :: IsChainState tx | |
| => Environment | |
| -> Ledger tx | |
| -> UTCTime | Current system time. |
| -> NodeState tx | Current NodeState to validate the command against. |
| -> Input tx | Input to be processed. |
| -> Outcome tx |
Handles inputs and converts them into StateChanged events along with
Effects, in case it is processed successfully. Later, the Node will
apply the events via aggregateNodeState, resulting in a new NodeState.
aggregateNodeState :: IsChainState tx => NodeState tx -> StateChanged tx -> NodeState tx Source #
Reflect StateChanged events onto the NodeState aggregateNodeState.
Events carrying a HeadId that does not match the current state are silently
ignored, preventing cross-head state contamination during event replay.
Events without a HeadId are always applied.
maxTxsPerSnapshot :: Int Source #
Maximum number of transaction ids per snapshot. This effectively limits our "block size" and ensures it does not grow arbitrarily with the backlog of pending transactions (localTxs). Only applied when requesting snapshots as a leader; followers accept larger requests, so this can change without a coordinated upgrade.
1000 was chosen from a sweep against 100/250 on sustained-load benchmarks (see hydra-clusterbenchBASELINES.md): per-round costs that scale with the backlog dominate at small caps (4.6-5.7x lower throughput at 100 with a deep backlog), while peak node memory was flat across the sweep.
aggregateChainStateHistory :: IsChainState tx => ChainStateHistory tx -> StateChanged tx -> ChainStateHistory tx Source #
onIdleClientInit :: Environment -> Outcome tx Source #
Arguments
| :: Environment | |
| -> ChainStateType tx | New chain state. |
| -> HeadId | |
| -> HeadSeed | |
| -> HeadParameters | |
| -> [OnChainId] | |
| -> Outcome tx |
Arguments
| :: tx | The transaction to be submitted to the head. |
| -> Outcome tx |
Arguments
| :: IsTx tx | |
| => Environment | |
| -> Ledger tx | |
| -> ChainSlot | |
| -> OpenState tx | |
| -> TTL | |
| -> PendingDeposits tx | |
| -> tx | The transaction to be submitted to the head. |
| -> Outcome tx |
Process a transaction request (ReqTx) from a party.
We apply this transaction to the seen utxo (ledger state). If not applicable,
we wait and retry later. If it applies, this yields an updated seen ledger
state. Then, we check whether we are the leader for the next snapshot and
emit a snapshot request ReqSn including this transaction if needed.
selectNextIncrementalAction Source #
Arguments
| :: IsTx tx | |
| => PendingDeposits tx | |
| -> Maybe (TxIdType tx) | |
| -> Maybe tx | Pending decommit tx |
| -> Maybe (UTxOType tx) | utxoToCommit of the last relevant confirmed snapshot |
| -> (Maybe tx, Maybe (TxIdType tx)) |
The incremental action to put in the next ReqSn: a commit or a decommit,
never both. A snapshot carrying both cannot be closed, since close and fanout
express a single incremental action (setIncrementalActionMaybe).
A commit wins: its deposit expires on-chain, while a decommit only waits. This
cannot starve the decommit, because selectNextDeposit refuses to start a
*new* commit while a decommit is pending — only one already in flight can win,
and that one stops being selected once it settles and leaves pendingDeposits.
Arguments
| :: IsTx tx | |
| => Environment | |
| -> Ledger tx | |
| -> PendingDeposits tx | |
| -> ChainSlot | |
| -> OpenState tx | |
| -> TTL | |
| -> Party | Party which sent the ReqSn. |
| -> SnapshotVersion | Requested snapshot version. |
| -> SnapshotNumber | Requested snapshot number. |
| -> [TxIdType tx] | List of transactions to snapshot. |
| -> Maybe tx | Optional decommit transaction of removing funds from the head. |
| -> Maybe (TxIdType tx) | Optional commit of additional funds into the head. |
| -> Outcome tx |
Process a snapshot request (ReqSn) from party.
This checks that s is the next snapshot number and that the party is
responsible for leading that snapshot. Then, we potentially wait until the
previous snapshot is confirmed (no snapshot is in flight), before we apply
(or wait until applicable) the requested transactions to the last confirmed
snapshot. Only then, we start tracking this new "seen" snapshot, compute a
signature of it and send the corresponding AckSn to all parties. Finally,
the pending transaction set gets pruned to only contain still applicable
transactions.
finalizedDepositTxId :: IsTx tx => Maybe (FinalizedSnapshot tx) -> Maybe (TxIdType tx) Source #
The deposit claimed by a retained finalized increment ($sel:finalizedCommit:CoordinatedHeadState),
if any. Such a deposit only resurfaces in pendingDeposits when a rollback
erased its increment, and it is settled solely by re-posting that increment
(maybeRepostIncrementTx): it must never be proposed for a snapshot again
(the deposited funds are already counted in the head) nor recovered (that
would corrupt the L2 ledger). See #2741.
Arguments
| :: IsTx tx | |
| => Environment | |
| -> PendingDeposits tx | |
| -> OpenState tx | |
| -> Party | Party which sent the AckSn. |
| -> Signature (Snapshot tx) | Signature from other party. |
| -> SnapshotNumber | Snapshot number of this AckSn. |
| -> Outcome tx |
Process a snapshot acknowledgement (AckSn) from a party.
We do require that the is from the last seen or next expected snapshot, and
potentially wait wait for the corresponding ReqSn before proceeding. If the
party hasn't sent us a signature yet, we store it. Once a signature from each
party has been collected, we aggregate a multi-signature and verify it is
correct. If everything is fine, the snapshot can be considered as the latest
confirmed one. Similar to processing a ReqTx, we check whether we are
leading the next snapshot and craft a corresponding ReqSn if needed.
Arguments
| :: IsTx tx | |
| => ChainSlot | |
| -> PendingDeposits tx | |
| -> Maybe (TxIdType tx) | Deposit claimed by the retained finalized increment of an open head, if
any (see |
| -> TxIdType tx | |
| -> Outcome tx |
Client request to recover deposited UTxO.
Transition: OpenState → OpenState
Client request to recover a deposit by posting a recover transaction on-chain.
Works in any head state (Open, Closed, or Idle after fanout). Deposits from a
previous head are never cleared from pendingDeposits on fanout, so recovery
remains available after a head closes. A new head only sees its own deposits via
depositsForHead, so old deposits are never accidentally ingested into L2.
On-chain, the deposit validator only enforces that the deadline has passed and
that the recovered outputs match the originals — it does not require the head to
still be active.
Arguments
| :: IsTx tx | |
| => HeadId | |
| -> Ledger tx | |
| -> ChainSlot | |
| -> CoordinatedHeadState tx | |
| -> tx | Decommit transaction. |
| -> Outcome tx |
requireDecommitOutputs :: IsTx tx => HeadId -> UTxOType tx -> tx -> Outcome tx -> Outcome tx Source #
Reject a decommit that materializes no output.
checkDecrement requires at least one, so such a decommit
can never settle on-chain, and recording it would block every later snapshot
(which cannot carry a different one).
Belongs after the applicability check at every call site: a transaction that does not apply is reported with the ledger's own, more precise reason.
onOpenNetworkReqDec :: IsTx tx => Environment -> Ledger tx -> TTL -> ChainSlot -> PendingDeposits tx -> OpenState tx -> tx -> Outcome tx Source #
Process the request ReqDec to decommit something from the Open head.
Transition: OpenState → OpenState
When node receives ReqDec network message it should:
- Check there is no decommit in flight:
- Alter it's state to record what is to be decommitted
- Issue a server output DecommitRequested with the relevant utxo
- Issue a ReqSn since all parties need to agree in order for decommit to
be taken out of a Head.
- Check if we are the leader
existingDeposit :: IsTx tx => PendingDeposits tx -> Maybe (TxIdType tx) -> Maybe (TxIdType tx, Deposit tx) Source #
The pending deposit that a local $sel:currentDepositTxId:CoordinatedHeadState still refers to, if any: the deposit must
be registered in pendingDeposits and not Expired.
Being registered and unexpired is what makes a recorded deposit id something the head may still
act on, and nothing else should be treated as a commit in flight. Neither of the two ways a
deposit stops being pending clears $sel:currentDepositTxId:CoordinatedHeadState: DepositExpired deliberately keeps the
deposit in the map so it can still be recovered, and DepositRecovered only deletes the map
entry. So a caller that reads $sel:currentDepositTxId:CoordinatedHeadState on its own can end up waiting on a deposit
that is unclaimable, or already gone, and that wait never resolves.
determineNextDepositStatus :: Environment -> PendingDeposits tx -> UTCTime -> PendingDeposits tx Source #
onChainTick :: IsTx tx => Environment -> PendingDeposits tx -> UTCTime -> Outcome tx Source #
Process the chain (and time) advancing in any head state.
Transition: AnyState → AnyState
This is primarily used to track deposits status changes.
onOpenChainTick :: IsTx tx => Environment -> UTCTime -> PendingDeposits tx -> OpenState tx -> Outcome tx Source #
maybeRequestSnapshotAfterVersionBump :: IsTx tx => HeadParameters -> Party -> SnapshotNumber -> Seq tx -> SnapshotVersion -> SnapshotVersion -> SeenSnapshot tx -> Maybe (TxIdType tx) -> Outcome tx Source #
If this node is the snapshot leader and there are pending local transactions, request the next snapshot with the bumped version after a commit or decommit finalises on-chain.
Guards:
* Only fires when StateChanged is ahead of the local CoordinatedHeadState: this
avoids duplicate SnapshotRequestDecided events when multiple parties
post the same on-chain tx and each posting produces a separate
finalisation observation, and it keeps a finalization re-landing after a
rollback (its StateChanged is at or behind the local version, which
never rolls back) from requesting a snapshot with a stale version.
* Skips when AckSns are already being collected (SeenSnapshot): the
in-flight snapshot will complete and maybeRequestNextSnapshot will chain
the next one with the bumped version. Firing here would use stale
$sel:localTxs:CoordinatedHeadState and cause BadInputsUTxO on other parties.
* Allows RequestedSnapshot: the in-flight ReqSn carries the old version
and will be parked by waitOnSnapshotVersion until TTL drops it, so we
re-request immediately with the new version to make progress without
waiting for the stale request's retries to exhaust.
The optional StateChanged argument is forwarded into ReqSn: commit
finalisation passes Nothing (deposit already included), while decommit
finalisation passes the next queued deposit if one is pending.
onOpenChainIncrementTx Source #
Arguments
| :: IsTx tx | |
| => Environment | |
| -> OpenState tx | |
| -> ChainStateType tx | |
| -> SnapshotVersion | New open state version |
| -> TxIdType tx | Deposit TxId |
| -> Outcome tx |
Observe a increment transaction. If the outputs match the ones of the
pending commit UTxO, then we consider the deposit/increment finalized, and remove the
increment UTxO from pendingDeposits from the local state.
Finally, if the client observing happens to be the leader, then a new ReqSn is broadcasted.
onOpenChainDecrementTx Source #
Arguments
| :: IsTx tx | |
| => Environment | |
| -> PendingDeposits tx | |
| -> OpenState tx | |
| -> ChainStateType tx | |
| -> SnapshotVersion | New open state version |
| -> UTxOType tx | Outputs removed by the decrement |
| -> Outcome tx |
setExistingDeposit :: IsTx tx => PendingDeposits tx -> Maybe (TxIdType tx) -> Maybe (TxIdType tx) Source #
Validate whether a current deposit in the local state actually exists in the map of pending deposits.
- If
currentDepositisNothing, returnsNothing. - If
currentDepositisandJusttxIdtxIdis present inpendingDepositsand notExpired, returns the originalcurrentDeposit. - Otherwise, returns
Nothing.
This is typically used to confirm that a local deposit that is to be
requested in ReqSn is indeed still pending and has not been processed or
removed.
Expired deposits are dropped rather than carried: requesting one makes every
receiving party hard-error with RequestedDepositExpired, so a deposit that
somehow became unclaimable would stall snapshots for the whole head instead of
just being abandoned by its depositor.
maybeRepostIncrementTx Source #
Arguments
| :: IsTx tx | |
| => HeadSeed | |
| -> HeadId | |
| -> HeadParameters | |
| -> PendingDeposits tx | |
| -> ConfirmedSnapshot tx | |
| -> Maybe (FinalizedSnapshot tx) | |
| -> ChainSlot | Slot rolled back to |
| -> Outcome tx |
On rollback, re-post the IncrementTx if there is a pending deposit whose confirmed snapshot contains a matching utxoToCommit. The rollback may have erased the original on-chain IncrementTx observation.
If the increment was already finalized (CommitFinalized consumed the
deposit and CoordinatedHeadState may have advanced past the incrementing
snapshot), fall back to the retained $sel:finalizedCommit:CoordinatedHeadState — but only when the
rollback reaches strictly before its observation slot; otherwise the
increment is still on chain and re-posting would only produce noise. See
#2741.
maybeRepostDecrementTx Source #
Arguments
| :: HeadSeed | |
| -> HeadId | |
| -> HeadParameters | |
| -> Maybe tx | |
| -> ConfirmedSnapshot tx | |
| -> Maybe (FinalizedSnapshot tx) | |
| -> ChainSlot | Slot rolled back to |
| -> Outcome tx |
On rollback, re-post the DecrementTx if there is a pending decommit whose confirmed snapshot contains a matching utxoToDecommit. The rollback may have erased the original on-chain DecrementTx observation.
If the decrement was already finalized (DecommitFinalized cleared
StateChanged and CoordinatedHeadState may have advanced past the decrementing
snapshot), fall back to the retained $sel:finalizedDecommit:CoordinatedHeadState — but only when the
rollback reaches strictly before its observation slot. See #2741.
onOpenClientClose :: OpenState tx -> Outcome tx Source #
Arguments
| :: IsTx tx | |
| => OpenState tx | |
| -> ChainStateType tx | New chain state. |
| -> SnapshotNumber | Closed snapshot number. |
| -> UTCTime | Contestation deadline. |
| -> Outcome tx |
Observe a close transaction. If the closed snapshot number is smaller than our last confirmed, we post a contest transaction. Also, we do schedule a notification for clients to fanout at the deadline.
Transition: OpenState → ClosedState
onOpenClientSideLoadSnapshot :: IsTx tx => OpenState tx -> ConfirmedSnapshot tx -> Outcome tx Source #
Client request to side load confirmed snapshot.
Note this is not covered by the spec as it is not reachable from an organic use of the protocol.
It must not have any effects outside of a neutral modification of the state to: * something it was before (in the case of the initial snapshot). * something it would be using side communication (in the case of a confirmed snapshot).
Besides the above, it is expected to work very much like the confirmed snapshot.
onClosedChainContestTx Source #
Arguments
| :: IsTx tx | |
| => ClosedState tx | |
| -> ChainStateType tx | New chain state. |
| -> SnapshotNumber | |
| -> UTCTime | Contestation deadline. |
| -> Outcome tx |
Observe a contest transaction. If the contested snapshot number is smaller than our last confirmed snapshot, we post a contest transaction.
Transition: ClosedState → ClosedState
onClosedClientFanout :: IsTx tx => ClosedState tx -> Outcome tx Source #
Client request to fanout the whole closed head automatically. Emits a
FanoutTx; the chain layer either lands a single full fanout (→ IdleState)
or falls back to dynamically-chunked partial fanouts. The first observed
partial fanout transitions the head to PartialFanout in AutoDrain mode,
which keeps draining the rest automatically until the final (burning) step.
This node becomes the fanout driver: it transitions into PartialFanout in
AutoDrain mode so that, as the chain layer chunks the fanout, this node
auto-continues to completion. Other parties that merely observe the resulting
partial fanout do not auto-drive (see onClosedChainPartialFanoutTx).
Transition: ClosedState → PartialFanoutState (then → IdleState once
the final fanout is observed).
fanoutStepStateChange Source #
Arguments
| :: HeadId | |
| -> NextFanoutStep tx | |
| -> UTxOType tx | The head's full remaining set |
| -> Outcome tx |
The state change that goes with a fanout step, for the callers that own the head state: taking the driver role, or recording the selection being distributed. Decided here once rather than in each handler.
The selection comes from the step rather than the caller, so a step can only ever be recorded against the set it actually distributes.
Arguments
| :: IsTx tx | |
| => NextFanoutStep tx | The step to emit, from |
| -> ConfirmedSnapshot tx | |
| -> SnapshotVersion | |
| -> HeadSeed | |
| -> UTCTime | |
| -> Outcome tx |
Post the transaction a decided NextFanoutStep calls for. The chain layer
chunks a full fanout, and sizes a partial one, dynamically.
Transactions only: any accompanying state change belongs to the caller that
owns the head state, via fanoutStepStateChange. That keeps this safe to call
where effects can be applied but state changes cannot, as in the startup
re-post in runHydraNode.
computeFullFanoutUTxO :: IsTx tx => ClosedState tx -> UTxOType tx Source #
Compute the full UTxO set to be fanned out, combining snapshot utxo with utxoToCommit/utxoToDecommit based on version.
Arguments
| :: IsTx tx | |
| => ConfirmedSnapshot tx | |
| -> SnapshotVersion | |
| -> UTxOType tx | Chunk source for the next step (the user selection, or the whole remaining set when auto-draining) |
| -> UTxOType tx | The head's full remaining set |
| -> OnChainFanoutDatum | |
| -> NextFanoutStep tx |
Decide the next fanout step. The multiset comparison here is the only one needed: callers pass the result on rather than re-deriving it.
sameOutputs :: IsTx tx => UTxOType tx -> UTxOType tx -> Bool Source #
Whether two UTxO sets have the same outputs (by content, as a multiset).
The a == b short-circuit avoids the O(n²) multiset comparison in the common
case where both arguments are the same tracked set (e.g. the auto-drain
sameOutputs remaining remaining check on every observed chunk), which is
exactly the large-UTxO heads partial fanout targets.
fanoutUTxOFromSnapshot :: IsTx tx => ConfirmedSnapshot tx -> SnapshotVersion -> UTxOType tx Source #
The fan-out-able UTxO of a confirmed snapshot at the given on-chain version: the snapshot UTxO plus a pending commit (if the increment landed on chain) or a pending decommit (if the decrement has not landed yet).
effectiveCommitDecommit Source #
Arguments
| :: SnapshotVersion | On-chain version |
| -> SnapshotVersion | Snapshot version |
| -> Maybe (UTxOType tx) | Pending commit |
| -> Maybe (UTxOType tx) | Pending decommit |
| -> (Maybe (UTxOType tx), Maybe (UTxOType tx)) |
Given the on-chain version and a snapshot's own version, decide which of a
pending commit / decommit is still to be distributed on fanout. When the
increment has landed on chain (versions match) the commit was already applied
(drop it) while a pending decommit still applies; otherwise the commit still
applies and the decommit was already paid out. Centralises the version check
shared by mkFullFanoutTx and fanoutUTxOFromSnapshot.
mkFullFanoutTx :: IsTx tx => ConfirmedSnapshot tx -> SnapshotVersion -> HeadSeed -> UTCTime -> PostChainTx tx Source #
Build the full automatic FanoutTx from a confirmed snapshot at the given
on-chain version. Shared by onClosedClientFanout and the rollback re-post in
FanoutProgress (repostFanoutStep).
onClosedClientPartialFanout :: IsTx tx => ClosedState tx -> UTxOType tx -> Outcome tx Source #
Client request to fan out a user-selected subset of a freshly closed head.
Validates the selection is a non-empty sub-multiset (by content) of the
fan-out-able UTxO, then transitions the head into PartialFanout and posts
whichever step nextFanoutStep decides on: a strict subset records the
selection and posts a PartialFanoutTx, while a selection covering the whole
head is a full fanout and drains it automatically instead.
Transition: ClosedState → PartialFanoutState
nullOutputs :: IsTx tx => UTxOType tx -> Bool Source #
Whether a UTxO has no outputs.
isSubMultisetOf :: IsTx tx => UTxOType tx -> UTxOType tx -> Bool Source #
Whether the outputs of sub are a sub-multiset (by content) of sup. This
mirrors how partial fanout tracks distributed UTxO by content rather than by
TxIn, so a user-provided selection is validated against what is actually
still in the head.
onPartialFanoutClientPartialFanout :: IsTx tx => PartialFanoutState tx -> UTxOType tx -> Outcome tx Source #
Client request to continue a selective partial fanout. Validates the
selection against the current StateChanged and emits whichever step
nextFanoutStep decides on, which is not always a recorded selection: one
covering the whole remainder finalizes the head, or, if nothing has been
distributed yet, becomes a full fanout that drains it automatically.
Transition: PartialFanoutState → PartialFanoutState
onChainFanoutDatum :: IsTx tx => UTxOType tx -> OnChainFanoutDatum Source #
The on-chain datum implied by how much has been distributed so far: still
Closed while nothing has landed, FanoutProgress once some has.
onClosedChainFanoutTx Source #
Arguments
| :: ClosedState tx | |
| -> ChainStateType tx | New chain state |
| -> UTxOType tx | |
| -> Outcome tx |
Observe a (full or final) fanout transaction, finalizing the head.
Transition: ClosedState → IdleState
onClosedChainPartialFanoutTx Source #
Arguments
| :: IsTx tx | |
| => ClosedState tx | |
| -> ChainStateType tx | New chain state |
| -> UTxOType tx | UTxO distributed in this partial fanout (keyed by new TxIn; values preserve duplicates) |
| -> Outcome tx |
Observe a partial fanout while this node is still Closed — i.e. a partial
fanout this node did NOT initiate (another party did). The fanout driver moved
to PartialFanout when it issued its Fanout/PartialFanout command, so this
handler is only reached by passive observers.
The observer transitions into PartialFanout in AwaitingSelection mode and
does not auto-drive the rest: only the driver advances the fanout. This is
what makes selective partial fanout work in a multi-party head — observers must
not steamroll the remaining UTxO the driver deliberately left.
Transition: ClosedState → PartialFanoutState
removeDistributedOutputs :: IsTx tx => [TxOutType tx] -> UTxOType tx -> UTxOType tx Source #
onPartialFanoutChainPartialFanoutTx Source #
Arguments
| :: IsTx tx | |
| => PartialFanoutState tx | |
| -> ChainStateType tx | New chain state |
| -> UTxOType tx | UTxO distributed in this partial fanout |
| -> Outcome tx |
Observe a partial fanout while in PartialFanout. Updates the remaining and
distributed sets and, depending on the current FanoutMode, either continues
draining automatically (or within the active selection) or waits for the next
PartialFanout command.
Transition: PartialFanoutState → PartialFanoutState
onPartialFanoutChainFanoutTx Source #
Arguments
| :: IsTx tx | |
| => PartialFanoutState tx | |
| -> ChainStateType tx | New chain state |
| -> UTxOType tx | |
| -> Outcome tx |
Observe the final fanout while in PartialFanout, finalizing the head with
the accumulated distributed outputs plus this final batch.
Transition: PartialFanoutState → IdleState
closedToFanoutProgress :: ClosedState tx -> ChainStateType tx -> UTxOType tx -> UTxOType tx -> FanoutMode tx -> HeadState tx Source #
Build the PartialFanout (FanoutProgress) head state from a closed head,
carrying over the snapshot/parameters and using the given chain state, remaining
and distributed UTxO and fanout StateChanged. Shared by the three Closed →
FanoutProgress transitions in aggregateNodeState.
fanoutProgressToClosed :: PartialFanoutState tx -> ClosedState tx Source #
Rebuild the ClosedState from a PartialFanoutState when reverting an
optimistic Closed → PartialFanout transition (see HeadFanoutReverted).
$sel:readyToFanoutSent:ClosedState is restored to True because a fanout is only reachable
after the head was announced ReadyToFanout.
currentFanoutStep :: IsTx tx => PartialFanoutState tx -> Maybe (NextFanoutStep tx) Source #
The step this node is currently driving. Nothing while a manual fanout
waits on the next selection, where nothing is pending.
matchesFanoutStep :: IsTx tx => NextFanoutStep tx -> PostChainTx tx -> Bool Source #
Whether a posted transaction is the one a given step posts, telling a failure of the step this node is driving from one it has already moved past.
Compares the distributed set, not just the transaction shape, since two selections can both be non-final steps. By content, because naming the same outputs under other inputs is a valid way to ask for the same set.
recordedSelectionMode Source #
Arguments
| :: IsTx tx | |
| => UTxOType tx | Distributed so far |
| -> UTxOType tx | The head's full remaining set |
| -> UTxOType tx | The recorded selection |
| -> FanoutMode tx |
The mode a recorded selection leaves the driver in. A selection covering the
whole remainder with nothing distributed yet is not distributed as a selection
at all: nextFanoutStep routes it to a full fanout and the driver auto-drains
the rest, so that is the mode it has to be recorded under.
fanoutStepStateChange never pairs the two - such a step is a
FullFanoutStep, recorded as HeadFanoutInitiated - so this only normalises
a selection recorded before that routing existed and replayed here. Without it
the re-posts (repostFanoutStep) would post the full fanout that
nextFanoutStep decides on while the mode kept claiming a selection is being
distributed, which is what every HeadPartiallyFannedOut then reports.
repostFanoutStep :: IsTx tx => PartialFanoutState tx -> Outcome tx Source #
Re-post the next fanout step after a chain rollback while in
FanoutProgress, so the fanout resumes instead of stalling with the
rolled-back transaction gone and nothing re-posted. This mirrors the
IncrementDecrement re-post on rollback (maybeRepostIncrementTx
maybeRepostDecrementTx): it uses the current best-effort bookkeeping and,
like those re-posts, assumes the rolled-back transactions re-appear — it does
not attempt to reconstruct fanout progress across a divergent rollback (the
same limitation the general rollback handling has).
Which step that is comes from currentFanoutStep, the same derivation the
revert guard uses to tell this step's failure from a superseded one's.
Arguments
| :: IsChainState tx | |
| => Environment | |
| -> UTCTime | Current system time |
| -> ChainPointType tx | Latest Chain point observed |
| -> UTCTime | Latest Chain point time representation observed |
| -> SyncedStatus | |
| -> Outcome tx |
Detect our view of the chain going out of sync and issue a NodeUnsynced
event when this is the case.
nextActiveDepositId :: IsTx tx => PendingDeposits tx -> Maybe (TxIdType tx) Source #
Find the oldest non-empty active deposit, if any. Deposits are selected
in FIFO order by their StateChanged timestamp. This mirrors the selection
logic in withNextActive used by onOpenChainTick.
Arguments
| :: IsTx tx | |
| => PendingDeposits tx | |
| -> Maybe (TxIdType tx) | |
| -> Maybe tx | Pending decommit tx |
| -> Maybe (UTxOType tx) | utxoToCommit of the last relevant confirmed snapshot |
| -> Maybe (TxIdType tx) |
Select the deposit to include in the next snapshot.
Prefers a deposit already tracked in $sel:currentDepositTxId:CoordinatedHeadState (if still pending).
Falls back to the oldest active deposit from pendingDeposits, but only
when neither a decommit is pending nor the last confirmed snapshot already
included a deposit (to avoid double-posting IncrementTx before
CommitFinalized removes the deposit).
eligibleDeposits :: IsTx tx => OpenState tx -> PendingDeposits tx -> PendingDeposits tx Source #
The deposits handlers of an open head may act on: scoped to the head and
excluding the deposit claimed by the retained finalized increment (see
finalizedDepositTxId).
openFinalizedDeposit :: IsTx tx => HeadState tx -> Maybe (TxIdType tx) Source #
The deposit blocked by a retained finalized increment of an open head, if any.
Deliberately Nothing for any other head state: an increment can only
settle into an open head, so once the head closes the retained snapshot can
never claim the deposit on-chain anymore. The resurfaced deposit's escape
hatch is then to recover it (after its deadline) and exclude the deposited
outputs from the fanout via PartialFanout — so Recover must not stay
blocked after close.
retainFinalizedCommit :: IsTx tx => ChainSlot -> TxIdType tx -> ConfirmedSnapshot tx -> Maybe (FinalizedSnapshot tx) -> Maybe (FinalizedSnapshot tx) Source #
Update the retained $sel:finalizedCommit:CoordinatedHeadState on (re-)observation of an increment
claiming depositTxId:
- the locally confirmed snapshot claims it: retain that snapshot — only it
can re-claim the deposit if a rollback erases the increment, and
CoordinatedHeadStatemay advance past it; - the already retained snapshot claims it: the increment re-landed (e.g. re-posted after a rollback) — re-stamp the observation slot, so a rollback of the re-landed increment still triggers a re-post;
- neither: the observation raced local snapshot confirmation (another
party collected the last AckSn and posted first) — retention happens
once the snapshot confirms, see the
SnapshotConfirmedbranch ofapplyEvent.
See #2741.
retainFinalizedDecommit :: IsTx tx => ChainSlot -> SnapshotVersion -> ConfirmedSnapshot tx -> Maybe (FinalizedSnapshot tx) -> Maybe (FinalizedSnapshot tx) Source #
Update the retained $sel:finalizedDecommit:CoordinatedHeadState on (re-)observation of a decrement
bumping to newVersion; the counterpart of retainFinalizedCommit. A
snapshot is tied to the decrement by identity, not by merely carrying a
decommit: only the decrement of that very snapshot bumps the version to
exactly one past the snapshot's (with a second decommit racing, the locally
confirmed snapshot may still be the previous, already settled one).
Arguments
| :: IsChainState tx | |
| => Environment | |
| -> Ledger tx | |
| -> UTCTime | Current system time. |
| -> ChainPointTime | Last known chain point time |
| -> PendingDeposits tx | |
| -> HeadState tx | Current HeadState to validate the command against. |
| -> Input tx | Input to be processed. |
| -> SyncedStatus | |
| -> Outcome tx |
Arguments
| :: IsChainState tx | |
| => Environment | |
| -> Ledger tx | |
| -> UTCTime | Current system time. |
| -> ChainPointTime | Last known chain point time |
| -> PendingDeposits tx | |
| -> HeadState tx | Current HeadState to validate the command against. |
| -> Input tx | Input to be processed. |
| -> SyncedStatus | |
| -> Outcome tx |
Arguments
| :: IsChainState tx | |
| => Environment | |
| -> Ledger tx | |
| -> UTCTime | Current system time. |
| -> ChainPointTime | Last known chain point time |
| -> PendingDeposits tx | |
| -> HeadState tx | Current HeadState to validate the command against. |
| -> Input tx | Input to be processed. |
| -> SyncedStatus | |
| -> Outcome tx |
Arguments
| :: IsChainState tx | |
| => Environment | |
| -> Ledger tx | |
| -> ChainPointTime | Last known chain point time |
| -> PendingDeposits tx | |
| -> HeadState tx | Current NodeState to validate the command against. |
| -> Input tx | Input to be processed. |
| -> Outcome tx |
Arguments
| :: IsChainState tx | |
| => Environment | |
| -> Ledger tx | |
| -> ChainPointTime | Last known chain point time |
| -> PendingDeposits tx | |
| -> HeadState tx | Current NodeState to validate the command against. |
| -> Input tx | Input to be processed. |
| -> Outcome tx |
onConnectionEvent :: Text -> Connectivity -> Outcome tx Source #
headIdOf :: HeadState tx -> Maybe HeadId Source #
Extract the HeadId from the current HeadState, if any.
eventHeadId :: StateChanged tx -> Maybe HeadId Source #
Extract the HeadId from a StateChanged event, if the event carries one.
Events that do not carry a HeadId always pass through aggregateNodeState unchanged.
applyEvent :: IsChainState tx => HeadState tx -> StateChanged tx -> HeadState tx Source #
Inputs that are processed by the head logic (the "core"). Corresponding to each of the "shell" layers, we distinguish between inputs from the client, the network and the chain.
Constructors
| ClientInput | Input received from clients via the Hydra.API. |
Fields
| |
| NetworkInput | Input received from peers via a Hydra.Network.
|
Fields
| |
| ChainInput | Input received from the chain via a Hydra.Chain. |
Fields
| |
Instances
data SideLoadRequirementFailure tx Source #
Constructors
| SideLoadInitialSnapshotMismatch | |
| SideLoadSnNumberInvalid | |
Fields
| |
| SideLoadSvNumberInvalid | |
Fields
| |
| SideLoadUTxOToCommitInvalid | |
Fields
| |
| SideLoadDepositTxIdInvalid | The side-loaded snapshot commits the same UTxO as the confirmed one, but
from a different deposit. Since the deposit is what identifies a pending
commit, this is a distinct disagreement from |
Fields
| |
| SideLoadUTxOToDecommitInvalid | |
Fields
| |
| SideLoadInvalidMultisignature | |
| SideLoadUTxOSetTooLarge | The snapshot commits to more outputs than the accumulator's trusted setup supports, so its commitment could never be computed. SECURITY: unlike its siblings, this one is raised at the client API
boundary rather than by the head logic, and it has to be: forcing an
over-capacity accumulator calls
|
Fields
| |
Instances
data LogicError tx Source #
Constructors
| UnhandledInput | |
Fields
| |
| RequireFailed | |
Fields | |
| AssertionFailed | |
Fields
| |
| NotOurHead | |
Fields
| |
| SideLoadSnapshotFailed | |
Fields | |
Instances
data RequirementFailure tx Source #
Constructors
| ReqSnNumberInvalid | |
Fields
| |
| ReqSvNumberInvalid | |
Fields
| |
| ReqSnNotLeader | |
Fields
| |
| ReqSnDecommitNotSettled | |
| ReqSnCommitNotSettled | |
| ReqSnBothCommitAndDecommit | A snapshot may settle a commit or a decommit, never both: close and fanout carry a single incremental action, so a snapshot with both would leave the head unclosable. |
Fields
| |
| ReqSnDecommitNoOutputs | A decommit that materializes no output cannot be settled: the decrement validator requires at least one decommit output. |
Fields
| |
| InvalidMultisignature | |
| SnapshotAlreadySigned | |
Fields
| |
| AckSnNumberInvalid | |
Fields
| |
| SnapshotDoesNotApply | |
Fields
| |
| NoMatchingDeposit | |
| RecoverBlockedByFinalizedCommit | The deposit is claimed by a signed snapshot whose increment settled on-chain but was rolled back: its funds are already accounted for in the head, so recovering them on-chain would corrupt the L2 ledger. The increment is re-posted instead, see #2741. |
Fields
| |
| ReqSnDepositBlockedByFinalizedCommit | The requested deposit is claimed by an already signed snapshot whose increment settled on-chain but was rolled back (which is the only way it resurfaces in the pending deposits): signing a second snapshot claiming it would double claim the deposit. Only re-posting the increment settles it, see #2741. |
Fields
| |
| RequestedDepositExpired | |
Fields
| |
| RequestedDepositNotFoundLocally | |
Fields
| |
| ReqSnUTxOSetTooLarge | |
Fields
| |
Instances
data CoordinatedHeadState tx Source #
Off-chain state of the Coordinated Head protocol.
Constructors
| CoordinatedHeadState | |
Fields
| |
Instances
The main state of the Hydra protocol state machine. It holds both, the
overall protocol state, but also the off-chain CoordinatedHeadState.
Each of the sub-types (OpenState, etc.) contain a black-box
IdleState corresponding to the ChainEvent that has been observed leading
to the state.
Note that rollbacks are currently not fully handled in the head logic and only this internal chain state gets replaced with the "rolled back to" version.
TODO: chainState would actually not be needed in the HeadState anymore as we
do not persist the HeadState and not access it in the HeadLogic either.
Constructors
| Idle (IdleState tx) | |
| Open (OpenState tx) | |
| Closed (ClosedState tx) | |
| FanoutProgress (PartialFanoutState tx) | A closed head whose UTxO is being fanned out across multiple
transactions (on-chain in the |
Instances
An Open head with a CoordinatedHeadState tracking off-chain
transactions.
Constructors
| OpenState | |
Fields
| |
Instances
| (IsTx tx, FromJSON (ChainStateType tx)) => FromJSON (OpenState tx) Source # | |
Defined in Hydra.HeadLogic.State Methods parseJSON :: Value -> Parser (OpenState tx) parseJSONList :: Value -> Parser [OpenState tx] omittedField :: Maybe (OpenState tx) | |
| (IsTx tx, ToJSON (ChainStateType tx)) => ToJSON (OpenState tx) Source # | |
Defined in Hydra.HeadLogic.State Methods toJSON :: OpenState tx -> Value toEncoding :: OpenState tx -> Encoding toJSONList :: [OpenState tx] -> Value toEncodingList :: [OpenState tx] -> Encoding | |
| Generic (OpenState tx) Source # | |
| (IsTx tx, Show (ChainStateType tx)) => Show (OpenState tx) Source # | |
| IsChainState tx => FromCBOR (OpenState tx) Source # | |
| IsChainState tx => ToCBOR (OpenState tx) Source # | |
Defined in Hydra.HeadLogic.State Methods toCBOR :: OpenState tx -> Encoding encodedSizeExpr :: (forall t. ToCBOR t => Proxy t -> Size) -> Proxy (OpenState tx) -> Size encodedListSizeExpr :: (forall t. ToCBOR t => Proxy t -> Size) -> Proxy [OpenState tx] -> Size | |
| (IsTx tx, Eq (ChainStateType tx)) => Eq (OpenState tx) Source # | |
| type Rep (OpenState tx) Source # | |
Defined in Hydra.HeadLogic.State type Rep (OpenState tx) = D1 ('MetaData "OpenState" "Hydra.HeadLogic.State" "hydra-node-2.4.1-Ld6araFLy8R3VgBqKXvTkN" 'False) (C1 ('MetaCons "OpenState" 'PrefixI 'True) ((S1 ('MetaSel ('Just "parameters") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 HeadParameters) :*: S1 ('MetaSel ('Just "coordinatedHeadState") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 (CoordinatedHeadState tx))) :*: (S1 ('MetaSel ('Just "chainState") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 (ChainStateType tx)) :*: (S1 ('MetaSel ('Just "headId") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 HeadId) :*: S1 ('MetaSel ('Just "headSeed") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 HeadSeed))))) | |
data SeenSnapshot tx Source #
Data structure to help in tracking whether we have seen or requested a ReqSn already and if seen, the signatures we collected already.
Constructors
| NoSeenSnapshot | Never saw a ReqSn. |
| LastSeenSnapshot | No snapshot in flight with last seen snapshot number as given. |
Fields
| |
| RequestedSnapshot | ReqSn was sent out and it should be considered already in flight. |
| SeenSnapshot | ReqSn for given snapshot was received. |
Fields
| |
Instances
data ClosedState tx Source #
An Closed head with an current candidate ConfirmedSnapshot, which may
be contested before the ClosedState.
Constructors
| ClosedState | |
Fields
| |
Instances
data FanoutMode tx Source #
How the node decides which UTxOs to distribute in the next fanout step
while the head is in FanoutProgress.
Constructors
| AutoDrain | Entered only via the |
| DistributingSelection (UTxOType tx) | Manual mode: keep distributing this (content-tracked) user selection, dynamically chunked, until it is exhausted. |
| AwaitingSelection | Manual mode: the previous selection has been fully distributed. Wait for
the next |
Instances
data PartialFanoutState tx Source #
A closed head whose UTxO is being distributed across multiple fanout
transactions (on-chain FanoutProgress). Holds the partial-fanout bookkeeping
that used to live in ClosedState.
Constructors
| PartialFanoutState | |
Fields
| |
Instances
data FinalizedSnapshot tx Source #
A snapshot whose settlement transaction (the increment of its commit, or
the decrement of its decommit) was observed on chain, retained when the
settlement is applied (CommitFinalized/DecommitFinalized) so it can be
re-posted if a rollback later erases it. The signed snapshot must be kept
here because CoordinatedHeadState may advance past it, and only this snapshot
can settle its commit/decommit on-chain. Kept until overwritten by the next
settlement of the same kind; $sel:observedAtSlot:FinalizedSnapshot makes stale entries inert
(re-post only when a rollback reaches strictly before it).
Constructors
| FinalizedSnapshot | |
Fields
| |
Instances
An Idle head only having a chain state with things seen on chain so far.
Constructors
| IdleState | |
Fields
| |
Instances
| FromJSON (ChainStateType tx) => FromJSON (IdleState tx) Source # | |
Defined in Hydra.HeadLogic.State Methods parseJSON :: Value -> Parser (IdleState tx) parseJSONList :: Value -> Parser [IdleState tx] omittedField :: Maybe (IdleState tx) | |
| ToJSON (ChainStateType tx) => ToJSON (IdleState tx) Source # | |
Defined in Hydra.HeadLogic.State Methods toJSON :: IdleState tx -> Value toEncoding :: IdleState tx -> Encoding toJSONList :: [IdleState tx] -> Value toEncodingList :: [IdleState tx] -> Encoding | |
| Generic (IdleState tx) Source # | |
| Show (ChainStateType tx) => Show (IdleState tx) Source # | |
| IsChainState tx => FromCBOR (IdleState tx) Source # | |
| IsChainState tx => ToCBOR (IdleState tx) Source # | |
Defined in Hydra.HeadLogic.State Methods toCBOR :: IdleState tx -> Encoding encodedSizeExpr :: (forall t. ToCBOR t => Proxy t -> Size) -> Proxy (IdleState tx) -> Size encodedListSizeExpr :: (forall t. ToCBOR t => Proxy t -> Size) -> Proxy [IdleState tx] -> Size | |
| Eq (ChainStateType tx) => Eq (IdleState tx) Source # | |
| type Rep (IdleState tx) Source # | |
Defined in Hydra.HeadLogic.State | |
getChainState :: HeadState tx -> ChainStateType tx Source #
Get the chain state in any HeadState.
isCollectingAcks :: SeenSnapshot tx -> Bool Source #
Whether AckSns are currently being collected for a snapshot.
Unlike snapshotInFlight, returns False for RequestedSnapshot — a
snapshot sent but not yet echoed is stale once the version bumps and should
not block a fresh request with the new version.
mkSeenSnapshot :: IsTx tx => Snapshot tx -> Map Party (Signature (Snapshot tx)) -> SeenSnapshot tx Source #
Smart constructor for SeenSnapshot that computes and caches
$sel:signableBytes:NoSeenSnapshot from SeenSnapshot, enforcing the invariant that they stay in sync.
seenSnapshotNumber :: SeenSnapshot tx -> SnapshotNumber Source #
Get the last seen snapshot number given a SeenSnapshot.
setChainState :: ChainStateType tx -> HeadState tx -> HeadState tx Source #
Update the chain state in any HeadState.
snapshotInFlight :: SeenSnapshot tx -> Bool Source #
Whether a snapshot is currently in-flight (requested or being signed).
Analogous to inputs, the pure head logic "core" can have effects emitted to the "shell" layers and we distinguish the same: effects onto the client, the network and the chain.
Constructors
| ClientEffect | Effect to be handled by the Hydra.API, results in sending this |
Fields
| |
| NetworkEffect | Effect to be handled by a Hydra.Network, results in a |
| OnChainEffect | Effect to be handled by a Hydra.Chain, results in a |
Fields
| |
Instances
Constructors
| Continue | Continue with the given state updates and side effects. |
Fields
| |
| Wait | Wait for some condition to be met with optional state updates. |
Fields
| |
| Error | Processing resulted in an error. |
Fields
| |
Instances
data StateChanged tx Source #
Head state changed event. These events represent all the internal state changes, get persisted and processed in an event sourcing manner.
Constructors
| NetworkConnected | |
| NetworkDisconnected | |
| PeerConnected | |
| PeerDisconnected | |
| NetworkVersionMismatch | |
Fields | |
| NetworkClusterIDMismatch | |
Fields
| |
| HeadOpened | |
Fields
| |
| TransactionReceived | |
Fields
| |
| TransactionAppliedToLocalUTxO | |
| SnapshotRequestDecided | |
Fields
| |
| SnapshotRequested | |
Fields
| |
| PartySignedSnapshot | |
Fields
| |
| SnapshotConfirmed | |
Fields
| |
| DepositRecorded | |
Fields
| |
| DepositActivated | |
Fields
| |
| DepositExpired | |
Fields
| |
| DepositRecovered | |
Fields
| |
| CommitApproved | |
Fields
| |
| CommitFinalized | |
Fields
| |
| DecommitRecorded | |
Fields
| |
| DecommitApproved | |
Fields
| |
| DecommitInvalid | |
Fields
| |
| DecommitFinalized | |
Fields
| |
| HeadClosed | |
Fields
| |
| HeadContested | |
Fields
| |
| HeadIsReadyToFanout | |
Fields
| |
| HeadFanoutInitiated | This node initiated a full automatic fanout ( |
Fields
| |
| HeadPartialFanoutSelected | A user initiated or updated a selective partial fanout. From |
Fields
| |
| HeadFanoutReverted | Revert an optimistic |
Fields
| |
| HeadFannedOut | |
Fields
| |
| HeadPartialFannedOut | |
Fields
| |
| ChainRolledBack | |
Fields
| |
| TickObserved | |
Fields
| |
| IgnoredHeadInitializing | |
Fields
| |
| TxInvalid | |
Fields
| |
| LocalStateCleared | |
Fields
| |
| Checkpoint | |
| NodeUnsynced | |
Fields
| |
| NodeSynced | |
Fields
| |
Instances
data WaitReason tx Source #
Constructors
| WaitOnNotApplicableTx | |
Fields | |
| WaitOnSnapshotNumber | |
Fields
| |
| WaitOnSnapshotVersion | |
Fields
| |
| WaitOnSeenSnapshot | |
| WaitOnTxs | |
Fields
| |
| WaitOnContestationDeadline | |
| WaitOnNotApplicableDecommitTx | |
Fields | |
| WaitOnUnresolvedCommit | |
Fields
| |
| WaitOnUnresolvedDecommit | |
Fields
| |
| WaitOnDepositObserved | |
Fields
| |
| WaitOnDepositActivation | |
Fields
| |
| WaitOnNodeInSync | |
Fields
| |
Instances
changes :: [StateChanged tx] -> Outcome tx Source #
newState :: StateChanged tx -> Outcome tx Source #
wait :: WaitReason tx -> Outcome tx Source #