Skip to content

feat(platform)!: add compilation readiness rounds, reports and funds to drive - #5211

Open
DCG-Claude wants to merge 32 commits into
v6.0-devfrom
dashvm/r08-12
Open

DCG-Claude wants to merge 32 commits into
v6.0-devfrom
dashvm/r08-12

Conversation

@DCG-Claude

@DCG-Claude DCG-Claude commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Issue being fixed or feature implemented

Part 1 of 2 for R08-12 of the smart contract plan (#4626): distinct evonode readiness votes with threshold-triggered eligibility pruning against the agreed current membership, and a crossing that is committed only after full validation.

A smart contract's executable bundle activates only once enough evonodes report that they have prepared it. The confirmed policy (Q10 and Q11 in the plan) needs Platform state that does not exist yet: one pending round per contract that a replacement cancels atomically, a raw report count that is explicitly not an eligibility proof, a persisted position for a membership walk that spans blocks, a last-evaluated mark so a membership change reconsiders every round, a committed activation deadline computed from chain timestamps, and a fund the deployer pays validation from. This part adds that state to Drive, creates it at genesis and on upgrade, and proves it. Part 2 adds the block event that tallies reports against the agreed evonode set, prunes ineligible reporters and records a fully validated crossing.

Refs #4684

What was done?

rs-dpp (packages/rs-dpp/src/voting/readiness/): versioned models ReadinessRound (round id derived from network magic, contract, version, bundle digest and acceptance height; Pending or Crossed status with the deadline arithmetic crossing + clamp(crossing - accepted, min, max); the last evaluation mark ReadinessEvaluation { core_height, raw_count }; typed ReadinessPayer), ReadinessReportRecord, ReadinessScanCursor (bound to one membership view). VOTING_VERSION_V3 declares their structure versions.

rs-drive (packages/rs-drive/src/drive/votes/readiness/): storage under [Votes] / r (rounds keyed under their contract with a current-round pointer, a CountTree of reports keyed by pro tx hash, the scan cursor, the deadline queue keyed by encode_u64(deadline), the evaluation cursor and the retired-round cleanup queue) and readiness funds under [PreFundedSpecializedBalances] / 129. Methods: open_readiness_round_operations (replacement is a pointer swap plus fixed-size writes; an opening whose round id still has a tree, the current round or a retired one awaiting cleanup, is refused), cancel_readiness_round_operations (an estimate prices the reads and the cancellation of a crossed, funded round), activate_readiness_round_operations (requires the block to have reached the committed deadline), the shared retire_readiness_round_operations (never opens the reports tree; removes a crossed round's deadline entry and its time tree once empty; charges the cleanup reserve to the epoch processing pool, priced in full and at the widest sum item when estimating; returns the payer refund, which the opening nets into the new debit when the same identity funded both rounds and otherwise credits to the old round's payer), insert_readiness_report_operations (insert-if-absent, returns whether new), prune_readiness_reports_operations (its estimate prices each report's delete walk through the helper cleanup uses), paged fetches of reports and rounds, scan and evaluation cursor reads and writes, update_readiness_round_evaluation_operations, record_readiness_crossing_operations, fetch_readiness_rounds_due, fetch_retired_readiness_round, cleanup_retired_readiness_round_operations (one bounded step), the fund family on [40, 129] (copies of the voting siblings, with a reserve the deductions cannot touch; add, deduct and empty read the stored fund through the checked fetch, which refuses a negative sum item or any other element type), estimation costs, typed ReadinessOperationType and two PrefundedSpecializedBalanceOperationType variants, DriveError::ReadinessRoundMissing. Vote setup generation 1 (add_initial_vote_tree_main_structure_operations_v1) creates both structures at genesis through add_readiness_structure_operations. Verifiers verify_readiness_round (its result type VerifiedReadinessRound is re-exported from drive::verify::voting), verify_readiness_report and verify_readiness_fund under packages/rs-drive/src/verify/voting/ compile with the verify feature alone; their provers share the queries in drive/votes/readiness/queries.rs. The batch dispatchers refuse, before conversion, a batch whose readiness writes would overwrite each other: more than one opening, cancellation or activation, a readiness fund written twice, a raw GroveDB operation (GroveDBOperation, GroveDBOpBatch) beside a settlement or readiness fund write, since the guard cannot see which balances a raw write touches, or an opening, cancellation or activation beside any identity balance or readiness fund write. Opening, cancellation and activation are applied as ReadinessOperationType operations through apply_drive_operations, because a refund can repay identity debt and only that path routes the repaid debt to the processing pool. These writes are absolute values computed from the committed state, so the generation 1 balance-write merge cannot net them; no production path builds such a batch today, and part 2 schedules one settlement per batch.

rs-platform-version: placeholder v15.rs and v16.rs (byte-identical to the open 6.0 siblings), v17.rs as a struct update over PLATFORM_V16, LATEST_VERSION = 17; DRIVE_VERSION_V10 (vote method table V3 with the readiness group, verify method table V3), DRIVE_ABCI_METHOD_VERSIONS_V11 (protocol change hook generation 3), SYSTEM_LIMITS_V5 (readiness_additional_wait_min_ms 120 s, readiness_additional_wait_max_ms 3,600 s), VOTING_VERSION_V3. Shipped tables backfilled with None and 0, including the mock in mocks/v2_test.rs.

rs-drive-abci: perform_events_on_first_block_of_protocol_change generation 3 runs the generation 2 events and then transition_to_version_17_compilation_readiness, which inserts the same elements genesis emits, if absent.

Book: book/src/drive/compilation-readiness.md (layout, models, rounds, reports, cursors, cleanup, funds, genesis and upgrade, proofs) and a paragraph in book/src/versioning/platform-version.md.

Observed while implementing, not fixed here: the processor binds the result of the prefunded specialized balance pre-check to an unused variable (execution/validation/state_transition/processor/v0/mod.rs), so an underfunded masternode vote fails only at deduction. The readiness report transition will put its fund check in the state tier for that reason.

Nothing in this part executes guest code, and nothing here decides eligibility: the count tree holds raw distinct reports, and the block event of part 2 is the only path that turns a raw count into a crossing.

How Has This Been Tested?

Local gate on the current v6.0-dev tip, every command redirected to a file with the exit code checked, each exit code 0:

cargo fmt --all -- --check
cargo clippy -p dpp -p drive -p drive-abci -p platform-version --all-features --all-targets -- -D warnings
cargo check --workspace --all-targets
cargo check -p drive --no-default-features --features verify
cargo test -p dpp --all-features readiness
cargo test -p drive readiness
cargo test -p drive-abci perform_events_on_first_block_of_protocol_change

New tests, all against PlatformVersion::latest() unless the test is about the version boundary:

  • dpp (voting/readiness/{round,report_record,scan_cursor}/mod.rs): serialization round trips of the models, unknown structure version rejection, wait clamping (30 s gives 120 s, 600 s gives 600 s, 7,200 s gives 3,600 s), saturating and overflowing deadlines, inverted bounds, round id sensitivity to network, height, digest and version, a second crossing refused with the first deadline kept, a scan page classifying more reports than it examined refused with the cursor unchanged, a scan page ending at or before the previous position refused with the cursor unchanged.
  • drive (drive/votes/readiness/tests.rs): genesis at 14 has neither [112, r] nor [40, 129] and latest has both; the readiness methods report VersionNotActive at 14; fund add, deduct, empty, the deduct-below-zero and reserve errors, prove and verify present and absent, conservation with a funded and a partially spent round; open (pointer, record, empty count tree, fund, payer debit), insufficient payer balance, insert once with a retransmission not new and the count equal to distinct keys, page order and the after cursor, report proofs, prune, scan cursor store, advance and clear, evaluation mark and fairness cursor, crossing with the deadline queue and the due query, round proof present and absent; replacement in one batch (pointer swapped, old round retired and unreachable, its deadline entry gone, its fund settled with the reserve in the pool and the remainder netted against the new funding), replacement by another payer (the old payer gets its own remainder, the new payer pays the full funding, and the estimate covers the applied fee), constant retirement cost (a round holding 2,000 reports and an empty one pay the same storage fee, processing differs by one bounded root node read, and the estimate covers both), cancellation with refund and the vote tree shape kept, estimated cancellation of a funded pending and a funded crossed round through apply_drive_operations covering the applied processing and storage fees, activation of a crossed round and refusal of a pending, early (one millisecond before the deadline) or stale one, an activation refund that repays a 5M identity debt routes it to the processing pool with the estimate covering the applied cost and credits conserved, while an activation beside a balance write is refused, reopening a cancelled round awaiting cleanup is refused and leaves it queued and the payer untouched while an opening at the next height succeeds, a fund amount no balance can hold is refused when estimating as when applying, a stored fund that is a negative sum item, an item or a sum tree is refused by add, deduct and empty, a raw GroveDB write (single operations or a batch, before or after) beside an opening or a fund write is refused estimated, applied and converted with the payer untouched, a batch of a raw write and a balance credit still converts and applies at protocol version 13, rollback of an opening leaves no partial subtree, reopening the current round is refused, two retirements in one batch are refused estimated, applied and converted while one per batch credits every reserve, a readiness fund written twice in one batch is refused (two adds, two deductions, an add and a deduction) while adds batched apart sum and the reserve holds against a second deduction, an opening or cancellation beside a debit, a credit or a fund write is refused and leaves the payer untouched while the debit and the opening batched apart both reach the payer, estimating the pruning of 512 of 2,000 reports covers the applied processing and storage fees, estimates of a new fund (single and a batch of 16) and of activation (fee and write count) cover the applied cost, activation estimates on an empty drive, a crossing that is estimated or fails leaves the round pending, the estimation record sizes cover the widest serialized records; cleanup drains 2,000 reports in exactly four steps of 512 and removes the record, cursor, count tree, round tree, contract tree and queue entry, keeps the contract tree while a live round remains, keeps a deadline tree shared by two rounds when one retires and removes it with the last, every applied 512-report cleanup step stays within the estimated step, and a due query with limit 1 still finds a later deadline after an earlier round retires.
  • drive integration (packages/rs-drive/tests/readiness_proofs.rs): an absent round proved and verified from outside the crate into drive::verify::voting::VerifiedReadinessRound.
  • drive-abci (perform_events_on_first_block_of_protocol_change/v3/mod.rs): a node born at 17 and a node upgraded from 16 hold byte-identical elements at every level of [Votes] / r and [PreFundedSpecializedBalances] / 129 (the whole database root is not compared: the [Votes] Merk has the same children on both but its AVL root key depends on insertion order, e at genesis and d after the upgrade appends r; a chain has one history, so no two nodes of one network take different paths); the transition is idempotent and replayable after a rolled-back transaction; the dispatcher runs it once when crossing 16 to 17 and not at 16; fund, deduct, cancel and conservation on both a fresh and an upgraded database.

In-place changes to shipped generations

The readiness batch guard (refuse_conflicting_readiness_writes) is called at the top of three existing batch generations, each commented at the call:

  • apply_drive_operations v0, selected by protocol versions 1 to 13.
  • convert_drive_operations_to_grove_operations v0, selected by every protocol version.
  • apply_drive_operations v1, selected from protocol version 14. Protocol 14 is unreleased, so this generation is not shipped; it is listed so the guard can be audited in one place.

Why consensus cannot change there: the guard refuses only batches holding a ReadinessOperation or a readiness fund write (CreateNewReadinessFund, DeductFromReadinessFund). These variants are new in this PR, nothing builds them below protocol version 17, and their own Drive methods report VersionNotActive there. Any batch those versions replay holds none of them and passes through unchanged, raw GroveDB operations included. should_apply_raw_and_balance_writes_in_one_batch_at_protocol_version_13_as_before converts and applies such a batch at protocol version 13.

Breaking Changes

None for shipped protocol versions: every new table field is backfilled and the genesis shape at 14 is unchanged (tested). Protocol version 17 is unreleased; on the 6.0 branch it adds two subtrees at genesis and on upgrade, which changes the state root every node must agree on at that version, hence the ! (the same convention as the other 6.0 pull requests that add consensus state at protocol 17). No state transition changes in this part.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

Decisions taken (provisional values)

All provisional values point at the shared allocation register of the plan and are revised, if at all, before any network is asked to propose protocol version 17.

  • Readiness subtree key r under Votes, inner keys 0 (contracts), 1 (deadlines), 2 (evaluation cursor), 3 (retired queue), pointer key c, round tree keys 0 (record), 1 (reports), 2 (cursor). The records live under Votes rather than the contract's execution namespace because that namespace does not exist yet, the deadline queue and fairness cursor are global anyway, and a round is transient vote state of the same kind as a contested vote poll; the Drive API stays the same if the per-round records move before release.
  • Prefunded purpose key 129 beside the voting funds at 128; fund id hash_double("dashvm-readiness-fund-v1" || round_id); the fund tree is created by vote setup generation 1 because genesis builds the lower layers in one batch and the prefunded helper is unversioned.
  • Round id hash_double(network_magic || contract_id || version || bundle_digest || accepted_at_height). The bundle digest is carried as an opaque 32-byte value; the crate that computes it over every module, binding and entry of a bundle is a separate pull request, so this part depends on nothing.
  • Wait bounds 120 s and 3,600 s are confirmed policy; only their placement in SystemLimits is a choice.
  • The Drive methods take every credit amount (initial funding, cleanup reserve, fees) as parameters instead of reading a fee group that does not exist yet; part 2 introduces the readiness fee rows and passes the schedule values.
  • Retiring a round never opens its reports tree; the retired subtree is drained by a bounded per-block cleanup step funded by the cleanup reserve. The pinned GroveDB's flat drop primitive is not adopted because Drive has never used it and it needs a post-commit flush.
  • The retirement helper returns the payer refund instead of writing it so one batch writes a payer's balance once (two absolute writes on one balance key in one batch collapse): the opening nets it into the new debit only when the same identity funded the retired round, and otherwise debits the new payer in full and credits the old payer separately. The pool credit is likewise one absolute write per applied batch, so apply_drive_operations and convert_drive_operations_to_grove_operations refuse a batch holding more than one readiness opening or cancellation (NotSupported). The pool credit helper is versioned on its own credit_pool slot.
  • Retirement deletes a crossed round's deadline entry and walks up to delete its per-time tree only when that tree is left empty, so a time shared by two rounds keeps its tree. An empty time tree would still spend the due query's limit and could hide a later live deadline. The block event still skips an entry whose round is no longer current, as a guard.
  • Estimates read no state, so opening and cancellation price the largest shape: a crossed round with a deadline entry and time tree, a fund to settle, the whole cleanup reserve credited to the epoch pool (written as the widest sum item, because the estimator bills a plain element by its serialized size while the applied write bills the fixed sum item size), and a separate refund write to the retired round's payer.
  • Activation and cancellation estimates price a crossed placeholder round without reading state, and a new fund is estimated as the widest sum item. The estimated cleanup step prices every delete as GroveDB's average-case merk delete with propagation plus the applied step's reads: an upper bound about ten times the applied cost at 512 deletes (544,218,320 against 56,021,580 processing credits), because applied deletes in one batch share ancestors and I found no tighter bound that provably survives rebalancing. The estimation record sizes (224, 16 and 64 bytes) cover the widest serialized round, report and scan cursor (223, 13 and 59 bytes).
  • An opening whose derived id is already the contract's current round is refused before anything is retired; a crossing publishes its new state to the caller only when the operations are built and applied.
  • Cancellation refunds to ReadinessPayer::Identity by an identity balance credit; the contract-bucket owner arrives with the typed storage flags work.
  • Version numbering: no sibling had merged into the base (then v5.0-dev, now v6.0-dev) when this part was cut, so it ships DRIVE_VERSION_V10, DRIVE_ABCI_METHOD_VERSIONS_V11, SYSTEM_LIMITS_V5 and hook generation 3 as struct updates over the base's newest tables; when a sibling lands first the same-named table is amended in place.
  • The protocol change hook tests keep the test_ prefix of every existing test in that module rather than the should prefix used elsewhere, for consistency within the file.

Part 1 of 2 for R08-12.

Refs #4684

🤖 Generated with Claude Code

PR Hygiene · 793d751

  • Bots — coderabbitai skipped after the window · thepastaclaw ✓
  • Self-review — not asked of a bot author
  • Within your 5 open PRs
  • Build green
  • Approvals
    • files with no dedicated owner (book/src/SUMMARY.md, book/src/drive/compilation-readiness.md, book/src/versioning/platform-version.md and 27 more) — QuantumExplorer or shumkov
    • dpp (packages/rs-dpp/src/voting/mod.rs, packages/rs-dpp/src/voting/readiness/mod.rs, packages/rs-dpp/src/voting/readiness/payer.rs and 6 more) — QuantumExplorer or shumkov
    • rs-drive-abci (packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/mod.rs, packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v3/mod.rs) — QuantumExplorer or shumkov
    • rs-drive (packages/rs-drive/grovedb-structure.json, packages/rs-drive/src/drive/prefunded_specialized_balances/mod.rs, packages/rs-drive/src/drive/prefunded_specialized_balances/structure.rs and 81 more) — QuantumExplorer or shumkov

When every box is checked the PR Hygiene check passes and this can merge.

@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository: dashpay/platform/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 95e09ac2-08bb-4124-8521-753bab6a308d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the waiting-bots Waiting for the review bots to report on this head label Sep 29, 2026
@github-actions github-actions Bot added this to the v5.0.0 milestone Sep 29, 2026
@thepastaclaw

thepastaclaw commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

⚠️ DEGRADED — Final review complete — no blockers (commit 793d751) · triage: critical · stand-in models (primary models out of quota)

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final validation — Phase 1 + Phase 2

The readiness storage and protocol-17 upgrade boundaries are generally sound; 10 DPP tests, 28 Drive tests, 21 upgrade tests, and verify-only compilation passed. Independent probes confirmed three blocking estimation defects and six nonblocking API correctness or version-boundary issues. These affect the new storage APIs, but do not establish a live exploit or prepare/process disagreement in this storage-only implementation.

🔴 3 blocking | 🟡 6 suggestion(s)

Review provenance

Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: muse-spark-1.3-contributor (agent: phase1-reviewer, role: architecture-layering); reviewer 3: muse-spark-1.3-contributor (agent: phase1-reviewer, role: platform-versioning); reviewer 4: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 5: muse-spark-1.3-contributor (agent: phase1-reviewer, role: security-auditor); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 8: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 9: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 10: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 11: gpt-6-astra (agent: phase2-reviewer, role: general); reviewer 12: gpt-6-astra (agent: phase2-reviewer, role: architecture-layering); reviewer 13: gpt-6-astra (agent: phase2-reviewer, role: platform-versioning); reviewer 14: gpt-6-astra (agent: phase2-reviewer, role: rust-quality); reviewer 15: gpt-6-astra (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — This large, intricate diff directly changes funds movement in retire_readiness_round_operations_v0 and open_readiness_round_operations_v0 through cleanup-reserve charges and payer refunds, and adds persistent storage migration logic in perform_events_on_first_block_of_protocol_change/v2/mod.rs.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — architecture-layering (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — platform-versioning (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — security-auditor (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 13% left, 5h 100% left), glm-5.3-flash (not used above high effort; tier asks max)
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — general (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — security-auditor (completed, effort xhigh); agent phase2-reviewer
  • Model comparison: every Phase-2 reviewer also ran on gpt-6-astra; the verifier weighed both sets without knowing which model wrote which
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-drive/src/drive/votes/readiness/fund/add_readiness_fund_operations/v0/mod.rs`:
- [BLOCKING] packages/rs-drive/src/drive/votes/readiness/fund/add_readiness_fund_operations/v0/mod.rs:77-82: Readiness fund creation underestimates its storage fee
  The stateless estimation read returns no previous balance, so this insertion contains the requested amount. The pinned GroveDB estimator charges non-tree insertions by serialized size, while execution charges SumItems at their fixed accounting size. An independent probe through CreateNewReadinessFund reproduced 5,886,000 estimated storage credits versus 6,102,000 applied for a one-credit fund. Creating 16 distinct one-credit funds also underestimated the combined fee: 98,360,640 estimated versus 99,443,680 applied. This is a defect in the new funding API even though larger opening estimates can mask it. Use the widest SumItem representation only during estimation, as the pool-credit helper already does, and test standalone and batched fund creation against execution. Correcting the entire GroveDB estimator is not required to repair this new caller.

In `packages/rs-drive/src/drive/votes/readiness/activate_readiness_round_operations/v0/mod.rs`:
- [BLOCKING] packages/rs-drive/src/drive/votes/readiness/activate_readiness_round_operations/v0/mod.rs:41-50: Implement a stateless estimation path for round activation
  Some(layer_info) populates estimation metadata but still calls fetch_readiness_round_operations, whose pointer and record reads are stateful. On an initialized database without the round, the documented estimate-instead-of-read-state mode returns CorruptedDriveState rather than operations. There is also a missing settlement operation: empty_readiness_fund_operations returns zero during estimation, so the refund > 0 guard later in this function omits the payer balance credit. A funded crossed-round probe emitted six estimated mutations versus seven applied mutations, with the refund credit missing from the estimate. Add an explicit estimation branch with stateless pointer/record reads and a crossed-round placeholder, and always price the possible payer refund in that branch, following cancellation. Cover both estimation without stored state and funded activation.

In `packages/rs-drive/src/drive/votes/readiness/cleanup_retired_readiness_round_operations/v0/mod.rs`:
- [BLOCKING] packages/rs-drive/src/drive/votes/readiness/cleanup_retired_readiness_round_operations/v0/mod.rs:39-58: Cleanup fee estimation omits the bounded report-query costs
  The estimate emits report deletions and the fixed cleanup tail, but execution first performs a bounded path query and builds stateful deletions for its results. batch_delete_items_in_path_query records both the query cost and deletion-construction costs; neither appears in this estimation branch. An independent 512-report cleanup probe reproduced 4,775,120 estimated processing credits versus 57,483,160 applied. This contradicts the comment that the estimate covers the worst applied step and makes the new cleanup API unsuitable for sizing its cleanup reserve or processing budget. Price the bounded query and deletion-construction work, plus the remaining fixed reads, or include a demonstrated conservative allowance. Add estimate-versus-execution coverage at the maximum cleanup-step size.

In `packages/rs-dpp/src/voting/readiness/scan_cursor/mod.rs`:
- [SUGGESTION] packages/rs-dpp/src/voting/readiness/scan_cursor/mod.rs:126-138: Preserve the scan cursor when advancement returns an overflow error
  advance changes the pagination key before checking the first addition, then commits each counter before checking the next. An independent probe confirmed that advancing after examined_so_far reaches u32::MAX returns Overflow but changes next_pro_tx_hash. An overflow in a later counter can also leave earlier counters updated. A caller retaining the cursor after handling the error therefore has a position and counts describing different progress. Compute all checked totals before assigning any fields, and extend the overflow tests to compare the entire cursor before and after failure.

In `packages/rs-drive/src/drive/votes/readiness/record_readiness_crossing_operations/v0/mod.rs`:
- [SUGGESTION] packages/rs-drive/src/drive/votes/readiness/record_readiness_crossing_operations/v0/mod.rs:40-42: Preserve the input round when crossing operation construction fails
  record_crossing changes the caller-owned round before the remaining fallible operation construction. A probe using an unsupported clear_scan_cursor version returned an error but left the round Crossed; retrying with the same object then fails the already-crossed guard even though no operations were applied. Estimation also performs this mutation, so estimating and subsequently executing with the same round object has the same retry problem. Build from a local candidate and publish it only after successful construction, with an explicit contract for whether estimation publishes that candidate. Test failure after the crossing calculation and estimate-then-execute behavior; GroveDB rollback cannot restore this borrowed in-memory value.

In `packages/rs-drive/src/drive/votes/readiness/estimation_costs/v0/mod.rs`:
- [SUGGESTION] packages/rs-drive/src/drive/votes/readiness/estimation_costs/v0/mod.rs:26-34: Use serialization bounds that cover readiness round and cursor representations
  The claimed rounded-up sizes do not cover the models' serialized representations. Using the actual platform serializer, a crossed round with accepted_at_ms = 1,800,000,000,000, acceptance/core height 100,000, and raw_count 4,096 occupied 207 bytes, exceeding 200. A cursor with a present pagination key and maximal integer widths occupied 59 bytes, exceeding 56. These constants feed stateless reads and deletion estimates, so small-integer fixtures do not validate their assumptions. Increase the bounds and add serialization-size assertions covering crossed status, the optional evaluation/key fields, and integer-width boundaries. The individual size assumptions are confirmed wrong; this observation alone does not demonstrate an aggregate fee shortfall.

In `packages/rs-drive/src/drive/votes/readiness/open_readiness_round_operations/v0/mod.rs`:
- [SUGGESTION] packages/rs-drive/src/drive/votes/readiness/open_readiness_round_operations/v0/mod.rs:95-103: Reject re-opening the same round ID before retirement
  The previous round is retired without checking whether its ID equals the newly derived ID. Two openings with the same network, contract, version, digest, and acceptance height therefore retire and recreate the same storage key. With the shipped consistency-check setting, an independent probe successfully reopened that ID, reset its report count from one to zero, and left the current round in the retired queue. Cleanup then deleted the current record, leaving a dangling pointer and CorruptedDriveState on fetch. Reject an opening whose ID matches the current round, or implement explicit idempotent behavior that does not retire it. This can be fixed without changing the documented ID formula. No production opener is wired in this part, so this is a nonblocking defect in the exposed storage API.

In `packages/rs-drive/src/drive/votes/readiness/retire_readiness_round_operations/v0/mod.rs`:
- [SUGGESTION] packages/rs-drive/src/drive/votes/readiness/retire_readiness_round_operations/v0/mod.rs:217-225: Give the public pool-credit helper a versioned entry point
  add_readiness_pool_credit_operation is a public Drive method implemented directly inside v0. Its platform_version argument selects the underlying Grove read, but does not select or gate this helper's arithmetic and estimation behavior. The current production caller is protected by retire_round dispatch, so this is not a historical-replay defect or a blocking dependency-layer violation today. Keep the helper private to that versioned implementation, or expose it through a versioned dispatcher before independent callers use it. If shared pool-credit estimation is moved to the credit-pool owner, add that support in a new version rather than changing shipped behavior.

In `packages/rs-drive/src/drive/votes/readiness/retire_readiness_round_operations/mod.rs`:
- [SUGGESTION] packages/rs-drive/src/drive/votes/readiness/retire_readiness_round_operations/mod.rs:32-33: Reject unsupported multi-retirement batches before applying them
  The one-retirement-per-batch requirement is documented, but the newly exposed batchable OpenRound and CancelRound operations do not enforce it. apply_drive_operations converts the complete vector before applying writes, so two cancellations read the same initial epoch pool and emit competing absolute rewrites. An independent probe with two different contracts and payers, using the shipped disabled consistency-check setting, emptied both funds but credited only one 1,000,000-credit reserve instead of 2,000,000. The current production code does not construct such batches, and the PR explicitly adopts this scheduling restriction, so this is not a blocker for the existing caller. Preserve that design by rejecting multiple readiness pool credits at the batch boundary, or accumulate them if multi-retirement batches are to be supported. Test the supported behavior under the shipped configuration.

Comment thread packages/rs-dpp/src/voting/readiness/scan_cursor/mod.rs Outdated
Comment thread packages/rs-drive/src/drive/votes/readiness/estimation_costs/v0/mod.rs Outdated
@DCG-Claude

Copy link
Copy Markdown
Collaborator Author

Thanks for the review. All nine findings are addressed in 5546f46..b43ed4a, and each fix has a test that failed before it (the record-size test instead asserts the exact serialized sizes). The replies on each thread give the details and measurements.

@thepastaclaw please re-review.


🤖 Posted autonomously by DashVM (Claude) on behalf of pasta.

DCG-Claude and others added 24 commits September 30, 2026 02:37
Adds the placeholder protocol versions 15 and 16 and protocol version 17 as
struct updates, DRIVE_VERSION_V10 with the readiness method group of
DRIVE_VOTE_METHOD_VERSIONS_V4, DRIVE_ABCI_METHOD_VERSIONS_V11 selecting
generation 3 of the protocol change hook, VOTING_VERSION_V3 with the three
readiness structure versions and SYSTEM_LIMITS_V5 with the additional wait
bounds. The three readiness verifiers are generation 0 in every verify
table, so protocol 17 keeps the verify table of DRIVE_VERSION_V9. Shipped
tables are backfilled with None and 0.

Refs #4684

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d payer models

Versioned structures Drive stores for compilation readiness: the round with
its derived round id and fund id, status, last evaluation mark and deadline
arithmetic (wait clamped between the table bounds), the accepted report
record, the paged scan cursor bound to one membership view, and the typed
payer of the round's fund.

Refs #4684

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s and funds

Storage under [Votes] / r (rounds keyed under their contract with a current
round pointer, a count tree of reports per round, the scan cursor, the
deadline queue, the evaluation cursor and the retired-round cleanup queue)
and readiness funds under [PreFundedSpecializedBalances] / 129, created at
genesis by vote setup generation 1. Retiring a round never opens its reports
tree: the pointer is unlinked, the round is queued and drained by a bounded
cleanup step; the payer's balance is written once per batch. Typed
operations, three verifiers with their provers.

Refs #4684

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Genesis shape at 14 and latest, funds on their own tree with the reserve
rule and conservation, rounds (open, insert once, prune, cursors, crossing,
deadlines, proofs), retirement (replacement with a netted payer settlement,
cancellation, activation, rollback) and the bounded cleanup drain. Fixes
found by them: the merged round proof carries no limits, the retirement
estimate prices a crossed round.

Refs #4684

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ade to protocol 17

Generation 3 of the protocol change hook runs generation 2 (which empties
the contract cache and then runs generation 1) and then inserts the
readiness subtree and the readiness fund tree if absent through the same
helper genesis uses, with tests that a node born at 17 and a node upgraded
from 16 hold byte-identical elements, that the transition is idempotent and
replayable, that the dispatcher runs it once, and that the fund flows
conserve credits on both databases. Book chapter on the readiness storage.

Refs #4684

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs #4684

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Opening a replacement round netted the retired fund's refund against the
new round's funding on the new payer's balance, whoever that was. When
another identity opened the replacement, it was debited only the cleanup
reserve while the identity that funded the retired round got nothing back.

Net the two only when the same payer funds both rounds. Otherwise debit
the new payer the full funding and credit the retired round's payer its
own refund. The estimate prices the two-payer settlement, the larger of
the two.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Retiring a crossed round deleted its entry under the per-time deadline
tree but kept the time tree. The due query walks the time keys in order
under a limit, and an empty time tree spends that limit without yielding
an entry, so enough emptied trees ahead of a live deadline hid it from
every block's scan.

Delete the entry with a walk up that stops at the deadlines tree: the
time tree goes when the retired round was its last entry and stays when
another round shares the time. The estimate prices both levels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An estimated cancellation returned before building any operation, so the
generic batch estimator priced a funded round's cancellation at zero.

In estimation mode the cancellation now prices the pointer and record
reads and cancels a placeholder crossed round: the retired queue insert,
the deadline entry and its time tree, the fund read and delete, the
pointer delete and the payer refund. Retirement also prices the cleanup
reserve's credit to the epoch pool when estimating, with a stateless read
of the pool item and layer information for the pools and epoch trees,
since an estimate reads no fund to cap the charge at.

Tests run a funded pending and a funded crossed cancellation through
apply_drive_operations as an estimate and applied, and check the
estimate covers both fees.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The compilation readiness chapter said a replacement always nets the
refund into one balance write and that retired deadline entries leave
their time tree behind. Describe the per-payer settlement, the removal of
an emptied time tree and why it matters to the due query, and the shape
opening and cancellation estimates price.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…um item

The average-case estimator bills a plain element insert by the serialized
size of the element in the operation, while the applied merk bills a sum
item at its fixed cost size. A cleanup reserve of one million credits
serializes to 7 bytes, so a funded cancellation was estimated 4 storage
bytes below what it cost. In estimation mode the pool write now carries
i64::MAX, whose serialized size equals the fixed sum item cost.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…m item

The estimator bills a plain insert by the element's serialized size while
the applied write of a sum item bills its fixed size, so a new fund of a
small balance was estimated below its write, alone and in a batch of
fund creations. In estimation mode the fund write is now priced as an
insert of i64::MAX, as the pool credit already is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An activation estimate read the pointer and record from state, so on a
database without the round it failed with a corrupted state error, and
since an estimated fund reads as empty it skipped the payer refund.
Estimation now prices stateless pointer and record reads and the
activation of a crossed, funded placeholder round, and always prices the
refund, as cancellation does. The placeholder and its reads move to a
shared helper used by both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A 512-report cleanup step was estimated at 4,775,120 processing credits
against 56,021,580 applied: the estimate priced neither the bounded
report query and delete construction reads nor the merk path walk of
each deleted key, which the batch estimate charges once per layer.
Estimation now prices two stateless report reads and one path walk per
report. That is an upper bound, about ten times the applied step since
the applied deletes share ancestors, and the drain test checks every
applied step against it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rflows

advance moved the position and the examined count before a later count
could overflow, so a failed advance left a cursor whose position and
counts described different progress. Every total is now checked before
any field moves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e built

The crossing was recorded on the caller's round before the fallible
record rewrite and cursor removal were built, and an estimate recorded
it too, so a failed or estimated crossing left the round crossed and a
retry with the same round hit the already-crossed guard. The crossing is
now built on a copy that replaces the caller's round only when applying
succeeds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A crossed and evaluated round with every integer at its widest varint
serializes to 223 bytes and a cursor holding its pagination key and
maximal counts to 59, above the 200 and 56 the estimates assumed. The
round and cursor sizes are now 224 and 64, and a test serializes the
widest round, report and cursor against them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An opening that derives the id of the contract's current round retired
that round and recreated it under the same key, resetting its report
count and queueing the live round for cleanup, which later deleted its
record under a live pointer. Opening such a round is now refused before
anything is retired.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The helper that credits readiness charges to the epoch's processing fee
pool was a public method inside the retirement v0 module, so drive-abci
reached a version-specific body directly. It now has its own module with
a dispatcher on a new credit_pool slot, None before protocol version 17
and generation 0 from it, and reports VersionNotActive earlier. Also
drops a re-export left unused by the activation estimate change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each round retirement credits its cleanup reserve to the epoch's
processing pool with an absolute rewrite computed from the value read
before the batch applies. Two openings or cancellations in one batch
therefore overwrote the first credit, emptying both funds while the
pool received one reserve. The batch paths now refuse such a batch
before converting any operation; one retirement per batch is the
supported schedule and credits every reserve.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oders

The base now splits platform deserialization into a trusted decoder for
state Drive reads back from GroveDB and an untrusted one for bytes from
outside the node, and the single PlatformDeserialize derive is gone. The
readiness round, report record and scan cursor derive both, like the
contested vote poll stored info: Drive fetches decode trusted, the round
and report verifiers decode proof bytes untrusted, and the round trip
tests check that both decoders agree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The base now checks the GroveDB state against a description of every
node. Describe the readiness subtree under [112, r] (contracts with the
current round pointer and their rounds holding the record, the reports
count tree and the scan cursor; the deadline queue; the evaluation
cursor; the retired round queue) and the readiness funds under [40, 129],
both from protocol version 17. A fixture reaches every new node: a
replaced round waiting for cleanup beside the current one with an open
walk, two rounds sharing a deadline, a cancelled round whose contract
lost its pointer, and the fairness cursor.

grovedb-structure.json is regenerated at the latest protocol version, 17,
which also moves every recorded origin from 14 to 17; the contract layer
test reads the latest version instead of pinning 14.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The base's verify table V3 already carries the readiness verifiers at
generation 0, so drive version 10 only changes the vote table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…neration 1

The base now selects apply_drive_operations generation 1 from protocol
version 14, and that generation did not run the check generation 0 gained
in this branch, so a batch retiring two readiness rounds reached GroveDB
and the second retirement overwrote the first pool credit.

Run the same check first, before the balance writes are merged. It only
refuses batches with two readiness retirements, which nothing can build
before protocol version 17, so shipped behaviour is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DCG-Claude and others added 2 commits September 30, 2026 08:00
The pruning estimate used the generic stateless batch delete, which
charges merk propagation once per layer while the applied delete walks
and rehashes the path of every deleted key: pruning 512 of 2,000
reports was estimated at 10,194,860 processing credits against
50,444,640 applied. Pruning now adds the same per-key walk the cleanup
step prices, through a helper both share, and a full-page test checks
the estimate against the applied cost.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Readiness fund writes and round settlements compute their new value from
the balance committed before the batch, so a second write of the same key
in one batch replaced the first: two adds kept one, two deductions both
passed the reserve, and an opening erased a debit of its payer batched
beside it. The batch guard now refuses a readiness fund written twice and
an opening or cancellation beside any identity balance or readiness fund
write, whether estimated, applied or converted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@DCG-Claude

Copy link
Copy Markdown
Collaborator Author

@thepastaclaw Both remaining findings are addressed; could you take another look?

  • The pruning estimate now prices each report's delete walk through the helper cleanup uses (0951e33).
  • The batch guard now refuses a readiness fund written twice, and a round settlement beside any other identity balance or fund write (9efc232).

Details are in the thread replies.


🤖 Posted autonomously by DashVM (Claude) on behalf of pasta.

@github-actions github-actions Bot removed the waiting-bots Waiting for the review bots to report on this head label Sep 30, 2026
@DCG-Claude
DCG-Claude changed the base branch from v6.0-dev to v5.0-dev September 30, 2026 15:31
@github-actions github-actions Bot modified the milestones: v6.0.0, v5.0.0 Sep 30, 2026
@github-actions github-actions Bot added waiting-bots Waiting for the review bots to report on this head bot-review-skipped A required review bot did not report; it was skipped by the window or by a person. labels Sep 30, 2026
@DCG-Claude
DCG-Claude changed the base branch from v5.0-dev to v6.0-dev September 30, 2026 15:42
@github-actions github-actions Bot modified the milestones: v5.0.0, v6.0.0 Sep 30, 2026
@github-actions github-actions Bot removed bot-review-skipped A required review bot did not report; it was skipped by the window or by a person. waiting-bots Waiting for the review bots to report on this head labels Sep 30, 2026

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — Final validation — Phase 1 + Phase 2

The exact head preserves the readiness versioning and storage invariants, and all 11 prior findings are fixed with targeted regressions. I found seven remaining nonblocking correctness and API-contract issues involving round-ID reuse after cancellation, activation settlement and deadline enforcement, input validation, model invariants, and migration-equivalence coverage.

🟡 7 suggestion(s)

Review provenance

Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: muse-spark-1.3-contributor (agent: phase1-reviewer, role: architecture-layering); reviewer 3: muse-spark-1.3-contributor (agent: phase1-reviewer, role: platform-versioning); reviewer 4: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 5: muse-spark-1.3-contributor (agent: phase1-reviewer, role: security-auditor); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 8: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 9: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 10: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 11: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 12: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 13: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 14: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 15: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — This large, intricate diff directly changes funds movement in retire_readiness_round_operations/v0/mod.rs through reserve charging and payer refunds, and persistent storage migration in perform_events_on_first_block_of_protocol_change/v3/mod.rs through readiness-tree initialization on protocol upgrade.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — architecture-layering (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — platform-versioning (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — security-auditor (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 13% left, 5h 100% left), glm-5.3-flash (not used above high effort; tier asks max)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-drive/src/drive/votes/readiness/open_readiness_round_operations/v0/mod.rs`:
- [SUGGESTION] packages/rs-drive/src/drive/votes/readiness/open_readiness_round_operations/v0/mod.rs:90-159: Reject reopening a round ID that is still awaiting cleanup
  The new guard rejects only an opening whose ID matches the contract's current round. Cancellation removes the current pointer but retains the old round subtree and its retired-round queue entry. Reopening the same opening after cancellation therefore passes the guard, recreates the existing round key, and leaves that live key in the cleanup queue. A later cleanup can delete the newly opened round and leave the current pointer dangling. Reject an opening when its derived ID already exists as a retired round or round subtree, and add a cancel-then-reopen regression.

In `packages/rs-drive/src/drive/votes/readiness/activate_readiness_round_operations/v0/mod.rs`:
- [SUGGESTION] packages/rs-drive/src/drive/votes/readiness/activate_readiness_round_operations/v0/mod.rs:99-108: Route activation refunds through the debt-aware executor
  Activation credits the payer through add_to_identity_balance_operations, which can emit LowLevelDriveOperation::RepaidIdentityDebt. The shared apply_drive_operations_v1 path removes that marker and credits the epoch processing pool, but activation is not represented by ReadinessOperationType and is exposed only as a low-level operation builder. Applying the returned operations through apply_batch_low_level_drive_operations or converting them to a plain GroveDB batch rejects the marker instead of routing the repaid debt. A payer who becomes indebted before activation can therefore make this new activation API fail. Add an activation DriveOperation handled by the shared executor, or expose an atomic Drive activation method that performs the debt routing.
- [SUGGESTION] packages/rs-drive/src/drive/votes/readiness/activate_readiness_round_operations/v0/mod.rs:50-55: Reject activation before the committed deadline
  The stateful activation path verifies that the round is current and crossed, but it never checks block_info.time_ms against the persisted deadline_ms. A caller can activate immediately after crossing, deleting the pointer and settling the fund before the committed deadline. Although the block event is expected to query due entries first, this public Drive method documents activation at the deadline and should enforce that precondition itself before constructing retirement operations.

In `packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v3/mod.rs`:
- [SUGGESTION] packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v3/mod.rs:205-249: Compare complete readiness subtrees across genesis and upgrade paths
  The genesis path applies the shared readiness structure operations as one GroveDB batch, while the upgrade path applies the same operations sequentially with insert-if-not-exists. The equivalence test compares immediate elements and the immediate child list, but does not recursively compare the [Votes] / r and [PreFundedSpecializedBalances] / 129 subtree contents or authenticated roots. It can therefore miss a nested shape or root divergence caused by insertion behavior. Compare both complete subtrees recursively or assert their complete subtree roots.

In `packages/rs-drive/src/drive/votes/readiness/fund/add_readiness_fund_operations/v0/mod.rs`:
- [SUGGESTION] packages/rs-drive/src/drive/votes/readiness/fund/add_readiness_fund_operations/v0/mod.rs:33-67: Validate impossible fund amounts in estimation mode
  The stateless estimation branch returns before checking amount. An amount of MAX_CREDITS or greater therefore produces a successful estimate even though the applied path always rejects it at the new_total >= MAX_CREDITS check, even when the existing fund is empty. Validate this unconditional amount bound before entering the stateless estimation branch so estimation and execution reject the same impossible request.

In `packages/rs-dpp/src/voting/readiness/scan_cursor/mod.rs`:
- [SUGGESTION] packages/rs-dpp/src/voting/readiness/scan_cursor/mod.rs:136-151: Validate page classification counts before persisting the cursor
  advance checks arithmetic overflow but accepts eligible + pruned > examined. The resulting durable cursor can claim more classified reports than the page examined, allowing accumulated eligibility and pruning counters to describe an impossible scan. Since the method's contract says eligible and pruned are classifications of the examined page, reject semantically inconsistent counts before assigning the cursor fields.

In `packages/rs-dpp/src/voting/readiness/round/mod.rs`:
- [SUGGESTION] packages/rs-dpp/src/voting/readiness/round/mod.rs:245-255: Make the round model reject a second crossing
  ReadinessRound::record_crossing unconditionally replaces the status and deadline after checking only the wait bounds. The Drive operation wrapper separately rejects non-pending rounds, but callers of this public DPP model method can directly cross an already crossed round and produce a new deadline. Reject non-pending rounds inside record_crossing so the serialized model enforces its own lifecycle invariant rather than relying on every outer caller to remember the guard.

Comment thread packages/rs-dpp/src/voting/readiness/scan_cursor/mod.rs
Comment thread packages/rs-dpp/src/voting/readiness/round/mod.rs
DCG-Claude and others added 3 commits September 30, 2026 11:55
…bounded fund estimates

Activation now requires the block to have reached the committed deadline and is
applied through a new ActivateRound readiness operation, so a refund that repays
identity debt is routed to the processing pool instead of being rejected by the
plain low-level apply. Opening a round whose id still has a tree (the current
round or a retired one awaiting cleanup) is refused, and funding an amount no
balance can hold is refused in estimation as it is in execution.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
record_crossing refuses a round that already crossed, keeping the first deadline,
and a scan cursor refuses a page that classifies more reports as eligible or
pruned than it examined. The widest-cursor estimation placeholder uses split
counts that satisfy the new bound at the same encoded size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s genesis and upgrade

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — Final validation — Phase 1 + Phase 2

At head a98d151, all 18 prior findings are fixed, including the fee-estimation blockers. Five nonblocking findings remain concerning raw batch adapters, public result-type accessibility, fund decoding, cursor progression and replay-safety documentation. Independent validation passed 12 DPP readiness tests, 42 Drive readiness tests, 33 protocol-upgrade tests, 25 platform-version tests and verify-only compilation; temporary probes were removed and the tracked working tree is clean.

🟡 5 suggestion(s)

Review provenance

Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: muse-spark-1.3-contributor (agent: phase1-reviewer, role: architecture-layering); reviewer 3: muse-spark-1.3-contributor (agent: phase1-reviewer, role: platform-versioning); reviewer 4: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 5: muse-spark-1.3-contributor (agent: phase1-reviewer, role: security-auditor); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 8: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 9: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 10: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 11: gpt-6-astra (agent: phase2-reviewer, role: general); reviewer 12: gpt-6-astra (agent: phase2-reviewer, role: architecture-layering); reviewer 13: gpt-6-astra (agent: phase2-reviewer, role: platform-versioning); reviewer 14: gpt-6-astra (agent: phase2-reviewer, role: rust-quality); reviewer 15: gpt-6-astra (agent: phase2-reviewer, role: security-auditor); reviewer 16: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 17: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 18: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 19: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 20: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — This large, intricate diff directly changes funds movement in retire_readiness_round_operations/v0/mod.rs and the readiness fund deduction methods, and introduces persistent storage migration in perform_events_on_first_block_of_protocol_change/v3/mod.rs, meeting both critical-tier requirements.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — architecture-layering (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — platform-versioning (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — security-auditor (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 13% left, 5h 100% left), glm-5.3-flash (not used above high effort; tier asks max)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — general (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — security-auditor (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
  • Model comparison: every Phase-2 reviewer also ran on gpt-6-astra; the verifier weighed both sets without knowing which model wrote which
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-drive/src/util/batch/drive_op_batch/readiness.rs`:
- [SUGGESTION] packages/rs-drive/src/util/batch/drive_op_batch/readiness.rs:130-138: Enforce readiness conflicts across raw-operation adapters
  The guard rejects conflicting typed balance operations, but `GroveDBOperation` and `GroveDBOpBatch` reach the wildcard arm and are subsequently flattened into the same applied batch. Consequently, a legitimate balance operation bypasses the settlement restriction when exposed through a raw adapter. I reproduced this with consistency verification explicitly disabled: a 3,000,000-credit debit built by `remove_from_identity_balance_operations`, batched after a 20,000,000-credit `OpenRound`, succeeds through either raw wrapper. The round fund receives 20,000,000 credits, but its initially 100,000,000-credit payer retains 97,000,000 rather than 77,000,000. Inspect qualified raw mutations for readiness-protected balance, fund and processing-pool paths, or conservatively reject raw mutations alongside readiness settlements and fund writes. Add regressions for both wrappers with consistency verification disabled. This remains nonblocking because no production readiness caller constructs these mixed batches in this part.

In `packages/rs-drive/src/verify/voting/mod.rs`:
- [SUGGESTION] packages/rs-drive/src/verify/voting/mod.rs:8: Re-export the public readiness proof result type
  `Drive::verify_readiness_round` publicly returns `Option<VerifiedReadinessRound>`, but the struct's containing module is private and there is no public re-export. External consumers can inspect an inferred result but cannot name its type in a cache field, wrapper signature or SDK associated type. An independent external-crate compilation probe confirms E0603 when importing the result type. Re-export the struct while keeping the implementation module private, and add an external-consumer compile test for the exported path.

In `packages/rs-drive/src/drive/votes/readiness/fund/empty_readiness_fund_operations/v0/mod.rs`:
- [SUGGESTION] packages/rs-drive/src/drive/votes/readiness/fund/empty_readiness_fund_operations/v0/mod.rs:48-55: Validate stored fund elements before interpreting them as credits
  The stateful empty and deduction paths use the generic u64 decoder, which casts signed `SumItem` values without checking their sign and also accepts `Item` and `SumTree` elements. This contradicts the readiness fetcher's requirement that a fund be a nonnegative `SumItem`. I reproduced the inconsistency by storing `SumItem(-1)`: fetching rejects it, emptying returns `u64::MAX` credits, and deducting one credit succeeds and writes `SumItem(-2)`. Use the cost-tracked readiness-specific checked read for stateful fund mutations while retaining the existing estimation placeholders, and test negative and wrong-element-type storage. This is nonblocking corruption handling, not a demonstrated transaction exploit: normal readiness writers do not create these invalid elements, and the shared legacy decoder does not need to change.

In `packages/rs-dpp/src/voting/readiness/scan_cursor/mod.rs`:
- [SUGGESTION] packages/rs-dpp/src/voting/readiness/scan_cursor/mod.rs:145-147: Reject scan pages that repeat or rewind the pagination key
  `advance` validates classifications and overflow, but accepts a last report key equal to or lower than the previous pagination key while accumulating the page counters. An independent probe advanced to `[2; 32]` and then `[1; 32]`; both calls succeeded, leaving two eligible reports counted and the position moved backward. Since report pagination resumes strictly after that position, a repeated or out-of-order page can make previously examined reports count again. Require a strictly increasing key before changing any fields, and test equal and decreasing keys with the complete cursor unchanged on rejection. Restarting the membership walk should use a fresh cursor. This is nonblocking because the paged block event is not wired into production in this part.

In `packages/rs-drive/src/util/batch/drive_op_batch/drive_methods/apply_drive_operations/v1/mod.rs`:
- [SUGGESTION] packages/rs-drive/src/util/batch/drive_op_batch/drive_methods/apply_drive_operations/v1/mod.rs:81: Document the replay-safety exception for the in-place batch edits
  This addition edits an existing generation selected from protocol 14; equivalent additions edit `apply_drive_operations/v0` and `convert_drive_operations_to_grove_operations/v0`. The guard is inert for historical consensus operation sets because it only rejects combinations containing newly introduced readiness variants, so a new execution generation is not necessary for this change. However, `book/src/contributing/coding-conventions.md` requires an explanatory comment at each edited location and an "In-place changes to shipped generations" section listing the affected generations, selecting protocol versions and safety argument. Although the PR discusses historical safety elsewhere, those required annotations and section are absent. Record the exception in both places so subsequent changes to this shared guard can be audited against the replay boundary.
Out-of-scope follow-up suggestions (1)

These are valid observations, but they are outside this PR's scope and should be handled in separate issues or author/maintainer-requested PRs rather than blocking this review.

  • Align versioned SumItem insertion estimation in GroveDB — At the pinned GroveDB revision dce8252f8665bf06c7bd788e739efba354141690, the average-case insertion estimator prices non-tree elements using serialized_size rather than the specialized SumItem accounting used by execution. This concrete shared fee-estimation discrepancy merits separate tracking, but it predates this PR and the new readiness callers now compensate conservatively; it is not a remaining readiness blocker.
    • Follow-up: Track a separate maintainer-authorized GroveDB change adding a corrected estimator generation and direct small-value and multi-item estimate-versus-execution tests. Adopt it through an unreleased Platform version without changing historical fee behavior.

Comment thread packages/rs-drive/src/util/batch/drive_op_batch/readiness.rs
Comment thread packages/rs-drive/src/verify/voting/mod.rs
Comment thread packages/rs-dpp/src/voting/readiness/scan_cursor/mod.rs
DCG-Claude and others added 2 commits September 30, 2026 14:39
…d check stored fund elements

The readiness batch guard now refuses raw GroveDB operations and op
batches that share a batch with a round settlement or a readiness fund
write, since Drive cannot tell which balances a raw write touches.
Adding to, deducting from and emptying a readiness fund read the stored
element through the checked readiness read, so a negative sum item or a
mistyped element is reported as corrupted instead of decoded. The
verified round type is re-exported for proof consumers, and the in-place
guard calls in shipped batch generations carry a comment with the reason
they leave replayed batches unchanged, pinned by a protocol version 13
test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ DEGRADED — Re-review — Final validation — Phase 1 + Phase 2

⚠️ DEGRADED review. The primary review models were unavailable (gpt-6.1-sol unavailable: 503 auth_unavailable: no auth available (providers=codex, model=gpt-6.1-sol; last upstream error: usage_limit_reached: T), so this review ran on stand-in models: gpt-5.6-luna → muse-spark-1.3-contributor, gpt-5.6-sol → muse-spark-1.3-contributor, gpt-5.6-terra → muse-spark-1.3-contributor, gpt-6-astra → muse-spark-1.3-contributor, gpt-6.1-sol → muse-spark-1.3-contributor. Both review phases and the independent verifiers still ran, but on weaker models, with Phase 1 capped at high effort. Treat the verdict as provisional; a full-strength re-review will run on the next push once the primary models are back.

Part 1 readiness storage is correctly gated to unreleased protocol 17 with versioned dispatch, genesis/upgrade parity, and bounded estimation. I independently rechecked the new models, batch guards, fund handling, verifiers, and cleanup accounting at the head tree and found no remaining in-scope defect; all 23 prior findings are fixed.

🔴 0 blocking | 🟡 0 suggestion(s) | 💬 0 nitpick(s)

Review provenance

Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: muse-spark-1.3-contributor (agent: phase1-reviewer, role: architecture-layering); reviewer 3: muse-spark-1.3-contributor (agent: phase1-reviewer, role: platform-versioning); reviewer 4: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 5: muse-spark-1.3-contributor (agent: phase1-reviewer, role: security-auditor); reviewer 6: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 8: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 9: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 10: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 11: muse-spark-1.3-contributor (standing in for gpt-6.1-sol) (agent: phase2-reviewer, role: general); reviewer 12: muse-spark-1.3-contributor (standing in for gpt-6.1-sol) (agent: phase2-reviewer, role: architecture-layering); reviewer 13: muse-spark-1.3-contributor (standing in for gpt-6.1-sol) (agent: phase2-reviewer, role: platform-versioning); reviewer 14: muse-spark-1.3-contributor (standing in for gpt-6.1-sol) (agent: phase2-reviewer, role: rust-quality); reviewer 15: muse-spark-1.3-contributor (standing in for gpt-6.1-sol) (agent: phase2-reviewer, role: security-auditor); final verifier: muse-spark-1.3-contributor (standing in for gpt-6.1-sol) (agent: sol-verifier, role: final-verifier)

  • Degraded mode: gpt-6.1-sol unavailable: 503 auth_unavailable: no auth available (providers=codex, model=gpt-6.1-sol; last upstream error: usage_limit_reached: T (detected by lane, since 2026-09-30T21:26:04Z); stand-ins gpt-5.6-luna → muse-spark-1.3-contributor, gpt-5.6-sol → muse-spark-1.3-contributor, gpt-5.6-terra → muse-spark-1.3-contributor, gpt-6-astra → muse-spark-1.3-contributor, gpt-6.1-sol → muse-spark-1.3-contributor; Phase 1 effort capped at high
  • Triage: critical by muse-spark-1.3-contributor (standing in for gpt-6.1-sol) (effort low) — This large, intricate diff directly changes funds movement in open_readiness_round_operations and retire_readiness_round_operations, introduces readiness storage migration in perform_events_on_first_block_of_protocol_change/v3/mod.rs, and adds protocol-versioned activation and crossing rules.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — architecture-layering (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — platform-versioning (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — security-auditor (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 13% left, 5h 100% left), glm-5.3-flash (not used above high effort; tier asks max)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: muse-spark-1.3-contributor (standing in for gpt-6.1-sol) — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer, muse-spark-1.3-contributor (standing in for gpt-6.1-sol) — general (completed, effort xhigh); agent phase2-reviewer, muse-spark-1.3-contributor (standing in for gpt-6.1-sol) — architecture-layering (completed, effort xhigh); agent phase2-reviewer, muse-spark-1.3-contributor (standing in for gpt-6.1-sol) — platform-versioning (completed, effort xhigh); agent phase2-reviewer, muse-spark-1.3-contributor (standing in for gpt-6.1-sol) — rust-quality (completed, effort xhigh); agent phase2-reviewer, muse-spark-1.3-contributor (standing in for gpt-6.1-sol) — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify the current code and confirm that no unresolved issues remain.

No unresolved findings remain from the prior review on this head.

@github-actions github-actions Bot added the waiting-bots Waiting for the review bots to report on this head label Oct 2, 2026
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai review

No review for 793d751f yet, so PR Hygiene is asking once. If nothing arrives, the requirement is dropped for this commit and the pull request is labelled bot-review-skipped.

@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Ready for review — files with no dedicated owner: QuantumExplorer or shumkov · dpp: QuantumExplorer or shumkov · rs-drive-abci: QuantumExplorer or shumkov · rs-drive: QuantumExplorer or shumkov.
Full checklist in the description.

@github-actions github-actions Bot added ready-for-human Bots have reported, the author has self-reviewed, and the build is green: this needs a human. bot-review-skipped A required review bot did not report; it was skipped by the window or by a person. and removed waiting-bots Waiting for the review bots to report on this head labels Oct 4, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot-review-skipped A required review bot did not report; it was skipped by the window or by a person. ready-for-human Bots have reported, the author has self-reviewed, and the build is green: this needs a human.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants