Skip to content

feat: SmtStorageReader and SparseMerkleTreeReader traits#967

Merged
bobbinth merged 32 commits intonextfrom
sergerad-largesmt-reader-trait
May 1, 2026
Merged

feat: SmtStorageReader and SparseMerkleTreeReader traits#967
bobbinth merged 32 commits intonextfrom
sergerad-largesmt-reader-trait

Conversation

@sergerad
Copy link
Copy Markdown
Contributor

@sergerad sergerad commented Apr 14, 2026

Description

Extract a new SmtStorageReader trait from SmtStorage, enabling LargeSmt<S> to work with read-only storage for read operations, and only require SmtStorage for mutations.

Extract a new SparseMerkleTreeReader trait from SparseMerkleTree, enabling LargeSmt<S> to implement read-only trait methods with only an S: SmtStorageReader bound.

Motivation

Previously, LargeSmt<S: SmtStorage> required full read+write storage for all operations, including purely read-only ones like get_leaf, get_value, open, and tree loading. This made it impossible to use LargeSmt with a read-only storage backend.

The SparseMerkleTree trait bundled read and write methods together, so impl SparseMerkleTree for LargeSmt<S> required S: SmtStorage. Splitting the trait allows LargeSmt to implement read-only methods with only the SmtStorageReader bound.

In miden-node (PR), we are working towards removing synchronization mechanisms by providing read-only types which can be cloned.

Changes

Storage trait hierarchy (storage/mod.rs):

  • SmtStorageReader&self methods (leaf/subtree reads, iterators, get_depth24)
  • SmtStorage: SmtStorageReader&mut self methods (inserts, removes, apply)

SMT trait hierarchy (smt/mod.rs):

  • SparseMerkleTreeReader — read-only methods (root, get_inner_node, get_value, get_leaf, open, get_path, compute_mutations, etc.)
  • SparseMerkleTree: SparseMerkleTreeReader — write methods (set_root, insert_inner_node, remove_inner_node, insert_value, insert, apply_mutations, etc.)
  • from_raw_parts removed from the trait and inlined into the inherent methods on Smt and SimpleSmt, since LargeSmt could never meaningfully implement it.

LargeSmt<S> struct bound relaxed (large/mod.rs):

  • Struct bound changed from S: SmtStorage to S: SmtStorageReader
  • Impl blocks split by required capability:
    • impl<S: SmtStorageReader> — accessors (root, get_leaf, get_value, open, is_empty), iterators, construction from existing storage (new, load, load_with_root)
    • impl<S: SmtStorage>insert, with_entries, batch construction methods

LargeSmt<S> trait impls split (large/smt_trait.rs):

  • impl<S: SmtStorageReader> SparseMerkleTreeReader for LargeSmt<S> — all read-only trait methods
  • impl<S: SmtStorage> SparseMerkleTree for LargeSmt<S> — write methods only

Other files updated:

  • Smt and SimpleSmt — trait impls split into SparseMerkleTreeReader + SparseMerkleTree
  • batch_ops.rs — read-only qualified calls updated to SparseMerkleTreeReader
  • construction.rs — split into Reader (load from storage) and Storage (build with entries) blocks
  • iter.rs — bounded by SmtStorageReader
  • memory.rs, rocksdb.rs — split impl SmtStorage into impl SmtStorageReader + impl SmtStorage
  • smt/mod.rs — re-exports SmtStorageReader and SparseMerkleTreeReader

Backwards compatibility

SmtStorage retains all original methods (read + write) via its supertrait SmtStorageReader, so existing code using S: SmtStorage continues to compile unchanged.

Both SparseMerkleTree and SparseMerkleTreeReader are pub(crate), so the trait split has no public API impact. from_raw_parts was only ever called through the inherent methods on Smt and SimpleSmt, which retain the same signature.

@sergerad sergerad changed the title Sergerad largesmt reader trait feat: SmtStorageReader trait Apr 14, 2026
Comment thread miden-crypto/src/merkle/smt/large_forest/mod.rs
Copy link
Copy Markdown
Collaborator

@iamrecursion iamrecursion left a comment

Choose a reason for hiding this comment

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

This seems reasonable at first glance, but I am concerned about the code that has been manually inlined from the SparseMerkleTree trait.

Also, what is your plan with regards to the fact that this PR is targeted for main? These changes should also be included on next in my view.

Comment thread miden-crypto/src/merkle/smt/large/storage/mod.rs Outdated
Comment thread miden-crypto/src/merkle/smt/large/storage/rocksdb.rs
Comment thread miden-crypto/src/merkle/smt/large/mod.rs Outdated
@sergerad
Copy link
Copy Markdown
Contributor Author

Also, what is your plan with regards to the fact that this PR is targeted for main? These changes should also be included on next in my view.

Will create a PR into next after this is merged. I'm working off of v0.23.0 because that is what miden-node / protocol are currently using.

@sergerad sergerad requested a review from iamrecursion April 15, 2026 21:52
@sergerad sergerad changed the title feat: SmtStorageReader trait feat: SmtStorageReader and SparseMerkleTreeReader traits Apr 15, 2026
Comment thread miden-crypto/src/merkle/smt/large/mod.rs
Copy link
Copy Markdown
Collaborator

@iamrecursion iamrecursion left a comment

Choose a reason for hiding this comment

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

This looks mostly good to me. I've left a few comments inline and I'd like to take another look before merge.

