batch_operator: crank chkdispute so a resolved dispute can unpause the epoch - #582
Conversation
…e epoch `sysio.chalg::opendispute` sends `sysio.epoch::pause`, and `chkdispute` is the only action that tallies the Tier-1 votes, dispatches the winning envelope and lifts that pause. Nothing drove it: `chkdispute` appears in no plugin in the tree, and `batch_operator_plugin` knows exactly two action names, `deliver` and `chkcons`. The sibling case shows why that is not survivable here. `chkuwchal` needs no cadence because `sysio.uwrit::chklocks` pokes it from every `sysio.epoch::advance` — which works precisely because an underwriter challenge does NOT pause the chain. An envelope dispute halts `advance` itself, so no inline poke can reach `chkdispute`, and it has no deadline-lapse path either. The result: Tier-1 reaches quorum, the dispute stays OPEN, `open_disputes` never decrements, and epoch advancement is paused until someone hand-cranks it. Crank it from the epoch tick that already runs (`--batch-epoch-poll-ms`, 15s), scanning `sysio.chalg::disputes` for OPEN rows. Gated on `is_active` rather than `is_elected`: the elected operator may be the one offline or the one that delivered the non-canonical envelope — often the reason the dispute exists. Disputes are rare, and `chkdispute` asserts the dispute is still OPEN, so a redundant push from a second operator is a cheap no-op. Also corrects the `chkdispute` comment, which asserted this cadence already existed. Comment-only: no contract artifact changes, and no .wasm/.abi is in this diff. Change-Id: I2495d8d4dadae912336e06d1698168ab34f20366
e2e gate: GREEN — and it proves the crank exists in productionRun 32518249867 · Release · 19:23:48 → 20:13:11 UTC (49 min) ·
Why this is the proof, not just a green tick
It also settles the open assumption in #81: the bootstrapped batch operators do keep their daemons and stay opreg-ACTIVE across the paused epoch, and their tick reaches the dispute even though Durations line up with the known-good baselineAgainst run Also verified
Merge order#582 first, then Wire-Network/wire-tools-ts#81. Reversed, the flow has no crank and times out. |
The bug
sysio.chalg::opendisputesendssysio.epoch::pause.chkdisputeis the ONLY action that tallies the Tier-1 votes, dispatches the winning envelope viaresolvedisp, and lifts that pause. Nothing drove it.No plugin in the tree references
chalgat all, andbatch_operator_pluginknows exactly two action names:The comment at
sysio.chalg.cpp:346— "batch operators call this on their ~15s cadence" — asserted a cadence that did not exist.Why the sibling survives and this one cannot
chkuwchal's header states the contract outright:An envelope dispute is the inverse: it pauses
advance, so the advance-driven poke is structurally unavailable.chkuwchalalso has a LAPSED deadline path;chkdisputehas none —if (!resolved) return;, "No plurality / tie-break — an unresolved tally just keeps waiting for more votes."So the sibling has two independent liveness mechanisms and
chkdisputehad zero. Tier-1 reaches quorum → dispute staysOPEN→open_disputesnever decrements → epoch paused until someone hand-cranks it.The change
Crank it from the epoch tick that already runs (
--batch-epoch-poll-ms, 15s default), scanningsysio.chalg::disputesfor OPEN rows.Gated on
is_active, notis_elected— deliberate, and the one design call worth reviewing.chkconsis elected-only to avoid burning trx slots every tick from every operator. Here the elected operator may be exactly the one offline or the one that delivered the non-canonical envelope — often why the dispute exists. Disputes are rare, andchkdisputeasserts the dispute is still OPEN, so a redundant push from a second operator is a cheap no-op logged atdlog.Full-table scan rather than the
byepochindex: the table retains RESOLVED rows as the audit trail, but disputes are rare andpoll_own_statusalready scans a comparably-sized table each tick. The comment names the index as the escape hatch if that ever changes.Accepted design decision
Pausing until Tier-1 votes is correct, and there is deliberately no timeout escape. This PR moves the liveness dependency from "operators must deliver" to "a majority of Tier-1 must vote", and that is the intended trade — the alternatives (plurality, or abandon-and-resume) either pick a winner on weak evidence or hand back the original deadlock. Recorded here so it is an explicit decision rather than an implicit one.
Contract file
contracts/sysio.chalg/src/sysio.chalg.cppis comment-only — it corrects the false cadence claim. No.wasm/.abiappears in this diff, and a comment cannot change the compiled artifact, socontracts_unit_testwas not re-run for it. Flag it if you want that gate run anyway.Verification