From 93806d7a3c38a07d19578ff6a2b12d4b014bdc77 Mon Sep 17 00:00:00 2001 From: Ivan Shumkov Date: Fri, 14 Aug 2026 00:12:39 +0700 Subject: [PATCH 01/25] fix(drive): skip the ranked offset by counting instead of walking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `prove = false` arm of a ranked query skipped its OFFSET by stepping a storage iterator once per skipped entry, so the skip alone cost `Theta(min(offset, population))` on a surface where offset has no ceiling. Ranked queries carry no fee, cannot be cancelled once dispatched, and share their rate budget with state transitions rather than having one of their own, so that made the skip an unmetered cost lever for an unauthenticated caller. The proved path never had it: its prover attests the skipped region from the counted subtree commitments instead of traversing it. grovedb now exposes that same counted descent to plain reads (dashpay/grovedb#792): it reads each subtree's aggregate count off its link and collapses any subtree that fits inside the remaining offset rather than stepping through it. Point the unproved executor at it and the skip becomes `O(log n)` at any offset — and an offset at or past the population is answered from the root's own count with no descent at all, making the worst input the cheapest request rather than the most expensive. `offset = 0` keeps the plain iterator path and never touches the tree, so the common unpaginated request costs exactly what it did. Pinned to the grovedb branch rev so this is reviewable now; to be re-pinned to the develop merge commit before merge. BEHAVIOUR CHANGE, wire-visible on unproved responses `RankedPage::skipped`, which reaches the wire as `GetDocumentsResponseV1.ResultData.Ranked.skipped`, stops echoing the request. The old read could not report how far a short walk got, so the server echoed the requested offset back; the counted descent tracks it, so both paths now report the same quantity — the requested offset when the skip succeeded, the ranking's population when the walk ran out of groups first. A client asserting `skipped == requested_offset` will see a different value past the end; one using it as the rank base for `entries[i]`, its documented purpose, is unaffected. The value is not attested on the unproved path. It equals the attested one on an honest node, and nothing forces a node to be honest — the same trust model as the entries beside it. The proto, the Objective-C client that carries proto prose, the developer book and the Rust docs all say so rather than letting "the true population" read as a guarantee. Three comments asserted things the code did not do, including the justification for leaving OFFSET uncapped. They are corrected here rather than earlier because two of them state the policy, and an accurate description of an uncapped lever is only safe to publish alongside the thing that removes it. Tests: four assertions changed across ~3,400, every one a `skipped` value — three in drive, one on the wire in drive-abci. No entry or ordering assertion moved, which is the claim: the counted read returns what the linear walk returned. drive --lib 3386 passed; drive-abci --lib query:: 623 passed; cargo clippy --workspace --all-features and cargo fmt --check --all both clean. --- Cargo.lock | 66 +++++----- book/src/drive/ranked-index-examples.md | 6 +- .../platform/v0/objective-c/Platform.pbobjc.h | 31 +++-- .../protos/platform/v0/platform.proto | 31 +++-- packages/rs-dpp/Cargo.toml | 2 +- packages/rs-drive-abci/Cargo.toml | 8 +- .../src/query/document_query/v1/tests.rs | 16 ++- packages/rs-drive/Cargo.toml | 12 +- .../v0/tests/ranked_index_e2e_tests.rs | 13 +- .../execute_top_k.rs | 114 +++++++++++------- .../executors/top_k_no_proof.rs | 11 +- .../query/drive_document_ranked_query/mod.rs | 52 +++++--- .../mode_detection/v0/mod.rs | 22 ++-- .../drive_document_ranked_query/tests.rs | 15 ++- packages/rs-platform-version/Cargo.toml | 2 +- packages/rs-platform-wallet/Cargo.toml | 2 +- packages/rs-sdk/Cargo.toml | 2 +- 17 files changed, 247 insertions(+), 158 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6775bdd0c17..58523ec401f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -576,7 +576,7 @@ dependencies = [ "bitflags 2.13.0", "cexpr", "clang-sys", - "itertools 0.10.5", + "itertools 0.13.0", "proc-macro2", "quote", "regex", @@ -1229,7 +1229,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2475,7 +2475,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2536,7 +2536,7 @@ checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2955,7 +2955,7 @@ dependencies = [ [[package]] name = "grovedb" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=a2791bbdca756d6a6113024aec48f09f7a33faa9#a2791bbdca756d6a6113024aec48f09f7a33faa9" +source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" dependencies = [ "axum 0.8.9", "bincode", @@ -2993,7 +2993,7 @@ dependencies = [ [[package]] name = "grovedb-bulk-append-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=a2791bbdca756d6a6113024aec48f09f7a33faa9#a2791bbdca756d6a6113024aec48f09f7a33faa9" +source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" dependencies = [ "bincode", "blake3", @@ -3009,7 +3009,7 @@ dependencies = [ [[package]] name = "grovedb-commitment-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=a2791bbdca756d6a6113024aec48f09f7a33faa9#a2791bbdca756d6a6113024aec48f09f7a33faa9" +source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" dependencies = [ "blake3", "grovedb-bulk-append-tree", @@ -3025,7 +3025,7 @@ dependencies = [ [[package]] name = "grovedb-costs" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=a2791bbdca756d6a6113024aec48f09f7a33faa9#a2791bbdca756d6a6113024aec48f09f7a33faa9" +source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" dependencies = [ "integer-encoding", "intmap", @@ -3035,7 +3035,7 @@ dependencies = [ [[package]] name = "grovedb-dense-fixed-sized-merkle-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=a2791bbdca756d6a6113024aec48f09f7a33faa9#a2791bbdca756d6a6113024aec48f09f7a33faa9" +source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" dependencies = [ "bincode", "blake3", @@ -3048,7 +3048,7 @@ dependencies = [ [[package]] name = "grovedb-element" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=a2791bbdca756d6a6113024aec48f09f7a33faa9#a2791bbdca756d6a6113024aec48f09f7a33faa9" +source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" dependencies = [ "bincode", "bincode_derive", @@ -3063,7 +3063,7 @@ dependencies = [ [[package]] name = "grovedb-epoch-based-storage-flags" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=a2791bbdca756d6a6113024aec48f09f7a33faa9#a2791bbdca756d6a6113024aec48f09f7a33faa9" +source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" dependencies = [ "grovedb-costs", "hex", @@ -3075,7 +3075,7 @@ dependencies = [ [[package]] name = "grovedb-merk" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=a2791bbdca756d6a6113024aec48f09f7a33faa9#a2791bbdca756d6a6113024aec48f09f7a33faa9" +source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" dependencies = [ "bincode", "bincode_derive", @@ -3101,7 +3101,7 @@ dependencies = [ [[package]] name = "grovedb-merkle-mountain-range" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=a2791bbdca756d6a6113024aec48f09f7a33faa9#a2791bbdca756d6a6113024aec48f09f7a33faa9" +source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" dependencies = [ "bincode", "blake3", @@ -3112,7 +3112,7 @@ dependencies = [ [[package]] name = "grovedb-path" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=a2791bbdca756d6a6113024aec48f09f7a33faa9#a2791bbdca756d6a6113024aec48f09f7a33faa9" +source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" dependencies = [ "hex", ] @@ -3120,7 +3120,7 @@ dependencies = [ [[package]] name = "grovedb-query" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=a2791bbdca756d6a6113024aec48f09f7a33faa9#a2791bbdca756d6a6113024aec48f09f7a33faa9" +source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" dependencies = [ "bincode", "byteorder", @@ -3136,7 +3136,7 @@ dependencies = [ [[package]] name = "grovedb-storage" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=a2791bbdca756d6a6113024aec48f09f7a33faa9#a2791bbdca756d6a6113024aec48f09f7a33faa9" +source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" dependencies = [ "blake3", "grovedb-costs", @@ -3155,7 +3155,7 @@ dependencies = [ [[package]] name = "grovedb-version" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=a2791bbdca756d6a6113024aec48f09f7a33faa9#a2791bbdca756d6a6113024aec48f09f7a33faa9" +source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" dependencies = [ "thiserror 2.0.18", "versioned-feature-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3164,7 +3164,7 @@ dependencies = [ [[package]] name = "grovedb-visualize" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=a2791bbdca756d6a6113024aec48f09f7a33faa9#a2791bbdca756d6a6113024aec48f09f7a33faa9" +source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" dependencies = [ "hex", "itertools 0.14.0", @@ -3173,7 +3173,7 @@ dependencies = [ [[package]] name = "grovedbg-types" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=a2791bbdca756d6a6113024aec48f09f7a33faa9#a2791bbdca756d6a6113024aec48f09f7a33faa9" +source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" dependencies = [ "serde", "serde_with 3.21.0", @@ -3589,7 +3589,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.4", "system-configuration", "tokio", "tower-service", @@ -3840,7 +3840,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4652,7 +4652,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -5542,7 +5542,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" dependencies = [ "heck 0.4.1", - "itertools 0.10.5", + "itertools 0.13.0", "log", "multimap", "petgraph", @@ -5563,7 +5563,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.13.0", "proc-macro2", "quote", "syn 2.0.117", @@ -5576,7 +5576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.13.0", "proc-macro2", "quote", "syn 2.0.117", @@ -5712,7 +5712,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.2", "rustls", - "socket2 0.5.10", + "socket2 0.6.4", "thiserror 2.0.18", "tokio", "tracing", @@ -5750,9 +5750,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.4", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6560,7 +6560,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6573,7 +6573,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -6632,7 +6632,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -7492,7 +7492,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -8941,7 +8941,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/book/src/drive/ranked-index-examples.md b/book/src/drive/ranked-index-examples.md index 20798741803..640f8684c6a 100644 --- a/book/src/drive/ranked-index-examples.md +++ b/book/src/drive/ranked-index-examples.md @@ -321,11 +321,11 @@ The response carries the skip back in `RankedEntries.skipped` (see [The Response Three properties worth stating plainly: -- **The skip is attested, not walked.** grovedb proves the skipped region from the counted subtree commitments (`HashWithCount` / `HashWithCountAndSum`) rather than by traversing it. Both the prover's work and the proof's size stay `O(log n + k)` **at any offset**. -- **There is therefore no offset ceiling.** An offset of 4 and an offset of four billion cost the same, so there is no denial-of-service lever a cap would close — and a cap would only stop honest deep pagination. +- **The skip is counted, not walked.** grovedb descends the secondary reading each subtree's aggregate count and collapses any subtree that fits entirely inside the remaining offset, instead of stepping through it. Both paths do this: the prover attests the skipped region from the counted subtree commitments (`HashWithCount` / `HashWithCountAndSum`), and the unproven read performs the same counted descent without building a proof. Work and proof size stay `O(log n + k)` **at any offset**. +- **There is therefore no offset ceiling.** An offset of 4 and an offset of four billion cost the same order of work — on either path, the deeper one in fact cheaper, since a tree that fits entirely inside the offset collapses at the root. There is no denial-of-service lever a cap would close, and a cap would only stop honest deep pagination. - **An offset past the end is a positive answer.** `entries` comes back empty and `skipped` is the ranking's *entire attested population*. "There are only 12 groups" is more information than a bare empty list. -On the **unproven** read there is nothing to attest and grovedb's read API does not report a short walk, so `skipped` simply echoes the requested offset. The proved and unproven paths therefore disagree in exactly one case — an offset past the end, where the unproven read reports the request and the proved one reports the truth. **Callers who need the population must prove.** +Both paths report the same `skipped`: the offset you asked for when the skip succeeded, and the ranking's total population when the walk ran out of groups first. What differs is the warrant, not the value. On the proved path it is cryptographically attested, re-derived by the verifier from the counted commitments. On the unproven path it is an **unverified claim, exactly like the entries beside it** — equal to the attested value on an honest node, with nothing forcing a node to be honest. **Callers who need to trust the population, rather than merely receive it, must still prove.** ## The Response diff --git a/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h index c4e66dadcb9..1aecbca6b63 100644 --- a/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h +++ b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h @@ -3846,16 +3846,27 @@ GPB_FINAL @interface GetDocumentsResponse_GetDocumentsResponseV1_RankedEntries : * population — a positive, useful answer ("there are only 12 * groups") rather than a bare empty list. * - * On the proved path the number is grovedb's cryptographically - * attested count, re-derived by the verifier from the counted - * subtree commitments in the proof bytes rather than trusted - * from this field; a proving client should use the verified - * value. On the unproven read there is nothing to attest and - * grovedb's read API does not report a short walk, so the server - * echoes the requested offset. The two therefore disagree in - * exactly one case — an offset past the end, where the unproven - * read reports the request and the proved one reports the truth. - * Callers who need the population must prove. + * Both paths report the same quantity: the offset you asked for + * when the skip succeeded, and the ranking's total population + * when the walk ran out of groups first. They no longer disagree + * anywhere, including past the end. + * + * What differs is the *warrant*, not the value. On the proved + * path the number is cryptographically attested — re-derived by + * the verifier from the counted subtree commitments in the proof + * bytes rather than trusted from this field — so a proving client + * should use the verified value and ignore this one. On the + * unproven path it is an **unverified claim**, exactly like the + * entries beside it: it equals the attested value on an honest + * node, and nothing forces a node to be honest. Read "the true + * population" as "what this node says the population is". + * Callers who need to trust it, rather than merely receive it, + * must still prove. + * + * Do not assume this field equals the offset you requested. It + * equals the offset only when the skip succeeded; when the walk + * ran out of groups first it is smaller, and that is the answer + * rather than an inconsistency. **/ @property(nonatomic, readwrite) uint64_t skipped; diff --git a/packages/dapi-grpc/protos/platform/v0/platform.proto b/packages/dapi-grpc/protos/platform/v0/platform.proto index 937962817c3..c507e72a40a 100644 --- a/packages/dapi-grpc/protos/platform/v0/platform.proto +++ b/packages/dapi-grpc/protos/platform/v0/platform.proto @@ -1421,16 +1421,27 @@ message GetDocumentsResponse { // population — a positive, useful answer ("there are only 12 // groups") rather than a bare empty list. // - // On the proved path the number is grovedb's cryptographically - // attested count, re-derived by the verifier from the counted - // subtree commitments in the proof bytes rather than trusted - // from this field; a proving client should use the verified - // value. On the unproven read there is nothing to attest and - // grovedb's read API does not report a short walk, so the server - // echoes the requested offset. The two therefore disagree in - // exactly one case — an offset past the end, where the unproven - // read reports the request and the proved one reports the truth. - // Callers who need the population must prove. + // Both paths report the same quantity: the offset you asked for + // when the skip succeeded, and the ranking's total population + // when the walk ran out of groups first. They no longer disagree + // anywhere, including past the end. + // + // What differs is the *warrant*, not the value. On the proved + // path the number is cryptographically attested — re-derived by + // the verifier from the counted subtree commitments in the proof + // bytes rather than trusted from this field — so a proving client + // should use the verified value and ignore this one. On the + // unproven path it is an **unverified claim**, exactly like the + // entries beside it: it equals the attested value on an honest + // node, and nothing forces a node to be honest. Read "the true + // population" as "what this node says the population is". + // Callers who need to trust it, rather than merely receive it, + // must still prove. + // + // Do not assume this field equals the offset you requested. It + // equals the offset only when the skip succeeded; when the walk + // ran out of groups first it is smaller, and that is the answer + // rather than an inconsistency. optional uint64 skipped = 2 [jstype = JS_STRING]; } diff --git a/packages/rs-dpp/Cargo.toml b/packages/rs-dpp/Cargo.toml index 7a1c3bf85f7..642dd68b594 100644 --- a/packages/rs-dpp/Cargo.toml +++ b/packages/rs-dpp/Cargo.toml @@ -71,7 +71,7 @@ strum = { version = "0.26", features = ["derive"] } json-schema-compatibility-validator = { path = '../rs-json-schema-compatibility-validator', optional = true } once_cell = "1.19.0" tracing = { version = "0.1.41" } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "a2791bbdca756d6a6113024aec48f09f7a33faa9", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee", optional = true } [dev-dependencies] tokio = { version = "1.40", features = ["full"] } diff --git a/packages/rs-drive-abci/Cargo.toml b/packages/rs-drive-abci/Cargo.toml index 23069f0158e..598f62f916a 100644 --- a/packages/rs-drive-abci/Cargo.toml +++ b/packages/rs-drive-abci/Cargo.toml @@ -82,7 +82,7 @@ derive_more = { version = "1.0", features = ["from", "deref", "deref_mut"] } async-trait = "0.1.77" console-subscriber = { version = "0.4", optional = true } bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f", optional = true } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "a2791bbdca756d6a6113024aec48f09f7a33faa9" } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee" } nonempty = "0.11" # Shielded-pool snapshot needs raw RocksDB SstFileWriter + ingest_external_file_cf # bindings, and blake3 for the snapshot-file checksum. @@ -107,7 +107,7 @@ dpp = { path = "../rs-dpp", default-features = false, features = [ drive = { path = "../rs-drive", features = ["fixtures-and-mocks"] } drive-proof-verifier = { path = "../rs-drive-proof-verifier" } strategy-tests = { path = "../strategy-tests" } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "a2791bbdca756d6a6113024aec48f09f7a33faa9", features = ["client"] } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee", features = ["client"] } assert_matches = "1.5.0" drive-abci = { path = ".", features = ["testing-config", "mocks", "shielded_test_data"] } bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f" } @@ -121,8 +121,8 @@ integer-encoding = { version = "4.0.0" } # For dump_only_default_and_aux_cfs_under_shielded_subtree_prefix — same # subtree-prefix algorithm grovedb uses internally. -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "a2791bbdca756d6a6113024aec48f09f7a33faa9" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "a2791bbdca756d6a6113024aec48f09f7a33faa9" } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee" } [features] default = ["bls-signatures"] diff --git a/packages/rs-drive-abci/src/query/document_query/v1/tests.rs b/packages/rs-drive-abci/src/query/document_query/v1/tests.rs index 475419587a2..168a8887899 100644 --- a/packages/rs-drive-abci/src/query/document_query/v1/tests.rs +++ b/packages/rs-drive-abci/src/query/document_query/v1/tests.rs @@ -2504,9 +2504,12 @@ mod ranked_tests { assert_eq!(tail.skipped, Some(3)); // A window entirely past the end is an empty page, not an - // error. On this *unproven* path grovedb's read API doesn't - // report the short walk, so `skipped` echoes the request; the - // proved path is where it becomes the attested population. + // error, and `skipped` collapses to the population the walk + // actually reached. That reaches the wire on this *unproven* + // path too: grovedb's counted descent tracks how far the skip + // got and returns it on the page, so the server no longer has + // to echo the requested 9 back. This is the wire-visible half + // of that behaviour change. let past_end = ranked_page(&platform, &state, paged(2, 9), version); assert!( past_end.entries.is_empty(), @@ -2514,7 +2517,12 @@ mod ranked_tests { error — got {:?}", group_keys(&past_end.entries) ); - assert_eq!(past_end.skipped, Some(9)); + assert_eq!( + past_end.skipped, + Some(5), + "the response reports the five groups the ranking holds, not the offset that \ + was asked for" + ); // And the same page proves. let result = platform diff --git a/packages/rs-drive/Cargo.toml b/packages/rs-drive/Cargo.toml index f04f8410396..e32e43be7d5 100644 --- a/packages/rs-drive/Cargo.toml +++ b/packages/rs-drive/Cargo.toml @@ -52,12 +52,12 @@ enum-map = { version = "2.0.3", optional = true } intmap = { version = "3.0.1", features = ["serde"], optional = true } chrono = { version = "0.4.35", optional = true } itertools = { version = "0.13", optional = true } -grovedb = { git = "https://github.com/dashpay/grovedb", rev = "a2791bbdca756d6a6113024aec48f09f7a33faa9", optional = true, default-features = false } -grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "a2791bbdca756d6a6113024aec48f09f7a33faa9", optional = true } -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "a2791bbdca756d6a6113024aec48f09f7a33faa9" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "a2791bbdca756d6a6113024aec48f09f7a33faa9", optional = true } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "a2791bbdca756d6a6113024aec48f09f7a33faa9" } -grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "a2791bbdca756d6a6113024aec48f09f7a33faa9" } +grovedb = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee", optional = true, default-features = false } +grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee", optional = true } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee", optional = true } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee" } +grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee" } [dev-dependencies] criterion = "0.5" diff --git a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs index c4e30fd9927..20270ebfaf9 100644 --- a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs +++ b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs @@ -1557,10 +1557,10 @@ fn an_offset_window_spanning_the_end_returns_the_short_tail() { /// The page comes back empty and `skipped` collapses below the requested /// offset — and *that shape* is the proof that the ranking holds exactly /// `skipped` groups in total, because the counted commitments cover the whole -/// walk. It is the only way this surface reports a population, and the one -/// place the proved and unproven paths differ: the unproven read cannot see -/// the short walk (grovedb's read API returns an empty vector either way) and -/// reports the requested offset. +/// walk. It is the only way this surface reports a population, and both paths +/// now report it: grovedb's counted descent tracks how far the skip got and +/// returns it on the page, so the unproven read no longer has to echo the +/// request back. What proving still adds is that the number is attested. #[test] fn an_offset_past_the_end_returns_an_empty_page_whose_skip_attests_the_population() { let (drive, contract) = setup_restaurants(); @@ -1572,8 +1572,9 @@ fn an_offset_past_the_end_returns_an_empty_page_whose_skip_attests_the_populatio "there is no rank 12 in a five-group ranking" ); assert_eq!( - page.skipped, 12, - "the unproven read echoes the requested offset — it has nothing to attest with" + page.skipped, 5, + "the unproven read reports the five groups the ranking holds, not the requested \ + offset of 12" ); let verified = verified_ranked_avg_page(&drive, &contract, 3, 12); diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/execute_top_k.rs b/packages/rs-drive/src/query/drive_document_ranked_query/execute_top_k.rs index fb31a6635ff..4530b9cfd5d 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/execute_top_k.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/execute_top_k.rs @@ -15,7 +15,7 @@ use crate::drive::Drive; use crate::error::drive::DriveError; use crate::error::Error; use dpp::version::PlatformVersion; -use grovedb::TransactionArg; +use grovedb::{IndexedTopKPage, TransactionArg}; use grovedb_costs::CostContext; impl DriveDocumentRankedQuery<'_> { @@ -38,12 +38,33 @@ impl DriveDocumentRankedQuery<'_> { /// no-proof and prove paths read the same code path in grovedb and /// cannot drift on the walk's semantics for offset-free queries. /// - /// [`RankedPage::skipped`] on this path is the *requested* offset: - /// grovedb's read API returns an empty vector when the walk runs out - /// during the skip and does not report how far it got, so an - /// unproven read cannot distinguish "skipped exactly `offset`" from - /// "the secondary holds fewer than `offset` groups". Only the proved - /// path attests the true value — see [`RankedPage::skipped`]. + /// # The offset is counted, not walked + /// + /// grovedb descends the secondary reading each subtree's aggregate + /// count off its link, and collapses any subtree that fits entirely + /// inside the remaining offset instead of stepping through it. The + /// skip therefore costs `O(log n)` at any offset rather than one + /// iterator step and one decode per skipped entry, and an offset at + /// or past the population is answered from the root's own count with + /// no descent at all — the cheapest request on this surface rather + /// than the most expensive. `offset = 0` keeps the plain iterator + /// path and never touches the tree, so the common unpaginated + /// request costs exactly what it always did. + /// + /// That is what makes an uncapped `OFFSET` safe rather than merely + /// tolerated. Ranked queries carry no fee, cannot be cancelled once + /// dispatched, and share their rate budget with state transitions + /// rather than having one of their own, so a skip whose cost grew + /// with the offset would be an unmetered lever for any + /// unauthenticated caller. It does not grow. + /// + /// [`RankedPage::skipped`] comes back from grovedb rather than being + /// echoed from the request: it is the requested offset when the skip + /// succeeded, and the secondary's whole population when the walk ran + /// out of groups first. That is the same quantity the proved path + /// attests, so the two no longer disagree — though on this path it is + /// the node's unverified claim rather than an attested value, exactly + /// like the entries beside it. See [`RankedPage::skipped`]. pub fn execute_top_k_no_proof( &self, drive: &Drive, @@ -55,13 +76,15 @@ impl DriveDocumentRankedQuery<'_> { let path_refs: Vec<&[u8]> = path.iter().map(|segment| segment.as_slice()).collect(); let offset = self.offset as u64; - // Costs are destructured away rather than `.unwrap()`-ed: + // The cost is dropped rather than `.unwrap()`-ed: // `CostContext::unwrap` is infallible (it drops the cost field) - // but reads like a panicking unwrap at the call site. The - // dispatcher wraps these executors with its own fee accounting, - // exactly as the count surface's `execute_range_count_no_proof` - // does. - let entries = match self.axis { + // but reads like a panicking unwrap at the call site. Dropping it + // is all there is to do with it — nothing meters a query on this + // surface: neither this executor's caller nor the dispatcher + // above it accumulates or charges the cost, and no credit is + // debited for a read. grovedb computes the `OperationCost` + // because its API always does, and it ends here. + let (entries, skipped) = match self.axis { RankedAxis::Count => { let CostContext { value, cost: _ } = drive.grove.indexed_count_top_k_paginated( path_refs.as_slice(), @@ -71,14 +94,18 @@ impl DriveDocumentRankedQuery<'_> { transaction, grove_version, ); - value - .map_err(|e| Error::GroveDB(Box::new(e)))? - .into_iter() - .map(|(count, key)| RankedEntry { - key, - value: RankedEntryValue::Count(count), - }) - .collect::>() + let IndexedTopKPage { entries, skipped } = + value.map_err(|e| Error::GroveDB(Box::new(e)))?; + ( + entries + .into_iter() + .map(|(count, key)| RankedEntry { + key, + value: RankedEntryValue::Count(count), + }) + .collect::>(), + skipped, + ) } RankedAxis::Sum => { let CostContext { value, cost: _ } = drive.grove.indexed_sum_top_k_paginated( @@ -89,14 +116,18 @@ impl DriveDocumentRankedQuery<'_> { transaction, grove_version, ); - value - .map_err(|e| Error::GroveDB(Box::new(e)))? - .into_iter() - .map(|(sum, key)| RankedEntry { - key, - value: RankedEntryValue::Sum(sum), - }) - .collect::>() + let IndexedTopKPage { entries, skipped } = + value.map_err(|e| Error::GroveDB(Box::new(e)))?; + ( + entries + .into_iter() + .map(|(sum, key)| RankedEntry { + key, + value: RankedEntryValue::Sum(sum), + }) + .collect::>(), + skipped, + ) } RankedAxis::Avg => { let CostContext { value, cost: _ } = drive.grove.indexed_avg_top_k_paginated( @@ -107,14 +138,18 @@ impl DriveDocumentRankedQuery<'_> { transaction, grove_version, ); - value - .map_err(|e| Error::GroveDB(Box::new(e)))? - .into_iter() - .map(|(avg, key)| RankedEntry { - key, - value: RankedEntryValue::AvgFixedPoint(avg), - }) - .collect::>() + let IndexedTopKPage { entries, skipped } = + value.map_err(|e| Error::GroveDB(Box::new(e)))?; + ( + entries + .into_iter() + .map(|(avg, key)| RankedEntry { + key, + value: RankedEntryValue::AvgFixedPoint(avg), + }) + .collect::>(), + skipped, + ) } }; @@ -131,10 +166,7 @@ impl DriveDocumentRankedQuery<'_> { self.k )))); } - Ok(RankedPage { - skipped: offset, - entries, - }) + Ok(RankedPage { skipped, entries }) } /// Generate the grovedb indexed-axis paginated top-k proof for this diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/executors/top_k_no_proof.rs b/packages/rs-drive/src/query/drive_document_ranked_query/executors/top_k_no_proof.rs index 281fdfe2c2a..4345618e5f3 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/executors/top_k_no_proof.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/executors/top_k_no_proof.rs @@ -1,6 +1,11 @@ -//! Ranked executor for `prove = false` — reads one page of `k` groups -//! straight out of the axis secondary, starting at rank `offset`, and -//! returns them in ranking order. +//! Ranked executor for `prove = false` — one page of `k` groups starting +//! at rank `offset`, in ranking order. +//! +//! The page is read from the axis secondary directly, with no proof +//! built. The `offset` is skipped by a counted descent rather than by +//! stepping through the skipped entries, so a deep offset costs +//! `O(log n)` — see +//! [`crate::query::DriveDocumentRankedQuery::execute_top_k_no_proof`]. use super::super::{DocumentRankedMode, RankedPage}; use super::ranked_query_for_mode; diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/mod.rs b/packages/rs-drive/src/query/drive_document_ranked_query/mod.rs index fed2b5dffbd..2b755d9dd60 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/mod.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/mod.rs @@ -312,14 +312,17 @@ pub struct DriveDocumentRankedQuery<'a> { /// How many ranks to skip before the returned page — the request's /// `OFFSET`. `0` for an unpaginated ranking. /// - /// Unbounded above (any `u32`), on purpose. grovedb attests the - /// skipped region through the counted subtree commitments - /// (`HashWithCount` / `HashWithCountAndSum`) rather than by walking - /// it, so both the prover's work and the proof's size stay - /// `O(log n + k)` **at any offset** — an offset of 4 and an offset - /// of four billion cost the same. There is therefore no - /// denial-of-service lever to cap, and capping would only stop - /// honest deep pagination. + /// Unbounded above (any `u32`), on purpose. grovedb skips by + /// counting rather than walking — descending the secondary on each + /// subtree's aggregate count (`HashWithCount` / + /// `HashWithCountAndSum`) and collapsing any subtree that fits + /// inside the remaining offset — so work and proof size stay + /// `O(log n + k)` **at any offset**, and an offset of 4 and an + /// offset of four billion cost the same order of work, the deeper + /// one in fact slightly less. Both executors go through that + /// descent, the unproved one without building a proof, so there is + /// no denial-of-service lever to cap on either path and capping + /// would only stop honest deep pagination. /// /// An offset past the end of the secondary is a provable answer, not /// an error: the page comes back empty and @@ -339,19 +342,28 @@ pub struct DriveDocumentRankedQuery<'a> { pub struct RankedPage { /// Number of secondary entries skipped before this page. /// - /// On the **proved** path this is grovedb's cryptographically - /// attested count, independently re-derived by the verifier from the - /// counted subtree commitments in the proof bytes: it equals the - /// requested offset unless the walk ran out of entries first, in - /// which case `entries` is empty and `skipped` is a proof that the - /// secondary holds exactly `skipped` groups in total. + /// Both paths report the same quantity, and it is never an echo of + /// the request: grovedb's counted descent tracks how far the skip + /// actually got, so this equals the requested offset when the skip + /// succeeded and the secondary's whole population when the walk ran + /// out of groups first (in which case `entries` is empty). /// - /// On the **unproven** read there is nothing to attest and grovedb's - /// read API does not report the short walk, so this is simply the - /// requested offset. The two paths therefore disagree in exactly one - /// case — an offset past the end — where the unproven read reports - /// the requested offset and the proved one reports the true - /// population. Callers that need the population must prove. + /// What differs between the paths is the warrant. On the **proved** + /// path the value is cryptographically attested — independently + /// re-derived by the verifier from the counted subtree commitments + /// in the proof bytes — so a verifying client uses its own + /// reconstruction rather than trusting the server's. On the + /// **unproven** read it is the node's unverified claim, exactly like + /// the entries beside it: equal to the attested value on an honest + /// node, with nothing forcing a node to be honest. + /// + /// One nuance worth knowing on the unproven path: the population is + /// read from the secondary's root aggregate, while grovedb's + /// per-node payload check only fires on nodes the descent visits. In + /// a *corrupt* secondary whose count violation lies outside the + /// visited region, this value can therefore disagree with the true + /// row count where the proved path's would not. On any valid + /// secondary the two are identical by construction. pub skipped: u64, /// The groups on this page, **in ranking order**. Never longer than /// the query's `k`. diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/mode_detection/v0/mod.rs b/packages/rs-drive/src/query/drive_document_ranked_query/mode_detection/v0/mod.rs index 8a96f033b47..37d87274e45 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/mode_detection/v0/mod.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/mode_detection/v0/mod.rs @@ -300,14 +300,20 @@ pub fn detect_ranked_mode_v0( // ---- OFFSET: optional, unbounded -------------------------------- // // No ceiling, and that is a deliberate statement about cost rather - // than an oversight: grovedb's paginated prover attests the skipped - // region from the counted subtree commitments instead of walking - // it, so proving `OFFSET 4` and `OFFSET 4_000_000_000` are the same - // O(log n + k) work and the same proof size. There is no - // denial-of-service lever here to cap, and an arbitrary cap would - // only break honest deep pagination. An offset past the end is a - // provable answer (empty page, `skipped` attesting the population), - // not an error. + // than an oversight. grovedb skips by *counting*, not by walking: + // it descends the secondary reading each subtree's aggregate count + // and collapses any subtree that fits inside the remaining offset, + // so `OFFSET 4` and `OFFSET 4_000_000_000` are the same order of + // O(log n + k) work — the deeper one in fact cheaper, since a tree + // that fits entirely inside the offset collapses at the root. + // + // Both executors get that: the prover attests the skipped region + // from the counted subtree commitments, and the unproved read + // performs the same counted descent without building a proof. So + // there is no denial-of-service lever here for a cap to close on + // either path, and an arbitrary cap would only break honest deep + // pagination. An offset past the end is a real answer (empty page, + // `skipped` reporting the population), not an error. let offset = pagination.offset.unwrap_or(0); // ---- START AT: must be absent ----------------------------------- diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/tests.rs b/packages/rs-drive/src/query/drive_document_ranked_query/tests.rs index 4a477c98195..18d3eeee326 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/tests.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/tests.rs @@ -1298,10 +1298,11 @@ fn top_k_larger_than_the_group_count_returns_every_group() { /// page is. /// 3. **A window entirely past the end** — the page is empty *and* /// `skipped` collapses to the secondary's true population, which is -/// the proof's way of saying "there is nothing here, and here is how -/// much there is in total". That is the one case where the proved -/// and unproven paths differ: the unproven read cannot see the short -/// walk and reports the requested offset. +/// the counted walk's way of saying "there is nothing here, and here +/// is how much there is in total". Both paths report it — the +/// unproven read gets it from grovedb's counted descent rather than +/// echoing the request, as it used to — so case (3) is where that +/// echo is pinned as gone. #[test] fn offset_pages_through_the_ranking_and_the_proof_attests_the_starting_rank() { let (drive, contract) = setup_restaurants(); @@ -1360,8 +1361,10 @@ fn offset_pages_through_the_ranking_and_the_proof_attests_the_starting_rank() { "there is no rank 9 in a five-group ranking" ); assert_eq!( - page.skipped, 9, - "the unproven read cannot see the short walk, so it echoes the requested offset" + page.skipped, 5, + "the unproven read reports the population it actually reached, not the requested \ + offset of 9: grovedb's counted descent knows how far the walk got and returns it \ + on the page. This assertion is where the old echo is pinned as gone." ); let verified = assert_proof_round_trips(&drive, &contract, &past_end, &page.entries); assert_eq!( diff --git a/packages/rs-platform-version/Cargo.toml b/packages/rs-platform-version/Cargo.toml index 27742bc04de..7b63bda56a1 100644 --- a/packages/rs-platform-version/Cargo.toml +++ b/packages/rs-platform-version/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT" thiserror = { version = "2.0.12" } bincode = { version = "=2.0.1" } versioned-feature-core = { git = "https://github.com/dashpay/versioned-feature-core", version = "1.0.0" } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "a2791bbdca756d6a6113024aec48f09f7a33faa9" } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee" } [features] mock-versions = [] diff --git a/packages/rs-platform-wallet/Cargo.toml b/packages/rs-platform-wallet/Cargo.toml index 7d404f6985a..4d8a545b750 100644 --- a/packages/rs-platform-wallet/Cargo.toml +++ b/packages/rs-platform-wallet/Cargo.toml @@ -69,7 +69,7 @@ zeroize = "1" log = "0.4" # Shielded pool (optional, behind `shielded` feature) -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "a2791bbdca756d6a6113024aec48f09f7a33faa9", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee", optional = true } # Direct `rusqlite` access so `FileBackedShieldedStore::open_path` can set # WAL + synchronous=NORMAL pragmas before handing the connection to # `ClientPersistentCommitmentTree`. Version locked to match the rev grovedb diff --git a/packages/rs-sdk/Cargo.toml b/packages/rs-sdk/Cargo.toml index 8f8ba61d9df..fafc6e9dbd3 100644 --- a/packages/rs-sdk/Cargo.toml +++ b/packages/rs-sdk/Cargo.toml @@ -18,7 +18,7 @@ drive = { path = "../rs-drive", default-features = false, features = [ ] } drive-proof-verifier = { path = "../rs-drive-proof-verifier", default-features = false } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "a2791bbdca756d6a6113024aec48f09f7a33faa9", features = [ +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee", features = [ "client", "sqlite", ], optional = true } From 0d504f2625ac679bb814c59e471b10d1c8193fa8 Mon Sep 17 00:00:00 2001 From: Ivan Shumkov Date: Fri, 14 Aug 2026 00:36:55 +0700 Subject: [PATCH 02/25] chore(deps): re-pin grovedb to cc7b3997 for the feature-gate fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `e41d57e0` exported `IndexedTopKPage` under `any(minimal, verify)` while the module holding it is gated on `minimal` alone, so any build enabling `verify` without `minimal` failed to compile: error[E0432]: unresolved import `operations::indexed_tree` note: found an item that was configured out — gated behind `minimal` That is drive's verifier-only cut, which CI builds as "Check transport-free feature cut" and which the Kotlin native-library job hits too. `cc7b3997` narrows the export's gate to match the module's, and adds a grovedb-side test pinning that the unproved `skipped` equals the proved path's attested value — the property this PR's assertions rest on. Re-pinned across all 14 workspace entries with `Cargo.lock` regenerated; no reference to the old rev remains anywhere in the tree. Verified with the exact invocation that reproduced the failure: `cargo check -p drive --no-default-features --features verify`, clean. --- Cargo.lock | 60 ++++++++++++------------- packages/rs-dpp/Cargo.toml | 2 +- packages/rs-drive-abci/Cargo.toml | 8 ++-- packages/rs-drive/Cargo.toml | 12 ++--- packages/rs-platform-version/Cargo.toml | 2 +- packages/rs-platform-wallet/Cargo.toml | 2 +- packages/rs-sdk/Cargo.toml | 2 +- 7 files changed, 44 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 58523ec401f..ac6e455f788 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -576,7 +576,7 @@ dependencies = [ "bitflags 2.13.0", "cexpr", "clang-sys", - "itertools 0.13.0", + "itertools 0.10.5", "proc-macro2", "quote", "regex", @@ -1229,7 +1229,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2475,7 +2475,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2955,7 +2955,7 @@ dependencies = [ [[package]] name = "grovedb" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" +source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" dependencies = [ "axum 0.8.9", "bincode", @@ -2993,7 +2993,7 @@ dependencies = [ [[package]] name = "grovedb-bulk-append-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" +source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" dependencies = [ "bincode", "blake3", @@ -3009,7 +3009,7 @@ dependencies = [ [[package]] name = "grovedb-commitment-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" +source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" dependencies = [ "blake3", "grovedb-bulk-append-tree", @@ -3025,7 +3025,7 @@ dependencies = [ [[package]] name = "grovedb-costs" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" +source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" dependencies = [ "integer-encoding", "intmap", @@ -3035,7 +3035,7 @@ dependencies = [ [[package]] name = "grovedb-dense-fixed-sized-merkle-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" +source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" dependencies = [ "bincode", "blake3", @@ -3048,7 +3048,7 @@ dependencies = [ [[package]] name = "grovedb-element" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" +source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" dependencies = [ "bincode", "bincode_derive", @@ -3063,7 +3063,7 @@ dependencies = [ [[package]] name = "grovedb-epoch-based-storage-flags" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" +source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" dependencies = [ "grovedb-costs", "hex", @@ -3075,7 +3075,7 @@ dependencies = [ [[package]] name = "grovedb-merk" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" +source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" dependencies = [ "bincode", "bincode_derive", @@ -3101,7 +3101,7 @@ dependencies = [ [[package]] name = "grovedb-merkle-mountain-range" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" +source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" dependencies = [ "bincode", "blake3", @@ -3112,7 +3112,7 @@ dependencies = [ [[package]] name = "grovedb-path" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" +source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" dependencies = [ "hex", ] @@ -3120,7 +3120,7 @@ dependencies = [ [[package]] name = "grovedb-query" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" +source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" dependencies = [ "bincode", "byteorder", @@ -3136,7 +3136,7 @@ dependencies = [ [[package]] name = "grovedb-storage" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" +source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" dependencies = [ "blake3", "grovedb-costs", @@ -3155,7 +3155,7 @@ dependencies = [ [[package]] name = "grovedb-version" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" +source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" dependencies = [ "thiserror 2.0.18", "versioned-feature-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3164,7 +3164,7 @@ dependencies = [ [[package]] name = "grovedb-visualize" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" +source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" dependencies = [ "hex", "itertools 0.14.0", @@ -3173,7 +3173,7 @@ dependencies = [ [[package]] name = "grovedbg-types" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=e41d57e08b36225a52c8a6730a5cc9a824876eee#e41d57e08b36225a52c8a6730a5cc9a824876eee" +source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" dependencies = [ "serde", "serde_with 3.21.0", @@ -3589,7 +3589,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.4", + "socket2 0.5.10", "system-configuration", "tokio", "tower-service", @@ -3840,7 +3840,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4652,7 +4652,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -5542,7 +5542,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" dependencies = [ "heck 0.4.1", - "itertools 0.13.0", + "itertools 0.10.5", "log", "multimap", "petgraph", @@ -5563,7 +5563,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.117", @@ -5576,7 +5576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.117", @@ -5712,7 +5712,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.2", "rustls", - "socket2 0.6.4", + "socket2 0.5.10", "thiserror 2.0.18", "tokio", "tracing", @@ -5750,7 +5750,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.4", + "socket2 0.5.10", "tracing", "windows-sys 0.59.0", ] @@ -6573,7 +6573,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -6632,7 +6632,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -7492,7 +7492,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -8941,7 +8941,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/packages/rs-dpp/Cargo.toml b/packages/rs-dpp/Cargo.toml index 642dd68b594..c2e2d647958 100644 --- a/packages/rs-dpp/Cargo.toml +++ b/packages/rs-dpp/Cargo.toml @@ -71,7 +71,7 @@ strum = { version = "0.26", features = ["derive"] } json-schema-compatibility-validator = { path = '../rs-json-schema-compatibility-validator', optional = true } once_cell = "1.19.0" tracing = { version = "0.1.41" } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f", optional = true } [dev-dependencies] tokio = { version = "1.40", features = ["full"] } diff --git a/packages/rs-drive-abci/Cargo.toml b/packages/rs-drive-abci/Cargo.toml index 598f62f916a..09c44570ec8 100644 --- a/packages/rs-drive-abci/Cargo.toml +++ b/packages/rs-drive-abci/Cargo.toml @@ -82,7 +82,7 @@ derive_more = { version = "1.0", features = ["from", "deref", "deref_mut"] } async-trait = "0.1.77" console-subscriber = { version = "0.4", optional = true } bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f", optional = true } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee" } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f" } nonempty = "0.11" # Shielded-pool snapshot needs raw RocksDB SstFileWriter + ingest_external_file_cf # bindings, and blake3 for the snapshot-file checksum. @@ -107,7 +107,7 @@ dpp = { path = "../rs-dpp", default-features = false, features = [ drive = { path = "../rs-drive", features = ["fixtures-and-mocks"] } drive-proof-verifier = { path = "../rs-drive-proof-verifier" } strategy-tests = { path = "../strategy-tests" } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee", features = ["client"] } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f", features = ["client"] } assert_matches = "1.5.0" drive-abci = { path = ".", features = ["testing-config", "mocks", "shielded_test_data"] } bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f" } @@ -121,8 +121,8 @@ integer-encoding = { version = "4.0.0" } # For dump_only_default_and_aux_cfs_under_shielded_subtree_prefix — same # subtree-prefix algorithm grovedb uses internally. -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee" } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f" } [features] default = ["bls-signatures"] diff --git a/packages/rs-drive/Cargo.toml b/packages/rs-drive/Cargo.toml index e32e43be7d5..3a475c19036 100644 --- a/packages/rs-drive/Cargo.toml +++ b/packages/rs-drive/Cargo.toml @@ -52,12 +52,12 @@ enum-map = { version = "2.0.3", optional = true } intmap = { version = "3.0.1", features = ["serde"], optional = true } chrono = { version = "0.4.35", optional = true } itertools = { version = "0.13", optional = true } -grovedb = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee", optional = true, default-features = false } -grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee", optional = true } -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee", optional = true } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee" } -grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee" } +grovedb = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f", optional = true, default-features = false } +grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f", optional = true } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f", optional = true } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f" } +grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f" } [dev-dependencies] criterion = "0.5" diff --git a/packages/rs-platform-version/Cargo.toml b/packages/rs-platform-version/Cargo.toml index 7b63bda56a1..8bccf00e58d 100644 --- a/packages/rs-platform-version/Cargo.toml +++ b/packages/rs-platform-version/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT" thiserror = { version = "2.0.12" } bincode = { version = "=2.0.1" } versioned-feature-core = { git = "https://github.com/dashpay/versioned-feature-core", version = "1.0.0" } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee" } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f" } [features] mock-versions = [] diff --git a/packages/rs-platform-wallet/Cargo.toml b/packages/rs-platform-wallet/Cargo.toml index 4d8a545b750..8c04bed5631 100644 --- a/packages/rs-platform-wallet/Cargo.toml +++ b/packages/rs-platform-wallet/Cargo.toml @@ -69,7 +69,7 @@ zeroize = "1" log = "0.4" # Shielded pool (optional, behind `shielded` feature) -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f", optional = true } # Direct `rusqlite` access so `FileBackedShieldedStore::open_path` can set # WAL + synchronous=NORMAL pragmas before handing the connection to # `ClientPersistentCommitmentTree`. Version locked to match the rev grovedb diff --git a/packages/rs-sdk/Cargo.toml b/packages/rs-sdk/Cargo.toml index fafc6e9dbd3..7b1267632cb 100644 --- a/packages/rs-sdk/Cargo.toml +++ b/packages/rs-sdk/Cargo.toml @@ -18,7 +18,7 @@ drive = { path = "../rs-drive", default-features = false, features = [ ] } drive-proof-verifier = { path = "../rs-drive-proof-verifier", default-features = false } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "e41d57e08b36225a52c8a6730a5cc9a824876eee", features = [ +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f", features = [ "client", "sqlite", ], optional = true } From 0f44e701304606e1c06f84e439614c88d18de8c3 Mon Sep 17 00:00:00 2001 From: Ivan Shumkov Date: Fri, 14 Aug 2026 00:41:07 +0700 Subject: [PATCH 03/25] docs(drive): stop calling the unproved skip attested, and drop the narration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two fixes from an independent review of the rebuilt diff. The past-the-end paragraph called `skipped` the ranking's "attested total population" while the paragraph below it correctly says an unproved response carries only the node's unverified claim, so the public contract contradicted itself within one comment. It now says "reported", and the warrant distinction is stated once, in the place that explains it. Synced across the proto, the Objective-C client that carries proto prose, and the book. The request-side proto text that said the response "echoes the attested number" is corrected the same way — it echoes nothing, and the number is only attested when proving. The comments also narrated the change rather than describing the code: "as it used to", "pinned as gone", "no longer has to", "the wire-visible half of that behaviour change". That reads as a diary to anyone who arrives after the change is history, which the repo's comment rule exists to prevent. Rewritten as current behaviour; the assertions are unchanged. --- book/src/drive/ranked-index-examples.md | 2 +- .../clients/platform/v0/objective-c/Platform.pbobjc.h | 2 +- packages/dapi-grpc/protos/platform/v0/platform.proto | 4 ++-- .../rs-drive-abci/src/query/document_query/v1/tests.rs | 5 ++--- .../insert_contract/v0/tests/ranked_index_e2e_tests.rs | 6 +++--- .../src/query/drive_document_ranked_query/tests.rs | 9 ++++----- 6 files changed, 13 insertions(+), 15 deletions(-) diff --git a/book/src/drive/ranked-index-examples.md b/book/src/drive/ranked-index-examples.md index 640f8684c6a..c702e51fa28 100644 --- a/book/src/drive/ranked-index-examples.md +++ b/book/src/drive/ranked-index-examples.md @@ -323,7 +323,7 @@ Three properties worth stating plainly: - **The skip is counted, not walked.** grovedb descends the secondary reading each subtree's aggregate count and collapses any subtree that fits entirely inside the remaining offset, instead of stepping through it. Both paths do this: the prover attests the skipped region from the counted subtree commitments (`HashWithCount` / `HashWithCountAndSum`), and the unproven read performs the same counted descent without building a proof. Work and proof size stay `O(log n + k)` **at any offset**. - **There is therefore no offset ceiling.** An offset of 4 and an offset of four billion cost the same order of work — on either path, the deeper one in fact cheaper, since a tree that fits entirely inside the offset collapses at the root. There is no denial-of-service lever a cap would close, and a cap would only stop honest deep pagination. -- **An offset past the end is a positive answer.** `entries` comes back empty and `skipped` is the ranking's *entire attested population*. "There are only 12 groups" is more information than a bare empty list. +- **An offset past the end is a positive answer.** `entries` comes back empty and `skipped` is the ranking's *entire reported population*. "There are only 12 groups" is more information than a bare empty list. Both paths report the same `skipped`: the offset you asked for when the skip succeeded, and the ranking's total population when the walk ran out of groups first. What differs is the warrant, not the value. On the proved path it is cryptographically attested, re-derived by the verifier from the counted commitments. On the unproven path it is an **unverified claim, exactly like the entries beside it** — equal to the attested value on an honest node, with nothing forcing a node to be honest. **Callers who need to trust the population, rather than merely receive it, must still prove.** diff --git a/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h index 1aecbca6b63..6c8dc26eb3e 100644 --- a/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h +++ b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h @@ -3842,7 +3842,7 @@ GPB_FINAL @interface GetDocumentsResponse_GetDocumentsResponseV1_RankedEntries : * group rather than the best. * * **When a requested offset exceeds the population**, `entries` - * is empty and `skipped` is the ranking's attested *total* + * is empty and `skipped` is the ranking's *total* reported * population — a positive, useful answer ("there are only 12 * groups") rather than a bare empty list. * diff --git a/packages/dapi-grpc/protos/platform/v0/platform.proto b/packages/dapi-grpc/protos/platform/v0/platform.proto index c507e72a40a..728bb8dffcd 100644 --- a/packages/dapi-grpc/protos/platform/v0/platform.proto +++ b/packages/dapi-grpc/protos/platform/v0/platform.proto @@ -1128,7 +1128,7 @@ message GetDocumentsRequest { // OFFSET 4` is the 5th-best group. The skip is **count-attested**, // not walked: grovedb proves it from the counted subtree // commitments, so the proof stays `O(log n + k)` at any offset and - // the response echoes the attested number in + // the response reports the skip it performed in // `RankedEntries.skipped`. There is deliberately no ceiling — an // offset of 4 and an offset of four billion cost the same, so // there is no denial-of-service lever a cap would close. An offset @@ -1417,7 +1417,7 @@ message GetDocumentsResponse { // group rather than the best. // // **When a requested offset exceeds the population**, `entries` - // is empty and `skipped` is the ranking's attested *total* + // is empty and `skipped` is the ranking's *total* reported // population — a positive, useful answer ("there are only 12 // groups") rather than a bare empty list. // diff --git a/packages/rs-drive-abci/src/query/document_query/v1/tests.rs b/packages/rs-drive-abci/src/query/document_query/v1/tests.rs index 168a8887899..50fadc2e293 100644 --- a/packages/rs-drive-abci/src/query/document_query/v1/tests.rs +++ b/packages/rs-drive-abci/src/query/document_query/v1/tests.rs @@ -2507,9 +2507,8 @@ mod ranked_tests { // error, and `skipped` collapses to the population the walk // actually reached. That reaches the wire on this *unproven* // path too: grovedb's counted descent tracks how far the skip - // got and returns it on the page, so the server no longer has - // to echo the requested 9 back. This is the wire-visible half - // of that behaviour change. + // got and returns it on the page, so the response carries a + // population rather than the offset that was requested. let past_end = ranked_page(&platform, &state, paged(2, 9), version); assert!( past_end.entries.is_empty(), diff --git a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs index 20270ebfaf9..a8ae0a604f8 100644 --- a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs +++ b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs @@ -1558,9 +1558,9 @@ fn an_offset_window_spanning_the_end_returns_the_short_tail() { /// offset — and *that shape* is the proof that the ranking holds exactly /// `skipped` groups in total, because the counted commitments cover the whole /// walk. It is the only way this surface reports a population, and both paths -/// now report it: grovedb's counted descent tracks how far the skip got and -/// returns it on the page, so the unproven read no longer has to echo the -/// request back. What proving still adds is that the number is attested. +/// report it: grovedb's counted descent tracks how far the skip got and returns +/// it on the page, so an unproven read reports the population rather than the +/// offset it was asked for. What proving adds is that the number is attested. #[test] fn an_offset_past_the_end_returns_an_empty_page_whose_skip_attests_the_population() { let (drive, contract) = setup_restaurants(); diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/tests.rs b/packages/rs-drive/src/query/drive_document_ranked_query/tests.rs index 18d3eeee326..0c79345d816 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/tests.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/tests.rs @@ -1299,10 +1299,9 @@ fn top_k_larger_than_the_group_count_returns_every_group() { /// 3. **A window entirely past the end** — the page is empty *and* /// `skipped` collapses to the secondary's true population, which is /// the counted walk's way of saying "there is nothing here, and here -/// is how much there is in total". Both paths report it — the -/// unproven read gets it from grovedb's counted descent rather than -/// echoing the request, as it used to — so case (3) is where that -/// echo is pinned as gone. +/// is how much there is in total". Both paths report it: the counted +/// descent tracks how far the skip got, so an unproven read reports a +/// population rather than the offset it was asked for. #[test] fn offset_pages_through_the_ranking_and_the_proof_attests_the_starting_rank() { let (drive, contract) = setup_restaurants(); @@ -1364,7 +1363,7 @@ fn offset_pages_through_the_ranking_and_the_proof_attests_the_starting_rank() { page.skipped, 5, "the unproven read reports the population it actually reached, not the requested \ offset of 9: grovedb's counted descent knows how far the walk got and returns it \ - on the page. This assertion is where the old echo is pinned as gone." + on the page" ); let verified = assert_proof_round_trips(&drive, &contract, &past_end, &page.entries); assert_eq!( From 7091b332120f35e7155a7867fa98a209c22bca6a Mon Sep 17 00:00:00 2001 From: Ivan Shumkov Date: Fri, 14 Aug 2026 01:27:07 +0700 Subject: [PATCH 04/25] chore(deps): re-pin grovedb to c4ceac67, the rev that tested green `cc7b3997` predated the fixes for dashpay/grovedb#792's own Linting and codecov failures. `c4ceac67` is the rev where all 11 of that PR's checks pass, so this pins the code that was actually verified rather than an intermediate commit. All 14 workspace entries plus `Cargo.lock`; no reference to any earlier rev (`cc7b3997`, `e41d57e0`, `a2791bbd`) remains anywhere in the tree. Still a branch rev, deliberately: pinning the tested commit beats pinning an untested one, and the alternative is blocking on a merge. To be re-pinned to the develop merge commit once #792 lands, since a Platform PR pinning a branch that could later be deleted is a fair review objection. Verified: `cargo check -p drive --no-default-features --features verify` (the cut that caught the last feature-gate bug) clean; drive ranked 71 passed, drive-abci ranked 18 passed, fmt clean. --- Cargo.lock | 44 ++++++++++++------------- packages/rs-dpp/Cargo.toml | 2 +- packages/rs-drive-abci/Cargo.toml | 8 ++--- packages/rs-drive/Cargo.toml | 12 +++---- packages/rs-platform-version/Cargo.toml | 2 +- packages/rs-platform-wallet/Cargo.toml | 2 +- packages/rs-sdk/Cargo.toml | 2 +- 7 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac6e455f788..43d8fe3a554 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -576,7 +576,7 @@ dependencies = [ "bitflags 2.13.0", "cexpr", "clang-sys", - "itertools 0.10.5", + "itertools 0.13.0", "proc-macro2", "quote", "regex", @@ -2955,7 +2955,7 @@ dependencies = [ [[package]] name = "grovedb" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" +source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" dependencies = [ "axum 0.8.9", "bincode", @@ -2993,7 +2993,7 @@ dependencies = [ [[package]] name = "grovedb-bulk-append-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" +source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" dependencies = [ "bincode", "blake3", @@ -3009,7 +3009,7 @@ dependencies = [ [[package]] name = "grovedb-commitment-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" +source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" dependencies = [ "blake3", "grovedb-bulk-append-tree", @@ -3025,7 +3025,7 @@ dependencies = [ [[package]] name = "grovedb-costs" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" +source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" dependencies = [ "integer-encoding", "intmap", @@ -3035,7 +3035,7 @@ dependencies = [ [[package]] name = "grovedb-dense-fixed-sized-merkle-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" +source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" dependencies = [ "bincode", "blake3", @@ -3048,7 +3048,7 @@ dependencies = [ [[package]] name = "grovedb-element" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" +source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" dependencies = [ "bincode", "bincode_derive", @@ -3063,7 +3063,7 @@ dependencies = [ [[package]] name = "grovedb-epoch-based-storage-flags" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" +source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" dependencies = [ "grovedb-costs", "hex", @@ -3075,7 +3075,7 @@ dependencies = [ [[package]] name = "grovedb-merk" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" +source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" dependencies = [ "bincode", "bincode_derive", @@ -3101,7 +3101,7 @@ dependencies = [ [[package]] name = "grovedb-merkle-mountain-range" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" +source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" dependencies = [ "bincode", "blake3", @@ -3112,7 +3112,7 @@ dependencies = [ [[package]] name = "grovedb-path" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" +source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" dependencies = [ "hex", ] @@ -3120,7 +3120,7 @@ dependencies = [ [[package]] name = "grovedb-query" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" +source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" dependencies = [ "bincode", "byteorder", @@ -3136,7 +3136,7 @@ dependencies = [ [[package]] name = "grovedb-storage" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" +source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" dependencies = [ "blake3", "grovedb-costs", @@ -3155,7 +3155,7 @@ dependencies = [ [[package]] name = "grovedb-version" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" +source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" dependencies = [ "thiserror 2.0.18", "versioned-feature-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3164,7 +3164,7 @@ dependencies = [ [[package]] name = "grovedb-visualize" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" +source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" dependencies = [ "hex", "itertools 0.14.0", @@ -3173,7 +3173,7 @@ dependencies = [ [[package]] name = "grovedbg-types" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=cc7b3997be5819c60eb09251f963c1ed1a89306f#cc7b3997be5819c60eb09251f963c1ed1a89306f" +source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" dependencies = [ "serde", "serde_with 3.21.0", @@ -3589,7 +3589,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.4", "system-configuration", "tokio", "tower-service", @@ -5542,7 +5542,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" dependencies = [ "heck 0.4.1", - "itertools 0.10.5", + "itertools 0.13.0", "log", "multimap", "petgraph", @@ -5563,7 +5563,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.13.0", "proc-macro2", "quote", "syn 2.0.117", @@ -5576,7 +5576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.13.0", "proc-macro2", "quote", "syn 2.0.117", @@ -5712,7 +5712,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.2", "rustls", - "socket2 0.5.10", + "socket2 0.6.4", "thiserror 2.0.18", "tokio", "tracing", @@ -5750,7 +5750,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.4", "tracing", "windows-sys 0.59.0", ] diff --git a/packages/rs-dpp/Cargo.toml b/packages/rs-dpp/Cargo.toml index c2e2d647958..cd86bb3b92f 100644 --- a/packages/rs-dpp/Cargo.toml +++ b/packages/rs-dpp/Cargo.toml @@ -71,7 +71,7 @@ strum = { version = "0.26", features = ["derive"] } json-schema-compatibility-validator = { path = '../rs-json-schema-compatibility-validator', optional = true } once_cell = "1.19.0" tracing = { version = "0.1.41" } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115", optional = true } [dev-dependencies] tokio = { version = "1.40", features = ["full"] } diff --git a/packages/rs-drive-abci/Cargo.toml b/packages/rs-drive-abci/Cargo.toml index 09c44570ec8..6268cde0068 100644 --- a/packages/rs-drive-abci/Cargo.toml +++ b/packages/rs-drive-abci/Cargo.toml @@ -82,7 +82,7 @@ derive_more = { version = "1.0", features = ["from", "deref", "deref_mut"] } async-trait = "0.1.77" console-subscriber = { version = "0.4", optional = true } bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f", optional = true } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f" } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115" } nonempty = "0.11" # Shielded-pool snapshot needs raw RocksDB SstFileWriter + ingest_external_file_cf # bindings, and blake3 for the snapshot-file checksum. @@ -107,7 +107,7 @@ dpp = { path = "../rs-dpp", default-features = false, features = [ drive = { path = "../rs-drive", features = ["fixtures-and-mocks"] } drive-proof-verifier = { path = "../rs-drive-proof-verifier" } strategy-tests = { path = "../strategy-tests" } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f", features = ["client"] } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115", features = ["client"] } assert_matches = "1.5.0" drive-abci = { path = ".", features = ["testing-config", "mocks", "shielded_test_data"] } bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f" } @@ -121,8 +121,8 @@ integer-encoding = { version = "4.0.0" } # For dump_only_default_and_aux_cfs_under_shielded_subtree_prefix — same # subtree-prefix algorithm grovedb uses internally. -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f" } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115" } [features] default = ["bls-signatures"] diff --git a/packages/rs-drive/Cargo.toml b/packages/rs-drive/Cargo.toml index 3a475c19036..11b8831eded 100644 --- a/packages/rs-drive/Cargo.toml +++ b/packages/rs-drive/Cargo.toml @@ -52,12 +52,12 @@ enum-map = { version = "2.0.3", optional = true } intmap = { version = "3.0.1", features = ["serde"], optional = true } chrono = { version = "0.4.35", optional = true } itertools = { version = "0.13", optional = true } -grovedb = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f", optional = true, default-features = false } -grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f", optional = true } -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f", optional = true } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f" } -grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f" } +grovedb = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115", optional = true, default-features = false } +grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115", optional = true } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115", optional = true } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115" } +grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115" } [dev-dependencies] criterion = "0.5" diff --git a/packages/rs-platform-version/Cargo.toml b/packages/rs-platform-version/Cargo.toml index 8bccf00e58d..ad69693e5e0 100644 --- a/packages/rs-platform-version/Cargo.toml +++ b/packages/rs-platform-version/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT" thiserror = { version = "2.0.12" } bincode = { version = "=2.0.1" } versioned-feature-core = { git = "https://github.com/dashpay/versioned-feature-core", version = "1.0.0" } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f" } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115" } [features] mock-versions = [] diff --git a/packages/rs-platform-wallet/Cargo.toml b/packages/rs-platform-wallet/Cargo.toml index 8c04bed5631..f59e66cd9c4 100644 --- a/packages/rs-platform-wallet/Cargo.toml +++ b/packages/rs-platform-wallet/Cargo.toml @@ -69,7 +69,7 @@ zeroize = "1" log = "0.4" # Shielded pool (optional, behind `shielded` feature) -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115", optional = true } # Direct `rusqlite` access so `FileBackedShieldedStore::open_path` can set # WAL + synchronous=NORMAL pragmas before handing the connection to # `ClientPersistentCommitmentTree`. Version locked to match the rev grovedb diff --git a/packages/rs-sdk/Cargo.toml b/packages/rs-sdk/Cargo.toml index 7b1267632cb..1998636a49a 100644 --- a/packages/rs-sdk/Cargo.toml +++ b/packages/rs-sdk/Cargo.toml @@ -18,7 +18,7 @@ drive = { path = "../rs-drive", default-features = false, features = [ ] } drive-proof-verifier = { path = "../rs-drive-proof-verifier", default-features = false } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "cc7b3997be5819c60eb09251f963c1ed1a89306f", features = [ +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115", features = [ "client", "sqlite", ], optional = true } From 6f70cc58ede6c631a16cbfa780673dfff4a5217a Mon Sep 17 00:00:00 2001 From: Ivan Shumkov Date: Fri, 14 Aug 2026 03:24:39 +0700 Subject: [PATCH 05/25] fix(drive): re-pin grovedb for snapshot-consistent counted reads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The counted descent performed several point reads where the linear scan it replaced performed one, and nothing replaced the consistency the iterator had been providing for free. A block committing mid-descent could pair a parent from the old state with a child from the new one, and merk does not verify a fetched child against the parent's recorded link hash, so the result was a silently mixed page rather than an error. The proved path is not exposed the same way: a torn read there fails the verifier's ancestor-chain reconciliation. grovedb `0100cb83` serves the whole page — root, descent and collect — from one transaction raw iterator with a pinned snapshot plus the transaction overlay, the same mechanism the linear scan used, and drops `RefWalker` from the walk. The guarantee is restored where the traversal lives rather than worked around here. Cost is one extra seek: deep offset 22 -> 23, offset 0 unchanged at 5, past-the-end flat at 4. Testing scope, stated rather than implied: the transaction-overlay behaviour is pinned by a new grovedb test. The commit-interleave case is not deterministically testable — there is no hook to pause a fetch and force a commit mid-descent — so that half rests on the mechanism, not on a test. Also folds a review suggestion: the `offset` request-side prose in the proto and the Objective-C client no longer calls the unproved skip count-attested. Both paths use the counted descent; only the proved result attests the count. Verified: narrow cut `cargo check -p drive --no-default-features --features verify` clean, drive ranked 71 passed, drive-abci ranked 18 passed, fmt clean. --- Cargo.lock | 44 +++++++++---------- .../platform/v0/objective-c/Platform.pbobjc.h | 13 +++--- .../protos/platform/v0/platform.proto | 13 +++--- packages/rs-dpp/Cargo.toml | 2 +- packages/rs-drive-abci/Cargo.toml | 8 ++-- packages/rs-drive/Cargo.toml | 12 ++--- packages/rs-platform-version/Cargo.toml | 2 +- packages/rs-platform-wallet/Cargo.toml | 2 +- packages/rs-sdk/Cargo.toml | 2 +- 9 files changed, 52 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 43d8fe3a554..0919ccc9f6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -576,7 +576,7 @@ dependencies = [ "bitflags 2.13.0", "cexpr", "clang-sys", - "itertools 0.13.0", + "itertools 0.10.5", "proc-macro2", "quote", "regex", @@ -2955,7 +2955,7 @@ dependencies = [ [[package]] name = "grovedb" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" +source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" dependencies = [ "axum 0.8.9", "bincode", @@ -2993,7 +2993,7 @@ dependencies = [ [[package]] name = "grovedb-bulk-append-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" +source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" dependencies = [ "bincode", "blake3", @@ -3009,7 +3009,7 @@ dependencies = [ [[package]] name = "grovedb-commitment-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" +source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" dependencies = [ "blake3", "grovedb-bulk-append-tree", @@ -3025,7 +3025,7 @@ dependencies = [ [[package]] name = "grovedb-costs" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" +source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" dependencies = [ "integer-encoding", "intmap", @@ -3035,7 +3035,7 @@ dependencies = [ [[package]] name = "grovedb-dense-fixed-sized-merkle-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" +source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" dependencies = [ "bincode", "blake3", @@ -3048,7 +3048,7 @@ dependencies = [ [[package]] name = "grovedb-element" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" +source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" dependencies = [ "bincode", "bincode_derive", @@ -3063,7 +3063,7 @@ dependencies = [ [[package]] name = "grovedb-epoch-based-storage-flags" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" +source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" dependencies = [ "grovedb-costs", "hex", @@ -3075,7 +3075,7 @@ dependencies = [ [[package]] name = "grovedb-merk" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" +source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" dependencies = [ "bincode", "bincode_derive", @@ -3101,7 +3101,7 @@ dependencies = [ [[package]] name = "grovedb-merkle-mountain-range" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" +source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" dependencies = [ "bincode", "blake3", @@ -3112,7 +3112,7 @@ dependencies = [ [[package]] name = "grovedb-path" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" +source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" dependencies = [ "hex", ] @@ -3120,7 +3120,7 @@ dependencies = [ [[package]] name = "grovedb-query" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" +source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" dependencies = [ "bincode", "byteorder", @@ -3136,7 +3136,7 @@ dependencies = [ [[package]] name = "grovedb-storage" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" +source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" dependencies = [ "blake3", "grovedb-costs", @@ -3155,7 +3155,7 @@ dependencies = [ [[package]] name = "grovedb-version" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" +source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" dependencies = [ "thiserror 2.0.18", "versioned-feature-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3164,7 +3164,7 @@ dependencies = [ [[package]] name = "grovedb-visualize" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" +source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" dependencies = [ "hex", "itertools 0.14.0", @@ -3173,7 +3173,7 @@ dependencies = [ [[package]] name = "grovedbg-types" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=c4ceac676630727d01b532314a7e488edbeaf115#c4ceac676630727d01b532314a7e488edbeaf115" +source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" dependencies = [ "serde", "serde_with 3.21.0", @@ -3589,7 +3589,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.4", + "socket2 0.5.10", "system-configuration", "tokio", "tower-service", @@ -5542,7 +5542,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" dependencies = [ "heck 0.4.1", - "itertools 0.13.0", + "itertools 0.10.5", "log", "multimap", "petgraph", @@ -5563,7 +5563,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.117", @@ -5576,7 +5576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.117", @@ -5712,7 +5712,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.2", "rustls", - "socket2 0.6.4", + "socket2 0.5.10", "thiserror 2.0.18", "tokio", "tracing", @@ -5750,7 +5750,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.4", + "socket2 0.5.10", "tracing", "windows-sys 0.59.0", ] diff --git a/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h index 6c8dc26eb3e..43afa3cd99e 100644 --- a/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h +++ b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h @@ -3155,11 +3155,14 @@ GPB_FINAL @interface GetDocumentsRequest_GetDocumentsRequestV1 : GPBMessage * routes to the ranked executor (`group_by` + a single `order_by` * naming the selected aggregate), `offset` skips that many ranks * before the returned page, so `ORDER BY avg(grade) DESC LIMIT 1 - * OFFSET 4` is the 5th-best group. The skip is **count-attested**, - * not walked: grovedb proves it from the counted subtree - * commitments, so the proof stays `O(log n + k)` at any offset and - * the response echoes the attested number in - * `RankedEntries.skipped`. There is deliberately no ceiling — an + * OFFSET 4` is the 5th-best group. The skip is **counted, not + * walked**: grovedb descends on each subtree's aggregate count and + * collapses whole subtrees that fit inside the remaining offset, so + * the work stays `O(log n + k)` at any offset and the response + * reports the skip it performed in `RankedEntries.skipped`. On a + * proved request that count is additionally *attested* — committed + * to by the proof and re-derived by the verifier; on an unproved + * one it is the node's own report. See `RankedEntries.skipped`. There is deliberately no ceiling — an * offset of 4 and an offset of four billion cost the same, so * there is no denial-of-service lever a cap would close. An offset * past the end of the ranking is a provable answer rather than an diff --git a/packages/dapi-grpc/protos/platform/v0/platform.proto b/packages/dapi-grpc/protos/platform/v0/platform.proto index 728bb8dffcd..173b08864fb 100644 --- a/packages/dapi-grpc/protos/platform/v0/platform.proto +++ b/packages/dapi-grpc/protos/platform/v0/platform.proto @@ -1125,11 +1125,14 @@ message GetDocumentsRequest { // routes to the ranked executor (`group_by` + a single `order_by` // naming the selected aggregate), `offset` skips that many ranks // before the returned page, so `ORDER BY avg(grade) DESC LIMIT 1 - // OFFSET 4` is the 5th-best group. The skip is **count-attested**, - // not walked: grovedb proves it from the counted subtree - // commitments, so the proof stays `O(log n + k)` at any offset and - // the response reports the skip it performed in - // `RankedEntries.skipped`. There is deliberately no ceiling — an + // OFFSET 4` is the 5th-best group. The skip is **counted, not + // walked**: grovedb descends on each subtree's aggregate count and + // collapses whole subtrees that fit inside the remaining offset, so + // the work stays `O(log n + k)` at any offset and the response + // reports the skip it performed in `RankedEntries.skipped`. On a + // proved request that count is additionally *attested* — committed + // to by the proof and re-derived by the verifier; on an unproved + // one it is the node's own report. See `RankedEntries.skipped`. There is deliberately no ceiling — an // offset of 4 and an offset of four billion cost the same, so // there is no denial-of-service lever a cap would close. An offset // past the end of the ranking is a provable answer rather than an diff --git a/packages/rs-dpp/Cargo.toml b/packages/rs-dpp/Cargo.toml index cd86bb3b92f..8566886e682 100644 --- a/packages/rs-dpp/Cargo.toml +++ b/packages/rs-dpp/Cargo.toml @@ -71,7 +71,7 @@ strum = { version = "0.26", features = ["derive"] } json-schema-compatibility-validator = { path = '../rs-json-schema-compatibility-validator', optional = true } once_cell = "1.19.0" tracing = { version = "0.1.41" } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8", optional = true } [dev-dependencies] tokio = { version = "1.40", features = ["full"] } diff --git a/packages/rs-drive-abci/Cargo.toml b/packages/rs-drive-abci/Cargo.toml index 6268cde0068..e8f3fd93749 100644 --- a/packages/rs-drive-abci/Cargo.toml +++ b/packages/rs-drive-abci/Cargo.toml @@ -82,7 +82,7 @@ derive_more = { version = "1.0", features = ["from", "deref", "deref_mut"] } async-trait = "0.1.77" console-subscriber = { version = "0.4", optional = true } bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f", optional = true } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115" } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8" } nonempty = "0.11" # Shielded-pool snapshot needs raw RocksDB SstFileWriter + ingest_external_file_cf # bindings, and blake3 for the snapshot-file checksum. @@ -107,7 +107,7 @@ dpp = { path = "../rs-dpp", default-features = false, features = [ drive = { path = "../rs-drive", features = ["fixtures-and-mocks"] } drive-proof-verifier = { path = "../rs-drive-proof-verifier" } strategy-tests = { path = "../strategy-tests" } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115", features = ["client"] } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8", features = ["client"] } assert_matches = "1.5.0" drive-abci = { path = ".", features = ["testing-config", "mocks", "shielded_test_data"] } bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f" } @@ -121,8 +121,8 @@ integer-encoding = { version = "4.0.0" } # For dump_only_default_and_aux_cfs_under_shielded_subtree_prefix — same # subtree-prefix algorithm grovedb uses internally. -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115" } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8" } [features] default = ["bls-signatures"] diff --git a/packages/rs-drive/Cargo.toml b/packages/rs-drive/Cargo.toml index 11b8831eded..8edec1af65d 100644 --- a/packages/rs-drive/Cargo.toml +++ b/packages/rs-drive/Cargo.toml @@ -52,12 +52,12 @@ enum-map = { version = "2.0.3", optional = true } intmap = { version = "3.0.1", features = ["serde"], optional = true } chrono = { version = "0.4.35", optional = true } itertools = { version = "0.13", optional = true } -grovedb = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115", optional = true, default-features = false } -grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115", optional = true } -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115", optional = true } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115" } -grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115" } +grovedb = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8", optional = true, default-features = false } +grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8", optional = true } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8", optional = true } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8" } +grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8" } [dev-dependencies] criterion = "0.5" diff --git a/packages/rs-platform-version/Cargo.toml b/packages/rs-platform-version/Cargo.toml index ad69693e5e0..8812e7c4767 100644 --- a/packages/rs-platform-version/Cargo.toml +++ b/packages/rs-platform-version/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT" thiserror = { version = "2.0.12" } bincode = { version = "=2.0.1" } versioned-feature-core = { git = "https://github.com/dashpay/versioned-feature-core", version = "1.0.0" } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115" } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8" } [features] mock-versions = [] diff --git a/packages/rs-platform-wallet/Cargo.toml b/packages/rs-platform-wallet/Cargo.toml index f59e66cd9c4..fd2da0da5a0 100644 --- a/packages/rs-platform-wallet/Cargo.toml +++ b/packages/rs-platform-wallet/Cargo.toml @@ -69,7 +69,7 @@ zeroize = "1" log = "0.4" # Shielded pool (optional, behind `shielded` feature) -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8", optional = true } # Direct `rusqlite` access so `FileBackedShieldedStore::open_path` can set # WAL + synchronous=NORMAL pragmas before handing the connection to # `ClientPersistentCommitmentTree`. Version locked to match the rev grovedb diff --git a/packages/rs-sdk/Cargo.toml b/packages/rs-sdk/Cargo.toml index 1998636a49a..ebaee72c2b1 100644 --- a/packages/rs-sdk/Cargo.toml +++ b/packages/rs-sdk/Cargo.toml @@ -18,7 +18,7 @@ drive = { path = "../rs-drive", default-features = false, features = [ ] } drive-proof-verifier = { path = "../rs-drive-proof-verifier", default-features = false } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "c4ceac676630727d01b532314a7e488edbeaf115", features = [ +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8", features = [ "client", "sqlite", ], optional = true } From ad8f553ad7ac08b9bab2680357a68ae06d1ed205 Mon Sep 17 00:00:00 2001 From: Ivan Shumkov Date: Fri, 14 Aug 2026 10:13:10 +0700 Subject: [PATCH 06/25] fix(drive): re-pin grovedb so root-key discovery shares the read snapshot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous pin put the root node, the descent and the collect behind one pinned iterator, but root-key *discovery* stayed outside it: `open_validated_axis_secondary` read `secondary_root_key` from the parent through the snapshotless transaction, and only afterwards did the traversal create its iterator and re-fetch the node that key names. A commit rotating the secondary's AVL root between those two steps left the old root key still present in the new snapshot — as a demoted child. The re-fetch then succeeded rather than tripping the absent-root guard, and the walk treated that child's aggregate and links as the whole ranking. The internal count checks pass, because a demoted child is a valid subtree; the page and its `skipped` are simply truncated, without an error. grovedb `63df14c2` creates the pinned view first, fetches the indexed-tree element through it, and derives `secondary_root_key` from that same snapshot, so discovery and traversal cannot straddle a rotation. An element absent from the read snapshot is now an explicit error rather than a silent fallback. Testing scope, unchanged in character from the previous fix: the snapshot-visibility behaviour is pinned by grovedb's tests; the commit-interleave case remains not deterministically testable, for want of a hook to pause a fetch and force a commit mid-descent, so that half is argued from the mechanism rather than proven. Verified: narrow cut `cargo check -p drive --no-default-features --features verify` clean, drive ranked 71 passed, drive-abci ranked 18 passed, fmt clean. --- Cargo.lock | 44 ++++++++++++------------- packages/rs-dpp/Cargo.toml | 2 +- packages/rs-drive-abci/Cargo.toml | 8 ++--- packages/rs-drive/Cargo.toml | 12 +++---- packages/rs-platform-version/Cargo.toml | 2 +- packages/rs-platform-wallet/Cargo.toml | 2 +- packages/rs-sdk/Cargo.toml | 2 +- 7 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0919ccc9f6c..3f6a77203b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -576,7 +576,7 @@ dependencies = [ "bitflags 2.13.0", "cexpr", "clang-sys", - "itertools 0.10.5", + "itertools 0.13.0", "proc-macro2", "quote", "regex", @@ -2955,7 +2955,7 @@ dependencies = [ [[package]] name = "grovedb" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" +source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" dependencies = [ "axum 0.8.9", "bincode", @@ -2993,7 +2993,7 @@ dependencies = [ [[package]] name = "grovedb-bulk-append-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" +source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" dependencies = [ "bincode", "blake3", @@ -3009,7 +3009,7 @@ dependencies = [ [[package]] name = "grovedb-commitment-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" +source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" dependencies = [ "blake3", "grovedb-bulk-append-tree", @@ -3025,7 +3025,7 @@ dependencies = [ [[package]] name = "grovedb-costs" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" +source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" dependencies = [ "integer-encoding", "intmap", @@ -3035,7 +3035,7 @@ dependencies = [ [[package]] name = "grovedb-dense-fixed-sized-merkle-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" +source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" dependencies = [ "bincode", "blake3", @@ -3048,7 +3048,7 @@ dependencies = [ [[package]] name = "grovedb-element" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" +source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" dependencies = [ "bincode", "bincode_derive", @@ -3063,7 +3063,7 @@ dependencies = [ [[package]] name = "grovedb-epoch-based-storage-flags" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" +source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" dependencies = [ "grovedb-costs", "hex", @@ -3075,7 +3075,7 @@ dependencies = [ [[package]] name = "grovedb-merk" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" +source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" dependencies = [ "bincode", "bincode_derive", @@ -3101,7 +3101,7 @@ dependencies = [ [[package]] name = "grovedb-merkle-mountain-range" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" +source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" dependencies = [ "bincode", "blake3", @@ -3112,7 +3112,7 @@ dependencies = [ [[package]] name = "grovedb-path" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" +source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" dependencies = [ "hex", ] @@ -3120,7 +3120,7 @@ dependencies = [ [[package]] name = "grovedb-query" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" +source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" dependencies = [ "bincode", "byteorder", @@ -3136,7 +3136,7 @@ dependencies = [ [[package]] name = "grovedb-storage" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" +source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" dependencies = [ "blake3", "grovedb-costs", @@ -3155,7 +3155,7 @@ dependencies = [ [[package]] name = "grovedb-version" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" +source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" dependencies = [ "thiserror 2.0.18", "versioned-feature-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3164,7 +3164,7 @@ dependencies = [ [[package]] name = "grovedb-visualize" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" +source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" dependencies = [ "hex", "itertools 0.14.0", @@ -3173,7 +3173,7 @@ dependencies = [ [[package]] name = "grovedbg-types" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=0100cb833075621659a68ddd3696baecc98e55b8#0100cb833075621659a68ddd3696baecc98e55b8" +source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" dependencies = [ "serde", "serde_with 3.21.0", @@ -3589,7 +3589,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.4", "system-configuration", "tokio", "tower-service", @@ -5542,7 +5542,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" dependencies = [ "heck 0.4.1", - "itertools 0.10.5", + "itertools 0.13.0", "log", "multimap", "petgraph", @@ -5563,7 +5563,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.13.0", "proc-macro2", "quote", "syn 2.0.117", @@ -5576,7 +5576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.13.0", "proc-macro2", "quote", "syn 2.0.117", @@ -5712,7 +5712,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.2", "rustls", - "socket2 0.5.10", + "socket2 0.6.4", "thiserror 2.0.18", "tokio", "tracing", @@ -5750,7 +5750,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.4", "tracing", "windows-sys 0.59.0", ] diff --git a/packages/rs-dpp/Cargo.toml b/packages/rs-dpp/Cargo.toml index 8566886e682..1e0748c555e 100644 --- a/packages/rs-dpp/Cargo.toml +++ b/packages/rs-dpp/Cargo.toml @@ -71,7 +71,7 @@ strum = { version = "0.26", features = ["derive"] } json-schema-compatibility-validator = { path = '../rs-json-schema-compatibility-validator', optional = true } once_cell = "1.19.0" tracing = { version = "0.1.41" } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f", optional = true } [dev-dependencies] tokio = { version = "1.40", features = ["full"] } diff --git a/packages/rs-drive-abci/Cargo.toml b/packages/rs-drive-abci/Cargo.toml index e8f3fd93749..2b3381d5967 100644 --- a/packages/rs-drive-abci/Cargo.toml +++ b/packages/rs-drive-abci/Cargo.toml @@ -82,7 +82,7 @@ derive_more = { version = "1.0", features = ["from", "deref", "deref_mut"] } async-trait = "0.1.77" console-subscriber = { version = "0.4", optional = true } bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f", optional = true } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8" } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f" } nonempty = "0.11" # Shielded-pool snapshot needs raw RocksDB SstFileWriter + ingest_external_file_cf # bindings, and blake3 for the snapshot-file checksum. @@ -107,7 +107,7 @@ dpp = { path = "../rs-dpp", default-features = false, features = [ drive = { path = "../rs-drive", features = ["fixtures-and-mocks"] } drive-proof-verifier = { path = "../rs-drive-proof-verifier" } strategy-tests = { path = "../strategy-tests" } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8", features = ["client"] } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f", features = ["client"] } assert_matches = "1.5.0" drive-abci = { path = ".", features = ["testing-config", "mocks", "shielded_test_data"] } bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f" } @@ -121,8 +121,8 @@ integer-encoding = { version = "4.0.0" } # For dump_only_default_and_aux_cfs_under_shielded_subtree_prefix — same # subtree-prefix algorithm grovedb uses internally. -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8" } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f" } [features] default = ["bls-signatures"] diff --git a/packages/rs-drive/Cargo.toml b/packages/rs-drive/Cargo.toml index 8edec1af65d..47b47a47eac 100644 --- a/packages/rs-drive/Cargo.toml +++ b/packages/rs-drive/Cargo.toml @@ -52,12 +52,12 @@ enum-map = { version = "2.0.3", optional = true } intmap = { version = "3.0.1", features = ["serde"], optional = true } chrono = { version = "0.4.35", optional = true } itertools = { version = "0.13", optional = true } -grovedb = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8", optional = true, default-features = false } -grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8", optional = true } -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8", optional = true } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8" } -grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8" } +grovedb = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f", optional = true, default-features = false } +grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f", optional = true } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f", optional = true } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f" } +grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f" } [dev-dependencies] criterion = "0.5" diff --git a/packages/rs-platform-version/Cargo.toml b/packages/rs-platform-version/Cargo.toml index 8812e7c4767..3dcc7609a46 100644 --- a/packages/rs-platform-version/Cargo.toml +++ b/packages/rs-platform-version/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT" thiserror = { version = "2.0.12" } bincode = { version = "=2.0.1" } versioned-feature-core = { git = "https://github.com/dashpay/versioned-feature-core", version = "1.0.0" } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8" } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f" } [features] mock-versions = [] diff --git a/packages/rs-platform-wallet/Cargo.toml b/packages/rs-platform-wallet/Cargo.toml index fd2da0da5a0..3dd76decb5d 100644 --- a/packages/rs-platform-wallet/Cargo.toml +++ b/packages/rs-platform-wallet/Cargo.toml @@ -69,7 +69,7 @@ zeroize = "1" log = "0.4" # Shielded pool (optional, behind `shielded` feature) -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f", optional = true } # Direct `rusqlite` access so `FileBackedShieldedStore::open_path` can set # WAL + synchronous=NORMAL pragmas before handing the connection to # `ClientPersistentCommitmentTree`. Version locked to match the rev grovedb diff --git a/packages/rs-sdk/Cargo.toml b/packages/rs-sdk/Cargo.toml index ebaee72c2b1..d0957e87337 100644 --- a/packages/rs-sdk/Cargo.toml +++ b/packages/rs-sdk/Cargo.toml @@ -18,7 +18,7 @@ drive = { path = "../rs-drive", default-features = false, features = [ ] } drive-proof-verifier = { path = "../rs-drive-proof-verifier", default-features = false } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "0100cb833075621659a68ddd3696baecc98e55b8", features = [ +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f", features = [ "client", "sqlite", ], optional = true } From 8fd1c36da97df6d76f141577ff079373d7fee719 Mon Sep 17 00:00:00 2001 From: Ivan Shumkov Date: Fri, 14 Aug 2026 11:14:26 +0700 Subject: [PATCH 07/25] docs: describe the ranked offset skip as counted, attested only when proved MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Five internal comments still explained the uncapped `OFFSET` in prover-only terms — "grovedb attests the skipped region from counted subtree commitments" — on routes that serve both `prove` settings. Since the unproved read performs the same counted descent and returns the node's own report, that wording implies a guarantee an unproved response does not carry, which is the exact confusion the public API docs were corrected for earlier in this PR. They now say the skipped region is *counted* from the subtree aggregates rather than walked, that both executors take that descent, and that only the proved result additionally attests the count. The review named two sites; a sweep of the surface found three more, in `rs-drive-proof-verifier` and the SDK's ranked query builder and result type — the places a client author reads before deciding whether to prove. The two comments that already distinguished the paths correctly (`mode_detection`, the book chapter) are unchanged. Comment-only; no behaviour change, so no test. --- .../src/query/document_query/v1/routing.rs | 6 ++++-- .../src/proof/document_ranked.rs | 6 +++--- .../src/query/drive_document_ranked_query/mod.rs | 11 ++++++----- .../rs-sdk/src/platform/documents/document_query.rs | 7 ++++--- .../src/platform/documents/document_ranked_entries.rs | 7 ++++--- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/packages/rs-drive-abci/src/query/document_query/v1/routing.rs b/packages/rs-drive-abci/src/query/document_query/v1/routing.rs index 3677b6bf0c3..bc354204f96 100644 --- a/packages/rs-drive-abci/src/query/document_query/v1/routing.rs +++ b/packages/rs-drive-abci/src/query/document_query/v1/routing.rs @@ -280,8 +280,10 @@ pub(super) fn validate_and_route( /// /// Offset pagination exists on exactly one path: the ranked executor, /// where `OFFSET m` is the rank the returned page starts at and costs -/// nothing to prove (grovedb attests the skipped region from counted -/// subtree commitments rather than walking it). Every other v1 shape — +/// nothing to skip on either `prove` setting: grovedb counts the +/// skipped region from the subtree aggregates rather than walking it, +/// and both executors take that descent. Only the proved result +/// additionally *attests* the count. Every other v1 shape — /// documents, and the grouped count / sum / average modes — has no /// offset primitive behind it and keeps the rejection it has always /// had, **message for message**: those callers paginate with diff --git a/packages/rs-drive-proof-verifier/src/proof/document_ranked.rs b/packages/rs-drive-proof-verifier/src/proof/document_ranked.rs index 63bd8d1e01e..327f8a48e2c 100644 --- a/packages/rs-drive-proof-verifier/src/proof/document_ranked.rs +++ b/packages/rs-drive-proof-verifier/src/proof/document_ranked.rs @@ -7,9 +7,9 @@ //! the per-axis *secondary* Merk of an indexed tree (grovedb PR #657), //! so it costs `O(log n + k)` and comes with a proof that commits to //! exactly the `k` returned `(aggregate, group key)` pairs — plus the -//! `OFFSET`, which grovedb attests from counted subtree commitments -//! rather than by walking the skipped region, so deep pages cost the -//! same as the first one. +//! `OFFSET`, which grovedb counts from the subtree aggregates rather +//! than by walking the skipped region — and additionally attests, on +//! this proved path — so deep pages cost the same as the first one. //! //! This module holds the client-facing result type //! ([`DocumentRankedEntries`]), the tenderdash-composition wrapper diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/mod.rs b/packages/rs-drive/src/query/drive_document_ranked_query/mod.rs index 2b755d9dd60..a78aa8b9641 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/mod.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/mod.rs @@ -55,11 +55,12 @@ //! 2. **`limit` is mandatory, `offset` is free, `start_at` is refused.** //! `limit` is the `k` of the walk and the ranked surface has no //! server default for it, so it must be supplied. `offset` is the -//! rank the page starts at and is unbounded above: grovedb's -//! paginated prover is `O(log n + k)` *regardless of offset* (the -//! skipped region is attested by counted subtree commitments, never -//! walked entry by entry), so a large offset is not a cost lever and -//! needs no ceiling. `start_at` / `start_after` name a document id, +//! rank the page starts at and is unbounded above: grovedb counts +//! the skipped region from the subtree aggregates rather than +//! walking it entry by entry, so both executors are `O(log n + k)` +//! *regardless of offset* and a large offset is not a cost lever on +//! either. Only the proved result additionally attests the count. +//! So the offset needs no ceiling. `start_at` / `start_after` name a document id, //! which does not appear anywhere in an aggregate-ordered keyspace. //! 3. **Entry order IS the ranking order.** The executor returns entries //! in the order grovedb walked the secondary; callers must not diff --git a/packages/rs-sdk/src/platform/documents/document_query.rs b/packages/rs-sdk/src/platform/documents/document_query.rs index 679dcd9f2c9..ba3b47c913d 100644 --- a/packages/rs-sdk/src/platform/documents/document_query.rs +++ b/packages/rs-sdk/src/platform/documents/document_query.rs @@ -363,9 +363,10 @@ impl DocumentQuery { /// Only the ranked surface honours it (see /// [`Self::order_by_selected_aggregate`]); on every other path the /// server rejects a set offset with `Unsupported`. There is no - /// ceiling: grovedb attests the skipped region from counted - /// subtree commitments instead of walking it, so a deep offset - /// costs exactly what a shallow one does. + /// ceiling: grovedb counts the skipped region from the subtree + /// aggregates instead of walking it, on both `prove` settings, so a + /// deep offset costs exactly what a shallow one does. Only a proved + /// response additionally attests the count. /// /// An offset past the end of the ranking is a legitimate answer /// rather than an error — the page comes back empty, and on a diff --git a/packages/rs-sdk/src/platform/documents/document_ranked_entries.rs b/packages/rs-sdk/src/platform/documents/document_ranked_entries.rs index f5a80ee3a59..99df7598ae7 100644 --- a/packages/rs-sdk/src/platform/documents/document_ranked_entries.rs +++ b/packages/rs-sdk/src/platform/documents/document_ranked_entries.rs @@ -346,9 +346,10 @@ mod tests { } /// An offset far past any plausible population is **not** capped: - /// grovedb attests the skipped region from counted commitments - /// rather than walking it, so a deep page costs what a shallow one - /// does and there is nothing for a cap to protect. + /// grovedb counts the skipped region from the subtree aggregates + /// rather than walking it, on both `prove` settings, so a deep page + /// costs what a shallow one does and there is nothing for a cap to + /// protect. #[test] fn a_very_deep_offset_is_not_capped() { let query = top_five_by_avg_grade().with_limit(1).with_offset(u32::MAX); From c1b860171aa4b2ec00f1a5caad8622e0cda1e916 Mon Sep 17 00:00:00 2001 From: Ivan Shumkov Date: Fri, 14 Aug 2026 13:16:00 +0700 Subject: [PATCH 08/25] docs: say the offset cost is bounded, not that every offset costs the same MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The public offset docs promised that a deep offset "costs exactly what a shallow one does". That was a fair shorthand when the alternative was a linear walk, but it is not what the counted read does, and the SDK docs are where a caller forms their cost model. Three shapes have three costs: `offset = 0` keeps the sequential fast path, a positive offset descends the tree in `O(log n)`, and an offset at or past the population is answered from the root without descending at all. What is true — and what matters for the absence of a ceiling — is that none of them grows with how far you page. Reworded across the SDK query builder, the ranked result type, the proof-verifier module docs, and the proto plus its Objective-C copy, which now says the two offsets cost the same *order* of work rather than the same work. Comment-only; no behaviour change, so no test. --- .../clients/platform/v0/objective-c/Platform.pbobjc.h | 5 +++-- packages/dapi-grpc/protos/platform/v0/platform.proto | 5 +++-- .../rs-drive-proof-verifier/src/proof/document_ranked.rs | 3 ++- packages/rs-sdk/src/platform/documents/document_query.rs | 9 +++++++-- .../src/platform/documents/document_ranked_entries.rs | 7 ++++--- 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h index 43afa3cd99e..c37388a2691 100644 --- a/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h +++ b/packages/dapi-grpc/clients/platform/v0/objective-c/Platform.pbobjc.h @@ -3163,8 +3163,9 @@ GPB_FINAL @interface GetDocumentsRequest_GetDocumentsRequestV1 : GPBMessage * proved request that count is additionally *attested* — committed * to by the proof and re-derived by the verifier; on an unproved * one it is the node's own report. See `RankedEntries.skipped`. There is deliberately no ceiling — an - * offset of 4 and an offset of four billion cost the same, so - * there is no denial-of-service lever a cap would close. An offset + * offset of 4 and an offset of four billion cost the same *order* + * of work — neither walks the region it skips — so there is no + * denial-of-service lever a cap would close. An offset * past the end of the ranking is a provable answer rather than an * error: `entries` comes back empty and `skipped` is the ranking's * whole population. diff --git a/packages/dapi-grpc/protos/platform/v0/platform.proto b/packages/dapi-grpc/protos/platform/v0/platform.proto index 173b08864fb..0ad3a197fd0 100644 --- a/packages/dapi-grpc/protos/platform/v0/platform.proto +++ b/packages/dapi-grpc/protos/platform/v0/platform.proto @@ -1133,8 +1133,9 @@ message GetDocumentsRequest { // proved request that count is additionally *attested* — committed // to by the proof and re-derived by the verifier; on an unproved // one it is the node's own report. See `RankedEntries.skipped`. There is deliberately no ceiling — an - // offset of 4 and an offset of four billion cost the same, so - // there is no denial-of-service lever a cap would close. An offset + // offset of 4 and an offset of four billion cost the same *order* + // of work — neither walks the region it skips — so there is no + // denial-of-service lever a cap would close. An offset // past the end of the ranking is a provable answer rather than an // error: `entries` comes back empty and `skipped` is the ranking's // whole population. diff --git a/packages/rs-drive-proof-verifier/src/proof/document_ranked.rs b/packages/rs-drive-proof-verifier/src/proof/document_ranked.rs index 327f8a48e2c..24f06524e7b 100644 --- a/packages/rs-drive-proof-verifier/src/proof/document_ranked.rs +++ b/packages/rs-drive-proof-verifier/src/proof/document_ranked.rs @@ -9,7 +9,8 @@ //! exactly the `k` returned `(aggregate, group key)` pairs — plus the //! `OFFSET`, which grovedb counts from the subtree aggregates rather //! than by walking the skipped region — and additionally attests, on -//! this proved path — so deep pages cost the same as the first one. +//! this proved path — so a deep page costs `O(log n + k)` like any +//! other rather than growing with the offset. //! //! This module holds the client-facing result type //! ([`DocumentRankedEntries`]), the tenderdash-composition wrapper diff --git a/packages/rs-sdk/src/platform/documents/document_query.rs b/packages/rs-sdk/src/platform/documents/document_query.rs index ba3b47c913d..7a0ae5ec573 100644 --- a/packages/rs-sdk/src/platform/documents/document_query.rs +++ b/packages/rs-sdk/src/platform/documents/document_query.rs @@ -364,8 +364,13 @@ impl DocumentQuery { /// [`Self::order_by_selected_aggregate`]); on every other path the /// server rejects a set offset with `Unsupported`. There is no /// ceiling: grovedb counts the skipped region from the subtree - /// aggregates instead of walking it, on both `prove` settings, so a - /// deep offset costs exactly what a shallow one does. Only a proved + /// aggregates instead of walking it, on both `prove` settings, so + /// the cost of a deep offset does not scale with the offset. It is + /// not identical to a shallow one — `offset = 0` keeps a sequential + /// fast path, a positive offset descends the tree in `O(log n)`, + /// and an offset at or past the population is answered from the + /// root without descending at all — but nothing here grows with how + /// far you page, which is why there is no ceiling. Only a proved /// response additionally attests the count. /// /// An offset past the end of the ranking is a legitimate answer diff --git a/packages/rs-sdk/src/platform/documents/document_ranked_entries.rs b/packages/rs-sdk/src/platform/documents/document_ranked_entries.rs index 99df7598ae7..f3791cac72b 100644 --- a/packages/rs-sdk/src/platform/documents/document_ranked_entries.rs +++ b/packages/rs-sdk/src/platform/documents/document_ranked_entries.rs @@ -347,9 +347,10 @@ mod tests { /// An offset far past any plausible population is **not** capped: /// grovedb counts the skipped region from the subtree aggregates - /// rather than walking it, on both `prove` settings, so a deep page - /// costs what a shallow one does and there is nothing for a cap to - /// protect. + /// rather than walking it, on both `prove` settings, so the cost of + /// a deep page does not grow with the offset — `O(log n)` in the + /// size of the ranking, not in how far you page — and there is + /// nothing for a cap to protect. #[test] fn a_very_deep_offset_is_not_capped() { let query = top_five_by_avg_grade().with_limit(1).with_offset(u32::MAX); From 764de7392470a3d9d7ac2b10caf47b9b40c5e3eb Mon Sep 17 00:00:00 2001 From: Ivan Shumkov Date: Wed, 19 Aug 2026 23:33:03 +0700 Subject: [PATCH 09/25] fix(drive): re-pin grovedb to the landed counted-skip merge commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The counted-skip feature merged to grovedb develop as 879cdbc2 (dashpay/grovedb#792) and its branch was deleted, so the previous pin (63df14c2) survived only on dangling-commit retention — and was two revisions behind the branch's final reviewed state besides. All 14 workspace entries now pin the merge commit itself. Crossing grovedb #795-#813 costs three mechanical adaptations, none of which touch the ranked read paths' behavior: - The verify_indexed_* family gained a &GroveVersion parameter (grovedb #801). verify_ranked_top_k_proof_v0 and verify_having_range_proof_v0 now take platform_version — their doc claim that nothing in the body needs a version is no longer true — and pass platform_version.drive.grove_version through. - grovedb's Query gained an optional read_mode field; the three literal initializers in the shielded-notes verifiers set it to None. Query's Display prints the field only when Some, so the pinned to_string assertion is unaffected. The unproved counted read (indexed_*_top_k_paginated -> IndexedTopKPage { entries, skipped }) is byte-identical between the old pin and the merge commit; the executors compile unchanged. Ranked (71), shielded (149 drive + 153 drive-abci), and having (38) suites pass. --- Cargo.lock | 45 ++++++++++--------- packages/rs-dpp/Cargo.toml | 2 +- packages/rs-drive-abci/Cargo.toml | 8 ++-- .../query/shielded/encrypted_notes/v0/mod.rs | 1 + packages/rs-drive/Cargo.toml | 12 ++--- .../verify_having_range_proof/mod.rs | 2 +- .../verify_having_range_proof/v0/mod.rs | 5 +++ .../verify_ranked_top_k_proof/mod.rs | 2 +- .../verify_ranked_top_k_proof/v0/mod.rs | 7 ++- .../verify_shielded_encrypted_notes/v0/mod.rs | 2 + packages/rs-platform-version/Cargo.toml | 2 +- packages/rs-platform-wallet/Cargo.toml | 2 +- packages/rs-sdk/Cargo.toml | 2 +- 13 files changed, 50 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3f6a77203b3..74817819cdb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -576,7 +576,7 @@ dependencies = [ "bitflags 2.13.0", "cexpr", "clang-sys", - "itertools 0.13.0", + "itertools 0.10.5", "proc-macro2", "quote", "regex", @@ -2955,7 +2955,7 @@ dependencies = [ [[package]] name = "grovedb" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" +source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" dependencies = [ "axum 0.8.9", "bincode", @@ -2993,7 +2993,7 @@ dependencies = [ [[package]] name = "grovedb-bulk-append-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" +source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" dependencies = [ "bincode", "blake3", @@ -3009,7 +3009,7 @@ dependencies = [ [[package]] name = "grovedb-commitment-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" +source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" dependencies = [ "blake3", "grovedb-bulk-append-tree", @@ -3025,7 +3025,7 @@ dependencies = [ [[package]] name = "grovedb-costs" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" +source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" dependencies = [ "integer-encoding", "intmap", @@ -3035,7 +3035,7 @@ dependencies = [ [[package]] name = "grovedb-dense-fixed-sized-merkle-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" +source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" dependencies = [ "bincode", "blake3", @@ -3048,11 +3048,12 @@ dependencies = [ [[package]] name = "grovedb-element" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" +source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" dependencies = [ "bincode", "bincode_derive", "grovedb-path", + "grovedb-query", "grovedb-version", "grovedb-visualize", "hex", @@ -3063,7 +3064,7 @@ dependencies = [ [[package]] name = "grovedb-epoch-based-storage-flags" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" +source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" dependencies = [ "grovedb-costs", "hex", @@ -3075,7 +3076,7 @@ dependencies = [ [[package]] name = "grovedb-merk" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" +source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" dependencies = [ "bincode", "bincode_derive", @@ -3101,7 +3102,7 @@ dependencies = [ [[package]] name = "grovedb-merkle-mountain-range" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" +source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" dependencies = [ "bincode", "blake3", @@ -3112,7 +3113,7 @@ dependencies = [ [[package]] name = "grovedb-path" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" +source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" dependencies = [ "hex", ] @@ -3120,7 +3121,7 @@ dependencies = [ [[package]] name = "grovedb-query" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" +source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" dependencies = [ "bincode", "byteorder", @@ -3136,7 +3137,7 @@ dependencies = [ [[package]] name = "grovedb-storage" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" +source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" dependencies = [ "blake3", "grovedb-costs", @@ -3155,7 +3156,7 @@ dependencies = [ [[package]] name = "grovedb-version" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" +source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" dependencies = [ "thiserror 2.0.18", "versioned-feature-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3164,7 +3165,7 @@ dependencies = [ [[package]] name = "grovedb-visualize" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" +source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" dependencies = [ "hex", "itertools 0.14.0", @@ -3173,7 +3174,7 @@ dependencies = [ [[package]] name = "grovedbg-types" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=63df14c27c4b9ad47881141a213af6d92ef8d10f#63df14c27c4b9ad47881141a213af6d92ef8d10f" +source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" dependencies = [ "serde", "serde_with 3.21.0", @@ -3589,7 +3590,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.4", + "socket2 0.5.10", "system-configuration", "tokio", "tower-service", @@ -5542,7 +5543,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" dependencies = [ "heck 0.4.1", - "itertools 0.13.0", + "itertools 0.10.5", "log", "multimap", "petgraph", @@ -5563,7 +5564,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.117", @@ -5576,7 +5577,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", - "itertools 0.13.0", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.117", @@ -5712,7 +5713,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.2", "rustls", - "socket2 0.6.4", + "socket2 0.5.10", "thiserror 2.0.18", "tokio", "tracing", @@ -5750,7 +5751,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.4", + "socket2 0.5.10", "tracing", "windows-sys 0.59.0", ] diff --git a/packages/rs-dpp/Cargo.toml b/packages/rs-dpp/Cargo.toml index 1e0748c555e..555c77900ad 100644 --- a/packages/rs-dpp/Cargo.toml +++ b/packages/rs-dpp/Cargo.toml @@ -71,7 +71,7 @@ strum = { version = "0.26", features = ["derive"] } json-schema-compatibility-validator = { path = '../rs-json-schema-compatibility-validator', optional = true } once_cell = "1.19.0" tracing = { version = "0.1.41" } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6", optional = true } [dev-dependencies] tokio = { version = "1.40", features = ["full"] } diff --git a/packages/rs-drive-abci/Cargo.toml b/packages/rs-drive-abci/Cargo.toml index 2b3381d5967..e75b2b1808b 100644 --- a/packages/rs-drive-abci/Cargo.toml +++ b/packages/rs-drive-abci/Cargo.toml @@ -82,7 +82,7 @@ derive_more = { version = "1.0", features = ["from", "deref", "deref_mut"] } async-trait = "0.1.77" console-subscriber = { version = "0.4", optional = true } bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f", optional = true } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f" } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6" } nonempty = "0.11" # Shielded-pool snapshot needs raw RocksDB SstFileWriter + ingest_external_file_cf # bindings, and blake3 for the snapshot-file checksum. @@ -107,7 +107,7 @@ dpp = { path = "../rs-dpp", default-features = false, features = [ drive = { path = "../rs-drive", features = ["fixtures-and-mocks"] } drive-proof-verifier = { path = "../rs-drive-proof-verifier" } strategy-tests = { path = "../strategy-tests" } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f", features = ["client"] } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6", features = ["client"] } assert_matches = "1.5.0" drive-abci = { path = ".", features = ["testing-config", "mocks", "shielded_test_data"] } bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f" } @@ -121,8 +121,8 @@ integer-encoding = { version = "4.0.0" } # For dump_only_default_and_aux_cfs_under_shielded_subtree_prefix — same # subtree-prefix algorithm grovedb uses internally. -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f" } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6" } [features] default = ["bls-signatures"] diff --git a/packages/rs-drive-abci/src/query/shielded/encrypted_notes/v0/mod.rs b/packages/rs-drive-abci/src/query/shielded/encrypted_notes/v0/mod.rs index 808d0e49051..86214e5b5f0 100644 --- a/packages/rs-drive-abci/src/query/shielded/encrypted_notes/v0/mod.rs +++ b/packages/rs-drive-abci/src/query/shielded/encrypted_notes/v0/mod.rs @@ -81,6 +81,7 @@ impl Platform { path: shielded_credit_pool_path_vec(), query: SizedQuery { query: Query { + read_mode: None, items: vec![QueryItem::Key(vec![SHIELDED_NOTES_KEY])], default_subquery_branch: SubqueryBranch { subquery_path: None, diff --git a/packages/rs-drive/Cargo.toml b/packages/rs-drive/Cargo.toml index 47b47a47eac..c0c4c78cb57 100644 --- a/packages/rs-drive/Cargo.toml +++ b/packages/rs-drive/Cargo.toml @@ -52,12 +52,12 @@ enum-map = { version = "2.0.3", optional = true } intmap = { version = "3.0.1", features = ["serde"], optional = true } chrono = { version = "0.4.35", optional = true } itertools = { version = "0.13", optional = true } -grovedb = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f", optional = true, default-features = false } -grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f", optional = true } -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f", optional = true } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f" } -grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f" } +grovedb = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6", optional = true, default-features = false } +grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6", optional = true } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6", optional = true } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6" } +grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6" } [dev-dependencies] criterion = "0.5" diff --git a/packages/rs-drive/src/verify/document_having/verify_having_range_proof/mod.rs b/packages/rs-drive/src/verify/document_having/verify_having_range_proof/mod.rs index b4ba63ead28..893a2766bbb 100644 --- a/packages/rs-drive/src/verify/document_having/verify_having_range_proof/mod.rs +++ b/packages/rs-drive/src/verify/document_having/verify_having_range_proof/mod.rs @@ -41,7 +41,7 @@ impl DriveDocumentHavingQuery<'_> { .document_ranked .verify_having_range_proof { - 0 => self.verify_having_range_proof_v0(proof), + 0 => self.verify_having_range_proof_v0(proof, platform_version), version => Err(Error::Drive(DriveError::UnknownVersionMismatch { method: "DriveDocumentHavingQuery::verify_having_range_proof".to_string(), known_versions: vec![0], diff --git a/packages/rs-drive/src/verify/document_having/verify_having_range_proof/v0/mod.rs b/packages/rs-drive/src/verify/document_having/verify_having_range_proof/v0/mod.rs index fc9a0ab1727..1189b00b8f1 100644 --- a/packages/rs-drive/src/verify/document_having/verify_having_range_proof/v0/mod.rs +++ b/packages/rs-drive/src/verify/document_having/verify_having_range_proof/v0/mod.rs @@ -2,6 +2,7 @@ use crate::error::drive::DriveError; use crate::error::Error; use crate::query::{DriveDocumentHavingQuery, RankedAxis, RankedEntry, RankedEntryValue}; use crate::verify::RootHash; +use dpp::version::PlatformVersion; use grovedb::operations::proof::indexed_axis::AxisEntries; use grovedb::GroveDb; @@ -40,6 +41,7 @@ impl DriveDocumentHavingQuery<'_> { pub(super) fn verify_having_range_proof_v0( &self, proof: &[u8], + platform_version: &PlatformVersion, ) -> Result<(RootHash, Vec), Error> { let path = self.indexed_property_name_tree_path()?; let path_refs: Vec<&[u8]> = path.iter().map(|segment| segment.as_slice()).collect(); @@ -51,18 +53,21 @@ impl DriveDocumentHavingQuery<'_> { path_refs.as_slice(), secondary_query, Some(self.limit), + &platform_version.drive.grove_version, ), RankedAxis::Sum => GroveDb::verify_indexed_sum_query( proof, path_refs.as_slice(), secondary_query, Some(self.limit), + &platform_version.drive.grove_version, ), RankedAxis::Avg => GroveDb::verify_indexed_avg_query( proof, path_refs.as_slice(), secondary_query, Some(self.limit), + &platform_version.drive.grove_version, ), } .map_err(|e| Error::GroveDB(Box::new(e)))?; diff --git a/packages/rs-drive/src/verify/document_ranked/verify_ranked_top_k_proof/mod.rs b/packages/rs-drive/src/verify/document_ranked/verify_ranked_top_k_proof/mod.rs index 16d576a52f0..39bacf417f4 100644 --- a/packages/rs-drive/src/verify/document_ranked/verify_ranked_top_k_proof/mod.rs +++ b/packages/rs-drive/src/verify/document_ranked/verify_ranked_top_k_proof/mod.rs @@ -41,7 +41,7 @@ impl DriveDocumentRankedQuery<'_> { .document_ranked .verify_ranked_top_k_proof { - 0 => self.verify_ranked_top_k_proof_v0(proof), + 0 => self.verify_ranked_top_k_proof_v0(proof, platform_version), version => Err(Error::Drive(DriveError::UnknownVersionMismatch { method: "DriveDocumentRankedQuery::verify_ranked_top_k_proof".to_string(), known_versions: vec![0], diff --git a/packages/rs-drive/src/verify/document_ranked/verify_ranked_top_k_proof/v0/mod.rs b/packages/rs-drive/src/verify/document_ranked/verify_ranked_top_k_proof/v0/mod.rs index 668ab227770..1198261baeb 100644 --- a/packages/rs-drive/src/verify/document_ranked/verify_ranked_top_k_proof/v0/mod.rs +++ b/packages/rs-drive/src/verify/document_ranked/verify_ranked_top_k_proof/v0/mod.rs @@ -4,6 +4,7 @@ use crate::query::{ DriveDocumentRankedQuery, RankedAxis, RankedEntry, RankedEntryValue, RankedPage, }; use crate::verify::RootHash; +use dpp::version::PlatformVersion; use grovedb::operations::proof::indexed_axis::AxisEntries; use grovedb::GroveDb; @@ -44,14 +45,11 @@ impl DriveDocumentRankedQuery<'_> { /// is a useful answer, and the only place that knows whether it is /// acceptable is the caller. /// - /// No `platform_version` argument: the parent dispatcher already - /// consumed it to select this version, and nothing in the body needs - /// a grove version (verification derives everything from the proof - /// bytes plus the path). #[inline(always)] pub(super) fn verify_ranked_top_k_proof_v0( &self, proof: &[u8], + platform_version: &PlatformVersion, ) -> Result<(RootHash, RankedPage), Error> { let path = self.indexed_property_name_tree_path()?; let path_refs: Vec<&[u8]> = path.iter().map(|segment| segment.as_slice()).collect(); @@ -63,6 +61,7 @@ impl DriveDocumentRankedQuery<'_> { self.k, self.offset as u64, self.descending, + &platform_version.drive.grove_version, ) .map_err(|e| Error::GroveDB(Box::new(e)))?; diff --git a/packages/rs-drive/src/verify/shielded/verify_shielded_encrypted_notes/v0/mod.rs b/packages/rs-drive/src/verify/shielded/verify_shielded_encrypted_notes/v0/mod.rs index 4129cb9447d..c3f6e582ffd 100644 --- a/packages/rs-drive/src/verify/shielded/verify_shielded_encrypted_notes/v0/mod.rs +++ b/packages/rs-drive/src/verify/shielded/verify_shielded_encrypted_notes/v0/mod.rs @@ -81,6 +81,7 @@ impl Drive { path: shielded_credit_pool_path_vec(), query: SizedQuery { query: Query { + read_mode: None, items: vec![QueryItem::Key(vec![SHIELDED_NOTES_KEY])], default_subquery_branch: SubqueryBranch { subquery_path: None, @@ -268,6 +269,7 @@ mod tests { path: shielded_credit_pool_path_vec(), query: SizedQuery { query: Query { + read_mode: None, items: vec![QueryItem::Key(vec![SHIELDED_NOTES_KEY])], default_subquery_branch: SubqueryBranch { subquery_path: None, diff --git a/packages/rs-platform-version/Cargo.toml b/packages/rs-platform-version/Cargo.toml index 3dcc7609a46..9aaf21f2236 100644 --- a/packages/rs-platform-version/Cargo.toml +++ b/packages/rs-platform-version/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT" thiserror = { version = "2.0.12" } bincode = { version = "=2.0.1" } versioned-feature-core = { git = "https://github.com/dashpay/versioned-feature-core", version = "1.0.0" } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f" } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6" } [features] mock-versions = [] diff --git a/packages/rs-platform-wallet/Cargo.toml b/packages/rs-platform-wallet/Cargo.toml index 3dd76decb5d..0f9ccf9a23c 100644 --- a/packages/rs-platform-wallet/Cargo.toml +++ b/packages/rs-platform-wallet/Cargo.toml @@ -69,7 +69,7 @@ zeroize = "1" log = "0.4" # Shielded pool (optional, behind `shielded` feature) -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6", optional = true } # Direct `rusqlite` access so `FileBackedShieldedStore::open_path` can set # WAL + synchronous=NORMAL pragmas before handing the connection to # `ClientPersistentCommitmentTree`. Version locked to match the rev grovedb diff --git a/packages/rs-sdk/Cargo.toml b/packages/rs-sdk/Cargo.toml index d0957e87337..3fda4e9a9a2 100644 --- a/packages/rs-sdk/Cargo.toml +++ b/packages/rs-sdk/Cargo.toml @@ -18,7 +18,7 @@ drive = { path = "../rs-drive", default-features = false, features = [ ] } drive-proof-verifier = { path = "../rs-drive-proof-verifier", default-features = false } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "63df14c27c4b9ad47881141a213af6d92ef8d10f", features = [ +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6", features = [ "client", "sqlite", ], optional = true } From c6098c51256dab38222b664eb0cd2377d00d0ad6 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Sat, 22 Aug 2026 05:00:21 +0700 Subject: [PATCH 10/25] fix(drive): align the startsAt cursor query's direction before merging grovedb's PathQuery::merge (GROVE_V4, path_query_methods.merge = 1) requires every input to agree on left_to_right and propagates the shared direction to the merged root. The startsAt cursor is a single-key path query whose direction carried no meaning, so it kept the default ascending direction and conflicted with any descending orderBy, failing every descending cursor page with "can not merge path queries with conflicting directions". Align it to the main query's direction: the single key selects the same element either way, and the merged root now keeps the direction the verifier rebuilds through this same path. Co-Authored-By: Claude Fable 5 --- packages/rs-drive/src/query/mod.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/rs-drive/src/query/mod.rs b/packages/rs-drive/src/query/mod.rs index a601d420761..7d6e98f3c5b 100644 --- a/packages/rs-drive/src/query/mod.rs +++ b/packages/rs-drive/src/query/mod.rs @@ -1381,7 +1381,16 @@ impl<'a> DriveDocumentQuery<'a> { return Ok(main_path_query); } - if let Some(start_at_path_query) = start_at_path_query { + if let Some(mut start_at_path_query) = start_at_path_query { + // The cursor query selects exactly one key, so its walk + // direction carries no meaning — but grovedb's merge (V4+) + // requires every input to agree on direction and propagates + // the shared one to the merged root. Align it to the main + // query's `orderBy` direction so a descending page merges, + // and so the merged root keeps the direction the verifier + // will rebuild through this same path. + start_at_path_query.query.query.left_to_right = + main_path_query.query.query.left_to_right; let limit = main_path_query.query.limit.take(); let mut merged = PathQuery::merge( vec![&start_at_path_query, &main_path_query], From de94b9cdb44f39ec6a9b6f3bc1580d9dd526b802 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Sat, 22 Aug 2026 05:00:21 +0700 Subject: [PATCH 11/25] chore(drive): bump grovedb to develop head fe17045a The pin moves past the direction-aligned merge so that grovedb #818 (verifier reads a synthesized path-component layer's direction off the proof) is in the tree: without it a descending cursor proof either fails subset verification with "Cannot verify lower bound of queried range" or, on a key-bearing opening node, verifies to an empty result set for a subtree that exists. Adaptations ride along: - indexed-axis reads return IndexedAxisEntry (ordering_value, primary_key, resolved value) instead of (T, key) tuples; the ranked and having executors and verifiers project to the ranking pair - TreeType::PrivateDocumentStore arms fail closed in the two generic empty-tree constructors (its entry size is not carried by TreeType) - CommitmentTree::append_many_raw takes a GroveVersion Fee goldens, query suites and the abci query tests are unchanged. Co-Authored-By: Claude Fable 5 --- Cargo.lock | 61 ++++++++++++------- packages/rs-dpp/Cargo.toml | 2 +- packages/rs-drive-abci/Cargo.toml | 8 +-- .../create_genesis_state/test/shielded.rs | 15 +++-- .../tests/document/ranked_group_drain.rs | 1 + packages/rs-drive/Cargo.toml | 12 ++-- .../v0/tests/ranked_index_e2e_tests.rs | 9 +++ packages/rs-drive/src/fees/op.rs | 11 ++++ .../execute_range.rs | 3 + .../execute_top_k.rs | 3 + .../grove_insert_empty_tree/v0/mod.rs | 8 +++ .../verify_having_range_proof/v0/mod.rs | 3 + .../verify_ranked_top_k_proof/v0/mod.rs | 3 + packages/rs-platform-version/Cargo.toml | 2 +- packages/rs-platform-wallet/Cargo.toml | 2 +- packages/rs-sdk/Cargo.toml | 2 +- 16 files changed, 103 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 29ab9c8052f..c3c9cb8da3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -576,7 +576,7 @@ dependencies = [ "bitflags 2.13.0", "cexpr", "clang-sys", - "itertools 0.10.5", + "itertools 0.13.0", "proc-macro2", "quote", "regex", @@ -2973,7 +2973,7 @@ dependencies = [ [[package]] name = "grovedb" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" +source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" dependencies = [ "axum 0.8.9", "bincode", @@ -2987,6 +2987,7 @@ dependencies = [ "grovedb-merk", "grovedb-merkle-mountain-range", "grovedb-path", + "grovedb-private-document-store", "grovedb-query", "grovedb-storage", "grovedb-version", @@ -3011,7 +3012,7 @@ dependencies = [ [[package]] name = "grovedb-bulk-append-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" +source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" dependencies = [ "bincode", "blake3", @@ -3020,6 +3021,7 @@ dependencies = [ "grovedb-merkle-mountain-range", "grovedb-query", "grovedb-storage", + "grovedb-version", "hex", "thiserror 2.0.18", ] @@ -3027,12 +3029,13 @@ dependencies = [ [[package]] name = "grovedb-commitment-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" +source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" dependencies = [ "blake3", "grovedb-bulk-append-tree", "grovedb-costs", "grovedb-storage", + "grovedb-version", "incrementalmerkletree", "orchard", "rusqlite", @@ -3043,7 +3046,7 @@ dependencies = [ [[package]] name = "grovedb-costs" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" +source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" dependencies = [ "integer-encoding", "intmap", @@ -3053,7 +3056,7 @@ dependencies = [ [[package]] name = "grovedb-dense-fixed-sized-merkle-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" +source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" dependencies = [ "bincode", "blake3", @@ -3066,7 +3069,7 @@ dependencies = [ [[package]] name = "grovedb-element" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" +source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" dependencies = [ "bincode", "bincode_derive", @@ -3082,7 +3085,7 @@ dependencies = [ [[package]] name = "grovedb-epoch-based-storage-flags" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" +source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" dependencies = [ "grovedb-costs", "hex", @@ -3094,7 +3097,7 @@ dependencies = [ [[package]] name = "grovedb-merk" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" +source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" dependencies = [ "bincode", "bincode_derive", @@ -3120,26 +3123,40 @@ dependencies = [ [[package]] name = "grovedb-merkle-mountain-range" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" +source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" dependencies = [ "bincode", "blake3", "grovedb-costs", "grovedb-storage", + "grovedb-version", ] [[package]] name = "grovedb-path" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" +source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" dependencies = [ "hex", ] +[[package]] +name = "grovedb-private-document-store" +version = "5.0.1" +source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" +dependencies = [ + "blake3", + "grovedb-bulk-append-tree", + "grovedb-costs", + "grovedb-storage", + "grovedb-version", + "thiserror 2.0.18", +] + [[package]] name = "grovedb-query" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" +source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" dependencies = [ "bincode", "byteorder", @@ -3155,7 +3172,7 @@ dependencies = [ [[package]] name = "grovedb-storage" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" +source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" dependencies = [ "blake3", "grovedb-costs", @@ -3174,7 +3191,7 @@ dependencies = [ [[package]] name = "grovedb-version" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" +source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" dependencies = [ "thiserror 2.0.18", "versioned-feature-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3183,7 +3200,7 @@ dependencies = [ [[package]] name = "grovedb-visualize" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" +source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" dependencies = [ "hex", "itertools 0.14.0", @@ -3192,7 +3209,7 @@ dependencies = [ [[package]] name = "grovedbg-types" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=879cdbc23f9f19f9cdc7974f90bde224c7c94db6#879cdbc23f9f19f9cdc7974f90bde224c7c94db6" +source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" dependencies = [ "serde", "serde_with 3.21.0", @@ -3608,7 +3625,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.4", "system-configuration", "tokio", "tower-service", @@ -5561,7 +5578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" dependencies = [ "heck 0.4.1", - "itertools 0.10.5", + "itertools 0.13.0", "log", "multimap", "petgraph", @@ -5582,7 +5599,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.13.0", "proc-macro2", "quote", "syn 2.0.117", @@ -5595,7 +5612,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.13.0", "proc-macro2", "quote", "syn 2.0.117", @@ -5731,7 +5748,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.2", "rustls", - "socket2 0.5.10", + "socket2 0.6.4", "thiserror 2.0.18", "tokio", "tracing", @@ -5769,7 +5786,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.4", "tracing", "windows-sys 0.59.0", ] diff --git a/packages/rs-dpp/Cargo.toml b/packages/rs-dpp/Cargo.toml index 555c77900ad..71e7be809d0 100644 --- a/packages/rs-dpp/Cargo.toml +++ b/packages/rs-dpp/Cargo.toml @@ -71,7 +71,7 @@ strum = { version = "0.26", features = ["derive"] } json-schema-compatibility-validator = { path = '../rs-json-schema-compatibility-validator', optional = true } once_cell = "1.19.0" tracing = { version = "0.1.41" } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274", optional = true } [dev-dependencies] tokio = { version = "1.40", features = ["full"] } diff --git a/packages/rs-drive-abci/Cargo.toml b/packages/rs-drive-abci/Cargo.toml index e75b2b1808b..ac2cf4aa8ac 100644 --- a/packages/rs-drive-abci/Cargo.toml +++ b/packages/rs-drive-abci/Cargo.toml @@ -82,7 +82,7 @@ derive_more = { version = "1.0", features = ["from", "deref", "deref_mut"] } async-trait = "0.1.77" console-subscriber = { version = "0.4", optional = true } bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f", optional = true } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6" } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274" } nonempty = "0.11" # Shielded-pool snapshot needs raw RocksDB SstFileWriter + ingest_external_file_cf # bindings, and blake3 for the snapshot-file checksum. @@ -107,7 +107,7 @@ dpp = { path = "../rs-dpp", default-features = false, features = [ drive = { path = "../rs-drive", features = ["fixtures-and-mocks"] } drive-proof-verifier = { path = "../rs-drive-proof-verifier" } strategy-tests = { path = "../strategy-tests" } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6", features = ["client"] } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274", features = ["client"] } assert_matches = "1.5.0" drive-abci = { path = ".", features = ["testing-config", "mocks", "shielded_test_data"] } bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f" } @@ -121,8 +121,8 @@ integer-encoding = { version = "4.0.0" } # For dump_only_default_and_aux_cfs_under_shielded_subtree_prefix — same # subtree-prefix algorithm grovedb uses internally. -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6" } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274" } [features] default = ["bls-signatures"] diff --git a/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/shielded.rs b/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/shielded.rs index 76ec156b84f..786012eaebc 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/shielded.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/shielded.rs @@ -364,12 +364,15 @@ impl Platform { cv_net: [0u8; 32], payload: n.encrypted_note, }); - let append_result = ct.append_many_raw(iter).value.map_err(|e| { - Error::Execution(ExecutionError::CorruptedCodeExecution(Box::leak( - format!("seed: append_many_raw (batch {batch_index}): {e}") - .into_boxed_str(), - ))) - })?; + let append_result = ct + .append_many_raw(iter, &platform_version.drive.grove_version) + .value + .map_err(|e| { + Error::Execution(ExecutionError::CorruptedCodeExecution(Box::leak( + format!("seed: append_many_raw (batch {batch_index}): {e}") + .into_boxed_str(), + ))) + })?; // Persist the Sinsemilla frontier per batch — cheap and // gives durable mid-bake checkpoints if we ever want to // resume from a crash. Mid-bake MMR `commit_mmr` is diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/ranked_group_drain.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/ranked_group_drain.rs index ac11d80cc67..10ada3042f7 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/ranked_group_drain.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/ranked_group_drain.rs @@ -89,6 +89,7 @@ fn ranked_count_groups( .unwrap() .expect("the ranked count read must succeed") .into_iter() + .map(|entry| entry.key_pair()) .map(|(count, key)| { ( count, diff --git a/packages/rs-drive/Cargo.toml b/packages/rs-drive/Cargo.toml index c0c4c78cb57..ae734f09d84 100644 --- a/packages/rs-drive/Cargo.toml +++ b/packages/rs-drive/Cargo.toml @@ -52,12 +52,12 @@ enum-map = { version = "2.0.3", optional = true } intmap = { version = "3.0.1", features = ["serde"], optional = true } chrono = { version = "0.4.35", optional = true } itertools = { version = "0.13", optional = true } -grovedb = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6", optional = true, default-features = false } -grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6", optional = true } -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6", optional = true } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6" } -grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6" } +grovedb = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274", optional = true, default-features = false } +grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274", optional = true } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274", optional = true } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274" } +grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274" } [dev-dependencies] criterion = "0.5" diff --git a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs index a8ae0a604f8..155f7cbb803 100644 --- a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs +++ b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs @@ -284,6 +284,9 @@ fn avg_top_k(drive: &Drive, path: &[Vec], k: u16, descending: bool) -> Vec<( ) .unwrap() .expect("indexed_avg_top_k must succeed") + .into_iter() + .map(|entry| entry.key_pair()) + .collect() } fn count_top_k(drive: &Drive, path: &[Vec], k: u16, descending: bool) -> Vec<(u64, Vec)> { @@ -299,6 +302,9 @@ fn count_top_k(drive: &Drive, path: &[Vec], k: u16, descending: bool) -> Vec ) .unwrap() .expect("indexed_count_top_k must succeed") + .into_iter() + .map(|entry| entry.key_pair()) + .collect() } fn sum_top_k(drive: &Drive, path: &[Vec], k: u16, descending: bool) -> Vec<(i64, Vec)> { @@ -314,6 +320,9 @@ fn sum_top_k(drive: &Drive, path: &[Vec], k: u16, descending: bool) -> Vec<( ) .unwrap() .expect("indexed_sum_top_k must succeed") + .into_iter() + .map(|entry| entry.key_pair()) + .collect() } // --------------------------------------------------------------------------- diff --git a/packages/rs-drive/src/fees/op.rs b/packages/rs-drive/src/fees/op.rs index 6dcc2fc1cba..7e55a751bd4 100644 --- a/packages/rs-drive/src/fees/op.rs +++ b/packages/rs-drive/src/fees/op.rs @@ -1568,6 +1568,17 @@ impl LowLevelDriveOperationTreeTypeConverter for TreeType { batch_insert_empty_provable_count_provable_sum_indexed_tree) instead.", ))) } + // A private document store's entry size lives only on the + // `Element` (it does not affect Merk node layout), so `TreeType` + // cannot describe the element to insert. Drive has no private + // document store surface yet; when it does, creation must go + // through a dedicated helper that takes the entry size. + TreeType::PrivateDocumentStore(_) => { + return Err(Error::Drive(DriveError::NotSupported( + "empty_tree_operation_for_known_path_key cannot create a \ + PrivateDocumentStore — the entry size is not carried by TreeType", + ))) + } }; Ok(LowLevelDriveOperation::insert_for_known_path_key_element( diff --git a/packages/rs-drive/src/query/drive_document_having_query/execute_range.rs b/packages/rs-drive/src/query/drive_document_having_query/execute_range.rs index c9188d91f93..b43fdb7d491 100644 --- a/packages/rs-drive/src/query/drive_document_having_query/execute_range.rs +++ b/packages/rs-drive/src/query/drive_document_having_query/execute_range.rs @@ -60,6 +60,7 @@ impl DriveDocumentHavingQuery<'_> { value .map_err(|e| Error::GroveDB(Box::new(e)))? .into_iter() + .map(|entry| entry.key_pair()) .map(|(count, key)| RankedEntry { key, value: RankedEntryValue::Count(count), @@ -79,6 +80,7 @@ impl DriveDocumentHavingQuery<'_> { value .map_err(|e| Error::GroveDB(Box::new(e)))? .into_iter() + .map(|entry| entry.key_pair()) .map(|(sum, key)| RankedEntry { key, value: RankedEntryValue::Sum(sum), @@ -98,6 +100,7 @@ impl DriveDocumentHavingQuery<'_> { value .map_err(|e| Error::GroveDB(Box::new(e)))? .into_iter() + .map(|entry| entry.key_pair()) .map(|(avg, key)| RankedEntry { key, value: RankedEntryValue::AvgFixedPoint(avg), diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/execute_top_k.rs b/packages/rs-drive/src/query/drive_document_ranked_query/execute_top_k.rs index 4530b9cfd5d..7f4e6b348da 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/execute_top_k.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/execute_top_k.rs @@ -99,6 +99,7 @@ impl DriveDocumentRankedQuery<'_> { ( entries .into_iter() + .map(|entry| entry.key_pair()) .map(|(count, key)| RankedEntry { key, value: RankedEntryValue::Count(count), @@ -121,6 +122,7 @@ impl DriveDocumentRankedQuery<'_> { ( entries .into_iter() + .map(|entry| entry.key_pair()) .map(|(sum, key)| RankedEntry { key, value: RankedEntryValue::Sum(sum), @@ -143,6 +145,7 @@ impl DriveDocumentRankedQuery<'_> { ( entries .into_iter() + .map(|entry| entry.key_pair()) .map(|(avg, key)| RankedEntry { key, value: RankedEntryValue::AvgFixedPoint(avg), diff --git a/packages/rs-drive/src/util/grove_operations/grove_insert_empty_tree/v0/mod.rs b/packages/rs-drive/src/util/grove_operations/grove_insert_empty_tree/v0/mod.rs index 0f14e32d071..4b3a2f065b8 100644 --- a/packages/rs-drive/src/util/grove_operations/grove_insert_empty_tree/v0/mod.rs +++ b/packages/rs-drive/src/util/grove_operations/grove_insert_empty_tree/v0/mod.rs @@ -56,6 +56,14 @@ impl Drive { batch_insert_empty_provable_count_provable_sum_indexed_tree instead.", ))) } + // The private document store's entry size is not carried by + // `TreeType` either (see `fees::op`); no generic creation path. + TreeType::PrivateDocumentStore(_) => { + return Err(Error::Drive(DriveError::NotSupported( + "grove_insert_empty_tree cannot create a PrivateDocumentStore — the entry \ + size is not carried by TreeType", + ))) + } }; let cost_context = self.grove.insert( path, diff --git a/packages/rs-drive/src/verify/document_having/verify_having_range_proof/v0/mod.rs b/packages/rs-drive/src/verify/document_having/verify_having_range_proof/v0/mod.rs index 1189b00b8f1..9caf4f9df85 100644 --- a/packages/rs-drive/src/verify/document_having/verify_having_range_proof/v0/mod.rs +++ b/packages/rs-drive/src/verify/document_having/verify_having_range_proof/v0/mod.rs @@ -75,6 +75,7 @@ impl DriveDocumentHavingQuery<'_> { let entries = match (self.bounds.axis(), result.entries) { (RankedAxis::Count, AxisEntries::Count(entries)) => entries .into_iter() + .map(|entry| entry.key_pair()) .map(|(count, key)| RankedEntry { key, value: RankedEntryValue::Count(count), @@ -82,6 +83,7 @@ impl DriveDocumentHavingQuery<'_> { .collect::>(), (RankedAxis::Sum, AxisEntries::Sum(entries)) => entries .into_iter() + .map(|entry| entry.key_pair()) .map(|(sum, key)| RankedEntry { key, value: RankedEntryValue::Sum(sum), @@ -89,6 +91,7 @@ impl DriveDocumentHavingQuery<'_> { .collect::>(), (RankedAxis::Avg, AxisEntries::Avg(entries)) => entries .into_iter() + .map(|entry| entry.key_pair()) .map(|(avg, key)| RankedEntry { key, value: RankedEntryValue::AvgFixedPoint(avg), diff --git a/packages/rs-drive/src/verify/document_ranked/verify_ranked_top_k_proof/v0/mod.rs b/packages/rs-drive/src/verify/document_ranked/verify_ranked_top_k_proof/v0/mod.rs index 1198261baeb..82f5b0a2320 100644 --- a/packages/rs-drive/src/verify/document_ranked/verify_ranked_top_k_proof/v0/mod.rs +++ b/packages/rs-drive/src/verify/document_ranked/verify_ranked_top_k_proof/v0/mod.rs @@ -68,6 +68,7 @@ impl DriveDocumentRankedQuery<'_> { let entries = match (self.axis, result.entries) { (RankedAxis::Count, AxisEntries::Count(entries)) => entries .into_iter() + .map(|entry| entry.key_pair()) .map(|(count, key)| RankedEntry { key, value: RankedEntryValue::Count(count), @@ -75,6 +76,7 @@ impl DriveDocumentRankedQuery<'_> { .collect::>(), (RankedAxis::Sum, AxisEntries::Sum(entries)) => entries .into_iter() + .map(|entry| entry.key_pair()) .map(|(sum, key)| RankedEntry { key, value: RankedEntryValue::Sum(sum), @@ -82,6 +84,7 @@ impl DriveDocumentRankedQuery<'_> { .collect::>(), (RankedAxis::Avg, AxisEntries::Avg(entries)) => entries .into_iter() + .map(|entry| entry.key_pair()) .map(|(avg, key)| RankedEntry { key, value: RankedEntryValue::AvgFixedPoint(avg), diff --git a/packages/rs-platform-version/Cargo.toml b/packages/rs-platform-version/Cargo.toml index 9aaf21f2236..578c7724c0d 100644 --- a/packages/rs-platform-version/Cargo.toml +++ b/packages/rs-platform-version/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT" thiserror = { version = "2.0.12" } bincode = { version = "=2.0.1" } versioned-feature-core = { git = "https://github.com/dashpay/versioned-feature-core", version = "1.0.0" } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6" } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274" } [features] mock-versions = [] diff --git a/packages/rs-platform-wallet/Cargo.toml b/packages/rs-platform-wallet/Cargo.toml index 0f9ccf9a23c..fb85c864286 100644 --- a/packages/rs-platform-wallet/Cargo.toml +++ b/packages/rs-platform-wallet/Cargo.toml @@ -69,7 +69,7 @@ zeroize = "1" log = "0.4" # Shielded pool (optional, behind `shielded` feature) -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274", optional = true } # Direct `rusqlite` access so `FileBackedShieldedStore::open_path` can set # WAL + synchronous=NORMAL pragmas before handing the connection to # `ClientPersistentCommitmentTree`. Version locked to match the rev grovedb diff --git a/packages/rs-sdk/Cargo.toml b/packages/rs-sdk/Cargo.toml index fe5ba34afe5..4ea53e07cae 100644 --- a/packages/rs-sdk/Cargo.toml +++ b/packages/rs-sdk/Cargo.toml @@ -18,7 +18,7 @@ drive = { path = "../rs-drive", default-features = false, features = [ ] } drive-proof-verifier = { path = "../rs-drive-proof-verifier", default-features = false } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "879cdbc23f9f19f9cdc7974f90bde224c7c94db6", features = [ +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274", features = [ "client", "sqlite", ], optional = true } From e18e45c44e9ae840a86237d1053b010da4647a39 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Sat, 22 Aug 2026 05:32:20 +0700 Subject: [PATCH 12/25] fix(sdk): name grovedb's PrivateDocumentStore element in the FFI and wasm path-element mappers grovedb develop adds Element::PrivateDocumentStore; the exhaustive element-type matches in rs-sdk-ffi and wasm-sdk gain the arm, following the naming of the other append-only tree elements. Co-Authored-By: Claude Fable 5 --- packages/rs-sdk-ffi/src/system/queries/path_elements.rs | 4 ++++ packages/wasm-sdk/src/queries/system.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/packages/rs-sdk-ffi/src/system/queries/path_elements.rs b/packages/rs-sdk-ffi/src/system/queries/path_elements.rs index 0a54b9d8981..96466024445 100644 --- a/packages/rs-sdk-ffi/src/system/queries/path_elements.rs +++ b/packages/rs-sdk-ffi/src/system/queries/path_elements.rs @@ -203,6 +203,8 @@ fn format_element_data(element: &Element) -> String { Element::DenseAppendOnlyFixedSizeTree(_, _, _) => { "dense_append_only_fixed_size_tree".to_string() } + + Element::PrivateDocumentStore(_, _, _, _) => "private_document_store".to_string(), Element::NonCounted(inner) => format!("non_counted({})", format_element_data(inner)), Element::NotSummed(inner) => format!("not_summed({})", format_element_data(inner)), Element::NotCountedOrSummed(inner) => { @@ -241,6 +243,8 @@ fn format_element_type(element: &Element) -> String { Element::DenseAppendOnlyFixedSizeTree(_, _, _) => { "dense_append_only_fixed_size_tree".to_string() } + + Element::PrivateDocumentStore(_, _, _, _) => "private_document_store".to_string(), Element::NonCounted(inner) => format!("non_counted({})", format_element_type(inner)), Element::NotSummed(inner) => format!("not_summed({})", format_element_type(inner)), Element::NotCountedOrSummed(inner) => { diff --git a/packages/wasm-sdk/src/queries/system.rs b/packages/wasm-sdk/src/queries/system.rs index 55865024b75..4cb64d916f6 100644 --- a/packages/wasm-sdk/src/queries/system.rs +++ b/packages/wasm-sdk/src/queries/system.rs @@ -941,6 +941,8 @@ fn element_type_name(element: &Element) -> &'static str { Element::MmrTree(_, _) => "mmrTree", Element::BulkAppendTree(_, _, _) => "bulkAppendTree", Element::DenseAppendOnlyFixedSizeTree(_, _, _) => "denseAppendOnlyFixedSizeTree", + + Element::PrivateDocumentStore(_, _, _, _) => "privateDocumentStore", Element::NonCounted(inner) => non_counted_element_type_name(inner), Element::NotSummed(inner) => not_summed_element_type_name(inner), Element::NotCountedOrSummed(inner) => not_counted_or_summed_element_type_name(inner), @@ -974,6 +976,8 @@ fn non_counted_element_type_name(element: &Element) -> &'static str { Element::MmrTree(_, _) => "nonCountedMmrTree", Element::BulkAppendTree(_, _, _) => "nonCountedBulkAppendTree", Element::DenseAppendOnlyFixedSizeTree(_, _, _) => "nonCountedDenseAppendOnlyFixedSizeTree", + + Element::PrivateDocumentStore(_, _, _, _) => "nonCountedPrivateDocumentStore", Element::NonCounted(_) | Element::NotSummed(_) | Element::NotCountedOrSummed(_) => { element_type_name(element) } From 67f5d788fe27dfbc68b644c41604c3182b92ce29 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Sat, 22 Aug 2026 08:05:32 +0700 Subject: [PATCH 13/25] fix(wasm-sdk): export the private-document-store element names in the TypeScript union The path-element mapper can return "privateDocumentStore" and "nonCountedPrivateDocumentStore" since the grovedb bump, but the hand-written GroveElementType union did not list them, so TypeScript callers could receive values outside the advertised type. Both literals join the union and the mapper tripwire covers both elements. Co-Authored-By: Claude Fable 5 --- packages/wasm-sdk/src/queries/system.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/wasm-sdk/src/queries/system.rs b/packages/wasm-sdk/src/queries/system.rs index 4cb64d916f6..1051361908f 100644 --- a/packages/wasm-sdk/src/queries/system.rs +++ b/packages/wasm-sdk/src/queries/system.rs @@ -39,6 +39,7 @@ export type GroveElementType = | "mmrTree" | "bulkAppendTree" | "denseAppendOnlyFixedSizeTree" + | "privateDocumentStore" | "nonCountedItem" | "nonCountedReference" | "nonCountedTree" @@ -60,6 +61,7 @@ export type GroveElementType = | "nonCountedMmrTree" | "nonCountedBulkAppendTree" | "nonCountedDenseAppendOnlyFixedSizeTree" + | "nonCountedPrivateDocumentStore" | "notSummedSumTree" | "notSummedBigSumTree" | "notSummedCountSumTree" @@ -1943,6 +1945,14 @@ mod tests { Element::DenseAppendOnlyFixedSizeTree(1, 2, None), "denseAppendOnlyFixedSizeTree", ), + ( + Element::PrivateDocumentStore(0, 32, 2, None), + "privateDocumentStore", + ), + ( + Element::NonCounted(Box::new(Element::PrivateDocumentStore(0, 32, 2, None))), + "nonCountedPrivateDocumentStore", + ), ( Element::NonCounted(Box::new(Element::ReferenceWithSumItem( reference_path(), From 71d49c79945dcb975ad33b389ba002b8880381d9 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Sat, 22 Aug 2026 08:28:13 +0700 Subject: [PATCH 14/25] fix(drive): read ranked and having pages through keys-only indexed-axis reads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ranked and having-range executors only rank — every entry was projected to its (value, key) pair and the resolved primary value discarded — yet the resolving reads paid up to k primary point reads per page through the caller's transaction, outside the pinned iterator view the page came from, so a primary deleted or rewritten by a commit in between could surface as CorruptedData or pair a newer primary with an older page. grovedb's keys-only reads (dashpay/grovedb#824, indexed_*_top_k_paginated_keys / indexed_*_range_keys) return the ranking pairs straight from the secondary view and never open the primary; the executors and the e2e test helpers use them. grovedb pinned to develop head f52adb64, which also carries the append-only storage accounting (dashpay/grovedb#822 via #825/#826: write churn reported as replaced bytes, each note's permanent bytes charged once) and the keys-only projection on the unified PathQuery (#827). The genesis shielded seeding test passes the grove version to the commitment tree's save/commit_mmr, which now take it. The two shielded fee-floor tests (flat fee >= estimated write cost) remain red at this pin: the estimate for a 1-action transfer is now ~397M credits (storage 133M + processing 264M, the epoch's replaced bytes at the processing rate) against the 131M flat fee — down from 18.5B at the previous pin, and the subject of the shielded pricing change that follows this PR. Co-Authored-By: Claude Fable 5 --- Cargo.lock | 33 ++++++++++--------- packages/rs-dpp/Cargo.toml | 2 +- packages/rs-drive-abci/Cargo.toml | 8 ++--- .../create_genesis_state/test/shielded.rs | 23 +++++++------ packages/rs-drive/Cargo.toml | 12 +++---- .../v0/tests/ranked_index_e2e_tests.rs | 21 ++++-------- .../execute_range.rs | 9 ++--- .../execute_top_k.rs | 32 +++++++++--------- packages/rs-platform-version/Cargo.toml | 2 +- packages/rs-platform-wallet/Cargo.toml | 2 +- packages/rs-sdk/Cargo.toml | 2 +- 11 files changed, 68 insertions(+), 78 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c3c9cb8da3d..836a2c0a768 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2973,7 +2973,7 @@ dependencies = [ [[package]] name = "grovedb" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" +source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" dependencies = [ "axum 0.8.9", "bincode", @@ -3012,7 +3012,7 @@ dependencies = [ [[package]] name = "grovedb-bulk-append-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" +source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" dependencies = [ "bincode", "blake3", @@ -3029,7 +3029,7 @@ dependencies = [ [[package]] name = "grovedb-commitment-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" +source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" dependencies = [ "blake3", "grovedb-bulk-append-tree", @@ -3046,7 +3046,7 @@ dependencies = [ [[package]] name = "grovedb-costs" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" +source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" dependencies = [ "integer-encoding", "intmap", @@ -3056,7 +3056,7 @@ dependencies = [ [[package]] name = "grovedb-dense-fixed-sized-merkle-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" +source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" dependencies = [ "bincode", "blake3", @@ -3069,7 +3069,7 @@ dependencies = [ [[package]] name = "grovedb-element" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" +source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" dependencies = [ "bincode", "bincode_derive", @@ -3085,7 +3085,7 @@ dependencies = [ [[package]] name = "grovedb-epoch-based-storage-flags" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" +source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" dependencies = [ "grovedb-costs", "hex", @@ -3097,7 +3097,7 @@ dependencies = [ [[package]] name = "grovedb-merk" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" +source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" dependencies = [ "bincode", "bincode_derive", @@ -3123,19 +3123,20 @@ dependencies = [ [[package]] name = "grovedb-merkle-mountain-range" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" +source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" dependencies = [ "bincode", "blake3", "grovedb-costs", "grovedb-storage", "grovedb-version", + "integer-encoding", ] [[package]] name = "grovedb-path" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" +source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" dependencies = [ "hex", ] @@ -3143,7 +3144,7 @@ dependencies = [ [[package]] name = "grovedb-private-document-store" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" +source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" dependencies = [ "blake3", "grovedb-bulk-append-tree", @@ -3156,7 +3157,7 @@ dependencies = [ [[package]] name = "grovedb-query" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" +source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" dependencies = [ "bincode", "byteorder", @@ -3172,7 +3173,7 @@ dependencies = [ [[package]] name = "grovedb-storage" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" +source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" dependencies = [ "blake3", "grovedb-costs", @@ -3191,7 +3192,7 @@ dependencies = [ [[package]] name = "grovedb-version" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" +source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" dependencies = [ "thiserror 2.0.18", "versioned-feature-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3200,7 +3201,7 @@ dependencies = [ [[package]] name = "grovedb-visualize" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" +source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" dependencies = [ "hex", "itertools 0.14.0", @@ -3209,7 +3210,7 @@ dependencies = [ [[package]] name = "grovedbg-types" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=fe17045aa7bcf47acb1f2fe0db5d738cbea50274#fe17045aa7bcf47acb1f2fe0db5d738cbea50274" +source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" dependencies = [ "serde", "serde_with 3.21.0", diff --git a/packages/rs-dpp/Cargo.toml b/packages/rs-dpp/Cargo.toml index 71e7be809d0..805cea56f0c 100644 --- a/packages/rs-dpp/Cargo.toml +++ b/packages/rs-dpp/Cargo.toml @@ -71,7 +71,7 @@ strum = { version = "0.26", features = ["derive"] } json-schema-compatibility-validator = { path = '../rs-json-schema-compatibility-validator', optional = true } once_cell = "1.19.0" tracing = { version = "0.1.41" } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4", optional = true } [dev-dependencies] tokio = { version = "1.40", features = ["full"] } diff --git a/packages/rs-drive-abci/Cargo.toml b/packages/rs-drive-abci/Cargo.toml index ac2cf4aa8ac..75fc9b6d577 100644 --- a/packages/rs-drive-abci/Cargo.toml +++ b/packages/rs-drive-abci/Cargo.toml @@ -82,7 +82,7 @@ derive_more = { version = "1.0", features = ["from", "deref", "deref_mut"] } async-trait = "0.1.77" console-subscriber = { version = "0.4", optional = true } bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f", optional = true } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274" } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4" } nonempty = "0.11" # Shielded-pool snapshot needs raw RocksDB SstFileWriter + ingest_external_file_cf # bindings, and blake3 for the snapshot-file checksum. @@ -107,7 +107,7 @@ dpp = { path = "../rs-dpp", default-features = false, features = [ drive = { path = "../rs-drive", features = ["fixtures-and-mocks"] } drive-proof-verifier = { path = "../rs-drive-proof-verifier" } strategy-tests = { path = "../strategy-tests" } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274", features = ["client"] } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4", features = ["client"] } assert_matches = "1.5.0" drive-abci = { path = ".", features = ["testing-config", "mocks", "shielded_test_data"] } bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f" } @@ -121,8 +121,8 @@ integer-encoding = { version = "4.0.0" } # For dump_only_default_and_aux_cfs_under_shielded_subtree_prefix — same # subtree-prefix algorithm grovedb uses internally. -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274" } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4" } [features] default = ["bls-signatures"] diff --git a/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/shielded.rs b/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/shielded.rs index 786012eaebc..53eadcdf40e 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/shielded.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/shielded.rs @@ -384,11 +384,13 @@ impl Platform { // between batches corrupts the in-memory overlay (manifests // as "MMR get_root failed: Inconsistent store" on the next // call). One final `commit_mmr` follows the loop below. - ct.save().value.map_err(|e| { - Error::Execution(ExecutionError::CorruptedCodeExecution(Box::leak( - format!("seed: ct.save (batch {batch_index}): {e}").into_boxed_str(), - ))) - })?; + ct.save(&platform_version.drive.grove_version) + .value + .map_err(|e| { + Error::Execution(ExecutionError::CorruptedCodeExecution(Box::leak( + format!("seed: ct.save (batch {batch_index}): {e}").into_boxed_str(), + ))) + })?; last_sinsemilla_root = append_result.sinsemilla_root; last_bulk_state_root = append_result.bulk_state_root; @@ -427,11 +429,12 @@ impl Platform { // overlay accumulates across `append_many_raw` calls and is // persisted only here. See the in-loop comment above and the // upstream fix `1340db71` for the rationale. - ct.commit_mmr().map_err(|e| { - Error::Execution(ExecutionError::CorruptedCodeExecution(Box::leak( - format!("seed: ct.commit_mmr (final): {e}").into_boxed_str(), - ))) - })?; + ct.commit_mmr(&platform_version.drive.grove_version) + .map_err(|e| { + Error::Execution(ExecutionError::CorruptedCodeExecution(Box::leak( + format!("seed: ct.commit_mmr (final): {e}").into_boxed_str(), + ))) + })?; let combined_root = grovedb_commitment_tree::compute_commitment_tree_state_root( &last_sinsemilla_root, diff --git a/packages/rs-drive/Cargo.toml b/packages/rs-drive/Cargo.toml index ae734f09d84..a976baf749d 100644 --- a/packages/rs-drive/Cargo.toml +++ b/packages/rs-drive/Cargo.toml @@ -52,12 +52,12 @@ enum-map = { version = "2.0.3", optional = true } intmap = { version = "3.0.1", features = ["serde"], optional = true } chrono = { version = "0.4.35", optional = true } itertools = { version = "0.13", optional = true } -grovedb = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274", optional = true, default-features = false } -grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274", optional = true } -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274", optional = true } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274" } -grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274" } +grovedb = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4", optional = true, default-features = false } +grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4", optional = true } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4", optional = true } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4" } +grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4" } [dev-dependencies] criterion = "0.5" diff --git a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs index 155f7cbb803..b31234f8512 100644 --- a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs +++ b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs @@ -275,7 +275,7 @@ fn avg_top_k(drive: &Drive, path: &[Vec], k: u16, descending: bool) -> Vec<( let path_refs: Vec<&[u8]> = path.iter().map(|v| v.as_slice()).collect(); drive .grove - .indexed_avg_top_k( + .indexed_avg_top_k_keys( path_refs.as_slice(), k, descending, @@ -283,17 +283,14 @@ fn avg_top_k(drive: &Drive, path: &[Vec], k: u16, descending: bool) -> Vec<( &platform_version().drive.grove_version, ) .unwrap() - .expect("indexed_avg_top_k must succeed") - .into_iter() - .map(|entry| entry.key_pair()) - .collect() + .expect("indexed_avg_top_k_keys must succeed") } fn count_top_k(drive: &Drive, path: &[Vec], k: u16, descending: bool) -> Vec<(u64, Vec)> { let path_refs: Vec<&[u8]> = path.iter().map(|v| v.as_slice()).collect(); drive .grove - .indexed_count_top_k( + .indexed_count_top_k_keys( path_refs.as_slice(), k, descending, @@ -301,17 +298,14 @@ fn count_top_k(drive: &Drive, path: &[Vec], k: u16, descending: bool) -> Vec &platform_version().drive.grove_version, ) .unwrap() - .expect("indexed_count_top_k must succeed") - .into_iter() - .map(|entry| entry.key_pair()) - .collect() + .expect("indexed_count_top_k_keys must succeed") } fn sum_top_k(drive: &Drive, path: &[Vec], k: u16, descending: bool) -> Vec<(i64, Vec)> { let path_refs: Vec<&[u8]> = path.iter().map(|v| v.as_slice()).collect(); drive .grove - .indexed_sum_top_k( + .indexed_sum_top_k_keys( path_refs.as_slice(), k, descending, @@ -319,10 +313,7 @@ fn sum_top_k(drive: &Drive, path: &[Vec], k: u16, descending: bool) -> Vec<( &platform_version().drive.grove_version, ) .unwrap() - .expect("indexed_sum_top_k must succeed") - .into_iter() - .map(|entry| entry.key_pair()) - .collect() + .expect("indexed_sum_top_k_keys must succeed") } // --------------------------------------------------------------------------- diff --git a/packages/rs-drive/src/query/drive_document_having_query/execute_range.rs b/packages/rs-drive/src/query/drive_document_having_query/execute_range.rs index b43fdb7d491..6707461d8d5 100644 --- a/packages/rs-drive/src/query/drive_document_having_query/execute_range.rs +++ b/packages/rs-drive/src/query/drive_document_having_query/execute_range.rs @@ -48,7 +48,7 @@ impl DriveDocumentHavingQuery<'_> { // but reads like a panicking unwrap at the call site. let entries = match self.bounds { AxisRangeBounds::Count { lo, hi } => { - let CostContext { value, cost: _ } = drive.grove.indexed_count_range( + let CostContext { value, cost: _ } = drive.grove.indexed_count_range_keys( path_refs.as_slice(), lo, hi, @@ -60,7 +60,6 @@ impl DriveDocumentHavingQuery<'_> { value .map_err(|e| Error::GroveDB(Box::new(e)))? .into_iter() - .map(|entry| entry.key_pair()) .map(|(count, key)| RankedEntry { key, value: RankedEntryValue::Count(count), @@ -68,7 +67,7 @@ impl DriveDocumentHavingQuery<'_> { .collect::>() } AxisRangeBounds::Sum { lo, hi } => { - let CostContext { value, cost: _ } = drive.grove.indexed_sum_range( + let CostContext { value, cost: _ } = drive.grove.indexed_sum_range_keys( path_refs.as_slice(), lo, hi, @@ -80,7 +79,6 @@ impl DriveDocumentHavingQuery<'_> { value .map_err(|e| Error::GroveDB(Box::new(e)))? .into_iter() - .map(|entry| entry.key_pair()) .map(|(sum, key)| RankedEntry { key, value: RankedEntryValue::Sum(sum), @@ -88,7 +86,7 @@ impl DriveDocumentHavingQuery<'_> { .collect::>() } AxisRangeBounds::Avg { lo, hi } => { - let CostContext { value, cost: _ } = drive.grove.indexed_avg_range( + let CostContext { value, cost: _ } = drive.grove.indexed_avg_range_keys( path_refs.as_slice(), lo, hi, @@ -100,7 +98,6 @@ impl DriveDocumentHavingQuery<'_> { value .map_err(|e| Error::GroveDB(Box::new(e)))? .into_iter() - .map(|entry| entry.key_pair()) .map(|(avg, key)| RankedEntry { key, value: RankedEntryValue::AvgFixedPoint(avg), diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/execute_top_k.rs b/packages/rs-drive/src/query/drive_document_ranked_query/execute_top_k.rs index 7f4e6b348da..9cda62c9234 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/execute_top_k.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/execute_top_k.rs @@ -15,7 +15,7 @@ use crate::drive::Drive; use crate::error::drive::DriveError; use crate::error::Error; use dpp::version::PlatformVersion; -use grovedb::{IndexedTopKPage, TransactionArg}; +use grovedb::{IndexedTopKKeysPage, TransactionArg}; use grovedb_costs::CostContext; impl DriveDocumentRankedQuery<'_> { @@ -86,20 +86,20 @@ impl DriveDocumentRankedQuery<'_> { // because its API always does, and it ends here. let (entries, skipped) = match self.axis { RankedAxis::Count => { - let CostContext { value, cost: _ } = drive.grove.indexed_count_top_k_paginated( - path_refs.as_slice(), - self.k, - offset, - self.descending, - transaction, - grove_version, - ); - let IndexedTopKPage { entries, skipped } = + let CostContext { value, cost: _ } = + drive.grove.indexed_count_top_k_paginated_keys( + path_refs.as_slice(), + self.k, + offset, + self.descending, + transaction, + grove_version, + ); + let IndexedTopKKeysPage { entries, skipped } = value.map_err(|e| Error::GroveDB(Box::new(e)))?; ( entries .into_iter() - .map(|entry| entry.key_pair()) .map(|(count, key)| RankedEntry { key, value: RankedEntryValue::Count(count), @@ -109,7 +109,7 @@ impl DriveDocumentRankedQuery<'_> { ) } RankedAxis::Sum => { - let CostContext { value, cost: _ } = drive.grove.indexed_sum_top_k_paginated( + let CostContext { value, cost: _ } = drive.grove.indexed_sum_top_k_paginated_keys( path_refs.as_slice(), self.k, offset, @@ -117,12 +117,11 @@ impl DriveDocumentRankedQuery<'_> { transaction, grove_version, ); - let IndexedTopKPage { entries, skipped } = + let IndexedTopKKeysPage { entries, skipped } = value.map_err(|e| Error::GroveDB(Box::new(e)))?; ( entries .into_iter() - .map(|entry| entry.key_pair()) .map(|(sum, key)| RankedEntry { key, value: RankedEntryValue::Sum(sum), @@ -132,7 +131,7 @@ impl DriveDocumentRankedQuery<'_> { ) } RankedAxis::Avg => { - let CostContext { value, cost: _ } = drive.grove.indexed_avg_top_k_paginated( + let CostContext { value, cost: _ } = drive.grove.indexed_avg_top_k_paginated_keys( path_refs.as_slice(), self.k, offset, @@ -140,12 +139,11 @@ impl DriveDocumentRankedQuery<'_> { transaction, grove_version, ); - let IndexedTopKPage { entries, skipped } = + let IndexedTopKKeysPage { entries, skipped } = value.map_err(|e| Error::GroveDB(Box::new(e)))?; ( entries .into_iter() - .map(|entry| entry.key_pair()) .map(|(avg, key)| RankedEntry { key, value: RankedEntryValue::AvgFixedPoint(avg), diff --git a/packages/rs-platform-version/Cargo.toml b/packages/rs-platform-version/Cargo.toml index 578c7724c0d..7c44e773c31 100644 --- a/packages/rs-platform-version/Cargo.toml +++ b/packages/rs-platform-version/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT" thiserror = { version = "2.0.12" } bincode = { version = "=2.0.1" } versioned-feature-core = { git = "https://github.com/dashpay/versioned-feature-core", version = "1.0.0" } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274" } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4" } [features] mock-versions = [] diff --git a/packages/rs-platform-wallet/Cargo.toml b/packages/rs-platform-wallet/Cargo.toml index fb85c864286..8824f07e310 100644 --- a/packages/rs-platform-wallet/Cargo.toml +++ b/packages/rs-platform-wallet/Cargo.toml @@ -69,7 +69,7 @@ zeroize = "1" log = "0.4" # Shielded pool (optional, behind `shielded` feature) -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4", optional = true } # Direct `rusqlite` access so `FileBackedShieldedStore::open_path` can set # WAL + synchronous=NORMAL pragmas before handing the connection to # `ClientPersistentCommitmentTree`. Version locked to match the rev grovedb diff --git a/packages/rs-sdk/Cargo.toml b/packages/rs-sdk/Cargo.toml index 4ea53e07cae..08ea60d7efa 100644 --- a/packages/rs-sdk/Cargo.toml +++ b/packages/rs-sdk/Cargo.toml @@ -18,7 +18,7 @@ drive = { path = "../rs-drive", default-features = false, features = [ ] } drive-proof-verifier = { path = "../rs-drive-proof-verifier", default-features = false } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "fe17045aa7bcf47acb1f2fe0db5d738cbea50274", features = [ +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4", features = [ "client", "sqlite", ], optional = true } From 570459d9efbc795d9f2dcae7608009644bc1195c Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Sat, 22 Aug 2026 17:30:33 +0700 Subject: [PATCH 15/25] test(drive): hold the pool-paid shielded fee floors to the amortized epoch cost MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ShieldedTransfer and Unshield fee-floor tests asserted the flat fee against the per-append estimate. With grovedb's append-only accounting (dashpay/grovedb#822) that estimate is an honest upper bound per append — the compacting append rewrites the whole epoch as replaced bytes at the processing rate — and it is not the floor a pool-paid flat fee is held to: those transitions never validate affordability against an estimate; they book storage = min(actual_storage, flat) and processing = flat - storage, and the pool absorbs the one compaction per epoch by design (the other 2047 appends overpay it, and a client cannot land on it more often). The invariant is amortized: the flat fee must cover the average real write cost over a whole epoch including the compacting append, stay above the average real storage, and even the compacting append's real storage must stay below the flat fee so min() never zeroes the proposer's share. fee_floor_support measures one real epoch (2048 applied 1-action transfers on a fresh pool) once per test binary; Unshield adds its measured output-write delta over an ordinary transfer. The two tests take ~150s together for that measurement. Co-Authored-By: Claude Fable 5 --- .../shielded/mod.rs | 132 +++++++++++++++++ .../shielded/shielded_transfer_transition.rs | 120 ++++++--------- .../shielded/unshield_transition.rs | 137 +++++++++--------- 3 files changed, 248 insertions(+), 141 deletions(-) diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/mod.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/mod.rs index 4036adcb21f..1b4ae003b37 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/mod.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/mod.rs @@ -48,3 +48,135 @@ pub(super) fn update_balance<'a>(ops: &mut Vec>, new_total_ba ShieldedPoolOperationType::UpdateTotalBalance { new_total_balance }, )); } + +/// Measurement support for the pool-paid shielded fee-floor tests. +/// +/// The pool-paid shielded transitions (ShieldedTransfer, Unshield, +/// ShieldedWithdrawal) charge a FLAT fee and book +/// `storage_fee = min(actual_storage, flat)`, `processing = flat - +/// storage_fee`; they never validate affordability against a per-transition +/// estimate. The invariant such a fee has to satisfy is therefore an +/// **amortized** one: over a whole commitment-tree epoch — including the one +/// append per epoch that compacts the dense buffer into a chunk blob, by far +/// the most expensive append — the flat fee must cover the average real +/// write cost, and must stay above the average real storage so the booking +/// split never starves the proposer. The compaction append is the pool's to +/// absorb: the other `epoch - 1` appends overpay it by design, and a client +/// cannot choose to land on it more often than once per epoch. +/// +/// Measured once per test binary (an epoch is 2048 real appends). +#[cfg(test)] +pub(super) mod fee_floor_support { + use std::sync::OnceLock; + + use dpp::block::block_info::BlockInfo; + use dpp::block::epoch::Epoch; + use dpp::version::PlatformVersion; + + use crate::drive::shielded::paths::SHIELDED_NOTES_CHUNK_POWER; + use crate::state_transition_action::action_convert_to_operations::DriveHighLevelOperationConverter; + use crate::state_transition_action::shielded::shielded_transfer::v0::ShieldedTransferTransitionActionV0; + use crate::state_transition_action::shielded::shielded_transfer::ShieldedTransferTransitionAction; + use crate::state_transition_action::shielded::ShieldedActionNote; + use crate::util::test_helpers::setup::setup_drive_with_initial_state_structure; + + /// Real metered cost of one epoch of 1-action shielded transfers, + /// applied for real (`apply = true`) on a fresh pool. + pub(crate) struct TransferEpoch { + /// Appends measured (one epoch). + pub appends: u64, + /// Average total (storage + processing) per append. + pub avg_total: u64, + /// Average storage fee per append. + pub avg_storage: u64, + /// The compacting append's storage fee — the figure the pool-paid + /// booking split `min(actual_storage, flat)` must stay below. + pub boundary_storage: u64, + /// The compacting append's total. + pub boundary_total: u64, + /// An ordinary (non-compacting) append's total, late in the epoch. + pub ordinary_total: u64, + } + + /// A production-sized 1-action transfer, distinct per index. + pub(crate) fn transfer_action(i: u32, fee_amount: u64) -> ShieldedTransferTransitionAction { + ShieldedTransferTransitionAction::V0(ShieldedTransferTransitionActionV0 { + notes: vec![note(i)], + anchor: [0xAA; 32], + fee_amount, + current_total_balance: fee_amount + 1_000_000, + }) + } + + /// A production-sized note (216-byte ciphertext), distinct per index. + pub(crate) fn note(i: u32) -> ShieldedActionNote { + let b = i.to_be_bytes(); + let mut nf = [0u8; 32]; + nf[..4].copy_from_slice(&b); + nf[4] = 1; + let mut cmx = [0u8; 32]; + cmx[..4].copy_from_slice(&b); + cmx[4] = 2; + let mut cv = [0u8; 32]; + cv[..4].copy_from_slice(&b); + cv[4] = 3; + ShieldedActionNote { + nullifier: nf, + cmx, + cv_net: cv, + encrypted_note: vec![0x77; 216], + } + } + + static EPOCH: OnceLock = OnceLock::new(); + + /// One epoch of real 1-action transfers, measured once. + pub(crate) fn transfer_epoch() -> &'static TransferEpoch { + EPOCH.get_or_init(|| { + let drive = setup_drive_with_initial_state_structure(None); + let platform_version = PlatformVersion::latest(); + let epoch = Epoch::new(0).unwrap(); + let tx = drive.grove.start_transaction(); + let appends: u64 = 1u64 << SHIELDED_NOTES_CHUNK_POWER; + let fee_amount = 1_000_000_000; + let mut total = 0u64; + let mut storage = 0u64; + let mut boundary_storage = 0; + let mut boundary_total = 0; + let mut ordinary_total = 0; + for i in 0..appends { + let ops = transfer_action(i as u32, fee_amount) + .into_high_level_drive_operations(&epoch, platform_version) + .expect("operations"); + let fr = drive + .apply_drive_operations( + ops, + true, + &BlockInfo::default(), + Some(&tx), + platform_version, + None, + ) + .expect("apply"); + let t = fr.total_base_fee(); + total += t; + storage += fr.storage_fee; + if i + 1 == appends { + boundary_storage = fr.storage_fee; + boundary_total = t; + } + if i + 8 == appends { + ordinary_total = t; + } + } + TransferEpoch { + appends, + avg_total: total / appends, + avg_storage: storage / appends, + boundary_storage, + boundary_total, + ordinary_total, + } + }) + } +} diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/shielded_transfer_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/shielded_transfer_transition.rs index b5ebf06f921..2a595863aef 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/shielded_transfer_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/shielded_transfer_transition.rs @@ -152,94 +152,64 @@ mod tests { assert!(result.is_err()); } - /// Invariant: the flat `compute_minimum_shielded_fee` must cover the *actual* GroveDB write - /// cost (`Drive::calculate_fee`) of a shielded transition's operations. + /// Invariant: the flat `compute_minimum_shielded_fee` covers the **amortized** + /// real GroveDB write cost of a shielded transfer, and stays above the + /// amortized real storage so the pool-paid booking split + /// (`storage_fee = min(actual_storage, flat)`, `processing = flat - + /// storage_fee`) never starves the proposer. /// - /// A shielded transfer is the cleanest per-action case — insert nullifiers + notes + - /// pool-balance update, with no contract document — so it isolates the dominant variable - /// cost (note storage). We use production-sized notes (216-byte encrypted note → 280-byte - /// commitment-tree item) and measure the real cost in estimation mode (`apply = false`). + /// Amortized, because the fee is flat and pool-paid: the note appends that + /// compact a full dense-buffer epoch into a chunk blob cost far more than + /// the others (the epoch's bytes are rewritten as replaced storage, at the + /// processing rate), and the pool absorbs that by design — the other + /// `epoch - 1` appends overpay it, and a client cannot land on the + /// compaction more than once per epoch. The per-append worst-case + /// *estimate* is therefore not the floor for a pool-paid fee; the + /// measured epoch average is. Measured on a real pool, one epoch of real + /// appends including the compacting one (see `fee_floor_support`). /// - /// The flat fee covers it with large margin because it also bundles a flat 100M - /// proof-verification fee that `calculate_fee` never charges (Halo 2 verification is CPU, - /// not a GroveDB op). 16 is the max actions per bundle. + /// The booking split is checked at its worst point too: even the + /// compacting append's real storage must stay below the flat fee, so + /// `min()` never zeroes the proposer's processing share on any append. #[test] fn test_minimum_shielded_fee_covers_actual_grovedb_write_cost() { - use crate::util::test_helpers::setup::setup_drive_with_initial_state_structure; - use dpp::block::block_info::BlockInfo; + use super::super::fee_floor_support::transfer_epoch; use dpp::shielded::compute_minimum_shielded_fee; - let drive = setup_drive_with_initial_state_structure(None); let platform_version = PlatformVersion::latest(); - let epoch = Epoch::new(0).unwrap(); - - // Production-sized note: 216-byte encrypted note, distinct nullifier/cmx per action. - let realistic_note = |i: u8| ShieldedActionNote { - nullifier: [i.wrapping_add(1); 32], - cmx: [i.wrapping_add(101); 32], - cv_net: [i.wrapping_add(201); 32], - encrypted_note: vec![0x77; 216], - }; + let epoch = transfer_epoch(); - for num_actions in [1usize, 8, 16] { - let fee_amount = compute_minimum_shielded_fee(num_actions, platform_version) + for num_actions in [1u64, 8, 16] { + let fee_amount = compute_minimum_shielded_fee(num_actions as usize, platform_version) .expect("fee computation should not overflow"); - let notes: Vec<_> = (0..num_actions as u8).map(realistic_note).collect(); - let action = ShieldedTransferTransitionAction::V0(ShieldedTransferTransitionActionV0 { - notes, - anchor: [0xAA; 32], - fee_amount, - current_total_balance: fee_amount + 1_000_000, - }); - - let ops = action - .into_high_level_drive_operations(&epoch, platform_version) - .expect("operations"); - - // apply = false → estimation mode: no DB mutation, returns the real cost. - let fee_result = drive - .apply_drive_operations( - ops, - false, - &BlockInfo::default(), - None, - platform_version, - None, - ) - .expect("estimate write cost"); - let actual_cost = fee_result.total_base_fee(); - - // The fee must cover the real write cost. Measured margins over GroveDB cost - // (estimation mode, production-sized notes): ~10.9x at 1 action down to ~5.8x at - // the 16-action max. The margin is large and stays well above 1x because the - // per-action fee also prices the per-action Halo 2 verification CPU (which - // calculate_fee does not charge), so it exceeds the per-action GroveDB cost by - // design; see `shielded_per_action_processing_fee`. + // Each action is one note append + one nullifier; the amortized + // per-append cost scales linearly, and the flat fee also carries + // the fixed proof-verification term, so this holds with margin. assert!( - fee_amount >= actual_cost, - "compute_minimum_shielded_fee({num_actions}) = {fee_amount} must cover the actual \ - GroveDB write cost {actual_cost} (storage {} + processing {})", - fee_result.storage_fee, - fee_result.processing_fee + fee_amount >= num_actions * epoch.avg_total, + "compute_minimum_shielded_fee({num_actions}) = {fee_amount} must cover the \ + amortized real write cost {} x {} (epoch of {} appends; compacting append \ + total {}, ordinary append total {})", + num_actions, + epoch.avg_total, + epoch.appends, + epoch.boundary_total, + epoch.ordinary_total ); - - // Pin the booking-split invariant directly. The pool-paid booking in - // `execute_event/v0` splits the flat carved fee as - // storage_fee = min(real_metered_storage, flat_fee) - // processing_fee = flat_fee - storage_fee - // The `min()` only ever binds — zeroing the proposer's processing reward and - // undercharging storage — if the real metered storage EXCEEDS the flat fee. Asserting - // `flat_fee > real_metered_storage` here is exactly the condition that guarantees the - // `min()` is a no-op, so the proposer is always paid the processing remainder and - // storage is never undercharged. (Strict `>` because the flat fee also bundles the 100M - // proof-verification fee that GroveDB never meters.) assert!( - fee_amount > fee_result.storage_fee, - "compute_minimum_shielded_fee({num_actions}) = {fee_amount} must strictly exceed the \ - real metered storage {} so the booking split's min(real_storage, flat_fee) never \ - binds (proposer processing reward never zeroed, storage never undercharged)", - fee_result.storage_fee + fee_amount > num_actions * epoch.avg_storage, + "flat fee {fee_amount} must exceed the amortized real storage {} x {} so the \ + pool-paid booking split never starves the proposer", + num_actions, + epoch.avg_storage ); } + let fee_one = compute_minimum_shielded_fee(1, platform_version).expect("fee"); + assert!( + epoch.boundary_storage < fee_one, + "even the compacting append's real storage ({}) must stay below the flat fee \ + ({fee_one}), or `min(actual_storage, flat)` would zero the proposer's share", + epoch.boundary_storage + ); } } diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/unshield_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/unshield_transition.rs index b8a4c770563..adae1927679 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/unshield_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/unshield_transition.rs @@ -261,92 +261,97 @@ mod tests { assert!(result.is_err()); } - /// Invariant: the flat `compute_shielded_unshield_fee` must cover the *actual* GroveDB write - /// cost of an Unshield's operations, AND strictly exceed the metered storage so the pool-paid - /// booking split never undercharges. + /// Invariant: the flat `compute_shielded_unshield_fee` covers the **amortized** + /// real GroveDB write cost of an Unshield, and stays above the amortized + /// real storage so the pool-paid booking split never starves the + /// proposer — the same amortized reading as the ShieldedTransfer test + /// (see there, and `fee_floor_support`, for why a flat pool-paid fee is + /// held to the epoch average rather than the per-append worst case). /// - /// Unshield is pool-paid: `execute_event/v0` carves the flat fee from the shielded pool and - /// splits it as `storage_fee = min(real_metered_storage, flat_fee); processing_fee = flat_fee - - /// storage_fee`. The `min()` only binds (zeroing the proposer's processing reward and - /// undercharging storage) if real metered storage EXCEEDS the flat fee. Unshield also writes the - /// net to the output platform address (`AddBalanceToAddress`), so its fee - /// (`compute_shielded_unshield_fee`) prices that write as a flat storage component on top of the - /// base shielded fee — which is exactly why the `fee > storage` margin below holds with room to - /// spare. This test meters the real cost in estimation mode (`apply = false`, production-sized - /// 216-byte notes) and asserts both `fee >= total cost` and `fee > storage` — the latter being - /// exactly the condition that keeps the `min()` a no-op. Mirrors the ShieldedTransfer metering - /// test. + /// An Unshield is a transfer's note/nullifier writes plus the + /// `AddBalanceToAddress` output write, so its amortized cost is the + /// transfer epoch average plus the measured per-transition delta between + /// an ordinary unshield and an ordinary transfer. #[test] fn test_minimum_shielded_fee_covers_actual_grovedb_write_cost() { + use super::super::fee_floor_support::{note, transfer_action, transfer_epoch}; use crate::util::test_helpers::setup::setup_drive_with_initial_state_structure; use dpp::block::block_info::BlockInfo; use dpp::shielded::compute_shielded_unshield_fee; - let drive = setup_drive_with_initial_state_structure(None); let platform_version = PlatformVersion::latest(); - let epoch = Epoch::new(0).unwrap(); - - // Production-sized change note: 216-byte encrypted note, distinct nullifier/cmx per action. - let realistic_note = |i: u8| ShieldedActionNote { - nullifier: [i.wrapping_add(1); 32], - cmx: [i.wrapping_add(101); 32], - cv_net: [i.wrapping_add(201); 32], - encrypted_note: vec![0x77; 216], - }; - - for num_actions in [1usize, 8, 16] { - let fee_amount = compute_shielded_unshield_fee(num_actions, platform_version) - .expect("fee computation should not overflow"); - let notes: Vec<_> = (0..num_actions as u8).map(realistic_note).collect(); - // `amount` (unshielding_amount) must cover the fee with a positive net to the output - // address (so the AddBalanceToAddress write is exercised, the heaviest extra op). - let amount = fee_amount + 1_000_000; - let action = UnshieldTransitionAction::V0(UnshieldTransitionActionV0 { - output_address: PlatformAddress::P2pkh([0xBB; 20]), - amount, - notes, - anchor: [0xAA; 32], - fee_amount, - current_total_balance: amount + 1_000_000, - chargeable_failure: false, - }); - - let ops = action - .into_high_level_drive_operations(&epoch, platform_version) - .expect("operations"); - - // apply = false → estimation mode: no DB mutation, returns the real cost. - let fee_result = drive + let epoch_info = Epoch::new(0).unwrap(); + let epoch = transfer_epoch(); + + // The per-transition delta of an Unshield over a ShieldedTransfer at + // an ordinary (non-compacting) position: the output address write. + let measure = |ops: Vec| { + let drive = setup_drive_with_initial_state_structure(None); + drive .apply_drive_operations( ops, - false, + true, &BlockInfo::default(), None, platform_version, None, ) - .expect("estimate write cost"); - let actual_cost = fee_result.total_base_fee(); - + .expect("apply") + }; + let fee_one = compute_shielded_unshield_fee(1, platform_version).expect("fee"); + let amount = fee_one + 1_000_000; + let unshield_one = measure( + UnshieldTransitionAction::V0(UnshieldTransitionActionV0 { + output_address: PlatformAddress::P2pkh([0xBB; 20]), + amount, + notes: vec![note(7)], + anchor: [0xAA; 32], + fee_amount: fee_one, + current_total_balance: amount + 1_000_000, + chargeable_failure: false, + }) + .into_high_level_drive_operations(&epoch_info, platform_version) + .expect("operations"), + ); + let transfer_one = measure( + transfer_action(7, fee_one) + .into_high_level_drive_operations(&epoch_info, platform_version) + .expect("operations"), + ); + let delta_total = unshield_one + .total_base_fee() + .saturating_sub(transfer_one.total_base_fee()); + let delta_storage = unshield_one + .storage_fee + .saturating_sub(transfer_one.storage_fee); + + for num_actions in [1u64, 8, 16] { + let fee_amount = compute_shielded_unshield_fee(num_actions as usize, platform_version) + .expect("fee computation should not overflow"); assert!( - fee_amount >= actual_cost, - "compute_shielded_unshield_fee({num_actions}) = {fee_amount} must cover the actual \ - Unshield GroveDB write cost {actual_cost} (storage {} + processing {})", - fee_result.storage_fee, - fee_result.processing_fee + fee_amount >= num_actions * epoch.avg_total + delta_total, + "compute_shielded_unshield_fee({num_actions}) = {fee_amount} must cover the \ + amortized real Unshield write cost {} x {} + {} (output write)", + num_actions, + epoch.avg_total, + delta_total ); - - // The booking-split invariant: flat fee must strictly exceed real metered storage so - // `storage_fee = min(real_storage, flat_fee)` never binds (proposer processing reward - // never zeroed, storage never undercharged). See `execute_event/v0`. assert!( - fee_amount > fee_result.storage_fee, - "compute_shielded_unshield_fee({num_actions}) = {fee_amount} must strictly exceed the \ - real metered Unshield storage {} so the booking split's min(real_storage, flat_fee) \ - never binds", - fee_result.storage_fee + fee_amount > num_actions * epoch.avg_storage + delta_storage, + "flat fee {fee_amount} must exceed the amortized real storage {} x {} + {} so \ + the pool-paid booking split never starves the proposer", + num_actions, + epoch.avg_storage, + delta_storage ); } + assert!( + epoch.boundary_storage + delta_storage < fee_one, + "even the compacting append's real storage ({} + {}) must stay below the flat fee \ + ({fee_one}), or `min(actual_storage, flat)` would zero the proposer's share", + epoch.boundary_storage, + delta_storage + ); } #[test] From 315961430e4c1ea29b88a909e2b61902451192e8 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Sat, 22 Aug 2026 18:22:13 +0700 Subject: [PATCH 16/25] fix(drive-abci)!: admit ShieldFromAssetLock on its flat pool fee at PV14, not on a per-transition estimate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Under GROVE_V4 (protocol v14) grovedb's commitment-tree estimator is a deliberate per-append upper bound — a full epoch compaction and dense recompute charged on every note (~397M credits each). The v0 `validate_fees_of_event` gated `PaidFromAssetLockToPool` on `fee >= estimate`, but a ShieldFromAssetLock's pool fee is the flat `compute_minimum_shielded_fee(n) + asset-lock base cost` (the transform enforces `lock >= shield + pool_fee`; clients don't choose it), so the gate became a fixed comparison of two consensus constants that rejected every PV14 ShieldFromAssetLock ("provided 212,851,200 but minimum required 793,403,740"). `validate_fees_of_event` v1 (DRIVE_ABCI_METHOD_VERSIONS_V10, protocol 14 only) advertises the authoritative pool fee for `gas_wanted` and does not estimate-gate that event — the same epoch-amortized model every other pool-paid event (`PaidFromShieldedPool`) already follows, with the flat fee pinned against the amortized real cost by rs-drive's fee-floor tests and execution booking `storage = min(actual, fee)`. Every other event, the transparent `Shield` (`PaidFromAddressInputs`) included, delegates to v0 unchanged. Protocol 13 keeps V9 (estimator and accounting are locked at GROVE_V3). Tests: a v0-rejects/v1-admits contrast test on the same event; `mainnet_halt_repro::dropped_shield_must_not_mutate_state` now runs under protocol 13, the last version whose locked estimator leaves the halting band open (at PV14 the band test measures width 0 — first headroom that reaches execution == first that executes == 929,279,120 credits at 494 notes — so no mid-band shield can be built); the band test stays ignored for cost only (~60 fresh-platform runs, ~11 min). Co-Authored-By: Claude Fable 5 --- .../validate_fees_of_event/mod.rs | 10 +- .../validate_fees_of_event/v1/mod.rs | 140 ++++++++++++++++ .../state_transitions/shield/tests.rs | 27 ++- .../drive_abci_method_versions/mod.rs | 1 + .../drive_abci_method_versions/v10.rs | 157 ++++++++++++++++++ .../rs-platform-version/src/version/v14.rs | 4 +- 6 files changed, 329 insertions(+), 10 deletions(-) create mode 100644 packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/v1/mod.rs create mode 100644 packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/v10.rs diff --git a/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/mod.rs index fa58f448d76..428effc6ba5 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/mod.rs @@ -1,4 +1,5 @@ mod v0; +mod v1; use crate::error::execution::ExecutionError; use crate::error::Error; @@ -72,9 +73,16 @@ where platform_version, previous_fee_versions, ), + 1 => self.validate_fees_of_event_v1( + event, + block_info, + transaction, + platform_version, + previous_fee_versions, + ), version => Err(Error::Execution(ExecutionError::UnknownVersionMismatch { method: "validate_fees_of_event".to_string(), - known_versions: vec![0], + known_versions: vec![0, 1], received: version, })), }; diff --git a/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/v1/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/v1/mod.rs new file mode 100644 index 00000000000..eee2e74fbdf --- /dev/null +++ b/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/v1/mod.rs @@ -0,0 +1,140 @@ +use crate::error::Error; +use crate::execution::types::execution_event::ExecutionEvent; +use crate::platform_types::platform::Platform; +use crate::rpc::core::CoreRPCLike; +use dpp::block::block_info::BlockInfo; +use dpp::fee::default_costs::CachedEpochIndexFeeVersions; +use dpp::fee::fee_result::FeeResult; +use dpp::prelude::ConsensusValidationResult; +use dpp::version::PlatformVersion; +use drive::grovedb::TransactionArg; + +impl Platform +where + C: CoreRPCLike, +{ + /// Version 1: identical to v0 except for `PaidFromAssetLockToPool` (the + /// `ShieldFromAssetLock` path), which is no longer gated on a per-transition GroveDB cost + /// estimate. + /// + /// The pool fee a `ShieldFromAssetLock` books is the flat + /// `compute_minimum_shielded_fee(num_actions) + asset-lock base cost` (its transform enforces + /// `lock_value >= shield_amount + pool_fee`), i.e. the same kind of flat fee a + /// `ShieldedTransfer`/`Unshield` carries — and those pool-paid events were never + /// estimate-gated. Under GROVE_V4 the commitment-tree estimator is a deliberate per-append + /// UPPER BOUND (a full epoch compaction and dense recompute charged on every note), so + /// `estimate >= actual` holds by construction and the v0 gate `fee >= estimate` degenerates + /// into a fixed comparison of two consensus constants — one the flat fee can never clear + /// without pricing every shield at the once-per-epoch compaction spike. The flat fee is + /// instead pinned against the epoch-AMORTIZED real cost (the + /// `test_minimum_shielded_fee_covers_actual_grovedb_write_cost` tests in rs-drive); the pool + /// absorbs the boundary append by design, and execution books + /// `storage = min(actual_storage, fee)`, `processing = fee - storage` whatever the estimate + /// would have said. + pub(super) fn validate_fees_of_event_v1( + &self, + event: &ExecutionEvent, + block_info: &BlockInfo, + transaction: TransactionArg, + platform_version: &PlatformVersion, + previous_fee_versions: &CachedEpochIndexFeeVersions, + ) -> Result, Error> { + match event { + ExecutionEvent::PaidFromAssetLockToPool { + fees_to_add_to_pool, + .. + } => { + // Advertise the authoritative pool fee for `gas_wanted`. Its storage/processing + // split is only known once the operations are metered at execution, so the whole + // fee is advertised as processing here; the total — what `gas_wanted` carries — + // is exact. + Ok(ConsensusValidationResult::new_with_data( + FeeResult::default_with_fees(0, *fees_to_add_to_pool), + )) + } + _ => self.validate_fees_of_event_v0( + event, + block_info, + transaction, + platform_version, + previous_fee_versions, + ), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::execution::types::execution_operation::ValidationOperation; + use crate::test::helpers::setup::TestPlatformBuilder; + use dpp::consensus::state::state_error::StateError; + use dpp::consensus::ConsensusError; + + /// Same event, same platform: a flat pool fee far below the estimated cost of the + /// transition's operations is rejected by v0 (`fee >= estimate`) and admitted by v1, which + /// still advertises the authoritative pool fee for `gas_wanted`. + #[test] + fn validate_fees_of_event_v1_paid_from_asset_lock_to_pool_is_not_estimate_gated() { + let platform = TestPlatformBuilder::new() + .with_latest_protocol_version() + .build_with_mock_rpc() + .set_initial_state_structure(); + + let platform_version = PlatformVersion::latest(); + let block_info = BlockInfo::default(); + let previous_fee_versions = Default::default(); + + let fees_to_add_to_pool = 1_000_000u64; + let estimated_cost = FeeResult::default_with_fees(0, 10 * fees_to_add_to_pool); + let event = ExecutionEvent::PaidFromAssetLockToPool { + fees_to_add_to_pool, + added_to_balance_outputs: None, + operations: vec![], + execution_operations: vec![ValidationOperation::PrecalculatedOperation(estimated_cost)], + }; + + let v0 = platform + .platform + .validate_fees_of_event_v0( + &event, + &block_info, + None, + platform_version, + &previous_fee_versions, + ) + .expect("v0 must be Ok"); + assert!( + matches!( + v0.errors.as_slice(), + [ConsensusError::StateError( + StateError::InvalidShieldedProofError(_) + )] + ), + "v0 gates the flat pool fee on the estimate, got {:?}", + v0.errors + ); + + let v1 = platform + .platform + .validate_fees_of_event_v1( + &event, + &block_info, + None, + platform_version, + &previous_fee_versions, + ) + .expect("v1 must be Ok"); + assert!( + v1.errors.is_empty(), + "v1 must not estimate-gate the flat pool fee, got {:?}", + v1.errors + ); + let fee = v1.into_data().expect("fee result present"); + assert_eq!( + fee.total_base_fee(), + fees_to_add_to_pool, + "advertised pool fee must equal fees_to_add_to_pool (gas_wanted parity)" + ); + } +} diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield/tests.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield/tests.rs index c538ac479fd..7d68d49adea 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield/tests.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield/tests.rs @@ -1908,11 +1908,15 @@ mod tests { /// per-transition rollback (shipped in 4.1.1), but still a transition that can never /// confirm despite paying the quoted fee. /// - /// Ignored until the estimation gap is closed: the estimated-cost path skips the keyless - /// commitment-tree append entirely (dashpay/grovedb#812), so the band is measurably open - /// (18,919,200 credits, 10.7% of the fee, at 494 notes). Re-enable with the grovedb pin - /// bump that fixes it. Also ~40 full Orchard proving runs, so keep it out of routine CI. - #[ignore = "open until the grovedb#812 estimator fix is pinned; ~40 Orchard proving runs"] + /// Under protocol version 13 (GROVE_V3) the estimated-cost path skips the keyless + /// commitment-tree append entirely (dashpay/grovedb#812, locked for replay), so the band + /// is measurably open there (18,919,200 credits, 10.7% of the fee, at 494 notes). From + /// protocol version 14 (GROVE_V4) the estimator is a per-append upper bound and the band + /// is closed: measured at 494 notes, the first headroom that reaches execution and the + /// first that executes coincide at 929,279,120 credits (width 0). Kept out of routine CI + /// only for cost — ~60 fresh-platform runs at ~10 s each (the Orchard proof is built + /// once); run it by hand after any change to the shielded fee model or the grovedb pin. + #[ignore = "band closed at PV14 (width 0); ~60 fresh-platform runs, ~11 min — run by hand"] #[tokio::test] async fn shield_fee_estimate_and_actual_must_not_leave_a_halting_band() { let pv = PlatformVersion::latest(); @@ -1984,11 +1988,20 @@ mod tests { /// the block without it compute a different app hash and can never agree. /// /// This pins the invariant that a dropped transition must not mutate state. + /// + /// Runs under protocol version 13: the version mainnet was on at the halt, and the last + /// one whose estimator leaves the band open — GROVE_V3's estimation path skips the + /// keyless commitment-tree append (dashpay/grovedb#812), locked there so historical + /// admission decisions replay identically. From protocol version 14 (GROVE_V4) the + /// estimator is a per-append upper bound, so no funding level reaches execution + /// under-funded and the mid-band shield this test needs cannot be built (see + /// `shield_fee_estimate_and_actual_must_not_leave_a_halting_band`). #[tokio::test] async fn dropped_shield_must_not_mutate_state() { - let pv = PlatformVersion::latest(); + let pv = PlatformVersion::get(13).expect("protocol version 13 should exist"); let b = build_bundle(); - // Sits inside the measured band: accepted by validation, rejected by execution. + // Sits inside the band measured under protocol version 13: accepted by validation, + // rejected by execution. let headroom = 177_215_759u64; let mut platform = setup_platform(); diff --git a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/mod.rs b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/mod.rs index 08921d8bd9f..55e85147afa 100644 --- a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/mod.rs +++ b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/mod.rs @@ -9,6 +9,7 @@ pub mod v6; pub mod v7; pub mod v8; pub mod v9; +pub mod v10; #[derive(Clone, Debug, Default)] pub struct DriveAbciMethodVersions { diff --git a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/v10.rs b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/v10.rs new file mode 100644 index 00000000000..a822d59d67d --- /dev/null +++ b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/v10.rs @@ -0,0 +1,157 @@ +use crate::version::drive_abci_versions::drive_abci_method_versions::{ + DriveAbciBlockEndMethodVersions, DriveAbciBlockFeeProcessingMethodVersions, + DriveAbciBlockStartMethodVersions, DriveAbciCoreBasedUpdatesMethodVersions, + DriveAbciCoreChainLockMethodVersionsAndConstants, DriveAbciCoreInstantSendLockMethodVersions, + DriveAbciEngineMethodVersions, DriveAbciEpochMethodVersions, + DriveAbciFeePoolInwardsDistributionMethodVersions, + DriveAbciFeePoolOutwardsDistributionMethodVersions, + DriveAbciIdentityCreditWithdrawalMethodVersions, DriveAbciInitializationMethodVersions, + DriveAbciMasternodeIdentitiesUpdatesMethodVersions, DriveAbciMethodVersions, + DriveAbciPlatformStateStorageMethodVersions, DriveAbciProtocolUpgradeMethodVersions, + DriveAbciStateTransitionProcessingMethodVersions, DriveAbciTokensProcessingMethodVersions, + DriveAbciVotingMethodVersions, +}; + +// Introduced in Protocol version 14. +// +// Identical to DRIVE_ABCI_METHOD_VERSIONS_V10 (the protocol-v13 method set) +// except ONE field: `validate_fees_of_event` 0 -> 1. The v1 drops the +// per-transition GroveDB cost-estimate gate from `PaidFromAssetLockToPool` +// (the `ShieldFromAssetLock` path). The pool fee that transition books is the +// flat `compute_minimum_shielded_fee(num_actions) + asset-lock base cost` — +// the same kind of flat, epoch-amortized fee a `ShieldedTransfer`/`Unshield` +// carries, and those pool-paid events were never estimate-gated. Under +// GROVE_V4 the commitment-tree estimator is a per-append UPPER BOUND (a full +// epoch compaction and dense recompute on every note), so the v0 gate +// `fee >= estimate` degenerated into a fixed comparison of two consensus +// constants that rejected every ShieldFromAssetLock. Everything else — +// `PaidFromAddressInputs` (the transparent `Shield`) included — is unchanged. +// Admission changes, so the bump MUST stay inactive on v13 (V9 keeps it 0). +pub const DRIVE_ABCI_METHOD_VERSIONS_V10: DriveAbciMethodVersions = DriveAbciMethodVersions { + engine: DriveAbciEngineMethodVersions { + init_chain: 0, + check_tx: 0, + run_block_proposal: 0, + finalize_block_proposal: 0, + consensus_params_update: 1, + }, + initialization: DriveAbciInitializationMethodVersions { + initial_core_height_and_time: 0, + create_genesis_state: 1, + }, + core_based_updates: DriveAbciCoreBasedUpdatesMethodVersions { + update_core_info: 0, + update_masternode_list: 0, + update_quorum_info: 0, + masternode_updates: DriveAbciMasternodeIdentitiesUpdatesMethodVersions { + get_voter_identity_key: 0, + get_operator_identity_keys: 0, + get_owner_identity_withdrawal_key: 0, + get_owner_identity_owner_key: 0, + get_voter_identifier_from_masternode_list_item: 0, + get_operator_identifier_from_masternode_list_item: 0, + create_operator_identity: 0, + create_owner_identity: 1, + create_voter_identity: 0, + disable_identity_keys: 0, + update_masternode_identities: 0, + update_operator_identity: 0, + update_owner_withdrawal_address: 1, + update_voter_identity: 0, + }, + }, + protocol_upgrade: DriveAbciProtocolUpgradeMethodVersions { + check_for_desired_protocol_upgrade: 1, + upgrade_protocol_version_on_epoch_change: 0, + perform_events_on_first_block_of_protocol_change: Some(1), + protocol_version_upgrade_percentage_needed: 67, + }, + block_fee_processing: DriveAbciBlockFeeProcessingMethodVersions { + add_process_epoch_change_operations: 0, + process_block_fees_and_validate_sum_trees: 1, + }, + tokens_processing: DriveAbciTokensProcessingMethodVersions { + validate_token_aggregated_balance: 0, + }, + core_chain_lock: DriveAbciCoreChainLockMethodVersionsAndConstants { + choose_quorum: 0, + verify_chain_lock: 0, + verify_chain_lock_locally: 0, + verify_chain_lock_through_core: 0, + make_sure_core_is_synced_to_chain_lock: 0, + recent_block_count_amount: 2, + }, + core_instant_send_lock: DriveAbciCoreInstantSendLockMethodVersions { + verify_recent_signature_locally: 0, + }, + fee_pool_inwards_distribution: DriveAbciFeePoolInwardsDistributionMethodVersions { + add_distribute_block_fees_into_pools_operations: 0, + add_distribute_storage_fee_to_epochs_operations: 0, + }, + fee_pool_outwards_distribution: DriveAbciFeePoolOutwardsDistributionMethodVersions { + add_distribute_fees_from_oldest_unpaid_epoch_pool_to_proposers_operations: 1, + add_epoch_pool_to_proposers_payout_operations: 0, + find_oldest_epoch_needing_payment: 0, + fetch_reward_shares_list_for_masternode: 0, + }, + withdrawals: DriveAbciIdentityCreditWithdrawalMethodVersions { + build_untied_withdrawal_transactions_from_documents: 0, + dequeue_and_build_unsigned_withdrawal_transactions: 0, + fetch_transactions_block_inclusion_status: 0, + pool_withdrawals_into_transactions_queue: 1, + update_broadcasted_withdrawal_statuses: 0, + rebroadcast_expired_withdrawal_documents: 1, + append_signatures_and_broadcast_withdrawal_transactions: 0, + cleanup_expired_locks_of_withdrawal_amounts: 0, + }, + voting: DriveAbciVotingMethodVersions { + keep_record_of_finished_contested_resource_vote_poll: 0, + clean_up_after_vote_poll_end: 0, + clean_up_after_contested_resources_vote_poll_end: 1, + check_for_ended_vote_polls: 0, + tally_votes_for_contested_document_resource_vote_poll: 0, + award_document_to_winner: 0, + delay_vote_poll: 0, + run_dao_platform_events: 0, + remove_votes_for_removed_masternodes: 0, + }, + state_transition_processing: DriveAbciStateTransitionProcessingMethodVersions { + execute_event: 0, + // Unchanged: the outer processing loop did not change at v13, so it stays at v0. Only the + // `process_validation_result` helper it dispatches to changed (below). + process_raw_state_transitions: 0, + // changed: v1 records the balance effects of paid-INVALID / unsuccessful-paid transitions + // (charged fees, adjusted outputs, applied chargeable-failure credits) that v0 dropped. Same + // v13 recorded-set expansion as `record_added_balance_outputs` below; kept in a real _v1 so + // no version conditional lives inside the _v0 helper. + process_validation_result: 1, + decode_raw_state_transitions: 0, + validate_fees_of_event: 1, + store_address_balances_to_recent_block_storage: Some(0), + cleanup_recent_block_storage_address_balances: Some(0), + // changed: v1 records shielded-spend transparent credits (Unshield net output, + // ShieldFromAssetLock surplus, identity-create fallback) folded at the executor. The storage + // method above is unchanged — only which credits feed its map did. + record_added_balance_outputs: 1, + }, + epoch: DriveAbciEpochMethodVersions { + gather_epoch_info: 0, + get_genesis_time: 0, + }, + block_start: DriveAbciBlockStartMethodVersions { + clear_drive_block_cache: 0, + }, + block_end: DriveAbciBlockEndMethodVersions { + update_state_cache: 0, + update_drive_cache: 0, + validator_set_update: 2, + should_checkpoint: Some(0), + update_checkpoints: Some(0), + record_shielded_pool_anchor: Some(0), + prune_shielded_pool_anchors: Some(0), + }, + platform_state_storage: DriveAbciPlatformStateStorageMethodVersions { + fetch_platform_state: 0, + store_platform_state: 0, + }, +}; diff --git a/packages/rs-platform-version/src/version/v14.rs b/packages/rs-platform-version/src/version/v14.rs index cfa1f2a0efc..6e0f2d7d7e2 100644 --- a/packages/rs-platform-version/src/version/v14.rs +++ b/packages/rs-platform-version/src/version/v14.rs @@ -15,7 +15,7 @@ use crate::version::dpp_versions::dpp_validation_versions::v5::DPP_VALIDATION_VE use crate::version::dpp_versions::dpp_voting_versions::v2::VOTING_VERSION_V2; use crate::version::dpp_versions::DPPVersion; use crate::version::drive_abci_versions::drive_abci_checkpoint_parameters::v1::DRIVE_ABCI_CHECKPOINT_PARAMETERS_V1; -use crate::version::drive_abci_versions::drive_abci_method_versions::v9::DRIVE_ABCI_METHOD_VERSIONS_V9; +use crate::version::drive_abci_versions::drive_abci_method_versions::v10::DRIVE_ABCI_METHOD_VERSIONS_V10; use crate::version::drive_abci_versions::drive_abci_query_versions::v3::DRIVE_ABCI_QUERY_VERSIONS_V3; use crate::version::drive_abci_versions::drive_abci_structure_versions::v1::DRIVE_ABCI_STRUCTURE_VERSIONS_V1; use crate::version::drive_abci_versions::drive_abci_validation_versions::v10::DRIVE_ABCI_VALIDATION_VERSIONS_V10; @@ -126,7 +126,7 @@ pub const PLATFORM_V14: PlatformVersion = PlatformVersion { drive: DRIVE_VERSION_V9, // changed: drive document method versions v4 — v2 index walkers (shared-prefix aggregate indexes become insertable) + the detect_ranked_mode slot drive_abci: DriveAbciVersion { structs: DRIVE_ABCI_STRUCTURE_VERSIONS_V1, - methods: DRIVE_ABCI_METHOD_VERSIONS_V9, + methods: DRIVE_ABCI_METHOD_VERSIONS_V10, // changed: validate_fees_of_event v1 — ShieldFromAssetLock admission no longer gated on a per-transition GroveDB cost estimate (the flat pool fee is epoch-amortized, like ShieldedTransfer) validation_and_processing: DRIVE_ABCI_VALIDATION_VERSIONS_V10, // changed: contested-index cross-check + refersTo document reference validation withdrawal_constants: DRIVE_ABCI_WITHDRAWAL_CONSTANTS_V2, query: DRIVE_ABCI_QUERY_VERSIONS_V3, // changed: ranked + boolean-HAVING routing gate From c5b348f16f144064197868fe090341da600cdc46 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Sat, 22 Aug 2026 18:25:14 +0700 Subject: [PATCH 17/25] style(platform-version): rustfmt module order for drive_abci_method_versions v10 Co-Authored-By: Claude Fable 5 --- .../drive_abci_versions/drive_abci_method_versions/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/mod.rs b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/mod.rs index 55e85147afa..189e3ccc7ab 100644 --- a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/mod.rs +++ b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/mod.rs @@ -1,6 +1,7 @@ use versioned_feature_core::{FeatureVersion, OptionalFeatureVersion}; pub mod v1; +pub mod v10; pub mod v2; pub mod v3; pub mod v4; @@ -9,7 +10,6 @@ pub mod v6; pub mod v7; pub mod v8; pub mod v9; -pub mod v10; #[derive(Clone, Debug, Default)] pub struct DriveAbciMethodVersions { From 1e2bf4f93e452839686d7ca5a5c08a511e43832a Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 24 Aug 2026 09:14:05 +0200 Subject: [PATCH 18/25] chore(deps)!: bump grovedb to the fixed per-append commitment-tree cost model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 753a11f1 brings grovedb #828 (O(height) dense-buffer root maintenance), #829 (fixed per-append cost: churn buffer, fixed dense model, amortized compaction, constant-price frontier) and #830 (prepaid puts carry no seek). Under GROVE_V4 a CommitmentTreeInsert now meters the same figure at every position of the tree — compaction included — and the estimators price that model tightly instead of bounding it. GROVE_V3 (protocol 13) figures are locked and unchanged. API fallout: CommitmentTree::open and compute_current_state_root take the grove version (the load charge and root read are now versioned) — threaded through the shielded snapshot bake/apply paths and the genesis seeder test. Co-Authored-By: Claude Fable 5 --- Cargo.lock | 34 ++++++++++--------- packages/rs-dpp/Cargo.toml | 2 +- packages/rs-drive-abci/Cargo.toml | 8 ++--- .../create_genesis_state/test/shielded.rs | 19 +++++++---- .../validate_fees_of_event/mod.rs | 10 +----- .../src/shielded_snapshot/mod.rs | 31 +++++++++++------ packages/rs-drive/Cargo.toml | 12 +++---- packages/rs-platform-version/Cargo.toml | 2 +- .../drive_abci_method_versions/mod.rs | 1 - .../rs-platform-version/src/version/v14.rs | 4 +-- packages/rs-platform-wallet/Cargo.toml | 2 +- packages/rs-sdk/Cargo.toml | 2 +- 12 files changed, 67 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 836a2c0a768..f2edf42fd96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2973,7 +2973,7 @@ dependencies = [ [[package]] name = "grovedb" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" +source = "git+https://github.com/dashpay/grovedb?rev=753a11f14c9a4bc72bf2d5302751dd43d174621e#753a11f14c9a4bc72bf2d5302751dd43d174621e" dependencies = [ "axum 0.8.9", "bincode", @@ -3012,7 +3012,7 @@ dependencies = [ [[package]] name = "grovedb-bulk-append-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" +source = "git+https://github.com/dashpay/grovedb?rev=753a11f14c9a4bc72bf2d5302751dd43d174621e#753a11f14c9a4bc72bf2d5302751dd43d174621e" dependencies = [ "bincode", "blake3", @@ -3023,13 +3023,14 @@ dependencies = [ "grovedb-storage", "grovedb-version", "hex", + "integer-encoding", "thiserror 2.0.18", ] [[package]] name = "grovedb-commitment-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" +source = "git+https://github.com/dashpay/grovedb?rev=753a11f14c9a4bc72bf2d5302751dd43d174621e#753a11f14c9a4bc72bf2d5302751dd43d174621e" dependencies = [ "blake3", "grovedb-bulk-append-tree", @@ -3046,7 +3047,7 @@ dependencies = [ [[package]] name = "grovedb-costs" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" +source = "git+https://github.com/dashpay/grovedb?rev=753a11f14c9a4bc72bf2d5302751dd43d174621e#753a11f14c9a4bc72bf2d5302751dd43d174621e" dependencies = [ "integer-encoding", "intmap", @@ -3056,20 +3057,21 @@ dependencies = [ [[package]] name = "grovedb-dense-fixed-sized-merkle-tree" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" +source = "git+https://github.com/dashpay/grovedb?rev=753a11f14c9a4bc72bf2d5302751dd43d174621e#753a11f14c9a4bc72bf2d5302751dd43d174621e" dependencies = [ "bincode", "blake3", "grovedb-costs", "grovedb-query", "grovedb-storage", + "grovedb-version", "thiserror 2.0.18", ] [[package]] name = "grovedb-element" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" +source = "git+https://github.com/dashpay/grovedb?rev=753a11f14c9a4bc72bf2d5302751dd43d174621e#753a11f14c9a4bc72bf2d5302751dd43d174621e" dependencies = [ "bincode", "bincode_derive", @@ -3085,7 +3087,7 @@ dependencies = [ [[package]] name = "grovedb-epoch-based-storage-flags" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" +source = "git+https://github.com/dashpay/grovedb?rev=753a11f14c9a4bc72bf2d5302751dd43d174621e#753a11f14c9a4bc72bf2d5302751dd43d174621e" dependencies = [ "grovedb-costs", "hex", @@ -3097,7 +3099,7 @@ dependencies = [ [[package]] name = "grovedb-merk" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" +source = "git+https://github.com/dashpay/grovedb?rev=753a11f14c9a4bc72bf2d5302751dd43d174621e#753a11f14c9a4bc72bf2d5302751dd43d174621e" dependencies = [ "bincode", "bincode_derive", @@ -3123,7 +3125,7 @@ dependencies = [ [[package]] name = "grovedb-merkle-mountain-range" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" +source = "git+https://github.com/dashpay/grovedb?rev=753a11f14c9a4bc72bf2d5302751dd43d174621e#753a11f14c9a4bc72bf2d5302751dd43d174621e" dependencies = [ "bincode", "blake3", @@ -3136,7 +3138,7 @@ dependencies = [ [[package]] name = "grovedb-path" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" +source = "git+https://github.com/dashpay/grovedb?rev=753a11f14c9a4bc72bf2d5302751dd43d174621e#753a11f14c9a4bc72bf2d5302751dd43d174621e" dependencies = [ "hex", ] @@ -3144,7 +3146,7 @@ dependencies = [ [[package]] name = "grovedb-private-document-store" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" +source = "git+https://github.com/dashpay/grovedb?rev=753a11f14c9a4bc72bf2d5302751dd43d174621e#753a11f14c9a4bc72bf2d5302751dd43d174621e" dependencies = [ "blake3", "grovedb-bulk-append-tree", @@ -3157,7 +3159,7 @@ dependencies = [ [[package]] name = "grovedb-query" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" +source = "git+https://github.com/dashpay/grovedb?rev=753a11f14c9a4bc72bf2d5302751dd43d174621e#753a11f14c9a4bc72bf2d5302751dd43d174621e" dependencies = [ "bincode", "byteorder", @@ -3173,7 +3175,7 @@ dependencies = [ [[package]] name = "grovedb-storage" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" +source = "git+https://github.com/dashpay/grovedb?rev=753a11f14c9a4bc72bf2d5302751dd43d174621e#753a11f14c9a4bc72bf2d5302751dd43d174621e" dependencies = [ "blake3", "grovedb-costs", @@ -3192,7 +3194,7 @@ dependencies = [ [[package]] name = "grovedb-version" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" +source = "git+https://github.com/dashpay/grovedb?rev=753a11f14c9a4bc72bf2d5302751dd43d174621e#753a11f14c9a4bc72bf2d5302751dd43d174621e" dependencies = [ "thiserror 2.0.18", "versioned-feature-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3201,7 +3203,7 @@ dependencies = [ [[package]] name = "grovedb-visualize" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" +source = "git+https://github.com/dashpay/grovedb?rev=753a11f14c9a4bc72bf2d5302751dd43d174621e#753a11f14c9a4bc72bf2d5302751dd43d174621e" dependencies = [ "hex", "itertools 0.14.0", @@ -3210,7 +3212,7 @@ dependencies = [ [[package]] name = "grovedbg-types" version = "5.0.1" -source = "git+https://github.com/dashpay/grovedb?rev=f52adb641aedb8d8d417ec7fb48243e282eb75f4#f52adb641aedb8d8d417ec7fb48243e282eb75f4" +source = "git+https://github.com/dashpay/grovedb?rev=753a11f14c9a4bc72bf2d5302751dd43d174621e#753a11f14c9a4bc72bf2d5302751dd43d174621e" dependencies = [ "serde", "serde_with 3.21.0", diff --git a/packages/rs-dpp/Cargo.toml b/packages/rs-dpp/Cargo.toml index 805cea56f0c..0759b188069 100644 --- a/packages/rs-dpp/Cargo.toml +++ b/packages/rs-dpp/Cargo.toml @@ -71,7 +71,7 @@ strum = { version = "0.26", features = ["derive"] } json-schema-compatibility-validator = { path = '../rs-json-schema-compatibility-validator', optional = true } once_cell = "1.19.0" tracing = { version = "0.1.41" } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "753a11f14c9a4bc72bf2d5302751dd43d174621e", optional = true } [dev-dependencies] tokio = { version = "1.40", features = ["full"] } diff --git a/packages/rs-drive-abci/Cargo.toml b/packages/rs-drive-abci/Cargo.toml index 75fc9b6d577..6d8a854e32d 100644 --- a/packages/rs-drive-abci/Cargo.toml +++ b/packages/rs-drive-abci/Cargo.toml @@ -82,7 +82,7 @@ derive_more = { version = "1.0", features = ["from", "deref", "deref_mut"] } async-trait = "0.1.77" console-subscriber = { version = "0.4", optional = true } bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f", optional = true } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4" } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "753a11f14c9a4bc72bf2d5302751dd43d174621e" } nonempty = "0.11" # Shielded-pool snapshot needs raw RocksDB SstFileWriter + ingest_external_file_cf # bindings, and blake3 for the snapshot-file checksum. @@ -107,7 +107,7 @@ dpp = { path = "../rs-dpp", default-features = false, features = [ drive = { path = "../rs-drive", features = ["fixtures-and-mocks"] } drive-proof-verifier = { path = "../rs-drive-proof-verifier" } strategy-tests = { path = "../strategy-tests" } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4", features = ["client"] } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "753a11f14c9a4bc72bf2d5302751dd43d174621e", features = ["client"] } assert_matches = "1.5.0" drive-abci = { path = ".", features = ["testing-config", "mocks", "shielded_test_data"] } bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f" } @@ -121,8 +121,8 @@ integer-encoding = { version = "4.0.0" } # For dump_only_default_and_aux_cfs_under_shielded_subtree_prefix — same # subtree-prefix algorithm grovedb uses internally. -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4" } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "753a11f14c9a4bc72bf2d5302751dd43d174621e" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "753a11f14c9a4bc72bf2d5302751dd43d174621e" } [features] default = ["bls-signatures"] diff --git a/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/shielded.rs b/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/shielded.rs index 53eadcdf40e..969d2cfc875 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/shielded.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/initialization/create_genesis_state/test/shielded.rs @@ -310,13 +310,18 @@ impl Platform { .raw_storage() .get_transactional_storage_context(subtree_path, Some(&data_batch), tx) .unwrap(); - let mut ct = CommitmentTree::<_, DashMemo>::open(0, chunk_power, storage_ctx) - .value - .map_err(|e| { - Error::Execution(ExecutionError::CorruptedCodeExecution(Box::leak( - format!("seed: CommitmentTree::open: {e}").into_boxed_str(), - ))) - })?; + let mut ct = CommitmentTree::<_, DashMemo>::open( + 0, + chunk_power, + storage_ctx, + &platform_version.drive.grove_version, + ) + .value + .map_err(|e| { + Error::Execution(ExecutionError::CorruptedCodeExecution(Box::leak( + format!("seed: CommitmentTree::open: {e}").into_boxed_str(), + ))) + })?; // Batched seed via repeated `append_many_raw` (grovedb PR #751). // Each batch: diff --git a/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/mod.rs index 428effc6ba5..fa58f448d76 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/mod.rs @@ -1,5 +1,4 @@ mod v0; -mod v1; use crate::error::execution::ExecutionError; use crate::error::Error; @@ -73,16 +72,9 @@ where platform_version, previous_fee_versions, ), - 1 => self.validate_fees_of_event_v1( - event, - block_info, - transaction, - platform_version, - previous_fee_versions, - ), version => Err(Error::Execution(ExecutionError::UnknownVersionMismatch { method: "validate_fees_of_event".to_string(), - known_versions: vec![0, 1], + known_versions: vec![0], received: version, })), }; diff --git a/packages/rs-drive-abci/src/shielded_snapshot/mod.rs b/packages/rs-drive-abci/src/shielded_snapshot/mod.rs index e7b64a893b5..a75dc3f0ad3 100644 --- a/packages/rs-drive-abci/src/shielded_snapshot/mod.rs +++ b/packages/rs-drive-abci/src/shielded_snapshot/mod.rs @@ -322,11 +322,16 @@ pub fn dump_shielded_subtree( // 4. Compute `combined_root` for the header by reloading CommitmentTree // from the same storage. Apply side recomputes independently and // cross-validates — drift surfaces as CombinedRootMismatch. - let ct = CommitmentTree::<_, DashMemo>::open(total_count, chunk_power, storage_ctx) - .value - .map_err(|e| ShieldedSnapshotError::GroveDb(format!("CommitmentTree::open: {e}")))?; + let ct = CommitmentTree::<_, DashMemo>::open( + total_count, + chunk_power, + storage_ctx, + &platform_version.drive.grove_version, + ) + .value + .map_err(|e| ShieldedSnapshotError::GroveDb(format!("CommitmentTree::open: {e}")))?; let combined_root = ct - .compute_current_state_root() + .compute_current_state_root(&platform_version.drive.grove_version) .map_err(|e| ShieldedSnapshotError::GroveDb(format!("compute_current_state_root: {e}")))?; // The CommitmentTree owns the storage_ctx. We need to drop it to free @@ -559,14 +564,18 @@ pub fn apply_shielded_snapshot( .get_transactional_storage_context(subtree_path, None, tx_ref) .unwrap(); - let ct = - CommitmentTree::<_, DashMemo>::open(header.total_count, header.chunk_power, storage_ctx) - .value - .map_err(|e| { - ShieldedSnapshotError::GroveDb(format!("CommitmentTree::open after ingest: {e}")) - })?; + let ct = CommitmentTree::<_, DashMemo>::open( + header.total_count, + header.chunk_power, + storage_ctx, + &platform_version.drive.grove_version, + ) + .value + .map_err(|e| { + ShieldedSnapshotError::GroveDb(format!("CommitmentTree::open after ingest: {e}")) + })?; let recomputed = ct - .compute_current_state_root() + .compute_current_state_root(&platform_version.drive.grove_version) .map_err(|e| ShieldedSnapshotError::GroveDb(format!("compute_current_state_root: {e}")))?; drop(ct); diff --git a/packages/rs-drive/Cargo.toml b/packages/rs-drive/Cargo.toml index a976baf749d..54012ec1f1f 100644 --- a/packages/rs-drive/Cargo.toml +++ b/packages/rs-drive/Cargo.toml @@ -52,12 +52,12 @@ enum-map = { version = "2.0.3", optional = true } intmap = { version = "3.0.1", features = ["serde"], optional = true } chrono = { version = "0.4.35", optional = true } itertools = { version = "0.13", optional = true } -grovedb = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4", optional = true, default-features = false } -grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4", optional = true } -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4", optional = true } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4" } -grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4" } +grovedb = { git = "https://github.com/dashpay/grovedb", rev = "753a11f14c9a4bc72bf2d5302751dd43d174621e", optional = true, default-features = false } +grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "753a11f14c9a4bc72bf2d5302751dd43d174621e", optional = true } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "753a11f14c9a4bc72bf2d5302751dd43d174621e" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "753a11f14c9a4bc72bf2d5302751dd43d174621e", optional = true } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "753a11f14c9a4bc72bf2d5302751dd43d174621e" } +grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "753a11f14c9a4bc72bf2d5302751dd43d174621e" } [dev-dependencies] criterion = "0.5" diff --git a/packages/rs-platform-version/Cargo.toml b/packages/rs-platform-version/Cargo.toml index 7c44e773c31..4ee353c872e 100644 --- a/packages/rs-platform-version/Cargo.toml +++ b/packages/rs-platform-version/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT" thiserror = { version = "2.0.12" } bincode = { version = "=2.0.1" } versioned-feature-core = { git = "https://github.com/dashpay/versioned-feature-core", version = "1.0.0" } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4" } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "753a11f14c9a4bc72bf2d5302751dd43d174621e" } [features] mock-versions = [] diff --git a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/mod.rs b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/mod.rs index 189e3ccc7ab..08921d8bd9f 100644 --- a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/mod.rs +++ b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/mod.rs @@ -1,7 +1,6 @@ use versioned_feature_core::{FeatureVersion, OptionalFeatureVersion}; pub mod v1; -pub mod v10; pub mod v2; pub mod v3; pub mod v4; diff --git a/packages/rs-platform-version/src/version/v14.rs b/packages/rs-platform-version/src/version/v14.rs index 6e0f2d7d7e2..cfa1f2a0efc 100644 --- a/packages/rs-platform-version/src/version/v14.rs +++ b/packages/rs-platform-version/src/version/v14.rs @@ -15,7 +15,7 @@ use crate::version::dpp_versions::dpp_validation_versions::v5::DPP_VALIDATION_VE use crate::version::dpp_versions::dpp_voting_versions::v2::VOTING_VERSION_V2; use crate::version::dpp_versions::DPPVersion; use crate::version::drive_abci_versions::drive_abci_checkpoint_parameters::v1::DRIVE_ABCI_CHECKPOINT_PARAMETERS_V1; -use crate::version::drive_abci_versions::drive_abci_method_versions::v10::DRIVE_ABCI_METHOD_VERSIONS_V10; +use crate::version::drive_abci_versions::drive_abci_method_versions::v9::DRIVE_ABCI_METHOD_VERSIONS_V9; use crate::version::drive_abci_versions::drive_abci_query_versions::v3::DRIVE_ABCI_QUERY_VERSIONS_V3; use crate::version::drive_abci_versions::drive_abci_structure_versions::v1::DRIVE_ABCI_STRUCTURE_VERSIONS_V1; use crate::version::drive_abci_versions::drive_abci_validation_versions::v10::DRIVE_ABCI_VALIDATION_VERSIONS_V10; @@ -126,7 +126,7 @@ pub const PLATFORM_V14: PlatformVersion = PlatformVersion { drive: DRIVE_VERSION_V9, // changed: drive document method versions v4 — v2 index walkers (shared-prefix aggregate indexes become insertable) + the detect_ranked_mode slot drive_abci: DriveAbciVersion { structs: DRIVE_ABCI_STRUCTURE_VERSIONS_V1, - methods: DRIVE_ABCI_METHOD_VERSIONS_V10, // changed: validate_fees_of_event v1 — ShieldFromAssetLock admission no longer gated on a per-transition GroveDB cost estimate (the flat pool fee is epoch-amortized, like ShieldedTransfer) + methods: DRIVE_ABCI_METHOD_VERSIONS_V9, validation_and_processing: DRIVE_ABCI_VALIDATION_VERSIONS_V10, // changed: contested-index cross-check + refersTo document reference validation withdrawal_constants: DRIVE_ABCI_WITHDRAWAL_CONSTANTS_V2, query: DRIVE_ABCI_QUERY_VERSIONS_V3, // changed: ranked + boolean-HAVING routing gate diff --git a/packages/rs-platform-wallet/Cargo.toml b/packages/rs-platform-wallet/Cargo.toml index 8824f07e310..755f7bb110b 100644 --- a/packages/rs-platform-wallet/Cargo.toml +++ b/packages/rs-platform-wallet/Cargo.toml @@ -69,7 +69,7 @@ zeroize = "1" log = "0.4" # Shielded pool (optional, behind `shielded` feature) -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "753a11f14c9a4bc72bf2d5302751dd43d174621e", optional = true } # Direct `rusqlite` access so `FileBackedShieldedStore::open_path` can set # WAL + synchronous=NORMAL pragmas before handing the connection to # `ClientPersistentCommitmentTree`. Version locked to match the rev grovedb diff --git a/packages/rs-sdk/Cargo.toml b/packages/rs-sdk/Cargo.toml index 08ea60d7efa..ee1bfca14ae 100644 --- a/packages/rs-sdk/Cargo.toml +++ b/packages/rs-sdk/Cargo.toml @@ -18,7 +18,7 @@ drive = { path = "../rs-drive", default-features = false, features = [ ] } drive-proof-verifier = { path = "../rs-drive-proof-verifier", default-features = false } -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "f52adb641aedb8d8d417ec7fb48243e282eb75f4", features = [ +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "753a11f14c9a4bc72bf2d5302751dd43d174621e", features = [ "client", "sqlite", ], optional = true } From 26676739e79f04865b9efb3875378f9a34cebadd Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 24 Aug 2026 09:14:05 +0200 Subject: [PATCH 19/25] =?UTF-8?q?revert(drive-abci):=20restore=20the=20Shi?= =?UTF-8?q?eldFromAssetLock=20estimate=20gate=20=E2=80=94=20the=20fixed-co?= =?UTF-8?q?st=20estimator=20makes=20it=20satisfiable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts the validate_fees_of_event v1 / DRIVE_ABCI_METHOD_VERSIONS_V10 admission change. It existed because the previous pin's worst-case estimator (~397M credits per note append, a full epoch compaction charged on every note) made the v0 gate `flat pool fee >= estimate` unsatisfiable. Under the fixed per-append model the estimate equals the metered fee, the gate passes with the untouched flat fee, and uniform estimate-gated admission is the better invariant — so PV14 keeps V9 and the v0 validator. Measured at 494 notes: the transparent Shield's admission threshold and its metered fee now coincide at 179,978,640 credits (band width 0, estimate == actual; previously the bound was 929,279,120 against a much smaller actual). The PV13 halt-repro stays pinned to protocol 13, where the locked estimator leaves the band open; the band test's numbers and ignore reason are refreshed. Co-Authored-By: Claude Fable 5 --- .../validate_fees_of_event/v1/mod.rs | 140 ---------------- .../state_transitions/shield/tests.rs | 20 ++- .../drive_abci_method_versions/v10.rs | 157 ------------------ 3 files changed, 11 insertions(+), 306 deletions(-) delete mode 100644 packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/v1/mod.rs delete mode 100644 packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/v10.rs diff --git a/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/v1/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/v1/mod.rs deleted file mode 100644 index eee2e74fbdf..00000000000 --- a/packages/rs-drive-abci/src/execution/platform_events/state_transition_processing/validate_fees_of_event/v1/mod.rs +++ /dev/null @@ -1,140 +0,0 @@ -use crate::error::Error; -use crate::execution::types::execution_event::ExecutionEvent; -use crate::platform_types::platform::Platform; -use crate::rpc::core::CoreRPCLike; -use dpp::block::block_info::BlockInfo; -use dpp::fee::default_costs::CachedEpochIndexFeeVersions; -use dpp::fee::fee_result::FeeResult; -use dpp::prelude::ConsensusValidationResult; -use dpp::version::PlatformVersion; -use drive::grovedb::TransactionArg; - -impl Platform -where - C: CoreRPCLike, -{ - /// Version 1: identical to v0 except for `PaidFromAssetLockToPool` (the - /// `ShieldFromAssetLock` path), which is no longer gated on a per-transition GroveDB cost - /// estimate. - /// - /// The pool fee a `ShieldFromAssetLock` books is the flat - /// `compute_minimum_shielded_fee(num_actions) + asset-lock base cost` (its transform enforces - /// `lock_value >= shield_amount + pool_fee`), i.e. the same kind of flat fee a - /// `ShieldedTransfer`/`Unshield` carries — and those pool-paid events were never - /// estimate-gated. Under GROVE_V4 the commitment-tree estimator is a deliberate per-append - /// UPPER BOUND (a full epoch compaction and dense recompute charged on every note), so - /// `estimate >= actual` holds by construction and the v0 gate `fee >= estimate` degenerates - /// into a fixed comparison of two consensus constants — one the flat fee can never clear - /// without pricing every shield at the once-per-epoch compaction spike. The flat fee is - /// instead pinned against the epoch-AMORTIZED real cost (the - /// `test_minimum_shielded_fee_covers_actual_grovedb_write_cost` tests in rs-drive); the pool - /// absorbs the boundary append by design, and execution books - /// `storage = min(actual_storage, fee)`, `processing = fee - storage` whatever the estimate - /// would have said. - pub(super) fn validate_fees_of_event_v1( - &self, - event: &ExecutionEvent, - block_info: &BlockInfo, - transaction: TransactionArg, - platform_version: &PlatformVersion, - previous_fee_versions: &CachedEpochIndexFeeVersions, - ) -> Result, Error> { - match event { - ExecutionEvent::PaidFromAssetLockToPool { - fees_to_add_to_pool, - .. - } => { - // Advertise the authoritative pool fee for `gas_wanted`. Its storage/processing - // split is only known once the operations are metered at execution, so the whole - // fee is advertised as processing here; the total — what `gas_wanted` carries — - // is exact. - Ok(ConsensusValidationResult::new_with_data( - FeeResult::default_with_fees(0, *fees_to_add_to_pool), - )) - } - _ => self.validate_fees_of_event_v0( - event, - block_info, - transaction, - platform_version, - previous_fee_versions, - ), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::execution::types::execution_operation::ValidationOperation; - use crate::test::helpers::setup::TestPlatformBuilder; - use dpp::consensus::state::state_error::StateError; - use dpp::consensus::ConsensusError; - - /// Same event, same platform: a flat pool fee far below the estimated cost of the - /// transition's operations is rejected by v0 (`fee >= estimate`) and admitted by v1, which - /// still advertises the authoritative pool fee for `gas_wanted`. - #[test] - fn validate_fees_of_event_v1_paid_from_asset_lock_to_pool_is_not_estimate_gated() { - let platform = TestPlatformBuilder::new() - .with_latest_protocol_version() - .build_with_mock_rpc() - .set_initial_state_structure(); - - let platform_version = PlatformVersion::latest(); - let block_info = BlockInfo::default(); - let previous_fee_versions = Default::default(); - - let fees_to_add_to_pool = 1_000_000u64; - let estimated_cost = FeeResult::default_with_fees(0, 10 * fees_to_add_to_pool); - let event = ExecutionEvent::PaidFromAssetLockToPool { - fees_to_add_to_pool, - added_to_balance_outputs: None, - operations: vec![], - execution_operations: vec![ValidationOperation::PrecalculatedOperation(estimated_cost)], - }; - - let v0 = platform - .platform - .validate_fees_of_event_v0( - &event, - &block_info, - None, - platform_version, - &previous_fee_versions, - ) - .expect("v0 must be Ok"); - assert!( - matches!( - v0.errors.as_slice(), - [ConsensusError::StateError( - StateError::InvalidShieldedProofError(_) - )] - ), - "v0 gates the flat pool fee on the estimate, got {:?}", - v0.errors - ); - - let v1 = platform - .platform - .validate_fees_of_event_v1( - &event, - &block_info, - None, - platform_version, - &previous_fee_versions, - ) - .expect("v1 must be Ok"); - assert!( - v1.errors.is_empty(), - "v1 must not estimate-gate the flat pool fee, got {:?}", - v1.errors - ); - let fee = v1.into_data().expect("fee result present"); - assert_eq!( - fee.total_base_fee(), - fees_to_add_to_pool, - "advertised pool fee must equal fees_to_add_to_pool (gas_wanted parity)" - ); - } -} diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield/tests.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield/tests.rs index 7d68d49adea..21d209d6fe9 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield/tests.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield/tests.rs @@ -1911,12 +1911,14 @@ mod tests { /// Under protocol version 13 (GROVE_V3) the estimated-cost path skips the keyless /// commitment-tree append entirely (dashpay/grovedb#812, locked for replay), so the band /// is measurably open there (18,919,200 credits, 10.7% of the fee, at 494 notes). From - /// protocol version 14 (GROVE_V4) the estimator is a per-append upper bound and the band - /// is closed: measured at 494 notes, the first headroom that reaches execution and the - /// first that executes coincide at 929,279,120 credits (width 0). Kept out of routine CI - /// only for cost — ~60 fresh-platform runs at ~10 s each (the Orchard proof is built - /// once); run it by hand after any change to the shielded fee model or the grovedb pin. - #[ignore = "band closed at PV14 (width 0); ~60 fresh-platform runs, ~11 min — run by hand"] + /// protocol version 14 (GROVE_V4) the estimator prices the fixed per-append model + /// (grovedb #829/#830) and the band is closed EXACTLY: measured at 494 notes, the first + /// headroom that reaches execution and the first that executes coincide at 179,978,640 + /// credits (width 0 — the estimate IS the metered fee, so an address funds precisely + /// what it is charged). Kept out of routine CI only for cost — ~60 fresh-platform runs + /// (the Orchard proof is built once), ~4.5 min; run it by hand after any change to the + /// shielded fee model or the grovedb pin. + #[ignore = "band closed at PV14 (width 0, estimate == actual); ~60 fresh-platform runs, ~4.5 min — run by hand"] #[tokio::test] async fn shield_fee_estimate_and_actual_must_not_leave_a_halting_band() { let pv = PlatformVersion::latest(); @@ -1993,9 +1995,9 @@ mod tests { /// one whose estimator leaves the band open — GROVE_V3's estimation path skips the /// keyless commitment-tree append (dashpay/grovedb#812), locked there so historical /// admission decisions replay identically. From protocol version 14 (GROVE_V4) the - /// estimator is a per-append upper bound, so no funding level reaches execution - /// under-funded and the mid-band shield this test needs cannot be built (see - /// `shield_fee_estimate_and_actual_must_not_leave_a_halting_band`). + /// estimator prices the fixed per-append model exactly (grovedb #829/#830), so no + /// funding level reaches execution under-funded and the mid-band shield this test needs + /// cannot be built (see `shield_fee_estimate_and_actual_must_not_leave_a_halting_band`). #[tokio::test] async fn dropped_shield_must_not_mutate_state() { let pv = PlatformVersion::get(13).expect("protocol version 13 should exist"); diff --git a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/v10.rs b/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/v10.rs deleted file mode 100644 index a822d59d67d..00000000000 --- a/packages/rs-platform-version/src/version/drive_abci_versions/drive_abci_method_versions/v10.rs +++ /dev/null @@ -1,157 +0,0 @@ -use crate::version::drive_abci_versions::drive_abci_method_versions::{ - DriveAbciBlockEndMethodVersions, DriveAbciBlockFeeProcessingMethodVersions, - DriveAbciBlockStartMethodVersions, DriveAbciCoreBasedUpdatesMethodVersions, - DriveAbciCoreChainLockMethodVersionsAndConstants, DriveAbciCoreInstantSendLockMethodVersions, - DriveAbciEngineMethodVersions, DriveAbciEpochMethodVersions, - DriveAbciFeePoolInwardsDistributionMethodVersions, - DriveAbciFeePoolOutwardsDistributionMethodVersions, - DriveAbciIdentityCreditWithdrawalMethodVersions, DriveAbciInitializationMethodVersions, - DriveAbciMasternodeIdentitiesUpdatesMethodVersions, DriveAbciMethodVersions, - DriveAbciPlatformStateStorageMethodVersions, DriveAbciProtocolUpgradeMethodVersions, - DriveAbciStateTransitionProcessingMethodVersions, DriveAbciTokensProcessingMethodVersions, - DriveAbciVotingMethodVersions, -}; - -// Introduced in Protocol version 14. -// -// Identical to DRIVE_ABCI_METHOD_VERSIONS_V10 (the protocol-v13 method set) -// except ONE field: `validate_fees_of_event` 0 -> 1. The v1 drops the -// per-transition GroveDB cost-estimate gate from `PaidFromAssetLockToPool` -// (the `ShieldFromAssetLock` path). The pool fee that transition books is the -// flat `compute_minimum_shielded_fee(num_actions) + asset-lock base cost` — -// the same kind of flat, epoch-amortized fee a `ShieldedTransfer`/`Unshield` -// carries, and those pool-paid events were never estimate-gated. Under -// GROVE_V4 the commitment-tree estimator is a per-append UPPER BOUND (a full -// epoch compaction and dense recompute on every note), so the v0 gate -// `fee >= estimate` degenerated into a fixed comparison of two consensus -// constants that rejected every ShieldFromAssetLock. Everything else — -// `PaidFromAddressInputs` (the transparent `Shield`) included — is unchanged. -// Admission changes, so the bump MUST stay inactive on v13 (V9 keeps it 0). -pub const DRIVE_ABCI_METHOD_VERSIONS_V10: DriveAbciMethodVersions = DriveAbciMethodVersions { - engine: DriveAbciEngineMethodVersions { - init_chain: 0, - check_tx: 0, - run_block_proposal: 0, - finalize_block_proposal: 0, - consensus_params_update: 1, - }, - initialization: DriveAbciInitializationMethodVersions { - initial_core_height_and_time: 0, - create_genesis_state: 1, - }, - core_based_updates: DriveAbciCoreBasedUpdatesMethodVersions { - update_core_info: 0, - update_masternode_list: 0, - update_quorum_info: 0, - masternode_updates: DriveAbciMasternodeIdentitiesUpdatesMethodVersions { - get_voter_identity_key: 0, - get_operator_identity_keys: 0, - get_owner_identity_withdrawal_key: 0, - get_owner_identity_owner_key: 0, - get_voter_identifier_from_masternode_list_item: 0, - get_operator_identifier_from_masternode_list_item: 0, - create_operator_identity: 0, - create_owner_identity: 1, - create_voter_identity: 0, - disable_identity_keys: 0, - update_masternode_identities: 0, - update_operator_identity: 0, - update_owner_withdrawal_address: 1, - update_voter_identity: 0, - }, - }, - protocol_upgrade: DriveAbciProtocolUpgradeMethodVersions { - check_for_desired_protocol_upgrade: 1, - upgrade_protocol_version_on_epoch_change: 0, - perform_events_on_first_block_of_protocol_change: Some(1), - protocol_version_upgrade_percentage_needed: 67, - }, - block_fee_processing: DriveAbciBlockFeeProcessingMethodVersions { - add_process_epoch_change_operations: 0, - process_block_fees_and_validate_sum_trees: 1, - }, - tokens_processing: DriveAbciTokensProcessingMethodVersions { - validate_token_aggregated_balance: 0, - }, - core_chain_lock: DriveAbciCoreChainLockMethodVersionsAndConstants { - choose_quorum: 0, - verify_chain_lock: 0, - verify_chain_lock_locally: 0, - verify_chain_lock_through_core: 0, - make_sure_core_is_synced_to_chain_lock: 0, - recent_block_count_amount: 2, - }, - core_instant_send_lock: DriveAbciCoreInstantSendLockMethodVersions { - verify_recent_signature_locally: 0, - }, - fee_pool_inwards_distribution: DriveAbciFeePoolInwardsDistributionMethodVersions { - add_distribute_block_fees_into_pools_operations: 0, - add_distribute_storage_fee_to_epochs_operations: 0, - }, - fee_pool_outwards_distribution: DriveAbciFeePoolOutwardsDistributionMethodVersions { - add_distribute_fees_from_oldest_unpaid_epoch_pool_to_proposers_operations: 1, - add_epoch_pool_to_proposers_payout_operations: 0, - find_oldest_epoch_needing_payment: 0, - fetch_reward_shares_list_for_masternode: 0, - }, - withdrawals: DriveAbciIdentityCreditWithdrawalMethodVersions { - build_untied_withdrawal_transactions_from_documents: 0, - dequeue_and_build_unsigned_withdrawal_transactions: 0, - fetch_transactions_block_inclusion_status: 0, - pool_withdrawals_into_transactions_queue: 1, - update_broadcasted_withdrawal_statuses: 0, - rebroadcast_expired_withdrawal_documents: 1, - append_signatures_and_broadcast_withdrawal_transactions: 0, - cleanup_expired_locks_of_withdrawal_amounts: 0, - }, - voting: DriveAbciVotingMethodVersions { - keep_record_of_finished_contested_resource_vote_poll: 0, - clean_up_after_vote_poll_end: 0, - clean_up_after_contested_resources_vote_poll_end: 1, - check_for_ended_vote_polls: 0, - tally_votes_for_contested_document_resource_vote_poll: 0, - award_document_to_winner: 0, - delay_vote_poll: 0, - run_dao_platform_events: 0, - remove_votes_for_removed_masternodes: 0, - }, - state_transition_processing: DriveAbciStateTransitionProcessingMethodVersions { - execute_event: 0, - // Unchanged: the outer processing loop did not change at v13, so it stays at v0. Only the - // `process_validation_result` helper it dispatches to changed (below). - process_raw_state_transitions: 0, - // changed: v1 records the balance effects of paid-INVALID / unsuccessful-paid transitions - // (charged fees, adjusted outputs, applied chargeable-failure credits) that v0 dropped. Same - // v13 recorded-set expansion as `record_added_balance_outputs` below; kept in a real _v1 so - // no version conditional lives inside the _v0 helper. - process_validation_result: 1, - decode_raw_state_transitions: 0, - validate_fees_of_event: 1, - store_address_balances_to_recent_block_storage: Some(0), - cleanup_recent_block_storage_address_balances: Some(0), - // changed: v1 records shielded-spend transparent credits (Unshield net output, - // ShieldFromAssetLock surplus, identity-create fallback) folded at the executor. The storage - // method above is unchanged — only which credits feed its map did. - record_added_balance_outputs: 1, - }, - epoch: DriveAbciEpochMethodVersions { - gather_epoch_info: 0, - get_genesis_time: 0, - }, - block_start: DriveAbciBlockStartMethodVersions { - clear_drive_block_cache: 0, - }, - block_end: DriveAbciBlockEndMethodVersions { - update_state_cache: 0, - update_drive_cache: 0, - validator_set_update: 2, - should_checkpoint: Some(0), - update_checkpoints: Some(0), - record_shielded_pool_anchor: Some(0), - prune_shielded_pool_anchors: Some(0), - }, - platform_state_storage: DriveAbciPlatformStateStorageMethodVersions { - fetch_platform_state: 0, - store_platform_state: 0, - }, -}; From 8835e1bd7abb21a991dbf12bd019f3823cef0293 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 24 Aug 2026 09:14:06 +0200 Subject: [PATCH 20/25] test(drive): pin ranked-index update fee estimation against the canonical-reference rows grovedb #817 made every axis secondary row a canonical reference bound to the primary node's committed value hash, so ANY update of a document under a ranked index refreshes every configured axis row (in place for a payload change, delete+insert for a group move). New test drives both update shapes through estimated (apply=false) and applied runs per axis family (PCPSIT/PCIT/PSIT) and requires the estimate to never undercharge either fee component, then runs the canonical-row integrity walk so no stale row survives the refresh paths. Also refreshes the shielded fee-floor doc comments: under the GROVE_V4 fixed per-append model the epoch-boundary compaction meters the same as every other append, so the measured epoch average IS the per-append cost; the tests keep measuring the full epoch so the floor stays honest under any model. Co-Authored-By: Claude Fable 5 --- .../v0/tests/ranked_index_e2e_tests.rs | 130 ++++++++++++++++++ .../shielded/mod.rs | 15 +- .../shielded/shielded_transfer_transition.rs | 16 +-- 3 files changed, 147 insertions(+), 14 deletions(-) diff --git a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs index b31234f8512..390621a9886 100644 --- a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs +++ b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/ranked_index_e2e_tests.rs @@ -1295,6 +1295,136 @@ fn estimated_mode_insert_on_ranked_indexes_produces_fees_and_does_not_undercharg } } +/// Run one document update twice — estimated (`apply: false`) then applied — +/// and return `(estimated, actual)` fees. The document must already be +/// inserted; the estimate runs against the same committed state the applied +/// run will see. +fn estimated_and_actual_update_fees( + drive: &Drive, + contract: &DataContract, + document_type_name: &str, + updated: &Document, +) -> ( + dpp::fee::fee_result::FeeResult, + dpp::fee::fee_result::FeeResult, +) { + use std::borrow::Cow; + + let pv = platform_version(); + let document_type = contract + .document_type_for_name(document_type_name) + .unwrap_or_else(|_| panic!("{document_type_name} doctype exists")); + let storage_flags = Some(Cow::Owned(StorageFlags::SingleEpoch(0))); + + let run = |apply: bool| { + drive + .update_document_for_contract( + updated, + contract, + document_type, + None, + BlockInfo::default(), + apply, + storage_flags.clone(), + None, + pv, + None, + ) + .unwrap_or_else(|e| { + panic!("expected the {document_type_name} update (apply={apply}) to succeed: {e}") + }) + }; + + let estimated = run(false); + let actual = run(true); + (estimated, actual) +} + +/// Updates under a ranked index are write-amplified by design: an axis row +/// binds the immediate primary node's committed value hash (grovedb #817), so +/// ANY update of an indexed document refreshes every configured axis row — +/// an in-place payload change rewrites the rows where they stand, and a +/// group move deletes the old row and inserts a new one. Both must be priced +/// by the dry-run estimate at least as high as the applied run meters, or the +/// fee gate admits transitions that execution rejects. Both update shapes are +/// exercised per axis family, and the canonical-row integrity walk must be +/// clean afterwards (no stale rows left by the refresh paths). +#[test] +fn estimated_mode_update_on_ranked_indexes_does_not_undercharge() { + for (document_type_name, aggregated_property) in [ + ("review", "grade"), // PCPSIT, Avg axis + ("visit", "guests"), // PCIT, Count axis + ("tip", "amount"), // PSIT, Sum axis + ] { + let (drive, contract) = setup_restaurants(); + let doc = build_doc( + &contract, + document_type_name, + aggregated_property, + "alpha", + 42, + 7, + ); + insert_doc(&drive, &contract, document_type_name, &doc); + + // Phase 1: in-place update — same group, new aggregated payload. + let mut in_place = doc.clone(); + let mut props = std::collections::BTreeMap::new(); + props.insert(GROUP_PROPERTY.to_string(), Value::Text("alpha".to_string())); + props.insert(aggregated_property.to_string(), Value::I64(43)); + in_place.set_properties(props); + let (estimated, actual) = + estimated_and_actual_update_fees(&drive, &contract, document_type_name, &in_place); + for (kind, est, act) in [ + ("storage", estimated.storage_fee, actual.storage_fee), + ( + "processing", + estimated.processing_fee, + actual.processing_fee, + ), + ] { + assert!( + est >= act, + "{document_type_name}: in-place update estimated {kind} fee {est} is BELOW the \ + applied {kind} fee {act} (short by {}) — the axis row refresh under-charges", + act.saturating_sub(est), + ); + } + assert!( + actual.processing_fee > 0, + "{document_type_name}: the applied in-place update must meter work" + ); + + // Phase 2: group move — the sort key changes, so the old row is + // deleted and a new one inserted under the new prefix. + let mut moved = doc.clone(); + let mut props = std::collections::BTreeMap::new(); + props.insert(GROUP_PROPERTY.to_string(), Value::Text("beta".to_string())); + props.insert(aggregated_property.to_string(), Value::I64(43)); + moved.set_properties(props); + let (estimated, actual) = + estimated_and_actual_update_fees(&drive, &contract, document_type_name, &moved); + for (kind, est, act) in [ + ("storage", estimated.storage_fee, actual.storage_fee), + ( + "processing", + estimated.processing_fee, + actual.processing_fee, + ), + ] { + assert!( + est >= act, + "{document_type_name}: group-move update estimated {kind} fee {est} is BELOW \ + the applied {kind} fee {act} (short by {}) — the row move under-charges", + act.saturating_sub(est), + ); + } + + // No stale or non-canonical rows may survive either refresh path. + assert_grovedb_is_consistent(&drive); + } +} + /// The PSIT arm: a sum-only ranked index ranks its groups by running sum. #[test] fn sum_axis_ranks_groups_by_running_sum() { diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/mod.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/mod.rs index 1b4ae003b37..0a0ac596835 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/mod.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/mod.rs @@ -57,12 +57,15 @@ pub(super) fn update_balance<'a>(ops: &mut Vec>, new_total_ba /// storage_fee`; they never validate affordability against a per-transition /// estimate. The invariant such a fee has to satisfy is therefore an /// **amortized** one: over a whole commitment-tree epoch — including the one -/// append per epoch that compacts the dense buffer into a chunk blob, by far -/// the most expensive append — the flat fee must cover the average real -/// write cost, and must stay above the average real storage so the booking -/// split never starves the proposer. The compaction append is the pool's to -/// absorb: the other `epoch - 1` appends overpay it by design, and a client -/// cannot choose to land on it more often than once per epoch. +/// append per epoch that compacts the dense buffer into a chunk blob — the +/// flat fee must cover the average real write cost, and must stay above the +/// average real storage so the booking split never starves the proposer. +/// Under the GROVE_V4 fixed per-append model (grovedb #829/#830) the +/// compaction is amortized inside GroveDB itself, so the epoch average and +/// the boundary append coincide; measuring the full epoch keeps the +/// invariant honest under any model — whatever a boundary append meters, +/// the pool absorbs it, and a client cannot land on it more than once per +/// epoch. /// /// Measured once per test binary (an epoch is 2048 real appends). #[cfg(test)] diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/shielded_transfer_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/shielded_transfer_transition.rs index 2a595863aef..60519a7da9b 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/shielded_transfer_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/shielded_transfer_transition.rs @@ -158,14 +158,14 @@ mod tests { /// (`storage_fee = min(actual_storage, flat)`, `processing = flat - /// storage_fee`) never starves the proposer. /// - /// Amortized, because the fee is flat and pool-paid: the note appends that - /// compact a full dense-buffer epoch into a chunk blob cost far more than - /// the others (the epoch's bytes are rewritten as replaced storage, at the - /// processing rate), and the pool absorbs that by design — the other - /// `epoch - 1` appends overpay it, and a client cannot land on the - /// compaction more than once per epoch. The per-append worst-case - /// *estimate* is therefore not the floor for a pool-paid fee; the - /// measured epoch average is. Measured on a real pool, one epoch of real + /// Amortized, because the fee is flat and pool-paid: whatever the + /// epoch-boundary append that compacts the dense buffer into a chunk + /// blob meters, the pool absorbs it by design, and a client cannot land + /// on it more than once per epoch. Under the GROVE_V4 fixed per-append + /// model (grovedb #829/#830) every append — the compacting one included — + /// meters the same figure, so the epoch average IS the per-append cost; + /// the measurement spans a full epoch anyway so the floor stays honest + /// under any cost model. Measured on a real pool, one epoch of real /// appends including the compacting one (see `fee_floor_support`). /// /// The booking split is checked at its worst point too: even the From e0f2789b24d1dd4db5ca6b31a6d985308f273ead Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 24 Aug 2026 09:17:21 +0200 Subject: [PATCH 21/25] test(drive): round the amortized fee-floor averages upward A truncated average could let a flat fee marginally below the exact epoch-wide cost pass the floor assertions; ceil keeps the floor exact. Co-Authored-By: Claude Fable 5 --- .../action_convert_to_operations/shielded/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/mod.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/mod.rs index 0a0ac596835..2f76727173c 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/mod.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/mod.rs @@ -174,8 +174,11 @@ pub(super) mod fee_floor_support { } TransferEpoch { appends, - avg_total: total / appends, - avg_storage: storage / appends, + // Ceiling division: a truncated average could let a flat fee + // marginally below the exact epoch-wide cost slip past the + // floor assertions. + avg_total: total.div_ceil(appends), + avg_storage: storage.div_ceil(appends), boundary_storage, boundary_total, ordinary_total, From 423268a46fb5d3438e32626c338cefe29a279b68 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 24 Aug 2026 11:42:07 +0200 Subject: [PATCH 22/25] docs(drive): describe OFFSET skip cost as depth-bounded, not free MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The counted descent reads one aggregate per skipped subtree: work bounded by tree depth, independent of the offset — bounded, not zero. Fixes the validation error message and three comments that said otherwise. Co-Authored-By: Claude Fable 5 --- .../rs-drive-abci/src/query/document_query/v1/routing.rs | 8 ++++---- .../rs-drive/src/query/drive_document_ranked_query/mod.rs | 3 ++- .../drive_document_ranked_query/mode_detection/v0/mod.rs | 3 ++- .../src/query/drive_document_ranked_query/tests.rs | 3 ++- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/rs-drive-abci/src/query/document_query/v1/routing.rs b/packages/rs-drive-abci/src/query/document_query/v1/routing.rs index bc354204f96..f686ffb47f4 100644 --- a/packages/rs-drive-abci/src/query/document_query/v1/routing.rs +++ b/packages/rs-drive-abci/src/query/document_query/v1/routing.rs @@ -279,10 +279,10 @@ pub(super) fn validate_and_route( /// The `OFFSET` gate, applied **after** routing. /// /// Offset pagination exists on exactly one path: the ranked executor, -/// where `OFFSET m` is the rank the returned page starts at and costs -/// nothing to skip on either `prove` setting: grovedb counts the -/// skipped region from the subtree aggregates rather than walking it, -/// and both executors take that descent. Only the proved result +/// where `OFFSET m` is the rank the returned page starts at; skipping +/// is a counted tree descent on either `prove` setting — grovedb counts +/// the skipped region from the subtree aggregates rather than walking +/// it, so the work is bounded by tree depth and does not grow with `m`. Only the proved result /// additionally *attests* the count. Every other v1 shape — /// documents, and the grouped count / sum / average modes — has no /// offset primitive behind it and keeps the rejection it has always diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/mod.rs b/packages/rs-drive/src/query/drive_document_ranked_query/mod.rs index a78aa8b9641..a72ccd7b7f4 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/mod.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/mod.rs @@ -52,7 +52,8 @@ //! by aggregate, not by group key — and is rejected rather than //! silently ignored, as is any non-equality prefix clause (`IN` //! included: one walk per element is a future multi-`IN` capability). -//! 2. **`limit` is mandatory, `offset` is free, `start_at` is refused.** +//! 2. **`limit` is mandatory, `offset` is depth-bounded, `start_at` is +//! refused.** //! `limit` is the `k` of the walk and the ranked surface has no //! server default for it, so it must be supplied. `offset` is the //! rank the page starts at and is unbounded above: grovedb counts diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/mode_detection/v0/mod.rs b/packages/rs-drive/src/query/drive_document_ranked_query/mode_detection/v0/mod.rs index 37d87274e45..abe883991e6 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/mode_detection/v0/mod.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/mode_detection/v0/mod.rs @@ -291,7 +291,8 @@ pub fn detect_ranked_mode_v0( proof commits one secondary entry per returned group, so its size grows \ linearly in the limit. Narrow the request — the ceiling is a hard limit, not \ a clamp, because `k` is echoed in the proof envelope and re-checked by the \ - verifier. Deep results are reached with `OFFSET`, which costs nothing." + verifier. Deep results are reached with `OFFSET`, whose skip work is bounded by \ + tree depth and does not grow with the offset." )))); } // Bounded by MAX_RANKED_LIMIT (a u16) immediately above. diff --git a/packages/rs-drive/src/query/drive_document_ranked_query/tests.rs b/packages/rs-drive/src/query/drive_document_ranked_query/tests.rs index 0c79345d816..0052ab8388e 100644 --- a/packages/rs-drive/src/query/drive_document_ranked_query/tests.rs +++ b/packages/rs-drive/src/query/drive_document_ranked_query/tests.rs @@ -160,7 +160,8 @@ fn offset_is_optional_defaults_to_zero_and_is_uncapped() { assert!(fifth_best.descending); // Far past any plausible population, and far past MAX_RANKED_LIMIT: - // still accepted, because offset costs nothing to prove. + // still accepted, because the skip is counted from subtree + // aggregates — work bounded by tree depth, not by the offset. let deep = detect_avg(false, Some(10), Some(u32::MAX)).expect("a huge OFFSET is well-formed"); assert_eq!(deep.offset, u32::MAX); assert_eq!(deep.k, 10); From 6eaf5ce0b0f2192130cc3089adaf0d19e672335c Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 24 Aug 2026 14:12:21 +0200 Subject: [PATCH 23/25] test(drive-abci): run the shield halting-band check in CI as a single-edge probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the fixed per-append estimator the band is closed exactly, and this is the tripwire that catches any constants or pin change reopening it — worth CI time. Since execution's metered cost is a fixed function of state, "the least headroom validation accepts must execute" is equivalent to band emptiness at every funding level, so one binary search plus one execution probe (~30 fresh-platform runs, the Orchard proof built once) replaces the two-edge search at half the cost. Co-Authored-By: Claude Fable 5 --- .../state_transitions/shield/tests.rs | 87 ++++++++----------- 1 file changed, 35 insertions(+), 52 deletions(-) diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield/tests.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield/tests.rs index 21d209d6fe9..609b5573d4d 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield/tests.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield/tests.rs @@ -1896,29 +1896,26 @@ mod tests { } } - /// Binary-search both edges of the funding range to measure the band where - /// `validate_fees_of_event` accepts a shield that execution then rejects. + /// Measure the lower edge of the funding range — the least headroom + /// `validate_fees_of_event` accepts — and require that exact headroom to EXECUTE. /// - /// * lower edge = the ESTIMATED fee (below it, validation rejects cleanly) - /// * upper edge = the ACTUAL metered fee (at or above it, the shield executes) - /// - /// If the two cost models agreed, the edges would coincide and the band would be empty. - /// Any width is a range of funding levels where the transition is accepted by validation + /// If the two cost models agreed everywhere, the least accepted headroom would execute; + /// any gap is a band of funding levels where the transition is accepted by validation /// and then dropped at execution — no longer a chain halt since the proposer-side /// per-transition rollback (shipped in 4.1.1), but still a transition that can never - /// confirm despite paying the quoted fee. + /// confirm despite paying the quoted fee. Because execution's metered cost is a fixed + /// function of the state, "the least accepted headroom executes" is equivalent to the + /// band being empty at every funding level, so one edge search plus one execution probe + /// replaces the old two-edge search at half the cost. /// /// Under protocol version 13 (GROVE_V3) the estimated-cost path skips the keyless /// commitment-tree append entirely (dashpay/grovedb#812, locked for replay), so the band /// is measurably open there (18,919,200 credits, 10.7% of the fee, at 494 notes). From /// protocol version 14 (GROVE_V4) the estimator prices the fixed per-append model - /// (grovedb #829/#830) and the band is closed EXACTLY: measured at 494 notes, the first - /// headroom that reaches execution and the first that executes coincide at 179,978,640 - /// credits (width 0 — the estimate IS the metered fee, so an address funds precisely - /// what it is charged). Kept out of routine CI only for cost — ~60 fresh-platform runs - /// (the Orchard proof is built once), ~4.5 min; run it by hand after any change to the - /// shielded fee model or the grovedb pin. - #[ignore = "band closed at PV14 (width 0, estimate == actual); ~60 fresh-platform runs, ~4.5 min — run by hand"] + /// (grovedb #829/#830) and the band is closed EXACTLY: measured at 494 notes, the least + /// accepted headroom and the metered fee coincide (the estimate IS the metered fee, so + /// an address funds precisely what it is charged). ~30 fresh-platform runs; the Orchard + /// proof is built once. #[tokio::test] async fn shield_fee_estimate_and_actual_must_not_leave_a_halting_band() { let pv = PlatformVersion::latest(); @@ -1932,54 +1929,40 @@ mod tests { "sanity: the upper bound must comfortably fund the shield ({top_msg})" ); - // Upper edge: least headroom that actually executes == the ACTUAL metered fee. + // Least headroom that validation lets through to execution: below it the outcome + // is a clean NotEnoughFunds (or a structural-minimum rejection further down). let (mut lo, mut hi) = (0u64, CEILING); while lo + 1 < hi { let mid = lo + (hi - lo) / 2; - if run_at(mid, &b, pv).await.0 == Outcome::Success { - hi = mid; - } else { + if run_at(mid, &b, pv).await.0 == Outcome::NotEnoughFunds { lo = mid; - } - } - let actual_fee = hi; - - // Lower edge: below the flat structural minimum shielded fee the transition is - // rejected in BASIC validation (`ShieldedInvalidValueBalanceError`) before any write, - // which is safe. The dangerous band starts where that gate stops rejecting. - let (mut lo2, mut hi2) = (0u64, actual_fee); - while lo2 + 1 < hi2 { - let mid = lo2 + (hi2 - lo2) / 2; - if run_at(mid, &b, pv).await.0 == Outcome::Internal { - hi2 = mid; } else { - lo2 = mid; + hi = mid; } } - let band_start = hi2; + let admission = hi; - let (edge_outcome, edge_msg) = run_at(band_start - 1, &b, pv).await; + let (at_admission, at_msg) = run_at(admission, &b, pv).await; + let (below, below_msg) = run_at(admission - 1, &b, pv).await; - println!("shield_amount = {}", b.shield_amount); - println!("band start = {band_start} (first headroom that reaches execution)"); - println!("actual fee = {actual_fee} (execution, apply=true)"); - println!("just below band = {edge_outcome:?} :: {edge_msg}"); - println!( - "HALTING BAND = [{band_start}, {actual_fee}) width = {} credits ({:.1}% of the fee)", - actual_fee - band_start, - 100.0 * (actual_fee - band_start) as f64 / actual_fee as f64 - ); + println!("shield_amount = {}", b.shield_amount); + println!("admission headroom = {admission}"); + println!("at admission = {at_admission:?} :: {at_msg}"); + println!("just below = {below:?} :: {below_msg}"); assert_eq!( - band_start, actual_fee, - "HALTING BAND: any shield whose fee headroom falls in [{band_start}, {actual_fee}) \ - clears both the structural minimum-fee gate and validate_fees_of_event (which \ - prices the batch with the synthetic apply=false cost model, and which SKIPS the \ - keyless commitment-tree append entirely), and is then REJECTED by \ - paid_from_address_inputs_and_outputs on the real apply=true cost — after its drive \ - operations were already written to the block transaction. Such a transition is \ - stripped from the block as TxAction::Removed while its writes remain in the \ - proposer's app hash, so no validator can reproduce that hash." + below, + Outcome::NotEnoughFunds, + "just below the admission threshold must be a clean validation rejection \ + ({below_msg})" + ); + assert_eq!( + at_admission, + Outcome::Success, + "HALTING BAND: the least headroom validation accepts ({admission}) must \ + execute. A different outcome means validation under-quotes execution's real \ + cost, and every funding level in the gap is accepted by validation and then \ + dropped at execution — the mainnet-halt band. Got {at_admission:?}: {at_msg}" ); } From bdbe222b265071078a9ccdd3df1bb288a5b1bb37 Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 24 Aug 2026 15:57:44 +0200 Subject: [PATCH 24/25] test(drive): assert the shielded fee components independently MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the protocol-14 rebalance (#4467) in, the fee floors tighten from "the whole flat fee covers the metered cost" to component independence: the per-action storage allowance alone must cover the amortized real storage per append, and the per-action processing fee alone must cover the amortized metered GroveDB processing — the proof-verification fee is reserved for Halo 2 CPU and takes part in no database assertion. At the current constants and pin: 15.07M >= 14.34M storage and 22M >= 3.55M processing, with the compute budget untouched. Co-Authored-By: Claude Fable 5 --- .../shielded/shielded_transfer_transition.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/shielded_transfer_transition.rs b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/shielded_transfer_transition.rs index 60519a7da9b..3db03db4424 100644 --- a/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/shielded_transfer_transition.rs +++ b/packages/rs-drive/src/state_transition_action/action_convert_to_operations/shielded/shielded_transfer_transition.rs @@ -211,5 +211,33 @@ mod tests { ({fee_one}), or `min(actual_storage, flat)` would zero the proposer's share", epoch.boundary_storage ); + + // Component independence: the fee's resource reservations must each + // cover their own cost, with nothing subsidized by the compute + // budget. The proof-verification fee is reserved for Halo 2 CPU and + // takes part in NO database assertion here. + let constants = &platform_version + .drive_abci + .validation_and_processing + .event_constants; + let storage = &platform_version.fee_version.storage; + let per_byte_rate = + storage.storage_disk_usage_credit_per_byte + storage.storage_processing_credit_per_byte; + let storage_component = constants.shielded_storage_bytes_per_action * per_byte_rate; + assert!( + storage_component >= epoch.avg_storage, + "the per-action storage allowance alone ({} bytes = {storage_component} credits) \ + must cover the amortized real storage per append ({}); raise \ + `shielded_storage_bytes_per_action`", + constants.shielded_storage_bytes_per_action, + epoch.avg_storage + ); + assert!( + constants.shielded_per_action_processing_fee >= epoch.avg_total - epoch.avg_storage, + "the per-action processing fee ({}) must cover the amortized metered GroveDB \ + processing per append ({}) on top of its verification work", + constants.shielded_per_action_processing_fee, + epoch.avg_total - epoch.avg_storage + ); } } From fbae91ad1f71e70ad7c6e6cd49c5724b9eddb91a Mon Sep 17 00:00:00 2001 From: Quantum Explorer Date: Mon, 24 Aug 2026 16:12:51 +0200 Subject: [PATCH 25/25] fix(drive-abci): search the halting-band probe on the one monotone predicate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The single-edge probe binary-searched on NotEnoughFunds, but below the band the rejection class varies with whichever gate fires (input minimum, structural fee minimum, metered affordability), so the search could converge on a class boundary instead of the admission threshold — under the protocol-14 rebalance it collapsed to headroom 1. Search on Success (monotone across the whole range) and assert the point one credit below is a validation rejection rather than the InternalError drop — the mid-band outcome — which certifies the band is empty. Measured at the rebalanced constants: least executing headroom 119,978,640 (exactly 60M below the pre-#4467 figure — the proof-fee cut), just below is a clean AddressesNotEnoughFunds; 75 s. Co-Authored-By: Claude Fable 5 --- .../state_transitions/shield/tests.rs | 41 +++++++++++-------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield/tests.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield/tests.rs index d282f5fa0dc..d0b03339f40 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield/tests.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shield/tests.rs @@ -1929,40 +1929,45 @@ mod tests { "sanity: the upper bound must comfortably fund the shield ({top_msg})" ); - // Least headroom that validation lets through to execution: below it the outcome - // is a clean NotEnoughFunds (or a structural-minimum rejection further down). + // Least headroom that EXECUTES. Success is the one monotone + // predicate over the whole funding range — below it the outcome + // is some rejection whose error class varies with the gate that + // fires (the input-minimum gate at tiny headroom, the structural + // fee minimum, then the metered-affordability rejection), so + // searching on any single rejection class would not converge. let (mut lo, mut hi) = (0u64, CEILING); while lo + 1 < hi { let mid = lo + (hi - lo) / 2; - if run_at(mid, &b, pv).await.0 == Outcome::NotEnoughFunds { - lo = mid; - } else { + if run_at(mid, &b, pv).await.0 == Outcome::Success { hi = mid; + } else { + lo = mid; } } let admission = hi; - let (at_admission, at_msg) = run_at(admission, &b, pv).await; let (below, below_msg) = run_at(admission - 1, &b, pv).await; println!("shield_amount = {}", b.shield_amount); - println!("admission headroom = {admission}"); - println!("at admission = {at_admission:?} :: {at_msg}"); + println!("least executing = {admission}"); println!("just below = {below:?} :: {below_msg}"); - assert_eq!( + // One credit below the least headroom that executes must be a + // VALIDATION rejection. If it is instead the InternalError drop, + // validation accepted a transition execution then rejected — the + // mainnet-halt band is open at every funding level in the gap + // between the two thresholds. + assert_ne!( below, - Outcome::NotEnoughFunds, - "just below the admission threshold must be a clean validation rejection \ - ({below_msg})" + Outcome::Internal, + "HALTING BAND: headroom {} is accepted by validation and dropped at \ + execution ({below_msg})", + admission - 1 ); - assert_eq!( - at_admission, + assert_ne!( + below, Outcome::Success, - "HALTING BAND: the least headroom validation accepts ({admission}) must \ - execute. A different outcome means validation under-quotes execution's real \ - cost, and every funding level in the gap is accepted by validation and then \ - dropped at execution — the mainnet-halt band. Got {at_admission:?}: {at_msg}" + "binary search must have found the least executing headroom" ); }