Skip to content

Don't observe unrelated Head deposits - #2743

Merged
vrom911 merged 12 commits into
masterfrom
unrelated-deposits
Jul 3, 2026
Merged

Don't observe unrelated Head deposits#2743
vrom911 merged 12 commits into
masterfrom
unrelated-deposits

Conversation

@v0d1ch

@v0d1ch v0d1ch commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

fix #2606

This branch scopes deposit chain observations to the current head — Open/Closed states only record OnDepositTx events matching their own headId, and Idle silently drops incoming deposits.

For recovery, the guard is intentionally different: OnRecoverTx is processed for any deposit tracked in pendingDeposits regardless of which head is currently active, since deposits from a previous head survive fanout and must remain recoverable. DepositRecovered is also exempted from aggregateNodeState's headId filter so the deposit is always cleaned up. The branch adds tests and documentation verifying that recovery works in all states — Open, Closed, Idle post-fanout, and while a new head is running.


  • CHANGELOG updated or not needed
  • Documentation updated or not needed
  • Haddocks updated or not needed
  • No new TODOs introduced or explained herafter

@v0d1ch v0d1ch self-assigned this Jun 24, 2026
@github-actions

Copy link
Copy Markdown

Transaction cost differences

No cost or size differences found

@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown

End-to-end benchmark differences

Comparing this PR (new) against master (old). Numbers come from cloud VMs, so changes under 5% are shown as and are likely run-to-run noise rather than a real regression or improvement. 🟢 = improvement, 🔴 = regression.

Baseline Scenario

Metric master PR Δ
End-to-end TPS (tx/s) 553.63 532.53 ≈ -21.10 (-3.8%)
Per-snapshot TPS P50 (tx/s) 4258.13 3992.06 🔴 -266.07 (-6.2%)
Per-snapshot TPS P95 (tx/s) 7640.99 7240.65 🔴 -400.34 (-5.2%)
Per-snapshot TPS max (tx/s) 7997.79 7600.35 ≈ -397.44 (-5.0%)
Avg. Confirmation Time (ms) 522.20 541.70 ≈ +19.50 (+3.7%)
P50 confirmation (ms) 524.80 544.80 ≈ +20.00 (+3.8%)
P95 confirmation (ms) 535.20 555.00 ≈ +19.80 (+3.7%)
P99 confirmation (ms) 535.40 555.50 ≈ +20.10 (+3.8%)
Invalid txs 0.00 0.00 ≈ +0.00 (n/a%)

Three local nodes

Metric master PR Δ
End-to-end TPS (tx/s) 317.38 307.88 ≈ -9.50 (-3.0%)
Per-snapshot TPS P50 (tx/s) 1074.07 1005.65 🔴 -68.42 (-6.4%)
Per-snapshot TPS P95 (tx/s) 3142.79 2358.85 🔴 -783.94 (-24.9%)
Per-snapshot TPS max (tx/s) 3833.78 2454.01 🔴 -1379.77 (-36.0%)
Avg. Confirmation Time (ms) 2556.30 2623.50 ≈ +67.20 (+2.6%)
P50 confirmation (ms) 2602.00 2676.60 ≈ +74.60 (+2.9%)
P95 confirmation (ms) 2811.20 2901.50 ≈ +90.30 (+3.2%)
P99 confirmation (ms) 2825.30 2910.30 ≈ +85.00 (+3.0%)
Invalid txs 0.00 0.00 ≈ +0.00 (n/a%)

@v0d1ch
v0d1ch force-pushed the unrelated-deposits branch 2 times, most recently from ffdd2fc to 912aac9 Compare June 24, 2026 09:25
@v0d1ch
v0d1ch requested a review from a team June 24, 2026 09:25
@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown

Transaction costs

Sizes and execution budgets for Hydra protocol transactions. Note that unlisted parameters are currently using arbitrary values and results are not fully deterministic and comparable to previous runs.

