Context & versions
- hydra-node on master .
- Head configured with L2 --ledger-protocol-parameters where utxoCostPerByte = 0 (the zero-fee setup suggested by the tutorial). The L2 ledger consequently accepts outputs below L1 min-ADA, e.g. token-only or tiny-lovelace outputs.
- L1 with standard protocol parameters (coinsPerUTxOByte = 4310).
Steps to reproduce
- Open a head with utxoCostPerByte = 0 in the L2 ledger parameters.
- Submit an L2 NewTx creating an output below L1 min-ADA (e.g. 100 000 lovelace) — the snapshot confirms fine.
- Close the head, wait for the contestation deadline, then Fanout.
Actual behavior
Every fanout attempt fails locally with ScriptFailedInWallet and head validator error H39 FanoutUTxOHashMismatch — the transaction is never submitted. The head stays in Closed indefinitely and funds are locked: a sub-min-ADA output can never exist on L1, so not even partial fanout can finalize the head, and the head tokens plus ada overhead remain locked as well.
ScriptFailedInWallet, redeemerPtr = ConwaySpending (AsIx 0)
CekError: The machine terminated because of an error ... ["H39","PT5"]
The root cause has two layers:
- An output below L1 min-ADA cannot be an output of any valid L1 transaction, so a head closed in this state is unfannable by construction.
- It surfaces as a confusing H39 instead of an honest OutputTooSmallUTxO because of the internal wallet: coverFee_ applies ensureMinCoinTxOut to all transaction outputs before running script estimation (Hydra.Chain.Direct.Wallet.coverFee_), silently bumping the dust output to min-ADA (100 000 → ~849 070 for a simple payment address). The bumped output no longer matches the accumulator commitment that the parties signed in the snapshot and that is stored in the closed datum, so the fanout membership check fails on every retry.
Expected behavior
- The node prevents accidentally closing a head whose UTxO cannot be fanned out on L1: SafeClose refuses to close (the API schema already advertises a pre-close safety check, but it currently only covers non-ADA assets), and plain Close at least emits a warning listing the offending outputs. The checks must run against L1 protocol parameters: per-output min-ADA, closing value covered by the head output, per-output size.
- The internal wallet does not silently mutate outputs of hash-committed protocol transactions — failing loudly would surface the real error (OutputTooSmallUTxO) instead of a misleading script failure.
Context & versions
Steps to reproduce
Actual behavior
Every fanout attempt fails locally with ScriptFailedInWallet and head validator error H39 FanoutUTxOHashMismatch — the transaction is never submitted. The head stays in Closed indefinitely and funds are locked: a sub-min-ADA output can never exist on L1, so not even partial fanout can finalize the head, and the head tokens plus ada overhead remain locked as well.
ScriptFailedInWallet, redeemerPtr = ConwaySpending (AsIx 0)
CekError: The machine terminated because of an error ... ["H39","PT5"]
The root cause has two layers:
Expected behavior