Skip to content

feat(platform)!: add the IdentityKeyLimitsUpdate state transition - #4807

Merged
QuantumExplorer merged 5 commits into
v4.2-devfrom
feat/identity-key-limits-update
Sep 18, 2026
Merged

QuantumExplorer merged 5 commits into
v4.2-devfrom
feat/identity-key-limits-update

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Sep 17, 2026

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

#4798 gave AUTHENTICATION keys a total_budget and an expires_at, and #4802 the query for what is left. A spent or expiring key still had to be replaced: "Changing limits. There is no top-up and no extension. Register a new key."

This adds IdentityKeyLimitsUpdate, a state transition that raises the budget of one of the identity's keys or moves its expiry later.

What was done?

Wire format (rs-dpp)

  • StateTransition::IdentityKeyLimitsUpdate (type 23, appended last), IdentityKeyLimitsUpdateTransitionV0 { identity_id, nonce, key_id, total_budget: Option<Credits>, expires_at: Option<TimestampMillis>, user_fee_increase, signature_public_key_id, signature }. Both limits carry the new absolute value, and what the wire carries is exactly what the execution proof shows; "top up by X" is SDK sugar. No identity revision is claimed or bumped: an identity update needs one because clients allocate key ids, while this transition names an existing key and allocates nothing, so a stale copy of the identity cannot make it collide.
  • security_level_requirement is [MASTER, CRITICAL]; a CRITICAL signer must carry no limits itself (checked against state, see below). Active from protocol version 14 (active_version_range, IDENTITY_KEY_LIMITS_UPDATE_INITIAL_PROTOCOL_VERSION).
  • IdentityPublicKeySettersV1 (set_total_budget, set_expires_at).

Rules. An update only ever loosens: a budget can grow, an expiry can move later, a limit the key does not have cannot be added; tightening is what disabling is for. This keeps the arithmetic safe (remaining <= total_budget always holds, both grow by the same amount, so no overflow is possible).

Stage Rule Error
basic structure at least one field set; a set budget is not zero new 10539 IdentityKeyLimitsUpdateEmptyError; 10537
identity signature v1 (PV14, in place) signer has no limits, so a key can never top itself up; refused before the remaining budget is read new 20017 PublicKeyWithLimitsCannotUpdateKeyLimitsError
state key exists and is enabled; has each limit being raised; each new value is greater; the key is not expired afterwards (an expired key may be revived by an extension, not topped up while it stays expired) 40209 / 40208; new 40220 IdentityPublicKeyLimitNotSetError; new 40221 IdentityPublicKeyLimitNotRaisedError; 40219; all paid

The minimum fee reuses state_transition_min_fees.identity_update (same shape of work), so the shipped fee tables are untouched.

