Skip to content

fix(drive)!: bound pre-programmed distribution amounts and queue a shared release-time tree once - #4837

Merged
QuantumExplorer merged 8 commits into
v4.2-devfrom
claude/zen-hermann-63dfe0
Sep 19, 2026
Merged

QuantumExplorer merged 8 commits into
v4.2-devfrom
claude/zen-hermann-63dfe0

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Sep 19, 2026

Copy link
Copy Markdown
Member

Stacked on #4834 (base claude/hungry-tu-ee9d28, rebased onto its head dc3c53de8b). It needs update_contract v2 from that PR. Retarget to v4.2-dev once #4834 merges.

Issue being fixed or feature implemented

Two pre-programmed token distribution inputs pass every validation and then fail inside Drive as internal errors. An InternalError state transition is never paid for and is stripped from every proposal, so the user just sees the transition vanish.

  1. Unbounded amounts. Each pre-programmed release is stored as a sum tree of its recipients' amounts. An amount above i64::MAX fails in add_pre_programmed_distributions (ProtocolError::Overflow), and amounts that each fit but total more fail in GroveDB (Overflow("sum is overflowing")). No consensus validation bounded either.
  2. Tokens of one contract sharing a release time. add_pre_programmed_distributions v0 looks for the shared [Tokens, distributions, timed, ms] / time tree in state only, so two tokens releasing at the same time queue the same tree twice in one batch.

Testing corrected two assumptions, and the corrections shape the fix:

  • Defect 2 is config-dependent. The batch is refused (GroveDBInsertion("insertion order error")) only when batching_consistency_verification is on. setup_drive_with_initial_state_structure forces it on, but the shipped default is off and dashmate does not override it. With it off GroveDB folds the two identical inserts and the contract is stored. The real hazard is a node with the flag on failing a transition every other node executes.
  • PV13 updates did not accept every over-limit token. apply_contract in fee-estimation mode cannot see the stored contract, so an update's estimation takes the insert path and hits the explicit amount > i64::MAX check. A single over-limit amount was therefore an internal error on update at PV13 too. Only a release whose amounts each fit but total over the limit was accepted (estimation does not sum, and the v1 update wrote no distribution storage).

What was done?

All behaviour changes are selected by protocol version 14 only. Shipped vN modules are untouched.

Amount bound

  • New TokenPreProgrammedDistribution::validate_amounts in rs-dpp (pure-data invariant tier), versioned by contract_versions.token_versions.validate_pre_programmed_distribution_amounts. One rule covers both cases: each release must total at most i64::MAX.
  • New PreProgrammedDistributionAmountOverLimitError, basic code 10277 (next free in the data contract band). Appended after feat(platform)!: once-per-identity token distribution #4827's variant at the BasicError tail and pinned at discriminant 188 in basic_error_tail_discriminants_are_frozen. Mapped in wasm-dpp; added to the book's error code table.
  • Create: called from create basic_structure v2 (PV14-only, extended in place). Unpaid, rejected at check_tx.
  • Update: called from DataContract::validate_update v1 (PV14-only, extended in place), for the tokens the update adds only. Paid. Tokens a contract already has are not judged, so a contract holding a legacy total-overflow token stays updatable. fix(drive)!: create distribution trees for tokens added by contract update #4834 found no such token on mainnet; other networks were not checked.

Shared release time

  • New add_pre_programmed_distributions v1, selected through a new DRIVE_TOKEN_METHOD_VERSIONS_V2 that only DRIVE_VERSION_V9 references. It uses the existing batch_insert_empty_tree_if_not_exists_check_existing_operations so the tree is queued once.
  • Why a new version and not an edit: with the shipped config v1 stores the same state as v0 (equal root hash, pinned by test) but lowers the processing fee by the existence read the later tokens no longer make (4,899,560 to 4,894,760 in the test), and fees are consensus. For a single token the operations are identical to v0.

v14.rs, drive_versions/v9.rs and the new table document both changes.

How Has This Been Tested?

