Home / Input Output / hydra
Jun 05, 9-10 AM (0)
Jun 05, 10-11 AM (0)
Jun 05, 11-12 PM (0)
Jun 05, 12-1 PM (0)
Jun 05, 1-2 PM (0)
Jun 05, 2-3 PM (0)
Jun 05, 3-4 PM (1)
Jun 05, 4-5 PM (0)
Jun 05, 5-6 PM (0)
Jun 05, 6-7 PM (0)
Jun 05, 7-8 PM (0)
Jun 05, 8-9 PM (0)
Jun 05, 9-10 PM (0)
Jun 05, 10-11 PM (0)
Jun 05, 11-12 AM (0)
Jun 06, 12-1 AM (0)
Jun 06, 1-2 AM (0)
Jun 06, 2-3 AM (0)
Jun 06, 3-4 AM (0)
Jun 06, 4-5 AM (0)
Jun 06, 5-6 AM (0)
Jun 06, 6-7 AM (0)
Jun 06, 7-8 AM (0)
Jun 06, 8-9 AM (0)
Jun 06, 9-10 AM (0)
Jun 06, 10-11 AM (0)
Jun 06, 11-12 PM (0)
Jun 06, 12-1 PM (0)
Jun 06, 1-2 PM (0)
Jun 06, 2-3 PM (0)
Jun 06, 3-4 PM (0)
Jun 06, 4-5 PM (0)
Jun 06, 5-6 PM (0)
Jun 06, 6-7 PM (0)
Jun 06, 7-8 PM (0)
Jun 06, 8-9 PM (0)
Jun 06, 9-10 PM (0)
Jun 06, 10-11 PM (0)
Jun 06, 11-12 AM (0)
Jun 07, 12-1 AM (0)
Jun 07, 1-2 AM (0)
Jun 07, 2-3 AM (0)
Jun 07, 3-4 AM (0)
Jun 07, 4-5 AM (0)
Jun 07, 5-6 AM (0)
Jun 07, 6-7 AM (0)
Jun 07, 7-8 AM (0)
Jun 07, 8-9 AM (0)
Jun 07, 9-10 AM (0)
Jun 07, 10-11 AM (0)
Jun 07, 11-12 PM (0)
Jun 07, 12-1 PM (0)
Jun 07, 1-2 PM (0)
Jun 07, 2-3 PM (0)
Jun 07, 3-4 PM (0)
Jun 07, 4-5 PM (0)
Jun 07, 5-6 PM (0)
Jun 07, 6-7 PM (0)
Jun 07, 7-8 PM (0)
Jun 07, 8-9 PM (0)
Jun 07, 9-10 PM (0)
Jun 07, 10-11 PM (0)
Jun 07, 11-12 AM (0)
Jun 08, 12-1 AM (0)
Jun 08, 1-2 AM (0)
Jun 08, 2-3 AM (0)
Jun 08, 3-4 AM (0)
Jun 08, 4-5 AM (0)
Jun 08, 5-6 AM (0)
Jun 08, 6-7 AM (0)
Jun 08, 7-8 AM (0)
Jun 08, 8-9 AM (0)
Jun 08, 9-10 AM (0)
Jun 08, 10-11 AM (0)
Jun 08, 11-12 PM (1)
Jun 08, 12-1 PM (0)
Jun 08, 1-2 PM (0)
Jun 08, 2-3 PM (0)
Jun 08, 3-4 PM (0)
Jun 08, 4-5 PM (0)
Jun 08, 5-6 PM (0)
Jun 08, 6-7 PM (0)
Jun 08, 7-8 PM (0)
Jun 08, 8-9 PM (0)
Jun 08, 9-10 PM (0)
Jun 08, 10-11 PM (0)
Jun 08, 11-12 AM (0)
Jun 09, 12-1 AM (0)
Jun 09, 1-2 AM (0)
Jun 09, 2-3 AM (0)
Jun 09, 3-4 AM (0)
Jun 09, 4-5 AM (0)
Jun 09, 5-6 AM (0)
Jun 09, 6-7 AM (0)
Jun 09, 7-8 AM (0)
Jun 09, 8-9 AM (1)
Jun 09, 9-10 AM (1)
Jun 09, 10-11 AM (0)
Jun 09, 11-12 PM (0)
Jun 09, 12-1 PM (4)
Jun 09, 1-2 PM (0)
Jun 09, 2-3 PM (0)
Jun 09, 3-4 PM (0)
Jun 09, 4-5 PM (0)
Jun 09, 5-6 PM (0)
Jun 09, 6-7 PM (0)
Jun 09, 7-8 PM (0)
Jun 09, 8-9 PM (0)
Jun 09, 9-10 PM (0)
Jun 09, 10-11 PM (0)
Jun 09, 11-12 AM (0)
Jun 10, 12-1 AM (0)
Jun 10, 1-2 AM (0)
Jun 10, 2-3 AM (0)
Jun 10, 3-4 AM (0)
Jun 10, 4-5 AM (0)
Jun 10, 5-6 AM (0)
Jun 10, 6-7 AM (0)
Jun 10, 7-8 AM (0)
Jun 10, 8-9 AM (0)
Jun 10, 9-10 AM (0)
Jun 10, 10-11 AM (0)
Jun 10, 11-12 PM (2)
Jun 10, 12-1 PM (1)
Jun 10, 1-2 PM (0)
Jun 10, 2-3 PM (1)
Jun 10, 3-4 PM (0)
Jun 10, 4-5 PM (0)
Jun 10, 5-6 PM (1)
Jun 10, 6-7 PM (0)
Jun 10, 7-8 PM (0)
Jun 10, 8-9 PM (0)
Jun 10, 9-10 PM (0)
Jun 10, 10-11 PM (0)
Jun 10, 11-12 AM (0)
Jun 11, 12-1 AM (0)
Jun 11, 1-2 AM (0)
Jun 11, 2-3 AM (0)
Jun 11, 3-4 AM (0)
Jun 11, 4-5 AM (0)
Jun 11, 5-6 AM (0)
Jun 11, 6-7 AM (0)
Jun 11, 7-8 AM (49)
Jun 11, 8-9 AM (0)
Jun 11, 9-10 AM (0)
Jun 11, 10-11 AM (13)
Jun 11, 11-12 PM (0)
Jun 11, 12-1 PM (0)
Jun 11, 1-2 PM (1)
Jun 11, 2-3 PM (0)
Jun 11, 3-4 PM (0)
Jun 11, 4-5 PM (0)
Jun 11, 5-6 PM (0)
Jun 11, 6-7 PM (0)
Jun 11, 7-8 PM (0)
Jun 11, 8-9 PM (0)
Jun 11, 9-10 PM (0)
Jun 11, 10-11 PM (0)
Jun 11, 11-12 AM (0)
Jun 12, 12-1 AM (0)
Jun 12, 1-2 AM (0)
Jun 12, 2-3 AM (0)
Jun 12, 3-4 AM (0)
Jun 12, 4-5 AM (0)
Jun 12, 5-6 AM (0)
Jun 12, 6-7 AM (0)
Jun 12, 7-8 AM (0)
Jun 12, 8-9 AM (1)
Jun 12, 9-10 AM (0)
77 commits this week Jun 05, 2026 - Jun 12, 2026
Enforce strict value equality in Close/Contest: prevent stuck-head griefing
  mustPreserveHeadValue used geq (≥) rather than == for the Close and
  Contest transitions. This allowed a malicious contester to add extra ADA
  to the head output — a griefing attack where the added value would cause
  the fanout's strict == conservation check to fail, permanently locking
  the head.

  The geq was a defensive measure for datum-growth min-UTxO scenarios, but
  neither closeTx nor contestTx ever changes the head value (both call
  modifyTxOutDatum only), and using geq to "top up" would break the fanout
  anyway. The correct invariant is ==, which matches the spec.

  Add ContestIncreaseHeadValue mutation test to document the property.