Metadata
Generated at 2026-07-03 09:25:54.255924891 UTC
Max. memory units 14000000
Max. CPU units 10000000000
Max. tx size (kB) 16384

Script summary

Name Hash Size (Bytes)
νHead fd75e24c9ea915ce8e48d3ff1d0c54ad09cc01191c24416ad7dba4a3 11621
μHead 83a964e973c065bbe70588f5e089817f92182ae81743e7a54cf3e29e* 4856
νDeposit c78e8c9205721eb3ef4410f3db9c6169fa6db497c24641d29c20529c 1615
νCRS 09db7ee6cf7a4b358dd5c8a2f19d2c048336ffc5a01ef35a47ca7072 2736
  • The minting policy hash is only usable for comparison. As the script is parameterized, the actual script is unique per head.

Init transaction costs

Parties Tx size % max Mem % max CPU Min fee ₳
1 5349 8.83 2.90 0.48
2 5445 9.50 3.13 0.49
3 5543 9.72 3.18 0.50
5 5736 11.07 3.62 0.52
10 6221 13.54 4.41 0.57
50 10056 34.59 11.00 0.95
100 14863 62.21 19.66 1.45
115 16300 69.50 21.89 1.59

Cost of Increment Transaction

Parties Tx size % max Mem % max CPU Min fee ₳
1 2282 17.76 6.40 0.44
2 2409 19.23 7.50 0.47
3 2541 19.67 8.24 0.48
5 2802 22.23 10.29 0.53
10 3462 26.57 14.74 0.62
50 8699 65.87 51.59 1.44
75 11978 89.96 74.51 1.94

Cost of Decrement Transaction

Parties Tx size % max Mem % max CPU Min fee ₳
1 598 16.03 5.75 0.35
2 729 16.94 6.65 0.37
3 862 17.79 7.54 0.39
5 1125 19.60 9.34 0.43
10 1779 24.10 13.84 0.52
50 7020 61.84 50.22 1.32
75 10294 85.74 73.04 1.82

Close transaction costs

Parties Tx size % max Mem % max CPU Min fee ₳
1 593 15.49 10.37 0.38
2 724 16.37 11.27 0.40
3 854 17.32 12.19 0.42
10 1776 23.70 18.52 0.55
50 7013 62.03 55.16 1.36
74 10157 85.37 77.23 1.84

Contest transaction costs

Parties Tx size % max Mem % max CPU Min fee ₳
1 620 18.79 13.48 0.43
2 755 19.85 14.43 0.45
3 885 20.94 15.39 0.47
5 1145 23.07 17.29 0.51
10 1807 28.50 22.08 0.62
50 7045 73.96 60.82 1.50
73 10057 99.95 83.06 2.00

FanOut transaction costs

Involves spending head output and burning head tokens. Uses ada-only UTXO for better comparability.

Parties UTxO UTxO (bytes) Tx size % max Mem % max CPU Min fee ₳
10 0 0 5529 22.68 41.73 0.88
10 1 57 5564 25.03 44.25 0.92
10 5 285 5699 35.28 54.57 1.08
10 10 570 5869 49.29 67.85 1.30
10 20 1139 6208 81.82 95.83 1.78
10 20 1137 6207 81.82 95.83 1.78

PartialFanOut transaction costs

Largest chunk of ada-only outputs that can be distributed in one partial fanout step, computed dynamically. The last row is the maximum total UTxO count where at least one output can still be distributed.

Distributed UTxO (bytes) Tx size % max Mem % max CPU Min fee ₳
11 570 987 34.31 65.19 0.94
25 1310 1429 67.64 98.26 1.47
30 1311 1430 67.64 98.26 1.47
40 1309 1428 67.64 98.26 1.47
50 1306 1421 67.64 98.26 1.47
100 1307 1426 67.64 98.26 1.47
150 1309 1428 67.64 98.26 1.47
200 1308 1427 67.64 98.26 1.47
200 1309 1428 67.64 98.26 1.47

PartialFanOut transaction costs (with native tokens)