Tests were written first and observed red: at unit level for each defect, and end to end with the three fix sites temporarily disabled (both amount cases failed as InternalError("... distribution amount over i64::Max")), then restored and checksum-verified.

  • rs-dpp: validate_amounts unit tests (limit, single amount over, total over, total not fitting a u64, releases judged independently); validate_update v1 tests for added tokens, carried-over tokens, and the PV13 pin; frozen discriminant test.
  • rs-drive (add_pre_programmed_distribution/v1): insert, estimate, and PV14 update of two tokens sharing a release time, with both releases fetched and claimed; sharing the tree with an earlier contract; PV13 pin; same root hash as v0 with the shipped batching config and a lower processing fee; a release totalling over i64::MAX passes estimation and fails at apply.
  • rs-drive-abci: create basic_structure v2 tests through the dispatcher including the PV13 pin; end-to-end create and update through process_raw_state_transitions for shared release times and both over-limit shapes (UnpaidConsensusError on create, PaidConsensusError on update); two PV13 update pins (total overflow accepted, single amount InternalError).

Targeted runs after rebasing onto dc3c53de8b: cargo test -p platform-version, -p dpp, -p drive, -p drive-abci with name filters (contract create/update, token, pre-programmed), all passing. cargo clippy --all-targets clean on the four crates, cargo fmt --check clean, cargo check -p wasm-dpp passes. Full suites were not run locally.

Breaking Changes

Consensus change, gated to protocol version 14:

  • Contract creates, and tokens added by contract updates, whose pre-programmed release totals more than i64::MAX are rejected with consensus error 10277. On create this input was already an internal error. On update, a total-overflow release was accepted before PV14.
  • The processing fee of storing a contract whose tokens share a pre-programmed release time drops by one existence read per additional token. Stored state is unchanged.

Protocol version 13 behaviour is unchanged and pinned by tests.

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

Summary by CodeRabbit

  • New Features

    • Added validation for pre-programmed token distribution amounts during data contract creation and updates.
    • Releases whose total exceeds the supported limit are now rejected with a dedicated error (code 10277).
    • Improved handling of multiple tokens sharing the same release time, preventing duplicate processing and reducing related overhead.
  • Documentation

    • Documented the new validation behavior and protocol version 14 changes.

QuantumExplorer and others added 6 commits September 19, 2026 06:41
…pdate

A token added through a data contract update never got its perpetual or
pre-programmed distribution storage: update_contract v1 only creates the
token's balance, identity info, status, contract info and supply entries,
while insert_contract v1 also calls add_perpetual_distribution and
add_pre_programmed_distributions. A claim on such a token read the missing
last-claim path as None and then failed while applying (the last-claim
insert for perpetual, the scheduled-reference delete for pre-programmed),
so it became an InternalError, was stripped from every proposal, and the
distribution was unclaimable.

- update_contract v2, selected by protocol version 14 only
  (DRIVE_CONTRACT_METHOD_VERSIONS_V4 amended in place), creates the
  storage for tokens absent from the original contract. v1 is unchanged
  outside its tests, which are now pinned to protocol version 13.
- transition_to_version_14 backfills the storage of tokens added by
  update before the upgrade
  (Drive::add_missing_token_distribution_storage_to_all_contracts). It is
  idempotent, runs inside the block transaction, and skips a
  pre-programmed distribution whose amounts no sum tree can hold instead
  of failing the upgrade block.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Brings in #4827 (once-per-identity token distribution).

Textual conflict in transition_to_version_14: both sides appended a step.
Kept both, the token history contract v2 and the once-per-identity root
tree first, then the distribution storage backfill.

Semantic conflict git merged silently: #4827 created the once-per-identity
claims subtree of a token added by update inside update_contract v1, but
protocol version 14 selects v2 since this branch, so on the merged tree
such a token got no subtree and
test_update_contract_v1_adds_token_with_once_per_identity_distribution
failed. The block and its test move into v2, inside the same
absent-from-the-original-contract gate as the perpetual and pre-programmed
storage, and v1 returns to its shipped body with its tests pinned to
protocol version 13.

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

The first block of protocol version 14 walked every contract to create the
distribution storage of tokens that an update had added before the
upgrade. Mainnet has no such token, checked at block 436796:

- all 20 data contract updates ever broadcast (12 succeeded) decode to a
  contract without tokens, and an update that adds a token has to carry it
- the 5 tokens on mainnet all belong to contracts still at version 1, so
  none was ever touched by a contract update or a token config update, and
  none has a perpetual or pre-programmed distribution

So the walk could only ever be a no-op there, and it was the riskiest part
of the change: state rewritten on the upgrade block, where an error halts
the chain. transition_to_version_14 and the migration module are back to
what v4.2-dev has. update_contract v2 is unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
v2 was a full copy of v1 with the distribution storage added. It now calls
update_contract_operations_v1 and appends the perpetual and pre-programmed
storage of the tokens the update adds, the way v1 builds on
update_contract_operations_v0.