Drive

  • update_identity_key_limits (identity update methods v2, None before 14): the key is read once, in transform_into_action (so the mempool answers a bad target key with consensus codes), and carried in the action; Drive sets the limits on that copy, rewrites it with replace_key_in_storage_operations priced by the byte delta (a bigger varint, or an expiry that was absent), refreshes the key references (they carry the key's value hash, as the disable path does), and add_to_identity_key_budget (new budget slot) raises the remaining budget by the difference. The action emits UpdateIdentityNonce, UpdateIdentityKeyLimits.
  • Proof: the rewritten key. The verifier requires the key holding exactly the requested values. That authenticates the resulting state, not this exact transition (the nonce and fee increase are not stored), so the outcome is classified as affected state and the SDKs use the affected-state wait.

Clients

  • rs-sdk: UpdateIdentityKeyLimits on Identity (update_key_limits, top_up_key_budget, extend_key_expiry); the signing key defaults to the first MASTER, else the first CRITICAL key without limits and without contract bounds, that the signer holds. Resolves to the key as stored after the update.
  • wasm-dpp2: IdentityKeyLimitsUpdate wrapper and the dispatch arms (type number 23, nonce, owner id, verifyPublicKey).
  • wasm-sdk: identityUpdateKeyLimits({ identity, keyId, addBudget?, expiresAt?, signer, settings? }); JS speaks "add", the wasm layer computes the total from the identity's key. js-evo-sdk: identities.updateKeyLimits.
  • wasm-dpp (legacy): the factory refuses the type with a message; the three new errors are mapped.

Docs: a "Raising Limits" section (rules, decision diagram, Drive, proof) in book/src/data-model/key-limits.md, the protocol reference, the enum listings in book/src/state-transitions/lifecycle.md (which also stopped at type 14), the evo-sdk guide, v14.rs.

Not included: Swift, Kotlin and FFI bindings; strategy-tests generation; lowering or removing limits (disable the key instead).

Note: #4760 on v4.3-dev also claims type 23; whichever lands second renumbers.

How Has This Been Tested?

  • rs-dpp: round trip, every limit field covered by the sig hash and the signature excluded, JSON and value wire shapes, the type tables, frozen error discriminants (17, 107, 108). cargo test -p dpp --all-features --lib -- identity_key_limits_update state_transition_types state_error signature_error umbrella (68 pass).
  • rs-drive (update_identity_key_limits): total and remaining growing by the same amount (with prior spending), expiry alone, estimate at least the actual rewrite, the whole database hash-consistent after the reference refresh (visualize_verify_grovedb), add_to_identity_key_budget on a budgeted key only, protocol version 13 inactive. cargo test -p drive --lib -- update_identity_key_limits (6 pass).
  • rs-drive-abci (identity_key_limits_update/tests.rs, through process_raw_state_transitions, check_tx and the proof): a spent key topped up and admitted again; an expired key revived by an extension and refused while it stays expired; each refusal pinned to its code (paid ones bump the nonce); MASTER and unlimited CRITICAL accepted, limited (20017) and HIGH refused, the builder refusing HIGH up front; mempool admission; proof round trip (affected state) with a wrong total not verifying; protocol version 13 refused at decode. cargo test -p drive-abci --lib -- identity_key_limits_update (13 pass).
  • dash-sdk offline suite plus the signing-key selection unit tests (a contract-bound CRITICAL key is skipped), wasm-dpp2 unit tests; new wasm-dpp2 and js-evo-sdk specs added but not run locally (no node_modules in this checkout), nor the wasm-sdk functional tests.
  • cargo fmt --all -- --check; cargo clippy ... --all-targets -- -D warnings over dpp, drive, drive-abci, platform-version, dash-sdk, wasm-dpp2, wasm-dpp; cargo clippy -p wasm-sdk --target wasm32-unknown-unknown (only the pre-existing DocumentPropertyType warning of v4.2-dev); cargo check --workspace --all-targets; cargo check -p drive --no-default-features --features verify.

Breaking Changes

Consensus-breaking, gated to protocol version 14: a new state transition type, new consensus errors, a rewrite of a stored key. Nothing changes for protocol versions up to 13.

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

QuantumExplorer and others added 2 commits September 18, 2026 00:46
A MASTER key, or a CRITICAL authentication key without limits, can raise the
total budget of one of the identity's keys (the remaining budget grows by the
same amount) or move its expiry later. An update only ever loosens limits.
State transition type 23, gated to protocol version 14; the identity revision
is bumped and the proof of execution binds the rewritten key.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Adds Identity::update_key_limits, top_up_key_budget and extend_key_expiry to
the Rust SDK, the IdentityKeyLimitsUpdate wrapper to wasm-dpp2,
identityUpdateKeyLimits to wasm-sdk and identities.updateKeyLimits to
js-evo-sdk, and documents the transition in the book and the protocol
reference.

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

coderabbitai Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 121 files, which is 21 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 22242845-7b77-4fdf-91de-a55e94d36947

📥 Commits

Reviewing files that changed from the base of the PR and between a14ace2 and e78b395.

