-
Notifications
You must be signed in to change notification settings - Fork 11
feat(sysio.system): derive producer rank from a score, and demote on missed rounds (WIRE-367) #599
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
947440c
31e666a
bcd82cd
23b52fc
bd35a87
07f743c
f88cb2b
05a25ef
4cd31dd
3b01987
75e815c
ce09933
f6b6950
b569e00
4fce5f4
991f955
007b6bb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -118,6 +118,11 @@ struct [[sysio::table("emitcfg"), sysio::contract("sysio.system")]] emission_con | |
|
|
||
| // Producer config | ||
| uint32_t standby_end_rank; // last standby rank (default 28) | ||
| // Share of the producer pool reserved for the standby retainer (basis points, <= 10000). The | ||
| // rest funds the per-block rate active producers are paid at. Each standby POSITION | ||
| // (22..standby_end_rank) holds a fixed, linearly decaying share of this slice; a vacant | ||
| // position's share stays in the treasury rather than flowing to the standbys present. | ||
| uint16_t standby_bps; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] Migrate
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nothing deployed, we do not need an upgrade path.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same ruling as the sibling thread — @heifner: nothing deployed, so there is no existing |
||
|
|
||
| // Audit-log retention. Caps the unbounded `epochlog` table at this many | ||
| // rows; payepoch prunes head-first after each insert. There is one row per | ||
|
|
@@ -142,7 +147,7 @@ struct [[sysio::table("emitcfg"), sysio::contract("sysio.system")]] emission_con | |
| (annual_initial_emission)(annual_max_emission)(annual_min_emission) | ||
| (compute_bps)(capex_bps)(governance_bps) | ||
| (producer_bps)(batch_op_bps) | ||
| (standby_end_rank)(epoch_log_retention_count) | ||
| (standby_end_rank)(standby_bps)(epoch_log_retention_count) | ||
| (pay_cadence_epochs)) | ||
| }; | ||
|
|
||
|
|
@@ -330,11 +335,27 @@ struct [[sysio::table("t5state"), sysio::contract("sysio.system")]] t5_state { | |
| // visible without breaking the OPP-handler never-throw contract. | ||
| int64_t capital_shortfall_total = 0; | ||
|
|
||
| /// Block slots the open pay period is entitled to, accumulated as each epoch accrues. | ||
| /// | ||
| /// The DIVISOR has to be built the same way the POOL is. `pending_emission_amount` above adds | ||
| /// each epoch's share at the moment that epoch accrues; computing the slot count at payout | ||
| /// instead -- current duration times the epoch count -- applies today's duration to epochs that | ||
| /// ran under a different one. A period spanning a duration change then mis-sizes the divisor: a | ||
| /// 60s epoch (120 slots) followed by a 120s epoch (240 slots) is 360 slots, but is computed as | ||
| /// 480, paying 75% of the active pool under full production. | ||
| /// | ||
| /// Reset with `pending_emission_amount` at each payout. | ||
| /// | ||
| /// DECLARED LAST, matching the tail of SYSLIB_SERIALIZE below. The ABI is generated from the | ||
| /// declarations while the wasm serializes in macro order, so a field inserted anywhere but the | ||
| /// end makes the two disagree silently. | ||
| uint64_t pending_nominal_slots = 0; | ||
|
|
||
| SYSLIB_SERIALIZE(t5_state, | ||
| (start_time)(epoch_count)(last_epoch_index) | ||
| (last_epoch_time)(last_epoch_emission)(total_distributed) | ||
| (pending_emission_amount)(period_start_epoch)(batch_group_epochs) | ||
| (capital_shortfall_total)) | ||
| (capital_shortfall_total)(pending_nominal_slots)) | ||
| }; | ||
|
|
||
| using t5state_t = sysio::kv::global<"t5state"_n, t5_state>; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P1] Reevaluate existing operators before activating new collateral minima
setconfig writes the new requirement vectors and only asks sysio.system to rescore. It never reevaluates existing operator statuses. Raising a minimum therefore leaves a now-undercollateralized producer ACTIVE; the score sweep lowers its composite but compute and is_schedulable still trust that stale status, so it can remain healthy, scheduled, and payable indefinitely. Lowering a minimum likewise leaves newly qualified UNKNOWN rows excluded. Please stage the config behind a bounded eligibility sweep or make authoritative consumers check the live minima.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P1] Residual on b569e00: the live producer ratio fixes only part of this\n\nA raised producer minimum is now caught by producer_score, but compute checks the stored ACTIVE status before evaluating that ratio. Lowering the minimum therefore cannot promote existing UNKNOWN producers. Batch operators and underwriters have no equivalent live-minimum check, so raising their minima leaves underbonded rows ACTIVE and still eligible for epoch selection or underwriter authorization until some balance mutation happens. The config change still needs a bounded status reconciliation or authoritative live checks for every role.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Confirmed on all three counts. The decision for this PR is to accept and document rather than reconcile, so flagging that explicitly rather than marking it fixed.
One correction to the finding, though: the underwriter half is narrower than it looks.
sysio.uwrit::try_select_winnerrefuses any candidate whose LIVEavailable_via_mirrorsdoes not coversrc + dst, so every underwritten leg stays collateral-backed at its real value — the minimum is an eligibility floor, not a capacity control. The exposure that remains is the batch-operator one: an underbonded op keeps serving in groups with a smaller slash-at-risk than governance just set.Accepted because that exposure is one role in one direction, self-corrects on any balance movement (
reevaluate_eligibilityfires on deposit, withdraw, withdraw-flush, slash and terminate), and is auditable off-chain by comparingbalancesagainstop_config. The operator guide now states that a minimum change binds new registrations immediately and existing operators on their next balance movement, and that a lowered minimum needs a balance touch to take effect.A bounded reconciliation sweep in opreg following the
flushwthdwshape is the right permanent fix for all three roles and both directions — but it is opreg work rather than producer ranking, and deciding what happens to an operator whose status flips mid-epoch deserves its own review.