Signed-off-by: Sasha Bogicevic <[email protected]>
Fix partial fanout StaleChainState when decommit applied before close
  When a DecrementTx was applied on-chain before the head was closed
  (snapshotVersion != openVersion), partialFanout passed fullUTxO to
  buildAndVerifyAccumulator. But the closed datum's accumulatorCommitment
  was built from utxoForProof (snapshot UTxO including the decommit set),
  so the check always failed with StaleChainState, leaving the head stuck.

  Separate the accumulator proof input from the distribution set by adding
  a proofUTxO parameter to partialFanout and findFittingFanoutTx. The
  FanoutTx call site now passes utxoForProof as proofUTxO so it matches
  what the closed datum was committed to, while fullUTxO (the actual
  distribution set) is used unchanged for fanout outputs.

Signed-off-by: Sasha Bogicevic <[email protected]>
Small cleanups: nullary IncrementalAction constructors and isG1Generator helper
  Remove UTxO fields from ToCommit and ToDecommit constructors of
  IncrementalAction — the UTxO was already ignored at every callsite.
  Update setIncrementalActionMaybe and pattern matches in Close.hs and
  Contest.hs accordingly.

  Extract repeated BLS12-381 generator check into isG1Generator helper in
  Head.hs, and add a comment explaining why mustConserveValue uses geq
  rather than == (min-UTxO overhead on the head output).

