Skip to content

feat(platform)!: delta-based data contract update transition for protocol version 15 - #4730

Open
QuantumExplorer wants to merge 1 commit into
v4.3-devfrom
feat/delta-contract-update-pv15
Open

QuantumExplorer wants to merge 1 commit into
v4.3-devfrom
feat/delta-contract-update-pv15

Conversation

@QuantumExplorer

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

A V0 DataContractUpdateTransition embeds 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). Everything else matches v14; the v10 validation table and the V3 serialization table stay byte-identical for v14 replay.

DataContractUpdateTransitionV1 (rs-dpp) carries only the delta, keyed by data_contract_id, owner_id and the new version:

  • config: Option<DataContractConfig>
  • updated_schema_defs / new_schema_defs, updated_document_schemas / new_document_schemas
  • new_groups, new_tokens (positions continue the stored ones)
  • add_keywords / remove_keywords
  • description: DescriptionUpdate { Keep | Clear | Set(String) } (a tri-state enum rather than Option<Option<String>>, so JSON round-trips)

Materialize, then reuse. DataContract::apply_update (new contract_versions.methods.apply_update slot) merges the delta onto the stored contract and rebuilds a DataContractInSerializationFormatV1. The result then goes through exactly what a V0 update goes through: the generation-1 validate_update rules, the identity checks for new groups and tokens, external token costs and refersTo reference validation. Only delta-shape checks are new:

  • the submitter must own the contract (DataContractUpdatePermissionError)
  • updated entries must exist and new entries must not (DataContractUpdateEntryNotFoundError 40011, DataContractUpdateEntryAlreadyExistsError 40010)
  • the sections of one transition must not overlap (DataContractUpdateOverlappingEntriesError 10277)

drive-abci gets generation 2 of contract update basic_structure and state and transform_into_action 1: 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 an UnsupportedVersionError consensus 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 produces DataContractUpdateTransitionActionV0; 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 learn keeps_history; verification checks every delta field against the proven contract (first_mismatch).

API changes (see Breaking Changes): data_contract() returns Option, set_data_contract returns Result, new data_contract_id(). new_from_data_contract always builds V0; new new_from_contract_update(old, new, ...) / from_contract_update pick V0 or V1 from the platform version's default (V1 from v15). platform-wallet's update_data_contract_with_signer uses the delta form; wasm-dpp / wasm-dpp2 expose the new accessors.

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.rs on 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: 24 passed. Covers V1 serialization round-trips (bincode, JSON, platform value), from_contract_update delta extraction, first_mismatch, 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 yields UnsupportedVersionError rather 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_delta pins 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 with DataContractUpdateOverlappingEntriesError (10277).
  • cargo test -p drive --lib data_contract_update: 22 passed (V0 and V1 action transformers, prove / verify of a delta against the stored contract).
  • 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

  • Protocol version 15 is introduced; from v15 clients default to the V1 (delta) contract update.
  • DataContractUpdateTransitionAccessorsV0::data_contract() returns Option<&DataContractInSerializationFormat> (V1 embeds no contract); set_data_contract returns Result.
  • new_from_data_contract always builds V0; use new_from_contract_update / from_contract_update for the delta form.
  • New consensus errors 10277, 40010, 40011 (appended to their enums).

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

…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>
@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

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

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

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: ead9415e-4100-4366-be18-21a5f377ba11

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

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

❤️ Share

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

@github-actions

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-14T00:32:01.373Z

@thepastaclaw

thepastaclaw commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

⛔ Final review complete — 3 blocking finding(s) (commit a9ce612) · triage: critical

@codecov

codecov Bot commented Sep 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 70.10989% with 680 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.91%. Comparing base (d020728) to head (a9ce612).

Files with missing lines Patch % Lines
...e_transitions/data_contract_update/state/v2/mod.rs 69.29% 183 Missing ⚠️
...ges/rs-drive-abci/src/execution/check_tx/v0/mod.rs 64.60% 80 Missing ⚠️
...ons/data_contract_update/basic_structure/v2/mod.rs 68.07% 68 Missing ⚠️
...tion/state_transitions/data_contract_update/mod.rs 69.32% 50 Missing ⚠️
...ntity_data_contract_nonce_action/v0/transformer.rs 13.46% 45 Missing ⚠️
...contract/data_contract_update_transition/v1/mod.rs 89.92% 40 Missing ⚠️
...p/src/data_contract/methods/apply_update/v0/mod.rs 73.88% 35 Missing ⚠️
...ns/contract/data_contract_update_transition/mod.rs 52.94% 32 Missing ⚠️
...contract_update_transition/v1/registration_cost.rs 62.31% 26 Missing ⚠️
...act/data_contract_update_transition/methods/mod.rs 61.22% 19 Missing ⚠️
... and 13 more

❗ There is a different number of reports uploaded between BASE (d020728) and HEAD (a9ce612). Click for more details.

HEAD has 2 uploads less than BASE
Flag BASE (d020728) HEAD (a9ce612)
rust 2 1
rust-shielded 2 1
Additional details and impacted files
@@             Coverage Diff              @@
##           v4.3-dev    #4730      +/-   ##
============================================
- Coverage     84.10%   77.91%   -6.19%     
============================================
  Files          2797     2806       +9     
  Lines        379933   403167   +23234     
============================================
- Hits         319541   314129    -5412     
- Misses        60392    89038   +28646     
Components Coverage Δ
dpp 75.29% <78.81%> (-9.44%) ⬇️
drive 78.48% <45.65%> (-3.46%) ⬇️
drive-abci 80.45% <67.81%> (-7.68%) ⬇️
sdk ∅ <ø> (∅)
dapi-client ∅ <ø> (∅)
platform-version ∅ <ø> (∅)
platform-value 86.67% <ø> (-6.25%) ⬇️
platform-wallet ∅ <ø> (∅)
drive-proof-verifier 34.73% <ø> (-8.95%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Final validation — Phase 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: critical by gpt-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); agent phase1-reviewer, muse-spark-1.3-contributor — ffi-engineer (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — security-auditor (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-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; agent astra-verifier
  • Phase 2 reviewers: gpt-6-astra — general (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — ffi-engineer (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-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.

Comment on lines +272 to +283
let remove_keywords = old_contract
.keywords()
.iter()
.filter(|keyword| !new_contract.keywords().contains(*keyword))
.cloned()
.collect();
let add_keywords = new_contract
.keywords()
.iter()
.filter(|keyword| !old_contract.keywords().contains(*keyword))
.cloned()
.collect();

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.

🔴 Blocking: 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.

source: gpt-6-astra (phase2-reviewer: general, ffi-engineer, rust-quality, security-auditor)

Comment on lines +121 to +134
let mismatch = match data_contract_update {
DataContractUpdateTransition::V0(v0) => {
let contract_for_serialization: DataContractInSerializationFormat =
contract
.clone()
.try_into_platform_versioned(platform_version)?;
contract_for_serialization
.first_mismatch(&v0.data_contract)
.map(|mismatch| mismatch.to_string())
}
DataContractUpdateTransition::V1(v1) => v1.first_mismatch(&contract),
};
if let Some(mismatch) = mismatch {
return Err(Error::Proof(ProofError::IncorrectProof(format!("proof of state transition execution did not contain exact expected contract after update with id {}: {}", contract_id, mismatch))));

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.

🔴 Blocking: 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.

source: muse-spark-1.3-contributor (phase1-reviewer: general, ffi-engineer, rust-quality, security-auditor); gpt-6-astra (phase2-reviewer: general)

Comment on lines +44 to +63
let document_schemas = updated_contract.document_schemas();
for (name, schema) in self
.new_document_schemas
.iter()
.chain(self.updated_document_schemas.iter())
{
if document_schemas.get(name) != Some(&schema) {
return Some(format!("document type '{name}' differs"));
}
}

let schema_defs = updated_contract.schema_defs();
for (name, definition) in self
.new_schema_defs
.iter()
.chain(self.updated_schema_defs.iter())
{
if schema_defs.and_then(|definitions| definitions.get(name)) != Some(definition) {
return Some(format!("schema definition '{name}' differs"));
}

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.

🔴 Blocking: 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.

source: gpt-6-astra (phase2-reviewer: general, ffi-engineer, rust-quality, security-auditor)

@github-actions github-actions Bot added this to the v4.3.0 milestone Sep 14, 2026
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