Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
53 changes: 53 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ dirs = { version = "5.0", optional = true }

[dev-dependencies]
assert_cmd = "2.2"
proptest = "1"
bytes = "1.10.1"
criterion = "0.5"
predicates = "3.0"
Expand Down
7 changes: 4 additions & 3 deletions src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ use crate::proof::Proof;
use crate::storage::NodeStorage;
use schemars::schema::RootSchema;
use serde::{Deserialize, Serialize};
use std::hash::Hash;
use std::hash::Hasher;
use std::sync::Arc;
use twox_hash::XxHash64;
Expand Down Expand Up @@ -747,7 +746,7 @@ impl<const N: usize> NodeChunk for ProllyNode<N> {

fn hash_item(item: &[u8], _base: u64, modulus: u64) -> u64 {
let mut hasher = XxHash64::with_seed(HASH_SEED);
item.hash(&mut hasher);
hasher.write(item);
hasher.finish() % modulus
}
}
Expand Down Expand Up @@ -1711,7 +1710,9 @@ mod tests {

node.insert(vec![32], value_for_all.clone(), &mut storage, Vec::new());

assert_eq!(node.traverse(&storage), "[L0:[[17], [20], [32]]]");
// hash_item now hashes raw bytes (platform-independent), which shifts the
// content-defined chunk boundary: [17] seals its own chunk.
assert_eq!(node.traverse(&storage), "[L0:[[17]]][L0:[[20], [32]]]");
}

#[test]
Expand Down
21 changes: 18 additions & 3 deletions src/streaming_chunker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ use crate::config::TreeConfig;
use crate::node::ProllyNode;
use crate::storage::NodeStorage;
use std::collections::VecDeque;
use std::hash::{Hash, Hasher};
use std::hash::Hasher;
use twox_hash::XxHash64;

const HASH_SEED: u64 = 0;
Expand Down Expand Up @@ -174,7 +174,7 @@ impl Splitter for RollingHashSplitter {

fn hash_item(item: &[u8], modulus: u64) -> u64 {
let mut hasher = XxHash64::with_seed(HASH_SEED);
item.hash(&mut hasher);
hasher.write(item);
hasher.finish() % modulus
}

Expand Down Expand Up @@ -458,7 +458,22 @@ impl<'s, const N: usize, S: NodeStorage<N>> Chunker<'s, N, S> {
let root = self.builder.build();
let root_hash = root.get_hash();
let _ = storage.insert_node(root_hash, root.clone());
root
// RT-B F1: a content-defined boundary on the FINAL item emits the chunk and pops
// to a fresh parent level, which can leave this top-level node with exactly one
// child — a degenerate, non-minimal spine that diverges from a fresh batch build
// / a collapsing peer. Collapse single-child internal roots to their child so the
// canonical form is minimal. done() is the single chokepoint for every root-
// producing path (build + apply_mutations + try_pure_append all reach it), so
// build and merge collapse identically and P-CANON is preserved.
let mut node = root;
while !node.is_leaf && node.values.len() == 1 {
let child_digest = crate::digest::ValueDigest::raw_hash(&node.values[0]);
match storage.get_node_by_hash(&child_digest) {
Some(child) => node = (*child).clone(),
None => break,
}
}
node
}
}

Expand Down
Loading