Signed-off-by: Sasha Bogicevic <[email protected]>
Unify Close/Contest redeemers: collapse Dec/Inc variants
  CloseUnusedDec + CloseUnusedInc → CloseUnused
  CloseUsedDec  + CloseUsedInc   → CloseUsed
  ContestCurrent + ContestUnusedDec + ContestUnusedInc → ContestUnused
  ContestUsedDec + ContestUsedInc → ContestUsed

  The Dec/Inc suffix was redundant: the accumulator hash already
  cryptographically commits to the nature of the pending UTxO action,
  so the on-chain validator does not need to distinguish them. The
  Unused/Used split (which version to use for signature verification)
  is preserved.

  As a consequence, contestTx no longer needs IncrementalAction at all
  — the version comparison alone determines the redeemer — so the
  parameter and the BothCommitAndDecommitInContest error variant are
  removed.

  Also bounds genStOpen and genFanoutTx UTxO sizes to [1..5] so full
  fanout transactions with maximumNumberOfParties stay within the
  14M memory execution budget.

Signed-off-by: Sasha Bogicevic <[email protected]>
Fix genFanoutTx and tx-cost bench to correctly evaluate fanout transactions
  The init head output carries only tokens (0 ADA), so fanout evaluation was
  always failing with HeadValueIsNotPreserved (H4). Additionally, toCommit' was
  unconditionally generated causing accumulator commitment mismatches (H39) when
  version == openVersion.

  - genStOpen now generates a real u0 and inflates the head output with its
  total value so close/fanout operations can cover snapshot outputs
  - genFanoutTx only generates commit UTxO when openVersion /= version, keeping
  the accumulator commitment in the closed datum consistent with what fanoutTx
  builds; returns the inflated spendableUTxO so evaluation sees the correct
  head input value
  - forAllFanout uses the returned spendableUTxO instead of getKnownUTxO stClosed
  - TxCost genFanoutTx returns utxoToFanout (inflated) as the lookup UTxO,
  fixing the same value mismatch that caused the FanOut table to be empty

Signed-off-by: Sasha Bogicevic <[email protected]>
Fix FanOut tx-cost bench
  genFanoutTx generated a snapshot with utxoToCommit/utxoToDecommit but
  called unsafeFanout with mempty mempty — the BLS proof was built for a
  different accumulator than the one committed in the closed head datum,
  so every evaluateTx call failed script validation and the FanOut table
  produced no rows.

  Fix: pass utxoToCommit and utxoToDecommit through to unsafeFanout, and
  inflate the head output value by the total fanout value (same pattern as
  computePartialFanOutNominalCost) so mustConserveValue passes.

Signed-off-by: Sasha Bogicevic <[email protected]>
Fix CRS substitution: validate txOutAddress in withCRSLookup
  withCRSLookup only checked txOutReferenceScript (script hash) but not
  txOutAddress. An attacker could create a UTxO at any address, attach the
  legitimate CRS script as a reference, and provide malicious G2 identity
  elements as datum — bypassing the KZG membership check and fanning out
  arbitrary outputs after the contestation deadline.

  Fix: add addressCredential (txOutAddress resolved) == ScriptCredential
  expectedHash check. Since expectedHash is already compiled into the head
  validator, no new parameters are needed. Move CRS UTxO publishing to the
  CRS script address (which already always-fails) so the on-chain check is
  self-consistent. Update the test generator to match.

  Add WrongAddressCRS mutation tests to FinalPartialFanout, PartialFanout,
  and FanOut (the full fanout path had no CRS mutation coverage at all).

Signed-off-by: Sasha Bogicevic <[email protected]>
Fix stale mutation test error code expectations in hydra-tx
  Four mutation tests expected error codes that no longer match the
  validator's check ordering or error code assignments:

  - IncrementDifferentClaimRedeemer: H44 (DepositInputNotFound) is not
    referenced anywhere in the on-chain validator; H43 (DepositNotSpent)
    is the actual check that fires when the increment TxOutRef is absent
    from tx inputs.

  - DropDecommitOutput and ChangeDecrementedValue: signatures cover
    (headId, version, snapshotNumber, accumulatorHash), not output values,
    so removing or changing a decommit output fails mustDecreaseValue
    (H4) before any signature check (H12) can run.

  - ContestFromDifferentHead: replacePolicyIdWith mutates token values
    but not the datum's headId field, so mustBeSignedByParticipant fires
    H5 before the signature check reaches H37.

