mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-24 08:49:29 +00:00
eth/protocols/snap: address review — if-else guard, tighten path limit to 32
Per review on #34854: - Refactor the path-length guard to if-else style (no append-nil-and-break); the trie traversal lives in the else branch. - Tighten maxTrieNodePathLength from 33 to 32 bytes. The theoretical compact-encoded upper bound is 33 bytes (64 nibbles + 1 prefix byte), but in an MPT the value node is always embedded in its parent and is never addressed as a standalone node, so the hexary path length is at most 63 nibbles and the compact encoding never exceeds 32 bytes. - Update test comment to reflect the 32-byte real-world cap.
This commit is contained in:
parent
d68ff2bc91
commit
5dbf26db90
3 changed files with 27 additions and 26 deletions
|
|
@ -55,11 +55,13 @@ const (
|
|||
|
||||
// maxTrieNodePathLength is the longest a compact-encoded trie path can be
|
||||
// while still addressing a node in any Ethereum state trie. Trie keys are
|
||||
// Keccak256 hashes (32 bytes = 64 nibbles), and the compact encoding adds
|
||||
// at most one prefix byte, giving 32+1 = 33 bytes. Anything longer is
|
||||
// structurally invalid and cannot match a node, so the handler skips it
|
||||
// without performing a trie traversal.
|
||||
maxTrieNodePathLength = 33
|
||||
// Keccak256 hashes (32 bytes = 64 nibbles). The theoretical compact-encoded
|
||||
// upper bound is 33 bytes (64 nibbles + 1 prefix byte), but in an MPT the
|
||||
// value node is always embedded in its parent and is never addressed as a
|
||||
// standalone node, so the hexary path length is at most 63 nibbles and the
|
||||
// compact encoding never exceeds 32 bytes. Anything longer is structurally
|
||||
// invalid and the handler skips it without performing a trie traversal.
|
||||
maxTrieNodePathLength = 32
|
||||
)
|
||||
|
||||
// Handler is a callback to invoke from an outside runner after the boilerplate
|
||||
|
|
|
|||
|
|
@ -223,10 +223,12 @@ func TestServiceGetAccessListsQueryByteLimit(t *testing.T) {
|
|||
// TestServiceGetTrieNodesQueryPathLength verifies that ServiceGetTrieNodesQuery
|
||||
// rejects structurally invalid (over-length) compact-encoded paths without
|
||||
// allocating in compactToHex or walking the trie. Trie keys are Keccak256
|
||||
// hashes, so the longest valid compact-encoded path is 33 bytes; anything
|
||||
// longer cannot match a node in any state trie. A peer sending such a path
|
||||
// (up to the 10MB request size cap) would otherwise force the server to
|
||||
// allocate 2*len(path)+1 nibbles per entry and start a doomed traversal.
|
||||
// hashes; value nodes are always embedded in their parent and are never
|
||||
// addressed as standalone nodes, so the longest compact-encoded path that can
|
||||
// match a node is 32 bytes. Anything longer cannot match a node in any state
|
||||
// trie. A peer sending such a path (up to the 10MB request size cap) would
|
||||
// otherwise force the server to allocate 2*len(path)+1 nibbles per entry and
|
||||
// start a doomed traversal.
|
||||
func TestServiceGetTrieNodesQueryPathLength(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
|
|||
|
|
@ -476,15 +476,15 @@ func ServiceGetTrieNodesQuery(chain *core.BlockChain, req *GetTrieNodesPacket) (
|
|||
// the request, matching the existing behavior for non-existent nodes.
|
||||
if len(accKey) > maxTrieNodePathLength {
|
||||
nodes = append(nodes, nil)
|
||||
break
|
||||
} else {
|
||||
blob, resolved, err := accTrie.GetNode(accKey)
|
||||
loads += resolved // always account database reads, even for failures
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
nodes = append(nodes, blob)
|
||||
bytes += uint64(len(blob))
|
||||
}
|
||||
blob, resolved, err := accTrie.GetNode(accKey)
|
||||
loads += resolved // always account database reads, even for failures
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
nodes = append(nodes, blob)
|
||||
bytes += uint64(len(blob))
|
||||
|
||||
default:
|
||||
// Storage slots requested, open the storage trie and retrieve from there
|
||||
|
|
@ -528,18 +528,15 @@ func ServiceGetTrieNodesQuery(chain *core.BlockChain, req *GetTrieNodesPacket) (
|
|||
// the response stays positionally aligned with the request.
|
||||
if len(path) > maxTrieNodePathLength {
|
||||
nodes = append(nodes, nil)
|
||||
if bytes > req.Bytes || loads > maxTrieNodeLookups || time.Since(start) > maxTrieNodeTimeSpent {
|
||||
} else {
|
||||
blob, resolved, err := stTrie.GetNode(path)
|
||||
loads += resolved // always account database reads, even for failures
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
continue
|
||||
nodes = append(nodes, blob)
|
||||
bytes += uint64(len(blob))
|
||||
}
|
||||
blob, resolved, err := stTrie.GetNode(path)
|
||||
loads += resolved // always account database reads, even for failures
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
nodes = append(nodes, blob)
|
||||
bytes += uint64(len(blob))
|
||||
|
||||
// Sanity check limits to avoid DoS on the store trie loads
|
||||
if bytes > req.Bytes || loads > maxTrieNodeLookups || time.Since(start) > maxTrieNodeTimeSpent {
|
||||
|
|
|
|||
Loading…
Reference in a new issue