Skip to content

feat(platform): define protocol-versioned smart-contract computation limits and their gas representation - #4705

Open
DCG-Claude wants to merge 9 commits into
v5.0-devfrom
dashvm/r06-01
Open

DCG-Claude wants to merge 9 commits into
v5.0-devfrom
dashvm/r06-01

Conversation

@DCG-Claude

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

Copy link
Copy Markdown
Collaborator

Issue being fixed or feature implemented

Smart contracts need consensus limits on the computation one invocation and one block may perform, a deterministic unit to count that computation in, a protocol-versioned price that turns units into credits, and a defined path from that charge to the gas figures Tenderdash sees. None of this existed: the version tables had no field for it, the fee schedule had no price, and the 5.0 protocol version did not exist on the branch.

This is task R06-01 of the smart-contract plan in #4626 (section 6.1, "Smart-contract computation budget"). The owner review delegated the numbers (Q40): supply provisional limits and prices now from the shared allocation register, measure and revise before activation, and keep the engine profile pin and unmeasured native costs open.

Refs #4689

Dash-Tasks: R06-01

What was done?

Limits in the version tables (packages/rs-platform-version/src/version/system_limits/)

  • smart_contract.rs (new): pub type ComputationUnits = u64 and SmartContractComputationLimits { max_computation_units_per_invocation, max_computation_units_per_block }. The doc comment defines a unit (a deterministic count of admitted guest operations and host work under the active metering generation, never wall-clock), the single-counter rule for the per-invocation limit (nested calls, predicates, module initialisation and host entries all charge one counter, nothing is counted twice), the ordinary-plus-scheduled scope of the per-block limit, the credits mapping and the runtime interface. is_well_formed() (both limits non-zero, one invocation fits in a block) is the one invariant that must survive measurement.
  • SystemLimits gains smart_contract_computation: Option<SmartContractComputationLimits>, None on SYSTEM_LIMITS_V1 to V4 and on the hand-written mock in mocks/v2_test.rs. SystemLimits additionally derives PartialEq, Eq for the inheritance test below.
  • v5.rs (new): SYSTEM_LIMITS_V5 = { smart_contract_computation: Some(25_000_000 per invocation, 250_000_000 per block), ..SYSTEM_LIMITS_V4 } with a compile-time assertion on is_well_formed().
  • system_limits becomes a public module so the alias and the struct are nameable from dpp, drive-abci and the future runtime crate (nothing outside the crate named a system_limits path before).

Price in the fee schedule (packages/rs-platform-version/src/version/fee/)

  • dashvm/mod.rs and dashvm/v1.rs (new): FeeDashVmVersion { credits_per_computation_unit: u64 } with the same derive stack as the other groups, and FEE_DASHVM_VERSION1 at 1 credit per unit. The remaining register rows (deployment validation, readiness verification, host entry, per-byte copy) are added to this group in place by the pricing task.
  • FeeVersion gains dashvm: Option<FeeDashVmVersion> as its last field. FEE_VERSION1, FEE_VERSION2 and the pre-1.4 saved-state conversion carry None, so a schedule without contract pricing can never price computation at zero by accident.
  • v3.rs (new): FEE_VERSION3 = { dashvm: Some(FEE_DASHVM_VERSION1), ..FEE_VERSION2 }. fee_version_number stays 1 because no storage, processing, hashing or signature rate changes; for the same reason the schedule is not appended to FEE_VERSIONS, which holds one entry per number. This is the rule the fee-registry repair on the 4.3 branch documents (a schedule that only changes a group the history never serves keeps the number of the generation it agrees with).

Protocol versions 15, 16 and 17 (packages/rs-platform-version/src/version/)

  • v15.rs and v16.rs (new) are placeholders for the 4.3 and 4.4 protocol versions reserved by the allocation register. They are struct updates over their predecessor (PlatformVersion { protocol_version: PROTOCOL_VERSION_15, ..PLATFORM_V14 }), not file copies, so that the forward merge of the real 4.3 or 4.4 file is an add/add conflict resolved by taking the incoming file, after which every table it changes flows into 16 and 17 without a second edit.
  • v17.rs (new) is the 5.0 protocol version: PLATFORM_V17 = { fee_version: FEE_VERSION3, system_limits: SYSTEM_LIMITS_V5, ..PLATFORM_V16 }. No method version changes, no migration hook; a node at 17 behaves exactly like one at 16 until the enforcement tasks wire the runtime in.
  • LATEST_VERSION = PROTOCOL_VERSION_17, LATEST_PLATFORM_VERSION = &PLATFORM_V17, all three appended to PLATFORM_VERSIONS. DESIRED_PLATFORM_VERSION follows LATEST, as for every protocol version introduced on a development branch.