v1 is back to what v4.2-dev has, apart from the visibility of that one
function. That includes the once-per-identity claims subtree #4827 put
there, which v2 now gets by delegation instead of carrying its own copy,
and v1's tests, which run through v2 at the latest protocol version and so
need neither pinning nor duplicating.

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

#4827 created the claims subtree of a token added by update inside
update_contract v1, the generation protocol version 14 selected at the
time. The once-per-identity kind is new in protocol version 14 and that
version selects v2 now, so the block and its test move there, next to the
perpetual and pre-programmed storage and inside the same
absent-from-the-original-contract gate.

v1 is back to what it was before #4827, apart from the visibility of
update_contract_operations_v1.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ared release-time tree once

Two pre-programmed token distribution inputs passed every validation and then
failed inside Drive as internal errors, which are never paid for and are
stripped from every proposal. Both fixes are gated to protocol version 14.

Amounts. A release is stored as a sum tree of its recipients' amounts, so an
amount above i64::MAX, or amounts totalling more, can not be written. The new
TokenPreProgrammedDistribution::validate_amounts (rs-dpp) rejects such a
release with PreProgrammedDistributionAmountOverLimitError (basic, 10277):
- on contract create, in basic_structure v2 (unpaid, caught at check_tx);
- on contract update, in DataContract::validate_update v1, for the tokens the
  update adds only (paid).
What protocol version 13 did, verified by test: a single amount over the limit
was an internal error on create AND on update, because the fee estimation of
an update takes the contract insert path. Only a release whose amounts each
fit but total more was accepted by an update, since the estimation does not
sum and the v1 update wrote no distribution storage. A network may hold such
a token, so tokens a contract already has are not judged and the contract
stays updatable.

Shared release time. add_pre_programmed_distributions v0 looked for the
release-time tree all tokens share in state only, so two tokens of one
contract releasing at the same time queued it twice in one batch. That is
refused ("insertion order error") only by a Drive with
batching_consistency_verification on, which rs-drive tests force and shipped
nodes leave off; with it off GroveDB folds the identical inserts and the
contract is stored. v1 also looks among the gathered operations. It stores
the same state (equal root hash, pinned by test) and lowers the processing
fee by the existence read the later tokens no longer make, hence a new
version (DRIVE_TOKEN_METHOD_VERSIONS_V2, selected by DRIVE_VERSION_V9 only)
rather than an edit of v0.

The new BasicError variant goes after #4827's at the enum tail and is pinned
at discriminant 188 in basic_error_tail_discriminants_are_frozen.

Stacked on #4834 (update_contract v2).

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

coderabbitai Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

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

Review profile: CHILL

Plan: Advanced

Run ID: 5dad6feb-b37d-4292-88aa-23eb23f62ea0

📥 Commits

Reviewing files that changed from the base of the PR and between cb9a797 and 814e219.

📒 Files selected for processing (27)
  • book/src/error-handling/error-codes.md
  • packages/rs-dpp/src/data_contract/associated_token/token_pre_programmed_distribution/methods/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_pre_programmed_distribution/methods/validate_amounts/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_pre_programmed_distribution/methods/validate_amounts/v0/mod.rs
  • packages/rs-dpp/src/data_contract/associated_token/token_pre_programmed_distribution/mod.rs
  • packages/rs-dpp/src/data_contract/methods/validate_update/v1/mod.rs
  • packages/rs-dpp/src/errors/consensus/basic/basic_error.rs
  • packages/rs-dpp/src/errors/consensus/basic/data_contract/mod.rs
  • packages/rs-dpp/src/errors/consensus/basic/data_contract/pre_programmed_distribution_amount_over_limit_error.rs
  • packages/rs-dpp/src/errors/consensus/codes.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_create/basic_structure/v2/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_create/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_update/mod.rs
  • packages/rs-drive/src/drive/tokens/distribution/add_pre_programmed_distribution/mod.rs
  • packages/rs-drive/src/drive/tokens/distribution/add_pre_programmed_distribution/v1/mod.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/mod.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v1.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v2.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v3.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v4.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v5.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/v6.rs
  • packages/rs-platform-version/src/version/drive_versions/drive_token_method_versions/mod.rs
  • packages/rs-platform-version/src/version/drive_versions/drive_token_method_versions/v2.rs
  • packages/rs-platform-version/src/version/drive_versions/v9.rs
  • packages/rs-platform-version/src/version/v14.rs
  • packages/wasm-dpp/src/errors/consensus/consensus_error.rs
 _______________________________________
