Repository navigation
fix(sdk)!: version the non-unique identity lookup so 4.1 clients keep working - #5515
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
|
📖 Book Preview built successfully. Download the preview from the workflow artifacts. Updated at 2026-10-22T20:16:50.705Z |
|
🔍 Review in progress — actively reviewing now (commit c15b778) · triage: critical |
Restore the legacy nested nonunique identity proof response while preserving current server output and all verifier security, signature and freshness gates. Backport only the identity-discovery feature commits onto v5.0-dev, adapting fixture signing to its existing BLS API and regenerating latest fixtures at PV14. Separate signed producer query layout from retained verifier rules for state transition outcomes. A genuine PV13 document outcome verifies under a retained PV14 verifier without an invented owner balance; PV14 still requires its balance. Keep existing Drive callers and all shipped protocol generations unchanged. Validate current Drive ABCI response codecs, signed proof adversarial cases, SDK version policy, read-only mainnet/testnet discovery and pinned client output. Scoped CI clippy, verify-only Drive clippy, rustfmt and wire tests pass. Original commits: bb5b1ec, 6f40494. <sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub> Co-Authored-By: Codex GPT-6 <noreply@openai.com>
6f40494 to
85f1707
Compare
Restore v4.1.1 nonunique identity producer, verifier and combined query-builder function bodies exactly. Keep the historical verifier private behind complete index/identity proof checks and the GroveDB V1 security floor. Move the changed single-proof implementation and bounded-prefix builder into generation 1. Select producer/verifier/handler generation 1 only in protocol 14 tables. Keep protobuf request V0 and route the response by committed current protocol state: PV13 serves legacy field 2; PV14 serves current field 4. Client verification continues selecting the actual wire encoding, including deployed PV13 current proofs, without lowering retained verifier rules or retrying failed proofs. Represent producer formats explicitly and adapt their node/test callers. Exercise genuine gRPC responses, signed proofs, SDK pins/learning, populated identity lookup across lock-in/activation/restarts and read-only live networks. Scoped CI clippy, verify-only Drive clippy, formatting and wasm32 compilation pass. Direct Rust producer callers now match a legacy/current enum. The current-format WASM verifier remains source compatible. No persisted schema or consensus execution generation changes; no release rehearsal or WASM runtime is claimed. <sub>🤖 Co-authored by [Claudius the Magnificent](https://github.com/lklimek/claudius) AI Agent</sub> Co-Authored-By: Codex GPT-6 <noreply@openai.com>
|
Checked the protocol 13 path against a real 4.1.1 SDK: the unmodified 4.1.1
|
…version A node could answer a protocol 14 identity lookup in the legacy two-proof format (field 2), or a protocol 13 lookup in the single-proof format (field 4), and the client verified either. The signed protocol version's `identity_by_non_unique_public_key_hash_handler` generation now decides: generation 0 (protocol 13 and earlier) accepts only field 2, generation 1 (protocol 14) only field 4. Anything else is a verification error, and the answer is never re-read in the other format. The SDK treats it as a proof error: the request moves to another node, and the refused answer does not teach the SDK its version. No live network is affected. Mainnet runs protocol 13 on 4.1.x nodes, which answer in field 2; testnet runs protocol 14, which answers in field 4. Tests: signed PV12/PV13 field-4 and PV14 field-2 answers are refused by the verifier (✖ before the change, ✔ after) and through the SDK. The matching formats still verify with pagination and proved absence. The SDK replays and the fixture generator use the format each version emits; regenerated vectors are byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…own rules Since signed protocol versions select proof rules, the SDK verifies an answer signed by an unknown newer protocol with its newest known verifier table. Two paths still refused such answers outright because they also build the proved query from the producer's version: state-transition results and non-unique identity lookups. Once protocol 15 activated, clients on this SDK could no longer verify broadcast results or discover identities until they upgraded. The shared query-version helper now reads a version above the newest known one with the newest known construction rules. A wrong guess can only fail verification: the proof must still rebuild the root the quorum signed, and the signature still covers the newer version. The caller's verifier table, which the SDK never lowers below its stored version, is unchanged. Version 0 is still refused; reserved mock versions, which sit above the newest one, are read with the newest rules and never looked up. No other proof path in proof.rs refused unknown versions; latest-versions contract proofs already fell back to the verifier table. Tests (✖ before the change, ✔ after): a PV15-signed document outcome verifies under PV14 rules and still needs the owner balance, and a PV15-signed field-4 identity answer verifies with pagination. A PV15-signed field-2 answer is refused, and tampering with the newer version fails the signature. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rsion Clients pick the identity-lookup wire format to accept from the signed version's handler generation, while the handler builds its answer from the Drive producer generation. If the two drifted apart in any version table, that version would fail every proved lookup or have its answers refused. The test walks every protocol version and requires the handler, producer and verifier generations to agree. It also pins the deployed formats: protocol 13 and earlier answer with generation 0 (field 2), protocol 14 with generation 1 (field 4). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The legacy-format tests so far used synthetic proofs from a local Drive. Add two real answers recorded read-only from mainnet protocol 13 nodes (4.1.x) at height 456535: a first match for a non-unique key hash, and a proved absence after the largest identity ID. They are stored with the public key of the quorum that signed each one. The offline replay checks the raw protobuf layout: response version 0 carries result field 2, holding the index proof and, for the match, the separate identity proof. It then runs full proof and signature verification under the protocol 13 and newest verifier tables. It also checks that altering the identity proof, relabelling the answer as protocol 14 or changing the cursor is refused. An ignored live test regenerates the vectors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on rules Follow-ups to the format gate and the newest-rules fallback from review: - Drive ABCI's signed merged-proof tests still sent single proofs signed as protocol 12/13 and expected them to verify. They now build each version's answer in the format it emits, check the single-proof adversarial cases only where that format is legitimate, and expect unknown or reserved versions to reach the signature check instead of a decode error. - Version-tampering cases now relabel an answer to a version that emits the same format, so they fail on the signature rather than the format gate, and assert `InvalidSignature`. Same for the recorded mainnet answer. - New SDK-level tests replay an identity answer and a document outcome signed by a version newer than the SDK. Both verify with the newest known rules, and the SDK does not learn the unknown version. The vectors come from the existing deterministic generators; the other vectors regenerate byte-identically. - The fallback comment no longer overclaims: a wrong guess cannot authenticate data outside the signed root, and changed layouts are expected to fail. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ules The identity-keys page said a non-unique lookup answer's format was independent of the protocol version, that protocol 13 nodes could answer in the single-proof format, and that newer protocols fail closed. Now each signed version accepts one format (field 2 up to protocol 13, field 4 from 14), and newer versions are read with the newest known rules. The put-operations page now also covers outcomes signed by a newer protocol. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@lklimek Ivan asked us to finish this for rc.8, so I pushed six commits on top of yours. Nothing of yours was rewritten.
The |
|
Waiting for bot review — coderabbitai ✓ · thepastaclaw not yet. Wait for the missing reviews, or a writer can post |
…e sentinel
`should_forward_producer_and_verifier_tables_independently` used verifier
generation 1 as its "unknown verifier" sentinel. Protocol 14 now selects
generation 1, so the verifier accepted it and the `expect_err` panicked. The
sentinel and the `UnknownVersionMismatch { received }` assertion now use 255.
The producer-slot success and verifier-slot rejection checks are unchanged.
Test would have caught this in CI if it ran there: ✖ before (panics at the
`expect_err`), ✔ after. The whole identity lib suite (4 tests) passes on
wasm32-unknown-unknown under wasm-bindgen-test-runner. No PR CI job runs this
crate's wasm32 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oves
The SDK replays for answers signed by an unknown newer protocol read
`pv{latest+1}` vectors and the `pv{latest}` contract. On a branch whose latest
version is 15 those files do not exist, and re-running the exporters could not
create them: the document-outcome exporter only writes the protocol 13 and 14
contracts.
The recorded "newer" vectors are now signed by protocol version 1000, far above
any real version, and stored without a version in their name
(`newer-current-0.bin`, `newer-{request,response}.bin`). They are built over the
protocol 14 layout and the document outcome uses `pv14-contract.bin`. The SDK
tests assert that the signed version is still unknown to the build, instead of
computing it. The identity exporter's version list is fixed at 12, 13 and 14,
so re-running it on a later branch rewrites the files the SDK tests read. The
in-process verifier tests keep using latest+1. The existing vectors regenerate
byte-identically.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… tests `verify_fetching_quorum_key` completes on its first poll, so `tokio::join!` always learned the older version before the newer one, and the tests could not see a downgrade. They now verify the protocol 14 answer, then the older one, and assert the stored version stays 14 after each step. Mutation check of `maybe_update_protocol_version`: removing only the `received_version <= current` guard keeps both tests green, which is correct because `fetch_max` still prevents the downgrade. Removing the guard and replacing `fetch_max` with a plain store fails both (✖ 12 and 13 instead of 14); the restored code passes (✔). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `identity_by_non_unique_public_key_hash_rejects_malformed_legacy_proof` only reached the missing-metadata error. It now signs the decoded field-2 answer as protocol 13, with an index proof that passes the envelope check over garbage bytes, so the legacy verifier itself must refuse it (GroveDB error). - New: a field-2 answer without its index proof is `NoProofInResult`, even when identity bytes are present. - New: a verified lookup returns the signed proof, which for a legacy answer is the index proof, as the book documents. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The capture test shares a binary with the read-only live checks, so running those with `--ignored` silently rewrote the committed mainnet vectors with the current height, quorum and signature. It now does nothing unless `DASH_SDK_CAPTURE_MAINNET_VECTORS=1` is set, and says so. The vectors README gives the full capture command. Checked: without the variable the test skips and no vector changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tables The legacy two-proof verifier first checks both proofs strictly, against queries built from the producer's tables and verified with this verifier's GroveDB version. It then hands both proofs to the restored v0 decoder, which rebuilt the identity query from the verifier's tables. Today every GroveDB version merges that query the same way. A future one that merges it differently would have rejected every genuine protocol 13 answer, and those errors are retryable, so honest nodes would have been banned. The decoder now gets the producer's tables, so both constructions match. The strict checks before it already applied the verifier's version to the same bytes, so the security floor is unchanged. Dropping the second verification instead would have left the restored v0 decoder without a caller, and this PR keeps that body identical to v4.1.1. No regression test: no two existing GroveDB versions build this query differently, so the false reject cannot be reproduced. The legacy tamper tests, the recorded mainnet replay and the Drive and Drive ABCI non-unique suites pass unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- identity-keys: V0 answers with two proofs at every protocol version, V1 with one proof from protocol 14; the response version must match the request; how the SDK picks V1 only after verifying a protocol that serves it; which older clients keep working. - feature-versions: the protocol 14 query table excerpt now shows the nested non-unique lookup bounds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The two-proof verifier proves the index entry under the verifier's GroveDB rules and then lets the 4.1.1 decoder read the identity under the producer's tables. It compared only their roots; it now also requires the decoded identity to be the one the index entry names. Today both read the same bytes with the same queries, so this cannot be reached by a test; it keeps the two reads from silently diverging if a GroveDB version ever decodes the same bytes differently. Also from review: error messages say "two-proof" instead of "legacy" (V0 is a supported request version), the stray `#[rustfmt::skip]` on the 4.1.1 decoder goes (the body formats unchanged), and a redundant test import and a dated doc phrase are fixed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sion A matched mock expectation answers with invented metadata at the latest protocol version. It went through the same acceptance as a verified answer, so it raised the verified protocol version, and a non-unique lookup registered with `expect_fetch` before an earlier mocked fetch was keyed in V0 while the SDK then sent V1 and found no expectation. The mock parser now reports when an expectation answered; that metadata still moves the stored version as before but is not recorded as verified. Recording moves out of `verify_response_metadata_as_of` into the two entry points that take verified metadata, and the public `verify_response_metadata` documents that it records it. Test (red before, green after): two lookup pages registered up front are both answered and the verified version stays 0. Also: a verified answer of another query switches the lookup to V1; the network-below-14 test covers protocol 12 pages too; the response metadata reader is tested for both versions, missing metadata and a missing version; the reader's import moves to the top. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The answer's shape was encoded as `Option<Option<&[u8]>>` (Some for V0, with or without identity bytes; None for V1). It is now an explicit "answered with two proofs" flag next to the identity bytes, so the pairing check reads as what it means. No behaviour change. New in-file tests pin the error order for both versions: a proved answer without metadata is `EmptyResponseMetadata`, a request without a version is `EmptyVersion`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The mainnet replay asserts why altered answers are refused: a truncated identity proof does not decode, a changed byte of the identity's own data fails GroveDB's check, and another cursor fails the two-proof guard. A two-proof message packed into a V1 proof fails to decode as a GroveDB proof. - The drive-abci exporter builds the rs-sdk and wasm vectors at protocol 14 instead of the latest version, so a later branch rewrites the same files (on a v5.1-dev trial merge, latest() made it fail). - The JS and Rust wire tests round-trip V0 identity proof bytes too; the Rust wire test file is renamed for what it covers, and the new V1 handler tests are named `should_…`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Book: 4.1 nodes and every node from 5.0.0-rc.8 on answer V0-only clients (the rc.4-rc.7 nodes on testnet do not); restore the caveat that an absence answer can verify at an index location a newer protocol no longer uses. - Drive version 9 doc: the two-proof slots are 0 in every drive version, so they are no difference from V8. - Protocol 14 query table doc: "the wire surface is unchanged" refers to documents. - Vectors README: the newer-version vectors are built with protocol 14 rules and keep verifying while the newest rules build the same query. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Waiting for bot review — coderabbitai ✓ · thepastaclaw not yet. Wait for the missing reviews, or a writer can post |
…identity-discovery Bring in #5541 (445ea06), which carries the document-outcome fix this branch had duplicated. Conflicts in the document-outcome files are resolved to #5541's versions: put-operations.md, the query-version verifier docs and test in rs-drive, the SDK document-outcome tests, and the comment on proof_query_platform_version. The identity lookup in proof.rs keeps this branch's code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The document-outcome tests now live in tests/document_outcome.rs from the merged operation-proof branch, so drop this branch's copy of them and of their README pointer. The identity discovery tests take the test quorum, its signer and NEWER_PROTOCOL_VERSION from tests/signed_response instead of keeping their own. The exporter rewrites byte-identical vectors. No behaviour change: test-only refactor (identity_discovery 13 + 1 ignored, document_outcome 6 + 1 ignored, all passing). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A node that has not reached the activation block refuses every V1 request it gets, not only the first, and each refusal bans it again with a longer backoff. The ban count resets once the node answers after catching up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README told callers to replay the non-unique key hash lookup with the direct verifier. That only works for a V1 answer's single proof. An SDK sends V0 until it has verified a protocol 14 answer, and for a V0 answer the proof returned with proof info is the index proof alone, which the direct verifier cannot check. wasm-sdk and js-evo-sdk have no doc comments on the proof-info methods, so the README is the only place to say this. No behaviour change: documentation only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The doc comment called the decoder "the 4.1 verifier, unchanged" and said its proof checks are not strict. Both of its verify_query calls are strict. What it lacks are the shape and pairing checks: it ignores the index row's value, accepts absence alongside an identity proof, and does not bind the decoded identity to the index entry. The comment now says that and no longer depends on release history. No behaviour change: comment only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…quest The SdkBuilder and proof-parsing docs said a pin governs request construction. Some requests, such as the non-unique key hash lookup, also need a verified network version before they use a newer request version, so an SDK pinned at protocol 14 still sends V0 until it has verified a protocol 14 answer. Point at QuerySettings::verified_protocol_version. No behaviour change: documentation only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The comment called every recorded page a single-proof vector. Page 0 is a V0 two-proof answer, because the SDK has verified nothing yet; its signed protocol 14 metadata makes pages 1-3 V1 single-proof answers. No behaviour change: comment only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The exporter compared its pretty-printed output with the committed wasm-drive-verify fixture byte for byte. The committed file has no final newline while .editorconfig asks for one, so an editor adding it would fail CI with "single proofs changed" although no proof changed. Compare parsed serde_json values instead. Checked by hand: with a final newline appended to the fixture the test passes; with one proof value altered it still fails with "single proofs changed". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd an env var `cargo test -p drive-proof-verifier -- --ignored` rewrote the committed identity discovery vectors in place, since the exporter was behind #[ignore] alone. Like the mainnet capture, it now also needs DASH_SDK_EXPORT_NONUNIQUE_VECTORS=1 and otherwise returns after printing how to enable it. The vectors README shows the new command. Checked: with --ignored and no env var the exporter prints "skipped" and writes nothing; with the env var it rewrites byte-identical vectors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Waiting for bot review — coderabbitai not yet · thepastaclaw not yet. Wait for the missing reviews, or a writer can post |
Capture gh output as bytes and decode at the call boundary, allowing replacement only for PR diffs while keeping API/JSON strict. Small binary blobs can appear as text hunks; preserve Unicode and inline placement rather than dropping the review. Close running steps and the gate on unexpected worker crashes, just as for classified failures. Add real-subprocess and worker publication regressions for the binary fixture behind dashpay/platform#5515. Co-authored-by: PastaClaw <thepastaclaw@users.noreply.github.com> Co-authored-by: Claude Code <noreply@anthropic.com>
Basic explanation
What this does: Gives each proof shape of
getIdentityByNonUniquePublicKeyHashits own request/response version, so 4.1.x clients, the 5.0 betas up to beta.4 and current SDKs all work against 4.1.x nodes and against nodes from this release on. V0 is again exactly the 4.1.1 wire shape (separate index and identity proofs) and every node serves it at every protocol version. V1 is new and carries the single merged proof; nodes accept it from protocol 14. The SDK sends V1 only after it has verified a protocol 14 answer.Value: 4.1.x clients and the published dashwallet-ios App Store builds (9.1.1–9.1.3) keep discovering identities through the 5.0 rollout and after protocol 14 activates on mainnet. Current SDKs get the merged proof once the network has proven it serves it.
Risks: Read-only query behaviour only; no consensus or stored-state change. On testnet (already at protocol 14) V0 answers move from field 4 back to field 2, so SDKs from 5.0.0-beta.5 to rc.7 lose this lookup there and must update (see Breaking Changes). A node that has not reached the protocol 14 activation block refuses V1, and each refusal bans it again, for longer each time, until it catches up and answers.
Issue being fixed or feature implemented
#5361 changed V0 in place from two proofs (field 2) to one merged proof (field 4) at every protocol version. 4.1.x nodes still answer in field 2 and 4.1.x clients only read field 2, so each side broke against the other. This PR's first version restored the 4.1.1 code but kept one V0 envelope carrying both shapes, switched by the producer's protocol version through an extra handler slot. Ivan rejected that shape: different shapes need different request and response versions.
Builds on #5541 (merged), which fixed the operation proofs for
BroadcastStateTransitionsigned by older and newer protocol versions. That part has left this PR;v5.0-devis merged in, so this diff is the identity lookup only.What was done?
Proto (
platform.proto).v0 = 1(unchanged),v1 = 2(new, same fields).oneof result { IdentityResponse identity = 1; IdentityProvedResponse proof = 2; },reserved 4. Byte-for-byte the 4.1.1 shape, including the variant name, so the generated JS/ObjC/Python APIs and serde keys match 4.1.1 too.oneof result { IdentityResponse identity = 1; Proof proof = 2; }.VersionedGrpcResponse(its V0 proof is not aProof), as in 4.1.1. All published clients regenerated with the pinned toolchain.Versions (
rs-platform-version).v5.0-dev).…_two_proofsprove/verify slots, 0 in every table.Drive.
v5.0-devbodies, untouched.prove_/verify_full_identity_by_non_unique_public_key_hash_two_proofs.0 | 1dispatch, thev1/copies and the unused unbounded query builder.drive-abci. The handler dispatches on the request version after the bounds check, as
getDocumentsdoes: V0 → the 4.1.1 handler over the two-proof producer, V1 → the single-proof handler.Verifier (
FromProof). The response version must equal the request version (ResponseDecodeErrorotherwise); V0 → two-proof verifier, V1 → single-proof verifier. The signature covers the state, not the envelope, so the client's own request decides the shape. The check that derived the format from the signed version is gone. Query rules still follow the signed producer version, and versions newer than the SDK use the newest known rules.SDK request selection. A refusal bans the node for every method, and V0 is served everywhere, so the encoder sends V1 only when both the stored protocol version and the highest version this SDK has seen in a verified, signed answer select it. Until something is verified it sends V0, whatever the seed, pin or persisted version. The verified version is recorded with
fetch_maxafter proof, signature and freshness checks, is never seeded or pinned, and is shared between clones. FFI, JNI, Swift, Kotlin, wasm-sdk, js-evo-sdk and the masternode locator all go through this encoder and need no change.Unchanged from earlier in this PR: the restored 4.1.1 bodies and their strict guards, the newer-protocol fallback, the recorded mainnet vectors and the review hardening. The identity discovery tests now sign their answers with #5541's shared test harness (
tests/signed_response).Compatibility:
How Has This Been Tested?
Local, macOS (pasta), at
c15b778f8a(after #5541 andv5.0-devwere merged in and the review fixes below were applied):identity_discovery(both versions, tamper/relabel/absence substitution, mainnet PV13 replay)non_uniqueidentity_discovery_tests(V0 then V1, seeded/pinned V0 on older networks, learning, mock expectations)signed_version_testsplatform::types::identity::tests(the cross-table invariant and selection table)mainidentity (offline vectors: page 0 V0, pages 1–3 V1; recorded 5.0-beta PV14 two-proof answers now verify)proof_optionstwo_proofsidentity_by_non_unique(V0 at 12/13/latest, V1 at latest, V1 refused at 12/13)nonunique_merged_proof(real transport, signed answers throughFromProof, both shapes, WASM fixture check)The identity discovery exporter now also needs
DASH_SDK_EXPORT_NONUNIQUE_VECTORS=1; with it, it rewrites byte-identical vectors. The WASM fixture check passes with a final newline added to the fixture and fails when a proof value changes.Static:
cargo check --workspace --all-targets, CI-flag clippy (--workspace --all-targets --all-features --locked -- --no-deps -D warnings),cargo fmt --check --all: all pass.Run earlier at
919855507aand not repeated here: drivenon_unique_public_key_hash30, drive-proof-verifier lib 356, dash-sdk lib 344, dash-sdkmain146 (+15 network-only ignored), dapi-grpc JS wire tests 47 (all four JS clients), platform-version 27, wasm-drive-verify identity lib on wasm32 4. Clients were regenerated withyarn workspace @dashevo/dapi-grpc build(pinned toolchain).Older clients against this branch's answers (out-of-tree replays, at
919855507a; the V0 path has not changed since). V0 wire bytes captured from this branch's real gRPC server at protocols 12, 13 and 14 (9 cases each: first match, pagination, cursor before/between/past, exhaustion, unknown key, unknown key with cursor), re-signed, then verified by the unmodified clientFromProofof each release:a35c4ce8(5.0.0-beta.4, App Store 9.1.3)e826b8f9(App Store 9.1.2)So 4.1.x clients and the published App Store builds keep discovering identities from these nodes at protocol 13 and after protocol 14 activates.
Mutation checks (at
919855507a; each mutant applied, the named tests went red, source restored byte-identical and green again):FromProofpairing check removed → pairing tests in the verifier (unit + integration), drive-abci and the SDK;Forward merge into
v5.1-dev(throwaway trial merge of919855507ainto4545b88cb5, LATEST 15; not pushed, deleted): one mechanical conflict inrs-sdk/src/mock/sdk.rs(v5.1 addstime_msto the mock metadata; keep both), andrs-drive-proof-verifier/tests/identity_discovery.rsneeds the v5.1 BLS API (dpp::bls::SecretKey,sign(&digest),to_bytes()). With those, every identity-lookup suite above passes at protocol 15. One document-outcome test (document_outcome::should_verify_document_outcome_signed_by_an_unknown_newer_version_with_newest_rules) fails there withunsupported property type: bytesfrom newer v5.1-dev contract validation; that test came from #5541 and is not part of the identity lookup.Live, read-only (mainnet, 4.1.2 nodes at protocol 13):
identity_discovery_live::should_discover_mainnet_identitypasses for 6 key hashes through this SDK (V0, two proofs, height 456794). A raw V1 request to the same nodes is refused withUNKNOWN"decoding error: could not decode identity by public key non unique hash query", which is why the SDK sends V0 until it has verified a protocol 14 answer.Reviews. The design spec went through two multi-agent review rounds, and the code through four independent reviewers (proof soundness, conventions, test coverage, compatibility/docs).
919855507athen had two more independent reviews: an Opus/code-reviewat max effort (10 finder angles plus verification) and an adversarial Sol (Codex) review. Neither found anything blocking. Neither found a proof-soundness hole in either shape or any request/response version confusion. Both confirmed that V0 matches 4.1.1 on the wire and that the version tables match the design. Fixes applied after them:…WithProofInfoproof from a V0 answer is the index proof alone;SdkBuilder::with_version(a pin alone may not select a newer request version) and the SDK vector comment (page 0 is V0, pages 1–3 are V1);--ignored.Follow-ups, not in this PR:
FromProofdoes;In-place changes to shipped generations
Compared with stable 4.1.1:
prove_/verify_full_identity_by_non_unique_public_key_hashv0mean one proof, not two. That change shipped in the 5.0 prereleases and stays. It cannot be reached at protocol 13 or below: only the V1 handler calls the producer and the bounds refuse V1 there; the verifier is client-side.v0is identical to 4.1.1 again, apart from calling the producer under its new…_two_proofsname.DRIVE_ABCI_QUERY_VERSIONS_V2(protocol 14) has its bounds widened to accept V1.Protocol 13 (stable, mainnet-active) is back to the 4.1.x behaviour: the same wire shape and producer code answer V0 with two proofs, and V1 is refused (
UNKNOWN, as 4.1 nodes refuse it by decoding). The proof bytes are not claimed to be identical to a 4.1.2 node's, since the GroveDB revisions differ; what is shown is that the unmodified 4.1.1, 9.1.2 and 9.1.3FromProofaccept this branch's answers (see testing), and that the recorded mainnet protocol 13 answers still verify.Protocol 14 is not released stable but is active on testnet (since height 623038, under #5361-era builds). Ivan approved this query-only exception: there, V0 answers move from field 4 back to field 2 and V1 is added. No changed table or method is read during block execution, so the "cannot modify consensus" test holds. Adding the new
…_two_proofsslots at 0 to every table is ordinary practice, not an in-place edit.Breaking Changes
None at the consensus level. The
!marks these API and wire breaks:get_identity_by_non_unique_public_key_hash_response_v0::Result::ProofisIdentityProvedResponseagain (it wasProoffrom 5.0.0-beta.5 to rc.7), andGetIdentityByNonUniquePublicKeyHashResponseno longer derivesVersionedGrpcResponseorVersionedGrpcMessage(no genericproof()/metadata(), noFrom<…ResponseV0>). Both request and responseVersionenums gain aV1variant, which breaks exhaustive matches. The generated JS/ObjC/Python V0 API matches 4.1.1 again; V1 messages are new.dash_sdk::platform::QuerySettingsgains the public fieldverified_protocol_version; struct literals must set it (0 means nothing verified yet).fetch_with_metadata_and_proofand the wasm/JS…WithProofInfocalls return the signed index proof for a V0 answer, as 4.1.1 did; on rc.7 they returned the complete single proof. A full offline replay of a V0 answer needs both blobs; the direct WASM verifierverifyFullIdentityByNonUniquePublicKeyHashtakes only a V1 proof and cannot replay that index proof (its README says so).prove_/verify_full_identity_by_non_unique_public_key_hashwork on the single proof (unchanged fromv5.0-dev); the 4.1.1 two-proof producer and verifier are now…_two_proofs.Checklist:
structure.rs, regeneratedgrovedb-structure.json, and checked the structure viewer link posted on this pull requestFor repository code-owners and collaborators only
Original work by @lklimek (restored 4.1.1 producer, decoder, handler and strict guards, and the mainnet vectors; the document-outcome fix, also by @lklimek, shipped in #5541); reworked into versioned envelopes by @shumkov with Claude.
🤖 Generated with Claude Code
PR Hygiene ·
c15b778/skip-botsproceeds without the ones not yet reported/self-reviewedbook/src/sdk/identity-keys.md,book/src/versioning/feature-versions.md,packages/dapi-grpc/build.rsand 47 more) — QuantumExplorer or shumkovrs-drive-abci(packages/rs-drive-abci/src/query/identity_based_queries/identity_by_non_unique_public_key_hash/mod.rs,packages/rs-drive-abci/src/query/identity_based_queries/identity_by_non_unique_public_key_hash/v0/mod.rs,packages/rs-drive-abci/src/query/identity_based_queries/identity_by_non_unique_public_key_hash/v1/mod.rsand 1 more) — QuantumExplorer or shumkovrs-drive(packages/rs-drive/src/drive/identity/fetch/prove/mod.rs,packages/rs-drive/src/drive/identity/fetch/prove/prove_full_identity_by_non_unique_public_key_hash_two_proofs/mod.rs,packages/rs-drive/src/drive/identity/fetch/prove/prove_full_identity_by_non_unique_public_key_hash_two_proofs/v0/mod.rsand 5 more) — QuantumExplorer or shumkovrust-sdk— you own itWhen every merge requirement is met, the
PR Hygienecheck passes. Reviewer limits do not block merging; other required GitHub checks and protections still apply.Summary by CodeRabbit