Units to credits (packages/rs-dpp/src/fee/smart_contract_computation.rs, new)

  • computation_units_to_credits(units, &PlatformVersion) -> Result<Credits, ProtocolError>: checked_mul by platform_version.fee_version.dashvm.credits_per_computation_unit; ProtocolError::CorruptedCodeExecution when the protocol version has no dashvm group (same shape as daily_withdrawal_limit_v2 reading a missing table entry), ProtocolError::Overflow when the charge does not fit in credits. The table is the versioned part; a formula change would earn a DPPMethodVersions slot then, not now.
  • The price is read from the active protocol version, never from the persisted epoch fee history. That history (previous_fee_versions) is keyed by fee_version_number, records a schedule only when the number changes, is saved as numbers and restored through FeeVersion::get(number), and serves only the storage, processing, hashing and signature groups (KnownCostItem) plus the storage refund rates. The dashvm group is read like data_contract_registration, state_transition_min_fees and vote_resolution_fund_fees, which the history never carried either. The function takes &PlatformVersion so a history entry cannot be passed to it; the field, group and schedule docs state the rule.
  • The module doc states the gas mapping: the charge enters FeeResult.processing_fee, which is what gas_used (abci/app/execution_result.rs) and gas_wanted (abci/handler/check_tx.rs) already report through total_base_fee(). Gas stays credits; computation units are never reported to Tenderdash and no unit equivalence with Tenderdash gas is introduced. The Tenderdash block max_gas in dashmate is unchanged.
  • Re-exports ComputationUnits and SmartContractComputationLimits for dpp consumers.

Per-block ledger (packages/rs-drive-abci/src/execution/types/block_computation_budget.rs, new)

  • BlockComputationBudget::for_platform_version(&PlatformVersion) -> Option<Self> (None before the 5.0 version so callers skip the contract path), reserve(bound) -> Result<ComputationReservation, BlockComputationBudgetExceeded> (an admission outcome that leaves the ledger unchanged), settle(reservation, actual) -> Result<released, Error> (actual > bound is ExecutionError::CorruptedCodeExecution, because the runtime cannot legally exceed the budget it was given), limit(), consumed(), remaining().
  • ComputationReservation is #[must_use], not Clone, consumed by settle, and bound to the ledger that issued it (a process-local identity, never serialised), so a reservation can neither be spent twice nor settled into another ledger, which would credit that ledger with units it never held; a foreign reservation is ExecutionError::CorruptedCodeExecution with the ledger unchanged. Cloning a ledger (the block execution context is Clone) copies the counters into a ledger with its own identity, so reservations issued before the clone settle only into the original; the ledger therefore has no PartialEq (equality over the identity would make a ledger unequal to its own clone), callers compare the counters. Reserving the admitted bound rather than the actual consumption means per-block exhaustion is never a mid-execution paid failure and an invocation's outcome does not depend on its position in the block. Every operation uses checked arithmetic. No version wrapper: the ledger is in-memory block state, never serialised.
  • Nothing wires it into the block loop yet (see "What stays open").

Book (book/src/fees/overview.md, book/src/versioning/feature-versions.md, book/src/versioning/platform-version.md): the unit, the two limits, the price, why the fee version number stays 1, the gas mapping, the nested optional limits group, and the placeholder versions with their forward-merge rule.

What stays byte-identical: PLATFORM_V1 to PLATFORM_V14 behaviour (their tables gain only None fields), FEE_VERSION1 and FEE_VERSION2 on every value the fee history serves, FEE_VERSIONS, every vN method module in dpp, drive and drive-abci, process_raw_state_transitions, check_tx, execution_result.rs, BlockExecutionContextV0, NotExecutedReason, and every native budget (proposer timer, withdrawal and shielded per-block caps, Tenderdash max_gas). Replay of every block at protocol versions 1 to 14 is unchanged because no shipped table changes a non-None value and no code path reads the new fields.

