hydra-node
Safe HaskellSafe-Inferred
LanguageGHC2021

Hydra.Chain.Direct.Handlers

Description

Provide infrastructure-independent "handlers" for posting transactions and following the chain.

This module encapsulates the transformation logic between cardano transactions and HydraNode abstractions PostChainTx and OnChainTx, and maintenance of on-chain relevant state.

Synopsis

Documentation

data LocalChainState m tx Source #

Handle of a mutable local chain state that is kept in the direct chain layer.

Constructors

LocalChainState 

Fields

newLocalChainState :: forall m tx. (IsChainState tx, MonadLabelledSTM m) => ChainStateHistory tx -> m (LocalChainState m tx) Source #

Initialize a new local chain state from a given chain state history.

Posting Transactions

type SubmitTx m = Tx -> m () Source #

A callback used to actually submit a transaction to the chain.

type GetTimeHandle m = m TimeHandle Source #

A way to acquire a TimeHandle

mkChain Source #

Arguments

:: (MonadSTM m, MonadThrow (STM m)) 
=> Tracer m CardanoChainLog 
-> GetTimeHandle m

Means to acquire a new TimeHandle.

-> TinyWallet m 
-> ChainContext 
-> DepositPeriod

Configured deposit period, bounding the validity of deposit txs.

-> LocalChainState m Tx 
-> SubmitTx m 
-> Chain Tx m 

Create a Chain component for posting "real" cardano transactions.

This component does not actually interact with a cardano-node, but creates cardano transactions from PostChainTx transactions emitted by a HydraNode, balancing and signing them using given TinyWallet, before handing it off to the given SubmitTx callback. There is also a draftTx option for drafting a commit tx on behalf of the user using their selected utxo.

NOTE: Given the constraints on m this function should work within IOSim and does not require any actual IO to happen which makes it highly suitable for simulations and testing.

rejectUnobservableDeposit :: NetworkId -> Tx -> Either (PostTxError Tx) () Source #

Reject a drafted deposit the node would never observe on chain.

observeDepositTx requires the deposit output's value to equal the value recorded in its datum. Balancing tops every output up to its minimum ADA, and the deposit output needs more than the deposited outputs themselves because its inline datum embeds them. The datum keeps the original value, so the finalized transaction would be ignored by every node, never reach L2 and only be recoverable by hand. Report how much ADA is missing instead.

rejectLowDeposits :: PParams LedgerEra -> UTxO -> Either (PostTxError Tx) () Source #

rejectByronAddresses :: UTxO -> Either (PostTxError Tx) () Source #

Reject any UTxO containing a Byron address, which cannot be represented in the Hydra head protocol.

rejectOversizedDeposit Source #

Arguments

:: PParams LedgerEra

Layer 1 protocol parameters (from $sel:getPParams:TinyWallet of the wallet, NOT the L2 ledger parameters passed to the draft endpoint).

-> ChainContext 
-> UTxO

Spendable UTxO containing the current head output.

-> HeadId 
-> ConfirmedSnapshot Tx

Current confirmed snapshot, basis for the dry-run increment.

-> Tx

Drafted (unbalanced) deposit transaction.

-> SlotNo

Upper validity slot for the dry-run increment.

-> Either (PostTxError Tx) () 

Reject deposits which could never be claimed: builds a dry-run increment transaction for the drafted deposit and rejects with DepositTooLarge when it would violate layer 1 ledger limits - the maximum transaction size, or the maximum serialized value size of the merged head output.

serializedValueSize :: PParams LedgerEra -> Value -> Natural Source #

Serialized size of a value, computed exactly like the ledger's OutputTooBigUTxO check. NOTE: Keep in sync with validateOutputTooBigUTxO.

incrementTxBalancingMargin :: Natural Source #

Byte headroom added to the unbalanced dry-run increment transaction when comparing against the maximum transaction size, covering what $sel:coverFee:TinyWallet and $sel:sign:TinyWallet add to the real increment: a fee input (~40 bytes), a collateral input (~40 bytes), an ada-only change output (~70 bytes), the script integrity hash (~37 bytes), the fee field (~5 bytes), one key witness (~102 bytes), and wider integer encodings for the estimated execution units, snapshot number and validity slot (~40 bytes) - around 334 bytes in total. We add some overhead and use 512 just to be more safe.

finalizeTx :: MonadThrow m => TinyWallet m -> ChainContext -> UTxO -> UTxO -> Tx -> m Tx Source #

Balance and sign the given partial transaction.

Following the Chain

data ChainSyncHandler m Source #

A handler that takes care of following the chain.

Constructors

ChainSyncHandler 

Fields

data TimeConversionException Source #

Conversion of a slot number to a time failed. This can be usually be considered an internal error and may be happening because the used era history is too old.

Constructors

TimeConversionException 

Fields

chainSyncHandler Source #

Arguments

:: forall m. (MonadSTM m, MonadThrow m) 
=> Tracer m CardanoChainLog

Tracer for logging

-> ChainCallback Tx m 
-> (SlotNo -> GetTimeHandle m)

Means to acquire a TimeHandle able to convert the given slot.

-> ChainContext

Contextual information about our chain connection.

-> LocalChainState m Tx 
-> ChainSyncHandler m

A chain-sync handler to use in a local-chain-sync client.

Creates a ChainSyncHandler that can notify the given callback of events happening on-chain.

This forms the other half of a ChainComponent along with mkChain but is decoupled from actual interactions with the chain.

A TimeHandle is needed to do `SlotNo -> POSIXTime` conversions for Tick events.

Throws TimeConversionException when a received block's SlotNo cannot be converted to a UTCTime with the given TimeHandle.

prepareTxToPost Source #

Arguments

:: forall m. (MonadSTM m, MonadThrow (STM m)) 
=> TimeHandle 
-> ChainContext 
-> UTxOType Tx

Spendable UTxO

-> PostChainTx Tx 
-> STM m Tx 

canBeVerifiedOnChain :: Int -> Bool Source #

Whether a fanout distributing this many outputs can be verified on chain at all, and so is worth building.

Above deployedFanoutBatchSize the head validator rejects the membership proof however cheap the transaction is, and building the transaction means computing that proof over the whole set — discarded on every fanout step. Callers guard on this rather than passing the built transaction and discarding it, so the expensive call is unreachable rather than merely unforced.

findLargestFitting Source #

Arguments

:: Monad m 
=> (Int -> m (Maybe tx))

Construct and check a transaction; Just tx = fits, Nothing = doesn't fit; may throw on structural failure

-> Int

Upper bound of chunk sizes to search (inclusive)

-> m (Either () tx) 

Binary search for the largest chunk size in [1..maxChunk] for which tryTx returns Just. Assumes the predicate is monotone: if size n fits, all sizes < n also fit. Uses upper-mid so the search terminates correctly when hi = lo + 1. Returns 'Left ()' if no size fits. tryTx may throw to abort the search early.

fitsTx :: Monad m => Tracer m CardanoChainLog -> (Tx -> m Bool) -> (Tx -> UTxO -> m (Either EvaluationError EvaluationReport)) -> UTxO -> Tx -> m Bool Source #

Check whether a transaction fits within protocol size and script execution limits. Size check is cheap so it short-circuits before the expensive UPLC evaluation. Structural errors (script failures, protocol parameter conversion errors) are traced via the provided tracer; transient misses (size exceeded, budget overrun) are silent.

findFittingFanoutTx Source #

Arguments

:: forall m. MonadThrow m 
=> Tracer m CardanoChainLog 
-> TinyWallet m 
-> ChainContext 
-> UTxO

Spendable UTxO containing head output

-> TxIn

Seed TxIn

-> Maybe Tx

Preferred tx to try first (FanoutTx or FinalPartialFanoutTx); Nothing skips straight to the fallback loop

-> UTxO

UTxO for the accumulator check in the partial-fanout fallback (matches the on-chain datum)

-> UTxO

UTxOs to distribute in the partial-fanout fallback

-> Int

Upper bound (inclusive) of chunk sizes to search in the fallback. For the final/full fanout fallback this is size - 1 (the preferred tx handles the full set; a partial fanout must leave at least one output). For an explicit non-final partial fanout this is the full size: the selection is a strict subset of the head's remaining UTxO, so even distributing all of it leaves the unselected remainder in the accumulator and mustNotBeLastBatch holds (see the caller for what keeps that true). The search caps this at deployedFanoutBatchSize regardless: no larger subset can be verified, however cheap its transaction is.

-> SlotNo

Contestation deadline as SlotNo

-> m Tx 

Try the preferred transaction first; if it doesn't fit within the script execution budget or exceeds the maximum transaction size, fall back to a binary search over partial fanout chunk sizes. Returns the largest chunk that fits, minimising the number of fanout steps.

Error mapping: * StaleChainState from preparePartialFanoutStalePartialFanoutTx (race condition; HeadLogic silently ignores it and the chain observation loop triggers the correct next step). * Any other PartialFanoutErrorFailedToConstructPartialFanoutTx (structural mismatch that will not resolve on retry). * No chunk fits within budget → FailedToConstructPartialFanoutTx (budget exhaustion; also not a race condition).

