Open
Conversation
PoseidonHasher::hash_node now converts felt-aligned trie nodes directly to Goldilocks field elements (8 bytes per felt) without the injective byte-to-felt encoding, roughly halving the circuit constraints for trie node hashing in ZK proofs.
qp-poseidon/core and qp-poseidon/substrate
these tests fail due to import issues but we don't need them
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
POC of what it would take to make a TrieHasher approach.
Put on top of no-length-trie branch
Wormhole tests fail
Pretty extensive code changes, not sure
So this PR is for evaluating these things.
===================================
Structured Trie Hasher
Problem
The generic
hash_db::Hashertrait provides a singlehash(&[u8]) -> Outmethod with no context about what is being hashed. WhenPoseidonHasherreceives raw bytes, it must apply the injective byte-to-felt encoding (injective_bytes_to_felts) to safely convert arbitrary bytes into Goldilocks field elements before hashing. This encoding uses 4 bytes per felt with a terminator — it's safe for arbitrary data but expensive to prove in ZK circuits.However, ZK-trie nodes are already felt-aligned (8-byte chunks that map directly to Goldilocks field elements). The injective encoding is redundant for this data — it doubles the number of field elements and circuit constraints for no safety benefit.
Solution
Extend the
Hashertrait with context-aware hashing methods soPoseidonHashercan apply the optimal encoding for each data type.Core API change (
hash-db)Added two methods with default implementations to the
Hashertrait:Defaults delegate to
hash, so all existingHasherimplementations (e.g.Blake2Hasher) continue to work without changes.A
TrieHashermarker trait with a blanket impl keeps call-site bounds readable:PoseidonHasher optimization (
qp-poseidon)PoseidonHasheroverrideshash_nodeto skip injective encoding and directly convert 8-byte chunks to field elements:hash_valuedelegates tohash_for_circuit(injective encoding) since storage values are arbitrary bytes.Inline node threshold (
trie-db)Added
MAX_INLINE_NODEtoTrieLayoutto control the child inlining threshold:Both
LayoutV0andLayoutV1setMAX_INLINE_NODE: Some(0)to force all children to be hashed (no inline nodes), consistent with the existingMAX_INLINE_VALUE: Some(0)policy.HashDB routing (
memory-db)HashDB::insertnow callsH::hash_value(for storage values). A newinsert_nodemethod callsH::hash_node(for encoded trie nodes). All trie-building call sites intrie-dbroute through the appropriate method.Impact
PoseidonHasher::hash_nodeproduces different output thanPoseidonHasher::hash. Requires genesis reset.Blake2Hasher) are unaffected — default implementations delegate tohash.Changed packages
New local forks (in
primitives/)hash-dbparitytech/triehash_node/hash_valueonHasher,TrieHasheralias,insert_nodeonHashDBmemory-dbparitytech/trieinsertroutes throughhash_value, newinsert_noderoutes throughhash_nodetrie-rootparitytech/triehash_nodefor trie root,hash_valuefor large inline valuesModified in-repo crates
trie-dbTrieLayout::Hashbound →TrieHasher,MAX_INLINE_NODEconstant, ~15 call sites updatedsp-trieMAX_INLINE_NODE: Some(0), codec handlesInline(_, 0)sentinelsp-state-machinestorage_hash/child_storage_hash→hash_value, allH: Hasher→H: TrieHasherCompanion PR (separate repo)
qp-poseidonPoseidonHasheroverrideshash_node(direct felt alignment) andhash_value(injective encoding)Test results
qp-poseidontests passsp-trietests pass (4 pre-existing failures onilluzen/no-length-trie, unrelated to this PR)