Skip to content

fix(drive): verify identity-create-from-shielded-pool results without unbounded terminal-key queries#3859

Merged
QuantumExplorer merged 1 commit into
v3.1-devfrom
fix/type20-result-proof-verify
Jun 11, 2026
Merged

fix(drive): verify identity-create-from-shielded-pool results without unbounded terminal-key queries#3859
QuantumExplorer merged 1 commit into
v3.1-devfrom
fix/type20-result-proof-verify

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Jun 11, 2026

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

On devnet (paloma, drive 4.0.0-rc.1, 2026-06-11), fetching the execution-result proof for an IdentityCreateFromShieldedPool (state transition type 20) always failed with:

Drive error: grovedb: query error: not supported error: terminal keys are not supported with unbounded ranges

even though the transition executed successfully on-chain (pool balance dropped by the denomination, notes +2, chain kept advancing). The client's broadcast_and_wait::<StateTransitionProofResult> therefore reported the registration as failed and never persisted the new identity — leaving an orphaned-but-live identity.

Root cause (client-side verification, not proof generation): the type-20 verifier arm rebuilds the merged {nullifiers, full-identity} query and verified it with GroveDb::verify_query_with_absence_proof. That entry point enumerates the query's terminal keys to synthesize absence entries — which is impossible for full_identity_query, whose all-keys sub-query is an unbounded RangeFull over the identity keys tree, so grovedb rejects the query outright for every honest proof. The other shielded spend types (unshield/transfer/withdrawal) only merge explicit-key sub-queries, which is why their result proofs round-tripped fine; the component unique to type 20 is the full-identity query.

What was done?

  • The type-20 arm of verify_state_transition_was_executed_with_proof now verifies the same merged query with strict GroveDb::verify_query (succinctness on, no absence synthesis). Absence entries were never needed: every queried element (the spent nullifiers and the created identity) must be PRESENT, so each nullifier's spend status is now derived from membership in the proved result set, with an explicit rejection of unrequested nullifier entries. The succinctness check still rejects proofs padded with branches beyond {nullifiers, identity}, preserving the strict guarantee from Tighten Unshield & ShieldedWithdrawal execution proofs to a single strict merged query (parity with ShieldFromAssetLock) #3812.
  • The prove side is unchanged (it already generated valid proofs); only its comment was updated.
  • Added a drive-abci regression test (executed_transition_result_proof_roundtrips) that executes a type-20 success action through the real execute_event path (synthetic action — no Halo 2 proving), commits, generates the result proof with prove_state_transition, and verifies it — failing with the exact devnet error before the fix, passing after.

How Has This Been Tested?

  • New regression test fails pre-fix with GroveDB(QueryError(NotSupported("terminal keys are not supported with unbounded ranges"))) and passes post-fix (verified both ways by stashing/restoring the fix).
  • cargo test -p drive --features full,verify --lib verify_state_transition_was_executed_with_proof — 61 passed.
  • cargo test -p drive-abci --lib shielded — 144 passed, 0 failed (includes the real-Orchard prove/verify round-trips for the other shielded types).
  • cargo check -p drive --no-default-features --features verify (verify-only build), cargo check -p drive --all-features --tests, cargo check -p drive-abci --all-features --tests, cargo fmt --all -- --check, clippy clean on the changed code.

Breaking Changes

None — this is a node/SDK query-proof verification fix; no consensus or wire-format change.

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

Follow-up note (not fixed here): SwiftExampleApp misattributes this broadcast-result error to the "Generating Halo 2 proof" progress step in the registration progress view (packages/swift-sdk/SwiftExampleApp) — the proof had already been generated and the transition broadcast/executed by the time this error fires. Worth a separate UI fix so result-proof failures surface as their own step.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests

    • Added regression test for state transition result-proof roundtrip verification in identity creation.
  • Bug Fixes

    • Improved verification logic for identity creation to use stricter query validation.
  • Documentation

    • Enhanced comments clarifying proof verification behavior and requirements.

… unbounded terminal-key queries

