Skip to content

Add merkle tree interfaces for reading & writing#472

Closed
malcolmgreaves wants to merge 1 commit intomg/oxen_macrosfrom
mg/merkle_interface
Closed

Add merkle tree interfaces for reading & writing#472
malcolmgreaves wants to merge 1 commit intomg/oxen_macrosfrom
mg/merkle_interface

Conversation

@malcolmgreaves
Copy link
Copy Markdown
Collaborator

@malcolmgreaves malcolmgreaves commented Apr 21, 2026

New interface traits: MerkleReader, MerkleWriter, and unifying MerkleStore.

MerkleStore
A MerkleStore is just a MerkleReader + MerkleWriter where their Error type
is the same. Includes a blanket impl. for any type that has a reader & writer impl.
with equal Error types.

MerkleReader
Can retrieve Merkle tree nodes from storage given a hash. Supports existence
checks, retrieving the node, and retrieving a node's children, if applicable.

MerkleWriter
Produces a MerkleWriteSession that allows for writing arbitrary Merkle tree nodes
to the underlying physical store. Within the write session, writing a node creates a
new NodeWriteSession instance. This instance can write a node as well as a node's
children. Both session types have finish, which ensures their writes go to the
underlying physical store.

These tiered write sessions match how the Oxen code writes Merkle tree nodes.
Additionally, this design allows for implementations to be eager, buffer writes in-
memory, or provide transaction semantics.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 21, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds read/write Merkle tree abstractions: new merkle_reader and merkle_writer modules, MerkleReader/MerkleWriter traits, MerkleNodeRecord, and a composing MerkleStore trait with a blanket impl enforcing a single aligned error type.

Changes