Largest chunk of native-token outputs that can be distributed in one partial fanout step, computed dynamically. The last row is the maximum total UTxO count where at least one output can still be distributed.

Distributed UTxO (bytes) Tx size % max Mem % max CPU Min fee ₳
11 1140 1631 41.40 67.74 1.04
25 2310 2566 75.99 98.08 1.58
30 2352 2610 75.99 98.08 1.59
40 2499 2764 75.99 98.13 1.59
50 2562 2827 75.99 98.13 1.60
100 2121 2369 75.97 97.98 1.57
150 1953 2193 75.97 97.97 1.57
200 2121 2369 75.99 97.98 1.57
200 2310 2567 75.97 98.07 1.58

FinalPartialFanOut transaction costs (with native tokens)

Terminal partial fanout step (FanoutProgress → Final) with outputs carrying a native token. Burns all head tokens and proves accumulator exhaustion via BLS proof.

Distributed UTxO (bytes) Tx size % max Mem % max CPU Min fee ₳
1 114 5419 21.54 43.19 0.87
5 550 5770 35.15 54.72 1.08
10 1120 6235 53.35 69.52 1.36
10 1060 6176 53.23 69.47 1.35

End-to-end benchmark results

This page is intended to collect the latest end-to-end benchmark results produced by Hydra's continuous integration (CI) system from the latest master code.

Please note that these results are approximate as they are currently produced from limited cloud VMs and not controlled hardware. Rather than focusing on the absolute results, the emphasis should be on relative results, such as how the timings for a scenario evolve as the code changes.

Generated at 2026-07-03 09:28:48.966019494 UTC

Baseline Scenario

Number of nodes 1
Number of txs 300
Avg. Confirmation Time (ms) 1346.5
P99 1380.2ms
P95 1380.1ms
P50 1343.8ms
End-to-end TPS 217.03 tx/s
Snapshots observed 4
Per-snapshot TPS P50 3138.71 tx/s
Per-snapshot TPS P95 4872.08 tx/s
Per-snapshot TPS max 5051.96 tx/s
Number of Invalid txs 0
Fanout outputs 0

Three local nodes

Number of nodes 3
Number of txs 900
Avg. Confirmation Time (ms) 6036.1
P99 6489.5ms
P95 6488.8ms
P50 6245.7ms
End-to-end TPS 138.55 tx/s
Snapshots observed 10
Per-snapshot TPS P50 767.77 tx/s
Per-snapshot TPS P95 2589.11 tx/s
Per-snapshot TPS max 3026.07 tx/s
Number of Invalid txs 0
Fanout outputs 0

Scenario benchmark results

This page collects results from the scenario matrix: every combination of cluster size, UTxO shape, and incremental-ops mode is exercised by CI from the latest master code and reported below.

Numbers are approximate. They come from cloud VMs rather than controlled hardware, so the useful signal is the relative change between cells and between commits, not the absolute throughput.

Generated at 2026-07-03 09:37:09.757707933 UTC

Summary across cells

TPS columns are rates (transactions per second); Wall clock (s) is the measured elapsed time from the first tx submission to the last confirmation. Times are rounded to one decimal.

