Conversation
|
changed base to 451 branch |
|
Security and correctness review of PR #452 on top of #451. PR #452 Review: Security & CorrectnessThis PR does three things: (A) removes length prefixes from branch children in the trie encoding, (B) forces all values to be hashed (never inlined), and (C) bypasses the node cache for value lookups to fix a stale-cache bug. It also switches account ID derivation to an injective Poseidon hash. Critical: Zero-hash sentinel in branch decodingIn let is_zero_hash = data[range.clone()].iter().all(|&b| b == 0);
if is_zero_hash {
children[i] = Some(NodeHandlePlan::Inline(range.start..range.start));
} else {
children[i] = Some(NodeHandlePlan::Hash(range));
}Concerns:
Recommendation: Use an explicit sentinel type or separate proof format rather than overloading the hash space. At minimum, add a comment explaining which call paths reach this code and confirming the proof verifier handles zero-length inlines correctly. Critical: Inline children silently hashed during encodingIn &Some(ChildReference::Inline(inline_data, len)) => {
let child_hash = H::hash(&inline_data.as_ref()[..len]);
output.extend_from_slice(child_hash.as_ref());
true
},If an inline child is passed, it gets silently hashed. But inline children by definition are not stored separately in the DB -- they were embedded directly in the parent node. After this hashing, the decoder will see a The assumption is that with debug_assert!(
data.len() > MAX_INLINE_THRESHOLD,
"Node too small ({} bytes) - would be inlined but inline children not supported",
data.len()
);Recommendation: Make this a hard Important: Cache bypass is correct but heavy-handedThe
This effectively means every value read hits the database. The stale-cache regression test ( However, the node cache still caches trie structure nodes (branches, leaves). The issue is specifically that value nodes were being cached by their hash, but when a key's value changes, the old value hash's cached entry isn't invalidated. Since the new value has a different hash, the old entry becomes orphaned but harmless -- unless the trie happens to look up the same hash for a different reason. The real question: With Important: Consensus-breaking changesTwo changes break consensus with existing chain state:
These require a chain reset or coordinated migration. If this is intended (e.g., testnet reset), fine. If targeting a live chain, this needs a migration path. Minor: All dependencies pinned to feature branchesqp-poseidon = { git = "...", branch = "illuzen/injectivity" }
qp-plonky2 = { git = "...", branch = "illuzen/new-rate" }
# ... 14 more git branch overridesThis is fine for development but these branches can be force-pushed, making builds non-reproducible. Before merging to main, these should be pinned to tags or commit SHAs. Minor: Excessive debug logging
Tests: Good coverage, some cleanup neededThe regression test for cache poisoning is well-targeted. The encode/decode round-trip tests for nibble_count 30/31 and the realistic chain storage test cover the exact failure scenario. The Summary
|
primitives/wormhole/src/lib.rs
Outdated
| let preimage_felts = preimage.to_felts(); | ||
| PoseidonHasher::hash_variable_length(preimage_felts) | ||
| let preimage_felts = unsafe_digest_bytes_to_felts(&preimage); | ||
| PoseidonHasher::hash_variable_length(preimage_felts.to_vec()) |
There was a problem hiding this comment.
Shouldn't this use the same poseidon hash we use for normal addresses?
PR #452 ReviewContext update
Findings (ordered by severity)Critical
Medium
Low
Removed concern
Current merge stance
|
PR #452 Review — No length prefix in triePR: #452 SummaryThis PR removes length prefixes from branch children in the trie encoding, Context
Verdict: ready to mergeNo critical issues remain after the zero-hash sentinel removal. Remaining findingsMedium —
|
…hain into illuzen/no-length-trie
fail early if something unexpected happens
|
AI review - see human review below for a much shorter version Here's my review of PR #452 "No length prefix in trie": OverviewThis PR does four things:
It also changes PoW from double-hash to single-hash in FindingsCritical:
|
| Finding | Severity | Action |
|---|---|---|
debug_assert! guards core invariants in release |
Critical | Upgrade to assert! |
| Inline-to-hash silent conversion | Medium | Add hard assert or error |
| Heavy debug logging in hot path | Medium | Strip before merge |
| Branch-based git dependencies | Medium | Pin to tags/SHAs |
| Forked trie-db divergence undocumented | Low | Add documentation |
| PoW hash change not called out | Low | Note in PR description |
decode_hex duplication in tests |
Low | Extract helper |
The core approach is sound -- removing length prefixes simplifies the trie encoding, forcing all values to be hashed eliminates inline-vs-hashed ambiguity, and the cache bypass correctly fixes the stale value bug. The test coverage is solid. Main concern is hardening the debug_assert! guards before this goes to main.
|
Human review: I fixed the assert calls, now they all fail early in case things go wrong We need to fix the patch github dependencies but everything needs to be released for that. The rest is minor stuff GTG ✅ |
this sits on top of #451
it removes the length prefixes from the trie and makes a corresponding change in trie-db to fix the cache usage.
it also uses the updated PoseidonHasher to be non-injective