From c412b58e868571488dde90cde48cd54c54b0e868 Mon Sep 17 00:00:00 2001 From: Huang-Ming Huang Date: Mon, 7 Sep 2026 18:59:07 +0000 Subject: [PATCH] Fix WIRE-385 stale outbound operator rosters Change-Id: I2fe1bac5e738da09019a96deae976fe727638b4f --- .../include/sysio.epoch/sysio.epoch.hpp | 4 + contracts/sysio.epoch/src/sysio.epoch.cpp | 210 ++++++++++-------- contracts/sysio.epoch/sysio.epoch.abi | 19 ++ contracts/sysio.epoch/sysio.epoch.wasm | Bin 79792 -> 83254 bytes contracts/tests/sysio.msgch_chain_tests.cpp | 167 +++++++++++++- 5 files changed, 301 insertions(+), 99 deletions(-) diff --git a/contracts/sysio.epoch/include/sysio.epoch/sysio.epoch.hpp b/contracts/sysio.epoch/include/sysio.epoch/sysio.epoch.hpp index 5a8e6a5154..59a998090b 100644 --- a/contracts/sysio.epoch/include/sysio.epoch/sysio.epoch.hpp +++ b/contracts/sysio.epoch/include/sysio.epoch/sysio.epoch.hpp @@ -31,6 +31,10 @@ namespace sysio { [[sysio::action]] void advance(); + /// Internal continuation after epoch-close operator mutations execute. + [[sysio::action]] + void finishadv(uint32_t epoch_index, int64_t emission_amount); + /// Group assignment — reads AVAILABLE batch ops from sysio.opreg. [[sysio::action]] void schbatchgps(); diff --git a/contracts/sysio.epoch/src/sysio.epoch.cpp b/contracts/sysio.epoch/src/sysio.epoch.cpp index 3f4e9dd23b..7f4ec40d15 100644 --- a/contracts/sysio.epoch/src/sysio.epoch.cpp +++ b/contracts/sysio.epoch/src/sysio.epoch.cpp @@ -19,6 +19,7 @@ #include #include #include +#include namespace sysio { @@ -45,6 +46,7 @@ namespace { constexpr name SYSTEM_ACCOUNT = "sysio"_n; constexpr name TOKEN_ACCOUNT = "sysio.token"_n; +constexpr name FINISH_ADVANCE = "finishadv"_n; /// Action identifiers owned by sysio.chalg and invoked by epoch close. namespace chalg_actions { @@ -594,9 +596,9 @@ void epoch::advance() { // // Invariant — no cross-epoch double slash: opreg::slash THROWS on an already-SLASHED operator, // which would abort advance and stall OPP epoch advancement. These inline slashes execute only - // after advance returns, so the schedule slide below can temporarily place a just-slashed - // operator in its new tail while the operator still reads ACTIVE. That member cannot create a - // later non-canonical observation: sysio.msgch::deliver requires its current sysio.opreg status + // after advance returns. The finishadv continuation waits for those mutations before + // selecting the new tail. A removed member cannot create a later non-canonical + // observation: sysio.msgch::deliver requires its current sysio.opreg status // to be ACTIVE before accepting delivery. Once the slash has executed, the scheduled SLASHED // member cannot deliver or be queued for another non-canonical-delivery slash. The collection // above also deduplicates multiple non-canonical observations for one member in this advance. @@ -642,13 +644,84 @@ void epoch::advance() { // cron tick and trips kv-index-remove on already-evicted buckets. } - const bool had_expiring_group = state.current_epoch_index > 0; - state.current_epoch_index++; state.current_epoch_start = (state.next_epoch_start.sec_since_epoch() == 0) ? now : state.next_epoch_start; state.next_epoch_start = state.current_epoch_start + microseconds(static_cast(cfg.epoch_duration_sec) * 1'000'000); + state_tbl.set(state, ram_payer); + + // Withdrawal flushing can also change eligibility. Its nested callbacks + // must complete before schedule selection and roster serialization. + // Inline siblings execute their complete subtrees in order, atomically. + action( + permission_level{get_self(), "owner"_n}, + OPREG_ACCOUNT, + "flushwtdw"_n, + std::make_tuple(state.current_epoch_index) + ).send(); + + // Keep the refund subtree at its original depth; refundwire can itself + // transfer a fee or sweep expired claims. It needs the new epoch index, + // but not the new schedule, and finishes before roster publication. + // Drain the swap-from-WIRE queue: each row queued via + // `sysio.uwrit::swapfromwire` since the last advance is re-validated + // (target reserve ACTIVE + public, variance) and either becomes a + // PENDING uwreq for the single-leg underwriter race or is refunded. + // Runs before `buildenv` so this epoch's envelopes reflect any state + // the drain produced; never throws (refund-and-drop semantics). + action( + permission_level{get_self(), "owner"_n}, + UWRIT_ACCOUNT, + "drainfwq"_n, + std::make_tuple() + ).send(); + + action( + permission_level{get_self(), "owner"_n}, + get_self(), + FINISH_ADVANCE, + std::make_tuple(state.current_epoch_index, gate.emission_amount) + ).send(); + + // Preserve payout depth: finishadv and its accrual/history descendants + // complete before this sibling executes. + if (gate.is_pay_epoch) { + action( + permission_level{get_self(), "owner"_n}, + SYSTEM_ACCOUNT, + "payepoch"_n, + std::make_tuple( + state.current_epoch_index, + std::vector>{}, + gate.period_emission + ) + ).send(); + } +} + +void epoch::finishadv(uint32_t epoch_index, int64_t emission_amount) { + require_auth(get_self()); + check(get_sender() == get_self(), "finishadv must be sent inline by sysio.epoch"); + epochcfg_t cfg_tbl(get_self()); + const auto cfg = cfg_tbl.get(); + epochstate_t state_tbl(get_self()); + auto state = state_tbl.get(); + check(state.current_epoch_index == epoch_index, "finishadv epoch mismatch"); + const bool had_expiring_group = epoch_index > 1; + + // A seated operator can have lost eligibility since the window was built. + // Preserve healthy members' order and never reuse a resident to fill a gap. + opreg::operators_t current_ops(OPREG_ACCOUNT); + for (auto& group : state.batch_op_groups) { + group.erase(std::remove_if(group.begin(), group.end(), [&](name account) { + const auto key = opreg::operator_key{account.value}; + if (!current_ops.contains(key)) return true; + const auto op = current_ops.get(key); + return op.status != OperatorStatus::OPERATOR_STATUS_ACTIVE || + op.type != OperatorType::OPERATOR_TYPE_BATCH; + }), group.end()); + } // ── Slide the schedule window ─────────────────────────────────────────── // Skip on the genesis advance (0 → 1): schbatchgps just placed @@ -665,6 +738,7 @@ void epoch::advance() { // After: window = [current, current+1, ..., current+N-1], front is // always the active group → current_batch_op_group stays at 0. if (had_expiring_group && !state.batch_op_groups.empty()) { + const auto expired = state.batch_op_groups.front(); state.batch_op_groups.erase(state.batch_op_groups.begin()); // Collect already-resident accounts so the new tail excludes them. @@ -709,6 +783,31 @@ void epoch::advance() { return a.first < b.first; }); + // Repair future seats before selecting the tail. Otherwise a removed + // operator leaves a hole that eventually becomes an empty active group, + // even when a healthy standby could have been announced one epoch ahead. + // Prefer true standbys: recycling the expired group early would shorten + // its duty interval unnecessarily. All selections consume the same pool, + // so repaired groups and the tail remain disjoint. + // Vacancy recovery is an exception to the normal N-epoch duty spacing: + // absence from this window does not prove an operator has never served + // recently, particularly with windows larger than three groups. + // Do not insert a new member into the CURRENT group here: outposts have + // not received this window yet, and their old chunk-slot assignments may + // collide with a replacement's position. That case retains the existing + // incomplete-window withholding behavior and requires roster recovery. + for (size_t g = 1; g < state.batch_op_groups.size(); ++g) { + auto& group = state.batch_op_groups[g]; + while (group.size() < cfg.operators_per_epoch) { + const auto standby = std::find_if(pool.begin(), pool.end(), [&](const auto& candidate) { + return std::find(expired.begin(), expired.end(), candidate.first) == expired.end(); + }); + if (standby == pool.end()) break; + group.push_back(standby->first); + pool.erase(standby); + } + } + std::vector new_tail; new_tail.reserve(cfg.operators_per_epoch); for (size_t i = 0; i < pool.size() && new_tail.size() < cfg.operators_per_epoch; ++i) { @@ -727,9 +826,8 @@ void epoch::advance() { // is an exact half and two competing digests can both tip. // // So the schedule is left as-is and the DECISION is pushed to the emit - // site: an empty active group is never published (see the withhold - // below). Short-but-non-empty is pre-existing behaviour and is not made - // safe here -- it is reported so the roster can be repaired off-chain. + // site: an incomplete window is never published (see the withhold + // below), and is reported so the roster can be repaired off-chain. if (new_tail.size() < cfg.operators_per_epoch) { sysio::print("sysio.epoch::advance: only ", new_tail.size(), " of ", cfg.operators_per_epoch, @@ -756,21 +854,6 @@ void epoch::advance() { state_tbl.set(state, ram_payer); - // Drain matured rows from `sysio.opreg::wtdwqueue`. Operators that queued - // a withdrawal at least WITHDRAW_WAIT_EPOCHS ago are now eligible — opreg - // subtracts from the balance and emits OPERATOR_ACTION(WITHDRAW_REMIT) to - // the matching outpost (or, for WIRE-direct withdraws, CREDITS the operator's - // `sysio.opreg::remitclaims` row, which it pulls with `claimremit` — nothing - // is transferred from this path, precisely because it runs inline from here). - // Slashed-during-the-wait rows are dropped silently inside - // opreg's flushwtdw. See CLAUDE-WIRE-OPERATOR-COLLATERAL-IMPL-PLAN.md §3.3. - action( - permission_level{get_self(), "owner"_n}, - OPREG_ACCOUNT, - "flushwtdw"_n, - std::make_tuple(state.current_epoch_index) - ).send(); - // Queue OPERATORS attestation (full roster with authex chain addresses) for each outpost. // IMPORTANT: Must come before BATCH_OPERATOR_GROUPS so that the ETH outpost's // _handleOperators populates operatorEthAddress before _handleBatchOperatorGroups @@ -900,41 +983,18 @@ void epoch::advance() { const uint32_t next_group_index = next_index < group_count ? next_index : state.current_batch_op_group; - // NEVER publish an empty "next". The index names the group the outpost - // will admit `epoch_in` against and size its quorum from, so an empty - // one is not a degraded roster -- it is an invalid attestation, and - // seating it wedges the outpost permanently (the handler that could - // replace the window runs only past the gate the empty group breaks). - // - // This is the ONE sound guarantee available here. The slide cannot buy - // non-emptiness by backfilling: with an ACTIVE pool smaller than the - // window, N groups that are both FULL and DISJOINT do not exist, and - // both escapes are unsound (see the slide's comment -- re-seating a - // resident breaks Ethereum's chunk-position disjointness; a short group - // lowers the quorum denominator it defines). So the schedule is left - // alone and the decision lands here. Withholding the attestation leaves - // the outpost on its previous window -- the same end state its own - // guards reach, without shipping an invalid payload. - // - // Cost, accepted deliberately: the withheld attestation also carries - // `epoch_duration_sec` and the whole-window resync that - // batch-operator-schedule-window.md wants on every envelope, so both are - // skipped for this epoch too. Shipping the payload with the index pinned - // to the CURRENT group instead would keep them, but it names a group the - // outpost must not treat as next, and the Solana handler refuses a window - // carrying an empty group regardless -- so it buys nothing here. - // - // Withheld by SKIPPING THE QUEUEOUT ONLY -- never by returning from - // `advance`, which still has the epoch's remaining attestations and - // actions to issue after this block. - const bool have_next_group = - next_group_index < group_count && !state.batch_op_groups[next_group_index].empty(); - if (!have_next_group) { - sysio::print("sysio.epoch::advance: no non-empty next group to publish at epoch ", + // Removing ineligible members must not lower an outpost's quorum + // denominator or publish an empty group. Withhold an incomplete window, + // while still sending OPERATORS with the authoritative removal statuses. + // Never duplicate residents to fill it: Ethereum's chunk routing assumes + // disjoint groups. Epoch accounting and envelope construction still run. + const bool have_complete_window = next_group_index < group_count && + std::all_of(state.batch_op_groups.begin(), state.batch_op_groups.end(), + [&](const auto& group) { return group.size() == cfg.operators_per_epoch; }); + if (!have_complete_window) { + sysio::print("sysio.epoch::finishadv: incomplete operator window at epoch ", state.current_epoch_index, - " (groups=", group_count, ", next_index=", next_group_index, - "); withholding BatchOperatorGroups -- outposts retain their " - "previous window\n"); + "; withholding BatchOperatorGroups until the roster is repaired\n"); } attest.active_group_index = zpp::bits::vuint32_t{next_group_index}; attest.epoch_index = zpp::bits::vuint32_t{state.current_epoch_index}; @@ -959,8 +1019,8 @@ void epoch::advance() { auto out = zpp::bits::out{encoded, zpp::bits::no_size{}}; (void)out(attest); - // `have_next_group` gates the QUEUEOUT, not `advance` -- see above. - if (have_next_group) { + // Withhold only the group attestation, never the remaining epoch work. + if (have_complete_window) { sysio::chains::chains_t chains_tbl(CHAINS_ACCOUNT); for (auto it = chains_tbl.begin(); it != chains_tbl.end(); ++it) { if (!is_active_outpost(*it)) continue; @@ -978,19 +1038,6 @@ void epoch::advance() { } } - // Drain the swap-from-WIRE queue: each row queued via - // `sysio.uwrit::swapfromwire` since the last advance is re-validated - // (target reserve ACTIVE + public, variance) and either becomes a - // PENDING uwreq for the single-leg underwriter race or is refunded. - // Runs before `buildenv` so this epoch's envelopes reflect any state - // the drain produced; never throws (refund-and-drop semantics). - action( - permission_level{get_self(), "owner"_n}, - UWRIT_ACCOUNT, - "drainfwq"_n, - std::make_tuple() - ).send(); - // Build outbound envelopes for each outpost { sysio::chains::chains_t chains_tbl(CHAINS_ACCOUNT); @@ -1005,7 +1052,7 @@ void epoch::advance() { } } - // Emissions side. Three inline actions queued in FIFO order: + // Emissions side. Accrual and history precede advance's payout sibling: // 1. accrueepoch: always queued. Records this epoch's per-epoch share // onto t5state (pending_emission_amount + batch_group_epochs[group] // + last_epoch_emission for decay continuity). @@ -1014,7 +1061,7 @@ void epoch::advance() { // 3. payepoch: queued only on pay-epochs. Reads the now-updated t5state // (which already includes this epoch's contribution from step 1), // distributes period_emission, and resets the accumulator. - // Both run after advance() returns; their FIFO ordering guarantees + // The continuation completes before that sibling; this ordering guarantees // payepoch sees the post-accrue roster history and state. std::vector active_batch_op_members; if (state.current_batch_op_group < state.batch_op_groups.size()) { @@ -1028,7 +1075,7 @@ void epoch::advance() { std::make_tuple( state.current_epoch_index, state.current_batch_op_group, - gate.emission_amount + emission_amount ) ).send(); @@ -1039,19 +1086,6 @@ void epoch::advance() { std::make_tuple(state.current_epoch_index, active_batch_op_members) ).send(); - if (gate.is_pay_epoch) { - action( - permission_level{get_self(), "owner"_n}, - SYSTEM_ACCOUNT, - "payepoch"_n, - std::make_tuple( - state.current_epoch_index, - std::vector>{}, - gate.period_emission - ) - ).send(); - } - // Working tables on `sysio.msgch` (`envelopes` / `messages` / // `attestations` / `outenvelopes`) are now drained inline by the // `evalcons` consensus-reach + `buildenv` write paths. The durable diff --git a/contracts/sysio.epoch/sysio.epoch.abi b/contracts/sysio.epoch/sysio.epoch.abi index 7d7ed6b341..2f292b432f 100644 --- a/contracts/sysio.epoch/sysio.epoch.abi +++ b/contracts/sysio.epoch/sysio.epoch.abi @@ -121,6 +121,20 @@ } ] }, + { + "name": "finishadv", + "base": "", + "fields": [ + { + "name": "epoch_index", + "type": "uint32" + }, + { + "name": "emission_amount", + "type": "int64" + } + ] + }, { "name": "pause", "base": "", @@ -169,6 +183,11 @@ "type": "advance", "ricardian_contract": "" }, + { + "name": "finishadv", + "type": "finishadv", + "ricardian_contract": "" + }, { "name": "pause", "type": "pause", diff --git a/contracts/sysio.epoch/sysio.epoch.wasm b/contracts/sysio.epoch/sysio.epoch.wasm index 23a5d0d81266971407d8f33bb7b854608acf1ff5..3f5f0a766af9abaeb85822509ff7323fb5594cb2 100755 GIT binary patch literal 83254 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 FzW}*#TjT%$ diff --git a/contracts/tests/sysio.msgch_chain_tests.cpp b/contracts/tests/sysio.msgch_chain_tests.cpp index 3cb66df44a..0d2c909d5b 100644 --- a/contracts/tests/sysio.msgch_chain_tests.cpp +++ b/contracts/tests/sysio.msgch_chain_tests.cpp @@ -25,6 +25,7 @@ /// oracle == Solidity/Rust (golden vectors) and contract == oracle (round-trip assertions) /// together give contract == outposts. #include +#include #include #include #include @@ -687,6 +688,59 @@ class sysio_msgch_chain_tester : public tester { return groups_attestation; } + /// Inspect the actual emitted envelope, after inline buildenv drained queueout. + /// The final OPERATORS snapshot must match the registry, and any published + /// schedule must follow that snapshot and contain only active batch operators. + void require_fresh_roster(uint64_t chain_code, name account, + opp::types::OperatorStatus expected_status) { + const auto row = find_outbound_envelope(chain_code); + BOOST_REQUIRE(!row.is_null()); + const auto env = decode_envelope(row["raw_envelope"].as>()); + BOOST_REQUIRE_EQUAL(env.messages_size(), 1); + bool found_operator = false; + bool have_operators = false; + for (const auto& att : env.messages(0).payload().attestations()) { + if (att.type() == opp::types::ATTESTATION_TYPE_OPERATORS) { + opp::attestations::Operators roster; + BOOST_REQUIRE(roster.ParseFromString(att.data())); + have_operators = true; + for (const auto& entry : roster.operators()) { + const auto registered = get_operator(name{entry.account().name()}); + BOOST_REQUIRE(!registered.is_null()); + BOOST_REQUIRE_EQUAL(entry.status(), + registered["status"].as()); + if (entry.account().name() == account.to_string()) { + BOOST_REQUIRE_EQUAL(entry.status(), expected_status); + found_operator = true; + } + } + } else if (att.type() == opp::types::ATTESTATION_TYPE_BATCH_OPERATOR_GROUPS) { + BOOST_REQUIRE(have_operators); + opp::attestations::BatchOperatorGroups groups; + BOOST_REQUIRE(groups.ParseFromString(att.data())); + std::set members; + for (const auto& group : groups.groups()) { + for (const auto& address : group.operators()) { + BOOST_REQUIRE(members.insert(address.address()).second); + const auto registered = get_operator(name{address.address()}); + BOOST_REQUIRE(!registered.is_null()); + BOOST_REQUIRE_EQUAL(opp::types::OPERATOR_STATUS_ACTIVE, + registered["status"].as()); + } + } + } + } + BOOST_REQUIRE(found_operator); + if (expected_status != opp::types::OPERATOR_STATUS_ACTIVE) { + const auto state = read_epoch_state(); + for (const auto& group : state["batch_op_groups"].get_array()) { + for (const auto& member : group.get_array()) { + BOOST_REQUIRE(member.as_string() != account.to_string()); + } + } + } + } + /// How many BATCH_OPERATOR_GROUPS attestations the most recent `advance` shipped to /// `chain_code` -- 0 when the depot WITHHELD it. Distinct from /// `shipped_batch_operator_groups`, which fails the test on absence: the withhold path @@ -856,8 +910,10 @@ class sysio_msgch_chain_tester : public tester { /// bootstrap() variant for a real rotation: THREE single-operator groups (so a resident op is on /// duty once per 3-epoch rotation), the SEC-28 percent rail disabled up to its accepted ceiling /// (99, so an anchored run terminates on the CONSECUTIVE rail), and `terminate_window_ms` set by - /// the caller (the exact span bound for this schedule). ETH outpost registered; genesis advance run. - void bootstrap_rotation(uint64_t terminate_window_ms) { + /// the caller (the exact span bound for this schedule). The target is non-bootstrapped + /// by default; healthy-rotation tests may opt into the bootstrap exemption. + /// ETH outpost registered; genesis advance run. + void bootstrap_rotation(uint64_t terminate_window_ms, bool batchop_is_bootstrapped = false) { BOOST_REQUIRE_EQUAL(success(), push(EPOCH_ACCOUNT, epoch_abi, EPOCH_ACCOUNT, "setconfig"_n, mvo() ("epoch_duration_sec", EPOCH_DURATION_SEC) ("operators_per_epoch", 1) @@ -883,13 +939,13 @@ class sysio_msgch_chain_tester : public tester { register_chain(opp::types::ChainKind::CHAIN_KIND_EVM, "ETH", 31337); - // BATCHOP is the termination target: NON-bootstrapped (bootstrapped operators are exempt from + // By default BATCHOP is the termination target: NON-bootstrapped (bootstrapped operators are exempt from // rolling-window termination -- see opreg::termcheck) and collateralized so it activates. // BATCHOP_B / BATCHOP_C are bootstrapped fillers for the other two groups. schbatchgps sorts // non-bootstrapped first, so BATCHOP lands in group 0 (on duty at epochs 1, 4, 7, ...). BOOST_REQUIRE_EQUAL(success(), push(OPREG_ACCOUNT, opreg_abi, OPREG_ACCOUNT, "regoperator"_n, mvo() ("account", BATCHOP.to_string())("type", opp::types::OperatorType::OPERATOR_TYPE_BATCH) - ("is_bootstrapped", false))); + ("is_bootstrapped", batchop_is_bootstrapped))); BOOST_REQUIRE_EQUAL(success(), depositinle(BATCHOP, "ETH", "ETH", 1)); for (const auto& op : {BATCHOP_B, BATCHOP_C}) { BOOST_REQUIRE_EQUAL(success(), push(OPREG_ACCOUNT, opreg_abi, OPREG_ACCOUNT, "regoperator"_n, mvo() @@ -899,7 +955,8 @@ class sysio_msgch_chain_tester : public tester { BOOST_REQUIRE(!get_operator(BATCHOP).is_null()); BOOST_REQUIRE(opp::types::OperatorStatus::OPERATOR_STATUS_ACTIVE == get_operator(BATCHOP)["status"].as()); - BOOST_REQUIRE_EQUAL(0, get_operator(BATCHOP)["is_bootstrapped"].as_uint64()); + BOOST_REQUIRE_EQUAL(static_cast(batchop_is_bootstrapped), + get_operator(BATCHOP)["is_bootstrapped"].as_uint64()); BOOST_REQUIRE_EQUAL(success(), push(EPOCH_ACCOUNT, epoch_abi, EPOCH_ACCOUNT, "schbatchgps"_n, mvo())); BOOST_REQUIRE_EQUAL(success(), push(EPOCH_ACCOUNT, epoch_abi, EPOCH_ACCOUNT, "advance"_n, mvo())); @@ -1646,6 +1703,11 @@ BOOST_FIXTURE_TEST_CASE(noncanonical_delivery_slashes_before_termination, sysio_ slash_action_count(BATCHOP, SOL_OUTPOST_ID)); BOOST_REQUIRE_EQUAL(epoch + kEpochAdvanceCount, current_epoch()); BOOST_REQUIRE_EQUAL(kExpectedDeliveredLogCount, delivered_dellog_count(BATCHOP)); + for (const auto chain : {ETH_OUTPOST_ID, SOL_OUTPOST_ID}) { + require_fresh_roster(chain, BATCHOP, opp::types::OPERATOR_STATUS_SLASHED); + // The remaining two members must not be advertised with a reduced quorum. + BOOST_REQUIRE_EQUAL(0, shipped_batch_operator_groups_count(chain)); + } } FC_LOG_AND_RETHROW() } // SEC-28 (huang review): terminate on the CONSECUTIVE-miss rail through the REAL rotation -- a @@ -1703,6 +1765,8 @@ BOOST_FIXTURE_TEST_CASE(terminate_at_duty_rotation_via_advance, sysio_msgch_chai // whereas termination + reason hold either way. BATCHOP delivered exactly once, so exactly // one delivered row must remain. BOOST_REQUIRE_EQUAL(1u, delivered_dellog_count(BATCHOP)); + require_fresh_roster(ETH_OUTPOST_ID, BATCHOP, + opp::types::OPERATOR_STATUS_TERMINATED); } else { // Still ACTIVE: BATCHOP must not terminate before its sixth miss (its 7th duty). BOOST_REQUIRE(status == opp::types::OperatorStatus::OPERATOR_STATUS_ACTIVE); @@ -1998,10 +2062,10 @@ BOOST_FIXTURE_TEST_CASE(slash_after_delivery_does_not_count_toward_consensus, sy /// rotation so every group is promised (and verified) at least once. BOOST_FIXTURE_TEST_CASE(advance_ships_lookahead_batch_operator_group, sysio_msgch_chain_tester) { try { constexpr uint32_t kGroups = 3; - // Termination rails are irrelevant here — a comfortably wide window keeps recorddel/termcheck - // quiet while the rotation is walked (same span shape the SEC-28 fixture derives). + // Use bootstrapped operators so missed deliveries cannot terminate a + // member during this test of healthy rotation and lookahead. constexpr uint64_t kRotationWindowMs = 12ULL * kGroups * EPOCH_DURATION_SEC * 1000ULL; - bootstrap_rotation(kRotationWindowMs); + bootstrap_rotation(kRotationWindowMs, /*batchop_is_bootstrapped=*/true); for (uint32_t round = 0; round < kGroups + 1; ++round) { const auto shipped = shipped_batch_operator_groups(ETH_OUTPOST_ID); @@ -2077,9 +2141,9 @@ BOOST_FIXTURE_TEST_CASE(advance_withholds_batch_operator_groups_when_next_group_ } produce_blocks(); - // The terminated pair still occupy their seats until they slide out, so the sole survivor is - // resident and the residency-excluded pool is empty: every tail from here is empty. Walk the - // window so an empty group reaches the lookahead seat, then hold there. + // Removed operators are pruned from surviving seats immediately. The pool + // cannot fill the window, so group attestations remain withheld while epoch + // accounting and authoritative operator-status publication continue. bool observed_withhold = false; for (uint32_t round = 0; round < kGroups + 1; ++round) { advance_to_next_epoch(); @@ -2096,4 +2160,85 @@ BOOST_FIXTURE_TEST_CASE(advance_withholds_batch_operator_groups_when_next_group_ "starved window never withheld BATCH_OPERATOR_GROUPS -- an empty active group was published"); } FC_LOG_AND_RETHROW() } +// WIRE-385: a removal during this advance must be visible in BOTH emitted +// attestations, with a healthy standby filling the newly selected tail. +BOOST_FIXTURE_TEST_CASE(advance_roster_excludes_same_epoch_termination, + sysio_msgch_chain_tester) { try { + bootstrap(/*n_batch_ops=*/3, /*batchop_is_bootstrapped=*/false); + BOOST_REQUIRE_EQUAL(success(), push(OPREG_ACCOUNT, opreg_abi, OPREG_ACCOUNT, + "regoperator"_n, mvo()("account", BATCHOP_D.to_string()) + ("type", opp::types::OPERATOR_TYPE_BATCH)("is_bootstrapped", true))); + produce_blocks(); + advance_to_next_epoch(); // missed delivery terminates only the non-bootstrap operator + for (const auto chain : {ETH_OUTPOST_ID, SOL_OUTPOST_ID}) { + require_fresh_roster(chain, BATCHOP, opp::types::OPERATOR_STATUS_TERMINATED); + require_fresh_roster(chain, BATCHOP_B, opp::types::OPERATOR_STATUS_ACTIVE); + const auto groups = shipped_batch_operator_groups(chain); + BOOST_REQUIRE_EQUAL(groups.groups_size(), 1); + BOOST_REQUIRE_EQUAL(groups.groups(0).operators_size(), 3); + BOOST_REQUIRE_EQUAL(groups.groups(0).operators(0).address(), BATCHOP_B.to_string()); + BOOST_REQUIRE_EQUAL(groups.groups(0).operators(1).address(), BATCHOP_C.to_string()); + BOOST_REQUIRE_EQUAL(groups.groups(0).operators(2).address(), BATCHOP_D.to_string()); + } +} FC_LOG_AND_RETHROW() } + +BOOST_FIXTURE_TEST_CASE(advance_removes_inactive_surviving_group_members, + sysio_msgch_chain_tester) { try { + constexpr uint32_t GROUP_COUNT = 3; + constexpr uint64_t WINDOW_MS = 12ULL * GROUP_COUNT * EPOCH_DURATION_SEC * 1000ULL; + bootstrap_rotation(WINDOW_MS); + for (const auto op : {BATCHOP_B, BATCHOP_C}) { + BOOST_REQUIRE_EQUAL(success(), push(OPREG_ACCOUNT, opreg_abi, CHALG_ACCOUNT, + "slash"_n, mvo()("account", op.to_string())("reason", "roster regression"))); + } + produce_blocks(); + advance_to_next_epoch(); + for (const auto op : {BATCHOP_B, BATCHOP_C}) { + require_fresh_roster(ETH_OUTPOST_ID, op, opp::types::OPERATOR_STATUS_SLASHED); + } + BOOST_REQUIRE_EQUAL(0, shipped_batch_operator_groups_count(ETH_OUTPOST_ID)); +} FC_LOG_AND_RETHROW() } + +BOOST_FIXTURE_TEST_CASE(advance_repairs_future_group_before_it_becomes_current, + sysio_msgch_chain_tester) { try { + constexpr uint64_t WINDOW_MS = 12ULL * 3 * EPOCH_DURATION_SEC * 1000ULL; + bootstrap_rotation(WINDOW_MS, /*batchop_is_bootstrapped=*/true); + // schbatchgps interleaves the sorted roster: the initial window is [A,C,B]. + // Remove the last group so its vacancy is still in the future after sliding. + const auto initial = shipped_batch_operator_groups(ETH_OUTPOST_ID); + BOOST_REQUIRE_EQUAL(initial.groups_size(), 3); + BOOST_REQUIRE_EQUAL(initial.groups(2).operators(0).address(), BATCHOP_B.to_string()); + BOOST_REQUIRE_EQUAL(success(), push(OPREG_ACCOUNT, opreg_abi, OPREG_ACCOUNT, + "regoperator"_n, mvo()("account", BATCHOP_D.to_string()) + ("type", opp::types::OPERATOR_TYPE_BATCH)("is_bootstrapped", true))); + BOOST_REQUIRE_EQUAL(success(), push(OPREG_ACCOUNT, opreg_abi, OPREG_ACCOUNT, + "terminate"_n, mvo()("account", BATCHOP_B.to_string())("reason", "future seat removed"))); + produce_blocks(); + advance_to_next_epoch(); + require_fresh_roster(ETH_OUTPOST_ID, BATCHOP_B, opp::types::OPERATOR_STATUS_TERMINATED); + const auto repaired = shipped_batch_operator_groups(ETH_OUTPOST_ID); + BOOST_REQUIRE_EQUAL(repaired.groups_size(), 3); + for (int i = 0; i < repaired.groups_size(); ++i) { + BOOST_REQUIRE_EQUAL(repaired.groups(i).operators_size(), 1); + } + BOOST_REQUIRE_EQUAL(repaired.groups(0).operators(0).address(), BATCHOP_C.to_string()); + BOOST_REQUIRE_EQUAL(repaired.groups(1).operators(0).address(), BATCHOP_D.to_string()); + BOOST_REQUIRE_EQUAL(repaired.groups(2).operators(0).address(), BATCHOP.to_string()); + advance_to_next_epoch(); + const auto next = shipped_batch_operator_groups(ETH_OUTPOST_ID); + BOOST_REQUIRE_EQUAL(next.groups_size(), 3); + BOOST_REQUIRE_EQUAL(next.groups(0).operators_size(), 1); + BOOST_REQUIRE_EQUAL(next.groups(0).operators(0).address(), BATCHOP_D.to_string()); + require_fresh_roster(ETH_OUTPOST_ID, BATCHOP_B, opp::types::OPERATOR_STATUS_TERMINATED); +} FC_LOG_AND_RETHROW() } + +BOOST_FIXTURE_TEST_CASE(finishadv_rejects_direct_calls, sysio_msgch_chain_tester) { try { + bootstrap(); + const auto args = mvo()("epoch_index", current_epoch())("emission_amount", int64_t{0}); + BOOST_REQUIRE_EQUAL(error("missing authority of sysio.epoch"), + push(EPOCH_ACCOUNT, epoch_abi, BATCHOP, "finishadv"_n, args)); + BOOST_REQUIRE_EQUAL(error("assertion failure with message: finishadv must be sent inline by sysio.epoch"), + push(EPOCH_ACCOUNT, epoch_abi, EPOCH_ACCOUNT, "finishadv"_n, args)); +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END()