Scenario Txs Wall clock (s) End-to-end TPS (tx/s) Per-snapshot p50 TPS (tx/s) Avg conf (ms) P95 conf (ms)
Nodes=1, Constant, incremental ops off, fire and forget 30 0.1 531.68 2532.98 55.5 56.2
Nodes=1, Constant, incremental ops off, wait for tx valid 30 0.2 187.84 197.17 5.3 6.0
Nodes=1, Growing, incremental ops off, fire and forget 30 0.1 465.10 1099.43 63.4 64.3
Nodes=1, Growing, incremental ops off, wait for tx valid 30 0.3 115.95 122.03 8.5 13.8
Nodes=1, Mixed, incremental ops off, fire and forget 30 0.1 544.79 2287.45 54.3 54.8
Nodes=1, Mixed, incremental ops off, wait for tx valid 30 0.2 147.85 151.77 6.7 8.4
Nodes=2, Constant, incremental ops off, fire and forget 60 0.1 406.46 1403.84 145.6 146.7
Nodes=2, Constant, incremental ops off, wait for tx valid 60 0.5 117.11 126.94 16.9 22.3
Nodes=2, Growing, incremental ops off, fire and forget 60 0.2 336.34 853.80 176.2 177.1
Nodes=2, Growing, incremental ops off, wait for tx valid 60 0.9 65.05 65.74 30.2 45.7
Nodes=2, Mixed, incremental ops off, fire and forget 60 0.2 371.32 1746.45 160.1 161.4
Nodes=2, Mixed, incremental ops off, wait for tx valid 60 0.7 84.50 85.12 23.3 32.9
Nodes=3, Constant, incremental ops off, fire and forget 90 0.3 328.64 1497.17 269.9 273.1
Nodes=3, Constant, incremental ops off, wait for tx valid 90 1.0 93.93 79.56 31.2 40.9
Nodes=3, Growing, incremental ops off, fire and forget 90 0.3 268.18 481.21 330.1 334.2
Nodes=3, Growing, incremental ops off, wait for tx valid 90 1.9 47.40 46.00 61.1 94.7
Nodes=3, Mixed, incremental ops off, fire and forget 90 0.3 317.45 1054.03 279.5 282.6
Nodes=3, Mixed, incremental ops off, wait for tx valid 90 1.4 62.08 59.12 47.3 71.6

Nodes=1, Constant, incremental ops off, fire and forget

Number of nodes 1
Number of txs 30
Avg. Confirmation Time (ms) 55.5
P99 56.2ms
P95 56.2ms
P50 55.6ms
End-to-end TPS 531.68 tx/s
Snapshots observed 2
Per-snapshot TPS P50 2532.98 tx/s
Per-snapshot TPS P95 4794.91 tx/s
Per-snapshot TPS max 4995.97 tx/s
Number of Invalid txs 0
Fanout outputs 0

Nodes=1, Constant, incremental ops off, wait for tx valid

Number of nodes 1
Number of txs 30
Avg. Confirmation Time (ms) 5.3
P99 7.1ms
P95 6.0ms
P50 5.0ms
End-to-end TPS 187.84 tx/s
Snapshots observed 30
Per-snapshot TPS P50 197.17 tx/s
Per-snapshot TPS P95 207.49 tx/s
Per-snapshot TPS max 209.46 tx/s
Number of Invalid txs 0
Fanout outputs 0

Nodes=1, Growing, incremental ops off, fire and forget

Number of nodes 1
Number of txs 30
Avg. Confirmation Time (ms) 63.4
P99 64.3ms
P95 64.3ms
P50 63.7ms
End-to-end TPS 465.10 tx/s
Snapshots observed 2
Per-snapshot TPS P50 1099.43 tx/s
Per-snapshot TPS P95 2071.33 tx/s
Per-snapshot TPS max 2157.72 tx/s
Number of Invalid txs 0
Fanout outputs 0

Nodes=1, Growing, incremental ops off, wait for tx valid

Number of nodes 1
Number of txs 30
Avg. Confirmation Time (ms) 8.5
P99 17.9ms
P95 13.8ms
P50 8.1ms
End-to-end TPS 115.95 tx/s
Snapshots observed 30
Per-snapshot TPS P50 122.03 tx/s
Per-snapshot TPS P95 164.87 tx/s
Per-snapshot TPS max 177.87 tx/s
Number of Invalid txs 0
Fanout outputs 0

Nodes=1, Mixed, incremental ops off, fire and forget

Each client first grows its UTxO set (1-in to 2-out) for half of its tx budget, then contracts it back (2-in to 1-out) for the remainder.