📒 Files selected for processing (121)
  • book/src/data-model/key-limits.md
  • book/src/evo-sdk/state-transitions.md
  • book/src/state-transitions/lifecycle.md
  • docs/protocol/authentication-key-limits.md
  • packages/js-evo-sdk/src/identities/facade.ts
  • packages/js-evo-sdk/src/state-transitions/facade.ts
  • packages/js-evo-sdk/tests/unit/facades/identities.spec.ts
  • packages/rs-dpp/src/errors/consensus/basic/basic_error.rs
  • packages/rs-dpp/src/errors/consensus/basic/identity/identity_key_limits_update_empty_error.rs
  • packages/rs-dpp/src/errors/consensus/basic/identity/mod.rs
  • packages/rs-dpp/src/errors/consensus/codes.rs
  • packages/rs-dpp/src/errors/consensus/signature/mod.rs
  • packages/rs-dpp/src/errors/consensus/signature/public_key_with_limits_cannot_update_key_limits_error.rs
  • packages/rs-dpp/src/errors/consensus/signature/signature_error.rs
  • packages/rs-dpp/src/errors/consensus/state/identity/identity_public_key_already_expired_error.rs
  • packages/rs-dpp/src/errors/consensus/state/identity/identity_public_key_limit_not_raised_error.rs
  • packages/rs-dpp/src/errors/consensus/state/identity/identity_public_key_limit_not_set_error.rs
  • packages/rs-dpp/src/errors/consensus/state/identity/mod.rs
  • packages/rs-dpp/src/errors/consensus/state/state_error.rs
  • packages/rs-dpp/src/state_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transition_types.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_key_limits_update_transition/accessors/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_key_limits_update_transition/accessors/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_key_limits_update_transition/fields.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_key_limits_update_transition/identity_signed.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_key_limits_update_transition/methods/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_key_limits_update_transition/methods/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_key_limits_update_transition/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_key_limits_update_transition/state_transition_estimated_fee_validation.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_key_limits_update_transition/state_transition_like.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_key_limits_update_transition/v0/identity_signed.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_key_limits_update_transition/v0/mod.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_key_limits_update_transition/v0/state_transition_like.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_key_limits_update_transition/v0/types.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_key_limits_update_transition/v0/v0_methods.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_key_limits_update_transition/v0/version.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/identity_key_limits_update_transition/version.rs
  • packages/rs-dpp/src/state_transition/state_transitions/identity/mod.rs
  • packages/rs-dpp/src/state_transition/traits/state_transition_like.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/common/validate_state_transition_identity_signed/v1/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/processor/traits/address_balances_and_nonces.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/processor/traits/address_witnesses.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/processor/traits/addresses_minimum_balance.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/processor/traits/basic_structure.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/processor/traits/identity_balance.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/processor/traits/identity_based_signature.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/processor/traits/identity_nonces.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/processor/traits/is_allowed.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/processor/traits/state.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_key_limits_update/basic_structure/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_key_limits_update/basic_structure/v0/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_key_limits_update/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_key_limits_update/nonce/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_key_limits_update/nonce/v0/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_key_limits_update/state/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_key_limits_update/state/v0/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_key_limits_update/tests.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/transformer/mod.rs
  • packages/rs-drive-abci/tests/strategy_tests/verify_state_transitions.rs
  • packages/rs-drive/src/drive/identity/key/budget/add_to_identity_key_budget/mod.rs
  • packages/rs-drive/src/drive/identity/key/budget/add_to_identity_key_budget/v0/mod.rs
  • packages/rs-drive/src/drive/identity/key/budget/mod.rs
  • packages/rs-drive/src/drive/identity/key/insert/replace_key_in_storage/v0/mod.rs
  • packages/rs-drive/src/drive/identity/update/methods/mod.rs
  • packages/rs-drive/src/drive/identity/update/methods/update_identity_key_limits/mod.rs
  • packages/rs-drive/src/drive/identity/update/methods/update_identity_key_limits/v0/mod.rs
  • packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs
  • packages/rs-drive/src/state_transition_action/action_convert_to_operations/identity/identity_key_limits_update_transition.rs
  • packages/rs-drive/src/state_transition_action/action_convert_to_operations/identity/mod.rs
  • packages/rs-drive/src/state_transition_action/action_convert_to_operations/mod.rs
  • packages/rs-drive/src/state_transition_action/identity/identity_key_limits_update/mod.rs
  • packages/rs-drive/src/state_transition_action/identity/identity_key_limits_update/transformer.rs
  • packages/rs-drive/src/state_transition_action/identity/identity_key_limits_update/v0/mod.rs
  • packages/rs-drive/src/state_transition_action/identity/identity_key_limits_update/v0/transformer.rs
  • packages/rs-drive/src/state_transition_action/identity/mod.rs
  • packages/rs-drive/src/state_transition_action/mod.rs
  • packages/rs-drive/src/state_transition_action/system/bump_identity_nonce_action/transformer.rs
  • packages/rs-drive/src/state_transition_action/system/bump_identity_nonce_action/v0/transformer.rs
  • packages/rs-drive/src/util/batch/drive_op_batch/identity.rs
  • packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_state_transition_serialization_versions/mod.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_state_transition_serialization_versions/v1.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_state_transition_serialization_versions/v2.rs
  • packages/rs-platform-version/src/version/dpp_versions/dpp_state_transition_serialization_versions/v3.rs
  • packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/mod.rs
  • packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v1.rs
  • packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v10.rs
  • packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v2.rs
  • packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v3.rs
  • packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v4.rs
  • packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v5.rs
  • packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v6.rs
  • packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v7.rs
  • packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v8.rs
  • packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_validation_versions/v9.rs
  • packages/rs-platform-version/src/version/drive_versions/drive_identity_method_versions/mod.rs
  • packages/rs-platform-version/src/version/drive_versions/drive_identity_method_versions/v1.rs
  • packages/rs-platform-version/src/version/drive_versions/drive_identity_method_versions/v2.rs
  • packages/rs-platform-version/src/version/drive_versions/drive_state_transition_method_versions/mod.rs
  • packages/rs-platform-version/src/version/drive_versions/drive_state_transition_method_versions/v1.rs
  • packages/rs-platform-version/src/version/drive_versions/drive_state_transition_method_versions/v2.rs
  • packages/rs-platform-version/src/version/drive_versions/drive_state_transition_method_versions/v3.rs
  • packages/rs-platform-version/src/version/drive_versions/drive_state_transition_method_versions/v4.rs
  • packages/rs-platform-version/src/version/feature_initial_protocol_versions.rs
  • packages/rs-platform-version/src/version/v14.rs
  • packages/rs-sdk/src/platform/transition.rs
  • packages/rs-sdk/src/platform/transition/broadcast.rs
  • packages/rs-sdk/src/platform/transition/update_identity_key_limits.rs
  • packages/wasm-dpp/src/errors/consensus/consensus_error.rs
  • packages/wasm-dpp/src/state_transition/state_transition_factory.rs
  • packages/wasm-dpp2/src/identity/mod.rs
  • packages/wasm-dpp2/src/identity/public_key.rs
  • packages/wasm-dpp2/src/identity/transitions/key_limits_update_transition.rs
  • packages/wasm-dpp2/src/identity/transitions/mod.rs
  • packages/wasm-dpp2/src/lib.rs
  • packages/wasm-dpp2/src/state_transitions/base/state_transition.rs
  • packages/wasm-dpp2/tests/unit/IdentityKeyLimitsUpdateTransition.spec.ts
  • packages/wasm-sdk/README.md
  • packages/wasm-sdk/src/state_transitions/broadcast.rs
  • packages/wasm-sdk/src/state_transitions/identity.rs

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


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

