feat(mpt): add TrieNode variant + NodeEncoder (M2.2)#17
Open
kyonRay wants to merge 1 commit into
Open
Conversation
- TrieNode = variant<Empty, Leaf, Extension, Branch>; NodeRef = inline-bytes or 32B hash - encodeRaw() -> RLP bytes; encodeAndRef() -> (raw, ref) with <32B inline rule - keccak256() wrapper around bcos-crypto OpenSSLHasher<Keccak256> (0x01 padding) - EmptyTrieRoot test verifies keccak256(0x80) == emptyRootHash() (Ethereum constant) Refs: spec §5.5 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds Ethereum-canonical RLP encoding + reference (inline vs hash) logic for MPT trie nodes, including Keccak256 hashing and unit tests that pin down key spec constants and the 32-byte inline threshold.
Changes:
- Introduces
TrieNode(std::variant) node model plusNodeReffor branch children. - Implements
NodeEncoderfor raw RLP encoding and(raw, NodeRef)computation with Keccak256 hashing. - Adds
NodeEncoderSuitetests and wiresbcos-cryptointoledgerbuild/link.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| bcos-ledger/test/unittests/mpt/NodeEncoderTest.cpp | Adds unit tests for RLP encoding, inline/hash threshold, and the empty-trie-root Keccak validation. |
| bcos-ledger/bcos-ledger/mpt/TrieNode.h | Defines trie node types (Empty/Leaf/Extension/Branch) and NodeRef representation. |
| bcos-ledger/bcos-ledger/mpt/NodeEncoder.h | Declares NodeEncoder API and keccak256(span<...>) helper. |
| bcos-ledger/bcos-ledger/mpt/NodeEncoder.cpp | Implements Keccak256 wrapper plus RLP encoding for each trie node kind and inline/hash reference selection. |
| bcos-ledger/CMakeLists.txt | Adds NodeEncoder.cpp to ledger target and links bcos-crypto. |
| #include <bcos-codec/rlp/Common.h> | ||
| #include <bcos-codec/rlp/RLPEncode.h> | ||
| #include <bcos-crypto/hasher/OpenSSLHasher.h> | ||
| #include <bcos-utilities/Common.h> |
| #include <bcos-ledger/mpt/NodeEncoder.h> | ||
| #include <bcos-ledger/mpt/TrieNode.h> | ||
| #include <bcos-utilities/Common.h> | ||
| #include <boost/test/unit_test.hpp> |
Comment on lines
+43
to
+48
| /// An extension node: stores a shared nibble prefix and an already-RLP-encoded child reference. | ||
| struct ExtensionNode | ||
| { | ||
| std::vector<uint8_t> sharedNibbles; ///< Shared nibble prefix (no HP terminator) | ||
| bcos::bytes child; ///< Already RLP-encoded child ref (inline bytes or 33-byte hash string) | ||
| }; |
Comment on lines
+88
to
+102
| // Encode ExtensionNode → RLP list [HP(sharedNibbles, leaf=false), child_ref_raw] | ||
| // ExtensionNode.child is ALREADY a complete RLP-encoded child reference, so we must | ||
| // splice it raw rather than re-encode it as a byte-string (which would double-wrap). | ||
| static bcos::bytes encodeExtension(ExtensionNode const& node) | ||
| { | ||
| using namespace bcos::codec::rlp; | ||
| bcos::bytes out; | ||
| auto hp = hexPrefixEncode(node.sharedNibbles, /*terminator=*/false); | ||
| // child contributes its own bytes directly (not as an RLP-wrapped byte-string). | ||
| const size_t payloadLen = length(hp) + node.child.size(); | ||
| encodeHeader(out, {.isList = true, .payloadLength = payloadLen}); | ||
| encode(out, hp); | ||
| out.insert(out.end(), node.child.begin(), node.child.end()); | ||
| return out; | ||
| } |
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.
Summary
TrieNode.h—variant<EmptyNode, LeafNode, ExtensionNode, BranchNode>plusNodeRef { kind: Inline | Hash, inlineBytes, hash }. A default-constructed child (Inline+ emptyinlineBytes) is the explicit "absent" state and encodes as RLP-empty (0x80).NodeEncoder.{h,cpp}—encodeRaw(TrieNode)produces canonical Ethereum RLP for each node kind;encodeAndRef(TrieNode)returns(raw bytes, NodeRef)with the spec's<32B inline / >=32B hashboundary. Visitor usesif constexprwith astatic_assert(!sizeof(T*), ...)fallback to force a compile error if a newTrieNodealternative is added without an explicit handler.keccak256(span<bcos::byte const>)free function wrappingbcos::crypto::hasher::openssl::OpenSSL_Keccak256_Hasher(Ethereum 0x01 padding, not NIST SHA3-256's 0x06).bcos-cryptotoledger'starget_link_librariesso the hasher is reachable from the library.Stacking
feat/mpt-pr03-nibble-types(this is a stacked PR in the fork — it depends on M2.1).--onto release-3.18.0and a fresh PR opened against upstream.Test plan
./bcos-ledger/test/test-bcos-ledger --run_test=NodeEncoderSuite— 4 cases:EncodeLeafShort— short leaf producesInlineref withinlineBytes == rawEncodeBranchHashedWhenLarge— branch carrying a 32-byte hash child crosses the inline boundary and yieldsHashrefEmptyTrieRootMatchesEthereumConstant—keccak256(encodeRaw(EmptyNode{})) == emptyRootHash()validates the Keccak 0x01-padding patch (proves the hasher is Ethereum-flavoured, not NIST SHA3-256)InlineThresholdAt31Bytes— leaf encoded at exactly 31 bytes →Inline; at exactly 32 bytes →HashNibbleSuite(7) andHexPrefixSuite(6) from M2.1 still greenclang-format --dry-run --Werrorclean on all new files