Cohort / File(s) Summary
Merkle Tree Module Exports
crates/lib/src/model/merkle_tree.rs
Publishes new submodules merkle_reader and merkle_writer, re-exports MerkleReader/MerkleWriter, and adds MerkleStore trait plus a blanket impl requiring aligned error types.
Merkle Reader Interface
crates/lib/src/model/merkle_tree/merkle_reader.rs
Adds MerkleReader trait (exists, get_node, get_children) with associated Error bound and MerkleNodeRecord struct (constructor, accessors, into_node).
Merkle Writer Interface
crates/lib/src/model/merkle_tree/merkle_writer.rs
Adds MerkleWriter trait (begin() with associated Session<'a>), MerkleWriteSession (create_node, finish) and NodeWriteSession (node_id, add_child, finish) for batched/multi-node write sessions and lifetimes ensuring node sessions finish before session finish.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant MW as MerkleWriter
    participant WS as MerkleWriteSession
    participant NS as NodeWriteSession

    Client->>MW: begin()
    MW-->>Client: Session (WS)
    Client->>WS: create_node(node, parent_id)
    WS-->>Client: NodeSession (NS)
    Client->>NS: add_child(child_node)
    NS-->>Client: Ok
    Client->>NS: finish()
    NS-->>Client: Ok
    Client->>WS: finish()
    WS-->>Client: Ok
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • rpschoenburg
  • CleanCut
  • Lilianxr

Poem

🐰 I hop through nodes both stout and small,
I peek at leaves and listen for each call.
I write a session, add a child with care,
Then finish up and tidy every pair—
A rabbit's joy in trees beyond the wall.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add merkle tree interfaces for reading & writing' accurately summarizes the main change: introducing MerkleReader and MerkleWriter traits.
Description check ✅ Passed The description is directly related to the changeset, providing detailed explanations of the three new traits (MerkleStore, MerkleReader, MerkleWriter) and their design rationale.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mg/merkle_interface

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Collaborator Author

@malcolmgreaves malcolmgreaves Apr 21, 2026

Choose a reason for hiding this comment

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

Why are there lifetimes here? The upcoming LMDB implementation will need to maintain a heed::RwTxn for some explicit lifetime in the MerkleWriteSession. That same read-write transaction will be borrowed in the NodeWriteSession too, so that also needs to support an explicit lifetime.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/lib/src/model/merkle_tree/merkle_reader.rs`:
- Around line 12-22: Replace the typo "underyling" with "underlying" in the
public API documentation comments for the Merkle reader methods — update the doc
comments for exists, get_node, get_children (and any other MerkleReader methods
in this file showing the same typo) so the docs read "underlying storage layer"
consistently.
- Around line 29-35: MerkleNodeRecord currently has only pub(crate) fields and
no public constructor, and the MerkleReader trait's get_node is not implemented
anywhere; add a pub fn new(...) -> MerkleNodeRecord constructor on
MerkleNodeRecord that accepts (hash: MerkleHash, dtype: MerkleTreeNodeType,
parent_id: Option<MerkleHash>, node: EMerkleTreeNode, num_children: u64) and
returns Self, then implement the MerkleReader::get_node method on the
appropriate type to construct and return MerkleNodeRecord via that new() so
external callers can obtain instances without accessing internal fields; also
correct the documentation typos changing "underyling" to "underlying" wherever
present.

In `@crates/lib/src/model/merkle_tree/merkle_writer.rs`:
- Around line 29-37: Update the documentation comments for
MerkleWriteSession/NodeWriteSession to correct typos: change "comitting" to
"committing", "finialize" to "finalize", "Peristance" to "Persistence",
"invairant" to "invariant", "guarentee" to "guarantee", and "chose" to "choose"
in the section that references create_node, add_child, and finish so the session
semantics read correctly and consistently.
- Around line 15-17: Update the intra-doc links in the `begin` method docs to be
explicit: change the first ambiguous [`finish`] (referring to the returned
session) to [`MerkleWriteSession::finish`] and the second ambiguous [`finish`]
(referring to per-node sessions) to [`NodeWriteSession::finish`], so the docs
unambiguously point to the correct methods.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c87ee43a-2cf4-45d1-96bb-1539f358f5f7

📥 Commits

Reviewing files that changed from the base of the PR and between f92e457 and 4be8e81.

📒 Files selected for processing (3)
  • crates/lib/src/model/merkle_tree.rs
  • crates/lib/src/model/merkle_tree/merkle_reader.rs
  • crates/lib/src/model/merkle_tree/merkle_writer.rs

Comment thread crates/lib/src/model/merkle_tree/merkle_reader.rs Outdated
Comment thread crates/lib/src/model/merkle_tree/merkle_reader.rs
Comment thread crates/lib/src/model/merkle_tree/merkle_writer.rs Outdated
Comment thread crates/lib/src/model/merkle_tree/merkle_writer.rs Outdated
@malcolmgreaves malcolmgreaves force-pushed the mg/merkle_interface branch 3 times, most recently from 68453db to f25df1c Compare April 22, 2026 00:41
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
crates/lib/src/model/merkle_tree/merkle_writer.rs (1)

31-33: ⚠️ Potential issue | 🟡 Minor

Qualify remaining finish intra-doc links to avoid ambiguous rustdoc resolution.

Line 31 and Line 37 use unqualified [finish] while referring to different methods (NodeWriteSession::finish vs MerkleWriteSession::finish). Please make these explicit as done in the begin docs.

Proposed doc fix
-/// Each [`NodeWriteSession`] must have its [`finish`] called to finalize the written node
-/// information. Once all nodes have been written, the [`finish`] method of the [`MerkleWriteSession`]
+/// Each [`NodeWriteSession`] must have its [`NodeWriteSession::finish`] called to finalize the written node
+/// information. Once all nodes have been written, the [`MerkleWriteSession::finish`] method of the [`MerkleWriteSession`]
 /// must be called to persist the changes to the store.
@@
-/// are called. The invariant is that [`finish`] must be called to **ensure** that writes are
+/// are called. The invariant is that [`MerkleWriteSession::finish`] and each
+/// [`NodeWriteSession::finish`] must be called to **ensure** that writes are
 /// persisted.

Also applies to: 37-37

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/lib/src/model/merkle_tree/merkle_writer.rs` around lines 31 - 33, Doc
comments use ambiguous unqualified [`finish`] links; update them to
fully-qualified intra-doc links `NodeWriteSession::finish` and
`MerkleWriteSession::finish` so rustdoc resolves to the correct methods. Locate
the doc sentences referencing the session finish behavior (the paragraph that
mentions "Each `NodeWriteSession` must have its `finish` called..." and the
paragraph about finalizing the Merkle write session) and replace the plain
[`finish`] with `[`NodeWriteSession::finish`]` and
`[`MerkleWriteSession::finish`]` respectively, matching the style used for
`begin`.
🧹 Nitpick comments (1)
crates/lib/src/model/merkle_tree/merkle_writer.rs (1)

63-66: Clarify the contract for invalid finish ordering.

Line 64 says calling finish with active node sessions is “invalid,” but expected behavior is unspecified (error vs panic vs rollback). Please define this contract explicitly so implementations stay consistent.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/lib/src/model/merkle_tree/merkle_writer.rs` around lines 63 - 66, The
docs for the MerkleWriter trait’s finish(self: Box<Self>) -> Result<(),
Self::Error> are ambiguous about what "invalid" means when there are active node
write sessions; update the contract comment on finish to state that calling
finish while any node write sessions are still active must return an
Err(Self::Error) (not panic) describing the invalid ordering, and specify that
implementations should not perform partial commits or rollbacks but must leave
the writer in a well-defined state documented (e.g., unchanged) so callers can
handle the error; reference the finish method and the Self::Error type in the
comment so implementers (and callers of open/finish session APIs) follow the
same error-return semantics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@crates/lib/src/model/merkle_tree/merkle_writer.rs`:
- Around line 31-33: Doc comments use ambiguous unqualified [`finish`] links;
update them to fully-qualified intra-doc links `NodeWriteSession::finish` and
`MerkleWriteSession::finish` so rustdoc resolves to the correct methods. Locate
the doc sentences referencing the session finish behavior (the paragraph that
mentions "Each `NodeWriteSession` must have its `finish` called..." and the
paragraph about finalizing the Merkle write session) and replace the plain
[`finish`] with `[`NodeWriteSession::finish`]` and
`[`MerkleWriteSession::finish`]` respectively, matching the style used for
`begin`.

---

Nitpick comments:
In `@crates/lib/src/model/merkle_tree/merkle_writer.rs`:
- Around line 63-66: The docs for the MerkleWriter trait’s finish(self:
Box<Self>) -> Result<(), Self::Error> are ambiguous about what "invalid" means
when there are active node write sessions; update the contract comment on finish
to state that calling finish while any node write sessions are still active must
return an Err(Self::Error) (not panic) describing the invalid ordering, and
specify that implementations should not perform partial commits or rollbacks but
must leave the writer in a well-defined state documented (e.g., unchanged) so
callers can handle the error; reference the finish method and the Self::Error
type in the comment so implementers (and callers of open/finish session APIs)
follow the same error-return semantics.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9829bffb-72ae-4e34-9168-677dd0297054

📥 Commits

Reviewing files that changed from the base of the PR and between 68453db and f25df1c.

📒 Files selected for processing (3)
  • crates/lib/src/model/merkle_tree.rs
  • crates/lib/src/model/merkle_tree/merkle_reader.rs
  • crates/lib/src/model/merkle_tree/merkle_writer.rs
✅ Files skipped from review due to trivial changes (1)
  • crates/lib/src/model/merkle_tree/merkle_reader.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/lib/src/model/merkle_tree.rs

@malcolmgreaves malcolmgreaves force-pushed the mg/merkle_interface branch 2 times, most recently from 8c766ba to 96ee80c Compare April 29, 2026 00:28
@malcolmgreaves malcolmgreaves changed the base branch from main to mg/oxen_macros April 29, 2026 00:28
@malcolmgreaves
Copy link
Copy Markdown
Collaborator Author

@coderabbitai please re-review this entire PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

@malcolmgreaves Sure! I'll kick off a full re-review of PR #472 now.


🧠 Learnings used
Learnt from: malcolmgreaves
Repo: Oxen-AI/Oxen PR: 476
File: crates/lib/src/model/merkle_tree/merkle_hash.rs:128-130
Timestamp: 2026-04-21T22:36:11.956Z
Learning: In the Oxen-AI/Oxen codebase, `HexHash::node_db_prefix()` in `crates/lib/src/model/merkle_tree/merkle_hash.rs` has a known edge case where hashes smaller than 0x1000 produce an empty `dir_suffix`, resulting in a malformed single-segment path. The definitive fix is PR `#474` ("Make hex-encoded hash always 32 characters"), which zero-pads the `MerkleHash` hex display to 32 chars (`{:032x}`), but it requires a data migration. PR `#476` is an interim stopgap that avoids panics but does not resolve the empty-suffix edge case.
✅ Actions performed

Full review triggered.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
crates/lib/src/model/merkle_tree/merkle_reader.rs (1)

43-56: Enforce MerkleNodeRecord invariants in constructor.

new currently accepts hash/dtype independently from node (Line 43), so callers can build inconsistent records. Consider validating these relationships to prevent invalid state construction.

Suggested guard in constructor
 impl MerkleNodeRecord {
     pub fn new(
         hash: MerkleHash,
         dtype: MerkleTreeNodeType,
         parent_id: Option<MerkleHash>,
         node: EMerkleTreeNode,
         num_children: u64,
     ) -> Self {
+        debug_assert_eq!(&hash, node.hash());
+        debug_assert_eq!(dtype, node.node_type());
         Self {
             hash,
             dtype,
             parent_id,
             node,
             num_children,
         }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/lib/src/model/merkle_tree/merkle_reader.rs` around lines 43 - 56, The
constructor MerkleNodeRecord::new currently accepts hash, dtype, parent_id,
node, and num_children independently which allows inconsistent records; update
new to validate invariants (e.g., ensure node's internal hash equals the
provided hash, node variant/type matches dtype, and num_children matches the
node variant's expected children count) and fail fast on mismatch—preferably
change the signature to return Result<Self, MerkleError> (or another error type)
and return Err(...) when checks fail, or at minimum assert/unwrap with clear
messages; reference the MerkleNodeRecord::new function, the dtype and node
parameters, and num_children/parent_id fields when adding these checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/lib/src/model/merkle_tree/merkle_writer.rs`:
- Around line 61-65: The trait method create_node currently takes &'b self but
must be changed to &'b mut self to allow transaction-backed backends to
open/commit write transactions; update the signature of create_node<N:
TMerkleTreeNode>(&'b mut self, node: &N, parent_id: Option<MerkleHash>) ->
Result<Self::NodeSession<'b>, Self::Error>, and propagate this change to any
trait definitions and where NodeWriteSession::add_child(&mut self, ...) is
expected so implementations (e.g., any MerkleWriteSession impls or future
heed-backed sessions) can mutably borrow self to manage transaction state
without relying on interior mutability; adjust trait bounds/usages and tests
accordingly.

---

Nitpick comments:
In `@crates/lib/src/model/merkle_tree/merkle_reader.rs`:
- Around line 43-56: The constructor MerkleNodeRecord::new currently accepts
hash, dtype, parent_id, node, and num_children independently which allows
inconsistent records; update new to validate invariants (e.g., ensure node's
internal hash equals the provided hash, node variant/type matches dtype, and
num_children matches the node variant's expected children count) and fail fast
on mismatch—preferably change the signature to return Result<Self, MerkleError>
(or another error type) and return Err(...) when checks fail, or at minimum
assert/unwrap with clear messages; reference the MerkleNodeRecord::new function,
the dtype and node parameters, and num_children/parent_id fields when adding
these checks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 84dede9a-a6c4-4cc0-98e1-99730b5baa5f

📥 Commits

Reviewing files that changed from the base of the PR and between 6bf35c2 and 96ee80c.

📒 Files selected for processing (3)
  • crates/lib/src/model/merkle_tree.rs
  • crates/lib/src/model/merkle_tree/merkle_reader.rs
  • crates/lib/src/model/merkle_tree/merkle_writer.rs

Comment on lines +61 to +65
fn create_node<'b, N: TMerkleTreeNode>(
&'b self,
node: &N,
parent_id: Option<MerkleHash>,
) -> Result<Self::NodeSession<'b>, Self::Error>;
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In the Rust heedcrate, do write operations onRwTxn require mutable access (&mut self), and is borrowing a mutable transaction into nested write-session objects the idiomatic pattern?

💡 Result:

Yes, write operations on RwTxn in the Rust heed crate require mutable access (&mut self). The Database::put method signature is pub fn put<'a, T>(&self, txn: &mut RwTxn<', ', T>, key: &'a KC::EItem, data: &'a DC::EItem) -> Result<>, explicitly taking &mut RwTxn for writes like put, delete, etc. All official examples use &mut wtxn for write operations, such as db.put(&mut wtxn, key, value)?. Yes, borrowing a mutable transaction into nested write-session objects is the idiomatic pattern. RwTxn supports nested transactions via RwTxn::nested, which takes a &mut RwTxn (parent) and returns a new RwTxn<'e, 'p> borrowing the parent's lifetime. Recent versions (e.g., 0.22.1-nested-rtxns) emphasize nested rtxns, and source code shows pub(crate) fn nested<'p: 'e>(env: &'e Env, parent: &'p mut RwTxn) -> Result<RwTxn<'e, 'p, T>>. This enables hierarchical transactions while maintaining LMDB's single-writer rule through borrowing.

Citations:


🏁 Script executed:

# Find the trait definition and understand the structure
find crates/lib/src/model/merkle_tree -type f -name "*.rs" | head -20

Repository: Oxen-AI/Oxen

Length of output: 913


🏁 Script executed:

# Look at the merkle_writer.rs file to see the trait definition
head -100 crates/lib/src/model/merkle_tree/merkle_writer.rs

Repository: Oxen-AI/Oxen

Length of output: 4952


🏁 Script executed:

# Search for create_node calls to understand how it's used
rg "create_node" crates/lib/src/model/merkle_tree/ -A 3 -B 1

Repository: Oxen-AI/Oxen

Length of output: 1189


🏁 Script executed:

# Check if heed is a dependency
rg "heed" crates/lib/Cargo.toml

Repository: Oxen-AI/Oxen

Length of output: 38


🏁 Script executed:

# Search for implementations of MerkleWriteSession
rg "impl.*MerkleWriteSession" --type rust

Repository: Oxen-AI/Oxen

Length of output: 38


🏁 Script executed:

# Search for heed usage anywhere in the codebase
rg "heed" --type rust

Repository: Oxen-AI/Oxen

Length of output: 1623


🏁 Script executed:

# Look for NodeWriteSession implementations which might reveal transaction handling
rg "impl.*NodeWriteSession" --type rust

Repository: Oxen-AI/Oxen

Length of output: 38


🏁 Script executed:

# Check Cargo.toml in different locations for dependencies
find . -name "Cargo.toml" -exec grep -l "heed\|lmdb\|rocksdb" {} \;

Repository: Oxen-AI/Oxen

Length of output: 155


🏁 Script executed:

# Search for struct definitions that might implement the traits
rg "struct.*Session" crates/lib/src/model/merkle_tree/ -A 5

Repository: Oxen-AI/Oxen

Length of output: 38


🏁 Script executed:

# Look at the full merkle_tree module structure
ls -la crates/lib/src/model/merkle_tree/

Repository: Oxen-AI/Oxen

Length of output: 537


🏁 Script executed:

# Search for any create_node method calls in the codebase
rg "\.create_node\(" --type rust -B 2 -A 2

Repository: Oxen-AI/Oxen

Length of output: 38


🏁 Script executed:

# Check if there are any imports or uses of MerkleWriteSession
rg "MerkleWriteSession\|MerkleWriter" --type rust

Repository: Oxen-AI/Oxen

Length of output: 38


🏁 Script executed:

# Search across all crates
rg "impl MerkleWriteSession\|impl MerkleWriter" --type rust

Repository: Oxen-AI/Oxen

Length of output: 38


🏁 Script executed:

# Search for Merkle in all files to understand usage
rg "Merkle" crates/lib/src/ -l | head -20

Repository: Oxen-AI/Oxen

Length of output: 795


🏁 Script executed:

# Check if there's any git history or comments about this
rg "create_node\|NodeWriteSession\|MerkleWriter" crates/lib/src/ --type rust -B 1 -A 1

Repository: Oxen-AI/Oxen

Length of output: 38


🏁 Script executed:

# Look at the full merkle_writer.rs file to see all trait definitions
cat crates/lib/src/model/merkle_tree/merkle_writer.rs

Repository: Oxen-AI/Oxen

Length of output: 4952


Change create_node to &'b mut self for transaction-backed implementations.

The current signature with &'b self is inconsistent with NodeWriteSession::add_child(&mut self, ...), which also performs state modification. For backends using transaction-based storage like heed, mutable access is required to safely manage write transactions. The current immutable design would necessitate interior mutability workarounds, which is less idiomatic and harder to reason about. Since no implementations of MerkleWriteSession exist yet in the codebase, this change can be made without breaking impact.

Proposed API adjustment
-    fn create_node<'b, N: TMerkleTreeNode>(
-        &'b self,
+    fn create_node<'b, N: TMerkleTreeNode>(
+        &'b mut self,
         node: &N,
         parent_id: Option<MerkleHash>,
     ) -> Result<Self::NodeSession<'b>, Self::Error>;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fn create_node<'b, N: TMerkleTreeNode>(
&'b self,
node: &N,
parent_id: Option<MerkleHash>,
) -> Result<Self::NodeSession<'b>, Self::Error>;
fn create_node<'b, N: TMerkleTreeNode>(
&'b mut self,
node: &N,
parent_id: Option<MerkleHash>,
) -> Result<Self::NodeSession<'b>, Self::Error>;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/lib/src/model/merkle_tree/merkle_writer.rs` around lines 61 - 65, The
trait method create_node currently takes &'b self but must be changed to &'b mut
self to allow transaction-backed backends to open/commit write transactions;
update the signature of create_node<N: TMerkleTreeNode>(&'b mut self, node: &N,
parent_id: Option<MerkleHash>) -> Result<Self::NodeSession<'b>, Self::Error>,
and propagate this change to any trait definitions and where
NodeWriteSession::add_child(&mut self, ...) is expected so implementations
(e.g., any MerkleWriteSession impls or future heed-backed sessions) can mutably
borrow self to manage transaction state without relying on interior mutability;
adjust trait bounds/usages and tests accordingly.

New interface traits: `MerkleReader`, `MerkleWriter`, and unifying `MerkleStore`.

**`MerkleStore`**
A `MerkleStore` is just a `MerkleReader` + `MerkleWriter` where their `Error` type
is the same. Includes a blanket impl. for any type that has a reader & writer impl.
with equal `Error` types.

**`MerkleReader`**
Can retrieve Merkle tree nodes from storage given a hash. Supports existence
checks, retrieving the node, and retrieving a node's children, if applicable.

**`MerkleWriter`**
Produces a `MerkleWriteSession` that allows for writing arbitrary Merkle tree nodes
to the underlying physical store. Within the write session, writing a node creates a
new `NodeWriteSession` instance. This instance can write a node as well as a node's
children. Both session types have `finish`, which ensures their writes go to the
underlying physical store.

These tiered write sessions match how the Oxen code writes Merkle tree nodes.
Additionally, this design allows for implementations to be eager, buffer writes in-
memory, or provide transaction semantics.
@malcolmgreaves
Copy link
Copy Markdown
Collaborator Author

STACKED PR: Do not merge until #496 has been merged.

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.

1 participant