Don't observe unrelated Head deposits - #2743
Conversation
Transaction cost differencesNo cost or size differences found |
End-to-end benchmark differencesComparing this PR ( Baseline Scenario
Three local nodes
|
ffdd2fc to
912aac9
Compare
Transaction costsSizes and execution budgets for Hydra protocol transactions. Note that unlisted parameters are currently using
Script summary
|
| 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 |
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>
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>
9cd6cd8 to
d7a3099
Compare
fix #2606
This branch scopes deposit chain observations to the current head — Open/Closed states only record
OnDepositTxevents matching their own headId, andIdlesilently drops incoming deposits.For recovery, the guard is intentionally different:
OnRecoverTxis processed for any deposit tracked inpendingDepositsregardless of which head is currently active, since deposits from a previous head survive fanout and must remain recoverable.DepositRecoveredis 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.