Number of nodes 1
Number of txs 30
Avg. Confirmation Time (ms) 54.3
P99 54.8ms
P95 54.8ms
P50 54.5ms
End-to-end TPS 544.79 tx/s
Snapshots observed 2
Per-snapshot TPS P50 2287.45 tx/s
Per-snapshot TPS P95 4327.67 tx/s
Per-snapshot TPS max 4509.02 tx/s
Number of Invalid txs 0
Fanout outputs 0

Nodes=1, Mixed, incremental ops off, wait for tx valid

Each client first grows its UTxO set (1-in to 2-out) for half of its tx budget, then contracts it back (2-in to 1-out) for the remainder.

Number of nodes 1
Number of txs 30
Avg. Confirmation Time (ms) 6.7
P99 10.1ms
P95 8.4ms
P50 6.5ms
End-to-end TPS 147.85 tx/s
Snapshots observed 30
Per-snapshot TPS P50 151.77 tx/s
Per-snapshot TPS P95 178.96 tx/s
Per-snapshot TPS max 198.60 tx/s
Number of Invalid txs 0
Fanout outputs 0

Nodes=2, Constant, incremental ops off, fire and forget

Number of nodes 2
Number of txs 60
Avg. Confirmation Time (ms) 145.6
P99 146.8ms
P95 146.7ms
P50 145.9ms
End-to-end TPS 406.46 tx/s
Snapshots observed 2
Per-snapshot TPS P50 1403.84 tx/s
Per-snapshot TPS P95 2660.14 tx/s
Per-snapshot TPS max 2771.81 tx/s
Number of Invalid txs 0
Fanout outputs 0

Nodes=2, Constant, incremental ops off, wait for tx valid

Number of nodes 2
Number of txs 60
Avg. Confirmation Time (ms) 16.9
P99 27.3ms
P95 22.3ms
P50 16.1ms
End-to-end TPS 117.11 tx/s
Snapshots observed 60
Per-snapshot TPS P50 126.94 tx/s
Per-snapshot TPS P95 160.35 tx/s
Per-snapshot TPS max 168.97 tx/s
Number of Invalid txs 0
Fanout outputs 0

Nodes=2, Growing, incremental ops off, fire and forget

Number of nodes 2
Number of txs 60
Avg. Confirmation Time (ms) 176.2
P99 177.2ms
P95 177.1ms
P50 176.7ms
End-to-end TPS 336.34 tx/s
Snapshots observed 2
Per-snapshot TPS P50 853.80 tx/s
Per-snapshot TPS P95 1615.92 tx/s
Per-snapshot TPS max 1683.66 tx/s
Number of Invalid txs 0
Fanout outputs 0

Nodes=2, Growing, incremental ops off, wait for tx valid

Number of nodes 2
Number of txs 60
Avg. Confirmation Time (ms) 30.2
P99 50.9ms
P95 45.7ms
P50 29.9ms
End-to-end TPS 65.05 tx/s
Snapshots observed 60
Per-snapshot TPS P50 65.74 tx/s
Per-snapshot TPS P95 115.20 tx/s
Per-snapshot TPS max 130.65 tx/s
Number of Invalid txs 0
Fanout outputs 0

Nodes=2, Mixed, incremental ops off, fire and forget

Each client first grows its UTxO set (1-in to 2-out) for half of its tx budget, then contracts it back (2-in to 1-out) for the remainder.

Number of nodes 2
Number of txs 60
Avg. Confirmation Time (ms) 160.1
P99 161.5ms
P95 161.4ms
P50 160.0ms
End-to-end TPS 371.32 tx/s
Snapshots observed 2
Per-snapshot TPS P50 1746.45 tx/s
Per-snapshot TPS P95 3312.01 tx/s
Per-snapshot TPS max 3451.17 tx/s
Number of Invalid txs 0
Fanout outputs 0

Nodes=2, Mixed, incremental ops off, wait for tx valid

Each client first grows its UTxO set (1-in to 2-out) for half of its tx budget, then contracts it back (2-in to 1-out) for the remainder.