@iamrecursion
Copy link
Copy Markdown
Collaborator

Also, what is your plan with regards to the fact that this PR is targeted for main? These changes should also be included on next in my view.

Will create a PR into next after this is merged. I'm working off of v0.23.0 because that is what miden-node / protocol are currently using.

Souds good to me.

Comment thread miden-crypto/src/merkle/smt/large/storage/rocksdb.rs Outdated
Comment thread miden-crypto/src/merkle/smt/large/mod.rs Outdated
Copy link
Copy Markdown
Collaborator

@huitseeker huitseeker left a comment

Choose a reason for hiding this comment

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

re: #967 (comment)
I have ported the RocksDB changes to the node.

I opened #980 as one way of addressing the issues raised in threads here.

@sergerad sergerad requested a review from huitseeker April 21, 2026 20:10
Copy link
Copy Markdown
Collaborator

@huitseeker huitseeker left a comment

Choose a reason for hiding this comment

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

Checkpointing my partial re-review. Overall LGTM except for the line comment, will come back a bit later to stamp.

}
}

Ok(MemoryStorage { leaves, subtrees }.into_snapshot())
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Come to think of it: could this snapshot keep the depth-24 cache too?

LargeSmt::load will accept this SmtStorageSnapshot, and because it is backed by MemoryStorage, get_depth24() returns an empty vec even when the snapshot has leaves/subtrees — so a non-empty snapshot panics in debug in initialize_from_storage and reconstructs the wrong top in release.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I opted to add an implementation to get_depth24() for MemoryStorage and added a test. Let me know if you would have approached it differently.

@sergerad sergerad requested a review from huitseeker April 24, 2026 03:51
Copy link
Copy Markdown
Collaborator

@huitseeker huitseeker left a comment

Choose a reason for hiding this comment

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

LGTM with one caveat, @sergerad : this looks like a breaking public API change. SmtStorage is re-exported from miden_crypto::merkle::smt, and every downstream impl SmtStorage now needs a new SmtStorageReader impl, type Reader, and reader().

I'd at least call that out as breaking in the PR description and changelog, even if we do expect external backends to adapt quickly. If only to help with versioning.

@sergerad sergerad changed the base branch from main to next April 27, 2026 19:53
@sergerad
Copy link
Copy Markdown
Contributor Author

Also, what is your plan with regards to the fact that this PR is targeted for main? These changes should also be included on next in my view.

Will create a PR into next after this is merged. I'm working off of v0.23.0 because that is what miden-node / protocol are currently using.

Ended up pointing to next branch because this PR has made breaking changes.

@sergerad sergerad enabled auto-merge (squash) April 27, 2026 21:22
Copy link
Copy Markdown
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

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

Looks good! Thank you! I left some comments/questions inline.

Comment thread miden-crypto/src/boxed_storage.rs Outdated
Comment on lines +6 to +7
/// Type alias for boxed storage with boxed reader
pub type BoxedSmtStorage = Box<dyn SmtStorage<Reader = Box<dyn SmtStorageReader>>>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If these are for benchmarks only, should we put them behind the testing feature? (not sure if that will work - but would be good to check)

Comment thread miden-crypto/src/merkle/smt/large/mod.rs
Comment on lines +41 to +42
/// Implementations may return either a point-in-time snapshot or a live view.
pub trait SmtStorageReader: 'static + fmt::Debug + Send + Sync {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Related to the previous comment: I think returning point-in-time snapshots is fine - but can we really return readers with "live views"? At the storage level this is possible, but since we clone the in-memory portion of the SMT, the in-memory portion would become inconsistent with the storage portion as soon as the data in the storage changes.

Unless the above reasoning is incorrect, I think we should make it an explicit requirement that storage readers must always be against point-in-time snapshots.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

#985 is one way to approach this.

Comment on lines +229 to +232
/// Implementations may return either a point-in-time snapshot or a live view. Either way, the
/// view must be of consistent / committed state (not partial). Holding the reader must not
/// block writes in any way.
fn reader(&self) -> Result<Self::Reader, StorageError>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same comment as above re "live view".

Comment thread miden-crypto/src/merkle/smt/large/storage/memory.rs Outdated
Comment thread miden-crypto/src/merkle/smt/large/storage/rocksdb.rs
Comment thread CHANGELOG.md Outdated
@sergerad
Copy link
Copy Markdown
Contributor Author

sergerad commented May 1, 2026

If these are for benchmarks only, should we put them behind the testing feature? (not sure if that will work - but would be good to check)

This is all already behind the executable feature

#[cfg(feature = "executable")]
mod boxed_storage;

Copy link
Copy Markdown
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

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

Looks good! Thank you!

I didn't review RocksDbSnapshot and related structs in depth because I'm assuming this will serve more like a guiding implementation for the structs that will live in miden-node.

Comment on lines +1332 to +1334
pub struct RocksDbSnapshotStorage {
inner: Arc<RocksDbSnapshotInner>,
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

When we move RocksDB storage into miden-node completely, it would be good to add some integration tests to make sure this all works as expected.

@bobbinth bobbinth disabled auto-merge May 1, 2026 02:36
@bobbinth bobbinth merged commit a7f748e into next May 1, 2026
29 checks passed
@bobbinth bobbinth deleted the sergerad-largesmt-reader-trait branch May 1, 2026 02:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants