Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
219ba2e
feat: orchard zk
QuantumExplorer Feb 9, 2026
8957d80
fixes
QuantumExplorer Feb 10, 2026
44eaab5
clippy
QuantumExplorer Feb 10, 2026
164f375
more work
QuantumExplorer Feb 12, 2026
757a2be
more work
QuantumExplorer Feb 12, 2026
1c229f7
more work
QuantumExplorer Feb 12, 2026
4a05f07
more work
QuantumExplorer Feb 12, 2026
3c68b0c
more work
QuantumExplorer Feb 12, 2026
9b01260
fixes
QuantumExplorer Feb 12, 2026
3b89abc
fixes
QuantumExplorer Feb 12, 2026
da5fbc7
fixes
QuantumExplorer Feb 12, 2026
266b5e9
more work
QuantumExplorer Feb 12, 2026
c7f098a
more work
QuantumExplorer Feb 14, 2026
761344c
new trees
QuantumExplorer Feb 14, 2026
1db922b
more work
QuantumExplorer Feb 14, 2026
d659ed2
more work
QuantumExplorer Feb 15, 2026
26b2f30
more work
QuantumExplorer Feb 16, 2026
bf11591
more work
QuantumExplorer Feb 17, 2026
0f56726
more work
QuantumExplorer Feb 17, 2026
f7b295c
more work
QuantumExplorer Feb 17, 2026
8a88520
more work
QuantumExplorer Feb 17, 2026
66ea6e2
more work
QuantumExplorer Feb 18, 2026
d4d22af
more work
QuantumExplorer Feb 18, 2026
d5bec5b
more work
QuantumExplorer Feb 18, 2026
814331f
more work
QuantumExplorer Feb 18, 2026
2645f87
more work
QuantumExplorer Feb 18, 2026
a489864
more work
QuantumExplorer Feb 18, 2026
fcaab54
more work
QuantumExplorer Feb 18, 2026
dc8c250
more work
QuantumExplorer Feb 18, 2026
e2b960b
better dense fixed size tree
QuantumExplorer Feb 19, 2026
20e2b47
better dense fixed size tree
QuantumExplorer Feb 19, 2026
08a2d9e
more work
QuantumExplorer Feb 20, 2026
6ec4165
more work
QuantumExplorer Feb 22, 2026
e42425f
more work
QuantumExplorer Feb 22, 2026
e7de0cf
Merge branch 'develop' into feat/orchardZK
QuantumExplorer Feb 22, 2026
752c731
more work
QuantumExplorer Feb 22, 2026
72ec9d9
Merge branch 'develop' into feat/orchardZK
QuantumExplorer Feb 22, 2026
8841052
more work
QuantumExplorer Feb 22, 2026
4d778c6
Merge branch 'develop' into feat/orchardZK
QuantumExplorer Feb 23, 2026
679ea5a
fmt
QuantumExplorer Feb 23, 2026
56120fb
Merge branch 'develop' into feat/orchardZK
QuantumExplorer Feb 24, 2026
59c25a7
fmt
QuantumExplorer Feb 24, 2026
0df4c6b
test: add checkpoint ID uniqueness tests for commitment tree
QuantumExplorer Feb 25, 2026
ca972f7
more work
QuantumExplorer Feb 26, 2026
affb75f
more work
QuantumExplorer Feb 26, 2026
22a26d9
Merge branch 'develop' into feat/orchardZK
QuantumExplorer Feb 26, 2026
e1e3aab
refactor: split bloated GroveOp variants into dedicated non-Merk tree…
QuantumExplorer Feb 26, 2026
6315a60
more work
QuantumExplorer Feb 27, 2026
63e0f06
more work
QuantumExplorer Feb 27, 2026
6483e12
Merge branch 'develop' into feat/orchardZK
QuantumExplorer Feb 27, 2026
35c5fe6
test: add audit-driven tests for non-Merk tree types and proof genera…
QuantumExplorer Feb 27, 2026
9020fb2
fix: V1 proof SumItem/ItemWithSumItem + audit-driven quality fixes an…
QuantumExplorer Feb 27, 2026
9ce377f
more work
QuantumExplorer Feb 27, 2026
6f631e8
more work
QuantumExplorer Feb 27, 2026
3595fbe
more work
QuantumExplorer Feb 27, 2026
91accc8
more work
QuantumExplorer Feb 27, 2026
86b108d
more work
QuantumExplorer Feb 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ members = [
"grovedb-version",
"grovedb-epoch-based-storage-flags",
"grovedb-element",
"grovedb-commitment-tree",
"grovedb-merkle-mountain-range",
"grovedb-bulk-append-tree",
"grovedb-dense-fixed-sized-merkle-tree",
"grovedb-bulk-append-tree",
"grovedb-commitment-tree",
Expand Down
1 change: 1 addition & 0 deletions grovedb-commitment-tree/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ rusqlite = { version = "0.38", features = ["bundled"], optional = true }
grovedb-costs = { version = "4.0.0", path = "../costs" }
grovedb-storage = { version = "4.0.0", path = "../storage", optional = true }
grovedb-bulk-append-tree = { version = "4.0.0", path = "../grovedb-bulk-append-tree", optional = true }
blake3 = "1.8.1"
thiserror = "2.0"

[dev-dependencies]
Expand Down
19 changes: 15 additions & 4 deletions grovedb-commitment-tree/src/commitment_tree/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use orchard::{
zcash_note_encryption::note_bytes::NoteBytes,
};

use crate::{CommitmentFrontier, CommitmentTreeError};
use crate::{compute_commitment_tree_state_root, CommitmentFrontier, CommitmentTreeError};

mod tests;

Expand Down Expand Up @@ -356,11 +356,22 @@ impl<'db, S: StorageContext<'db>, M: MemoSize> CommitmentTree<S, M> {
self.bulk_tree.total_count
}

/// Compute the current BulkAppendTree state root without modification.
/// Compute the combined state root that binds the Sinsemilla anchor to the
/// BulkAppendTree data root.
///
/// Returns `blake3("ct_state" || sinsemilla_root || bulk_state_root)`.
/// This is the value that flows as the Merk child hash, ensuring both the
/// Orchard anchor and the bulk data are authenticated.
pub fn compute_current_state_root(&self) -> Result<[u8; 32], CommitmentTreeError> {
self.bulk_tree
let bulk_root = self
.bulk_tree
.compute_current_state_root()
.map_err(|e| CommitmentTreeError::InvalidData(format!("state root: {}", e)))
.map_err(|e| CommitmentTreeError::InvalidData(format!("state root: {}", e)))?;
let sinsemilla_root = self.frontier.root_hash();
Ok(compute_commitment_tree_state_root(
&sinsemilla_root,
&bulk_root,
))
}

/// Get a single value from the dense tree buffer by buffer-local position.
Expand Down
6 changes: 4 additions & 2 deletions grovedb-commitment-tree/src/commitment_tree/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -851,9 +851,11 @@ mod storage_tests {
let computed = ct
.compute_current_state_root()
.expect("state root should succeed");
let expected =
crate::compute_commitment_tree_state_root(&r.sinsemilla_root, &r.bulk_state_root);
assert_eq!(
computed, r.bulk_state_root,
"computed state root should match append result"
computed, expected,
"computed state root should match combined sinsemilla + bulk root"
);
}

Expand Down
19 changes: 19 additions & 0 deletions grovedb-commitment-tree/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,25 @@ pub use client::ClientPersistentCommitmentTree;
#[cfg(feature = "sqlite")]
pub use client::{SqliteShardStore, SqliteShardStoreError};
pub use commitment_frontier::*;
/// Compute the combined CommitmentTree state root that binds the Sinsemilla
/// anchor to the BulkAppendTree data root.
///
/// `ct_state_root = blake3("ct_state" || sinsemilla_root || bulk_state_root)`
///
/// This ensures both the Orchard-compatible anchor (authenticating cmx values)
/// and the BulkAppendTree root (authenticating cmx||payload entries) are
/// cryptographically bound to the GroveDB root hash.
pub fn compute_commitment_tree_state_root(
sinsemilla_root: &[u8; 32],
bulk_state_root: &[u8; 32],
) -> [u8; 32] {
let mut hasher = blake3::Hasher::new();
hasher.update(b"ct_state");
hasher.update(sinsemilla_root);
hasher.update(bulk_state_root);
*hasher.finalize().as_bytes()
}

#[cfg(feature = "storage")]
pub use commitment_tree::{
ciphertext_payload_size, deserialize_ciphertext, serialize_ciphertext, CommitmentAppendResult,
Expand Down
9 changes: 9 additions & 0 deletions grovedb-dense-fixed-sized-merkle-tree/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ pub(crate) fn validate_height(height: u8) -> Result<(), DenseMerkleError> {
///
/// All nodes use the same scheme — leaf nodes simply have `[0; 32]` for
/// both child hashes.
///
/// **No leaf/internal domain separation tag is needed here.** The tree
/// structure (`height` and `count`) is externally authenticated in the
/// parent `Element::DenseAppendOnlyFixedSizeTree`, which flows through
/// the Merk hierarchy. The verifier always knows exactly which positions
/// are leaves vs internal nodes, so an attacker cannot substitute one for
/// the other without breaking the parent authentication chain. Exploiting
/// the lack of a prefix byte would require a second-preimage attack on
/// blake3 (2^128 security), which is computationally infeasible.
pub(crate) fn node_hash(
value_hash: &[u8; 32],
left_hash: &[u8; 32],
Expand Down
72 changes: 72 additions & 0 deletions grovedb-element/src/element/constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,4 +288,76 @@ impl Element {
) -> Self {
Element::ProvableCountSumTree(maybe_root_key, count_value, sum_value, flags)
}

/// Set element to an empty commitment tree
pub fn empty_commitment_tree(chunk_power: u8) -> Self {
assert!(chunk_power <= 31, "chunk_power must be <= 31");
Element::CommitmentTree(0, chunk_power, None)
}

/// Set element to an empty commitment tree with flags
pub fn empty_commitment_tree_with_flags(chunk_power: u8, flags: Option<ElementFlags>) -> Self {
assert!(chunk_power <= 31, "chunk_power must be <= 31");
Element::CommitmentTree(0, chunk_power, flags)
}

/// Set element to a commitment tree with all fields
pub fn new_commitment_tree(
total_count: u64,
chunk_power: u8,
flags: Option<ElementFlags>,
) -> Self {
Element::CommitmentTree(total_count, chunk_power, flags)
}

/// Set element to an empty MMR tree
pub fn empty_mmr_tree() -> Self {
Element::MmrTree(0, None)
}

/// Set element to an empty MMR tree with flags
pub fn empty_mmr_tree_with_flags(flags: Option<ElementFlags>) -> Self {
Element::MmrTree(0, flags)
}

/// Set element to an MMR tree with the given size
pub fn new_mmr_tree(mmr_size: u64, flags: Option<ElementFlags>) -> Self {
Element::MmrTree(mmr_size, flags)
}

/// Set element to an empty bulk append tree without flags
pub fn empty_bulk_append_tree(chunk_power: u8) -> Self {
assert!(chunk_power <= 31, "chunk_power must be <= 31");
Element::BulkAppendTree(0, chunk_power, None)
}

/// Set element to an empty bulk append tree with flags
pub fn empty_bulk_append_tree_with_flags(chunk_power: u8, flags: Option<ElementFlags>) -> Self {
assert!(chunk_power <= 31, "chunk_power must be <= 31");
Element::BulkAppendTree(0, chunk_power, flags)
}

/// Set element to a bulk append tree with all fields
pub fn new_bulk_append_tree(
total_count: u64,
chunk_power: u8,
flags: Option<ElementFlags>,
) -> Self {
Element::BulkAppendTree(total_count, chunk_power, flags)
}

/// Set element to an empty dense tree without flags
pub fn empty_dense_tree(height: u8) -> Self {
Element::DenseAppendOnlyFixedSizeTree(0, height, None)
}

/// Set element to an empty dense tree with flags
pub fn empty_dense_tree_with_flags(height: u8, flags: Option<ElementFlags>) -> Self {
Element::DenseAppendOnlyFixedSizeTree(0, height, flags)
}

/// Set element to a dense tree with all fields
pub fn new_dense_tree(count: u16, height: u8, flags: Option<ElementFlags>) -> Self {
Element::DenseAppendOnlyFixedSizeTree(count, height, flags)
}
}
80 changes: 76 additions & 4 deletions grovedb-element/src/element/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,65 @@ impl Element {
| Element::CountSumTree(..)
| Element::ProvableCountTree(..)
| Element::ProvableCountSumTree(..)
| Element::CommitmentTree(..)
| Element::MmrTree(..)
| Element::BulkAppendTree(..)
| Element::DenseAppendOnlyFixedSizeTree(..)
)
}

/// Check if the element is a commitment tree
pub fn is_commitment_tree(&self) -> bool {
matches!(self, Element::CommitmentTree(..))
}

/// Check if the element is an MMR tree
pub fn is_mmr_tree(&self) -> bool {
matches!(self, Element::MmrTree(..))
}

/// Check if the element is a bulk append tree
pub fn is_bulk_append_tree(&self) -> bool {
matches!(self, Element::BulkAppendTree(..))
}

/// Check if the element is a dense append-only fixed-size tree
pub fn is_dense_tree(&self) -> bool {
matches!(self, Element::DenseAppendOnlyFixedSizeTree(..))
}

/// Check if the element is a tree type that stores data in the data
/// namespace as non-Merk entries. These tree types have an always-empty
/// Merk (root_key = None) and never contain child subtrees. The data
/// namespace must be cleared directly rather than iterated as Merk
/// elements.
///
/// Note: This must be kept in sync with
/// `TreeType::uses_non_merk_data_storage()` in the merk crate.
pub fn uses_non_merk_data_storage(&self) -> bool {
matches!(
self,
Element::CommitmentTree(..)
| Element::MmrTree(..)
| Element::BulkAppendTree(..)
| Element::DenseAppendOnlyFixedSizeTree(..)
)
}

/// Returns the entry count for non-Merk data tree types, or `None` for
/// regular Merk trees and non-tree elements. This is used by delete
/// and is_empty_tree operations to determine emptiness without
/// iterating the data namespace.
pub fn non_merk_entry_count(&self) -> Option<u64> {
match self {
Element::CommitmentTree(count, ..) => Some(*count),
Element::MmrTree(mmr_size, _) => Some(*mmr_size),
Element::BulkAppendTree(count, ..) => Some(*count),
Element::DenseAppendOnlyFixedSizeTree(count, ..) => Some(*count as u64),
_ => None,
}
}

/// Check if the element is a reference
pub fn is_reference(&self) -> bool {
matches!(self, Element::Reference(..))
Expand Down Expand Up @@ -202,7 +258,11 @@ impl Element {
| Element::CountSumTree(.., flags)
| Element::ProvableCountTree(.., flags)
| Element::ProvableCountSumTree(.., flags)
| Element::ItemWithSumItem(.., flags) => flags,
| Element::ItemWithSumItem(.., flags)
| Element::CommitmentTree(.., flags)
| Element::MmrTree(.., flags)
| Element::BulkAppendTree(.., flags)
| Element::DenseAppendOnlyFixedSizeTree(.., flags) => flags,
}
}

Expand All @@ -219,7 +279,11 @@ impl Element {
| Element::CountSumTree(.., flags)
| Element::ProvableCountTree(.., flags)
| Element::ProvableCountSumTree(.., flags)
| Element::ItemWithSumItem(.., flags) => flags,
| Element::ItemWithSumItem(.., flags)
| Element::CommitmentTree(.., flags)
| Element::MmrTree(.., flags)
| Element::BulkAppendTree(.., flags)
| Element::DenseAppendOnlyFixedSizeTree(.., flags) => flags,
}
}

Expand All @@ -236,7 +300,11 @@ impl Element {
| Element::CountSumTree(.., flags)
| Element::ProvableCountTree(.., flags)
| Element::ProvableCountSumTree(.., flags)
| Element::ItemWithSumItem(.., flags) => flags,
| Element::ItemWithSumItem(.., flags)
| Element::CommitmentTree(.., flags)
| Element::MmrTree(.., flags)
| Element::BulkAppendTree(.., flags)
| Element::DenseAppendOnlyFixedSizeTree(.., flags) => flags,
}
}

Expand All @@ -253,7 +321,11 @@ impl Element {
| Element::CountSumTree(.., flags)
| Element::ProvableCountTree(.., flags)
| Element::ProvableCountSumTree(.., flags)
| Element::ItemWithSumItem(.., flags) => *flags = new_flags,
| Element::ItemWithSumItem(.., flags)
| Element::CommitmentTree(.., flags)
| Element::MmrTree(.., flags)
| Element::BulkAppendTree(.., flags)
| Element::DenseAppendOnlyFixedSizeTree(.., flags) => *flags = new_flags,
}
}

Expand Down
Loading
Loading