feat(platform)!: add compilation readiness rounds, reports and funds to drive - #5211
DCG-Claude wants to merge 32 commits into
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository: dashpay/platform/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
|
|
thepastaclaw
left a comment
There was a problem hiding this comment.
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:
criticalbygpt-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); agentphase1-reviewer,muse-spark-1.3-contributor— architecture-layering (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— platform-versioning (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— rust-quality (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— security-auditor (completed, effort xhigh); agentphase1-reviewer - Phase 1 model:
muse-spark-1.3-contributor— not quota-gated; passed overgemini-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; agentsol-verifier - Phase 2 reviewers:
gpt-6.1-sol— general (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— architecture-layering (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— platform-versioning (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— rust-quality (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— security-auditor (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— general (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— architecture-layering (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— platform-versioning (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— rust-quality (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— security-auditor (completed, effort xhigh); agentphase2-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.
|
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. |
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>
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>
|
@thepastaclaw Both remaining findings are addressed; could you take another look?
Details are in the thread replies. 🤖 Posted autonomously by DashVM (Claude) on behalf of pasta. |
thepastaclaw
left a comment
There was a problem hiding this comment.
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:
criticalbygpt-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); agentphase1-reviewer,muse-spark-1.3-contributor— architecture-layering (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— platform-versioning (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— rust-quality (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— security-auditor (completed, effort xhigh); agentphase1-reviewer - Phase 1 model:
muse-spark-1.3-contributor— not quota-gated; passed overgemini-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; agentsol-verifier - Phase 2 reviewers:
gpt-6.1-sol— general (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— architecture-layering (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— platform-versioning (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— rust-quality (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— security-auditor (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— general (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— architecture-layering (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— platform-versioning (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— rust-quality (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— security-auditor (completed, effort xhigh); agentphase2-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.
…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
left a comment
There was a problem hiding this comment.
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:
criticalbygpt-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); agentphase1-reviewer,muse-spark-1.3-contributor— architecture-layering (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— platform-versioning (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— rust-quality (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— security-auditor (completed, effort xhigh); agentphase1-reviewer - Phase 1 model:
muse-spark-1.3-contributor— not quota-gated; passed overgemini-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; agentsol-verifier - Phase 2 reviewers:
gpt-6.1-sol— general (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— architecture-layering (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— platform-versioning (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— rust-quality (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— security-auditor (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— general (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— architecture-layering (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— platform-versioning (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— rust-quality (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— security-auditor (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— general (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— architecture-layering (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— platform-versioning (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— rust-quality (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— security-auditor (completed, effort xhigh); agentphase2-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.
…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
left a comment
There was a problem hiding this comment.
⚠️ DEGRADED — Re-review — Final validation — Phase 1 + Phase 2
⚠️ DEGRADED review. The primary review models were unavailable (gpt-6.1-solunavailable: 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 athigheffort. 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-solunavailable: 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-insgpt-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 athigh - Triage:
criticalbymuse-spark-1.3-contributor(standing in forgpt-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); agentphase1-reviewer,muse-spark-1.3-contributor— architecture-layering (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— platform-versioning (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— rust-quality (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— security-auditor (completed, effort xhigh); agentphase1-reviewer - Phase 1 model:
muse-spark-1.3-contributor— not quota-gated; passed overgemini-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 forgpt-6.1-sol) — final-verifier; agentsol-verifier - Phase 2 reviewers:
gpt-6.1-sol— general (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— architecture-layering (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— platform-versioning (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— rust-quality (completed, effort xhigh); agentphase2-reviewer,gpt-6.1-sol— security-auditor (completed, effort xhigh); agentphase2-reviewer,muse-spark-1.3-contributor(standing in forgpt-6.1-sol) — general (completed, effort xhigh); agentphase2-reviewer,muse-spark-1.3-contributor(standing in forgpt-6.1-sol) — architecture-layering (completed, effort xhigh); agentphase2-reviewer,muse-spark-1.3-contributor(standing in forgpt-6.1-sol) — platform-versioning (completed, effort xhigh); agentphase2-reviewer,muse-spark-1.3-contributor(standing in forgpt-6.1-sol) — rust-quality (completed, effort xhigh); agentphase2-reviewer,muse-spark-1.3-contributor(standing in forgpt-6.1-sol) — security-auditor (completed, effort xhigh); agentphase2-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.
|
@coderabbitai review No review for |
|
Ready for review — files with no dedicated owner: QuantumExplorer or shumkov · |
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 modelsReadinessRound(round id derived from network magic, contract, version, bundle digest and acceptance height;PendingorCrossedstatus with the deadline arithmeticcrossing + clamp(crossing - accepted, min, max); the last evaluation markReadinessEvaluation { core_height, raw_count }; typedReadinessPayer),ReadinessReportRecord,ReadinessScanCursor(bound to one membership view).VOTING_VERSION_V3declares 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, aCountTreeof reports keyed by pro tx hash, the scan cursor, the deadline queue keyed byencode_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 sharedretire_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, typedReadinessOperationTypeand twoPrefundedSpecializedBalanceOperationTypevariants,DriveError::ReadinessRoundMissing. Vote setup generation 1 (add_initial_vote_tree_main_structure_operations_v1) creates both structures at genesis throughadd_readiness_structure_operations. Verifiersverify_readiness_round(its result typeVerifiedReadinessRoundis re-exported fromdrive::verify::voting),verify_readiness_reportandverify_readiness_fundunderpackages/rs-drive/src/verify/voting/compile with theverifyfeature alone; their provers share the queries indrive/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 asReadinessOperationTypeoperations throughapply_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.rsandv16.rs(byte-identical to the open 6.0 siblings),v17.rsas a struct update overPLATFORM_V16,LATEST_VERSION = 17;DRIVE_VERSION_V10(vote method table V3 with thereadinessgroup, verify method table V3),DRIVE_ABCI_METHOD_VERSIONS_V11(protocol change hook generation 3),SYSTEM_LIMITS_V5(readiness_additional_wait_min_ms120 s,readiness_additional_wait_max_ms3,600 s),VOTING_VERSION_V3. Shipped tables backfilled withNoneand0, including the mock inmocks/v2_test.rs.rs-drive-abci:
perform_events_on_first_block_of_protocol_changegeneration 3 runs the generation 2 events and thentransition_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 inbook/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-devtip, every command redirected to a file with the exit code checked, each exit code 0:New tests, all against
PlatformVersion::latest()unless the test is about the version boundary: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/votes/readiness/tests.rs): genesis at 14 has neither[112, r]nor[40, 129]and latest has both; the readiness methods reportVersionNotActiveat 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 theaftercursor, 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 throughapply_drive_operationscovering 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.packages/rs-drive/tests/readiness_proofs.rs): an absent round proved and verified from outside the crate intodrive::verify::voting::VerifiedReadinessRound.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] / rand[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,eat genesis anddafter the upgrade appendsr; 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_operationsv0, selected by protocol versions 1 to 13.convert_drive_operations_to_grove_operationsv0, selected by every protocol version.apply_drive_operationsv1, 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
ReadinessOperationor 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 reportVersionNotActivethere. 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_beforeconverts 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:
For repository code-owners and collaborators only
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.
runderVotes, inner keys0(contracts),1(deadlines),2(evaluation cursor),3(retired queue), pointer keyc, round tree keys0(record),1(reports),2(cursor). The records live underVotesrather 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.129beside the voting funds at128; fund idhash_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.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.SystemLimitsis a choice.apply_drive_operationsandconvert_drive_operations_to_grove_operationsrefuse a batch holding more than one readiness opening or cancellation (NotSupported). The pool credit helper is versioned on its owncredit_poolslot.ReadinessPayer::Identityby an identity balance credit; the contract-bucket owner arrives with the typed storage flags work.v5.0-dev, nowv6.0-dev) when this part was cut, so it shipsDRIVE_VERSION_V10,DRIVE_ABCI_METHOD_VERSIONS_V11,SYSTEM_LIMITS_V5and 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.test_prefix of every existing test in that module rather than theshouldprefix used elsewhere, for consistency within the file.Part 1 of 2 for R08-12.
Refs #4684
🤖 Generated with Claude Code
PR Hygiene ·
793d751book/src/SUMMARY.md,book/src/drive/compilation-readiness.md,book/src/versioning/platform-version.mdand 27 more) — QuantumExplorer or shumkovdpp(packages/rs-dpp/src/voting/mod.rs,packages/rs-dpp/src/voting/readiness/mod.rs,packages/rs-dpp/src/voting/readiness/payer.rsand 6 more) — QuantumExplorer or shumkovrs-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 shumkovrs-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.rsand 81 more) — QuantumExplorer or shumkovWhen every box is checked the
PR Hygienecheck passes and this can merge.