What stays open (owned by later tasks, deliberately not here): opcode weights and the metering generation (R03-07, R08-01), the remaining price rows (R12-03), enforcement in the block loop and CheckTx (R06-02, R08-05, R11-04: the NotExecutedReason variant, the ledger's place on the block execution context, affordability), scheduled-work admission (R06-09), consensus error codes for exhaustion (R12-07), the engine/profile pin (A04), and any native-event budget change (rejected by policy).

How Has This Been Tested?

New tests, all next to the code they exercise:

  • packages/rs-platform-version/src/version/system_limits/mod.rs
    • smart_contract_computation_limits_and_pricing_activate_together: for every registered version, limits and price are both Some or both None, every Some limits value is well formed, every Some price is non-zero, and every version below 17 is None. A cross-table invariant (limits without a price would meter for free, a price without limits would refuse every invocation), not a restated literal.
    • the_5_0_protocol_version_changes_only_the_smart_contract_computation_tables: PLATFORM_V17 minus the two new groups equals PLATFORM_V16. Pins the delta the 5.0 version adds and fails when a forward merge changes 16 without 17 inheriting it or keeps this branch's generation over an incoming one.
    • mock_platform_versions_have_no_smart_contract_computation_limits (mock-versions): the mock registry stays None.
  • packages/rs-platform-version/src/version/fee/v3.rs: should_agree_with_its_registered_fee_history_generation_on_every_group_the_history_serves: FEE_VERSION3 and the registered generation its number resolves to agree on storage, processing, hashing and signature (the condition under which sharing the number is sound), and the registered generation carries no dashvm group.
  • packages/rs-dpp/src/fee/smart_contract_computation.rs: pricing at the active version's rate against PlatformVersion::latest(), zero units, overflow at u64::MAX units with a 2-credit schedule, and the corrupted-code-execution error on protocol version 14.
  • packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/upgrade_protocol_version/v0/mod.rs: test_upgrade_to_the_5_0_protocol_version_keeps_the_fee_history_and_prices_computation_from_the_active_version: a platform at protocol version 16 with one history entry upgrades to 17 on an epoch change through the real hook; the history gains no entry and still resolves (through EpochCosts::active_fee_version) to number 1 without contract pricing with the same storage rates the upgraded version charges; the state is serialized through the saving format and deserialized; the reloaded history is unchanged and the price is obtained from current_platform_version() on both sides of the restart with the same result.
  • packages/rs-drive-abci/src/execution/types/block_computation_budget.rs: None before 5.0 and the per-block limit as remaining at latest(); exact-limit fits and one more unit is refused with the ledger unchanged; settle releases the unused bound; settling above the bound is rejected with the bound still held; a sequence of reserve/settle pairs keeps consumed + held + remaining == limit at every step and cannot re-spend released-then-consumed units; an unsettled reservation keeps its bound held until settled with zero; checked arithmetic at u64::MAX; a reservation issued by another ledger is rejected without changing either ledger; a clone starts from the same counters with its own identity and neither ledger sees the other's settlements.

Local gate (each command's output redirected to a file, exit code checked):

cargo fmt --all
cargo clippy -p platform-version -p dpp -p drive-abci --all-features --all-targets -- -D warnings
cargo check --workspace --all-targets
cargo test -p platform-version --features mock-versions
cargo test -p dpp smart_contract_computation
cargo test -p drive-abci --lib block_computation_budget
cargo test -p drive-abci --lib upgrade_protocol_version

The verify-only cut is not affected (no drive/src/verify change).

Breaking Changes

None observable. Every shipped protocol version's tables gain only None fields, no method version changes, and no code path reads the new values. The new protocol versions are development-branch versions no network has run. The Encode/Decode derives of FeeVersion change shape, which matters nowhere: saved state V1 stores fee version numbers and saved state V0 decodes the separate legacy struct.

Decisions taken (provisional values)

  • Per invocation 25,000,000 units, per block 250,000,000 units (SYSTEM_LIMITS_V5): the "Compute" row of the DashVM allocation register, marked provisional in a code comment. Measured and revised by the workload measurement task (R12-04) before activation.
  • 1 credit per computation unit (FEE_DASHVM_VERSION1): the register's "Provisional price", marked provisional in a code comment. Revised together with the limits.
  • Protocol version numbers 15 (4.3), 16 (4.4), 17 (5.0): the register's allocation. The tree holds nothing above 14 on any development branch, so these are the next free numbers; the number is consensus-visible only after a network runs it, and none has. If the register drops an activation (4.4 ships no consensus change), 5.0 becomes 16: delete the placeholder and rename v17.rs, PROTOCOL_VERSION_17 and PLATFORM_V17.
  • Activation gate: the numbers are live on the development branch because the enforcement and test tasks need them to run against, and a binary can only propose protocol version 17 once 5.0 ships. The 5.0 release checklist (R14-02) must record the measured revision of these three numbers before any network is asked to propose version 17; this is added as a row in the workstream evidence table.
  • Forward-merge rules: (a) add/add on v15.rs or v16.rs: take the incoming file, keep v17.rs. (b) add/add on system_limits/v5.rs or fee/v3.rs: keep the incoming generation, renumber this branch's constant to the next free number, rebase it on the incoming one and point v17.rs at it. The three registry tests fail if a merge gets any of this wrong.
  • Limits as one nested optional group rather than two flat Option<u64> fields, so consumers pass one value and later limits of the same family (host calls per invocation, returned read bytes) extend the group.
  • Pricing as a pure function over the active protocol version's fee table, not a versioned method: the table is the versioned part. The persisted epoch fee history never carries the dashvm group and is never consulted for it; registering FEE_VERSION3 under a new number would not put the price into the history, it would switch every storage refund at protocol version 17 onto the epoch-history refund path for rates that did not change.
  • The per-invocation counter is the runtime's: this PR adds no second counter in dpp or drive-abci; the engine takes the limit as its budget and reports consumption in ComputationUnits.

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

🤖 Generated with Claude Code


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

DCG-Claude and others added 7 commits September 12, 2026 00:50
…imits

Introduce `SystemLimits::smart_contract_computation`, an optional group holding
the per-invocation and per-block computation limits in one contract-only unit
(`ComputationUnits`), and `SYSTEM_LIMITS_V5` carrying the provisional numbers
from the DashVM allocation register (25 million units per outer invocation,
250 million per block). Every shipped table and the hand-written mock carry
`None`, so no shipped protocol version changes behaviour. A compile-time
assertion pins the one invariant that must survive measurement: both limits
are non-zero and one maximal invocation fits in a block.

`system_limits` becomes a public module so the unit alias and the limits
struct are nameable from dpp, drive-abci and the future runtime crate.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Add the optional `dashvm` group to `FeeVersion` with one row for now, the
credits charged per computation unit, and `FEE_VERSION3` carrying the
provisional register price (1 credit per unit) on top of `FEE_VERSION2`.
`FEE_VERSION1`, `FEE_VERSION2` and the pre-1.4 saved-state conversion carry
`None`, so a schedule without contract pricing can never price computation
at zero by accident.

`fee_version_number` stays 1 because no storage, processing, hashing or
signature rate changes, and for the same reason the new schedule is not
appended to `FEE_VERSIONS`: the registry holds one entry per number and the
persisted fee history keys nothing new.

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

Register protocol versions 15 (4.3 placeholder), 16 (4.4 placeholder) and 17
(5.0) following the DashVM allocation register. The two placeholders are
struct updates over their predecessor so that a forward merge of the real
4.3 or 4.4 version file is resolved by taking the incoming file and every
table it changes flows into 17 without a second edit. Version 17 overrides
only `system_limits` (`SYSTEM_LIMITS_V5`, the computation limits) and
`fee_version` (`FEE_VERSION3`, the computation price); nothing dispatches on
either yet, so a node at 17 behaves exactly like one at 16.

`LATEST_VERSION` and `LATEST_PLATFORM_VERSION` move to 17. Three tests guard
the registry: the existing length assertion, a cross-table invariant that
limits and price activate together and only from 17, and an inheritance test
pinning that 17 differs from 16 in exactly those two tables.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Add `dpp::fee::smart_contract_computation` with
`computation_units_to_credits`: checked multiplication of the units an
invocation consumed by the fee schedule's `dashvm` price, a corrupted code
execution error when the schedule has no contract pricing and an overflow
error when the charge does not fit in credits. The module documents the gas
mapping: the charge enters the invocation's processing fee, which is what
Tenderdash's gas fields already report in credits, so no new gas unit and no
unit equivalence with Tenderdash gas is introduced. The unit alias and the
limits struct are re-exported from platform-version for dpp consumers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Add `execution::types::block_computation_budget` with
`BlockComputationBudget`, the deterministic per-block accounting proposal
creation and proposal validation will share: reserve an invocation's
admitted bound against the block before it runs, settle the actual
consumption afterwards and release the unused part. Exhaustion is an
admission outcome that leaves the ledger unchanged, settling more than the
reserved bound is a corrupted code execution error, and every operation
uses checked arithmetic. `ComputationReservation` is `#[must_use]`, not
`Clone`, and consumed by settlement, so a reservation cannot be spent twice.
`for_platform_version` returns `None` before the 5.0 protocol version so
callers skip the contract path entirely. Nothing wires the ledger into the
block loop yet.

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

Document in the platform book what a computation unit is, the per-invocation
and per-block limits in the system limits, the price in the fee schedule, why
the fee version number stays 1, how the charge reaches Tenderdash gas through
the processing fee, and why the 5.0 branch carries placeholder protocol
versions 15 and 16 as struct updates.

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

The persisted epoch fee history is keyed by `fee_version_number`, records a
schedule only when that number changes, and is restored from saved state
through `FeeVersion::get(number)`. It serves the storage, processing, hashing
and signature groups and the storage refund rates, and nothing else, so it
never carries the `dashvm` group, exactly as it never carried the contract
registration, minimum fee or vote resolution groups. `computation_units_to_credits`
now takes `&PlatformVersion` and reads `fee_version.dashvm` from the active
protocol version, so a history entry cannot be passed to it by mistake; the
field, group and schedule docs state the rule.

Two tests pin it: `FEE_VERSION3` agrees with the registered generation its
number resolves to on every group the history serves, and an upgrade from
protocol version 16 to 17 on an epoch change through the real hook records no
new history entry, survives a save and reload with the history unchanged, and
prices computation from the active version on both sides of the restart.

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

coderabbitai Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

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

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

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: aa8a5c98-5bfe-48c8-a4df-4d064decc2bd

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

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

❤️ Share

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

@github-actions

github-actions Bot 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-12T20:22:54.680Z

@thepastaclaw

thepastaclaw commented Sep 12, 2026

Copy link
Copy Markdown
Collaborator

✅ Final review complete — no blockers (commit 43e23ec) · triage: normal · Phase 2 only (queue backlog)

@codecov

codecov Bot commented Sep 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 74.61240% with 131 lines in your changes missing coverage. Please review.
✅ Project coverage is 84.78%. Comparing base (5f1e0cc) to head (43e23ec).

Files with missing lines Patch % Lines
...ci/src/execution/types/block_computation_budget.rs 66.29% 121 Missing ⚠️
...rotocol_upgrade/upgrade_protocol_version/v0/mod.rs 91.95% 7 Missing ⚠️
...kages/rs-dpp/src/fee/smart_contract_computation.rs 95.71% 3 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           v5.0-dev    #4705      +/-   ##
============================================
- Coverage     86.36%   84.78%   -1.59%     
============================================
  Files          2766     2798      +32     
  Lines        366105   375781    +9676     
============================================
+ Hits         316191   318609    +2418     
- Misses        49914    57172    +7258     
Components Coverage Δ
dpp 85.98% <95.71%> (-1.31%) ⬇️
drive 84.61% <ø> (+0.36%) ⬆️
drive-abci 89.56% <71.30%> (-0.10%) ⬇️
sdk ∅ <ø> (∅)
dapi-client ∅ <ø> (∅)
platform-version ∅ <ø> (∅)
platform-value 66.85% <ø> (-26.08%) ⬇️
platform-wallet ∅ <ø> (∅)
drive-proof-verifier 49.78% <ø> (ø)
🚀 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 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 Phase-2 findings against exact head b0ec338 and consolidated the two overlapping reports into one confirmed suggestion. The new ledger accepts foreign reservations, violating its documented accounting invariant; no production callers currently expose this as a consensus failure. Verification used source and caller inspection; no tests were run during this verification.

🟡 1 suggestion(s)

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 diff that directly changes consensus-critical protocol-versioned smart-contract computation limits, block execution budgeting, fee-to-gas mapping, and protocol upgrade behavior in rs-drive-abci and rs-platform-version.
  • Phase 1 reviewers: not run (skipped for throughput: 19 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-drive-abci/src/execution/types/block_computation_budget.rs`:
- [SUGGESTION] packages/rs-drive-abci/src/execution/types/block_computation_budget.rs:127-134: Bind computation reservations to their originating ledger
  A reservation stores only its bound, so `settle` cannot establish that the receiving ledger reserved those units. For example, create budgets A and B with `with_limit(100)`, reserve 80 from A, then call `B.settle(reservation, 0)`: settlement succeeds and B reports 180 remaining, allowing it to admit more than its limit. Checked arithmetic prevents integer overflow, not this accounting violation, and making the token non-Clone only prevents repeated settlement. Bind reservations to their issuing ledger and reject foreign reservations before changing counters, with explicit ownership semantics for `BlockComputationBudget::clone`. Add regression coverage for independent ledgers and cloned budgets. This is a correctness gap in the API introduced here, not a current production consensus failure: the ledger is not yet wired into block execution.

…ued them

A reservation carried only its bound, so settling it into a different
`BlockComputationBudget` credited that ledger with units it never held and
let it admit more than its limit. Each ledger now has a process-local
identity that its reservations carry, and `settle` rejects a foreign
reservation as a corrupted code execution before touching any counter.
Cloning a ledger copies the counters into a ledger with its own identity, so
reservations issued before the clone settle only into the original. The
identity is never serialised and never consensus-visible.

Two regression tests cover independent ledgers and cloned budgets.

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

Copy link
Copy Markdown
Collaborator Author

The red policy / reconcile check is not caused by this PR. The shared review engine was re-pinned today (#4713) to a revision that reads .github/workflows/pr-review-policy.yml from the PR's base branch, and v5.0-dev does not carry that file yet (it exists on v4.2-dev only), so the job 404s on the fetch and exits before doing anything. Every PR against v5.0-dev will hit this until v4.2-dev is merged forward; a rerun fails the same way, and the check is not among the required ones for v5.0-dev.


🤖 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)

Verified the Phase-2 findings against head 97584ea. The reservation-provenance issue is fixed; one non-blocking Clone/Eq trait-contract issue remains. Runtime enforcement is explicitly deferred by this metadata-and-ledger PR and does not justify expanding its scope.

🟡 1 suggestion(s)

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: normal by gpt-6-astra (effort low) — The cross-crate additions define versioned limits, pricing helpers and a reservation ledger, but packages/rs-platform-version/src/version/v17.rs explicitly leaves enforcement unwired and the protocol-upgrade changes are tests, so the diff does not introduce intricate changes to active consensus enforcement or funds movement.
  • Phase 1 reviewers: not run (skipped for throughput: 12 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 high); agent phase2-reviewer, gpt-6-astra — rust-quality (completed, effort high); agent phase2-reviewer, gpt-6-astra — security-auditor (completed, effort high); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-drive-abci/src/execution/types/block_computation_budget.rs`:
- [SUGGESTION] packages/rs-drive-abci/src/execution/types/block_computation_budget.rs:90-98: Keep the ledger's Clone and equality implementations consistent
  The fresh ledger identity correctly isolates reservations, but BlockComputationBudget derives PartialEq/Eq over that identity as well as its counters. Consequently, a budget compares unequal to its own clone; the regression test explicitly asserts this at line 358. This violates Clone's documented requirement that cloning an Eq value preserves equality, so comparing a ledger against a cloned snapshot reports a difference even when its accounting is unchanged. Preserve the fresh identity, but remove PartialEq/Eq from BlockComputationBudget and compare identities and counters explicitly in tests, or implement equality consistently with Clone. This is a public API contract issue, not a current consensus failure.

With a per-ledger identity, the derived `PartialEq` made a ledger unequal to
its own clone, which `Clone` and `Eq` together promise not to happen. Two
ledgers with the same counters are still different ledgers, so the type has
no equality; tests compare the counters and the identity explicitly.

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

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Final validation — Phase 2 only (queue backlog)

Verified the supplied findings against head 43e23ec: both prior ledger issues are fixed, and no remaining in-scope findings were identified. The source supports the reviewers' conclusions about active-version pricing, unchanged fee-history generations, and deliberately deferred runtime integration. Regression tests were inspected but not rerun during this verification.

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: normal by gpt-6-astra (effort low) — Although cross-cutting and substantial, the diff adds versioned computation tables, conversion helpers, and an isolated reservation ledger without wiring them into runtime admission or charging, while the protocol-upgrade changes are tests rather than changes to consensus execution.
  • 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 high); agent phase2-reviewer, gpt-6-astra — rust-quality (completed, effort high); agent phase2-reviewer, gpt-6-astra — security-auditor (completed, effort high); agent phase2-reviewer

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.

2 participants