maxGraceTime :: NominalDiffTime Source #

Maximum delay we put on the upper bound of transactions to fit into a block. NOTE: This is highly depending on the network. If the security parameter and epoch length result in a short horizon, this is problematic.

data StartingDecision Source #

Constructors

FromProvided ChainPoint 
FromTip ChainPoint 
FromPersisted 

Fields

  • chainPoint :: ChainPoint
     
  • startChainFromSet :: Bool

    Whether the user-provided --start-chain-from point was set but ignored, because it was older than persisted points.

Instances

Instances details
ToJSON StartingDecision Source # 
Instance details

Defined in Hydra.Chain.Direct.Handlers

Generic StartingDecision Source # 
Instance details

Defined in Hydra.Chain.Direct.Handlers

Associated Types

type Rep StartingDecision :: Type -> Type Source #

Show StartingDecision Source # 
Instance details

Defined in Hydra.Chain.Direct.Handlers

Eq StartingDecision Source # 
Instance details

Defined in Hydra.Chain.Direct.Handlers

type Rep StartingDecision Source # 
Instance details

Defined in Hydra.Chain.Direct.Handlers

type Rep StartingDecision = D1 ('MetaData "StartingDecision" "Hydra.Chain.Direct.Handlers" "hydra-node-2.4.1-Ld6araFLy8R3VgBqKXvTkN" 'False) (C1 ('MetaCons "FromProvided" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 ChainPoint)) :+: (C1 ('MetaCons "FromTip" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 ChainPoint)) :+: C1 ('MetaCons "FromPersisted" 'PrefixI 'True) (S1 ('MetaSel ('Just "chainPoint") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 ChainPoint) :*: S1 ('MetaSel ('Just "startChainFromSet") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Bool))))

data CardanoChainLog Source #

Constructors

ToPost 

Fields

PostingTx 

Fields

PostedTx 

Fields

PostingFailed 

Fields

RolledForward 

Fields

RolledBackward 

Fields

Wallet TinyWalletLog 
StartingChainDecision StartingDecision 
BlockfrostTransientError 

Fields

PartialFanoutFailed 

Fields

Instances

Instances details
ToJSON CardanoChainLog Source # 
Instance details

Defined in Hydra.Chain.Direct.Handlers

Generic CardanoChainLog Source # 
Instance details

Defined in Hydra.Chain.Direct.Handlers

Associated Types

type Rep CardanoChainLog :: Type -> Type Source #

Show CardanoChainLog Source # 
Instance details

Defined in Hydra.Chain.Direct.Handlers

Eq CardanoChainLog Source # 
Instance details

Defined in Hydra.Chain.Direct.Handlers

type Rep CardanoChainLog Source # 
Instance details

Defined in Hydra.Chain.Direct.Handlers

type Rep CardanoChainLog = D1 ('MetaData "CardanoChainLog" "Hydra.Chain.Direct.Handlers" "hydra-node-2.4.1-Ld6araFLy8R3VgBqKXvTkN" 'False) (((C1 ('MetaCons "ToPost" 'PrefixI 'True) (S1 ('MetaSel ('Just "toPost") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 (PostChainTx Tx))) :+: C1 ('MetaCons "PostingTx" 'PrefixI 'True) (S1 ('MetaSel ('Just "txId") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 TxId))) :+: (C1 ('MetaCons "PostedTx" 'PrefixI 'True) (S1 ('MetaSel ('Just "txId") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 TxId)) :+: (C1 ('MetaCons "PostingFailed" 'PrefixI 'True) (S1 ('MetaSel ('Just "tx") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Tx) :*: S1 ('MetaSel ('Just "postTxError") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 (PostTxError Tx))) :+: C1 ('MetaCons "RolledForward" 'PrefixI 'True) (S1 ('MetaSel ('Just "point") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 ChainPoint) :*: S1 ('MetaSel ('Just "receivedTxIds") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 [TxId]))))) :+: ((C1 ('MetaCons "RolledBackward" 'PrefixI 'True) (S1 ('MetaSel ('Just "point") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 ChainPoint)) :+: C1 ('MetaCons "Wallet" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 TinyWalletLog))) :+: (C1 ('MetaCons "StartingChainDecision" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 StartingDecision)) :+: (C1 ('MetaCons "BlockfrostTransientError" 'PrefixI 'True) (S1 ('MetaSel ('Just "reason") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "retryDelay") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Int)) :+: C1 ('MetaCons "PartialFanoutFailed" 'PrefixI 'True) (S1 ('MetaSel ('Just "reason") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text))))))