May 28, 7-8 PM (44)
May 28, 8-9 PM (34)
May 28, 9-10 PM (17)
May 28, 10-11 PM (36)
May 28, 11-12 AM (32)
May 29, 12-1 AM (12)
May 29, 1-2 AM (13)
May 29, 2-3 AM (4)
May 29, 3-4 AM (3)
May 29, 4-5 AM (0)
May 29, 5-6 AM (2)
May 29, 6-7 AM (5)
May 29, 7-8 AM (16)
May 29, 8-9 AM (37)
May 29, 9-10 AM (34)
May 29, 10-11 AM (69)
May 29, 11-12 PM (25)
May 29, 12-1 PM (44)
May 29, 1-2 PM (66)
May 29, 2-3 PM (61)
May 29, 3-4 PM (26)
May 29, 4-5 PM (26)
May 29, 5-6 PM (83)
May 29, 6-7 PM (11)
May 29, 7-8 PM (19)
May 29, 8-9 PM (15)
May 29, 9-10 PM (9)
May 29, 10-11 PM (27)
May 29, 11-12 AM (8)
May 30, 12-1 AM (16)
May 30, 1-2 AM (25)
May 30, 2-3 AM (1)
May 30, 3-4 AM (7)
May 30, 4-5 AM (2)
May 30, 5-6 AM (11)
May 30, 6-7 AM (0)
May 30, 7-8 AM (2)
May 30, 8-9 AM (11)
May 30, 9-10 AM (13)
May 30, 10-11 AM (10)
May 30, 11-12 PM (5)
May 30, 12-1 PM (8)
May 30, 1-2 PM (5)
May 30, 2-3 PM (18)
May 30, 3-4 PM (5)
May 30, 4-5 PM (1)
May 30, 5-6 PM (9)
May 30, 6-7 PM (9)
May 30, 7-8 PM (1)
May 30, 8-9 PM (5)
May 30, 9-10 PM (4)
May 30, 10-11 PM (27)
May 30, 11-12 AM (12)
May 31, 12-1 AM (17)
May 31, 1-2 AM (0)
May 31, 2-3 AM (1)
May 31, 3-4 AM (1)
May 31, 4-5 AM (0)
May 31, 5-6 AM (0)
May 31, 6-7 AM (7)
May 31, 7-8 AM (4)
May 31, 8-9 AM (10)
May 31, 9-10 AM (3)
May 31, 10-11 AM (4)
May 31, 11-12 PM (4)
May 31, 12-1 PM (1)
May 31, 1-2 PM (2)
May 31, 2-3 PM (24)
May 31, 3-4 PM (16)
May 31, 4-5 PM (2)
May 31, 5-6 PM (1)
May 31, 6-7 PM (2)
May 31, 7-8 PM (2)
May 31, 8-9 PM (2)
May 31, 9-10 PM (7)
May 31, 10-11 PM (25)
May 31, 11-12 AM (11)
Jun 01, 12-1 AM (14)
Jun 01, 1-2 AM (7)
Jun 01, 2-3 AM (3)
Jun 01, 3-4 AM (10)
Jun 01, 4-5 AM (13)
Jun 01, 5-6 AM (16)
Jun 01, 6-7 AM (10)
Jun 01, 7-8 AM (14)
Jun 01, 8-9 AM (46)
Jun 01, 9-10 AM (50)
Jun 01, 10-11 AM (19)
Jun 01, 11-12 PM (27)
Jun 01, 12-1 PM (49)
Jun 01, 1-2 PM (40)
Jun 01, 2-3 PM (44)
Jun 01, 3-4 PM (34)
Jun 01, 4-5 PM (54)
Jun 01, 5-6 PM (5)
Jun 01, 6-7 PM (32)
Jun 01, 7-8 PM (37)
Jun 01, 8-9 PM (9)
Jun 01, 9-10 PM (12)
Jun 01, 10-11 PM (30)
Jun 01, 11-12 AM (22)
Jun 02, 12-1 AM (13)
Jun 02, 1-2 AM (8)
Jun 02, 2-3 AM (5)
Jun 02, 3-4 AM (14)
Jun 02, 4-5 AM (10)
Jun 02, 5-6 AM (43)
Jun 02, 6-7 AM (32)
Jun 02, 7-8 AM (58)
Jun 02, 8-9 AM (65)
Jun 02, 9-10 AM (28)
Jun 02, 10-11 AM (19)
Jun 02, 11-12 PM (15)
Jun 02, 12-1 PM (47)
Jun 02, 1-2 PM (66)
Jun 02, 2-3 PM (97)
Jun 02, 3-4 PM (23)
Jun 02, 4-5 PM (15)
Jun 02, 5-6 PM (27)
Jun 02, 6-7 PM (29)
Jun 02, 7-8 PM (18)
Jun 02, 8-9 PM (9)
Jun 02, 9-10 PM (19)
Jun 02, 10-11 PM (33)
Jun 02, 11-12 AM (22)
Jun 03, 12-1 AM (13)
Jun 03, 1-2 AM (31)
Jun 03, 2-3 AM (16)
Jun 03, 3-4 AM (0)
Jun 03, 4-5 AM (7)
Jun 03, 5-6 AM (12)
Jun 03, 6-7 AM (80)
Jun 03, 7-8 AM (16)
Jun 03, 8-9 AM (24)
Jun 03, 9-10 AM (22)
Jun 03, 10-11 AM (39)
Jun 03, 11-12 PM (76)
Jun 03, 12-1 PM (93)
Jun 03, 1-2 PM (28)
Jun 03, 2-3 PM (62)
Jun 03, 3-4 PM (26)
Jun 03, 4-5 PM (24)
Jun 03, 5-6 PM (23)
Jun 03, 6-7 PM (15)
Jun 03, 7-8 PM (17)
Jun 03, 8-9 PM (16)
Jun 03, 9-10 PM (8)
Jun 03, 10-11 PM (31)
Jun 03, 11-12 AM (14)
Jun 04, 12-1 AM (12)
Jun 04, 1-2 AM (4)
Jun 04, 2-3 AM (1)
Jun 04, 3-4 AM (3)
Jun 04, 4-5 AM (1)
Jun 04, 5-6 AM (0)
Jun 04, 6-7 AM (14)
Jun 04, 7-8 AM (10)
Jun 04, 8-9 AM (11)
Jun 04, 9-10 AM (19)
Jun 04, 10-11 AM (11)
Jun 04, 11-12 PM (13)
Jun 04, 12-1 PM (53)
Jun 04, 1-2 PM (38)
Jun 04, 2-3 PM (60)
Jun 04, 3-4 PM (12)
Jun 04, 4-5 PM (4)
Jun 04, 5-6 PM (7)
Jun 04, 6-7 PM (40)
Jun 04, 7-8 PM (3)
3,421 commits this week May 28, 2026 - Jun 04, 2026
chore(deps): bump github.com/blinklabs-io/plutigo from 0.1.13 to 0.1.14 (#737)
Bumps [github.com/blinklabs-io/plutigo](https://github.com/blinklabs-io/plutigo) from 0.1.13 to 0.1.14.
- [Release notes](https://github.com/blinklabs-io/plutigo/releases)
- [Changelog](https://github.com/blinklabs-io/plutigo/blob/main/RELEASE_NOTES.md)
- [Commits](https://github.com/blinklabs-io/plutigo/compare/v0.1.13...v0.1.14)

---
updated-dependencies:
- dependency-name: github.com/blinklabs-io/plutigo
  dependency-version: 0.1.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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]>
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]>
Restore numberOfFanoutOutputs to Fanout redeemer
  The accumulator-based headIsFinalizedWith used txInfoOutputs directly,
  which includes the wallet's change output appended by coverFee. Since
  that output is not in the accumulator, the KZG membership proof failed
  for every real on-chain fanout.

  Restore numberOfFanoutOutputs :: Integer to the Fanout redeemer (as in
  the original hash-based validator) so the validator slices txInfoOutputs
  to only the distributed UTxOs before running the pairing check and value
  conservation. Also update the FanOut.hs head output to carry the UTxO
  value so mustConserveValue passes, and add a unit test that appends a
  trailing wallet change output and asserts the transaction still
  evaluates — this would have caught the regression at the contract test
  level rather than requiring a devnet E2E run.

Signed-off-by: Sasha Bogicevic <[email protected]>
Replace UTxO hash verification with BLS accumulator commitment
  Remove utxoHash/alphaUTxOHash/omegaUTxOHash from ClosedDatum, all
  Close/Contest redeemers, and the snapshot signing tuple. The BLS
  accumulator already commits to the full UTxO set (utxo ∪ alpha ∪
  omega), making the three separate SHA256 hashes redundant.

  Full fanout now verifies outputs via a KZG membership proof (same as
  partial fanout) rather than hash comparison. The Fanout redeemer gains
  proof and crsRef fields; the three output-count fields are dropped.

  Snapshot signing shrinks from a 7-tuple to a 4-tuple
  (headId, version, snapshotNumber, accumulatorHash).

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]>
Fix fanout for pre-settled UTxOs: remove isG1Generator, add headAdaOverhead
  When a UTxO is settled on-chain via Increment or Decrement before Close,
  its scalar remains in the snapshot's KZG accumulator but its value has
  already left (or entered) the head. Fanning out only the remaining UTxOs
  produces a subset membership proof, but the old on-chain check required
  isG1Generator(proof) — only true when the subset equals the full
  accumulator. This caused H39 FanoutUTxOHashMismatch for any CloseUsed
  scenario.

  Fix (two inseparable halves):

  1. Remove `&& isG1Generator proof` from headIsFinalizedWith and
     checkFinalPartialFanout. Fanout and final-partial-fanout now accept
     any valid subset membership proof.

  2. Replace the `geq` conservation check with strict equality using a new
     `headAdaOverhead :: Integer` field in ClosedDatum and
     FanoutProgressDatum. This field holds the fixed lovelace overhead in
     the head UTxO not attributable to any L2 UTxO (the min-UTxO cost set
     at CollectCom). It closes the security hole that geq would leave: a
     valid subset proof + >= would let an attacker leave value unaccounted.

  Off-chain: fanoutTx receives a separate utxoForProof argument (the full
  snapshot UTxO set including pre-settled ones) to rebuild the accumulator
  matching the ClosedDatum commitment, while the fanned-out outputs cover
  only the UTxOs whose values are still in the head. closeTx computes
  headAdaOverhead as lovelace(headInputValue) - lovelace(UTxOs currently
  in head), with case analysis on incrementalAction × (version ==
  openVersion). contestTx propagates it unchanged; partialFanoutTx threads
  it through FanoutProgressDatum.

  checkContest preserves headAdaOverhead so it cannot be altered after the
  initial Close. checkPartialFanout also preserves it across intermediate
  steps.

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]>
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]>
Use dynamic binary search to find largest fitting fanout chunk
  Replace the hardcoded numToDistribute = totalUTxO - 1 in the partial
  fanout benchmarks with a binary search that finds the largest chunk
  actually fitting within both the tx size and execution budget, mirroring
  findFittingFanoutTx in the real node. The Remaining column in the output
  table now carries meaningful information instead of always showing 1.

  Also add ContestationDeadlineOutsideTimeHorizon to the PostTxError
  oneOf in api.yaml, which was defined in Chain.hs but missing from the
  schema, causing ServerOutputSpec to fail.

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