# stemedb-merkle

BLAKE3-based Merkle tree for append-only assertion diff detection in StemeDB.

## Overview

This crate provides an efficient Merkle tree implementation optimized for StemeDB's append-only assertion store. The primary use case is **incremental sync between distributed nodes** - quickly identifying which assertions differ between local and remote stores.

## Design Principles

- **Append-Only**: Trees grow monotonically with O(log N) insert performance
- **Content-Addressed**: Uses BLAKE3 for cryptographic hash verification
- **Efficient Diff**: O(log N) comparison to identify divergent subtrees
- **Zero-Copy Serialization**: Uses rkyv for fast persistence and network transfer
- **No unwrap/expect**: All operations return `Result` for defensive error handling

## Architecture

The tree is a binary Merkle tree where:
- **Leaves** contain assertion hashes (BLAKE3 digests)
- **Internal nodes** contain `BLAKE3(left_hash || right_hash)`
- **Root hash** represents the entire assertion set

```
       root (BLAKE3(h12 || h34))
      /                        \
    h12 (BLAKE3(h1 || h2))    h34 (BLAKE3(h3 || h4))
   /    \                     /    \
  h1    h2                   h3    h4
  |     |                    |     |
 a1    a2                   a3    a4  (assertion hashes)
```

## Example Usage

### Basic Tree Operations

```rust
use stemedb_merkle::MerkleTree;

// Create a tree and insert assertion hashes
let mut tree = MerkleTree::new();
tree.insert([1u8; 32]).expect("insert");
tree.insert([2u8; 32]).expect("insert");
tree.insert([3u8; 32]).expect("insert");

// Get root hash (O(1) - cached)
let root = tree.root().expect("root");

// Check tree size
assert_eq!(tree.len(), 3);
```

### Incremental Sync (Fast Diff)

```rust
use stemedb_merkle::{MerkleTree, DiffResult, roots_equal};

let mut local = MerkleTree::new();
local.insert([1u8; 32]).expect("insert");
local.insert([2u8; 32]).expect("insert");

let mut remote = MerkleTree::new();
remote.insert([1u8; 32]).expect("insert");
remote.insert([2u8; 32]).expect("insert");
remote.insert([3u8; 32]).expect("insert");  // New assertion
remote.insert([4u8; 32]).expect("insert");  // New assertion

// Quick check: do we need to sync? (O(1))
if !roots_equal(&local, &remote) {
    // Find what remote has that local doesn't (O(N))
    let diff = DiffResult::diff(&local, &remote);

    println!("Need to fetch {} assertions", diff.len());
    // Request missing assertions: [3, 4]
    for hash in diff.missing_hashes {
        // fetch_assertion(hash)...
    }
}
```

### Persistence (Crash Recovery)

```rust
use stemedb_merkle::{MerkleTree, serialize::{serialize_tree, deserialize_tree}};

let mut tree = MerkleTree::new();
tree.insert([1u8; 32]).expect("insert");
tree.insert([2u8; 32]).expect("insert");

// Serialize to disk
let bytes = serialize_tree(&tree).expect("serialize");
std::fs::write("merkle_tree.bin", &bytes).expect("write");

// Restore after crash
let bytes = std::fs::read("merkle_tree.bin").expect("read");
let recovered = deserialize_tree(&bytes).expect("deserialize");

assert_eq!(tree.root(), recovered.root());
```

## Performance Characteristics

| Operation | Complexity | Notes |
|-----------|------------|-------|
| Insert | O(log N) | Recompute path from leaf to root |
| Root | O(1) | Cached after each insert |
| Diff | O(N) | Set-based comparison of leaves |
| Serialize | O(N) | Write all leaves to bytes |
| Deserialize | O(N log N) | Rebuild tree from leaves |

## Future Optimizations

For very large trees (millions of assertions), we plan to implement:

- **Subtree-based diff**: Skip identical subtrees by comparing intermediate hashes
  - Reduces diff from O(N) to O(diff_size * log N)
- **Incremental serialization**: Only persist changes since last checkpoint
- **Range queries**: Find assertions inserted between timestamps

## Integration with StemeDB

This crate will be used by:

1. **Write-ahead log (WAL)**: Build Merkle tree as assertions are appended
2. **Replication**: Exchange root hashes to detect drift, then sync missing data
3. **Checkpointing**: Persist tree state for fast bootstrap after restart

See `docs/research/distributed-write-path.md` for architecture details.