❤️ Share

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

@github-actions github-actions Bot added this to the v4.2.0 milestone Sep 17, 2026
@github-actions

github-actions Bot commented Sep 17, 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-18T09:56:35.386Z

@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

PR Hygiene

State: waiting-bots · commit e78b395c903ef6231c40c4b75dc270dafd456306

  • 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 17, 2026

Copy link
Copy Markdown
Collaborator

⚠️ DEGRADED — Queued for automated review — 37th in line, estimated start in ~14 h (commit e78b395)
Estimated review time once started: ~45 min (two-phase automated review; median of recent runs).
The primary review models are currently out of quota; this review will run on stand-in models and be marked as degraded.

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

@codecov

codecov Bot commented Sep 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.64368% with 172 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.77%. Comparing base (c96ff32) to head (e78b395).
⚠️ Report is 2 commits behind head on v4.2-dev.

Files with missing lines Patch % Lines
...pdate/methods/update_identity_key_limits/v0/mod.rs 79.14% 34 Missing ⚠️
..._action/identity/identity_key_limits_update/mod.rs 14.28% 24 Missing ⚠️
...tate_transitions/identity_key_limits_update/mod.rs 74.39% 21 Missing ⚠️
...y/update/methods/update_identity_key_limits/mod.rs 93.62% 16 Missing ⚠️
...ntity/identity_key_limits_update_transition/mod.rs 92.54% 12 Missing ⚠️
...state_transition_was_executed_with_proof/v0/mod.rs 73.80% 11 Missing ⚠️
...ransitions/identity_key_limits_update/nonce/mod.rs 70.00% 9 Missing ⚠️
...ntity/key/budget/add_to_identity_key_budget/mod.rs 70.96% 9 Missing ⚠️
...on/state_transition/processor/traits/is_allowed.rs 33.33% 8 Missing ⚠️
...sitions/identity_key_limits_update/nonce/v0/mod.rs 78.57% 6 Missing ⚠️
... and 8 more
Additional details and impacted files
@@             Coverage Diff              @@
##           v4.2-dev    #4807      +/-   ##
============================================
+ Coverage     77.43%   79.77%   +2.33%     
============================================
  Files          2944     2962      +18     
  Lines        424764   424638     -126     