Signed-off-by: Sasha Bogicevic <[email protected]>
Bit more benchmarking information on PRs (#2635)
Adds some more benchmarking information to the PRs, and in particular a
kind of "TPS" count; both in the case where we wait for TxValid, and in
the case where we just fire as many txns as possible, then just count
how many are in the confirmed snapshots.

### Todo

- [x] Double check TPS is sensible
- [x] Have a scenario where we don't wait for TxValid; but just submit
repeatedly
- [x] More scenarios?
- [ ] ~Tracking over time~ (Later)
- [x] ~Perhaps run shorter benchmarks on PR and longer ones nightly?~
Half done; do the other half later
Add regression test for FinalPartialFanoutTx with pending deposit UTxO
  When snapshotVersion < version (an IncrementTx confirmed on L1 after the
  last snapshot), computeFullFanoutUTxO includes utxoToCommit in the full
  fanout set. After a PartialFanoutTx distributes all other outputs, a
  FinalPartialFanoutTx must distribute the pending deposit UTxO alone.

  Adds genClosedStateWithPendingCommit to the testlib and a property test
  that verifies finalPartialFanout succeeds in this scenario — guarding
  against the StaleChainState failure observed in practice when the
  in-memory deposit UTxO accumulator element diverges from the on-chain
  commitment.

Signed-off-by: Sasha Bogicevic <[email protected]>
Fix genCloseTx head UTxO value for strict mustPreserveHeadValue
  genStOpen inflates the head UTxO by u0Value but genCloseTx was not
  adjusting it before calling unsafeClose. With mustPreserveHeadValue
  now using strict equality (instead of geq), the head value must be
  exactly headAdaOverhead + confirmedUTxO + decommit at close time,
  or the Plutus script fires H65. Adjust the UTxO in genCloseTx the
  same way genClosedStateForFanout and genFanoutTx already do.

Signed-off-by: Sasha Bogicevic <[email protected]>
Fix genCloseTx head value mismatch in close property tests
  genStOpen inflates the head UTxO by u0Value but leaves headAdaOverhead
  in the open datum unchanged. genCloseTx now adjusts the head UTxO value
  to headAdaOverhead + confirmedUTxOValue + decommitValue before building
  the close tx, so closeTx computes the correct headAdaOverhead' for the
  closed datum (fixes H65).

  forAllClose was discarding the adjusted UTxO returned by genCloseTx and
  recomputing it from the original inflated OpenState, causing the
  evaluator to see a different input value than what the tx was built
  against (fixes H4/HeadValueIsNotPreserved).

Signed-off-by: Sasha Bogicevic <[email protected]>
Fix partial fanout StaleChainState when decommit applied before close
  When a DecrementTx was applied on-chain before the head was closed
  (snapshotVersion != openVersion), partialFanout passed fullUTxO to
  buildAndVerifyAccumulator. But the closed datum's accumulatorCommitment
  was built from utxoForProof (snapshot UTxO including the decommit set),
  so the check always failed with StaleChainState, leaving the head stuck.

  Separate the accumulator proof input from the distribution set by adding
  a proofUTxO parameter to partialFanout and findFittingFanoutTx. The
  FanoutTx call site now passes utxoForProof as proofUTxO so it matches
  what the closed datum was committed to, while fullUTxO (the actual
  distribution set) is used unchanged for fanout outputs.

Signed-off-by: Sasha Bogicevic <[email protected]>
Add contract tests for CloseAny, CloseInitial mutations, and ContestCommit
  Three coverage gaps in the contract test suite are filled:

  - CloseAny: new test module exercising the NoThing → CloseAny redeemer
    path, including the unique on-chain snapshotNumber > 0 check (21
    mutations; signature/number failures all map to FailedCloseAny because
    that path wraps both checks in a single traceIfFalse).

  - CloseInitial: expanded from 2 to 15 mutations, adding the three
    CloseInitial-specific checks (snapshotNumber == 0, version == 0,
    accumulatorCommitment == G1 generator → FailedCloseInitial) plus the
    ten shared close checks (parties, headId, contestation period, validity
    bounds, minting, contesters, value, required signer).

  - ContestInc: new test module contesting with a commit-type snapshot
    (utxoToCommit = Just depositedUTxO), exercising the ToCommit
    accumulator commitment path that ContestDec (decommit path) left
    uncovered.

Signed-off-by: Sasha Bogicevic <[email protected]>