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 expected error code for MutateSnapshotVersion in CloseInitial mutation test
mustNotChangeVersion (H13) fires before mustBeValidSnapshot (H28) in checkClose because && evaluates left-to-right; setting closedVersion != openVersion always triggers H13 first. Signed-off-by: Sasha Bogicevic <[email protected]>
Test closeTx for both commit-pending and commit-applied scenarios
Add CloseCommitUnused and CloseCommitUsed contract tests to cover the (ToCommit, True) and (ToCommit, False) branches of headAdaOverhead computation in closeTx, which were previously untested. Both new test suites include a healthy-tx property and a full mutation generator. 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]>
Changelog
Also reduce a diff in the test code 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]>
Regenerate golden files
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]>
Golden files
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 golden test locking Increment redeemer to constructor index 0
deposit.ak checks the head redeemer constructor index via raw CBOR arithmetic; if Increment ever shifts from index 0, deposits are permanently locked. The test catches that at build time. Signed-off-by: Sasha Bogicevic <[email protected]>
Simplify findLargestFitting to a single callback
Merge the two-callback interface (mkTx + fitsCheck) into one (Int -> m (Maybe tx)) where Nothing means doesn't fit and Just tx means fits. This enforces at the type level that construction and checking always happen together. Also rename mkTxM to buildTx in findFittingFanoutTx's where bindings. 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]>