Skip to content

fix(platform): resolve fee versions by registered number and price refunds at the storage epoch rate - #4703

Open
DCG-Claude wants to merge 7 commits into
v4.3-devfrom
dashvm/r12-01
Open

DCG-Claude wants to merge 7 commits into
v4.3-devfrom
dashvm/r12-01

Conversation

@DCG-Claude

@DCG-Claude DCG-Claude commented Sep 12, 2026

Copy link
Copy Markdown
Collaborator

Issue being fixed or feature implemented

Part of the smart-contract plan in #4626 (task R12-01, fees workstream). The fee version registry and its consumers had four gaps that the plan's fee-version integrity work requires closed before new fee schedules can be introduced:

  • FeeVersion::get and FeeVersion::get_optional resolved a fee_version_number by array position (FEE_VERSIONS[n - 1]), not by the number the entry carries. That only works while the registry happens to be numbered contiguously, and nothing enforced it.
  • FeeVersion::as_static called expect on the lookup, and the saved-state loader (PlatformStateForSavingV1 into PlatformState) did the same, so a persisted state holding a fee version number this build does not know aborted the node at start instead of failing with a descriptive error.
  • FeeRefunds::from_storage_removal prices every removed byte at the storage rate active at the removal epoch. A refund is the unpaid remainder of the fee originally charged, and that fee was priced at the rate active when the bytes were written, so the rate must be resolved at the storage epoch through the fee history. The corrected rule is a new generation behind the calculate_fee method version, not an edit to the shipped one.
  • None of this had tests: the registry module, the epoch fee-history resolution and the refund path in Drive had no coverage, and the two saved-state fixture tests only asserted that decoding succeeds.

Refs #4675

What was done?

packages/rs-platform-version/src/version/fee/mod.rs

  • FeeVersion::get and get_optional resolve by matching fee_version_number in FEE_VERSIONS; zero and any unregistered number are PlatformVersionError::UnknownVersionError (same message text as before).
  • FeeVersion::as_static now returns Result<&'static FeeVersion, PlatformVersionError>.
  • Two compile-time assertions: the registry is non-empty (so first() and latest() cannot panic) and the entries are numbered 1, 2, ... in order (registry_numbers_are_contiguous_from_one, a const fn). A mis-numbered or duplicated entry no longer compiles.
  • Doc comment on FEE_VERSIONS defining a fee-history generation and when a schedule needs a new number.
  • Lookup by carried number is factored into find_registered, and with the mock-versions feature get_optional consults FEE_TEST_VERSIONS after the shipped registry.
  • Nine tests: every registered number resolves to the entry carrying it, find_registered against a registry whose numbers do not line up with positions (number 3 at position 0, number 1 at position 1), the mock generation resolves through get, get_optional and as_static although no registry position corresponds to its number, zero and unregistered numbers are rejected on every entry point, the numbering guard, every schedule referenced by PLATFORM_VERSIONS (and TEST_PLATFORM_V2 / TEST_PLATFORM_V3 under mock-versions) is registered and agrees with the registered generation on the storage, processing, hashing and signature groups, the number 1 storage rates are frozen with a replay rationale, and as_static on both sides.

packages/rs-platform-version/src/version/mocks/fee_test.rs (new, mock-versions feature only)

  • TEST_FEE_VERSION_DOUBLED_STORAGE_RATE: a fee-history generation with twice the shipped disk usage rate, numbered (1 << TEST_PROTOCOL_VERSION_SHIFT_BYTES) + 1 so it can never collide with a number a released network persisted and is never a position in the shipped registry. A compile-time assertion keeps every mock number above the shift. mock-versions is a dev-dependency feature of drive, drive-abci and strategy-tests; release builds never enable it.

packages/rs-dpp/src/fee/fee_result/refunds.rs

  • FeeRefunds::from_storage_removal (generation 0) is byte-identical to the shipped body; its doc comment now states it is frozen and selected by calculate_fee version 0.
  • New FeeRefunds::from_storage_removal_v1: the same function with the storage rate resolved with Epoch::new(epoch_index) (the storage epoch, the key of each removal entry) instead of Epoch::new(current_epoch_index). The dust filter, era arithmetic and error mapping are unchanged. Doc comment explains the rule and which method version selects it.
  • Tests with a synthetic number 2 generation (54000 credits per byte, not registered anywhere): the shipped generation keeps pricing both sides of a boundary at the current epoch's rate (frozen replay pin); generation 1 prices removals on both sides of a rate boundary at the rate their bytes were charged and uses the first generation before the earliest history entry; on every input reachable today (empty history, or a history where every entry is number 1) both generations produce identical refunds; the dust filter holds in both.

packages/rs-dpp/src/fee/default_costs/mod.rs (tests only)

  • Five tests for EpochCosts::active_fee_version: empty history resolves to the first registered generation (the genesis-epoch fallback the epoch-change hook relies on), exact epoch match, nearest lower entry, before the earliest entry, and a consumer-level check that for every PlatformVersion and every KnownCostItem variant the referenced schedule and the registered generation return the same cost.

packages/rs-drive/src/fees/calculate_fee/v1/mod.rs (new), calculate_fee/mod.rs and packages/rs-drive/src/fees/op.rs

  • Drive::calculate_fee_v1: a copy of calculate_fee_v0 that calls LowLevelDriveOperation::consume_to_fees_v1. The dispatcher gains the 1 => arm and known_versions: vec![0, 1]. calculate_fee_v0 and consume_to_fees_v0 are byte-identical to the base branch.
  • LowLevelDriveOperation::consume_to_fees_v1: a copy of consume_to_fees_v0 whose two refund arms call from_storage_removal_v1. The fee version number 1 arm still prices against an empty history, so both generations agree on every shipped schedule.
  • No DRIVE_VERSION_V* table selects calculate_fee: 1 yet. Every PLATFORM_V* keeps calculate_fee: 0, and the mock versions too. The unreleased protocol version that registers a schedule under a new fee version number sets calculate_fee: 1 in its drive table; that is the explicit version boundary.
  • Tests in op.rs run both generations: number 1 refunds through the legacy empty-history path with None history and ignores a supplied history (both generations); any other number requires the history (DriveError::CorruptedCodeExecution, both generations); with a boundary history, generation 0 prices every epoch at the current epoch's rate and generation 1 at the storage-epoch rate; system bytes land in removed_bytes_from_system.
  • Tests in calculate_fee/mod.rs go through the public dispatcher with a TEST_PLATFORM_V2 clone carrying the mock generation and an explicit calculate_fee slot: generation 0 and generation 1 produce their respective pricing across a boundary, both agree when every history entry is number 1, both reject a missing history, and slot 2 is UnknownVersionMismatch.

packages/rs-drive-abci/src/platform_types/platform_state/platform_state_for_saving/v1/mod.rs and platform_state/mod.rs

  • From<PlatformStateForSavingV1> for PlatformState became TryFrom with Error. An unknown stored number produces ExecutionError::CorruptedCachedState("platform state stores fee version {n} for epoch {e}, which this build does not know"). The struct, its derive stack and its bincode encoding are unchanged. The V1 arm of TryFromPlatformVersioned<PlatformStateForSaving> calls try_from. The V0 arm (legacy pre-1.4 format, every entry mapped to the first generation) is unchanged. fetch_platform_state_v0, the checkpoint loader and the verify subcommand all go through this path.
  • Tests: the two existing fixture tests are kept, and new ones assert that both the V0 testnet fixture and the V1 devnet fixture resolve every entry to number 1, that every registered number round-trips through serialize_to_bytes and versioned_deserialize, that the mock generation's number (not a registry position) is what gets stored and comes back resolved to the mock entry with storage priced identically before and after reload, that a map built the way the epoch-change hook builds it (a reference into PLATFORM_VERSIONS) and the reloaded map agree on every KnownCostItem for epochs 0 to 3, and that a V1 state carrying number 99 for epoch 3 fails to load with an error naming both.

packages/rs-drive-abci/src/execution/platform_events/block_processing_end_events/tests.rs

  • The single as_static() call site uses the fallible signature.

book/src/fees/overview.md

  • Refunds section: the rate is resolved at the storage epoch through the fee history; the current epoch only fixes how many era shares were already paid. Fee Versioning section: what fee_version_number names, lookup by number, unknown numbers are load errors, why FEE_VERSION1 and FEE_VERSION2 share number 1, and why the empty-history fallback is deliberate.

Not changed: every FEE_VERSION* and fee-group constant, every PLATFORM_V* and mock version, consume_to_fees_v0, EpochCosts::active_fee_version, upgrade_protocol_version_on_epoch_change_v0, both PlatformStateForSaving layouts, the V0 legacy mapping, SystemLimits, proto and SDK surfaces.

How Has This Been Tested?

Local gate, each command's output captured to a log file and its exit code checked (macOS, Rust 1.92):

cargo fmt --all -- --check
cargo clippy -p platform-version -p dpp -p drive -p drive-abci --all-features --all-targets -- -D warnings
cargo clippy -p platform-version --all-targets -- -D warnings
cargo check --workspace --all-targets
cargo test -p platform-version --all-features fee::
cargo test -p platform-version fee::
cargo test -p dpp --lib fee::
cargo test -p drive --lib fees::
cargo test -p drive --lib fees::calculate_fee
cargo test -p drive-abci --lib platform_types::platform_state
cargo test -p drive-abci --lib test_document_refund
cargo test -p drive-abci --lib protocol_upgrade::upgrade_protocol_version

Compile-time guard verified negatively: registering FEE_VERSION1 twice fails cargo check -p platform-version with "fee version numbers must be registered in order, starting at 1 and without gaps".

