feat(platform)!: delta-based data contract update transition for protocol version 15 - #4730
QuantumExplorer wants to merge 7 commits into
Conversation
…ocol version 15 A V0 data contract update re-sends the whole contract even for a one-keyword change. Protocol version 15 adds DataContractUpdateTransitionV1, which carries only the delta: new and updated document schemas and shared definitions, new groups and tokens, added and removed keywords, an optional config and a tri-state description change, keyed by the contract id, the owner and the new version. Validation merges the delta onto the stored contract (DataContract::apply_update, new contract method version slot) and then holds the merged contract to exactly the checks a full-contract update gets: the generation-1 update rules, the identities new groups and tokens name, external token costs and reference declarations. The delta shape itself adds three checks with their own consensus errors: the submitter must own the contract, updated entries must exist and new entries must not (DataContractUpdateEntryNotFoundError 40011, DataContractUpdateEntryAlreadyExistsError 40010), and the sections of one transition must not overlap (DataContractUpdateOverlappingEntriesError 10277). A missing contract is a consensus error with a nonce bump, never an execution error. Drive gains a V1 update action that carries the delta's registration cost, so a delta pays only for what it adds where a full-contract update pays for the whole contract again. Proving fetches the stored contract to learn whether it keeps history; verification checks every field of the delta against the proven contract. Protocol version 15 is introduced here as the first consensus change on the 4.3 line: v15.rs, DRIVE_ABCI_VALIDATION_VERSIONS_V11 (contract update basic structure 2, state 2, transform_into_action 1) and STATE_TRANSITION_SERIALIZATION_VERSIONS_V4 (the V1 update joins the wire and becomes the client default). The v10 validation table and the V3 serialization table stay byte-identical for protocol version 14 replay, and a delta reaching a pre-15 validator is an UnsupportedVersionError consensus error. The update accessor now returns the embedded contract as an Option, with a data_contract_id accessor beside it. new_from_data_contract keeps building V0; new_from_contract_update and from_contract_update take the stored and the updated contract and pick the form the platform version defaults to (V1 from protocol version 15). platform-wallet's contract update uses the delta form; wasm-dpp and wasm-dpp2 expose the new variant. Reimplements the update half of PR #3021 from scratch. The create transition V1 from that attempt is deliberately left for a follow-up. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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-18T01:48:58.651Z |
|
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## v4.3-dev #4730 +/- ##
===========================================
Coverage ? 78.34%
===========================================
Files ? 2873
Lines ? 420759
Branches ? 0
===========================================
Hits ? 329643
Misses ? 91116
Partials ? 0
🚀 New features to boost your workflow:
|
thepastaclaw
left a comment
There was a problem hiding this comment.
Final validation — Phase 1 + Phase 2
The delta transition is integrated across the versioned protocol, execution, storage, and client layers, but the new delta representation has correctness gaps. In particular, keyword reordering is silently lost during delta extraction, and execution-proof verification authenticates only the fields mentioned by the delta rather than the complete materialized contract, allowing false-positive verification. The WASM declarations and wallet FFI also do not expose the full V1 delta surface.
🔴 3 blocking | 🟡 2 suggestion(s)
2 finding(s) not shown inline (the lines are not part of this PR's diff)
🟡 Suggestion: Expose the V1 delta shape in the WASM TypeScript declarations
packages/wasm-dpp2/src/data_contract/transitions/update.rs:19-41
The Rust transition crossing this boundary is now the DataContractUpdateTransition enum, which can contain either the V0 full-contract form or the V1 delta form. The generic conversion methods can consequently return V1 values, but DataContractUpdateTransitionObject and DataContractUpdateTransitionJSON still require the V0-only dataContract field and omit the V1 fields such as dataContractId, ownerId, version, delta maps, keyword changes, config, and description. Typed consumers cannot construct valid V1 objects for fromObject/fromJSON, and the declared return type does not describe V1 values returned by those methods. Update the declarations to represent the V0/V1 union and the corresponding JSON/object field types.
source: muse-spark-1.3-contributor (phase1-reviewer: general, ffi-engineer, rust-quality, security-auditor); gpt-6-astra (phase2-reviewer: general, ffi-engineer, rust-quality, security-auditor)
🟡 Suggestion: Provide an FFI representation for clearing the contract description
packages/rs-platform-wallet-ffi/src/data_contract.rs:90-102
V1 uses DescriptionUpdate as a tri-state value: Keep, Clear, or Set. The wallet FFI API exposes description as an optional C string, and its current conversion treats NULL or an empty string as absence/preserve rather than as an explicit clear operation. As a result, C and Swift callers using this API can retain or replace a description but cannot construct the supported Clear delta. Add an explicit clear flag or another unambiguous sentinel while preserving the existing distinction between omitted description and an empty description.
source: muse-spark-1.3-contributor (phase1-reviewer: general, ffi-engineer, rust-quality, security-auditor)
Review provenance
Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: muse-spark-1.3-contributor (agent: phase1-reviewer, role: ffi-engineer); reviewer 3: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 4: muse-spark-1.3-contributor (agent: phase1-reviewer, role: security-auditor); reviewer 5: gpt-6-astra (agent: phase2-reviewer, role: general); reviewer 6: gpt-6-astra (agent: phase2-reviewer, role: ffi-engineer); reviewer 7: gpt-6-astra (agent: phase2-reviewer, role: rust-quality); reviewer 8: gpt-6-astra (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6-astra (agent: astra-verifier, role: final-verifier)
- Triage:
criticalbygpt-6-astra(effort low) — This large, intricate change directly modifies consensus validation in packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_update/state/v2/mod.rs and basic_structure/v2/mod.rs, introducing protocol-v15 delta materialization, ownership and entry checks, nonce handling, and delta-based fee calculation across versioned execution paths. - Phase 1 reviewers:
muse-spark-1.3-contributor— general (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— ffi-engineer (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— rust-quality (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— security-auditor (completed, effort xhigh); agentphase1-reviewer - Phase 1 model:
muse-spark-1.3-contributor— not quota-gated; passed overgemini-3.8-flash-high(antigravity below 15% reserve: weekly 11% left, 5h 100% left),glm-5.3-flash(zai below 15% reserve: 5h 100% left, weekly 13% left) - Fresh verifier:
gpt-6-astra— final-verifier; agentastra-verifier - Phase 2 reviewers:
gpt-6-astra— general (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— ffi-engineer (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— rust-quality (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— security-auditor (completed, effort xhigh); agentphase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `packages/rs-dpp/src/state_transition/state_transitions/contract/data_contract_update_transition/v1/mod.rs`:
- [BLOCKING] packages/rs-dpp/src/state_transition/state_transitions/contract/data_contract_update_transition/v1/mod.rs:272-283: Reject keyword reordering instead of silently dropping it
The delta extractor computes keyword changes only as set differences. If the old contract contains `["one", "two"]` and the new contract contains `["two", "one"]`, both delta lists are empty, so applying the resulting transition retains the old order rather than producing the supplied contract. The same loss of ordering occurs when removals and additions alter the relative order: removals preserve the remaining old order and additions are appended. Keyword order is part of the stored contract representation and existing contract comparison treats the vectors positionally, so this API can successfully produce a transition whose applied contract differs from the caller's contract. Reject reorder-only changes as not expressible, or extend the delta format to encode ordering explicitly.
In `packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs`:
- [BLOCKING] packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs:121-134: Verify the complete materialized contract for V1 update proofs
V0 update proof verification compares the complete proven contract with the contract embedded in the signed transition. The V1 path instead calls `first_mismatch`, which checks only fields named by the delta and intentionally ignores untouched fields. The proof supplies only the resulting contract, and for a non-history contract does not supply the pre-update contract. Therefore a proven contract with the same ID, owner, version, and carried delta values can contain arbitrary changes to untouched configuration, descriptions, document types, definitions, groups, tokens, or keyword ordering and still pass verification. This permits a light client to report that a particular signed delta executed when the authenticated state was produced by a different update. The proof must authenticate the pre-state or otherwise bind and compare the complete materialized result against the signed delta.
In `packages/rs-dpp/src/state_transition/state_transitions/contract/data_contract_update_transition/v1/first_mismatch.rs`:
- [BLOCKING] packages/rs-dpp/src/state_transition/state_transitions/contract/data_contract_update_transition/v1/first_mismatch.rs:44-63: Reject contradictory delta sections during proof verification
The V1 proof verifier checks that entries in `new_document_schemas` and `updated_document_schemas` match the proven contract, but it does not reject the same document type appearing in both sections. The same omission exists for `new_schema_defs` versus `updated_schema_defs`, and duplicate additions/removals are not checked here either. Basic-structure validation rejects these malformed transitions on the normal platform path, but `first_mismatch` is independently used by proof verification and does not run that validation. A malformed signed transition can therefore be rejected by the platform while an authenticated proof of the existing contract still passes `first_mismatch`, causing the client to report successful execution. Proof verification must validate delta shape and reject overlapping or contradictory sections before checking the resulting contract.
In `packages/wasm-dpp2/src/data_contract/transitions/update.rs`:
- [SUGGESTION] packages/wasm-dpp2/src/data_contract/transitions/update.rs:19-41: Expose the V1 delta shape in the WASM TypeScript declarations
The Rust transition crossing this boundary is now the `DataContractUpdateTransition` enum, which can contain either the V0 full-contract form or the V1 delta form. The generic conversion methods can consequently return V1 values, but `DataContractUpdateTransitionObject` and `DataContractUpdateTransitionJSON` still require the V0-only `dataContract` field and omit the V1 fields such as `dataContractId`, `ownerId`, `version`, delta maps, keyword changes, config, and description. Typed consumers cannot construct valid V1 objects for `fromObject`/`fromJSON`, and the declared return type does not describe V1 values returned by those methods. Update the declarations to represent the V0/V1 union and the corresponding JSON/object field types.
In `packages/rs-platform-wallet-ffi/src/data_contract.rs`:
- [SUGGESTION] packages/rs-platform-wallet-ffi/src/data_contract.rs:90-102: Provide an FFI representation for clearing the contract description
V1 uses `DescriptionUpdate` as a tri-state value: `Keep`, `Clear`, or `Set`. The wallet FFI API exposes description as an optional C string, and its current conversion treats NULL or an empty string as absence/preserve rather than as an explicit clear operation. As a result, C and Swift callers using this API can retain or replace a description but cannot construct the supported `Clear` delta. Add an explicit clear flag or another unambiguous sentinel while preserving the existing distinction between omitted description and an empty description.
…oduces Execution-proof verification of a delta-based (V1) contract update checked only the fields the delta named, so a proven state produced by some other update passed as this one. The pure merge now lives outside the validation gate as DataContractUpdateValues::merge_onto (apply_update calls it and then runs the group and token rules only the merged result can show), and the verifier applies the delta to the pre-update contract it takes from the known-contracts provider, requires that contract to sit one version below the delta's, and compares the whole materialized contract with the proven one. A missing or wrong-version known contract is MissingContextRequirement, a delta that does not apply is InvalidTransition, a proven state the delta did not produce is IncorrectProof. platform-wallet registers the pre-update contract with the SDK's context provider before broadcasting and the confirmed one afterwards. The partial first_mismatch is gone. The overlap check is DataContractUpdateTransitionV1::overlapping_entry in dpp, shared by the generation-2 basic structure validator and the verifier, which refuses a malformed delta before touching the proof. from_contract_update recomputes the keywords the delta would produce (stored order minus removals, additions appended) and rejects a reordered keyword list as not expressible instead of silently dropping the reorder. The wasm-dpp2 TypeScript declarations describe the V0/V1 union of the update transition's object and JSON forms. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
v4.3-dev was fast-forwarded to v4.2-dev's tip (cb6514e). The only conflict was in the data contract update test module, where this branch adds the delta processing test and the base adds the contract cache coherence tests (#4755); both are kept. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
thepastaclaw
left a comment
There was a problem hiding this comment.
Re-review — Final validation — Phase 1 + Phase 2
The v15 delta transition and proof materialization changes are correctly integrated, and the previously reported proof-soundness and keyword-order issues are fixed. Three functional gaps remain: the FFI cannot express description clearing, the WASM declarations use the wrong serialized nonce key, and the legacy WASM wrapper cannot expose the target contract ID. The v2 validator also fails to validate an inherited legacy configuration version after materialization, allowing an effective v0 configuration to pass the new v15 path.
🔴 1 blocking | 🟡 5 suggestion(s)
2 finding(s) not shown inline (the lines are not part of this PR's diff)
🟡 Suggestion: Provide an FFI representation for clearing the contract description
packages/rs-platform-wallet-ffi/src/data_contract.rs:90-116
The V1 transition distinguishes DescriptionUpdate::Keep, Clear, and Set, but this C ABI accepts only a nullable string and decodes it with read_optional_str. Both NULL and an empty string become None; downstream None means preserve the existing description, so an FFI caller cannot clear a non-empty description. Add an explicit clear flag or another tagged representation at the ABI boundary and propagate it through the wallet update path.
source: muse-spark-1.3-contributor (phase1-reviewer: general, architecture-layering, ffi-engineer, platform-versioning, rust-quality); gpt-6-astra (phase2-reviewer: general, architecture-layering, ffi-engineer, platform-versioning, rust-quality, security-auditor)
🟡 Suggestion: Expose the V1 target contract ID in the legacy WASM wrapper
packages/wasm-dpp/src/data_contract/state_transition/data_contract_update_transition/mod.rs:115-119
The wrapper can deserialize the new V1 delta transition, but getDataContract() correctly fails because V1 carries no full contract. The wrapper exposes getOwnerId but does not expose the transition's new data_contract_id() accessor, leaving JavaScript callers unable to identify which contract a deserialized delta targets. Add a getDataContractId binding for parity with the Rust accessor and the newer WASM2 surface.
#[wasm_bindgen(js_name=getDataContractId)]
pub fn get_data_contract_id(&self) -> IdentifierWrapper {
self.0.data_contract_id().into()
}
source: gpt-6-astra (phase2-reviewer: ffi-engineer)
Review provenance
Source: reviewer 1: muse-spark-1.3-contributor (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: gpt-6-astra (agent: phase2-reviewer, role: general); reviewer 8: gpt-6-astra (agent: phase2-reviewer, role: architecture-layering); reviewer 9: gpt-6-astra (agent: phase2-reviewer, role: ffi-engineer); reviewer 10: gpt-6-astra (agent: phase2-reviewer, role: platform-versioning); reviewer 11: gpt-6-astra (agent: phase2-reviewer, role: rust-quality); reviewer 12: gpt-6-astra (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6-astra (agent: astra-verifier, role: final-verifier)
- Triage:
criticalbygpt-6-astra(effort low) — This large, intricate diff changes consensus validation and serialization, peer-facing state-transition deserialization, execution-proof verification, storage action transformation, and protocol-version behavior in files such as rs-drive-abci data-contract-update validation and rs-platform-version v15.rs. - Phase 1 reviewers:
muse-spark-1.3-contributor— general (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— architecture-layering (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— ffi-engineer (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— platform-versioning (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— rust-quality (completed, effort xhigh); agentphase1-reviewer,muse-spark-1.3-contributor— security-auditor (completed, effort xhigh); agentphase1-reviewer - Phase 1 model:
muse-spark-1.3-contributor— not quota-gated; passed overgemini-3.8-flash-high(antigravity below 15% reserve: weekly 11% left, 5h 100% left),glm-5.3-flash(zai below 15% reserve: 5h 0% left, weekly 53% left) - Fresh verifier:
gpt-6-astra— final-verifier; agentastra-verifier - Phase 2 reviewers:
gpt-6-astra— general (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— architecture-layering (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— ffi-engineer (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— platform-versioning (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— rust-quality (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— security-auditor (completed, effort xhigh); agentphase2-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/data_contract_update/basic_structure/v2/mod.rs`:
- [BLOCKING] packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_update/basic_structure/v2/mod.rs:54-71: Reject delta updates that inherit a legacy configuration version
The v2 basic-structure validator checks the configuration minimum only when the delta explicitly supplies `config`. When `config` is `None`, the merge retains the stored configuration, so a contract created with configuration version 0 can be updated through the v15 delta path while retaining that unsupported effective configuration. The full-contract validator checks the embedded configuration version, but this delta path does not apply that check to the materialized contract. Validate the effective merged configuration, or otherwise reject an inherited version below `platform_version.dpp.contract_versions.config.min_version`, before accepting the delta.
In `packages/rs-platform-wallet-ffi/src/data_contract.rs`:
- [SUGGESTION] packages/rs-platform-wallet-ffi/src/data_contract.rs:90-116: Provide an FFI representation for clearing the contract description
The V1 transition distinguishes `DescriptionUpdate::Keep`, `Clear`, and `Set`, but this C ABI accepts only a nullable string and decodes it with `read_optional_str`. Both NULL and an empty string become `None`; downstream `None` means preserve the existing description, so an FFI caller cannot clear a non-empty description. Add an explicit clear flag or another tagged representation at the ABI boundary and propagate it through the wallet update path.
In `packages/wasm-dpp2/src/data_contract/transitions/update.rs`:
- [SUGGESTION] packages/wasm-dpp2/src/data_contract/transitions/update.rs:43-46: Use the actual hyphenated nonce field in the V1 WASM declarations
The Rust V1 transition serializes its nonce as `$identity-contract-nonce` via `serde(rename = "$identity-contract-nonce")`, while the TypeScript V1 object and JSON declarations require `identityNonce`. Thus the declared shape does not describe the object emitted by `toObject`/`toJSON`, and callers following the declaration cannot construct the canonical serialized form without a cast or workaround. Change both V1 declarations to use the actual `$identity-contract-nonce` property.
In `packages/wasm-dpp/src/data_contract/state_transition/data_contract_update_transition/mod.rs`:
- [SUGGESTION] packages/wasm-dpp/src/data_contract/state_transition/data_contract_update_transition/mod.rs:115-119: Expose the V1 target contract ID in the legacy WASM wrapper
The wrapper can deserialize the new V1 delta transition, but `getDataContract()` correctly fails because V1 carries no full contract. The wrapper exposes `getOwnerId` but does not expose the transition's new `data_contract_id()` accessor, leaving JavaScript callers unable to identify which contract a deserialized delta targets. Add a `getDataContractId` binding for parity with the Rust accessor and the newer WASM2 surface.
In `packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/mod.rs`:
- [SUGGESTION] packages/rs-platform-version/src/version/dpp_versions/dpp_contract_versions/mod.rs:26-36: Gate the delta merge method from protocol version 15
`apply_update` is introduced by this PR as an unconditional `FeatureVersion` slot and is populated in historical contract-version tables as well as the v15 table. That makes the new method appear available on pre-v15 `PlatformVersion` snapshots, even though the transition itself is not admitted there. Represent the method as an `OptionalFeatureVersion`, use `None` before activation and `Some(0)` from v15, and fail closed when it is unavailable.
In `packages/rs-dpp/src/state_transition/mod.rs`:
- [SUGGESTION] packages/rs-dpp/src/state_transition/mod.rs:895-896: Use a version-table activation constant for V1 contract updates
The V1 transition's `active_version_range` hard-codes `15..=LATEST_VERSION`, whereas other newly activated transition kinds use named constants from `feature_initial_protocol_versions.rs`. Define a `DATA_CONTRACT_UPDATE_V1_INITIAL_PROTOCOL_VERSION` constant and use it here so the activation boundary is centralized with the protocol-version tables.
…sh the client surface apply_update is an OptionalFeatureVersion: None on every shipped contract versions table and Some(0) on the new CONTRACT_VERSIONS_V7 that PLATFORM_V15 selects, so the delta merge does not appear available on snapshots that admit no delta transition; the dispatcher fails closed before activation. The V1 activation boundary is DATA_CONTRACT_UPDATE_V1_INITIAL_PROTOCOL_VERSION in feature_initial_protocol_versions.rs, used by active_version_range. A delta that supplies no config keeps the stored one; the merged contract is held to the config update rules like any full-contract update, which reject a version below the floor. A state test pins that a stored legacy (v0) config can not ride through the delta path. The wasm-dpp2 TypeScript declarations use the serialized nonce key ($identity-contract-nonce) on both forms, and both wasm wrappers expose the target contract id (and wasm-dpp2 the owner id) so a deserialized delta can be attributed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
thepastaclaw
left a comment
There was a problem hiding this comment.
Re-review — Final validation — Phase 2 only (queue backlog)
The protocol-15 delta implementation addresses the prior merge, proof-completeness, keyword-ordering, activation-gating, and WASM-surface findings. However, several consensus-critical paths were extended in shipped v0 implementations while protocol 15 continues selecting those same generations, so the new behavior is not isolated by the required version tables. The platform-wallet FFI also still cannot express the new description-clear operation.
🔴 6 blocking | 🟡 1 suggestion(s)
2 finding(s) not shown inline (the lines are not part of this PR's diff)
🔴 Blocking: Version the V1 execution-proof prover instead of changing Drive v0
packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs:74-102
The V1 update branch now fetches the stored contract and selects the history query inside prove_state_transition_v0, while all current Drive versions, including protocol 15, still select prove_state_transition: 0. This changes a shipped consensus-visible proof-generation path without a new version boundary. Add a new prove-method generation and select it in the protocol-15 Drive version table, leaving v0 unchanged for historical versions.
source: gpt-6-astra (phase2-reviewer: general, architecture-layering, ffi-engineer, platform-versioning, rust-quality, security-auditor)
🟡 Suggestion: Provide an FFI representation for clearing the contract description
packages/rs-platform-wallet-ffi/src/data_contract.rs:90-116
The V1 transition introduces DescriptionUpdate::Clear, but this C ABI exposes only description: *const c_char and passes it through read_optional_str. Null and empty inputs are consequently treated as omitted, while the wallet update path treats omission as keeping the existing description; only a non-empty string can set a description. Callers of this public FFI cannot remove a description once set. Add an explicit clear flag or description-update mode and propagate the tri-state value through the wallet API.
source: gpt-6-astra (phase2-reviewer: general, architecture-layering, ffi-engineer, platform-versioning, rust-quality)
1 carried-forward finding(s) already raised on this PR; not re-posting as new inline comments.
Review provenance
Source: reviewer 1: gpt-6-astra (agent: phase2-reviewer, role: general); reviewer 2: gpt-6-astra (agent: phase2-reviewer, role: architecture-layering); reviewer 3: gpt-6-astra (agent: phase2-reviewer, role: ffi-engineer); reviewer 4: gpt-6-astra (agent: phase2-reviewer, role: platform-versioning); reviewer 5: gpt-6-astra (agent: phase2-reviewer, role: rust-quality); reviewer 6: gpt-6-astra (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6-astra (agent: astra-verifier, role: final-verifier)
- Triage:
criticalbygpt-6-astra(effort low) — This large, intricate diff changes consensus-critical protocol versioning, peer-facing state-transition serialization and validation, contract storage actions, fees, and execution-proof verification in files such as rs-dpp DataContractUpdateTransitionV1 and rs-drive-abci data-contract-update validation. - Phase 1 reviewers: not run (skipped for throughput: 17 PRs queued, above the 10 limit)
- Fresh verifier:
gpt-6-astra— final-verifier; agentastra-verifier - Phase 2 reviewers:
gpt-6-astra— general (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— architecture-layering (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— ffi-engineer (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— platform-versioning (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— rust-quality (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— security-auditor (completed, effort xhigh); agentphase2-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/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs`:
- [BLOCKING] packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs:105-164: Version the V1 execution-proof verifier instead of changing Drive v0
The V1 delta proof-verification path was added directly to `verify_state_transition_was_executed_with_proof_v0`. The dispatcher still selects only generation 0, including for protocol 15, so this consensus-visible verifier behavior is not isolated from historical versions. Add a new verifier generation and select it from the protocol-15 Drive version table, preserving the existing v0 implementation for versions that already shipped.
In `packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs`:
- [BLOCKING] packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs:74-102: Version the V1 execution-proof prover instead of changing Drive v0
The V1 update branch now fetches the stored contract and selects the history query inside `prove_state_transition_v0`, while all current Drive versions, including protocol 15, still select `prove_state_transition: 0`. This changes a shipped consensus-visible proof-generation path without a new version boundary. Add a new prove-method generation and select it in the protocol-15 Drive version table, leaving v0 unchanged for historical versions.
In `packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_update/identity_contract_nonce/v0/mod.rs`:
- [BLOCKING] packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_update/identity_contract_nonce/v0/mod.rs:43-54: Keep V1 contract-nonce validation out of the shipped v0 generation
The v0 identity-contract-nonce validator was changed to use the generic transition accessors so it can process V1 deltas. Protocol 15 still selects `nonce: Some(0)`, meaning the new transition behavior runs through the historical v0 generation. Add a V1 nonce-validation generation and select it in the protocol-15 validation table, or reject unsupported transition versions before invoking v0 while preserving the old implementation.
In `packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_update/basic_structure/v0/mod.rs`:
- [BLOCKING] packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_update/basic_structure/v0/mod.rs:27-36: Do not modify the frozen v0 contract-update validator for V1 rejection
The v0 basic-structure validator now calls `embedded_data_contract` and returns an error when a transition has no embedded contract. Pre-v15 tables still select `basic_structure: Some(0)`, so this is new behavior in the historical generation. Put the unsupported-version guard in the dispatcher or a new protocol-15 generation and restore v0 for historical validation.
In `packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_update/state/v0/mod.rs`:
- [BLOCKING] packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_update/state/v0/mod.rs:173-184: Do not modify the frozen v0 contract-update state validator for V1 rejection
The v0 state validator now handles a missing embedded contract by producing a nonce-bump result and consensus error. Protocol versions through 14 still select `state: 0`, so historical state validation behavior has been changed inside a shipped generation. Preserve `state/v0` and perform the unsupported-version handling in a version dispatcher or newly versioned validator selected for protocol 15.
In `packages/rs-platform-wallet-ffi/src/data_contract.rs`:
- [SUGGESTION] packages/rs-platform-wallet-ffi/src/data_contract.rs:90-116: Provide an FFI representation for clearing the contract description
The V1 transition introduces `DescriptionUpdate::Clear`, but this C ABI exposes only `description: *const c_char` and passes it through `read_optional_str`. Null and empty inputs are consequently treated as omitted, while the wallet update path treats omission as keeping the existing description; only a non-empty string can set a description. Callers of this public FFI cannot remove a description once set. Add an explicit clear flag or description-update mode and propagate the tri-state value through the wallet API.
The delta-based update had been threaded through generations that protocol versions up to 14 still select: the prover and the execution-proof verifier in Drive, and the basic structure, state and identity contract nonce validators in drive-abci. Those modules are restored to what shipped; the only remaining difference is the mechanical handling of the accessor that now returns an Option, an internal error that the dispatchers make unreachable. The delta form lives in new generations selected only by protocol version 15: prove_state_transition v1 and verify_state_transition_was_executed_with_proof v1 through DRIVE_VERSION_V10 (verify table v3), and identity contract nonce v1 through DRIVE_ABCI_VALIDATION_VERSIONS_V11. The dispatchers reject a V1 update ahead of any earlier generation as an unsupported transition version, a consensus error, so a delta reaching a pre-15 node can not stall a proposal. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…erations The state dispatcher now rejects a delta-based (V1) contract update before generations 0 and 1 run, with the same unsupported-version consensus error the other dispatchers return, and a test pins it on a protocol version 14 platform. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
thepastaclaw
left a comment
There was a problem hiding this comment.
Re-review — Final validation — Phase 2 only (queue backlog)
The protocol-15 delta implementation correctly isolates new consensus behavior behind new version generations and preserves the shipped pre-v15 paths. Two correctness gaps remain: delta extraction conflates absent and explicitly empty schema-definition maps, and the native wallet FFI cannot express clearing a contract description. The shared unversioned materialization helper also bypasses the versioned apply_update dispatch in proof verification, creating a future protocol-generation drift hazard.
🟡 3 suggestion(s)
1 finding(s) not shown inline (the lines are not part of this PR's diff)
🟡 Suggestion: Provide an FFI representation for clearing the contract description
packages/rs-platform-wallet-ffi/src/data_contract.rs:90-116
The V1 transition distinguishes Keep, Clear, and Set for descriptions, but this ABI exposes only a nullable C string. read_optional_str maps both a null pointer and an empty string to None, and the wallet update path interprets that value as keeping the existing description. Native FFI callers therefore cannot request the supported Clear operation. Add an explicit clear flag or another tri-state representation and pass it through to the wallet API.
source: gpt-6-astra (phase2-reviewer: general, architecture-layering, ffi-engineer, platform-versioning, rust-quality, security-auditor)
Review provenance
Source: reviewer 1: gpt-6-astra (agent: phase2-reviewer, role: general); reviewer 2: gpt-6-astra (agent: phase2-reviewer, role: architecture-layering); reviewer 3: gpt-6-astra (agent: phase2-reviewer, role: ffi-engineer); reviewer 4: gpt-6-astra (agent: phase2-reviewer, role: platform-versioning); reviewer 5: gpt-6-astra (agent: phase2-reviewer, role: rust-quality); reviewer 6: gpt-6-astra (agent: phase2-reviewer, role: security-auditor); reviewer 7: gpt-6-astra (agent: phase2-reviewer, role: general); reviewer 8: gpt-6-astra (agent: phase2-reviewer, role: architecture-layering); reviewer 9: gpt-6-astra (agent: phase2-reviewer, role: ffi-engineer); reviewer 10: gpt-6-astra (agent: phase2-reviewer, role: platform-versioning); reviewer 11: gpt-6-astra (agent: phase2-reviewer, role: rust-quality); reviewer 12: gpt-6-astra (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6-astra (agent: astra-verifier, role: final-verifier)
- Triage:
criticalbygpt-6-astra(effort low) — This is a large, intricate consensus and peer-facing deserialization change spanning protocol-version dispatch, state-transition validation, storage actions, proofs, and execution paths, notably in DataContractUpdateTransitionV1 and the v15 platform-version tables. - Phase 1 reviewers: not run (skipped for throughput: 15 PRs queued, above the 10 limit)
- Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
- Fresh verifier:
gpt-6-astra— final-verifier; agentastra-verifier - Phase 2 reviewers:
gpt-6-astra— general (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— architecture-layering (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— ffi-engineer (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— platform-versioning (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— rust-quality (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— security-auditor (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— general (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— architecture-layering (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— ffi-engineer (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— platform-versioning (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— rust-quality (completed, effort xhigh); agentphase2-reviewer,gpt-6-astra— security-auditor (completed, effort xhigh); agentphase2-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-platform-wallet-ffi/src/data_contract.rs`:
- [SUGGESTION] packages/rs-platform-wallet-ffi/src/data_contract.rs:90-116: Provide an FFI representation for clearing the contract description
The V1 transition distinguishes Keep, Clear, and Set for descriptions, but this ABI exposes only a nullable C string. `read_optional_str` maps both a null pointer and an empty string to `None`, and the wallet update path interprets that value as keeping the existing description. Native FFI callers therefore cannot request the supported Clear operation. Add an explicit clear flag or another tri-state representation and pass it through to the wallet API.
In `packages/rs-dpp/src/state_transition/state_transitions/contract/data_contract_update_transition/v1/mod.rs`:
- [SUGGESTION] packages/rs-dpp/src/state_transition/state_transitions/contract/data_contract_update_transition/v1/mod.rs:199-220: Preserve the optional `$defs` presence when extracting a delta
`schema_defs()` returns an `Option<BTreeMap<...>>`, but the extractor normalizes both `None` and `Some(empty_map)` to the same empty map before computing the delta. A change from `None` to `Some(empty)` produces no delta and materializes back to `None`; a change from `Some(empty)` to `None` likewise preserves the old representation. The resulting transition can therefore differ from the target supplied to `from_contract_update`, and one direction can silently request an unrepresentable removal. Compare the option presence first and reject changes that the delta format cannot encode, or add an explicit operation for this distinction. Add regression coverage for both directions.
In `packages/rs-dpp/src/data_contract/update_values.rs`:
- [SUGGESTION] packages/rs-dpp/src/data_contract/update_values.rs:88-111: Version the shared delta materialization used by proof verification
`DataContractUpdateValues::merge_onto` is consensus-visible contract materialization, but it is an unversioned helper with no `PlatformVersion` parameter. Consensus execution reaches it through the versioned `DataContract::apply_update` slot, while the V1 execution-proof verifier calls `merge_onto` directly. The current protocol-15 implementation is consistent, but a future protocol version could select a new apply_update generation while proof verification continues using the old merge semantics. Expose materialization through a versioned method, or otherwise make both execution and proof verification select the same versioned implementation.
…a delta DataContractUpdateValues::merge_onto now dispatches on the same contract_versions.methods.apply_update slot as DataContract::apply_update, so consensus execution and execution-proof verification select the same merge generation; the generation-0 body is merge_onto_v0. from_contract_update rejects a change in the presence of the optional $defs map (absent to empty or empty to absent) as not expressible instead of silently producing a delta that materializes to a different contract; tests cover both directions. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
thepastaclaw
left a comment
There was a problem hiding this comment.
⚠️ DEGRADED — Re-review — Final validation — Phase 1 + Phase 2
⚠️ DEGRADED review. The primary review models were unavailable (gpt-6-astraunavailable: 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-luna→muse-spark-1.3-contributor,gpt-5.6-sol→muse-spark-1.3-contributor,gpt-5.6-terra→muse-spark-1.3-contributor,gpt-6-astra→muse-spark-1.3-contributor. Both review phases and the independent verifiers still ran, but on weaker models, with Phase 1 capped athigheffort. Treat the verdict as provisional; a full-strength re-review will run on the next push once the primary models are back.
Delta-based V1 contract updates are correctly isolated to protocol 15 with versioned merge, frozen v0 generations, and full-contract proof verification. All 16 other prior findings verify fixed at this head. One client-surface gap remains: the FFI/wallet path cannot express DescriptionUpdate::Clear.
🟡 1 suggestion(s) | 💬 1 nitpick(s)
1 finding(s) not shown inline (the lines are not part of this PR's diff)
🟡 Suggestion: Provide an FFI representation for clearing the contract description
packages/rs-platform-wallet-ffi/src/data_contract.rs:90-116
The wallet update path switched by this PR to building a delta via new_from_contract_update, but neither it nor its FFI caller can produce the new Clear variant: read_optional_str maps NULL and empty to None, update_data_contract_with_signer filters empty to None, and merge_contract_update_payload only touches the description on Some. A caller that sets a description can therefore never remove it through FFI/wallet, while native DPP and WASM both support clear. Add a tri-state or explicit clear flag and thread it to DescriptionUpdate::Clear so mobile callers keep parity with the protocol.
source: gemini-3.8-flash-high (phase1-reviewer: general, architecture-layering, ffi-engineer, platform-versioning, rust-quality); muse-spark-1.3-contributor (phase2-reviewer: general, architecture-layering, ffi-engineer, platform-versioning, rust-quality)
Review provenance
Source: reviewer 1: gemini-3.8-flash-high (agent: phase1-reviewer, role: general); reviewer 2: gemini-3.8-flash-high (agent: phase1-reviewer, role: architecture-layering); reviewer 3: gemini-3.8-flash-high (agent: phase1-reviewer, role: ffi-engineer); reviewer 4: gemini-3.8-flash-high (agent: phase1-reviewer, role: platform-versioning); reviewer 5: gemini-3.8-flash-high (agent: phase1-reviewer, role: rust-quality); reviewer 6: gemini-3.8-flash-high (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); reviewer 13: muse-spark-1.3-contributor (standing in for gpt-6-astra) (agent: phase2-reviewer, role: general); reviewer 14: muse-spark-1.3-contributor (standing in for gpt-6-astra) (agent: phase2-reviewer, role: architecture-layering); reviewer 15: muse-spark-1.3-contributor (standing in for gpt-6-astra) (agent: phase2-reviewer, role: ffi-engineer); reviewer 16: muse-spark-1.3-contributor (standing in for gpt-6-astra) (agent: phase2-reviewer, role: platform-versioning); reviewer 17: muse-spark-1.3-contributor (standing in for gpt-6-astra) (agent: phase2-reviewer, role: rust-quality); reviewer 18: 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-astraunavailable: 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-insgpt-5.6-luna→muse-spark-1.3-contributor,gpt-5.6-sol→muse-spark-1.3-contributor,gpt-5.6-terra→muse-spark-1.3-contributor,gpt-6-astra→muse-spark-1.3-contributor; Phase 1 effort capped athigh - Triage:
criticalbymuse-spark-1.3-contributor(standing in forgpt-6-astra) (effort low) — Large 84-file (+8457) intricate change adds consensus-critical DataContractUpdateTransitionV1 with new validation in drive-abci state/v2 and DataContract::apply_update. - Phase 1 reviewers:
gemini-3.8-flash-high— general (completed, effort high); agentphase1-reviewer,gemini-3.8-flash-high— architecture-layering (completed, effort high); agentphase1-reviewer,gemini-3.8-flash-high— ffi-engineer (completed, effort high); agentphase1-reviewer,gemini-3.8-flash-high— platform-versioning (completed, effort high); agentphase1-reviewer,gemini-3.8-flash-high— rust-quality (completed, effort high); agentphase1-reviewer,gemini-3.8-flash-high— security-auditor (completed, effort high); agentphase1-reviewer - Phase 1 model:
gemini-3.8-flash-high— antigravity quota: weekly 100% left, 5h 100% left - Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
- Fresh verifier:
muse-spark-1.3-contributor(standing in forgpt-6-astra) — final-verifier; agentastra-verifier - Phase 2 reviewers:
muse-spark-1.3-contributor(standing in forgpt-6-astra) — general (completed, effort xhigh); agentphase2-reviewer,muse-spark-1.3-contributor(standing in forgpt-6-astra) — architecture-layering (completed, effort xhigh); agentphase2-reviewer,muse-spark-1.3-contributor(standing in forgpt-6-astra) — ffi-engineer (completed, effort xhigh); agentphase2-reviewer,muse-spark-1.3-contributor(standing in forgpt-6-astra) — platform-versioning (completed, effort xhigh); agentphase2-reviewer,muse-spark-1.3-contributor(standing in forgpt-6-astra) — rust-quality (completed, effort xhigh); agentphase2-reviewer,muse-spark-1.3-contributor(standing in forgpt-6-astra) — security-auditor (completed, effort xhigh); agentphase2-reviewer,muse-spark-1.3-contributor(standing in forgpt-6-astra) — general (completed, effort xhigh); agentphase2-reviewer,muse-spark-1.3-contributor(standing in forgpt-6-astra) — architecture-layering (completed, effort xhigh); agentphase2-reviewer,muse-spark-1.3-contributor(standing in forgpt-6-astra) — ffi-engineer (completed, effort xhigh); agentphase2-reviewer,muse-spark-1.3-contributor(standing in forgpt-6-astra) — platform-versioning (completed, effort xhigh); agentphase2-reviewer,muse-spark-1.3-contributor(standing in forgpt-6-astra) — rust-quality (completed, effort xhigh); agentphase2-reviewer,muse-spark-1.3-contributor(standing in forgpt-6-astra) — security-auditor (completed, effort xhigh); agentphase2-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-platform-wallet-ffi/src/data_contract.rs`:
- [SUGGESTION] packages/rs-platform-wallet-ffi/src/data_contract.rs:90-116: Provide an FFI representation for clearing the contract description
The wallet update path switched by this PR to building a delta via new_from_contract_update, but neither it nor its FFI caller can produce the new Clear variant: read_optional_str maps NULL and empty to None, update_data_contract_with_signer filters empty to None, and merge_contract_update_payload only touches the description on Some. A caller that sets a description can therefore never remove it through FFI/wallet, while native DPP and WASM both support clear. Add a tri-state or explicit clear flag and thread it to DescriptionUpdate::Clear so mobile callers keep parity with the protocol.
In `packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs`:
- [NITPICK] packages/rs-drive-abci/src/execution/check_tx/v0/mod.rs:2356-2360: Duplicated comment describing delta fee
Two consecutive comments in the new check_tx test say the same thing: the delta registers only the added document type and costs less than the full-contract form. The repetition is harmless but should be one comment.
| // A delta registers only the added document type, so it costs less | ||
| // than the full-contract form of the same update (27002879350). | ||
| // A delta registers only the added document type, where the | ||
| // full-contract form of the same update pays for the whole contract | ||
| // again (27002879350). |
There was a problem hiding this comment.
💬 Nitpick: Duplicated comment describing delta fee
Two consecutive comments in the new check_tx test say the same thing: the delta registers only the added document type and costs less than the full-contract form. The repetition is harmless but should be one comment.
| // A delta registers only the added document type, so it costs less | |
| // than the full-contract form of the same update (27002879350). | |
| // A delta registers only the added document type, where the | |
| // full-contract form of the same update pays for the whole contract | |
| // again (27002879350). | |
| // A delta registers only the added document type, so it costs less | |
| // than the full-contract form of the same update (27002879350). |
source: muse-spark-1.3-contributor (phase2-reviewer: general)
PR HygieneState: waiting-bots · commit
Self-review is an author attestation that you have read the diff: This check passes when the policy is satisfied; the repository decides whether merging requires it. |
Issue being fixed or feature implemented
A V0
DataContractUpdateTransitionembeds the whole contract, so a one-keyword change re-sends (and re-validates, and re-prices) every document type. PR #3021 set out to fix this with a delta-based V1 update transition, but it targeted protocol version 12, is 1800+ commits stale, and carried a review blocker; a rebase was not practical. This PR reimplements the update half of that idea from scratch on the 4.3 line, as the first consensus change of protocol version 15.What was done?
Protocol version 15 (
v15.rs,DRIVE_ABCI_VALIDATION_VERSIONS_V11,STATE_TRANSITION_SERIALIZATION_VERSIONS_V4,CONTRACT_VERSIONS_V7,DRIVE_VERSION_V10withDRIVE_VERIFY_METHOD_VERSIONS_V3). Everything else matches v14; the v10 validation table, the V3 serialization table, the v6 contract table and drive v9 stay byte-identical for v14 replay. Every shipped generation the delta touches keeps its logic: the delta form lives in new generations only PV15 selects (basic structure 2, state 2, identity contract nonce 1,transform_into_action1, prover 1, execution-proof verifier 1), and the dispatchers reject a V1 delta ahead of any earlier generation as anUnsupportedVersionError. The only edit inside shipped generations is the mechanical handling of the update accessor that now returns anOption, an internal error the dispatchers make unreachable. The V1 activation boundary isDATA_CONTRACT_UPDATE_V1_INITIAL_PROTOCOL_VERSIONinfeature_initial_protocol_versions.rs.DataContractUpdateTransitionV1(rs-dpp) carries only the delta, keyed bydata_contract_id,owner_idand the newversion:config: Option<DataContractConfig>updated_schema_defs/new_schema_defs,updated_document_schemas/new_document_schemasnew_groups,new_tokens(positions continue the stored ones)add_keywords/remove_keywordsdescription: DescriptionUpdate { Keep | Clear | Set(String) }(a tri-state enum rather thanOption<Option<String>>, so JSON round-trips)from_contract_updatecomputes the delta between two contracts and rejects what a delta cannot express: removed or changed document types, definitions, groups or tokens, a different id, and reordered keywords (a delta keeps the stored order and appends additions), and a change in whether the optional$defsmap is present.Materialize, then reuse.
DataContract::apply_update(newcontract_versions.methods.apply_updateslot, anOptionalFeatureVersionthat isNoneon every table beforeCONTRACT_VERSIONS_V7) merges the delta onto the stored contract and rebuilds aDataContractInSerializationFormatV1. The result then goes through exactly what a V0 update goes through: the generation-1validate_updaterules, the identity checks for new groups and tokens, external token costs andrefersToreference validation. Only delta-shape checks are new:DataContractUpdatePermissionError)DataContractUpdateEntryNotFoundError40011,DataContractUpdateEntryAlreadyExistsError40010)DataContractUpdateOverlappingEntriesError10277)drive-abci gets generation 2 of contract update
basic_structureandstateandtransform_into_action1: fetch the stored contract (fee-charged), turn a missing contract into a consensus error with a nonce bump (never an execution error), apply the delta, then run the generation-1 checks on the merged contract. A V1 transition that reaches a generation-0/1 validator (a pre-v15 node checking a newer transition) is anUnsupportedVersionErrorconsensus error, not an execution error, so a mis-versioned transition cannot stall a proposal.rs-drive adds a V1 update action transformer (
try_from_borrowed_v1_transition) that still producesDataContractUpdateTransitionActionV0; the action carries the delta's registration cost, so a delta pays only for what it adds (new/updated document types and their indexes, new tokens, added keywords, no base fee) where a V0 update pays for the whole contract again. Proving fetches the stored contract to learnkeeps_history.Execution-proof verification of a delta needs the pre-update contract. A delta embeds no contract, so the verifier takes the contract the delta was built against from the known-contracts provider (the way document verification takes the schema), requires it to be at
version - 1, materializes the delta on it withDataContractUpdateValues::merge_onto(the one definition of the merge, versioned on the sameapply_updateslot so execution and verification always pick the same generation) and compares the whole result with the proven contract. A missing or wrong-version known contract isProofError::MissingContextRequirement, a malformed delta (overlapping sections, entries that do not apply) isInvalidTransition, and a proven state the delta did not produce isIncorrectProof. platform-wallet registers the pre-update contract with the SDK's context provider before broadcasting and the confirmed one afterwards.API changes (see Breaking Changes):
data_contract()returnsOption,set_data_contractreturnsResult, newdata_contract_id().new_from_data_contractalways builds V0; newnew_from_contract_update(old, new, ...)/from_contract_updatepick V0 or V1 from the platform version's default (V1 from v15). platform-wallet'supdate_data_contract_with_signeruses the delta form; wasm-dpp exposesgetDataContractId, wasm-dpp2 exposesdataContractId/ownerIdgetters and declares the V0/V1 union of the object and JSON forms with the serialized$identity-contract-noncekey.Not included: the create transition V1 from #3021 (flat fields, id derived from owner and nonce). It was dead code there (never selected by any version table), saves about 40 bytes per registration, and touches every create validator and SDK registration path. It belongs in its own PR.
Note: #4706 also introduces
v15.rson this base. Whichever merges second rebases; the conflict is the v15 doc comment and the two slots each PR changes.How Has This Been Tested?
cargo check -p platform-version -p dpp -p drive -p drive-abci --all-targets: clean.cargo test -p platform-version: 20 passed (the version array and the v13/v14 gating tests).cargo test -p dpp --lib data_contract_update: 26 passed. Covers V1 serialization round-trips (bincode, JSON, platform value),from_contract_updatedelta extraction including the keyword-order rejection, the merge,registration_cost, and the latest-version default being the delta form.cargo test -p drive-abci --lib -- data_contract_update execution_event: 61 passed. Includes the generation-2 state tests (a delta adding a document type, keywords and a description; a missing contract as a paid consensus error with a nonce bump; an owner that does not own the contract; a new document type that already exists; a delta held to the full-contract update rules;delta_update_reaching_a_pre_v15_validator_is_an_unsupported_version_error, where a V1 handed to the v14 basic-structure validator and to the V0 Drive transformer yieldsUnsupportedVersionErrorrather than an execution error; a missing contract in the transformer is not an execution error) and the check_tx tests (data_contract_update_check_tx_latest_protocol_version_deltapins the delta's processing fee against the V0 fee for the same change).cargo test -p drive-abci --lib data_contract_update::basic_structure: 4 passed, new in this PR: a non-overlapping delta passes; a document type, a schema definition, or a keyword named in two conflicting sections is rejected withDataContractUpdateOverlappingEntriesError(10277).cargo test -p dpp --lib apply_update: the merge is inactive below protocol version 15 (apply_update_is_not_active_before_protocol_version_15).delta_update_keeping_a_legacy_config_is_rejected: a delta withoutconfigon a stored v0-config contract is rejected by the merged contract's config update rules (version floor), with a nonce bump.cargo test -p drive-abci --lib delta_execution_proof: 5 passed, new in this PR: an executed delta's proof verifies against the pre-update contract and yields the whole updated contract; verification without the pre-update contract, or with one at another version, isMissingContextRequirement; a delta that did not produce the proven state isIncorrectProof; a delta with overlapping sections isInvalidTransition.cargo check -p drive --no-default-features --features verify: clean (the merge the verifier runs is outside the validation feature).cargo test -p drive --lib -- data_contract_update prove_state_transition verify_state_transition: V0 and V1 action transformers, prover and verifier generations.cargo clippy -p platform-version -p dpp -p drive -p drive-abci --all-targets --all-features -- -D warnings: clean.cargo fmt --all -- --check: clean.Breaking Changes
DataContractUpdateTransitionAccessorsV0::data_contract()returnsOption<&DataContractInSerializationFormat>(V1 embeds no contract);set_data_contractreturnsResult.new_from_data_contractalways builds V0; usenew_from_contract_update/from_contract_updatefor the delta form.Checklist:
For repository code-owners and collaborators only
🤖 Generated with Claude Code