Skip to content

fix: prefix-free node hashing — distinct (k,v) sets must not collide - #192

Closed
presempathy-awb wants to merge 1 commit into
zhangfengcdt:mainfrom
presempathy-awb:fix/prefix-free-node-hash
Closed

fix: prefix-free node hashing — distinct (k,v) sets must not collide#192
presempathy-awb wants to merge 1 commit into
zhangfengcdt:mainfrom
presempathy-awb:fix/prefix-free-node-hash

Conversation

@presempathy-awb

Copy link
Copy Markdown
Contributor

Bug

ProllyNode::get_hash hashes keys.concat() ++ values.concat() with no length delimiters, so distinct content can produce identical hash input:

  • keys k1,k2, values ("ab","c") → input k1k2abc
  • keys k1,k2, values ("a","bc") → input k1k2abc

→ identical root hash. For a content-addressed / merge-convergent structure this is a correctness bug (false dedup, false merge convergence).

Repro (RED)

The added test fails on main:

distinct_contents_must_not_share_root … FAILED
"PREFIX-FREE COLLISION CONFIRMED: distinct (k,v) sets share a root hash"

Fix

Length-frame every element (u32 BE lengths — not usize, so the hash is identical on 32/64-bit and wasm), delimit the key region from the value region by count, and bind is_leaf + level so a leaf can't collide with an internal node of identical bytes.

Hash-function-agnostic: only the ValueDigest::new input changes, not the digest algorithm — and only node identity changes, chunk boundaries (tree shape) are untouched, so merge-canonicality and every existing test still hold.

Tests / verification

mod prefix_free_collision_probe (RED on the old concat hash, GREEN here). cargo test — 164/164 lib green, 0 regressions. Fully independent of any other change. Based on main @ 373128e.

…llide

get_hash concatenated keys.concat() ++ values.concat() with NO length
delimiters, so distinct content could produce identical hash input:
values ("ab","c") and ("a","bc") both concat to "abc" under the same
keys => identical root hash. For a content-addressed / merge-convergent
structure that is a correctness bug (false dedup, false merge
convergence).

Length-frame every element (u32 BE lengths — never usize, so the hash
is identical on 32- and 64-bit / wasm), delimit the key region from the
value region by count, and bind is_leaf + level so a leaf can't collide
with an internal node of identical bytes. Hash-function-agnostic: only
the ValueDigest::new INPUT changes, not the digest algorithm, and only
node identity changes — chunk boundaries (tree shape) are untouched, so
merge-canonicality and every existing test still hold.

Tests: mod prefix_free_collision_probe (fails on the old concat hash,
passes here).

@zhangfengcdt zhangfengcdt left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Reviewed: implementation and tests pass; no public API surface change detected.

@zhangfengcdt

Copy link
Copy Markdown
Owner

Resolved and integrated via #242 after the full CI matrix passed.

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.

2 participants