diff --git a/Cargo.lock b/Cargo.lock index d5813be584a..d16a76044ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -513,7 +513,7 @@ dependencies = [ "bitflags 2.11.0", "cexpr", "clang-sys", - "itertools 0.13.0", + "itertools 0.10.5", "proc-macro2", "quote", "regex", @@ -1138,7 +1138,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]] @@ -1264,21 +1264,18 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "core2" -version = "0.3.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239fa3ae9b63c2dc74bd3fa852d4792b8b305ae64eeede946265b6af62f1fff3" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" dependencies = [ "memchr", ] [[package]] -name = "core2" -version = "0.4.0" +name = "corez" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] +checksum = "4df6f98652d30167eaeea34d77b730e07c8caba6df17bd4551842b9b8da01deb" [[package]] name = "cpufeatures" @@ -2297,7 +2294,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]] @@ -2717,7 +2714,7 @@ dependencies = [ [[package]] name = "grovedb" version = "4.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=8f25b20d04bfc0e8bdfb3870676d647a0d74918b#8f25b20d04bfc0e8bdfb3870676d647a0d74918b" +source = "git+https://github.com/dashpay/grovedb?rev=dbd83dce59fbcf2866e9dd06be4ce6c320e37908#dbd83dce59fbcf2866e9dd06be4ce6c320e37908" dependencies = [ "axum 0.8.8", "bincode", @@ -2755,7 +2752,7 @@ dependencies = [ [[package]] name = "grovedb-bulk-append-tree" version = "4.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=8f25b20d04bfc0e8bdfb3870676d647a0d74918b#8f25b20d04bfc0e8bdfb3870676d647a0d74918b" +source = "git+https://github.com/dashpay/grovedb?rev=dbd83dce59fbcf2866e9dd06be4ce6c320e37908#dbd83dce59fbcf2866e9dd06be4ce6c320e37908" dependencies = [ "bincode", "blake3", @@ -2771,7 +2768,7 @@ dependencies = [ [[package]] name = "grovedb-commitment-tree" version = "4.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=8f25b20d04bfc0e8bdfb3870676d647a0d74918b#8f25b20d04bfc0e8bdfb3870676d647a0d74918b" +source = "git+https://github.com/dashpay/grovedb?rev=dbd83dce59fbcf2866e9dd06be4ce6c320e37908#dbd83dce59fbcf2866e9dd06be4ce6c320e37908" dependencies = [ "blake3", "grovedb-bulk-append-tree", @@ -2787,7 +2784,7 @@ dependencies = [ [[package]] name = "grovedb-costs" version = "4.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=8f25b20d04bfc0e8bdfb3870676d647a0d74918b#8f25b20d04bfc0e8bdfb3870676d647a0d74918b" +source = "git+https://github.com/dashpay/grovedb?rev=dbd83dce59fbcf2866e9dd06be4ce6c320e37908#dbd83dce59fbcf2866e9dd06be4ce6c320e37908" dependencies = [ "integer-encoding", "intmap", @@ -2797,7 +2794,7 @@ dependencies = [ [[package]] name = "grovedb-dense-fixed-sized-merkle-tree" version = "4.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=8f25b20d04bfc0e8bdfb3870676d647a0d74918b#8f25b20d04bfc0e8bdfb3870676d647a0d74918b" +source = "git+https://github.com/dashpay/grovedb?rev=dbd83dce59fbcf2866e9dd06be4ce6c320e37908#dbd83dce59fbcf2866e9dd06be4ce6c320e37908" dependencies = [ "bincode", "blake3", @@ -2810,7 +2807,7 @@ dependencies = [ [[package]] name = "grovedb-element" version = "4.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=8f25b20d04bfc0e8bdfb3870676d647a0d74918b#8f25b20d04bfc0e8bdfb3870676d647a0d74918b" +source = "git+https://github.com/dashpay/grovedb?rev=dbd83dce59fbcf2866e9dd06be4ce6c320e37908#dbd83dce59fbcf2866e9dd06be4ce6c320e37908" dependencies = [ "bincode", "bincode_derive", @@ -2825,7 +2822,7 @@ dependencies = [ [[package]] name = "grovedb-epoch-based-storage-flags" version = "4.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=8f25b20d04bfc0e8bdfb3870676d647a0d74918b#8f25b20d04bfc0e8bdfb3870676d647a0d74918b" +source = "git+https://github.com/dashpay/grovedb?rev=dbd83dce59fbcf2866e9dd06be4ce6c320e37908#dbd83dce59fbcf2866e9dd06be4ce6c320e37908" dependencies = [ "grovedb-costs", "hex", @@ -2837,7 +2834,7 @@ dependencies = [ [[package]] name = "grovedb-merk" version = "4.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=8f25b20d04bfc0e8bdfb3870676d647a0d74918b#8f25b20d04bfc0e8bdfb3870676d647a0d74918b" +source = "git+https://github.com/dashpay/grovedb?rev=dbd83dce59fbcf2866e9dd06be4ce6c320e37908#dbd83dce59fbcf2866e9dd06be4ce6c320e37908" dependencies = [ "bincode", "bincode_derive", @@ -2863,7 +2860,7 @@ dependencies = [ [[package]] name = "grovedb-merkle-mountain-range" version = "4.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=8f25b20d04bfc0e8bdfb3870676d647a0d74918b#8f25b20d04bfc0e8bdfb3870676d647a0d74918b" +source = "git+https://github.com/dashpay/grovedb?rev=dbd83dce59fbcf2866e9dd06be4ce6c320e37908#dbd83dce59fbcf2866e9dd06be4ce6c320e37908" dependencies = [ "bincode", "blake3", @@ -2874,7 +2871,7 @@ dependencies = [ [[package]] name = "grovedb-path" version = "4.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=8f25b20d04bfc0e8bdfb3870676d647a0d74918b#8f25b20d04bfc0e8bdfb3870676d647a0d74918b" +source = "git+https://github.com/dashpay/grovedb?rev=dbd83dce59fbcf2866e9dd06be4ce6c320e37908#dbd83dce59fbcf2866e9dd06be4ce6c320e37908" dependencies = [ "hex", ] @@ -2882,7 +2879,7 @@ dependencies = [ [[package]] name = "grovedb-query" version = "4.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=8f25b20d04bfc0e8bdfb3870676d647a0d74918b#8f25b20d04bfc0e8bdfb3870676d647a0d74918b" +source = "git+https://github.com/dashpay/grovedb?rev=dbd83dce59fbcf2866e9dd06be4ce6c320e37908#dbd83dce59fbcf2866e9dd06be4ce6c320e37908" dependencies = [ "bincode", "byteorder", @@ -2898,7 +2895,7 @@ dependencies = [ [[package]] name = "grovedb-storage" version = "4.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=8f25b20d04bfc0e8bdfb3870676d647a0d74918b#8f25b20d04bfc0e8bdfb3870676d647a0d74918b" +source = "git+https://github.com/dashpay/grovedb?rev=dbd83dce59fbcf2866e9dd06be4ce6c320e37908#dbd83dce59fbcf2866e9dd06be4ce6c320e37908" dependencies = [ "blake3", "grovedb-costs", @@ -2917,7 +2914,7 @@ dependencies = [ [[package]] name = "grovedb-version" version = "4.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=8f25b20d04bfc0e8bdfb3870676d647a0d74918b#8f25b20d04bfc0e8bdfb3870676d647a0d74918b" +source = "git+https://github.com/dashpay/grovedb?rev=dbd83dce59fbcf2866e9dd06be4ce6c320e37908#dbd83dce59fbcf2866e9dd06be4ce6c320e37908" dependencies = [ "thiserror 2.0.18", "versioned-feature-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2926,7 +2923,7 @@ dependencies = [ [[package]] name = "grovedb-visualize" version = "4.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=8f25b20d04bfc0e8bdfb3870676d647a0d74918b#8f25b20d04bfc0e8bdfb3870676d647a0d74918b" +source = "git+https://github.com/dashpay/grovedb?rev=dbd83dce59fbcf2866e9dd06be4ce6c320e37908#dbd83dce59fbcf2866e9dd06be4ce6c320e37908" dependencies = [ "hex", "itertools 0.14.0", @@ -2935,7 +2932,7 @@ dependencies = [ [[package]] name = "grovedbg-types" version = "4.0.0" -source = "git+https://github.com/dashpay/grovedb?rev=8f25b20d04bfc0e8bdfb3870676d647a0d74918b#8f25b20d04bfc0e8bdfb3870676d647a0d74918b" +source = "git+https://github.com/dashpay/grovedb?rev=dbd83dce59fbcf2866e9dd06be4ce6c320e37908#dbd83dce59fbcf2866e9dd06be4ce6c320e37908" dependencies = [ "serde", "serde_with 3.18.0", @@ -3324,7 +3321,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.3", "system-configuration", "tokio", "tower-service", @@ -3585,7 +3582,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4331,7 +4328,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]] @@ -4559,13 +4556,13 @@ dependencies = [ [[package]] name = "orchard" -version = "0.12.0" -source = "git+https://github.com/dashpay/orchard.git?rev=41c8f7169f2683c99cf0e0c63e8d25ec12c47a79#41c8f7169f2683c99cf0e0c63e8d25ec12c47a79" +version = "0.13.1" +source = "git+https://github.com/dashpay/orchard.git?rev=898258d76aab2822249492aede59a02d49278fff#898258d76aab2822249492aede59a02d49278fff" dependencies = [ "aes", "bitvec", "blake2b_simd", - "core2 0.3.3", + "corez", "ff", "fpe", "getset", @@ -5298,7 +5295,7 @@ dependencies = [ "quinn-udp", "rustc-hash 2.1.2", "rustls", - "socket2 0.5.10", + "socket2 0.6.3", "thiserror 2.0.18", "tokio", "tracing", @@ -5336,7 +5333,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.3", "tracing", "windows-sys 0.59.0", ] @@ -6065,7 +6062,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -6124,7 +6121,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -6947,7 +6944,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -7756,7 +7753,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abbf77aed65cb885a8ba07138c365879be3d9a93dce82bf6cc50feca9138ec15" dependencies = [ - "core2 0.4.0", + "core2", ] [[package]] @@ -8351,7 +8348,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 a2683750d65..bebcf31989b 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 = "8f25b20d04bfc0e8bdfb3870676d647a0d74918b", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "dbd83dce59fbcf2866e9dd06be4ce6c320e37908", optional = true } [dev-dependencies] tokio = { version = "1.40", features = ["full"] } diff --git a/packages/rs-dpp/src/balances/total_credits_balance/mod.rs b/packages/rs-dpp/src/balances/total_credits_balance/mod.rs index 40023e1d2d4..03318cc1d34 100644 --- a/packages/rs-dpp/src/balances/total_credits_balance/mod.rs +++ b/packages/rs-dpp/src/balances/total_credits_balance/mod.rs @@ -16,6 +16,8 @@ pub struct TotalCreditsBalance { pub total_specialized_balances: SignedCredits, /// all the credits in addresses pub total_in_addresses: SignedCredits, + /// all the credits inside shielded credit pools + pub total_in_shielded_balances: SignedCredits, } impl fmt::Display for TotalCreditsBalance { @@ -34,14 +36,19 @@ impl fmt::Display for TotalCreditsBalance { )?; writeln!( f, - " total_specialized_balances: {}", + " total_specialized_balances: {},", self.total_specialized_balances )?; writeln!( f, - " total_addresses_balances: {}", + " total_addresses_balances: {},", self.total_in_addresses )?; + writeln!( + f, + " total_in_shielded_balances: {}", + self.total_in_shielded_balances + )?; write!(f, "}}") } } @@ -56,6 +63,7 @@ impl TotalCreditsBalance { total_identity_balances, total_specialized_balances, total_in_addresses, + total_in_shielded_balances, } = *self; if total_in_pools < 0 { @@ -82,6 +90,12 @@ impl TotalCreditsBalance { )); } + if total_in_shielded_balances < 0 { + return Err(ProtocolError::CriticalCorruptedCreditsCodeExecution( + "Credits inside shielded balances are less than 0".to_string(), + )); + } + if total_credits_in_platform > MAX_CREDITS { return Err(ProtocolError::CriticalCorruptedCreditsCodeExecution( "Total credits in platform more than max credits size".to_string(), @@ -92,6 +106,7 @@ impl TotalCreditsBalance { .checked_add(total_identity_balances) .and_then(|partial_sum| partial_sum.checked_add(total_specialized_balances)) .and_then(|partial_sum| partial_sum.checked_add(total_in_addresses)) + .and_then(|partial_sum| partial_sum.checked_add(total_in_shielded_balances)) .ok_or(ProtocolError::CriticalCorruptedCreditsCodeExecution( "Overflow of total credits".to_string(), ))?; @@ -106,6 +121,7 @@ impl TotalCreditsBalance { total_identity_balances, total_specialized_balances, total_in_addresses, + total_in_shielded_balances, .. } = *self; @@ -113,6 +129,7 @@ impl TotalCreditsBalance { .checked_add(total_identity_balances) .and_then(|partial_sum| partial_sum.checked_add(total_specialized_balances)) .and_then(|partial_sum| partial_sum.checked_add(total_in_addresses)) + .and_then(|partial_sum| partial_sum.checked_add(total_in_shielded_balances)) .ok_or(ProtocolError::CriticalCorruptedCreditsCodeExecution( "Overflow of total credits".to_string(), ))?; diff --git a/packages/rs-drive-abci/Cargo.toml b/packages/rs-drive-abci/Cargo.toml index d4dd462d2b8..afaccec27cf 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 = "8f25b20d04bfc0e8bdfb3870676d647a0d74918b" } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "dbd83dce59fbcf2866e9dd06be4ce6c320e37908" } nonempty = "0.11" [dev-dependencies] @@ -103,7 +103,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 = "8f25b20d04bfc0e8bdfb3870676d647a0d74918b", features = ["client"] } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "dbd83dce59fbcf2866e9dd06be4ce6c320e37908", features = ["client"] } assert_matches = "1.5.0" drive-abci = { path = ".", features = ["testing-config", "mocks"] } bls-signatures = { git = "https://github.com/dashpay/bls-signatures", rev = "0842b17583888e8f46c252a4ee84cdfd58e0546f" } diff --git a/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs b/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs index 6f07ed40551..24bc42cfe4e 100644 --- a/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs +++ b/packages/rs-drive-abci/src/execution/platform_events/protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0/mod.rs @@ -27,9 +27,14 @@ use drive::drive::saved_block_transactions::{ ADDRESS_BALANCES_KEY_U8, COMPACTED_ADDRESSES_EXPIRATION_TIME_KEY_U8, COMPACTED_ADDRESS_BALANCES_KEY_U8, }; +use drive::drive::shielded::nullifiers::queries::{ + SHIELDED_COMPACTED_NULLIFIERS_KEY_U8, SHIELDED_NULLIFIERS_EXPIRATION_TIME_KEY_U8, + SHIELDED_RECENT_NULLIFIERS_KEY_U8, +}; use drive::drive::shielded::paths::{ - shielded_credit_pool_path, SHIELDED_ANCHORS_IN_POOL_KEY, SHIELDED_CREDIT_POOL_KEY_U8, - SHIELDED_NOTES_KEY, SHIELDED_NULLIFIERS_KEY, SHIELDED_TOTAL_BALANCE_KEY, + shielded_credit_pool_path, MAIN_SHIELDED_CREDIT_POOL_KEY_U8, SHIELDED_ANCHORS_BY_HEIGHT_KEY, + SHIELDED_ANCHORS_IN_POOL_KEY, SHIELDED_NOTES_CHUNK_POWER, SHIELDED_NOTES_KEY, + SHIELDED_NULLIFIERS_KEY, SHIELDED_TOTAL_BALANCE_KEY, }; use drive::drive::system::misc_path; use drive::drive::tokens::paths::{ @@ -607,45 +612,62 @@ impl Platform { Ok(()) } - /// We introduced in version 12 Shielded Pools + /// We introduced in version 12 Shielded Pools. + /// + /// Mirrors the layout produced by `Drive::create_initial_state_structure_v3` + /// for a fresh genesis-12 chain: a top-level `ShieldedBalances` SumTree + /// containing the main shielded credit pool at `MAIN_SHIELDED_CREDIT_POOL_KEY`, + /// and all eight pool subtrees inserted breadth-first. The recent-nullifiers + /// `CountSumTree` is wrapped in `Element::NotSummed` so its sum side does + /// not propagate into the pool's "credits" aggregate. fn transition_to_version_12( &self, transaction: &Transaction, platform_version: &PlatformVersion, ) -> Result<(), Error> { - let addresses_path = Drive::addresses_path(); + // Top-level ShieldedBalances SumTree — separate from AddressBalances so + // per-pool internal trees cannot contaminate the address-credit + // aggregate via sum propagation. + self.drive.grove_insert_if_not_exists( + SubtreePath::empty(), + &[RootTree::ShieldedBalances as u8], + Element::empty_sum_tree(), + Some(transaction), + None, + &platform_version.drive, + )?; - // Shielded credit pool SumTree under AddressBalances: [AddressBalances] / "s" + // Main shielded credit pool SumTree: [ShieldedBalances] / "M" self.drive.grove_insert_if_not_exists( - addresses_path.as_slice().into(), - &[SHIELDED_CREDIT_POOL_KEY_U8], + SubtreePath::from(&[&[RootTree::ShieldedBalances as u8] as &[u8]]), + &[MAIN_SHIELDED_CREDIT_POOL_KEY_U8], Element::empty_sum_tree(), Some(transaction), None, &platform_version.drive, )?; - // The four child inserts below are ordered breadth-first to match the + // The eight child inserts below are ordered breadth-first to match the // intended balanced shape of the parent Merk tree (see the layout - // diagram in `drive::drive::shielded::paths`): root first, then both - // depth-1 children, then the depth-2 leaf. AVL rebalancing is + // diagram in `drive::drive::shielded::paths`). AVL rebalancing is // order-sensitive, so this ordering is what actually places // `SHIELDED_NOTES_KEY` at the root and the spend-path keys at depth 1. // Level 0 (root): notes tree (CommitmentTree = CountTree items + Sinsemilla Frontier) - // [AddressBalances, "s"] / [128] + // [ShieldedBalances, "M"] / [128] let shielded_pool_path = shielded_credit_pool_path(); self.drive.grove_insert_if_not_exists( (&shielded_pool_path).into(), &[SHIELDED_NOTES_KEY], - Element::empty_commitment_tree(11).expect("chunk_power 11 is valid"), + Element::empty_commitment_tree(SHIELDED_NOTES_CHUNK_POWER) + .expect("SHIELDED_NOTES_CHUNK_POWER is valid"), Some(transaction), None, &platform_version.drive, )?; // Level 1 (left): nullifiers tree (ProvableCountTree) - // [AddressBalances, "s"] / [64] + // [ShieldedBalances, "M"] / [64] self.drive.grove_insert_if_not_exists( (&shielded_pool_path).into(), &[SHIELDED_NULLIFIERS_KEY], @@ -656,7 +678,7 @@ impl Platform { )?; // Level 1 (right): anchors tree (NormalTree) — anchor_bytes → block_height_be - // [AddressBalances, "s"] / [192] + // [ShieldedBalances, "M"] / [192] self.drive.grove_insert_if_not_exists( (&shielded_pool_path).into(), &[SHIELDED_ANCHORS_IN_POOL_KEY], @@ -667,7 +689,7 @@ impl Platform { )?; // Level 2: total balance SumItem(0) - // [AddressBalances, "s"] / [32] + // [ShieldedBalances, "M"] / [32] self.drive.grove_insert_if_not_exists( (&shielded_pool_path).into(), &[SHIELDED_TOTAL_BALANCE_KEY], @@ -677,6 +699,54 @@ impl Platform { &platform_version.drive, )?; + // Level 2: anchors-by-height tree (NormalTree) — block_height_be → anchor_bytes. + // [ShieldedBalances, "M"] / [96] + self.drive.grove_insert_if_not_exists( + (&shielded_pool_path).into(), + &[SHIELDED_ANCHORS_BY_HEIGHT_KEY], + Element::empty_tree(), + Some(transaction), + None, + &platform_version.drive, + )?; + + // Level 2: per-block recent-nullifiers CountSumTree wrapped in + // NotSummed — the sum side (per-block nullifier count) is suppressed + // so it does NOT propagate into the enclosing shielded pool SumTree. + // [ShieldedBalances, "M"] / [160] + self.drive.grove_insert_if_not_exists( + (&shielded_pool_path).into(), + &[SHIELDED_RECENT_NULLIFIERS_KEY_U8], + Element::new_not_summed(Element::empty_count_sum_tree()) + .expect("count sum tree is a valid NotSummed inner"), + Some(transaction), + None, + &platform_version.drive, + )?; + + // Level 2: compacted nullifiers NormalTree — + // (start_block, end_block) → serialized Vec<[u8;32]>. + // [ShieldedBalances, "M"] / [224] + self.drive.grove_insert_if_not_exists( + (&shielded_pool_path).into(), + &[SHIELDED_COMPACTED_NULLIFIERS_KEY_U8], + Element::empty_tree(), + Some(transaction), + None, + &platform_version.drive, + )?; + + // Level 3: nullifiers-expiration-time NormalTree (deepest leaf). + // [ShieldedBalances, "M"] / [240] + self.drive.grove_insert_if_not_exists( + (&shielded_pool_path).into(), + &[SHIELDED_NULLIFIERS_EXPIRATION_TIME_KEY_U8], + Element::empty_tree(), + Some(transaction), + None, + &platform_version.drive, + )?; + // Strip unknown top-level properties from all contract document type schemas. // The v1 document meta-schema enforces additionalProperties: false at the // document-type level. Contracts created under the v0 meta-schema (which did @@ -823,10 +893,10 @@ mod tests { .transition_to_version_12(&transaction, platform_version) .expect("expected version 12 transition to succeed"); - // Verify shielded credit pool tree was created under AddressBalances + // Verify shielded credit pool tree was created under ShieldedBalances let shielded_pool_element = platform.drive.grove.get( - SubtreePath::from(&[&[RootTree::AddressBalances as u8] as &[u8]]), - &[SHIELDED_CREDIT_POOL_KEY_U8], + SubtreePath::from(&[&[RootTree::ShieldedBalances as u8] as &[u8]]), + &[MAIN_SHIELDED_CREDIT_POOL_KEY_U8], Some(&transaction), &platform_version.drive.grove_version, ); @@ -920,8 +990,8 @@ mod tests { // Verify version 12 artifacts: shielded credit pool tree should exist let shielded_pool = platform.drive.grove.get( - SubtreePath::from(&[&[RootTree::AddressBalances as u8] as &[u8]]), - &[SHIELDED_CREDIT_POOL_KEY_U8], + SubtreePath::from(&[&[RootTree::ShieldedBalances as u8] as &[u8]]), + &[MAIN_SHIELDED_CREDIT_POOL_KEY_U8], Some(&transaction), &platform_version.drive.grove_version, ); diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/address_funds_transfer/tests.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/address_funds_transfer/tests.rs index 7f0823683ba..bb6a49b3595 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/address_funds_transfer/tests.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/address_funds_transfer/tests.rs @@ -7580,8 +7580,8 @@ mod tests { // Assert exact values - UPDATE THESE if fees legitimately change assert_eq!( - processing_fee, 508740, - "Processing fee changed! Was 508740, now {}", + processing_fee, 458920, + "Processing fee changed! Was 458920, now {}", processing_fee ); assert_eq!( @@ -7590,8 +7590,8 @@ mod tests { storage_fee ); assert_eq!( - total_fee, 6583740, - "Total fee changed! Was 6583740, now {}", + total_fee, 6533920, + "Total fee changed! Was 6533920, now {}", total_fee ); } @@ -7666,8 +7666,8 @@ mod tests { // Assert exact values assert_eq!( - processing_fee, 508740, - "Processing fee changed! Was 508740, now {}", + processing_fee, 458920, + "Processing fee changed! Was 458920, now {}", processing_fee ); assert_eq!( @@ -7676,8 +7676,8 @@ mod tests { storage_fee ); assert_eq!( - total_fee, 6583740, - "Total fee changed! Was 6583740, now {}", + total_fee, 6533920, + "Total fee changed! Was 6533920, now {}", total_fee ); } @@ -7754,8 +7754,8 @@ mod tests { // Assert exact values - 2 inputs should cost more processing than 1 input assert_eq!( - processing_fee, 639100, - "Processing fee changed! Was 639100, now {}", + processing_fee, 589280, + "Processing fee changed! Was 589280, now {}", processing_fee ); assert_eq!( @@ -7764,8 +7764,8 @@ mod tests { storage_fee ); assert_eq!( - total_fee, 6714100, - "Total fee changed! Was 6714100, now {}", + total_fee, 6664280, + "Total fee changed! Was 6664280, now {}", total_fee ); } @@ -7841,8 +7841,8 @@ mod tests { // Assert exact values - 2 outputs should cost more storage than 1 output assert_eq!( - processing_fee, 611120, - "Processing fee changed! Was 611120, now {}", + processing_fee, 561300, + "Processing fee changed! Was 561300, now {}", processing_fee ); assert_eq!( @@ -7851,8 +7851,8 @@ mod tests { storage_fee ); assert_eq!( - total_fee, 12761120, - "Total fee changed! Was 12761120, now {}", + total_fee, 12711300, + "Total fee changed! Was 12711300, now {}", total_fee ); } @@ -7936,8 +7936,8 @@ mod tests { // Assert exact values - P2SH with 2 signatures assert_eq!( - processing_fee, 528740, - "Processing fee changed! Was 528740, now {}", + processing_fee, 478920, + "Processing fee changed! Was 478920, now {}", processing_fee ); assert_eq!( @@ -7946,8 +7946,8 @@ mod tests { storage_fee ); assert_eq!( - total_fee, 6603740, - "Total fee changed! Was 6603740, now {}", + total_fee, 6553920, + "Total fee changed! Was 6553920, now {}", total_fee ); } @@ -8031,8 +8031,8 @@ mod tests { // Assert exact values - 3-of-5 multisig assert_eq!( - processing_fee, 543740, - "Processing fee changed! Was 543740, now {}", + processing_fee, 493920, + "Processing fee changed! Was 493920, now {}", processing_fee ); assert_eq!( @@ -8041,8 +8041,8 @@ mod tests { storage_fee ); assert_eq!( - total_fee, 6618740, - "Total fee changed! Was 6618740, now {}", + total_fee, 6568920, + "Total fee changed! Was 6568920, now {}", total_fee ); } @@ -8126,8 +8126,8 @@ mod tests { // Assert exact values assert_eq!( - processing_fee, 1017480, - "Processing fee changed! Was 1017480, now {}", + processing_fee, 917840, + "Processing fee changed! Was 917840, now {}", processing_fee ); assert_eq!( @@ -8136,8 +8136,8 @@ mod tests { storage_fee ); assert_eq!( - total_fee, 7092480, - "Total fee changed! Was 7092480, now {}", + total_fee, 6992840, + "Total fee changed! Was 6992840, now {}", total_fee ); } @@ -8224,8 +8224,8 @@ mod tests { // Assert exact values assert_eq!( - processing_fee, 3009500, - "Processing fee changed! Was 3009500, now {}", + processing_fee, 2959680, + "Processing fee changed! Was 2959680, now {}", processing_fee ); assert_eq!( @@ -8234,8 +8234,8 @@ mod tests { storage_fee ); assert_eq!( - total_fee, 9084500, - "Total fee changed! Was 9084500, now {}", + total_fee, 9034680, + "Total fee changed! Was 9034680, now {}", total_fee ); } @@ -8372,15 +8372,15 @@ mod tests { // Assert exact values for new address assert_eq!( - total_fee_new, 6583740, - "Total fee to new address changed! Was 6583740, now {}", + total_fee_new, 6533920, + "Total fee to new address changed! Was 6533920, now {}", total_fee_new ); // Assert exact values for existing address (much cheaper - only updates balance) assert_eq!( - total_fee_existing, 497220, - "Total fee to existing address changed! Was 497220, now {}", + total_fee_existing, 447400, + "Total fee to existing address changed! Was 447400, now {}", total_fee_existing ); } diff --git a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shielded_common/mod.rs b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shielded_common/mod.rs index 5fe667a753f..3a003d6f308 100644 --- a/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shielded_common/mod.rs +++ b/packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/shielded_common/mod.rs @@ -127,6 +127,9 @@ pub fn reconstruct_and_verify_bundle( InvalidShieldedProofError::new("invalid value commitment bytes".to_string()) })?; + // `Action::from_parts` returns `None` when `rk` is the identity key + // (an Orchard hardening added upstream in 0.13). Reject those as + // malformed rather than silently dropping them. let action = Action::from_parts( nullifier, rk, @@ -138,7 +141,10 @@ pub fn reconstruct_and_verify_bundle( ), cv_net, redpallas::Signature::from(a.spend_auth_sig), - ); + ) + .ok_or_else(|| { + InvalidShieldedProofError::new("action has identity randomizer key".to_string()) + })?; orchard_actions.push(action); } diff --git a/packages/rs-drive/Cargo.toml b/packages/rs-drive/Cargo.toml index 51ccd31a520..111cd9d257b 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 = "8f25b20d04bfc0e8bdfb3870676d647a0d74918b", optional = true, default-features = false } -grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "8f25b20d04bfc0e8bdfb3870676d647a0d74918b", optional = true } -grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "8f25b20d04bfc0e8bdfb3870676d647a0d74918b" } -grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "8f25b20d04bfc0e8bdfb3870676d647a0d74918b", optional = true } -grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "8f25b20d04bfc0e8bdfb3870676d647a0d74918b" } -grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "8f25b20d04bfc0e8bdfb3870676d647a0d74918b" } +grovedb = { git = "https://github.com/dashpay/grovedb", rev = "dbd83dce59fbcf2866e9dd06be4ce6c320e37908", optional = true, default-features = false } +grovedb-costs = { git = "https://github.com/dashpay/grovedb", rev = "dbd83dce59fbcf2866e9dd06be4ce6c320e37908", optional = true } +grovedb-path = { git = "https://github.com/dashpay/grovedb", rev = "dbd83dce59fbcf2866e9dd06be4ce6c320e37908" } +grovedb-storage = { git = "https://github.com/dashpay/grovedb", rev = "dbd83dce59fbcf2866e9dd06be4ce6c320e37908", optional = true } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "dbd83dce59fbcf2866e9dd06be4ce6c320e37908" } +grovedb-epoch-based-storage-flags = { git = "https://github.com/dashpay/grovedb", rev = "dbd83dce59fbcf2866e9dd06be4ce6c320e37908" } [dev-dependencies] criterion = "0.5" diff --git a/packages/rs-drive/src/drive/balances/calculate_total_credits_balance/mod.rs b/packages/rs-drive/src/drive/balances/calculate_total_credits_balance/mod.rs index 2f30b27790f..e88cd2d56ea 100644 --- a/packages/rs-drive/src/drive/balances/calculate_total_credits_balance/mod.rs +++ b/packages/rs-drive/src/drive/balances/calculate_total_credits_balance/mod.rs @@ -1,5 +1,6 @@ mod v0; mod v1; +mod v2; use crate::drive::Drive; use crate::error::drive::DriveError; @@ -38,9 +39,10 @@ impl Drive { { 0 => self.calculate_total_credits_balance_v0(transaction, drive_version), 1 => self.calculate_total_credits_balance_v1(transaction, drive_version), + 2 => self.calculate_total_credits_balance_v2(transaction, drive_version), version => Err(Error::Drive(DriveError::UnknownVersionMismatch { method: "calculate_total_credits_balance".to_string(), - known_versions: vec![0, 1], + known_versions: vec![0, 1, 2], received: version, })), } diff --git a/packages/rs-drive/src/drive/balances/calculate_total_credits_balance/v0/mod.rs b/packages/rs-drive/src/drive/balances/calculate_total_credits_balance/v0/mod.rs index 73efae4c037..428e964f639 100644 --- a/packages/rs-drive/src/drive/balances/calculate_total_credits_balance/v0/mod.rs +++ b/packages/rs-drive/src/drive/balances/calculate_total_credits_balance/v0/mod.rs @@ -66,6 +66,7 @@ impl Drive { total_identity_balances, total_specialized_balances, total_in_addresses: 0, + total_in_shielded_balances: 0, }) } } diff --git a/packages/rs-drive/src/drive/balances/calculate_total_credits_balance/v1/mod.rs b/packages/rs-drive/src/drive/balances/calculate_total_credits_balance/v1/mod.rs index 5934cd84719..13c00b3136c 100644 --- a/packages/rs-drive/src/drive/balances/calculate_total_credits_balance/v1/mod.rs +++ b/packages/rs-drive/src/drive/balances/calculate_total_credits_balance/v1/mod.rs @@ -75,6 +75,11 @@ impl Drive { total_identity_balances, total_specialized_balances, total_in_addresses, + // v1 predates the ShieldedBalances root tree (introduced at + // protocol v12 / drive v7 alongside the v2 calculator). On + // pre-v12 chains the tree does not exist, so v1 does not read + // it and leaves the field zeroed. + total_in_shielded_balances: 0, }) } } diff --git a/packages/rs-drive/src/drive/balances/calculate_total_credits_balance/v2/mod.rs b/packages/rs-drive/src/drive/balances/calculate_total_credits_balance/v2/mod.rs new file mode 100644 index 00000000000..a6e17adf685 --- /dev/null +++ b/packages/rs-drive/src/drive/balances/calculate_total_credits_balance/v2/mod.rs @@ -0,0 +1,94 @@ +use crate::drive::balances::TOTAL_SYSTEM_CREDITS_STORAGE_KEY; +use crate::drive::system::misc_path; +use crate::drive::{Drive, RootTree}; +use crate::error::drive::DriveError; +use crate::error::Error; +use crate::util::grove_operations::DirectQueryType; +use dpp::balances::total_credits_balance::TotalCreditsBalance; +use dpp::version::drive_versions::DriveVersion; +use grovedb::TransactionArg; +use grovedb_path::SubtreePath; + +impl Drive { + /// Verify that the sum tree identity credits + pool credits + refunds + address + /// credits + shielded credits are equal to the Total credits in the system. + /// + /// v2 adds the `ShieldedBalances` root sum tree (introduced at protocol v12 / + /// drive v7) as a fifth term in the equation. Earlier calculators do not + /// read it because the tree does not exist on pre-v12 chains. + #[inline(always)] + pub(super) fn calculate_total_credits_balance_v2( + &self, + transaction: TransactionArg, + drive_version: &DriveVersion, + ) -> Result { + let mut drive_operations = vec![]; + let path_holding_total_credits = misc_path(); + let total_credits_in_platform = self + .grove_get_raw_value_u64_from_encoded_var_vec( + (&path_holding_total_credits).into(), + TOTAL_SYSTEM_CREDITS_STORAGE_KEY, + DirectQueryType::StatefulDirectQuery, + transaction, + &mut drive_operations, + drive_version, + )? + .ok_or(Error::Drive(DriveError::CriticalCorruptedState( + "Credits not found in Platform", + )))?; + + let total_identity_balances = self.grove_get_sum_tree_total_value( + SubtreePath::empty(), + Into::<&[u8; 1]>::into(RootTree::Balances), + DirectQueryType::StatefulDirectQuery, + transaction, + &mut drive_operations, + drive_version, + )?; + + let total_specialized_balances = self.grove_get_sum_tree_total_value( + SubtreePath::empty(), + Into::<&[u8; 1]>::into(RootTree::PreFundedSpecializedBalances), + DirectQueryType::StatefulDirectQuery, + transaction, + &mut drive_operations, + drive_version, + )?; + + let total_in_pools = self.grove_get_sum_tree_total_value( + SubtreePath::empty(), + Into::<&[u8; 1]>::into(RootTree::Pools), + DirectQueryType::StatefulDirectQuery, + transaction, + &mut drive_operations, + drive_version, + )?; + + let total_in_addresses = self.grove_get_sum_tree_total_value( + SubtreePath::empty(), + Into::<&[u8; 1]>::into(RootTree::AddressBalances), + DirectQueryType::StatefulDirectQuery, + transaction, + &mut drive_operations, + drive_version, + )?; + + let total_in_shielded_balances = self.grove_get_sum_tree_total_value( + SubtreePath::empty(), + Into::<&[u8; 1]>::into(RootTree::ShieldedBalances), + DirectQueryType::StatefulDirectQuery, + transaction, + &mut drive_operations, + drive_version, + )?; + + Ok(TotalCreditsBalance { + total_credits_in_platform, + total_in_pools, + total_identity_balances, + total_specialized_balances, + total_in_addresses, + total_in_shielded_balances, + }) + } +} diff --git a/packages/rs-drive/src/drive/initialization/v0/mod.rs b/packages/rs-drive/src/drive/initialization/v0/mod.rs index 92cb71f47ae..bbf66d544b7 100644 --- a/packages/rs-drive/src/drive/initialization/v0/mod.rs +++ b/packages/rs-drive/src/drive/initialization/v0/mod.rs @@ -316,7 +316,7 @@ mod tests { &platform_version.drive, ) .expect("expected to get root elements"); - assert_eq!(elements.len(), 16); + assert_eq!(elements.len(), 17); } #[test] @@ -1297,7 +1297,7 @@ mod tests { drive_version, ) .expect("expected to get root elements"); - assert_eq!(proof.len(), 112); //it + left + right + assert_eq!(proof.len(), 112); // Merk Level 1 let mut query = Query::new(); @@ -1319,7 +1319,7 @@ mod tests { drive_version, ) .expect("expected to get root elements"); - assert_eq!(proof.len(), 180); //it + left + right + parent + parent other + assert_eq!(proof.len(), 180); let mut query = Query::new(); query.insert_key(vec![RootTree::Balances as u8]); @@ -1340,7 +1340,7 @@ mod tests { drive_version, ) .expect("expected to get root elements"); - assert_eq!(proof.len(), 181); //it + left + right + parent + parent other + assert_eq!(proof.len(), 181); // Merk Level 2 let mut query = Query::new(); @@ -1362,7 +1362,7 @@ mod tests { drive_version, ) .expect("expected to get root elements"); - assert_eq!(proof.len(), 285); //it + left + right + parent + sibling + parent sibling + grandparent (v11: 250, +35 from KVValueHashFeatureTypeWithChildHash) + assert_eq!(proof.len(), 285); let mut query = Query::new(); query.insert_key(vec![RootTree::Pools as u8]); @@ -1383,7 +1383,7 @@ mod tests { drive_version, ) .expect("expected to get root elements"); - assert_eq!(proof.len(), 287); //it + left + parent + sibling + parent sibling + grandparent (v11: 252, +35 from KVValueHashFeatureTypeWithChildHash) + assert_eq!(proof.len(), 287); let mut query = Query::new(); query.insert_key(vec![RootTree::WithdrawalTransactions as u8]); @@ -1404,7 +1404,7 @@ mod tests { drive_version, ) .expect("expected to get root elements"); - assert_eq!(proof.len(), 285); //it + left + right + parent + sibling + parent sibling + grandparent (v11: 250, +35 from KVValueHashFeatureTypeWithChildHash) + assert_eq!(proof.len(), 285); let mut query = Query::new(); query.insert_key(vec![RootTree::Votes as u8]); @@ -1425,7 +1425,7 @@ mod tests { drive_version, ) .expect("expected to get root elements"); - assert_eq!(proof.len(), 285); //it + left + right + parent + sibling + parent sibling + grandparent (v11: 250, +35 from KVValueHashFeatureTypeWithChildHash) + assert_eq!(proof.len(), 285); // Merk Level 3 @@ -1448,7 +1448,7 @@ mod tests { drive_version, ) .expect("expected to get root elements"); - assert_eq!(proof.len(), 248); //it + parent + sibling + parent sibling + grandparent + grandparent sibling + great-grandparent + assert_eq!(proof.len(), 248); let mut query = Query::new(); query.insert_key(vec![ @@ -1471,7 +1471,7 @@ mod tests { drive_version, ) .expect("expected to get root elements"); - assert_eq!(proof.len(), 248); //it + parent + sibling + parent sibling + grandparent + grandparent sibling + great-grandparent + assert_eq!(proof.len(), 248); let mut query = Query::new(); query.insert_key(vec![RootTree::PreFundedSpecializedBalances as u8]); @@ -1492,7 +1492,7 @@ mod tests { drive_version, ) .expect("expected to get root elements"); - assert_eq!(proof.len(), 320); //it + parent + sibling + parent sibling + grandparent + grandparent sibling + great-grandparent (v11: 287, +33 from KVValueHashFeatureTypeWithChildHash) + assert_eq!(proof.len(), 320); let mut query = Query::new(); query.insert_key(vec![RootTree::AddressBalances as u8]); @@ -1513,7 +1513,7 @@ mod tests { drive_version, ) .expect("expected to get root elements"); - assert_eq!(proof.len(), 286); //it + parent + sibling + parent sibling + grandparent + grandparent sibling + great-grandparent (v11: 251, +35 from KVValueHashFeatureTypeWithChildHash) + assert_eq!(proof.len(), 320); let mut query = Query::new(); query.insert_key(vec![RootTree::SpentAssetLockTransactions as u8]); @@ -1534,7 +1534,7 @@ mod tests { drive_version, ) .expect("expected to get root elements"); - assert_eq!(proof.len(), 248); //it + parent + sibling + parent sibling + grandparent + grandparent sibling + great-grandparent + assert_eq!(proof.len(), 248); let mut query = Query::new(); query.insert_key(vec![RootTree::GroupActions as u8]); @@ -1555,7 +1555,7 @@ mod tests { drive_version, ) .expect("expected to get root elements"); - assert_eq!(proof.len(), 248); //it + parent + sibling + parent sibling + grandparent + grandparent sibling + great-grandparent + assert_eq!(proof.len(), 248); let mut query = Query::new(); query.insert_key(vec![RootTree::Misc as u8]); @@ -1576,7 +1576,7 @@ mod tests { drive_version, ) .expect("expected to get root elements"); - assert_eq!(proof.len(), 285); //it + parent + sibling + parent sibling + grandparent + grandparent sibling + great-grandparent (v11: 250, +35 from KVValueHashFeatureTypeWithChildHash) + assert_eq!(proof.len(), 285); let mut query = Query::new(); query.insert_key(vec![RootTree::Versions as u8]); @@ -1597,7 +1597,7 @@ mod tests { drive_version, ) .expect("expected to get root elements"); - assert_eq!(proof.len(), 285); //it + parent + sibling + parent sibling + grandparent + grandparent sibling + great-grandparent (v11: 250, +35 from KVValueHashFeatureTypeWithChildHash) + assert_eq!(proof.len(), 285); // Merk Level 4 @@ -1620,6 +1620,6 @@ mod tests { drive_version, ) .expect("expected to get root elements"); - assert_eq!(proof.len(), 319); //it + parent + parent sibling + grandparent + grandparent sibling + great-grandparent + great-grandparent sibling + great-great-grandparent (v11: 286, +33 from KVValueHashFeatureTypeWithChildHash) + assert_eq!(proof.len(), 319); } } diff --git a/packages/rs-drive/src/drive/initialization/v3/mod.rs b/packages/rs-drive/src/drive/initialization/v3/mod.rs index 1863b9fb473..4af8a086224 100644 --- a/packages/rs-drive/src/drive/initialization/v3/mod.rs +++ b/packages/rs-drive/src/drive/initialization/v3/mod.rs @@ -41,6 +41,19 @@ impl Drive { drive_version, )?; + // ShieldedBalances top-level sum tree — separate from AddressBalances so + // per-pool internal trees (notes, nullifiers, anchors, …) cannot + // contaminate the address-credit aggregate via sum propagation. + self.grove_insert_empty_tree( + SubtreePath::empty(), + &[RootTree::ShieldedBalances as u8], + TreeType::SumTree, + transaction, + None, + &mut vec![], + drive_version, + )?; + // SavedBlockTransactions for address-based transaction sync self.grove_insert_empty_tree( SubtreePath::empty(), @@ -69,22 +82,25 @@ impl Drive { /// Adds shielded pool batch operations for initialization. /// - /// The eight subtree inserts are ordered breadth-first to match the - /// intended balanced shape of the parent Merk tree (see the layout - /// diagram in `crate::drive::shielded::paths`): root first, then both - /// depth-1 children, then the four depth-2 children, then the depth-3 - /// leaf. AVL rebalancing is order-sensitive, so this ordering is what - /// actually places `SHIELDED_NOTES_KEY` at the root and the spend-path - /// keys at depth 1. + /// The main shielded credit pool lives under `RootTree::ShieldedBalances` + /// at key `MAIN_SHIELDED_CREDIT_POOL_KEY` (`b"M"`). + /// + /// The eight subtree inserts inside the pool are ordered breadth-first to + /// match the intended balanced shape of the parent Merk tree (see the + /// layout diagram in `crate::drive::shielded::paths`): root first, then + /// both depth-1 children, then the four depth-2 children, then the + /// depth-3 leaf. AVL rebalancing is order-sensitive, so this ordering is + /// what actually places `SHIELDED_NOTES_KEY` at the root and the + /// spend-path keys at depth 1. pub(in crate::drive::initialization) fn initial_state_structure_shielded_pool_operations( &self, batch: &mut GroveDbOpBatch, ) -> Result<(), Error> { - // Parent: shielded credit pool SumTree under AddressBalances. Must be - // inserted before any of its children so the subtree exists. + // Parent: main shielded credit pool SumTree under ShieldedBalances. + // Must be inserted before any of its children so the subtree exists. batch.add_insert( - vec![vec![RootTree::AddressBalances as u8]], - vec![SHIELDED_CREDIT_POOL_KEY_U8], + vec![vec![RootTree::ShieldedBalances as u8]], + vec![MAIN_SHIELDED_CREDIT_POOL_KEY_U8], Element::empty_sum_tree(), ); @@ -129,12 +145,16 @@ impl Drive { Element::empty_tree(), ); - // Level 2: per-block recent-nullifiers CountSumTree. - // Each item is an ItemWithSumItem (serialized Vec<[u8;32]> + nullifier count as sum). + // Level 2: per-block recent-nullifiers CountSumTree, wrapped in + // NotSummed so its sum side (the per-block nullifier count, stored as + // the sum half of each ItemWithSumItem) does NOT propagate into the + // enclosing shielded pool SumTree — and therefore not into + // ShieldedBalances either. Without the wrapper, every spent nullifier + // would inflate the "credits in pool" aggregate by 1. batch.add_insert( shielded_credit_pool_path_vec(), vec![SHIELDED_RECENT_NULLIFIERS_KEY_U8], - Element::empty_count_sum_tree(), + Element::new_not_summed(Element::empty_count_sum_tree())?, ); // Level 2: compacted nullifiers NormalTree. diff --git a/packages/rs-drive/src/drive/mod.rs b/packages/rs-drive/src/drive/mod.rs index 613dcceae61..98eeafa0c24 100644 --- a/packages/rs-drive/src/drive/mod.rs +++ b/packages/rs-drive/src/drive/mod.rs @@ -181,8 +181,8 @@ pub struct Drive { // Tokens 16 Pools 48 WithdrawalTransactions 80 Votes 112 // / \ / \ / \ / \ // NUPKH->I 8 UPKH->I 24 PreFundedSpecializedBalances 40 AddressBalances 56 SpentAssetLockTransactions 72 GroupActions 88 Misc 104 Versions 120 -// / -// Saved Block Transactions 36 +// / / +// Saved Block Transactions 36 ShieldedBalances 52 /// Keys for the root tree. #[cfg(any(feature = "server", feature = "verify"))] @@ -204,6 +204,12 @@ pub enum RootTree { PreFundedSpecializedBalances = 40, /// Saved Block Transactions contains address based transactions that we save for sync purposes SavedBlockTransactions = 36, + /// Shielded credit pools (SumTree of shielded pool subtrees; only the main + /// pool exists today, but the layout reserves siblings for future pools). + /// Kept separate from `AddressBalances` so that per-pool internal trees + /// (notes, nullifiers, anchors, …) cannot contaminate the address-credit + /// aggregate via sum propagation. + ShieldedBalances = 52, /// Address Balances AddressBalances = 56, /// Spent Asset Lock Transactions @@ -237,6 +243,7 @@ impl fmt::Display for RootTree { RootTree::Pools => "Pools", RootTree::PreFundedSpecializedBalances => "PreFundedSpecializedBalances", RootTree::SavedBlockTransactions => "SavedBlockTransactions", + RootTree::ShieldedBalances => "ShieldedBalances", RootTree::AddressBalances => "SingleUseKeyBalances", // RootTree::MasternodeLists => "MasternodeLists" RootTree::SpentAssetLockTransactions => "SpentAssetLockTransactions", @@ -284,6 +291,7 @@ impl TryFrom for RootTree { // 56 => Ok(RootTree::MasternodeLists), //todo (reserved) 40 => Ok(RootTree::PreFundedSpecializedBalances), 36 => Ok(RootTree::SavedBlockTransactions), + 52 => Ok(RootTree::ShieldedBalances), 72 => Ok(RootTree::SpentAssetLockTransactions), 104 => Ok(RootTree::Misc), 80 => Ok(RootTree::WithdrawalTransactions), @@ -309,6 +317,7 @@ impl From for &'static [u8; 1] { RootTree::Pools => &[48], RootTree::PreFundedSpecializedBalances => &[40], RootTree::SavedBlockTransactions => &[36], + RootTree::ShieldedBalances => &[52], RootTree::AddressBalances => &[56], RootTree::Misc => &[104], RootTree::WithdrawalTransactions => &[80], diff --git a/packages/rs-drive/src/drive/shielded/estimated_costs.rs b/packages/rs-drive/src/drive/shielded/estimated_costs.rs index 7ff46f3c46f..57e801d9d78 100644 --- a/packages/rs-drive/src/drive/shielded/estimated_costs.rs +++ b/packages/rs-drive/src/drive/shielded/estimated_costs.rs @@ -30,12 +30,12 @@ impl Drive { /// /// Registers all shielded pool tree paths in the estimation cache so that /// the fee estimation system can calculate costs for shielded operations. - /// Also registers parent paths (root and AddressBalances) needed by the + /// Also registers parent paths (root and ShieldedBalances) needed by the /// GroveDB cost estimation system. pub(crate) fn add_estimation_costs_for_shielded_pool_operations( estimated_costs_only_with_layer_info: &mut HashMap, ) { - // Root level: [] — needed so GroveDB can estimate traversal to AddressBalances + // Root level: [] — needed so GroveDB can estimate traversal to ShieldedBalances estimated_costs_only_with_layer_info.insert( KeyInfoPath::from_known_path([]), EstimatedLayerInformation { @@ -55,19 +55,21 @@ impl Drive { }, ); - // AddressBalances level: [[56]] — parent of shielded pool subtree + // ShieldedBalances level: [[52]] — parent of shielded pool subtree(s). + // Today this layer contains only the main shielded credit pool (a SumTree); + // additional pool subtrees may be added as siblings in the future. estimated_costs_only_with_layer_info.insert( - KeyInfoPath::from_known_owned_path(vec![vec![RootTree::AddressBalances as u8]]), + KeyInfoPath::from_known_owned_path(vec![vec![RootTree::ShieldedBalances as u8]]), EstimatedLayerInformation { tree_type: TreeType::SumTree, - estimated_layer_count: EstimatedLevel(2, false), + estimated_layer_count: EstimatedLevel(1, false), estimated_layer_sizes: AllSubtrees( 1, SomeSumTrees { sum_trees_weight: 1, big_sum_trees_weight: 0, count_trees_weight: 0, - count_sum_trees_weight: 1, + count_sum_trees_weight: 0, non_sum_trees_weight: 0, }, None, @@ -75,10 +77,11 @@ impl Drive { }, ); - // Shielded credit pool: [AddressBalances, "s"] + // Shielded credit pool: [ShieldedBalances, "M"] // SumTree containing: notes (CommitmentTree), permanent nullifiers (ProvableCountTree), // total balance (SumItem), anchors (NormalTree), anchors-by-height (NormalTree), - // recent nullifiers (CountSumTree), compacted nullifiers (NormalTree), + // recent nullifiers (NotSummed CountSumTree — contributes 0 to pool sum), + // compacted nullifiers (NormalTree), // expiration time (NormalTree). // 8 elements total (7 subtrees + 1 item) → balanced Merk depth = ceil(log2(8)) = 3. // @@ -99,7 +102,7 @@ impl Drive { sum_trees_weight: 0, big_sum_trees_weight: 0, count_trees_weight: 1, // permanent nullifiers (ProvableCountTree) - count_sum_trees_weight: 1, // recent nullifiers (CountSumTree) + count_sum_trees_weight: 1, // recent nullifiers (NotSummed-wrapped CountSumTree) non_sum_trees_weight: 5, // notes (CommitmentTree), anchors, anchors-by-height, compacted nullifiers, expiration time }, None, @@ -111,7 +114,7 @@ impl Drive { }, ); - // Notes tree: [AddressBalances, "s", 128] + // Notes tree: [ShieldedBalances, "M", 128] // CommitmentTree - stores notes (cmx||encrypted_note items + Sinsemilla frontier) estimated_costs_only_with_layer_info.insert( KeyInfoPath::from_known_path(shielded_credit_pool_notes_path()), @@ -122,7 +125,7 @@ impl Drive { }, ); - // Nullifiers tree: [AddressBalances, "s", 64] + // Nullifiers tree: [ShieldedBalances, "M", 64] // ProvableCountTree - stores spent nullifiers (32-byte key -> empty item) estimated_costs_only_with_layer_info.insert( KeyInfoPath::from_known_path(shielded_credit_pool_nullifiers_path()), @@ -133,7 +136,7 @@ impl Drive { }, ); - // Anchors tree: [AddressBalances, "s", 192] + // Anchors tree: [ShieldedBalances, "M", 192] // NormalTree - stores anchor_bytes -> block_height_be estimated_costs_only_with_layer_info.insert( KeyInfoPath::from_known_path(shielded_credit_pool_anchors_path()), @@ -144,7 +147,7 @@ impl Drive { }, ); - // Anchors-by-height tree: [AddressBalances, "s", 96] + // Anchors-by-height tree: [ShieldedBalances, "M", 96] // NormalTree - stores block_height_be -> anchor_bytes (reverse index for pruning) estimated_costs_only_with_layer_info.insert( KeyInfoPath::from_known_path(shielded_credit_pool_anchors_by_height_path()), diff --git a/packages/rs-drive/src/drive/shielded/nullifiers/queries.rs b/packages/rs-drive/src/drive/shielded/nullifiers/queries.rs index d7c955653e5..6088e0672e7 100644 --- a/packages/rs-drive/src/drive/shielded/nullifiers/queries.rs +++ b/packages/rs-drive/src/drive/shielded/nullifiers/queries.rs @@ -1,4 +1,4 @@ -use crate::drive::shielded::paths::SHIELDED_CREDIT_POOL_KEY; +use crate::drive::shielded::paths::MAIN_SHIELDED_CREDIT_POOL_KEY; use crate::drive::RootTree; // Byte positions chosen to balance the parent shielded-pool Merk tree — @@ -28,56 +28,56 @@ pub const SHIELDED_NULLIFIERS_EXPIRATION_TIME_KEY: &[u8; 1] = &[240]; /// The subtree key for nullifiers expiration time storage as u8. pub const SHIELDED_NULLIFIERS_EXPIRATION_TIME_KEY_U8: u8 = 240; -/// Path to per-block nullifiers: [AddressBalances, "s", [160]] +/// Path to per-block nullifiers: [ShieldedBalances, "M", [160]] pub fn shielded_recent_nullifiers_path() -> [&'static [u8]; 3] { [ - Into::<&[u8; 1]>::into(RootTree::AddressBalances), - SHIELDED_CREDIT_POOL_KEY, + Into::<&[u8; 1]>::into(RootTree::ShieldedBalances), + MAIN_SHIELDED_CREDIT_POOL_KEY, SHIELDED_RECENT_NULLIFIERS_KEY, ] } -/// Path to per-block nullifiers as vec: [AddressBalances, "s", [160]] +/// Path to per-block nullifiers as vec: [ShieldedBalances, "M", [160]] pub fn shielded_recent_nullifiers_path_vec() -> Vec> { vec![ - vec![RootTree::AddressBalances as u8], - SHIELDED_CREDIT_POOL_KEY.to_vec(), + vec![RootTree::ShieldedBalances as u8], + MAIN_SHIELDED_CREDIT_POOL_KEY.to_vec(), vec![SHIELDED_RECENT_NULLIFIERS_KEY_U8], ] } -/// Path to compacted nullifiers: [AddressBalances, "s", [224]] +/// Path to compacted nullifiers: [ShieldedBalances, "M", [224]] pub fn shielded_compacted_nullifiers_path() -> [&'static [u8]; 3] { [ - Into::<&[u8; 1]>::into(RootTree::AddressBalances), - SHIELDED_CREDIT_POOL_KEY, + Into::<&[u8; 1]>::into(RootTree::ShieldedBalances), + MAIN_SHIELDED_CREDIT_POOL_KEY, SHIELDED_COMPACTED_NULLIFIERS_KEY, ] } -/// Path to compacted nullifiers as vec: [AddressBalances, "s", [224]] +/// Path to compacted nullifiers as vec: [ShieldedBalances, "M", [224]] pub fn shielded_compacted_nullifiers_path_vec() -> Vec> { vec![ - vec![RootTree::AddressBalances as u8], - SHIELDED_CREDIT_POOL_KEY.to_vec(), + vec![RootTree::ShieldedBalances as u8], + MAIN_SHIELDED_CREDIT_POOL_KEY.to_vec(), vec![SHIELDED_COMPACTED_NULLIFIERS_KEY_U8], ] } -/// Path to nullifiers expiration time: [AddressBalances, "s", [240]] +/// Path to nullifiers expiration time: [ShieldedBalances, "M", [240]] pub fn shielded_nullifiers_expiration_time_path() -> [&'static [u8]; 3] { [ - Into::<&[u8; 1]>::into(RootTree::AddressBalances), - SHIELDED_CREDIT_POOL_KEY, + Into::<&[u8; 1]>::into(RootTree::ShieldedBalances), + MAIN_SHIELDED_CREDIT_POOL_KEY, SHIELDED_NULLIFIERS_EXPIRATION_TIME_KEY, ] } -/// Path to nullifiers expiration time as vec: [AddressBalances, "s", [240]] +/// Path to nullifiers expiration time as vec: [ShieldedBalances, "M", [240]] pub fn shielded_nullifiers_expiration_time_path_vec() -> Vec> { vec![ - vec![RootTree::AddressBalances as u8], - SHIELDED_CREDIT_POOL_KEY.to_vec(), + vec![RootTree::ShieldedBalances as u8], + MAIN_SHIELDED_CREDIT_POOL_KEY.to_vec(), vec![SHIELDED_NULLIFIERS_EXPIRATION_TIME_KEY_U8], ] } diff --git a/packages/rs-drive/src/drive/shielded/nullifiers/store_nullifiers/v0/mod.rs b/packages/rs-drive/src/drive/shielded/nullifiers/store_nullifiers/v0/mod.rs index add2c1d9dc1..cb282da0856 100644 --- a/packages/rs-drive/src/drive/shielded/nullifiers/store_nullifiers/v0/mod.rs +++ b/packages/rs-drive/src/drive/shielded/nullifiers/store_nullifiers/v0/mod.rs @@ -117,7 +117,13 @@ impl Drive { &platform_version.drive, )?; - if let Some(Element::CountSumTree(_, count, sum, _)) = tree_element { + // The recent-nullifiers subtree is wrapped in `Element::NotSummed` so + // that its sum side does not propagate into the enclosing shielded + // pool SumTree. `underlying()` peels the wrapper so we can read the + // inner CountSumTree's own (count, sum) for the compaction trigger. + if let Some(Element::CountSumTree(_, count, sum, _)) = + tree_element.as_ref().map(|e| e.underlying()) + { let max_blocks = platform_version .drive .methods @@ -131,8 +137,8 @@ impl Drive { // Check if either threshold would be exceeded after adding the current block // count + 1 for the new block, sum + current nullifiers count - let new_count = count + 1; - let new_sum = sum + nullifiers.len() as i64; + let new_count = *count + 1; + let new_sum = *sum + nullifiers.len() as i64; if new_count >= max_blocks || new_sum >= max_nullifiers { // Trigger compaction, including the current block's nullifiers diff --git a/packages/rs-drive/src/drive/shielded/paths.rs b/packages/rs-drive/src/drive/shielded/paths.rs index 0f02c083c42..94717364c33 100644 --- a/packages/rs-drive/src/drive/shielded/paths.rs +++ b/packages/rs-drive/src/drive/shielded/paths.rs @@ -1,11 +1,11 @@ use crate::drive::RootTree; use grovedb::{PathQuery, Query, SizedQuery}; -/// The subtree key for the shielded credit pool under AddressBalances -pub const SHIELDED_CREDIT_POOL_KEY: &[u8; 1] = b"s"; +/// The subtree key for the shielded credit pool under ShieldedBalances +pub const MAIN_SHIELDED_CREDIT_POOL_KEY: &[u8; 1] = b"M"; /// The subtree key for the shielded credit pool as a u8 -pub const SHIELDED_CREDIT_POOL_KEY_U8: u8 = b's'; +pub const MAIN_SHIELDED_CREDIT_POOL_KEY_U8: u8 = b'M'; // The eight subtree keys of the shielded credit pool are placed at evenly-spaced // byte positions across [0, 255] so that GroveDB's AVL-balanced parent tree @@ -69,90 +69,90 @@ pub const SHIELDED_ANCHORS_IN_POOL_KEY: u8 = 192; /// Chunk power for the notes CommitmentTree (2^11 = 2048 items per chunk) pub const SHIELDED_NOTES_CHUNK_POWER: u8 = 11; -/// Path to the shielded credit pool: [AddressBalances, "s"] +/// Path to the shielded credit pool: [ShieldedBalances, "M"] pub fn shielded_credit_pool_path() -> [&'static [u8]; 2] { [ - Into::<&[u8; 1]>::into(RootTree::AddressBalances), - SHIELDED_CREDIT_POOL_KEY, + Into::<&[u8; 1]>::into(RootTree::ShieldedBalances), + MAIN_SHIELDED_CREDIT_POOL_KEY, ] } /// Path to the shielded credit pool as a vec pub fn shielded_credit_pool_path_vec() -> Vec> { vec![ - vec![RootTree::AddressBalances as u8], - SHIELDED_CREDIT_POOL_KEY.to_vec(), + vec![RootTree::ShieldedBalances as u8], + MAIN_SHIELDED_CREDIT_POOL_KEY.to_vec(), ] } -/// Path to the notes tree: [AddressBalances, "s", [128]] +/// Path to the notes tree: [ShieldedBalances, "M", [128]] pub fn shielded_credit_pool_notes_path() -> [&'static [u8]; 3] { [ - Into::<&[u8; 1]>::into(RootTree::AddressBalances), - SHIELDED_CREDIT_POOL_KEY, + Into::<&[u8; 1]>::into(RootTree::ShieldedBalances), + MAIN_SHIELDED_CREDIT_POOL_KEY, &[SHIELDED_NOTES_KEY], ] } -/// Path to the notes tree as a vec: [AddressBalances, "s", [128]] +/// Path to the notes tree as a vec: [ShieldedBalances, "M", [128]] pub fn shielded_credit_pool_notes_path_vec() -> Vec> { vec![ - vec![RootTree::AddressBalances as u8], - SHIELDED_CREDIT_POOL_KEY.to_vec(), + vec![RootTree::ShieldedBalances as u8], + MAIN_SHIELDED_CREDIT_POOL_KEY.to_vec(), vec![SHIELDED_NOTES_KEY], ] } -/// Path to the nullifiers tree: [AddressBalances, "s", [64]] +/// Path to the nullifiers tree: [ShieldedBalances, "M", [64]] pub fn shielded_credit_pool_nullifiers_path() -> [&'static [u8]; 3] { [ - Into::<&[u8; 1]>::into(RootTree::AddressBalances), - SHIELDED_CREDIT_POOL_KEY, + Into::<&[u8; 1]>::into(RootTree::ShieldedBalances), + MAIN_SHIELDED_CREDIT_POOL_KEY, &[SHIELDED_NULLIFIERS_KEY], ] } -/// Path to the nullifiers tree as a vec: [AddressBalances, "s", [64]] +/// Path to the nullifiers tree as a vec: [ShieldedBalances, "M", [64]] pub fn shielded_credit_pool_nullifiers_path_vec() -> Vec> { vec![ - vec![RootTree::AddressBalances as u8], - SHIELDED_CREDIT_POOL_KEY.to_vec(), + vec![RootTree::ShieldedBalances as u8], + MAIN_SHIELDED_CREDIT_POOL_KEY.to_vec(), vec![SHIELDED_NULLIFIERS_KEY], ] } -/// Path to the anchors tree: [AddressBalances, "s", [192]] +/// Path to the anchors tree: [ShieldedBalances, "M", [192]] pub fn shielded_credit_pool_anchors_path() -> [&'static [u8]; 3] { [ - Into::<&[u8; 1]>::into(RootTree::AddressBalances), - SHIELDED_CREDIT_POOL_KEY, + Into::<&[u8; 1]>::into(RootTree::ShieldedBalances), + MAIN_SHIELDED_CREDIT_POOL_KEY, &[SHIELDED_ANCHORS_IN_POOL_KEY], ] } -/// Path to the anchors tree as a vec: [AddressBalances, "s", [192]] +/// Path to the anchors tree as a vec: [ShieldedBalances, "M", [192]] pub fn shielded_credit_pool_anchors_path_vec() -> Vec> { vec![ - vec![RootTree::AddressBalances as u8], - SHIELDED_CREDIT_POOL_KEY.to_vec(), + vec![RootTree::ShieldedBalances as u8], + MAIN_SHIELDED_CREDIT_POOL_KEY.to_vec(), vec![SHIELDED_ANCHORS_IN_POOL_KEY], ] } -/// Path to the anchors-by-height tree: [AddressBalances, "s", [96]] +/// Path to the anchors-by-height tree: [ShieldedBalances, "M", [96]] pub fn shielded_credit_pool_anchors_by_height_path() -> [&'static [u8]; 3] { [ - Into::<&[u8; 1]>::into(RootTree::AddressBalances), - SHIELDED_CREDIT_POOL_KEY, + Into::<&[u8; 1]>::into(RootTree::ShieldedBalances), + MAIN_SHIELDED_CREDIT_POOL_KEY, &[SHIELDED_ANCHORS_BY_HEIGHT_KEY], ] } -/// Path to the anchors-by-height tree as a vec: [AddressBalances, "s", [96]] +/// Path to the anchors-by-height tree as a vec: [ShieldedBalances, "M", [96]] pub fn shielded_credit_pool_anchors_by_height_path_vec() -> Vec> { vec![ - vec![RootTree::AddressBalances as u8], - SHIELDED_CREDIT_POOL_KEY.to_vec(), + vec![RootTree::ShieldedBalances as u8], + MAIN_SHIELDED_CREDIT_POOL_KEY.to_vec(), vec![SHIELDED_ANCHORS_BY_HEIGHT_KEY], ] } @@ -192,7 +192,7 @@ pub fn shielded_latest_recorded_anchor_path_query() -> PathQuery { /// Resolves the nullifiers path based on pool type. /// /// Pool types: -/// - 0: Main credit shielded pool → `[AddressBalances, "s", [64]]` +/// - 0: Main credit shielded pool → `[ShieldedBalances, "M", [64]]` /// - 1: Main token shielded pool (not yet implemented) /// - 2: Individual token shielded pool (not yet implemented, requires pool_identifier) pub fn nullifiers_path_for_pool( diff --git a/packages/rs-drive/src/util/batch/grovedb_op_batch/mod.rs b/packages/rs-drive/src/util/batch/grovedb_op_batch/mod.rs index eaf298f2ffa..7e0c7e9ec8a 100644 --- a/packages/rs-drive/src/util/batch/grovedb_op_batch/mod.rs +++ b/packages/rs-drive/src/util/batch/grovedb_op_batch/mod.rs @@ -72,6 +72,7 @@ enum KnownPath { VotesRoot, //Level 1 GroupActionsRoot, //Level 1 SingleUseKeyBalancesRoot, //Level 1 + ShieldedBalancesRoot, //Level 1 } impl From for KnownPath { @@ -97,6 +98,7 @@ impl From for KnownPath { RootTree::Votes => KnownPath::VotesRoot, RootTree::GroupActions => KnownPath::GroupActionsRoot, RootTree::AddressBalances => KnownPath::SingleUseKeyBalancesRoot, + RootTree::ShieldedBalances => KnownPath::ShieldedBalancesRoot, } } } diff --git a/packages/rs-drive/src/verify/shielded/verify_compacted_nullifier_changes/v0/mod.rs b/packages/rs-drive/src/verify/shielded/verify_compacted_nullifier_changes/v0/mod.rs index 0694c486776..1bc888258ce 100644 --- a/packages/rs-drive/src/verify/shielded/verify_compacted_nullifier_changes/v0/mod.rs +++ b/packages/rs-drive/src/verify/shielded/verify_compacted_nullifier_changes/v0/mod.rs @@ -2,7 +2,7 @@ use crate::drive::shielded::nullifiers::queries::{ shielded_compacted_nullifiers_path_vec, SHIELDED_COMPACTED_NULLIFIERS_KEY_U8, }; use crate::drive::shielded::nullifiers::types::{CompactedNullifierChange, CompactedNullifiers}; -use crate::drive::shielded::paths::SHIELDED_CREDIT_POOL_KEY_U8; +use crate::drive::shielded::paths::MAIN_SHIELDED_CREDIT_POOL_KEY_U8; use crate::drive::Drive; use crate::drive::RootTree; use crate::error::proof::ProofError; @@ -79,9 +79,9 @@ impl Drive { }; // Navigate to the compacted nullifiers layer - // Path: AddressBalances -> SHIELDED_CREDIT_POOL_KEY -> CompactedNullifiers ('o') - let address_balances_key = vec![RootTree::AddressBalances as u8]; - let pool_key = vec![SHIELDED_CREDIT_POOL_KEY_U8]; + // Path: ShieldedBalances -> MAIN_SHIELDED_CREDIT_POOL_KEY -> CompactedNullifiers ('o') + let shielded_balances_key = vec![RootTree::ShieldedBalances as u8]; + let pool_key = vec![MAIN_SHIELDED_CREDIT_POOL_KEY_U8]; let compacted_key = vec![SHIELDED_COMPACTED_NULLIFIERS_KEY_U8]; // Extract KV entries from the compacted layer's merk proof to find @@ -93,7 +93,7 @@ impl Drive { let compacted_layer = v0 .root_layer .lower_layers - .get(&address_balances_key) + .get(&shielded_balances_key) .and_then(|layer| layer.lower_layers.get(&pool_key)) .and_then(|layer| layer.lower_layers.get(&compacted_key)); compacted_layer @@ -105,7 +105,7 @@ impl Drive { let compacted_layer = v1 .root_layer .lower_layers - .get(&address_balances_key) + .get(&shielded_balances_key) .and_then(|layer| layer.lower_layers.get(&pool_key)) .and_then(|layer| layer.lower_layers.get(&compacted_key)); compacted_layer diff --git a/packages/rs-drive/src/verify/shielded/verify_most_recent_shielded_anchor/v0/mod.rs b/packages/rs-drive/src/verify/shielded/verify_most_recent_shielded_anchor/v0/mod.rs index efebc103079..8889de59908 100644 --- a/packages/rs-drive/src/verify/shielded/verify_most_recent_shielded_anchor/v0/mod.rs +++ b/packages/rs-drive/src/verify/shielded/verify_most_recent_shielded_anchor/v0/mod.rs @@ -65,8 +65,8 @@ impl Drive { mod tests { use super::*; use crate::drive::shielded::paths::{ - shielded_credit_pool_anchors_by_height_path_vec, SHIELDED_ANCHORS_BY_HEIGHT_KEY, - SHIELDED_CREDIT_POOL_KEY, + shielded_credit_pool_anchors_by_height_path_vec, MAIN_SHIELDED_CREDIT_POOL_KEY, + SHIELDED_ANCHORS_BY_HEIGHT_KEY, }; use crate::drive::RootTree; use crate::util::batch::grovedb_op_batch::GroveDbOpBatchV0Methods; @@ -82,8 +82,8 @@ mod tests { platform_version: &PlatformVersion, ) { let by_height_path = vec![ - vec![RootTree::AddressBalances as u8], - SHIELDED_CREDIT_POOL_KEY.to_vec(), + vec![RootTree::ShieldedBalances as u8], + MAIN_SHIELDED_CREDIT_POOL_KEY.to_vec(), vec![SHIELDED_ANCHORS_BY_HEIGHT_KEY], ]; let op = QualifiedGroveDbOp::insert_only_known_to_not_already_exist_op( 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 e48bd1b0fa8..e40b02861b3 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 @@ -38,7 +38,7 @@ impl Drive { let limit = effective.min(u16::MAX as u32) as u16; // PathQuery must match the server-side proof generation exactly: - // path = [AddressBalances, "s"], key = [SHIELDED_NOTES_KEY], + // path = [ShieldedBalances, "M"], key = [SHIELDED_NOTES_KEY], // subquery = range_inclusive(start..=end) let end_index = start_index + limit as u64 - 1; let mut inner_query = Query::new(); diff --git a/packages/rs-platform-version/Cargo.toml b/packages/rs-platform-version/Cargo.toml index ff802beb856..0796b3263b3 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 = "8f25b20d04bfc0e8bdfb3870676d647a0d74918b" } +grovedb-version = { git = "https://github.com/dashpay/grovedb", rev = "dbd83dce59fbcf2866e9dd06be4ce6c320e37908" } [features] mock-versions = [] diff --git a/packages/rs-platform-version/src/version/drive_versions/v7.rs b/packages/rs-platform-version/src/version/drive_versions/v7.rs index 50ec75a550d..213f4197c2e 100644 --- a/packages/rs-platform-version/src/version/drive_versions/v7.rs +++ b/packages/rs-platform-version/src/version/drive_versions/v7.rs @@ -50,7 +50,7 @@ pub const DRIVE_VERSION_V7: DriveVersion = DriveVersion { add_to_system_credits_operations: 0, remove_from_system_credits: 0, remove_from_system_credits_operations: 0, - calculate_total_credits_balance: 1, + calculate_total_credits_balance: 2, // ShieldedBalances root tree adds a fifth term to the equation }, document: DRIVE_DOCUMENT_METHOD_VERSIONS_V2, vote: DRIVE_VOTE_METHOD_VERSIONS_V2, diff --git a/packages/rs-platform-wallet/Cargo.toml b/packages/rs-platform-wallet/Cargo.toml index 05e5eb0547c..9d9bdae0377 100644 --- a/packages/rs-platform-wallet/Cargo.toml +++ b/packages/rs-platform-wallet/Cargo.toml @@ -48,7 +48,7 @@ image = { version = "0.25", default-features = false, features = ["png", "jpeg", zeroize = "1" # Shielded pool (optional, behind `shielded` feature) -grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "8f25b20d04bfc0e8bdfb3870676d647a0d74918b", optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "dbd83dce59fbcf2866e9dd06be4ce6c320e37908", optional = true } zip32 = { version = "0.2.0", default-features = false, optional = true } [dev-dependencies] 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 432c4353d08..77766fc2e71 100644 --- a/packages/rs-sdk-ffi/src/system/queries/path_elements.rs +++ b/packages/rs-sdk-ffi/src/system/queries/path_elements.rs @@ -134,60 +134,11 @@ fn get_path_elements( .iter() .filter_map(|(key, element_opt)| { element_opt.as_ref().map(|element| { - let element_data = match element { - Element::Item(data, _) => hex::encode(data), - Element::Reference(reference, _, _) => format!("{:?}", reference), - Element::Tree(_, _) => "tree".to_string(), - Element::SumTree(_, _, _) => "sum_tree".to_string(), - Element::SumItem(value, _) => format!("sum_item:{}", value), - Element::BigSumTree(_, value, _) => { - format!("big_sum_tree:{}", value) - } - Element::CountTree(_, count, _) => format!("count_tree:{}", count), - Element::CountSumTree(_, count, sum, _) => { - format!("count_sum_tree:{}:{}", count, sum) - } - Element::ItemWithSumItem(data, sum, _) => { - format!("item_with_sum_item:{}:{}", hex::encode(data), sum) - } - Element::ProvableCountTree(_, count, _) => { - format!("provable_count_tree:{}", count) - } - Element::ProvableCountSumTree(_, count, sum, _) => { - format!("provable_count_sum_tree:{}:{}", count, sum) - } - Element::CommitmentTree(_, _, _) => "commitment_tree".to_string(), - Element::MmrTree(_, _) => "mmr_tree".to_string(), - Element::BulkAppendTree(_, _, _) => "bulk_append_tree".to_string(), - Element::DenseAppendOnlyFixedSizeTree(_, _, _) => { - "dense_append_only_fixed_size_tree".to_string() - } - }; - format!( r#"{{"key":"{}","element":"{}","type":"{}"}}"#, hex::encode(key), - element_data, - match element { - Element::Item(_, _) => "item", - Element::Reference(_, _, _) => "reference", - Element::Tree(_, _) => "tree", - Element::SumTree(_, _, _) => "sum_tree", - Element::SumItem(_, _) => "sum_item", - Element::BigSumTree(_, _, _) => "big_sum_tree", - Element::CountTree(_, _, _) => "count_tree", - Element::CountSumTree(_, _, _, _) => "count_sum_tree", - Element::ItemWithSumItem(_, _, _) => "item_with_sum_item", - Element::ProvableCountTree(_, _, _) => "provable_count_tree", - Element::ProvableCountSumTree(_, _, _, _) => { - "provable_count_sum_tree" - } - Element::CommitmentTree(_, _, _) => "commitment_tree", - Element::MmrTree(_, _) => "mmr_tree", - Element::BulkAppendTree(_, _, _) => "bulk_append_tree", - Element::DenseAppendOnlyFixedSizeTree(_, _, _) => - "dense_append_only_fixed_size_tree", - } + format_element_data(element), + format_element_type(element), ) }) }) @@ -200,6 +151,66 @@ fn get_path_elements( }) } +/// Renders the data side of an `Element` for the JSON `element` field. +/// +/// `NonCounted` and `NotSummed` are transparent wrappers around another +/// element; we render them as `non_counted()` / `not_summed()` +/// so that the wrapped element's value is still visible to the caller. +fn format_element_data(element: &Element) -> String { + match element { + Element::Item(data, _) => hex::encode(data), + Element::Reference(reference, _, _) => format!("{:?}", reference), + Element::Tree(_, _) => "tree".to_string(), + Element::SumTree(_, _, _) => "sum_tree".to_string(), + Element::SumItem(value, _) => format!("sum_item:{}", value), + Element::BigSumTree(_, value, _) => format!("big_sum_tree:{}", value), + Element::CountTree(_, count, _) => format!("count_tree:{}", count), + Element::CountSumTree(_, count, sum, _) => { + format!("count_sum_tree:{}:{}", count, sum) + } + Element::ItemWithSumItem(data, sum, _) => { + format!("item_with_sum_item:{}:{}", hex::encode(data), sum) + } + Element::ProvableCountTree(_, count, _) => format!("provable_count_tree:{}", count), + Element::ProvableCountSumTree(_, count, sum, _) => { + format!("provable_count_sum_tree:{}:{}", count, sum) + } + Element::CommitmentTree(_, _, _) => "commitment_tree".to_string(), + Element::MmrTree(_, _) => "mmr_tree".to_string(), + Element::BulkAppendTree(_, _, _) => "bulk_append_tree".to_string(), + Element::DenseAppendOnlyFixedSizeTree(_, _, _) => { + "dense_append_only_fixed_size_tree".to_string() + } + Element::NonCounted(inner) => format!("non_counted({})", format_element_data(inner)), + Element::NotSummed(inner) => format!("not_summed({})", format_element_data(inner)), + } +} + +/// Renders the variant name of an `Element` for the JSON `type` field. +fn format_element_type(element: &Element) -> String { + match element { + Element::Item(_, _) => "item".to_string(), + Element::Reference(_, _, _) => "reference".to_string(), + Element::Tree(_, _) => "tree".to_string(), + Element::SumTree(_, _, _) => "sum_tree".to_string(), + Element::SumItem(_, _) => "sum_item".to_string(), + Element::BigSumTree(_, _, _) => "big_sum_tree".to_string(), + Element::CountTree(_, _, _) => "count_tree".to_string(), + Element::CountSumTree(_, _, _, _) => "count_sum_tree".to_string(), + Element::ItemWithSumItem(_, _, _) => "item_with_sum_item".to_string(), + Element::ProvableCountTree(_, _, _) => "provable_count_tree".to_string(), + Element::ProvableCountSumTree(_, _, _, _) => "provable_count_sum_tree".to_string(), + Element::CommitmentTree(_, _, _) => "commitment_tree".to_string(), + Element::MmrTree(_, _) => "mmr_tree".to_string(), + Element::BulkAppendTree(_, _, _) => "bulk_append_tree".to_string(), + Element::DenseAppendOnlyFixedSizeTree(_, _, _) => { + "dense_append_only_fixed_size_tree".to_string() + } + Element::NonCounted(inner) => format!("non_counted({})", format_element_type(inner)), + Element::NotSummed(inner) => format!("not_summed({})", format_element_type(inner)), + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/packages/rs-sdk/Cargo.toml b/packages/rs-sdk/Cargo.toml index 8cb766a4d2c..77b7583c65f 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 = "8f25b20d04bfc0e8bdfb3870676d647a0d74918b", features = ["client", "sqlite"], optional = true } +grovedb-commitment-tree = { git = "https://github.com/dashpay/grovedb", rev = "dbd83dce59fbcf2866e9dd06be4ce6c320e37908", features = ["client", "sqlite"], optional = true } dash-async = { path = "../rs-dash-async" } dash-context-provider = { path = "../rs-context-provider", default-features = false } dash-platform-macros = { path = "../rs-dash-platform-macros" } diff --git a/packages/wasm-drive-verify/src/state_transition/state_transition_execution_path_queries/token_transition.rs b/packages/wasm-drive-verify/src/state_transition/state_transition_execution_path_queries/token_transition.rs index 85127464863..d78d63eb34b 100644 --- a/packages/wasm-drive-verify/src/state_transition/state_transition_execution_path_queries/token_transition.rs +++ b/packages/wasm-drive-verify/src/state_transition/state_transition_execution_path_queries/token_transition.rs @@ -420,6 +420,11 @@ fn serialize_query_item(item: &QueryItem) -> Result { ) .map_err(|_| JsValue::from_str("Failed to set endInclusive"))?; } + QueryItem::AggregateCountOnRange(_) => { + return Err(JsValue::from_str( + "AggregateCountOnRange QueryItem is not supported in token-transition path queries", + )); + } } Ok(obj.into())