Number of nodes 2
Number of txs 60
Avg. Confirmation Time (ms) 23.3
P99 34.6ms
P95 32.9ms
P50 23.1ms
End-to-end TPS 84.50 tx/s
Snapshots observed 60
Per-snapshot TPS P50 85.12 tx/s
Per-snapshot TPS P95 130.34 tx/s
Per-snapshot TPS max 150.79 tx/s
Number of Invalid txs 0
Fanout outputs 0

Nodes=3, Constant, incremental ops off, fire and forget

Number of nodes 3
Number of txs 90
Avg. Confirmation Time (ms) 269.9
P99 273.2ms
P95 273.1ms
P50 269.7ms
End-to-end TPS 328.64 tx/s
Snapshots observed 2
Per-snapshot TPS P50 1497.17 tx/s
Per-snapshot TPS P95 2840.88 tx/s
Per-snapshot TPS max 2960.32 tx/s
Number of Invalid txs 0
Fanout outputs 0

Nodes=3, Constant, incremental ops off, wait for tx valid

Number of nodes 3
Number of txs 90
Avg. Confirmation Time (ms) 31.2
P99 45.5ms
P95 40.9ms
P50 30.6ms
End-to-end TPS 93.93 tx/s
Snapshots observed 62
Per-snapshot TPS P50 79.56 tx/s
Per-snapshot TPS P95 183.44 tx/s
Per-snapshot TPS max 186.46 tx/s
Number of Invalid txs 0
Fanout outputs 0

Nodes=3, Growing, incremental ops off, fire and forget

Number of nodes 3
Number of txs 90
Avg. Confirmation Time (ms) 330.1
P99 334.3ms
P95 334.2ms
P50 332.2ms
End-to-end TPS 268.18 tx/s
Snapshots observed 2
Per-snapshot TPS P50 481.21 tx/s
Per-snapshot TPS P95 910.52 tx/s
Per-snapshot TPS max 948.68 tx/s
Number of Invalid txs 0
Fanout outputs 0

Nodes=3, Growing, incremental ops off, wait for tx valid

Number of nodes 3
Number of txs 90
Avg. Confirmation Time (ms) 61.1
P99 124.3ms
P95 94.7ms
P50 59.3ms
End-to-end TPS 47.40 tx/s
Snapshots observed 62
Per-snapshot TPS P50 46.00 tx/s
Per-snapshot TPS P95 123.31 tx/s
Per-snapshot TPS max 144.88 tx/s
Number of Invalid txs 0
Fanout outputs 0

Nodes=3, Mixed, incremental ops off, fire and forget

Each client first grows its UTxO set (1-in to 2-out) for half of its tx budget, then contracts it back (2-in to 1-out) for the remainder.

Number of nodes 3
Number of txs 90
Avg. Confirmation Time (ms) 279.5
P99 282.7ms
P95 282.6ms
P50 280.3ms
End-to-end TPS 317.45 tx/s
Snapshots observed 2
Per-snapshot TPS P50 1054.03 tx/s
Per-snapshot TPS P95 1998.86 tx/s
Per-snapshot TPS max 2082.85 tx/s
Number of Invalid txs 0
Fanout outputs 0

Nodes=3, Mixed, incremental ops off, wait for tx valid

Each client first grows its UTxO set (1-in to 2-out) for half of its tx budget, then contracts it back (2-in to 1-out) for the remainder.

Number of nodes 3
Number of txs 90
Avg. Confirmation Time (ms) 47.3
P99 81.9ms
P95 71.6ms
P50 47.1ms
End-to-end TPS 62.08 tx/s
Snapshots observed 63
Per-snapshot TPS P50 59.12 tx/s
Per-snapshot TPS P95 132.20 tx/s
Per-snapshot TPS max 147.10 tx/s
Number of Invalid txs 0
Fanout outputs 0

@vrom911 vrom911 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