============================================
+ Hits         328916   338740    +9824     
+ Misses        95848    85898    -9950     
Components Coverage Δ
dpp 78.31% <93.67%> (+1.98%) ⬆️
drive 80.77% <85.46%> (+1.68%) ⬆️
drive-abci 80.89% <84.76%> (+3.92%) ⬆️
sdk ∅ <ø> (∅)
dapi-client ∅ <ø> (∅)
platform-version ∅ <ø> (∅)
platform-value 87.13% <ø> (+0.53%) ⬆️
platform-wallet ∅ <ø> (∅)
drive-proof-verifier 29.31% <ø> (+2.23%) ⬆️
🚀 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.

@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.

I found two remaining issues in the execution-proof and WASM API paths.

Comment thread packages/wasm-dpp2/src/identity/transitions/key_limits_update_transition.rs Outdated
The proof of a key limits update shows the resulting key and revision, not
the nonce, so it is classified as affected state and the SDKs wait for it as
such. The Rust SDK skips contract-bound CRITICAL keys when it picks a signer.
The wasm-dpp2 wrapper reads an undefined userFeeIncrease as the default.

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

Copy link
Copy Markdown
Contributor

@coderabbitai review

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

@github-actions

Copy link
Copy Markdown
Contributor

@thepastaclaw review

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

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

⚠️ DEGRADED — Final validation — Phase 1 + Phase 2