< Uniformly random comments incoming... >
 ---------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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 19, 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-19T13:16:21.288Z

Base automatically changed from claude/hungry-tu-ee9d28 to v4.2-dev September 19, 2026 12:49
@github-actions github-actions Bot added this to the v4.2.0 milestone Sep 19, 2026
@github-actions

github-actions Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

PR Hygiene

State: waiting-bots · commit b9bc374e54790d538df5eba571f6602618135217

  • coderabbitai has not reported for the current head
  • thepastaclaw has not reported for the current head

Self-review is an author attestation that you have read the diff:
/self-reviewed — covers everything pushed so far; post it again after a new push.

This report does not bypass CI or repository protection rules.

@github-actions

Copy link
Copy Markdown
Contributor

PR Hygiene

State: waiting-bots · commit d1aff2ad72d1446ada5b97ad5dd84285be06a5a6

  • coderabbitai has not reported for the current head
  • thepastaclaw has not reported for the current head

Self-review is an author attestation that you have read the diff:
/self-reviewed — covers everything pushed so far; post it again after a new push.

This report does not bypass CI or repository protection rules.

@thepastaclaw

thepastaclaw commented Sep 19, 2026

Copy link
Copy Markdown
Collaborator

🕓 Queued for automated review — 6th in line, estimated start in ~35 min (commit b9bc374)
Estimated review time once started: ~15 min (two-phase automated review; median of recent runs).

  • Request priority review — click to move this review to the front of the queue.

QuantumExplorer and others added 2 commits September 19, 2026 19:54
Brings in #4838. v14.rs: #4826 and #4828 each added an item to the protocol
version 14 list after this PR numbered its own 11 and 12, leaving two 11s and
two 12s. The shipped items keep 11 and 12; this PR's follow as 13 and 14.
Comments only.

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

@QuantumExplorer QuantumExplorer left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Reviewed

@QuantumExplorer
QuantumExplorer merged commit fa48a8d into v4.2-dev Sep 19, 2026
7 checks passed
@QuantumExplorer
QuantumExplorer deleted the claude/zen-hermann-63dfe0 branch September 19, 2026 13:20
QuantumExplorer added a commit that referenced this pull request Sep 19, 2026
Conflict in the data contract update tests: this branch and #4837 both
appended a helper and its tests at the end of `token_tests`. Both blocks
are kept whole, #4837's first.

The protocol version 14 changelog merged cleanly but with two entries
numbered 13, since #4837 added 13 and 14. The entry for tokens added by a
contract update becomes 15.

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

codecov Bot commented Sep 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.77159% with 117 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.58%. Comparing base (c92a176) to head (b9bc374).
⚠️ Report is 1 commits behind head on v4.2-dev.

Files with missing lines Patch % Lines
...tion/state_transitions/data_contract_update/mod.rs 78.57% 36 Missing ⚠️
...tion/state_transitions/data_contract_create/mod.rs 81.81% 30 Missing ⚠️
...rc/data_contract/methods/validate_update/v1/mod.rs 85.71% 19 Missing ⚠️
...ons/data_contract_create/basic_structure/v2/mod.rs 86.53% 14 Missing ⚠️
...ribution/add_pre_programmed_distribution/v1/mod.rs 96.67% 12 Missing ⚠️
...ammed_distribution/methods/validate_amounts/mod.rs 70.58% 5 Missing ⚠️
...istribution/add_pre_programmed_distribution/mod.rs 90.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           v4.2-dev    #4837      +/-   ##
============================================
- Coverage     88.02%   78.58%   -9.45%     
============================================
  Files          2977     2981       +4     
  Lines        388320   436083   +47763     
============================================
+ Hits         341836   342682     +846     
- Misses        46484    93401   +46917     
Components Coverage Δ
dpp 76.28% <89.74%> (-13.62%) ⬇️
drive 78.95% <96.49%> (-8.26%) ⬇️
drive-abci 81.22% <81.69%> (-8.05%) ⬇️
sdk ∅ <ø> (∅)
dapi-client ∅ <ø> (∅)
platform-version ∅ <ø> (∅)
platform-value 86.67% <ø> (-6.31%) ⬇️
platform-wallet ∅ <ø> (∅)
drive-proof-verifier 29.51% <ø> (-8.28%) ⬇️
🚀 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.

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