fix(sysio): preserve epoch batch reward rosters (WIRE-343) - #576
fix(sysio): preserve epoch batch reward rosters (WIRE-343)#576huangminghuang wants to merge 5 commits into
Conversation
Change-Id: Iaa60f6d7cfad4dc0662b5b81b6e8781acf120741
heifner
left a comment
There was a problem hiding this comment.
Design looks sound — the roster recorded by rcrdbatch is read after the window slide so it matches the epoch/group accrueepoch credits, the KV key is big-endian so batchepochs iterates in ascending epoch order (contiguity walk is valid), the erase(it) loop is safe, and ABI table_id 22503 has no collisions.
5 findings inline: 2 chain-halt risks on the mandatory advance inline path, 1 missing build artifact, 2 minor.
Change-Id: I0cb745aebe72c6b63c0bc7bbe0af94254b27dd52
heifner
left a comment
There was a problem hiding this comment.
Re-reviewed at 1ab98aff5 (after the artifact refresh). Status of the 2026-08-20 findings, plus one new one.
Resolved
sysio.system.abi:2124— artifact mismatch.1ab98aff5adds bothsysio.system.wasmandsysio.epoch.wasm, so the source-tree pair is consistent again. One nit for the squash: that commit touches only the two.wasmfiles with no accompanying source change, which is the shapecommit-wasm-only-with-source-change.mdbans per-commit. The PR as a whole pairs source with artifacts, so this is history hygiene rather than a provenance gap.
Still open, unchanged at head
emissions.cpp:684—rcrdbatch's cap throw on the mandatory inlineadvancepath.emissions.cpp:823— incomplete-history hard abort. One more concrete trigger added in that thread.emissions.cpp:1043— the clear loop is still outside theaccrued_epochs > 0guard: the guard opens at L1035 and closes at L1039, andbatch_history.eraseruns unconditionally at L1043.sysio.epoch.cpp:889—payepoch's now-unused second parameter is still serialized on every pay epoch.
New
emissions.cpp:678— an O(cadence × roster) deserialize on everyadvance, used only to produce a count. Inline below.
Change-Id: Ic6236e59b9d59eb8727d55228ceaea2d9b8aee86
heifner
left a comment
There was a problem hiding this comment.
Re-reviewed at 619f5d0a5. All six findings from the previous rounds are fixed — confirmed against the source, so I'm resolving those threads:
| Finding | Fix |
|---|---|
emissions.cpp:684 cap throw → chain halt |
Counting loop deleted; O(1) contains/erase probe of epoch_index - MAX_PAY_CADENCE_EPOCHS, guarded against the uint32_t underflow |
emissions.cpp:823 incomplete history → chain halt |
check removed; non-halting retain path |
period_start_epoch == 0 ? 1 seed |
Seeds from the earliest recorded roster |
emissions.cpp:1043 erase outside the guard |
Now inside if (accrued_epochs > 0) |
sysio.epoch.cpp:889 unused payepoch arg |
Now sends an empty vector |
| ABI/wasm mismatch | Fixed in 1ab98aff5 |
The effective_pay_cadence_epochs clamp is a good addition I hadn't asked for. Payout math, roster/epoch attribution, the contiguity triple, big-endian KV ordering, and erase(const_iterator) safety in the clear loop all verified sound.
Four new findings inline. The first one is a refinement of my own earlier suggestion — I proposed "print and retain in treasury" to remove the halt cliff, and retained turns out to mean permanently absorbed, which isn't what I intended.
Change-Id: I53a5b7164995c9157a9dbe0a0a6e769a5d26cc0a
heifner
left a comment
There was a problem hiding this comment.
Re-reviewed at dab8091ed. Converging — the previous round's four findings are addressed and only two new ones surfaced, one of which is a residual of finding 1 rather than something new.
Verified this round: the period length is provably <= MAX_PAY_CADENCE_EPOCHS, so rcrdbatch's exact epoch_index - 10 prune holds the table at <= 10 rows; sum(active_epochs) == accrued_epochs by construction, so the two integer divisions partition the pools exactly with no div-by-zero; batch_payout_credits is provably <= 100 for any setter-accepted config; the runtime credit cap is fail-safe (it routes to retention rather than paying some operators and not others); and the reworked epochlog prune is exact.
Two items below, plus a question about the cadence ceiling that I think needs a decision before this merges.
Change-Id: Ifd82421f9204fe64c782a87f1daad096d17c03b5
Summary
batchepochs, so delayed batch payouts use the historical recipients that actually earned each epoch's credits.pay_cadence_epochsto 1-10 and each payout to at most 100 recipient credits. With the intended 21-operator roster, the supported maximum cadence is 4 epochs (about 24 minutes at six-minute epochs); raising this conservative execution bound requires separate CPU/KV evidence.sysiotreasury and leave the fee bucket insysio.reservfor the next complete payout period.epochlogrecords history completeness and the retained emission amount;batch_fee_retainedis reserved for fees that were swept during a complete-history payout but could not be distributed.epochloghistory by completed payment rows, and expose the completeness/retention audit fields through the generated ABI.sysio.systemcadence whensysio.epoch::setconfigchangesoperators_per_epoch, including a diagnostic that identifies the stored system setting.Validation
4e36183652ceb1c8963f90ad8c8366f61494bd7c.flow-emissions-soakpassed: 81 phases, 311 steps, including the 1,800-second stability loop and final claim (2,165.8 seconds total, zero fatal heartbeat events).flow-emissions-soak: E2E Flow Tests passed in 2,158 seconds, withBRANCH_WIRE_SYSIO=4e36183652ceb1c8963f90ad8c8366f61494bd7candFLOW_INCLUDE=flow-emissions-soak; the 1,800-second stability loop and final claim checks passed.Reviewer notes
epochlogrows is intentionally out of scope.sysio.systemandsysio.epochcontract updates together during a quiesced window before resuming epoch processing.operators_per_epoch * pay_cadence_epochs <= 100; update epoch capacity before system cadence when increasing the supported product.