feat(platform)!: add the IdentityKeyLimitsUpdate state transition - #4807
QuantumExplorer wants to merge 3 commits into
Conversation
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>
|
Important Review skippedToo many files! This PR contains 120 files, which is 20 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (120)
You can disable this status message by setting the Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
📖 Book Preview built successfully. Download the preview from the workflow artifacts. Updated at 2026-09-17T19:29:48.872Z |
PR HygieneState: waiting-bots · commit
Self-review is an author attestation that you have read the diff: This report does not bypass CI or repository protection rules. |
|
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## v4.2-dev #4807 +/- ##
============================================
+ Coverage 77.43% 79.64% +2.21%
============================================
Files 2944 2963 +19
Lines 424764 424934 +170
============================================
+ Hits 328916 338439 +9523
+ Misses 95848 86495 -9353
🚀 New features to boost your workflow:
|
QuantumExplorer
left a comment
There was a problem hiding this comment.
I found two remaining issues in the execution-proof and WASM API paths.
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>
|
@coderabbitai review No review for |
|
@thepastaclaw review No review for |
Issue being fixed or feature implemented
#4798 gave AUTHENTICATION keys a
total_budgetand anexpires_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, revision, 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: the client already holds a fresh identity because the transition claims its next revision (asIdentityUpdatedoes), and what the wire carries is exactly what the execution proof shows. "Top up by X" is SDK sugar.security_level_requirementis[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_budgetalways holds, both grow by the same amount, so no overflow is possible).IdentityKeyLimitsUpdateEmptyError; 10537PublicKeyWithLimitsCannotUpdateKeyLimitsErrorrevision == current + 1IdentityPublicKeyLimitNotSetError; new 40221IdentityPublicKeyLimitNotRaisedError; 40219; all paidThe 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,Nonebefore 14): reads the key, sets the limits, rewrites it withreplace_key_in_storage_operationspriced 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), andadd_to_identity_key_budget(new budget slot) raises the remaining budget by the difference. The action emitsUpdateIdentityRevision,UpdateIdentityNonce,UpdateIdentityKeyLimits.IdentityUpdate. The verifier requires the claimed revision and 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:UpdateIdentityKeyLimitsonIdentity(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:IdentityKeyLimitsUpdatewrapper 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 inbook/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_budgeton 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, throughprocess_raw_state_transitions,check_txand 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-sdkoffline suite plus the signing-key selection unit tests (a contract-bound CRITICAL key is skipped),wasm-dpp2unit tests; newwasm-dpp2andjs-evo-sdkspecs added but not run locally (nonode_modulesin this checkout), nor the wasm-sdk functional tests.cargo fmt --all -- --check;cargo clippy ... --all-targets -- -D warningsover dpp, drive, drive-abci, platform-version, dash-sdk, wasm-dpp2, wasm-dpp;cargo clippy -p wasm-sdk --target wasm32-unknown-unknown(only the pre-existingDocumentPropertyTypewarning 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:
For repository code-owners and collaborators only
🤖 Generated with Claude Code