Comment thread hydra-node/src/Hydra/HeadLogic.hs Outdated
v0d1ch and others added 12 commits July 3, 2026 10:17
  The deposit script address is global — every node sees every deposit on
  the network. The two chain input patterns for OnDepositTx and OnRecoverTx
  used a wildcard head state with no headId guard, causing nodes to track
  and persist deposits from unrelated heads, contaminating pendingDeposits
  and the chain state history.

  Fix the four deposit/recover patterns in handleChainInput to match on
  the current head state and compare headIds. Mismatches return Continue
  [] [] (silent no-op) rather than Error NotOurHead, since observing
  foreign deposits is expected and normal on a shared network. Add a
  defence-in-depth headId guard in aggregateNodeState so that old foreign
  DepositRecorded events replayed from pre-fix event logs are also ignored.

  Add tests covering: foreign deposits/recovers while Open are ignored,
  own-head deposit and recovery while Closed, post-fanout recovery while
  Idle, and that OnDepositTx while Idle is always a no-op.

Signed-off-by: Sasha Bogicevic <sasha.bogicevic@iohk.io>
  Deposits surviving from a previous head are never cleared from
  pendingDeposits on fanout, so recovery via ClientInput Recover
  works in Idle state without any logic changes. Adds a test that
  proves this, a comment on onClientRecover explaining the invariant,
  and expanded docs covering Open/Closed/Idle and new-head scenarios.

Signed-off-by: Sasha Bogicevic <sasha.bogicevic@iohk.io>
Signed-off-by: Sasha Bogicevic <sasha.bogicevic@iohk.io>
Signed-off-by: Sasha Bogicevic <sasha.bogicevic@iohk.io>
Signed-off-by: Sasha Bogicevic <sasha.bogicevic@iohk.io>
Signed-off-by: Sasha Bogicevic <sasha.bogicevic@iohk.io>
Signed-off-by: Sasha Bogicevic <sasha.bogicevic@iohk.io>
Signed-off-by: Sasha Bogicevic <sasha.bogicevic@iohk.io>
  When a deposit recovery TX is confirmed on-chain while a new head is
  open, the node must still acknowledge it regardless of which head is
  currently active. Previously, the Open/Closed handlers for OnRecoverTx
  guarded on `ourHeadId == headId`, silently discarding recoveries for
  old-head deposits — causing the HTTP DELETE /commits handler to never
  receive CommitRecovered and time out.

  Replace the three separate Open/Closed/Idle OnRecoverTx cases with a
  single wildcard that guards on `Map.member recoveredTxId
  pendingDeposits` instead. This recovers deposits from any tracked head
  (including previous heads whose deposits survive fanout) while silently
  ignoring truly unrelated deposits.

  Also fix `eventHeadId` to return `Nothing` for `DepositRecovered` so
  `aggregateNodeState` always removes the deposit from `pendingDeposits`
  regardless of current head context.

Signed-off-by: Sasha Bogicevic <sasha.bogicevic@iohk.io>
  Verify that applying a DepositRecovered state change via aggregateState
  removes the deposit from pendingDeposits in both Open (foreign head) and
  Idle (post-fanout) states, and does not affect an unrelated
  currentDepositTxId in the Open case.

Signed-off-by: Sasha Bogicevic <sasha.bogicevic@iohk.io>
  The Map.member guard requires the deposit to be present in
  pendingDeposits before emitting DepositRecovered. Seed pendingDeposits
  in the "emits DepositRecovered while Idle" test so the guard passes.

  Use inSync instead of inIdleState in the aggregateState test so the
  result matches NodeInSync rather than NodeCatchingUp.

Signed-off-by: Sasha Bogicevic <sasha.bogicevic@iohk.io>
@vrom911
vrom911 force-pushed the unrelated-deposits branch from 9cd6cd8 to d7a3099 Compare July 3, 2026 09:19
@vrom911
vrom911 enabled auto-merge July 3, 2026 09:20
@vrom911
vrom911 merged commit 32ba5d4 into master Jul 3, 2026
35 checks passed
@vrom911
vrom911 deleted the unrelated-deposits branch July 3, 2026 11:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Node observes and persists deposit events from unrelated Heads

2 participants