⚠️ DEGRADED review. The primary review models were unavailable (gpt-6-astra unavailable: All credentials for model gpt-6-astra are cooling down (last error: usage_limit_reached: The usage limit has been reache), so this review ran on stand-in models: gpt-5.6-lunamuse-spark-1.3-contributor, gpt-5.6-solmuse-spark-1.3-contributor, gpt-5.6-terramuse-spark-1.3-contributor, gpt-6-astramuse-spark-1.3-contributor. Both review phases and the independent verifiers still ran, but on weaker models, with Phase 1 capped at high effort. Treat the verdict as provisional; a full-strength re-review will run on the next push once the primary models are back.

New IdentityKeyLimitsUpdate transition is correctly wired across DPP, Drive, Drive-ABCI, and SDKs with no blocking defects. Three in-scope suggestions remain: a vacuous mempool assertion in the drive-abci test, a missing zero-amount guard in the SDK top-up path that would burn fees, and a silent saturating_sub in the Drive rewrite that should fail loudly.

🟡 3 suggestion(s)

Review provenance

Source: reviewer 1: gemini-3.8-flash-high (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: ffi-engineer); reviewer 4: muse-spark-1.3-contributor (agent: phase1-reviewer, role: platform-versioning); reviewer 5: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 6: muse-spark-1.3-contributor (agent: phase1-reviewer, role: security-auditor); reviewer 7: muse-spark-1.3-contributor (standing in for gpt-6-astra) (agent: phase2-reviewer, role: general); reviewer 8: muse-spark-1.3-contributor (standing in for gpt-6-astra) (agent: phase2-reviewer, role: architecture-layering); reviewer 9: muse-spark-1.3-contributor (standing in for gpt-6-astra) (agent: phase2-reviewer, role: ffi-engineer); reviewer 10: muse-spark-1.3-contributor (standing in for gpt-6-astra) (agent: phase2-reviewer, role: platform-versioning); reviewer 11: muse-spark-1.3-contributor (standing in for gpt-6-astra) (agent: phase2-reviewer, role: rust-quality); reviewer 12: muse-spark-1.3-contributor (standing in for gpt-6-astra) (agent: phase2-reviewer, role: security-auditor); final verifier: muse-spark-1.3-contributor (standing in for gpt-6-astra) (agent: astra-verifier, role: final-verifier)

  • Degraded mode: gpt-6-astra unavailable: All credentials for model gpt-6-astra are cooling down (last error: usage_limit_reached: The usage limit has been reache (detected by probe, since 2026-09-18T05:22:01Z); stand-ins gpt-5.6-lunamuse-spark-1.3-contributor, gpt-5.6-solmuse-spark-1.3-contributor, gpt-5.6-terramuse-spark-1.3-contributor, gpt-6-astramuse-spark-1.3-contributor; Phase 1 effort capped at high
  • Triage: critical by muse-spark-1.3-contributor (standing in for gpt-6-astra) (effort low) — Large 120-file (+5349) change adding consensus-critical IdentityKeyLimitsUpdate validation and key-budget execution, e.g. packages/rs-drive/src/drive/identity/update/methods/update_identity_key_limits/v0/mod.rs.
  • Phase 1 reviewers: gemini-3.8-flash-high — general (completed, effort high); agent phase1-reviewer, muse-spark-1.3-contributor — architecture-layering (completed, effort high); agent phase1-reviewer, muse-spark-1.3-contributor — ffi-engineer (completed, effort high); agent phase1-reviewer, muse-spark-1.3-contributor — platform-versioning (completed, effort high); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort high); agent phase1-reviewer, muse-spark-1.3-contributor — security-auditor (completed, effort high); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (lane failed), glm-5.3-flash (zai below 15% reserve: 5h 99% left, weekly 14% left)
  • Fresh verifier: muse-spark-1.3-contributor (standing in for gpt-6-astra) — final-verifier; agent astra-verifier
  • Phase 2 reviewers: muse-spark-1.3-contributor (standing in for gpt-6-astra) — general (completed, effort xhigh); agent phase2-reviewer, muse-spark-1.3-contributor (standing in for gpt-6-astra) — architecture-layering (completed, effort xhigh); agent phase2-reviewer, muse-spark-1.3-contributor (standing in for gpt-6-astra) — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, muse-spark-1.3-contributor (standing in for gpt-6-astra) — platform-versioning (completed, effort xhigh); agent phase2-reviewer, muse-spark-1.3-contributor (standing in for gpt-6-astra) — rust-quality (completed, effort xhigh); agent phase2-reviewer, muse-spark-1.3-contributor (standing in for 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/validation/state_transition/state_transitions/identity_key_limits_update/tests.rs`:
- [SUGGESTION] packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_key_limits_update/tests.rs:358-368: Mempool admission assertion passes vacuously for a limited signer
  Lines 358-368 build an IdentityKeyLimitsUpdate signed by LIMITED_KEY_ID (key 2), which carries limits, and assert check_tx contains no 20015 (budget exhausted). But validate_state_transition_identity_signed_v1 refuses any limited signer of this transition with 20017 before the remaining budget is ever read. So check_tx returns 20017, the `all(code != 20015)` assertion passes trivially, and the comment's claim that the key is usable again is unproven. The Drive-level budget assertions on lines 344-351 already pin the top-up; prove mempool usability with a transition type limited keys may sign (e.g. Batch) or remove these lines.

In `packages/rs-sdk/src/platform/transition/update_identity_key_limits.rs`:
- [SUGGESTION] packages/rs-sdk/src/platform/transition/update_identity_key_limits.rs:149-153: top_up_key_budget with amount 0 guarantees a paid on-chain failure
  With amount == 0, total_budget equals the current budget, which state validation rejects with 40221 (value not raised). Because that failure lands after the nonce check it is paid: the identity nonce bumps and fees are forfeited for a transition that could never succeed. The repo already guards this class client-side (shield_from_identity rejects amount == 0). Reject 0 up front in top_up_key_budget (and addBudget == Some(0) in the wasm adapter) to save a round trip and protect users from accidental fee burn.

In `packages/rs-drive/src/drive/identity/update/methods/update_identity_key_limits/v0/mod.rs`:
- [SUGGESTION] packages/rs-drive/src/drive/identity/update/methods/update_identity_key_limits/v0/mod.rs:179-184: saturating_sub silently accepts a lowered total budget
  Consensus state validation guarantees the new total exceeds the stored one, but this method rewrites the key first (lines 140-167) and only then adjusts the remaining budget. With saturating_sub, a direct-Drive caller passing a total below the stored value gets added = 0, skips the remaining-budget write, and leaves the rewritten lower total alongside the old higher remaining, breaking the remaining <= total invariant the module docs promise. The sibling budget-add path fails loudly with CorruptedDriveState on bad arithmetic; do the same here with checked_sub so a decrease errors instead of coercing into a no-op.
Out-of-scope follow-up suggestions (2)

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.

  • Type-23 collision with #4760 needs merge-order coordination — Within this PR's base (v4.2-dev, last type 22) type 23 is the correct next discriminant, but the PR body notes #4760 on v4.3-dev also claims type 23. Whichever lands second must renumber.
    • Follow-up: Coordinate merge order with #4760; the second landing PR renumbers its new transition type.
  • Transition-specific signer rule lives in shared signature helper — Design preference, not a defect — the new matches!(IdentityKeyLimitsUpdate) branch sits directly beside the pre-existing contract-bounds Batch exception in the same shared v1 helper and follows that established pattern; the guard is safe for all other callers. Extracting a per-transition hook is a broader refactor outside this PR's scope.
    • Follow-up: Consider creating a separate issue or author/maintainer-requested PR for this.

Comment thread packages/rs-sdk/src/platform/transition/update_identity_key_limits.rs Outdated
QuantumExplorer and others added 2 commits September 18, 2026 16:05
…swer the mempool with consensus codes

The target key is read once, in transform_into_action, and carried in the
action as it is stored, so the mempool refuses a version 0 or missing key
with 40220 / 40209 instead of an internal error, the user pays one key read
instead of two, and Drive rewrites the key without reading it again. Drive
refuses a total that is not raised, or a limit the key does not have, as a
corrupted state instead of coercing it with a saturating subtraction.

Also: the proof carries the rewritten key with the revision rather than
every key; the SDKs share raised_key_limits, which refuses a zero top-up,
an expiry that is not later, and a limit the key lacks before signing;
the wasm public key exposes totalBudget and expiresAt; the wasm-dpp2 spec
covers toObject / fromObject / toJSON / fromJSON; the mempool re-admission
test probes with a contract creation signed by the limited key on
committed state; the expired-key error text fits both raise sites;
IDENTITY_TRANSITION_TYPE lists the missing identity types; the docs name
the contract-bounds rule and the smaller proof.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The transition no longer claims the identity's next revision, and the
identity's revision is no longer bumped: an identity update needs the
claim because clients allocate key ids, while this transition names an
existing key and allocates nothing, so a stale copy of the identity cannot
make it collide. Every top-up from a cached identity was a paid 40203
unless the client refetched first; now the client only needs to hold the
signing key, and a total computed from a stale key is refused only when
it no longer raises the stored one.

The advanced structure stage and its version slot go, the signature stage
no longer requests the revision, the action and the Drive operation carry
no revision, and the proof is the rewritten key alone, verified without a
revision pin. The wasm-dpp2 wrapper, its spec, the SDK docs, the book and
the protocol reference follow.

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 9f81413 into v4.2-dev Sep 18, 2026
46 checks passed
@QuantumExplorer
QuantumExplorer deleted the feat/identity-key-limits-update branch September 18, 2026 10:29
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