The type-20 verifier arm rebuilt the merged {nullifiers, full-identity}
query and verified it with verify_query_with_absence_proof, which
enumerates the query's terminal keys — impossible for
full_identity_query's unbounded all-keys RangeFull. grovedb rejected the
query outright ("terminal keys are not supported with unbounded
ranges"), so every honest type-20 result proof failed client-side even
though the transition executed on-chain, leaving an orphaned-but-live
identity the client never persisted.

Verify with strict GroveDb::verify_query instead (succinctness still
rejects proofs padded with extra branches); derive nullifier spend
statuses from membership in the proved result set and reject
unrequested nullifier entries. The prove side is unchanged.

Adds a drive-abci regression test that executes a type-20 success
action through the real execute_event path (synthetic action, no Halo 2
proving), commits, then round-trips prove_state_transition →
verify_state_transition_was_executed_with_proof — failing with the
exact devnet error before the fix.

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

thepastaclaw commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

✅ Review complete (commit 65599df)

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4c6d89eb-9a7e-448f-845e-69945ba2d76f

📥 Commits

Reviewing files that changed from the base of the PR and between 93b657d and 65599df.

📒 Files selected for processing (3)
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_create_from_shielded_pool/tests.rs
  • packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs
  • packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs

📝 Walkthrough

Walkthrough

This PR refactors the IdentityCreateFromShieldedPool state-transition verification to use strict merged-query verification over combined nullifier and full-identity ranges, replacing the previous absence-proof-based logic. It adds documentation explaining why unbounded identity queries require this approach, and includes an integration test validating the end-to-end execute→prove→verify roundtrip.

Changes

IdentityCreateFromShieldedPool strict merged-query verification

Layer / File(s) Summary
Strict merged-query verification refactoring
packages/rs-drive/src/verify/state_transition/verify_state_transition_was_executed_with_proof/v0/mod.rs
Replaces verify_query_with_absence_proof with strict verify_query over merged {nullifiers, identity} PathQuery; introduces spent_nullifiers set tracking proven-present keys (instead of absence-synthesis), validates proven nullifier keys against transition-requested keys, and derives final (nullifier, spent) statuses by membership in the proved-present set.
Prove-side documentation of merged-query constraint
packages/rs-drive/src/prove/prove_state_transition/v0/mod.rs
Expands comments clarifying that the verifier rebuilds and strictly verifies the merged query, and explains why absence-proof variant cannot be used because full_identity_query's unbounded all-keys range makes terminal key enumeration impossible.
Integration test and module documentation
packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/identity_create_from_shielded_pool/tests.rs
Updates module documentation to describe synthetic execute→prove→verify roundtrip; adds executed_transition_result_proof_roundtrips test that executes the success transition, commits state, generates result proof, and verifies the proof returns correct identity and all nullifiers marked spent.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • dashpay/platform#3813: The PR's strict merged-query verification and execute→prove→verify regression test directly implement the behavior described in this issue.

Possibly related PRs

  • dashpay/platform#3814: Both PRs apply strict merged-query verification to GroveDB state-transition proofs, with this PR targeting IdentityCreateFromShieldedPool and the related PR targeting Unshield/ShieldedWithdrawal.

Suggested labels

ready for final review

Suggested reviewers

  • shumkov
  • thepastaclaw

Poem

🐰 A shielded pool identity blooms bright,
Merged queries prove the spent nullifiers right,
No absence tricks, just strict verification,
The roundtrip test brings confident validation,
From execute to prove to verify—pure delight! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: changing identity-create-from-shielded-pool verification to avoid unbounded terminal-key queries by using strict query verification instead of absence-proof verification.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/type20-result-proof-verify

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 and usage tips.

@QuantumExplorer QuantumExplorer left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Reviewed

@QuantumExplorer QuantumExplorer merged commit e46c37d into v3.1-dev Jun 11, 2026
17 of 19 checks passed
@QuantumExplorer QuantumExplorer deleted the fix/type20-result-proof-verify branch June 11, 2026 20:55

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

Code Review

Narrowly scoped client-side verifier fix for type-20 (IdentityCreateFromShieldedPool) result proofs: swaps verify_query_with_absence_proof for verify_query because the embedded full-identity query has an unbounded all-keys range that the absence-proof variant cannot enumerate. Strict succinctness is preserved, spent-status is derived from membership in the proved-present set, and a regression test was added. All six agents converged on no in-scope findings.

@codecov

codecov Bot commented Jun 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.73%. Comparing base (93b657d) to head (65599df).
⚠️ Report is 4 commits behind head on v3.1-dev.

❗ There is a different number of reports uploaded between BASE (93b657d) and HEAD (65599df). Click for more details.

HEAD has 1 upload less than BASE
Flag BASE (93b657d) HEAD (65599df)
rust 2 1
Additional details and impacted files
@@              Coverage Diff              @@
##           v3.1-dev    #3859       +/-   ##
=============================================
- Coverage     87.15%   70.73%   -16.43%     
=============================================
  Files          2641       20     -2621     
  Lines        327793     2788   -325005     
=============================================
- Hits         285701     1972   -283729     
+ Misses        42092      816    -41276     
Components Coverage Δ
dpp ∅ <ø> (∅)
drive ∅ <ø> (∅)
drive-abci ∅ <ø> (∅)
sdk ∅ <ø> (∅)
dapi-client ∅ <ø> (∅)
platform-version ∅ <ø> (∅)
platform-value ∅ <ø> (∅)
platform-wallet ∅ <ø> (∅)
drive-proof-verifier ∅ <ø> (∅)
🚀 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.

@github-actions

Copy link
Copy Markdown
Contributor

✅ DashSDKFFI.xcframework built for this PR.

SwiftPM (host the zip at a stable URL, then use):

.binaryTarget(
  name: "DashSDKFFI",
  url: "https://your.cdn.example/DashSDKFFI.xcframework.zip",
  checksum: "9d7744bad2d12d83b767b1ffb00c52e78f5492098724faa7bf791cbf3064929c"
)

Xcode manual integration:

  • Download 'DashSDKFFI.xcframework' artifact from the run link above.
  • Drag it into your app target (Frameworks, Libraries & Embedded Content) and set Embed & Sign.
  • If using the Swift wrapper package, point its binaryTarget to the xcframework location or add the package and place the xcframework at the expected path.

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