No verify-only cut needed (no src/verify/** change). Full drive-abci suite and strategy tests are left to CI.

Breaking Changes

None on any shipped protocol version, so no !.

The refund rule correction (rate at the storage epoch instead of the removal epoch) lives in a new generation: FeeRefunds::from_storage_removal_v1, consume_to_fees_v1 and Drive::calculate_fee_v1, selected only by calculate_fee: 1, which no DRIVE_VERSION_V* table sets. Generation 0 is byte-identical to the base branch. Every released protocol version therefore runs exactly the code it shipped with. The tests also show the two generations agree on every input reachable today (every registered schedule shares one storage table), so switching the slot to 1 in the unreleased protocol version alongside a new fee version number is where the correction first becomes observable.

In-workspace Rust API: FeeVersion::as_static is now fallible and PlatformStateForSavingV1 converts to PlatformState through TryFrom. Each had one in-tree caller, both updated. No client crate uses either.

Decisions taken (provisional values)

  • FEE_VERSION2 keeps fee_version_number: 1 and is not registered separately. fee_version_number names a fee-history generation: the set of values KnownCostItem can read (storage, processing, hashing, signature). The two schedules differ only in data_contract_registration, which the history never serves, so both resolve to the same generation. Renumbering would flip protocol versions 9 to 14 onto the map-driven refund branch of consume_to_fees_v0 for historical blocks, and several block-lifecycle callers (withdrawal cleanup, epoch change, masternode identity updates at init chain) pass None history to Drive and would hit CorruptedCodeExecution. That caller audit belongs to the next task in this workstream; it is recorded here so the reviewer sees it was considered.
  • Unknown stored number is an internal load error, not a consensus error. It means the binary is older than the state it is loading. The error class is ExecutionError::CorruptedCachedState, surfaced through ProtocolError::Generic by versioned_deserialize, which is the existing path for load failures.
  • The registry numbering guard is a compile-time assertion rather than a runtime check, so the constraint cannot be violated in a running node and the expect in first() and latest() has a proof.
  • A mock fee generation lives under mock-versions, not in the shipped registry. The review asked for a lookup that cannot be satisfied by array position. Adding a real second generation would be a consensus change (it needs a protocol version and the caller audit). The mock generation follows the existing test protocol version pattern: its number sits above TEST_PROTOCOL_VERSION_SHIFT_BYTES, it is consulted after the shipped registry, and release builds never compile it.
  • The refund correction is a new calculate_fee generation, not an in-place edit. The first revision changed from_storage_removal in place on the argument that the change is unobservable on shipped versions; review pointed out the conventions freeze shipped generations regardless, so generation 0 is restored byte-identical and the corrected rule is calculate_fee_v1 / consume_to_fees_v1 / from_storage_removal_v1. No version table selects it in this PR: the slot flips to 1 in the unreleased protocol version that also introduces a new fee version number, so both changes share one boundary. The registry change is a lookup repair with identical results for every currently registered input, and the saved-state change only turns an abort into an error; neither alters versioned behaviour.
  • Provisional numbers: none. This task introduces no new fee rates, limits or constants. The synthetic number 2 generation with 54000 credits per byte exists only inside test modules.

Findings recorded for follow-up tasks in the workstream (no code change here):

  • The block-lifecycle callers that pass None fee history to Drive are safe while every registered number is 1; the tests in op.rs pin that a later number requires the history on those paths.
  • An end-to-end replay-versus-saved-state comparison across a live rate boundary at the ABCI level can now use TEST_FEE_VERSION_DOUBLED_STORAGE_RATE on a mock protocol version through replace_test_versions; this PR covers the boundary at the Drive dispatcher and the saved-state layers. test_document_refund_after_10_epochs_on_different_fee_version_increasing_fees cannot observe a boundary today because its "higher fees" version is a clone of the latest schedule (number 1).

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

Dash-Tasks: R12-01


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

🤖 Generated with Claude Code

DCG-Claude and others added 6 commits September 11, 2026 20:22
The fee version registry resolved a fee_version_number by array position and
as_static aborted on an unknown number. Lookup is now by number on every entry
point, zero and unregistered numbers are errors, as_static is fallible, and a
compile time assertion keeps the registry numbered contiguously from one.

Tests pin that every schedule a platform version references is registered and
agrees with the registered generation on every group the fee history serves,
and that the number one storage rates stay frozen for replay.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e stored

A refund is the unpaid remainder of the storage fee originally charged, and
that fee was priced with the storage table active at the storage epoch. The
refund now resolves the rate through the fee history at the storage epoch
instead of the removal epoch. Every shipped schedule carries the same storage
table under fee version number 1, so no reachable input changes; the tests
pin both the boundary behaviour and the shipped-input equivalence.

Also adds tests for the epoch fee-history resolution and for agreement between
every platform version's schedule and its registered generation on every
known cost item.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Pins the legacy empty-history path for fee version number one, the fee
history requirement for any other number, storage-epoch rate resolution
across a history boundary, and the calculate_fee dispatcher forwarding the
platform schedule and history.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…n number

Restoring PlatformState from the version 1 saving format resolved every
stored fee version number with expect, so a state written by a newer build
aborted the node at start. The conversion is now TryFrom and returns a
CorruptedCachedState error naming the number and the epoch. The struct and
its encoding are unchanged; the legacy version 0 mapping to the first
generation is unchanged.

Tests cover both fixture formats resolving to number 1, every registered
number round-tripping through saved state, in-memory versus reloaded fee
history agreeing on every known cost item, and the unknown-number rejection.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…th a mock generation

Adds a mock fee-history generation under the mock-versions feature whose
number lives above the test protocol version shift, so it is never a position
in the shipped registry and can never collide with a persisted number. The
registry consults it after the shipped entries. Lookup by carried number is
factored into a helper and tested against a registry whose numbers do not
line up with positions.

Drive::calculate_fee is now driven through the dispatcher with a platform
version carrying the mock generation: refunds across a storage-rate boundary
are priced at the storage-epoch rate, and a missing fee history is rejected.
Saved state round-trips the mock number and prices storage identically after
reload.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions github-actions Bot added this to the v4.2.0 milestone Sep 12, 2026
@github-actions

github-actions Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

📖 Book Preview built successfully.

Download the preview from the workflow artifacts.
To view locally: download the artifact, unzip, and open index.html.

Updated at 2026-09-12T05:41:59.206Z

@coderabbitai

coderabbitai Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: c2ebd5ce-325c-43e3-b495-3d36601774c3

📥 Commits

Reviewing files that changed from the base of the PR and between d020728 and 2cb4ed4.

📒 Files selected for processing (11)
  • book/src/fees/overview.md
  • packages/rs-dpp/src/fee/default_costs/mod.rs
  • packages/rs-dpp/src/fee/fee_result/refunds.rs
  • packages/rs-drive-abci/src/execution/platform_events/block_processing_end_events/tests.rs
  • packages/rs-drive-abci/src/platform_types/platform_state/mod.rs
  • packages/rs-drive-abci/src/platform_types/platform_state/platform_state_for_saving/v1/mod.rs
  • packages/rs-drive/src/fees/calculate_fee/mod.rs
  • packages/rs-drive/src/fees/op.rs
  • packages/rs-platform-version/src/version/fee/mod.rs
  • packages/rs-platform-version/src/version/mocks/fee_test.rs
  • packages/rs-platform-version/src/version/mocks/mod.rs

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The change resolves fee versions by carried numbers, validates registry numbering, prices storage refunds at storage epochs, and makes saved-state restoration fallible for unknown versions. Tests cover fee-history boundaries, dispatch paths, round trips, legacy states, and invalid numbers.

Changes

Fee generation and refund handling

Layer / File(s) Summary
Fee registry and history resolution
packages/rs-platform-version/src/version/fee/mod.rs, packages/rs-platform-version/src/version/mocks/*, packages/rs-dpp/src/fee/default_costs/mod.rs, packages/rs-drive-abci/src/execution/platform_events/block_processing_end_events/tests.rs
Fee versions resolve by fee_version_number instead of registry position. Registry numbering must start at 1 and remain contiguous. Fee-history lookups cover empty histories, exact entries, lower entries, and platform-version schedules.
Storage-epoch refund pricing
packages/rs-dpp/src/fee/fee_result/refunds.rs, packages/rs-drive/src/fees/calculate_fee/mod.rs, packages/rs-drive/src/fees/op.rs
Storage refunds use the fee rate active when bytes were stored. Tests cover rate boundaries, legacy fee version 1 behavior, missing history, and dispatcher paths.
Saved-state fee version restoration
packages/rs-drive-abci/src/platform_types/platform_state/mod.rs, packages/rs-drive-abci/src/platform_types/platform_state/platform_state_for_saving/v1/mod.rs
Saved states resolve stored fee version numbers through the registry. Unknown numbers return CorruptedCachedState errors. Round-trip tests preserve registered, mock, and legacy fee versions.
Fee generation documentation
book/src/fees/overview.md
The fee overview documents storage-epoch refund pricing, fee-history generations, number-based registry lookup, saved-state errors, and empty-history behavior.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant Drive
  participant LowLevelDriveOperation
  participant FeeRefunds
  participant FeeHistory
  Drive->>LowLevelDriveOperation: calculate fee for storage removal
  LowLevelDriveOperation->>FeeRefunds: consume storage removal
  FeeRefunds->>FeeHistory: resolve rate at each storage epoch
  FeeHistory-->>FeeRefunds: return fee generation
  FeeRefunds-->>Drive: return refunds and fee result
Loading

Merge Risk: ⚪ Minimal · up to 2cb4e

The fee-version lookup, storage-epoch refund pricing, and saved-state error paths are covered without an identified merge-blocking issue.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 79.41% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 68 functions across 10 files. (1 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main changes: registered-number fee-version resolution and storage-epoch refund pricing.
Full details: Docstring Coverage

Explanation

Docstring coverage is 79.41% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 68 functions across 10 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dashvm/r12-01

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.

@DCG-Claude
DCG-Claude changed the base branch from v4.2-dev to v4.3-dev September 12, 2026 02:12
@codecov

codecov Bot commented Sep 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.18652% with 40 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.12%. Comparing base (d020728) to head (078c446).

Files with missing lines Patch % Lines
packages/rs-drive/src/fees/op.rs 91.42% 15 Missing ⚠️
packages/rs-drive/src/fees/calculate_fee/v1/mod.rs 75.86% 7 Missing ⚠️
...rive-abci/src/platform_types/platform_state/mod.rs 95.77% 6 Missing ⚠️
packages/rs-drive/src/fees/calculate_fee/mod.rs 97.59% 5 Missing ⚠️
packages/rs-dpp/src/fee/fee_result/refunds.rs 97.88% 4 Missing ⚠️
packages/rs-dpp/src/fee/default_costs/mod.rs 95.89% 3 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           v4.3-dev    #4703      +/-   ##
============================================
+ Coverage     82.94%   84.12%   +1.18%     
============================================
  Files          2767     2798      +31     
  Lines        376906   380743    +3837     
============================================
+ Hits         312618   320301    +7683     
+ Misses        64288    60442    -3846     
Components Coverage Δ
dpp 84.77% <97.32%> (+1.13%) ⬆️
drive 81.96% <93.44%> (+1.31%) ⬆️
drive-abci 88.13% <96.17%> (+1.74%) ⬆️
sdk ∅ <ø> (∅)
dapi-client ∅ <ø> (∅)
platform-version ∅ <ø> (∅)
platform-value 92.92% <ø> (ø)
platform-wallet ∅ <ø> (∅)
drive-proof-verifier 43.67% <ø> (+0.53%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@thepastaclaw

thepastaclaw commented Sep 12, 2026

Copy link
Copy Markdown
Collaborator

✅ Final review complete — no blockers (commit 078c446) · triage: critical · Phase 2 only (queue backlog)

@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 2 only (queue backlog)

Verified the supplied finding against the exact head and confirmed one architectural blocker: the refund algorithm changes through a shared helper without a new method-version boundary. The number-1/empty-history path supports the PR's claim that shipped protocol versions retain their refund pricing, but it does not satisfy the repository's explicit frozen-generation rule. This verification used source and diff inspection; tests were not independently rerun.

🔴 1 blocking

Review provenance

Source: reviewer 1: gpt-6-astra (agent: phase2-reviewer, role: general); reviewer 2: gpt-6-astra (agent: phase2-reviewer, role: rust-quality); reviewer 3: gpt-6-astra (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6-astra (agent: astra-verifier, role: final-verifier)

  • Triage: critical by gpt-6-astra (effort low) — This is a large, intricate cross-cutting change that directly alters consensus fee resolution and funds movement through storage-removal refund pricing, with additional persisted-state loading behavior in platform state.
  • Phase 1 reviewers: not run (skipped for throughput: 13 PRs queued, above the 10 limit)
  • Fresh verifier: gpt-6-astra — final-verifier; agent astra-verifier
  • Phase 2 reviewers: gpt-6-astra — general (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
🤖 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-dpp/src/fee/fee_result/refunds.rs`:
- [BLOCKING] packages/rs-dpp/src/fee/fee_result/refunds.rs:65-69: Give the changed refund algorithm a versioned dispatch boundary
  This changes refund pricing from the removal epoch to the storage epoch inside the shared helper reached by `calculate_fee_v0` through `consume_to_fees_v0`, while the dispatcher still supports only `calculate_fee == 0`. The PR's compatibility argument is sound for shipped refund pricing: both shipped schedules carry number 1, that branch supplies empty history, and empty history always resolves to the first generation. However, `book/src/contributing/coding-conventions.md` explicitly requires new generations for behavior changes, including changes inactive for old versions, and requires shared-helper behavior to have a versioned boundary. A new fee-history number selects schedule data; it does not preserve the previous refund algorithm. The added mixed-rate dispatcher test demonstrates the new algorithm running under method version 0. Preserve the legacy implementation and introduce a versioned refund/fee implementation selected only by the unreleased protocol version's tables, with dispatcher tests covering both algorithms. This is a structural versioning violation, not a claim that the PR changes refunds on currently shipped blocks.

Comment thread packages/rs-dpp/src/fee/fee_result/refunds.rs
…rsion 1

Review pointed out that changing FeeRefunds::from_storage_removal in place
edits a shipped generation even though no released protocol version can
observe the difference. The shipped helper, consume_to_fees_v0 and
calculate_fee_v0 are restored byte-identical to the base branch. The
storage-epoch rule now lives in from_storage_removal_v1, consume_to_fees_v1
and calculate_fee_v1, reachable only through calculate_fee version 1, which
no drive version table selects yet. The unreleased protocol version that
registers a schedule under a new fee version number flips the slot.

Tests run both generations: the shipped rule is pinned to current-epoch
pricing across a boundary, generation 1 prices at the storage epoch, both
agree whenever every history entry is number 1, and the dispatcher covers
both arms plus an unknown slot.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@DCG-Claude

Copy link
Copy Markdown
Collaborator Author

CI note: the red "Rust workspace tests / Tests" check on 078c446 is runner infrastructure, not this PR. Both attempts landed on the self-hosted runner mac-runner-brian and died in the toolchain setup step, before any cargo command ran:

rustup toolchain install 1.92 ...
error: could not read settings file: '/Users/latte-ssh/.rustup/settings.toml': Operation not permitted (os error 1)

The same runner failed the v4.2-dev base run 34656761940 with the identical error. This PR's previous head passed the same job on mac-runner-pasta (run 34666771516), and the local gate (fmt, clippy with all features and all targets on the four touched crates, workspace check with all targets, dpp, drive and drive-abci fee and state tests) is green on 078c446. I have used the one rerun; it needs either the runner's rustup permissions fixed or the job re-queued onto a healthy runner.


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

@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 2 only (queue backlog)

The PR preserves the shipped generation-0 refund behavior and places the storage-epoch refund correction behind the new calculate_fee version-1 dispatch boundary. Fee-version lookup, saved-state error handling, registry invariants, and targeted tests are consistent with the repository's versioning and replay requirements. No additional actionable issues were found.

Review provenance

Source: reviewer 1: gpt-6-astra (agent: phase2-reviewer, role: general); reviewer 2: gpt-6-astra (agent: phase2-reviewer, role: rust-quality); reviewer 3: gpt-6-astra (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6-astra (agent: astra-verifier, role: final-verifier)

  • Triage: critical by gpt-6-astra (effort low) — This is a large, intricate change that directly alters consensus fee-version resolution and storage-removal refund pricing in FeeRefunds::from_storage_removal and calculate_fee generation dispatch, affecting funds movement and persisted-state compatibility.
  • Phase 1 reviewers: not run (skipped for throughput: 11 PRs queued, above the 10 limit)
  • Fresh verifier: gpt-6-astra — final-verifier; agent astra-verifier
  • Phase 2 reviewers: gpt-6-astra — general (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

@QuantumExplorer QuantumExplorer modified the milestones: v4.2.0, v4.3.0 Sep 16, 2026
@DCG-Claude

Copy link
Copy Markdown
Collaborator Author

Friendly nudge: this PR has been green and bot-approved for 5 days and awaits a human review.


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

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants