diff --git a/trie/bintrie/binary_node_test.go b/trie/bintrie/binary_node_test.go
new file mode 100644
index 0000000000..5db6e111f0
--- /dev/null
+++ b/trie/bintrie/binary_node_test.go
@@ -0,0 +1,269 @@
+// Copyright 2025 go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package bintrie
+
+import (
+ "bytes"
+ "errors"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+)
+
+// TestSerializeDeserializeInternalNode tests serialization and deserialization of InternalNode
+func TestSerializeDeserializeInternalNode(t *testing.T) {
+ // Create an internal node with two hashed children
+ leftHash := common.HexToHash("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef")
+ rightHash := common.HexToHash("0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321")
+
+ node := &InternalNode{
+ depth: 5,
+ Left: HashedNode(leftHash),
+ Right: HashedNode(rightHash),
+ }
+
+ // Serialize the node
+ serialized := SerializeNode(node)
+
+ // Check the serialized format
+ if serialized[0] != 1 {
+ t.Errorf("Expected type byte to be 1, got %d", serialized[0])
+ }
+
+ if len(serialized) != 65 {
+ t.Errorf("Expected serialized length to be 65, got %d", len(serialized))
+ }
+
+ // Deserialize the node
+ deserialized, err := DeserializeNode(serialized, 5)
+ if err != nil {
+ t.Fatalf("Failed to deserialize node: %v", err)
+ }
+
+ // Check that it's an internal node
+ internalNode, ok := deserialized.(*InternalNode)
+ if !ok {
+ t.Fatalf("Expected InternalNode, got %T", deserialized)
+ }
+
+ // Check the depth
+ if internalNode.depth != 5 {
+ t.Errorf("Expected depth 5, got %d", internalNode.depth)
+ }
+
+ // Check the left and right hashes
+ if internalNode.Left.Hash() != leftHash {
+ t.Errorf("Left hash mismatch: expected %x, got %x", leftHash, internalNode.Left.Hash())
+ }
+
+ if internalNode.Right.Hash() != rightHash {
+ t.Errorf("Right hash mismatch: expected %x, got %x", rightHash, internalNode.Right.Hash())
+ }
+}
+
+// TestSerializeDeserializeStemNode tests serialization and deserialization of StemNode
+func TestSerializeDeserializeStemNode(t *testing.T) {
+ // Create a stem node with some values
+ stem := make([]byte, 31)
+ for i := range stem {
+ stem[i] = byte(i)
+ }
+
+ var values [256][]byte
+ // Add some values at different indices
+ values[0] = common.HexToHash("0x0101010101010101010101010101010101010101010101010101010101010101").Bytes()
+ values[10] = common.HexToHash("0x0202020202020202020202020202020202020202020202020202020202020202").Bytes()
+ values[255] = common.HexToHash("0x0303030303030303030303030303030303030303030303030303030303030303").Bytes()
+
+ node := &StemNode{
+ Stem: stem,
+ Values: values[:],
+ depth: 10,
+ }
+
+ // Serialize the node
+ serialized := SerializeNode(node)
+
+ // Check the serialized format
+ if serialized[0] != 2 {
+ t.Errorf("Expected type byte to be 2, got %d", serialized[0])
+ }
+
+ // Check the stem is correctly serialized
+ if !bytes.Equal(serialized[1:32], stem) {
+ t.Errorf("Stem mismatch in serialized data")
+ }
+
+ // Deserialize the node
+ deserialized, err := DeserializeNode(serialized, 10)
+ if err != nil {
+ t.Fatalf("Failed to deserialize node: %v", err)
+ }
+
+ // Check that it's a stem node
+ stemNode, ok := deserialized.(*StemNode)
+ if !ok {
+ t.Fatalf("Expected StemNode, got %T", deserialized)
+ }
+
+ // Check the stem
+ if !bytes.Equal(stemNode.Stem, stem) {
+ t.Errorf("Stem mismatch after deserialization")
+ }
+
+ // Check the values
+ if !bytes.Equal(stemNode.Values[0], values[0]) {
+ t.Errorf("Value at index 0 mismatch")
+ }
+ if !bytes.Equal(stemNode.Values[10], values[10]) {
+ t.Errorf("Value at index 10 mismatch")
+ }
+ if !bytes.Equal(stemNode.Values[255], values[255]) {
+ t.Errorf("Value at index 255 mismatch")
+ }
+
+ // Check that other values are nil
+ if stemNode.Values[1] != nil {
+ t.Errorf("Expected nil value at index 1, got %x", stemNode.Values[1])
+ }
+}
+
+// TestDeserializeEmptyNode tests deserialization of empty node
+func TestDeserializeEmptyNode(t *testing.T) {
+ // Empty byte slice should deserialize to Empty node
+ deserialized, err := DeserializeNode([]byte{}, 0)
+ if err != nil {
+ t.Fatalf("Failed to deserialize empty node: %v", err)
+ }
+
+ _, ok := deserialized.(Empty)
+ if !ok {
+ t.Fatalf("Expected Empty node, got %T", deserialized)
+ }
+}
+
+// TestDeserializeInvalidType tests deserialization with invalid type byte
+func TestDeserializeInvalidType(t *testing.T) {
+ // Create invalid serialized data with unknown type byte
+ invalidData := []byte{99, 0, 0, 0} // Type byte 99 is invalid
+
+ _, err := DeserializeNode(invalidData, 0)
+ if err == nil {
+ t.Fatal("Expected error for invalid type byte, got nil")
+ }
+}
+
+// TestDeserializeInvalidLength tests deserialization with invalid data length
+func TestDeserializeInvalidLength(t *testing.T) {
+ // InternalNode with type byte 1 but wrong length
+ invalidData := []byte{1, 0, 0} // Too short for internal node
+
+ _, err := DeserializeNode(invalidData, 0)
+ if err == nil {
+ t.Fatal("Expected error for invalid data length, got nil")
+ }
+
+ if err.Error() != "invalid serialized node length" {
+ t.Errorf("Expected 'invalid serialized node length' error, got: %v", err)
+ }
+}
+
+// TestKeyToPath tests the keyToPath function
+func TestKeyToPath(t *testing.T) {
+ tests := []struct {
+ name string
+ depth int
+ key []byte
+ expected []byte
+ wantErr bool
+ }{
+ {
+ name: "depth 0",
+ depth: 0,
+ key: []byte{0x80}, // 10000000 in binary
+ expected: []byte{1},
+ wantErr: false,
+ },
+ {
+ name: "depth 7",
+ depth: 7,
+ key: []byte{0xFF}, // 11111111 in binary
+ expected: []byte{1, 1, 1, 1, 1, 1, 1, 1},
+ wantErr: false,
+ },
+ {
+ name: "depth crossing byte boundary",
+ depth: 10,
+ key: []byte{0xFF, 0x00}, // 11111111 00000000 in binary
+ expected: []byte{1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
+ wantErr: false,
+ },
+ {
+ name: "max valid depth",
+ depth: 31 * 8,
+ key: make([]byte, 32),
+ expected: make([]byte, 31*8+1),
+ wantErr: false,
+ },
+ {
+ name: "depth too large",
+ depth: 31*8 + 1,
+ key: make([]byte, 32),
+ wantErr: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ path, err := keyToPath(tt.depth, tt.key)
+ if tt.wantErr {
+ if err == nil {
+ t.Errorf("Expected error for depth %d, got nil", tt.depth)
+ }
+ return
+ }
+ if err != nil {
+ t.Errorf("Unexpected error: %v", err)
+ return
+ }
+ if !bytes.Equal(path, tt.expected) {
+ t.Errorf("Path mismatch: expected %v, got %v", tt.expected, path)
+ }
+ })
+ }
+}
+
+// Mock resolver function for testing
+func mockResolver(path []byte, hash common.Hash) ([]byte, error) {
+ // Return a simple stem node for testing
+ if hash == common.HexToHash("0x1234") {
+ stem := make([]byte, 31)
+ var values [256][]byte
+ values[0] = common.HexToHash("0xabcd").Bytes()
+ node := &StemNode{
+ Stem: stem,
+ Values: values[:],
+ }
+ return SerializeNode(node), nil
+ }
+ return nil, errors.New("node not found")
+}
+
+// Mock flush function for testing
+func mockFlushFn(path []byte, node BinaryNode) {
+ // Just a stub for testing
+}
diff --git a/trie/bintrie/empty_test.go b/trie/bintrie/empty_test.go
new file mode 100644
index 0000000000..a3c3681cad
--- /dev/null
+++ b/trie/bintrie/empty_test.go
@@ -0,0 +1,222 @@
+// Copyright 2025 go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package bintrie
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+)
+
+// TestEmptyGet tests the Get method
+func TestEmptyGet(t *testing.T) {
+ node := Empty{}
+
+ key := make([]byte, 32)
+ value, err := node.Get(key, nil)
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+
+ if value != nil {
+ t.Errorf("Expected nil value from empty node, got %x", value)
+ }
+}
+
+// TestEmptyInsert tests the Insert method
+func TestEmptyInsert(t *testing.T) {
+ node := Empty{}
+
+ key := make([]byte, 32)
+ key[0] = 0x12
+ key[31] = 0x34
+ value := common.HexToHash("0xabcd").Bytes()
+
+ newNode, err := node.Insert(key, value, nil)
+ if err != nil {
+ t.Fatalf("Failed to insert: %v", err)
+ }
+
+ // Should create a StemNode
+ stemNode, ok := newNode.(*StemNode)
+ if !ok {
+ t.Fatalf("Expected StemNode, got %T", newNode)
+ }
+
+ // Check the stem (first 31 bytes of key)
+ if !bytes.Equal(stemNode.Stem, key[:31]) {
+ t.Errorf("Stem mismatch: expected %x, got %x", key[:31], stemNode.Stem)
+ }
+
+ // Check the value at the correct index (last byte of key)
+ if !bytes.Equal(stemNode.Values[key[31]], value) {
+ t.Errorf("Value mismatch at index %d: expected %x, got %x", key[31], value, stemNode.Values[key[31]])
+ }
+
+ // Check that other values are nil
+ for i := 0; i < 256; i++ {
+ if i != int(key[31]) && stemNode.Values[i] != nil {
+ t.Errorf("Expected nil value at index %d, got %x", i, stemNode.Values[i])
+ }
+ }
+}
+
+// TestEmptyCopy tests the Copy method
+func TestEmptyCopy(t *testing.T) {
+ node := Empty{}
+
+ copied := node.Copy()
+ copiedEmpty, ok := copied.(Empty)
+ if !ok {
+ t.Fatalf("Expected Empty, got %T", copied)
+ }
+
+ // Both should be empty
+ if node != copiedEmpty {
+ // Empty is a zero-value struct, so copies should be equal
+ t.Errorf("Empty nodes should be equal")
+ }
+}
+
+// TestEmptyHash tests the Hash method
+func TestEmptyHash(t *testing.T) {
+ node := Empty{}
+
+ hash := node.Hash()
+
+ // Empty node should have zero hash
+ if hash != (common.Hash{}) {
+ t.Errorf("Expected zero hash for empty node, got %x", hash)
+ }
+}
+
+// TestEmptyGetValuesAtStem tests the GetValuesAtStem method
+func TestEmptyGetValuesAtStem(t *testing.T) {
+ node := Empty{}
+
+ stem := make([]byte, 31)
+ values, err := node.GetValuesAtStem(stem, nil)
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+
+ // Should return an array of 256 nil values
+ if len(values) != 256 {
+ t.Errorf("Expected 256 values, got %d", len(values))
+ }
+
+ for i, v := range values {
+ if v != nil {
+ t.Errorf("Expected nil value at index %d, got %x", i, v)
+ }
+ }
+}
+
+// TestEmptyInsertValuesAtStem tests the InsertValuesAtStem method
+func TestEmptyInsertValuesAtStem(t *testing.T) {
+ node := Empty{}
+
+ stem := make([]byte, 31)
+ stem[0] = 0x42
+
+ var values [256][]byte
+ values[0] = common.HexToHash("0x0101").Bytes()
+ values[10] = common.HexToHash("0x0202").Bytes()
+ values[255] = common.HexToHash("0x0303").Bytes()
+
+ newNode, err := node.InsertValuesAtStem(stem, values[:], nil, 5)
+ if err != nil {
+ t.Fatalf("Failed to insert values: %v", err)
+ }
+
+ // Should create a StemNode
+ stemNode, ok := newNode.(*StemNode)
+ if !ok {
+ t.Fatalf("Expected StemNode, got %T", newNode)
+ }
+
+ // Check the stem
+ if !bytes.Equal(stemNode.Stem, stem) {
+ t.Errorf("Stem mismatch: expected %x, got %x", stem, stemNode.Stem)
+ }
+
+ // Check the depth
+ if stemNode.depth != 5 {
+ t.Errorf("Depth mismatch: expected 5, got %d", stemNode.depth)
+ }
+
+ // Check the values
+ if !bytes.Equal(stemNode.Values[0], values[0]) {
+ t.Error("Value at index 0 mismatch")
+ }
+ if !bytes.Equal(stemNode.Values[10], values[10]) {
+ t.Error("Value at index 10 mismatch")
+ }
+ if !bytes.Equal(stemNode.Values[255], values[255]) {
+ t.Error("Value at index 255 mismatch")
+ }
+
+ // Check that values is the same slice (not a copy)
+ if &stemNode.Values[0] != &values[0] {
+ t.Error("Expected values to be the same slice reference")
+ }
+}
+
+// TestEmptyCollectNodes tests the CollectNodes method
+func TestEmptyCollectNodes(t *testing.T) {
+ node := Empty{}
+
+ var collected []BinaryNode
+ flushFn := func(path []byte, n BinaryNode) {
+ collected = append(collected, n)
+ }
+
+ err := node.CollectNodes([]byte{0, 1, 0}, flushFn)
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+
+ // Should not collect anything for empty node
+ if len(collected) != 0 {
+ t.Errorf("Expected no collected nodes for empty, got %d", len(collected))
+ }
+}
+
+// TestEmptyToDot tests the toDot method
+func TestEmptyToDot(t *testing.T) {
+ node := Empty{}
+
+ dot := node.toDot("parent", "010")
+
+ // Should return empty string for empty node
+ if dot != "" {
+ t.Errorf("Expected empty string for empty node toDot, got %s", dot)
+ }
+}
+
+// TestEmptyGetHeight tests the GetHeight method
+func TestEmptyGetHeight(t *testing.T) {
+ node := Empty{}
+
+ height := node.GetHeight()
+
+ // Empty node should have height 0
+ if height != 0 {
+ t.Errorf("Expected height 0 for empty node, got %d", height)
+ }
+}
diff --git a/trie/bintrie/hashed_node_test.go b/trie/bintrie/hashed_node_test.go
new file mode 100644
index 0000000000..dbc6a7a244
--- /dev/null
+++ b/trie/bintrie/hashed_node_test.go
@@ -0,0 +1,174 @@
+// Copyright 2025 go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package bintrie
+
+import (
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+)
+
+// TestHashedNodeHash tests the Hash method
+func TestHashedNodeHash(t *testing.T) {
+ hash := common.HexToHash("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef")
+ node := HashedNode(hash)
+
+ // Hash should return the stored hash
+ if node.Hash() != hash {
+ t.Errorf("Hash mismatch: expected %x, got %x", hash, node.Hash())
+ }
+}
+
+// TestHashedNodeCopy tests the Copy method
+func TestHashedNodeCopy(t *testing.T) {
+ hash := common.HexToHash("0xabcdef")
+ node := HashedNode(hash)
+
+ copied := node.Copy()
+ copiedHash, ok := copied.(HashedNode)
+ if !ok {
+ t.Fatalf("Expected HashedNode, got %T", copied)
+ }
+
+ // Hash should be the same
+ if common.Hash(copiedHash) != hash {
+ t.Errorf("Hash mismatch after copy: expected %x, got %x", hash, copiedHash)
+ }
+
+ // But should be a different object
+ if &node == &copiedHash {
+ t.Error("Copy returned same object reference")
+ }
+}
+
+// TestHashedNodeInsert tests that Insert returns an error
+func TestHashedNodeInsert(t *testing.T) {
+ node := HashedNode(common.HexToHash("0x1234"))
+
+ key := make([]byte, 32)
+ value := make([]byte, 32)
+
+ _, err := node.Insert(key, value, nil)
+ if err == nil {
+ t.Fatal("Expected error for Insert on HashedNode")
+ }
+
+ if err.Error() != "insert not implemented for hashed node" {
+ t.Errorf("Unexpected error message: %v", err)
+ }
+}
+
+// TestHashedNodeGetValuesAtStem tests that GetValuesAtStem returns an error
+func TestHashedNodeGetValuesAtStem(t *testing.T) {
+ node := HashedNode(common.HexToHash("0x1234"))
+
+ stem := make([]byte, 31)
+ _, err := node.GetValuesAtStem(stem, nil)
+ if err == nil {
+ t.Fatal("Expected error for GetValuesAtStem on HashedNode")
+ }
+
+ if err.Error() != "attempted to get values from an unresolved node" {
+ t.Errorf("Unexpected error message: %v", err)
+ }
+}
+
+// TestHashedNodeInsertValuesAtStem tests that InsertValuesAtStem returns an error
+func TestHashedNodeInsertValuesAtStem(t *testing.T) {
+ node := HashedNode(common.HexToHash("0x1234"))
+
+ stem := make([]byte, 31)
+ values := make([][]byte, 256)
+
+ _, err := node.InsertValuesAtStem(stem, values, nil, 0)
+ if err == nil {
+ t.Fatal("Expected error for InsertValuesAtStem on HashedNode")
+ }
+
+ if err.Error() != "insertValuesAtStem not implemented for hashed node" {
+ t.Errorf("Unexpected error message: %v", err)
+ }
+}
+
+// TestHashedNodeGet tests that Get panics (as per implementation)
+func TestHashedNodeGet(t *testing.T) {
+ node := HashedNode(common.HexToHash("0x1234"))
+
+ defer func() {
+ if r := recover(); r == nil {
+ t.Error("Expected panic for Get on HashedNode")
+ }
+ }()
+
+ key := make([]byte, 32)
+ _, _ = node.Get(key, nil)
+}
+
+// TestHashedNodeCollectNodes tests that CollectNodes panics (as per implementation)
+func TestHashedNodeCollectNodes(t *testing.T) {
+ node := HashedNode(common.HexToHash("0x1234"))
+
+ defer func() {
+ if r := recover(); r == nil {
+ t.Error("Expected panic for CollectNodes on HashedNode")
+ }
+ }()
+
+ path := []byte{0, 1, 0}
+ node.CollectNodes(path, func([]byte, BinaryNode) {})
+}
+
+// TestHashedNodeGetHeight tests that GetHeight panics (as per implementation)
+func TestHashedNodeGetHeight(t *testing.T) {
+ node := HashedNode(common.HexToHash("0x1234"))
+
+ defer func() {
+ r := recover()
+ if r == nil {
+ t.Error("Expected panic for GetHeight on HashedNode")
+ }
+ // Check the panic message
+ if r != "tried to get the height of a hashed node, this is a bug" {
+ t.Errorf("Unexpected panic message: %v", r)
+ }
+ }()
+
+ _ = node.GetHeight()
+}
+
+// TestHashedNodeToDot tests the toDot method for visualization
+func TestHashedNodeToDot(t *testing.T) {
+ hash := common.HexToHash("0x1234")
+ node := HashedNode(hash)
+
+ dot := node.toDot("parent", "010")
+
+ // Should contain the hash value and parent connection
+ expectedHash := "hash010"
+ if !contains(dot, expectedHash) {
+ t.Errorf("Expected dot output to contain %s", expectedHash)
+ }
+
+ if !contains(dot, "parent -> hash010") {
+ t.Error("Expected dot output to contain parent connection")
+ }
+}
+
+// Helper function
+func contains(s, substr string) bool {
+ return len(s) >= len(substr) && s[:len(s)] != "" && len(substr) > 0
+}
diff --git a/trie/bintrie/internal_node_test.go b/trie/bintrie/internal_node_test.go
new file mode 100644
index 0000000000..142f1e95c1
--- /dev/null
+++ b/trie/bintrie/internal_node_test.go
@@ -0,0 +1,458 @@
+// Copyright 2025 go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package bintrie
+
+import (
+ "bytes"
+ "errors"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+)
+
+// TestInternalNodeGet tests the Get method
+func TestInternalNodeGet(t *testing.T) {
+ // Create a simple tree structure
+ leftStem := make([]byte, 31)
+ rightStem := make([]byte, 31)
+ rightStem[0] = 0x80 // First bit is 1
+
+ var leftValues, rightValues [256][]byte
+ leftValues[0] = common.HexToHash("0x0101").Bytes()
+ rightValues[0] = common.HexToHash("0x0202").Bytes()
+
+ node := &InternalNode{
+ depth: 0,
+ Left: &StemNode{
+ Stem: leftStem,
+ Values: leftValues[:],
+ depth: 1,
+ },
+ Right: &StemNode{
+ Stem: rightStem,
+ Values: rightValues[:],
+ depth: 1,
+ },
+ }
+
+ // Get value from left subtree
+ leftKey := make([]byte, 32)
+ leftKey[31] = 0
+ value, err := node.Get(leftKey, nil)
+ if err != nil {
+ t.Fatalf("Failed to get left value: %v", err)
+ }
+ if !bytes.Equal(value, leftValues[0]) {
+ t.Errorf("Left value mismatch: expected %x, got %x", leftValues[0], value)
+ }
+
+ // Get value from right subtree
+ rightKey := make([]byte, 32)
+ rightKey[0] = 0x80
+ rightKey[31] = 0
+ value, err = node.Get(rightKey, nil)
+ if err != nil {
+ t.Fatalf("Failed to get right value: %v", err)
+ }
+ if !bytes.Equal(value, rightValues[0]) {
+ t.Errorf("Right value mismatch: expected %x, got %x", rightValues[0], value)
+ }
+}
+
+// TestInternalNodeGetWithResolver tests Get with HashedNode resolution
+func TestInternalNodeGetWithResolver(t *testing.T) {
+ // Create an internal node with a hashed child
+ hashedChild := HashedNode(common.HexToHash("0x1234"))
+
+ node := &InternalNode{
+ depth: 0,
+ Left: hashedChild,
+ Right: Empty{},
+ }
+
+ // Mock resolver that returns a stem node
+ resolver := func(path []byte, hash common.Hash) ([]byte, error) {
+ if hash == common.Hash(hashedChild) {
+ stem := make([]byte, 31)
+ var values [256][]byte
+ values[5] = common.HexToHash("0xabcd").Bytes()
+ stemNode := &StemNode{
+ Stem: stem,
+ Values: values[:],
+ depth: 1,
+ }
+ return SerializeNode(stemNode), nil
+ }
+ return nil, errors.New("node not found")
+ }
+
+ // Get value through the hashed node
+ key := make([]byte, 32)
+ key[31] = 5
+ value, err := node.Get(key, resolver)
+ if err != nil {
+ t.Fatalf("Failed to get value: %v", err)
+ }
+
+ expectedValue := common.HexToHash("0xabcd").Bytes()
+ if !bytes.Equal(value, expectedValue) {
+ t.Errorf("Value mismatch: expected %x, got %x", expectedValue, value)
+ }
+}
+
+// TestInternalNodeInsert tests the Insert method
+func TestInternalNodeInsert(t *testing.T) {
+ // Start with an internal node with empty children
+ node := &InternalNode{
+ depth: 0,
+ Left: Empty{},
+ Right: Empty{},
+ }
+
+ // Insert a value into the left subtree
+ leftKey := make([]byte, 32)
+ leftKey[31] = 10
+ leftValue := common.HexToHash("0x0101").Bytes()
+
+ newNode, err := node.Insert(leftKey, leftValue, nil)
+ if err != nil {
+ t.Fatalf("Failed to insert: %v", err)
+ }
+
+ internalNode, ok := newNode.(*InternalNode)
+ if !ok {
+ t.Fatalf("Expected InternalNode, got %T", newNode)
+ }
+
+ // Check that left child is now a StemNode
+ leftStem, ok := internalNode.Left.(*StemNode)
+ if !ok {
+ t.Fatalf("Expected left child to be StemNode, got %T", internalNode.Left)
+ }
+
+ // Check the inserted value
+ if !bytes.Equal(leftStem.Values[10], leftValue) {
+ t.Errorf("Value mismatch: expected %x, got %x", leftValue, leftStem.Values[10])
+ }
+
+ // Right child should still be Empty
+ _, ok = internalNode.Right.(Empty)
+ if !ok {
+ t.Errorf("Expected right child to remain Empty, got %T", internalNode.Right)
+ }
+}
+
+// TestInternalNodeCopy tests the Copy method
+func TestInternalNodeCopy(t *testing.T) {
+ // Create an internal node with stem children
+ leftStem := &StemNode{
+ Stem: make([]byte, 31),
+ Values: make([][]byte, 256),
+ depth: 1,
+ }
+ leftStem.Values[0] = common.HexToHash("0x0101").Bytes()
+
+ rightStem := &StemNode{
+ Stem: make([]byte, 31),
+ Values: make([][]byte, 256),
+ depth: 1,
+ }
+ rightStem.Stem[0] = 0x80
+ rightStem.Values[0] = common.HexToHash("0x0202").Bytes()
+
+ node := &InternalNode{
+ depth: 0,
+ Left: leftStem,
+ Right: rightStem,
+ }
+
+ // Create a copy
+ copied := node.Copy()
+ copiedInternal, ok := copied.(*InternalNode)
+ if !ok {
+ t.Fatalf("Expected InternalNode, got %T", copied)
+ }
+
+ // Check depth
+ if copiedInternal.depth != node.depth {
+ t.Errorf("Depth mismatch: expected %d, got %d", node.depth, copiedInternal.depth)
+ }
+
+ // Check that children are copied
+ copiedLeft, ok := copiedInternal.Left.(*StemNode)
+ if !ok {
+ t.Fatalf("Expected left child to be StemNode, got %T", copiedInternal.Left)
+ }
+
+ copiedRight, ok := copiedInternal.Right.(*StemNode)
+ if !ok {
+ t.Fatalf("Expected right child to be StemNode, got %T", copiedInternal.Right)
+ }
+
+ // Verify deep copy (children should be different objects)
+ if copiedLeft == leftStem {
+ t.Error("Left child not properly copied")
+ }
+ if copiedRight == rightStem {
+ t.Error("Right child not properly copied")
+ }
+
+ // But values should be equal
+ if !bytes.Equal(copiedLeft.Values[0], leftStem.Values[0]) {
+ t.Error("Left child value mismatch after copy")
+ }
+ if !bytes.Equal(copiedRight.Values[0], rightStem.Values[0]) {
+ t.Error("Right child value mismatch after copy")
+ }
+}
+
+// TestInternalNodeHash tests the Hash method
+func TestInternalNodeHash(t *testing.T) {
+ // Create an internal node
+ node := &InternalNode{
+ depth: 0,
+ Left: HashedNode(common.HexToHash("0x1111")),
+ Right: HashedNode(common.HexToHash("0x2222")),
+ }
+
+ hash1 := node.Hash()
+
+ // Hash should be deterministic
+ hash2 := node.Hash()
+ if hash1 != hash2 {
+ t.Errorf("Hash not deterministic: %x != %x", hash1, hash2)
+ }
+
+ // Changing a child should change the hash
+ node.Left = HashedNode(common.HexToHash("0x3333"))
+ hash3 := node.Hash()
+ if hash1 == hash3 {
+ t.Error("Hash didn't change after modifying left child")
+ }
+
+ // Test with nil children (should use zero hash)
+ nodeWithNil := &InternalNode{
+ depth: 0,
+ Left: nil,
+ Right: HashedNode(common.HexToHash("0x4444")),
+ }
+ hashWithNil := nodeWithNil.Hash()
+ if hashWithNil == (common.Hash{}) {
+ t.Error("Hash shouldn't be zero even with nil child")
+ }
+}
+
+// TestInternalNodeGetValuesAtStem tests GetValuesAtStem method
+func TestInternalNodeGetValuesAtStem(t *testing.T) {
+ // Create a tree with values at different stems
+ leftStem := make([]byte, 31)
+ rightStem := make([]byte, 31)
+ rightStem[0] = 0x80
+
+ var leftValues, rightValues [256][]byte
+ leftValues[0] = common.HexToHash("0x0101").Bytes()
+ leftValues[10] = common.HexToHash("0x0102").Bytes()
+ rightValues[0] = common.HexToHash("0x0201").Bytes()
+ rightValues[20] = common.HexToHash("0x0202").Bytes()
+
+ node := &InternalNode{
+ depth: 0,
+ Left: &StemNode{
+ Stem: leftStem,
+ Values: leftValues[:],
+ depth: 1,
+ },
+ Right: &StemNode{
+ Stem: rightStem,
+ Values: rightValues[:],
+ depth: 1,
+ },
+ }
+
+ // Get values from left stem
+ values, err := node.GetValuesAtStem(leftStem, nil)
+ if err != nil {
+ t.Fatalf("Failed to get left values: %v", err)
+ }
+ if !bytes.Equal(values[0], leftValues[0]) {
+ t.Error("Left value at index 0 mismatch")
+ }
+ if !bytes.Equal(values[10], leftValues[10]) {
+ t.Error("Left value at index 10 mismatch")
+ }
+
+ // Get values from right stem
+ values, err = node.GetValuesAtStem(rightStem, nil)
+ if err != nil {
+ t.Fatalf("Failed to get right values: %v", err)
+ }
+ if !bytes.Equal(values[0], rightValues[0]) {
+ t.Error("Right value at index 0 mismatch")
+ }
+ if !bytes.Equal(values[20], rightValues[20]) {
+ t.Error("Right value at index 20 mismatch")
+ }
+}
+
+// TestInternalNodeInsertValuesAtStem tests InsertValuesAtStem method
+func TestInternalNodeInsertValuesAtStem(t *testing.T) {
+ // Start with an internal node with empty children
+ node := &InternalNode{
+ depth: 0,
+ Left: Empty{},
+ Right: Empty{},
+ }
+
+ // Insert values at a stem in the left subtree
+ stem := make([]byte, 31)
+ var values [256][]byte
+ values[5] = common.HexToHash("0x0505").Bytes()
+ values[10] = common.HexToHash("0x1010").Bytes()
+
+ newNode, err := node.InsertValuesAtStem(stem, values[:], nil, 0)
+ if err != nil {
+ t.Fatalf("Failed to insert values: %v", err)
+ }
+
+ internalNode, ok := newNode.(*InternalNode)
+ if !ok {
+ t.Fatalf("Expected InternalNode, got %T", newNode)
+ }
+
+ // Check that left child is now a StemNode with the values
+ leftStem, ok := internalNode.Left.(*StemNode)
+ if !ok {
+ t.Fatalf("Expected left child to be StemNode, got %T", internalNode.Left)
+ }
+
+ if !bytes.Equal(leftStem.Values[5], values[5]) {
+ t.Error("Value at index 5 mismatch")
+ }
+ if !bytes.Equal(leftStem.Values[10], values[10]) {
+ t.Error("Value at index 10 mismatch")
+ }
+}
+
+// TestInternalNodeCollectNodes tests CollectNodes method
+func TestInternalNodeCollectNodes(t *testing.T) {
+ // Create an internal node with two stem children
+ leftStem := &StemNode{
+ Stem: make([]byte, 31),
+ Values: make([][]byte, 256),
+ depth: 1,
+ }
+
+ rightStem := &StemNode{
+ Stem: make([]byte, 31),
+ Values: make([][]byte, 256),
+ depth: 1,
+ }
+ rightStem.Stem[0] = 0x80
+
+ node := &InternalNode{
+ depth: 0,
+ Left: leftStem,
+ Right: rightStem,
+ }
+
+ var collectedPaths [][]byte
+ var collectedNodes []BinaryNode
+
+ flushFn := func(path []byte, n BinaryNode) {
+ pathCopy := make([]byte, len(path))
+ copy(pathCopy, path)
+ collectedPaths = append(collectedPaths, pathCopy)
+ collectedNodes = append(collectedNodes, n)
+ }
+
+ err := node.CollectNodes([]byte{1}, flushFn)
+ if err != nil {
+ t.Fatalf("Failed to collect nodes: %v", err)
+ }
+
+ // Should have collected 3 nodes: left stem, right stem, and the internal node itself
+ if len(collectedNodes) != 3 {
+ t.Errorf("Expected 3 collected nodes, got %d", len(collectedNodes))
+ }
+
+ // Check paths
+ expectedPaths := [][]byte{
+ {1, 0}, // left child
+ {1, 1}, // right child
+ {1}, // internal node itself
+ }
+
+ for i, expectedPath := range expectedPaths {
+ if !bytes.Equal(collectedPaths[i], expectedPath) {
+ t.Errorf("Path %d mismatch: expected %v, got %v", i, expectedPath, collectedPaths[i])
+ }
+ }
+}
+
+// TestInternalNodeGetHeight tests GetHeight method
+func TestInternalNodeGetHeight(t *testing.T) {
+ // Create a tree with different heights
+ // Left subtree: depth 2 (internal -> stem)
+ // Right subtree: depth 1 (stem)
+ leftInternal := &InternalNode{
+ depth: 1,
+ Left: &StemNode{
+ Stem: make([]byte, 31),
+ Values: make([][]byte, 256),
+ depth: 2,
+ },
+ Right: Empty{},
+ }
+
+ rightStem := &StemNode{
+ Stem: make([]byte, 31),
+ Values: make([][]byte, 256),
+ depth: 1,
+ }
+
+ node := &InternalNode{
+ depth: 0,
+ Left: leftInternal,
+ Right: rightStem,
+ }
+
+ height := node.GetHeight()
+ // Height should be max(left height, right height) + 1
+ // Left height: 2, Right height: 1, so total: 3
+ if height != 3 {
+ t.Errorf("Expected height 3, got %d", height)
+ }
+}
+
+// TestInternalNodeDepthTooLarge tests handling of excessive depth
+func TestInternalNodeDepthTooLarge(t *testing.T) {
+ // Create an internal node at max depth
+ node := &InternalNode{
+ depth: 31*8 + 1,
+ Left: Empty{},
+ Right: Empty{},
+ }
+
+ stem := make([]byte, 31)
+ _, err := node.GetValuesAtStem(stem, nil)
+ if err == nil {
+ t.Fatal("Expected error for excessive depth")
+ }
+ if err.Error() != "node too deep" {
+ t.Errorf("Expected 'node too deep' error, got: %v", err)
+ }
+}
diff --git a/trie/binary_iterator.go b/trie/bintrie/iterator.go
similarity index 86%
rename from trie/binary_iterator.go
rename to trie/bintrie/iterator.go
index e4799d207a..94ce3895df 100644
--- a/trie/binary_iterator.go
+++ b/trie/bintrie/iterator.go
@@ -14,31 +14,35 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package trie
+package bintrie
import (
+ "errors"
+
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/trie/bintrie"
+ "github.com/ethereum/go-ethereum/trie"
)
+var errIteratorEnd = errors.New("end of iteration")
+
type binaryNodeIteratorState struct {
- Node bintrie.BinaryNode
+ Node BinaryNode
Index int
}
type binaryNodeIterator struct {
trie *BinaryTrie
- current bintrie.BinaryNode
+ current BinaryNode
lastErr error
stack []binaryNodeIteratorState
}
-func newBinaryNodeIterator(trie *BinaryTrie, _ []byte) (NodeIterator, error) {
- if trie.Hash() == zero {
- return new(nodeIterator), nil
+func newBinaryNodeIterator(t *BinaryTrie, _ []byte) (trie.NodeIterator, error) {
+ if t.Hash() == zero {
+ return &binaryNodeIterator{trie: t, lastErr: errIteratorEnd}, nil
}
- it := &binaryNodeIterator{trie: trie, current: trie.root}
+ it := &binaryNodeIterator{trie: t, current: t.root}
// it.err = it.seek(start)
return it, nil
}
@@ -59,13 +63,13 @@ func (it *binaryNodeIterator) Next(descend bool) bool {
}
switch node := it.current.(type) {
- case *bintrie.InternalNode:
+ case *InternalNode:
// index: 0 = nothing visited, 1=left visited, 2=right visited
context := &it.stack[len(it.stack)-1]
// recurse into both children
if context.Index == 0 {
- if _, isempty := node.Left.(bintrie.Empty); node.Left != nil && !isempty {
+ if _, isempty := node.Left.(Empty); node.Left != nil && !isempty {
it.stack = append(it.stack, binaryNodeIteratorState{Node: node.Left})
it.current = node.Left
return it.Next(descend)
@@ -75,7 +79,7 @@ func (it *binaryNodeIterator) Next(descend bool) bool {
}
if context.Index == 1 {
- if _, isempty := node.Right.(bintrie.Empty); node.Right != nil && !isempty {
+ if _, isempty := node.Right.(Empty); node.Right != nil && !isempty {
it.stack = append(it.stack, binaryNodeIteratorState{Node: node.Right})
it.current = node.Right
return it.Next(descend)
@@ -94,7 +98,7 @@ func (it *binaryNodeIterator) Next(descend bool) bool {
it.current = it.stack[len(it.stack)-1].Node
it.stack[len(it.stack)-1].Index++
return it.Next(descend)
- case *bintrie.StemNode:
+ case *StemNode:
// Look for the next non-empty value
for i := it.stack[len(it.stack)-1].Index; i < 256; i++ {
if node.Values[i] != nil {
@@ -108,13 +112,13 @@ func (it *binaryNodeIterator) Next(descend bool) bool {
it.current = it.stack[len(it.stack)-1].Node
it.stack[len(it.stack)-1].Index++
return it.Next(descend)
- case bintrie.HashedNode:
+ case HashedNode:
// resolve the node
data, err := it.trie.FlatdbNodeResolver(it.Path(), common.Hash(node))
if err != nil {
panic(err)
}
- it.current, err = bintrie.DeserializeNode(data, len(it.stack)-1)
+ it.current, err = DeserializeNode(data, len(it.stack)-1)
if err != nil {
panic(err)
}
@@ -123,12 +127,12 @@ func (it *binaryNodeIterator) Next(descend bool) bool {
it.stack[len(it.stack)-1].Node = it.current
parent := &it.stack[len(it.stack)-2]
if parent.Index == 0 {
- parent.Node.(*bintrie.InternalNode).Left = it.current
+ parent.Node.(*InternalNode).Left = it.current
} else {
- parent.Node.(*bintrie.InternalNode).Right = it.current
+ parent.Node.(*InternalNode).Right = it.current
}
return it.Next(descend)
- case bintrie.Empty:
+ case Empty:
// do nothing
return false
default:
@@ -179,7 +183,7 @@ func (it *binaryNodeIterator) NodeBlob() []byte {
// Leaf returns true iff the current node is a leaf node.
func (it *binaryNodeIterator) Leaf() bool {
- _, ok := it.current.(*bintrie.StemNode)
+ _, ok := it.current.(*StemNode)
return ok
}
@@ -187,7 +191,7 @@ func (it *binaryNodeIterator) Leaf() bool {
// positioned at a leaf. Callers must not retain references to the value after
// calling Next.
func (it *binaryNodeIterator) LeafKey() []byte {
- leaf, ok := it.current.(*bintrie.StemNode)
+ leaf, ok := it.current.(*StemNode)
if !ok {
panic("Leaf() called on an binary node iterator not at a leaf location")
}
@@ -199,7 +203,7 @@ func (it *binaryNodeIterator) LeafKey() []byte {
// is not positioned at a leaf. Callers must not retain references to the value
// after calling Next.
func (it *binaryNodeIterator) LeafBlob() []byte {
- leaf, ok := it.current.(*bintrie.StemNode)
+ leaf, ok := it.current.(*StemNode)
if !ok {
panic("LeafBlob() called on an binary node iterator not at a leaf location")
}
@@ -211,7 +215,7 @@ func (it *binaryNodeIterator) LeafBlob() []byte {
// iterator is not positioned at a leaf. Callers must not retain references
// to the value after calling Next.
func (it *binaryNodeIterator) LeafProof() [][]byte {
- _, ok := it.current.(*bintrie.StemNode)
+ _, ok := it.current.(*StemNode)
if !ok {
panic("LeafProof() called on an binary node iterator not at a leaf location")
}
@@ -231,6 +235,6 @@ func (it *binaryNodeIterator) LeafProof() [][]byte {
// Before adding a similar mechanism to any other place in Geth, consider
// making trie.Database an interface and wrapping at that level. It's a huge
// refactor, but it could be worth it if another occurrence arises.
-func (it *binaryNodeIterator) AddResolver(NodeResolver) {
+func (it *binaryNodeIterator) AddResolver(trie.NodeResolver) {
// Not implemented, but should not panic
}
diff --git a/trie/binary_iterator_test.go b/trie/bintrie/iterator_test.go
similarity index 80%
rename from trie/binary_iterator_test.go
rename to trie/bintrie/iterator_test.go
index 01a4373cd2..8773e9e0c5 100644
--- a/trie/binary_iterator_test.go
+++ b/trie/bintrie/iterator_test.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package trie
+package bintrie
import (
"testing"
@@ -22,9 +22,23 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/triedb"
+ "github.com/ethereum/go-ethereum/triedb/hashdb"
+ "github.com/ethereum/go-ethereum/triedb/pathdb"
"github.com/holiman/uint256"
)
+func newTestDatabase(diskdb ethdb.Database, scheme string) *triedb.Database {
+ config := &triedb.Config{Preimages: true}
+ if scheme == rawdb.HashScheme {
+ config.HashDB = &hashdb.Config{CleanCacheSize: 0}
+ } else {
+ config.PathDB = &pathdb.Config{TrieCleanSize: 0, StateCleanSize: 0}
+ }
+ return triedb.NewDatabase(diskdb, config)
+}
+
func TestBinaryIterator(t *testing.T) {
trie, err := NewBinaryTrie(types.EmptyVerkleHash, newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.PathScheme))
if err != nil {
diff --git a/trie/bintrie/stem_node_test.go b/trie/bintrie/stem_node_test.go
new file mode 100644
index 0000000000..47dda38f59
--- /dev/null
+++ b/trie/bintrie/stem_node_test.go
@@ -0,0 +1,373 @@
+// Copyright 2025 go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package bintrie
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+)
+
+// TestStemNodeInsertSameStem tests inserting values with the same stem
+func TestStemNodeInsertSameStem(t *testing.T) {
+ stem := make([]byte, 31)
+ for i := range stem {
+ stem[i] = byte(i)
+ }
+
+ var values [256][]byte
+ values[0] = common.HexToHash("0x0101").Bytes()
+
+ node := &StemNode{
+ Stem: stem,
+ Values: values[:],
+ depth: 0,
+ }
+
+ // Insert another value with the same stem but different last byte
+ key := make([]byte, 32)
+ copy(key[:31], stem)
+ key[31] = 10
+ value := common.HexToHash("0x0202").Bytes()
+
+ newNode, err := node.Insert(key, value, nil)
+ if err != nil {
+ t.Fatalf("Failed to insert: %v", err)
+ }
+
+ // Should still be a StemNode
+ stemNode, ok := newNode.(*StemNode)
+ if !ok {
+ t.Fatalf("Expected StemNode, got %T", newNode)
+ }
+
+ // Check that both values are present
+ if !bytes.Equal(stemNode.Values[0], values[0]) {
+ t.Errorf("Value at index 0 mismatch")
+ }
+ if !bytes.Equal(stemNode.Values[10], value) {
+ t.Errorf("Value at index 10 mismatch")
+ }
+}
+
+// TestStemNodeInsertDifferentStem tests inserting values with different stems
+func TestStemNodeInsertDifferentStem(t *testing.T) {
+ stem1 := make([]byte, 31)
+ for i := range stem1 {
+ stem1[i] = 0x00
+ }
+
+ var values [256][]byte
+ values[0] = common.HexToHash("0x0101").Bytes()
+
+ node := &StemNode{
+ Stem: stem1,
+ Values: values[:],
+ depth: 0,
+ }
+
+ // Insert with a different stem (first bit different)
+ key := make([]byte, 32)
+ key[0] = 0x80 // First bit is 1 instead of 0
+ value := common.HexToHash("0x0202").Bytes()
+
+ newNode, err := node.Insert(key, value, nil)
+ if err != nil {
+ t.Fatalf("Failed to insert: %v", err)
+ }
+
+ // Should now be an InternalNode
+ internalNode, ok := newNode.(*InternalNode)
+ if !ok {
+ t.Fatalf("Expected InternalNode, got %T", newNode)
+ }
+
+ // Check depth
+ if internalNode.depth != 0 {
+ t.Errorf("Expected depth 0, got %d", internalNode.depth)
+ }
+
+ // Original stem should be on the left (bit 0)
+ leftStem, ok := internalNode.Left.(*StemNode)
+ if !ok {
+ t.Fatalf("Expected left child to be StemNode, got %T", internalNode.Left)
+ }
+ if !bytes.Equal(leftStem.Stem, stem1) {
+ t.Errorf("Left stem mismatch")
+ }
+
+ // New stem should be on the right (bit 1)
+ rightStem, ok := internalNode.Right.(*StemNode)
+ if !ok {
+ t.Fatalf("Expected right child to be StemNode, got %T", internalNode.Right)
+ }
+ if !bytes.Equal(rightStem.Stem, key[:31]) {
+ t.Errorf("Right stem mismatch")
+ }
+}
+
+// TestStemNodeInsertInvalidValueLength tests inserting value with invalid length
+func TestStemNodeInsertInvalidValueLength(t *testing.T) {
+ stem := make([]byte, 31)
+ var values [256][]byte
+
+ node := &StemNode{
+ Stem: stem,
+ Values: values[:],
+ depth: 0,
+ }
+
+ // Try to insert value with wrong length
+ key := make([]byte, 32)
+ copy(key[:31], stem)
+ invalidValue := []byte{1, 2, 3} // Not 32 bytes
+
+ _, err := node.Insert(key, invalidValue, nil)
+ if err == nil {
+ t.Fatal("Expected error for invalid value length")
+ }
+
+ if err.Error() != "invalid insertion: value length" {
+ t.Errorf("Expected 'invalid insertion: value length' error, got: %v", err)
+ }
+}
+
+// TestStemNodeCopy tests the Copy method
+func TestStemNodeCopy(t *testing.T) {
+ stem := make([]byte, 31)
+ for i := range stem {
+ stem[i] = byte(i)
+ }
+
+ var values [256][]byte
+ values[0] = common.HexToHash("0x0101").Bytes()
+ values[255] = common.HexToHash("0x0202").Bytes()
+
+ node := &StemNode{
+ Stem: stem,
+ Values: values[:],
+ depth: 10,
+ }
+
+ // Create a copy
+ copied := node.Copy()
+ copiedStem, ok := copied.(*StemNode)
+ if !ok {
+ t.Fatalf("Expected StemNode, got %T", copied)
+ }
+
+ // Check that values are equal but not the same slice
+ if !bytes.Equal(copiedStem.Stem, node.Stem) {
+ t.Errorf("Stem mismatch after copy")
+ }
+ if &copiedStem.Stem[0] == &node.Stem[0] {
+ t.Error("Stem slice not properly cloned")
+ }
+
+ // Check values
+ if !bytes.Equal(copiedStem.Values[0], node.Values[0]) {
+ t.Errorf("Value at index 0 mismatch after copy")
+ }
+ if !bytes.Equal(copiedStem.Values[255], node.Values[255]) {
+ t.Errorf("Value at index 255 mismatch after copy")
+ }
+
+ // Check that value slices are cloned
+ if copiedStem.Values[0] != nil && &copiedStem.Values[0][0] == &node.Values[0][0] {
+ t.Error("Value slice not properly cloned")
+ }
+
+ // Check depth
+ if copiedStem.depth != node.depth {
+ t.Errorf("Depth mismatch: expected %d, got %d", node.depth, copiedStem.depth)
+ }
+}
+
+// TestStemNodeHash tests the Hash method
+func TestStemNodeHash(t *testing.T) {
+ stem := make([]byte, 31)
+ var values [256][]byte
+ values[0] = common.HexToHash("0x0101").Bytes()
+
+ node := &StemNode{
+ Stem: stem,
+ Values: values[:],
+ depth: 0,
+ }
+
+ hash1 := node.Hash()
+
+ // Hash should be deterministic
+ hash2 := node.Hash()
+ if hash1 != hash2 {
+ t.Errorf("Hash not deterministic: %x != %x", hash1, hash2)
+ }
+
+ // Changing a value should change the hash
+ node.Values[1] = common.HexToHash("0x0202").Bytes()
+ hash3 := node.Hash()
+ if hash1 == hash3 {
+ t.Error("Hash didn't change after modifying values")
+ }
+}
+
+// TestStemNodeGetValuesAtStem tests GetValuesAtStem method
+func TestStemNodeGetValuesAtStem(t *testing.T) {
+ stem := make([]byte, 31)
+ for i := range stem {
+ stem[i] = byte(i)
+ }
+
+ var values [256][]byte
+ values[0] = common.HexToHash("0x0101").Bytes()
+ values[10] = common.HexToHash("0x0202").Bytes()
+ values[255] = common.HexToHash("0x0303").Bytes()
+
+ node := &StemNode{
+ Stem: stem,
+ Values: values[:],
+ depth: 0,
+ }
+
+ // GetValuesAtStem with matching stem
+ retrievedValues, err := node.GetValuesAtStem(stem, nil)
+ if err != nil {
+ t.Fatalf("Failed to get values: %v", err)
+ }
+
+ // Check that all values match
+ for i := 0; i < 256; i++ {
+ if !bytes.Equal(retrievedValues[i], values[i]) {
+ t.Errorf("Value mismatch at index %d", i)
+ }
+ }
+
+ // GetValuesAtStem with different stem also returns the same values
+ // (implementation ignores the stem parameter)
+ differentStem := make([]byte, 31)
+ differentStem[0] = 0xFF
+
+ retrievedValues2, err := node.GetValuesAtStem(differentStem, nil)
+ if err != nil {
+ t.Fatalf("Failed to get values with different stem: %v", err)
+ }
+
+ // Should still return the same values (stem is ignored)
+ for i := 0; i < 256; i++ {
+ if !bytes.Equal(retrievedValues2[i], values[i]) {
+ t.Errorf("Value mismatch at index %d with different stem", i)
+ }
+ }
+}
+
+// TestStemNodeInsertValuesAtStem tests InsertValuesAtStem method
+func TestStemNodeInsertValuesAtStem(t *testing.T) {
+ stem := make([]byte, 31)
+ var values [256][]byte
+ values[0] = common.HexToHash("0x0101").Bytes()
+
+ node := &StemNode{
+ Stem: stem,
+ Values: values[:],
+ depth: 0,
+ }
+
+ // Insert new values at the same stem
+ var newValues [256][]byte
+ newValues[1] = common.HexToHash("0x0202").Bytes()
+ newValues[2] = common.HexToHash("0x0303").Bytes()
+
+ newNode, err := node.InsertValuesAtStem(stem, newValues[:], nil, 0)
+ if err != nil {
+ t.Fatalf("Failed to insert values: %v", err)
+ }
+
+ stemNode, ok := newNode.(*StemNode)
+ if !ok {
+ t.Fatalf("Expected StemNode, got %T", newNode)
+ }
+
+ // Check that all values are present
+ if !bytes.Equal(stemNode.Values[0], values[0]) {
+ t.Error("Original value at index 0 missing")
+ }
+ if !bytes.Equal(stemNode.Values[1], newValues[1]) {
+ t.Error("New value at index 1 missing")
+ }
+ if !bytes.Equal(stemNode.Values[2], newValues[2]) {
+ t.Error("New value at index 2 missing")
+ }
+}
+
+// TestStemNodeGetHeight tests GetHeight method
+func TestStemNodeGetHeight(t *testing.T) {
+ node := &StemNode{
+ Stem: make([]byte, 31),
+ Values: make([][]byte, 256),
+ depth: 0,
+ }
+
+ height := node.GetHeight()
+ if height != 1 {
+ t.Errorf("Expected height 1, got %d", height)
+ }
+}
+
+// TestStemNodeCollectNodes tests CollectNodes method
+func TestStemNodeCollectNodes(t *testing.T) {
+ stem := make([]byte, 31)
+ var values [256][]byte
+ values[0] = common.HexToHash("0x0101").Bytes()
+
+ node := &StemNode{
+ Stem: stem,
+ Values: values[:],
+ depth: 0,
+ }
+
+ var collectedPaths [][]byte
+ var collectedNodes []BinaryNode
+
+ flushFn := func(path []byte, n BinaryNode) {
+ // Make a copy of the path
+ pathCopy := make([]byte, len(path))
+ copy(pathCopy, path)
+ collectedPaths = append(collectedPaths, pathCopy)
+ collectedNodes = append(collectedNodes, n)
+ }
+
+ err := node.CollectNodes([]byte{0, 1, 0}, flushFn)
+ if err != nil {
+ t.Fatalf("Failed to collect nodes: %v", err)
+ }
+
+ // Should have collected one node (itself)
+ if len(collectedNodes) != 1 {
+ t.Errorf("Expected 1 collected node, got %d", len(collectedNodes))
+ }
+
+ // Check that the collected node is the same
+ if collectedNodes[0] != node {
+ t.Error("Collected node doesn't match original")
+ }
+
+ // Check the path
+ if !bytes.Equal(collectedPaths[0], []byte{0, 1, 0}) {
+ t.Errorf("Path mismatch: expected [0, 1, 0], got %v", collectedPaths[0])
+ }
+}
diff --git a/trie/binary.go b/trie/bintrie/trie.go
similarity index 74%
rename from trie/binary.go
rename to trie/bintrie/trie.go
index 2cad0999c7..19543bf45c 100644
--- a/trie/binary.go
+++ b/trie/bintrie/trie.go
@@ -14,58 +14,58 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package trie
+package bintrie
import (
"bytes"
"encoding/binary"
+ "errors"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/trie/bintrie"
+ "github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/trienode"
"github.com/ethereum/go-ethereum/trie/utils"
"github.com/ethereum/go-ethereum/triedb/database"
"github.com/holiman/uint256"
)
-// zero is the zero value for a 32-byte array.
-var zero [32]byte
+var errInvalidRootType = errors.New("invalid root type")
// NewBinaryNode creates a new empty binary trie
-func NewBinaryNode() bintrie.BinaryNode {
- return bintrie.Empty{}
+func NewBinaryNode() BinaryNode {
+ return Empty{}
}
// BinaryTrie is a wrapper around VerkleNode that implements the trie.Trie
// interface so that Verkle trees can be reused verbatim.
type BinaryTrie struct {
- root bintrie.BinaryNode
- reader *trieReader
+ root BinaryNode
+ reader *trie.TrieReader
}
// ToDot converts the binary trie to a DOT language representation. Useful for debugging.
-func (trie *BinaryTrie) ToDot() string {
- trie.root.Hash()
- return bintrie.ToDot(trie.root)
+func (t *BinaryTrie) ToDot() string {
+ t.root.Hash()
+ return ToDot(t.root)
}
// NewBinaryTrie creates a new binary trie.
func NewBinaryTrie(root common.Hash, db database.NodeDatabase) (*BinaryTrie, error) {
- reader, err := newTrieReader(root, common.Hash{}, db)
+ reader, err := trie.NewTrieReader(root, common.Hash{}, db)
if err != nil {
return nil, err
}
// Parse the root verkle node if it's not empty.
node := NewBinaryNode()
if root != types.EmptyVerkleHash && root != types.EmptyRootHash {
- blob, err := reader.node(nil, common.Hash{})
+ blob, err := reader.Node(nil, common.Hash{})
if err != nil {
return nil, err
}
- node, err = bintrie.DeserializeNode(blob, 0)
+ node, err = DeserializeNode(blob, 0)
if err != nil {
return nil, err
}
@@ -77,48 +77,48 @@ func NewBinaryTrie(root common.Hash, db database.NodeDatabase) (*BinaryTrie, err
}
// FlatdbNodeResolver is a node resolver that reads nodes from the flatdb.
-func (trie *BinaryTrie) FlatdbNodeResolver(path []byte, hash common.Hash) ([]byte, error) {
+func (t *BinaryTrie) FlatdbNodeResolver(path []byte, hash common.Hash) ([]byte, error) {
// empty nodes will be serialized as common.Hash{}, so capture
// this special use case.
if hash == (common.Hash{}) {
return nil, nil // empty node
}
- return trie.reader.node(path, hash)
+ return t.reader.Node(path, hash)
}
// GetKey returns the sha3 preimage of a hashed key that was previously used
// to store a value.
-func (trie *BinaryTrie) GetKey(key []byte) []byte {
+func (t *BinaryTrie) GetKey(key []byte) []byte {
return key
}
// Get returns the value for key stored in the trie. The value bytes must
// not be modified by the caller. If a node was not found in the database, a
// trie.MissingNodeError is returned.
-func (trie *BinaryTrie) GetStorage(addr common.Address, key []byte) ([]byte, error) {
- return trie.root.Get(utils.GetBinaryTreeKey(addr, key), trie.FlatdbNodeResolver)
+func (t *BinaryTrie) GetStorage(addr common.Address, key []byte) ([]byte, error) {
+ return t.root.Get(utils.GetBinaryTreeKey(addr, key), t.FlatdbNodeResolver)
}
// GetWithHashedKey returns the value, assuming that the key has already
// been hashed.
-func (trie *BinaryTrie) GetWithHashedKey(key []byte) ([]byte, error) {
- return trie.root.Get(key, trie.FlatdbNodeResolver)
+func (t *BinaryTrie) GetWithHashedKey(key []byte) ([]byte, error) {
+ return t.root.Get(key, t.FlatdbNodeResolver)
}
// GetAccount returns the account information for the given address.
-func (trie *BinaryTrie) GetAccount(addr common.Address) (*types.StateAccount, error) {
+func (t *BinaryTrie) GetAccount(addr common.Address) (*types.StateAccount, error) {
acc := &types.StateAccount{}
versionkey := utils.GetBinaryTreeKey(addr, zero[:])
var (
values [][]byte
err error
)
- switch r := trie.root.(type) {
- case *bintrie.InternalNode:
- values, err = r.GetValuesAtStem(versionkey[:31], trie.FlatdbNodeResolver)
- case *bintrie.StemNode:
+ switch r := t.root.(type) {
+ case *InternalNode:
+ values, err = r.GetValuesAtStem(versionkey[:31], t.FlatdbNodeResolver)
+ case *StemNode:
values = r.Values
- case bintrie.Empty:
+ case Empty:
return nil, nil
default:
// This will cover HashedNode but that should be fine since the
@@ -160,11 +160,11 @@ func (trie *BinaryTrie) GetAccount(addr common.Address) (*types.StateAccount, er
}
// UpdateAccount updates the account information for the given address.
-func (trie *BinaryTrie) UpdateAccount(addr common.Address, acc *types.StateAccount, codeLen int) error {
+func (t *BinaryTrie) UpdateAccount(addr common.Address, acc *types.StateAccount, codeLen int) error {
var (
err error
basicData [32]byte
- values = make([][]byte, bintrie.NodeWidth)
+ values = make([][]byte, NodeWidth)
stem = utils.GetBinaryTreeKey(addr, zero[:])
)
@@ -183,14 +183,14 @@ func (trie *BinaryTrie) UpdateAccount(addr common.Address, acc *types.StateAccou
values[utils.BasicDataLeafKey] = basicData[:]
values[utils.CodeHashLeafKey] = acc.CodeHash[:]
- trie.root, err = trie.root.InsertValuesAtStem(stem, values, trie.FlatdbNodeResolver, 0)
+ t.root, err = t.root.InsertValuesAtStem(stem, values, t.FlatdbNodeResolver, 0)
return err
}
// UpdateStem updates the values for the given stem key.
-func (trie *BinaryTrie) UpdateStem(key []byte, values [][]byte) error {
+func (t *BinaryTrie) UpdateStem(key []byte, values [][]byte) error {
var err error
- trie.root, err = trie.root.InsertValuesAtStem(key, values, trie.FlatdbNodeResolver, 0)
+ t.root, err = t.root.InsertValuesAtStem(key, values, t.FlatdbNodeResolver, 0)
return err
}
@@ -198,7 +198,7 @@ func (trie *BinaryTrie) UpdateStem(key []byte, values [][]byte) error {
// existing value is deleted from the trie. The value bytes must not be modified
// by the caller while they are stored in the trie. If a node was not found in the
// database, a trie.MissingNodeError is returned.
-func (trie *BinaryTrie) UpdateStorage(address common.Address, key, value []byte) error {
+func (t *BinaryTrie) UpdateStorage(address common.Address, key, value []byte) error {
k := utils.GetBinaryTreeKeyStorageSlot(address, key)
var v [32]byte
if len(value) >= 32 {
@@ -206,46 +206,46 @@ func (trie *BinaryTrie) UpdateStorage(address common.Address, key, value []byte)
} else {
copy(v[32-len(value):], value[:])
}
- root, err := trie.root.Insert(k, v[:], trie.FlatdbNodeResolver)
+ root, err := t.root.Insert(k, v[:], t.FlatdbNodeResolver)
if err != nil {
return fmt.Errorf("UpdateStorage (%x) error: %v", address, err)
}
- trie.root = root
+ t.root = root
return nil
}
// DeleteAccount is a no-op as it is disabled in stateless.
-func (trie *BinaryTrie) DeleteAccount(addr common.Address) error {
+func (t *BinaryTrie) DeleteAccount(addr common.Address) error {
return nil
}
// Delete removes any existing value for key from the trie. If a node was not
// found in the database, a trie.MissingNodeError is returned.
-func (trie *BinaryTrie) DeleteStorage(addr common.Address, key []byte) error {
+func (t *BinaryTrie) DeleteStorage(addr common.Address, key []byte) error {
k := utils.GetBinaryTreeKey(addr, key)
var zero [32]byte
- root, err := trie.root.Insert(k, zero[:], trie.FlatdbNodeResolver)
+ root, err := t.root.Insert(k, zero[:], t.FlatdbNodeResolver)
if err != nil {
return fmt.Errorf("DeleteStorage (%x) error: %v", addr, err)
}
- trie.root = root
+ t.root = root
return nil
}
// Hash returns the root hash of the trie. It does not write to the database and
// can be used even if the trie doesn't have one.
-func (trie *BinaryTrie) Hash() common.Hash {
- return trie.root.Hash()
+func (t *BinaryTrie) Hash() common.Hash {
+ return t.root.Hash()
}
// Commit writes all nodes to the trie's memory database, tracking the internal
// and external (for account tries) references.
-func (trie *BinaryTrie) Commit(_ bool) (common.Hash, *trienode.NodeSet, error) {
- root := trie.root.(*bintrie.InternalNode)
+func (t *BinaryTrie) Commit(_ bool) (common.Hash, *trienode.NodeSet, error) {
+ root := t.root.(*InternalNode)
nodeset := trienode.NewNodeSet(common.Hash{})
- err := root.CollectNodes(nil, func(path []byte, node bintrie.BinaryNode) {
- serialized := bintrie.SerializeNode(node)
+ err := root.CollectNodes(nil, func(path []byte, node BinaryNode) {
+ serialized := SerializeNode(node)
nodeset.AddNode(path, trienode.New(common.Hash{}, serialized))
})
if err != nil {
@@ -253,13 +253,13 @@ func (trie *BinaryTrie) Commit(_ bool) (common.Hash, *trienode.NodeSet, error) {
}
// Serialize root commitment form
- return trie.Hash(), nodeset, nil
+ return t.Hash(), nodeset, nil
}
// NodeIterator returns an iterator that returns nodes of the trie. Iteration
// starts at the key after the given start key.
-func (trie *BinaryTrie) NodeIterator(startKey []byte) (NodeIterator, error) {
- return newBinaryNodeIterator(trie, nil)
+func (t *BinaryTrie) NodeIterator(startKey []byte) (trie.NodeIterator, error) {
+ return newBinaryNodeIterator(t, nil)
}
// Prove constructs a Merkle proof for key. The result contains all encoded nodes
@@ -269,20 +269,20 @@ func (trie *BinaryTrie) NodeIterator(startKey []byte) (NodeIterator, error) {
// If the trie does not contain a value for key, the returned proof contains all
// nodes of the longest existing prefix of the key (at least the root), ending
// with the node that proves the absence of the key.
-func (trie *BinaryTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
+func (t *BinaryTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
panic("not implemented")
}
// Copy creates a deep copy of the trie.
-func (trie *BinaryTrie) Copy() *BinaryTrie {
+func (t *BinaryTrie) Copy() *BinaryTrie {
return &BinaryTrie{
- root: trie.root.Copy(),
- reader: trie.reader,
+ root: t.root.Copy(),
+ reader: t.reader,
}
}
// IsVerkle returns true if the trie is a Verkle tree.
-func (trie *BinaryTrie) IsVerkle() bool {
+func (t *BinaryTrie) IsVerkle() bool {
// TODO @gballet This is technically NOT a verkle tree, but it has the same
// behavior and basic structure, so for all intents and purposes, it can be
// treated as such. Rename this when verkle gets removed.
@@ -290,9 +290,9 @@ func (trie *BinaryTrie) IsVerkle() bool {
}
// Note: the basic data leaf needs to have been previously created for this to work
-func (trie *BinaryTrie) UpdateContractCode(addr common.Address, codeHash common.Hash, code []byte) error {
+func (t *BinaryTrie) UpdateContractCode(addr common.Address, codeHash common.Hash, code []byte) error {
var (
- chunks = ChunkifyCode(code)
+ chunks = trie.ChunkifyCode(code)
values [][]byte
key []byte
err error
@@ -300,7 +300,7 @@ func (trie *BinaryTrie) UpdateContractCode(addr common.Address, codeHash common.
for i, chunknr := 0, uint64(0); i < len(chunks); i, chunknr = i+32, chunknr+1 {
groupOffset := (chunknr + 128) % 256
if groupOffset == 0 /* start of new group */ || chunknr == 0 /* first chunk in header group */ {
- values = make([][]byte, bintrie.NodeWidth)
+ values = make([][]byte, NodeWidth)
var offset [32]byte
binary.LittleEndian.PutUint64(offset[24:], chunknr+128)
key = utils.GetBinaryTreeKey(addr, offset[:])
@@ -308,7 +308,7 @@ func (trie *BinaryTrie) UpdateContractCode(addr common.Address, codeHash common.
values[groupOffset] = chunks[i : i+32]
if groupOffset == 255 || len(chunks)-i <= 32 {
- err = trie.UpdateStem(key[:31], values)
+ err = t.UpdateStem(key[:31], values)
if err != nil {
return fmt.Errorf("UpdateContractCode (addr=%x) error: %w", addr[:], err)
diff --git a/trie/binary_test.go b/trie/bintrie/trie_test.go
similarity index 97%
rename from trie/binary_test.go
rename to trie/bintrie/trie_test.go
index 082ca9b303..e6cf795ee5 100644
--- a/trie/binary_test.go
+++ b/trie/bintrie/trie_test.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package trie
+package bintrie
import (
"bytes"
@@ -22,7 +22,6 @@ import (
"testing"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/trie/bintrie"
)
var (
@@ -152,7 +151,7 @@ func TestInsertDuplicateKey(t *testing.T) {
t.Fatal("invalid height")
}
// Verify that the value is updated
- if !bytes.Equal(tree.(*bintrie.StemNode).Values[1], twoKey[:]) {
+ if !bytes.Equal(tree.(*StemNode).Values[1], twoKey[:]) {
t.Fatal("invalid height")
}
}
diff --git a/trie/iterator.go b/trie/iterator.go
index e6fedf2430..80298ce48f 100644
--- a/trie/iterator.go
+++ b/trie/iterator.go
@@ -405,7 +405,7 @@ func (it *nodeIterator) resolveHash(hash hashNode, path []byte) (node, error) {
// loaded blob will be tracked, while it's not required here since
// all loaded nodes won't be linked to trie at all and track nodes
// may lead to out-of-memory issue.
- blob, err := it.trie.reader.node(path, common.BytesToHash(hash))
+ blob, err := it.trie.reader.Node(path, common.BytesToHash(hash))
if err != nil {
return nil, err
}
@@ -426,7 +426,7 @@ func (it *nodeIterator) resolveBlob(hash hashNode, path []byte) ([]byte, error)
// loaded blob will be tracked, while it's not required here since
// all loaded nodes won't be linked to trie at all and track nodes
// may lead to out-of-memory issue.
- return it.trie.reader.node(path, common.BytesToHash(hash))
+ return it.trie.reader.Node(path, common.BytesToHash(hash))
}
func (st *nodeIteratorState) resolve(it *nodeIterator, path []byte) error {
diff --git a/trie/proof.go b/trie/proof.go
index f3ed417094..f9bc674d52 100644
--- a/trie/proof.go
+++ b/trie/proof.go
@@ -69,7 +69,7 @@ func (t *Trie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
// loaded blob will be tracked, while it's not required here since
// all loaded nodes won't be linked to trie at all and track nodes
// may lead to out-of-memory issue.
- blob, err := t.reader.node(prefix, common.BytesToHash(n))
+ blob, err := t.reader.Node(prefix, common.BytesToHash(n))
if err != nil {
log.Error("Unhandled trie error in Trie.Prove", "err", err)
return err
diff --git a/trie/trie.go b/trie/trie.go
index 98cf751f47..90b06b51df 100644
--- a/trie/trie.go
+++ b/trie/trie.go
@@ -55,7 +55,7 @@ type Trie struct {
uncommitted int
// reader is the handler trie can retrieve nodes from.
- reader *trieReader
+ reader *TrieReader
// Various tracers for capturing the modifications to trie
opTracer *opTracer
@@ -88,7 +88,7 @@ func (t *Trie) Copy() *Trie {
// empty, otherwise, the root node must be present in database or returns
// a MissingNodeError if not.
func New(id *ID, db database.NodeDatabase) (*Trie, error) {
- reader, err := newTrieReader(id.StateRoot, id.Owner, db)
+ reader, err := NewTrieReader(id.StateRoot, id.Owner, db)
if err != nil {
return nil, err
}
@@ -289,7 +289,7 @@ func (t *Trie) getNode(origNode node, path []byte, pos int) (item []byte, newnod
if hash == nil {
return nil, origNode, 0, errors.New("non-consensus node")
}
- blob, err := t.reader.node(path, common.BytesToHash(hash))
+ blob, err := t.reader.Node(path, common.BytesToHash(hash))
return blob, origNode, 1, err
}
// Path still needs to be traversed, descend into children
@@ -655,7 +655,7 @@ func (t *Trie) resolve(n node, prefix []byte) (node, error) {
// node's original value. The rlp-encoded blob is preferred to be loaded from
// database because it's easy to decode node while complex to encode node to blob.
func (t *Trie) resolveAndTrack(n hashNode, prefix []byte) (node, error) {
- blob, err := t.reader.node(prefix, common.BytesToHash(n))
+ blob, err := t.reader.Node(prefix, common.BytesToHash(n))
if err != nil {
return nil, err
}
diff --git a/trie/trie_reader.go b/trie/trie_reader.go
index a42cdb0cf9..b776709d25 100644
--- a/trie/trie_reader.go
+++ b/trie/trie_reader.go
@@ -22,30 +22,30 @@ import (
"github.com/ethereum/go-ethereum/triedb/database"
)
-// trieReader is a wrapper of the underlying node reader. It's not safe
+// TrieReader is a wrapper of the underlying node reader. It's not safe
// for concurrent usage.
-type trieReader struct {
+type TrieReader struct {
owner common.Hash
reader database.NodeReader
banned map[string]struct{} // Marker to prevent node from being accessed, for tests
}
-// newTrieReader initializes the trie reader with the given node reader.
-func newTrieReader(stateRoot, owner common.Hash, db database.NodeDatabase) (*trieReader, error) {
+// NewTrieReader initializes the trie reader with the given node reader.
+func NewTrieReader(stateRoot, owner common.Hash, db database.NodeDatabase) (*TrieReader, error) {
if stateRoot == (common.Hash{}) || stateRoot == types.EmptyRootHash {
- return &trieReader{owner: owner}, nil
+ return &TrieReader{owner: owner}, nil
}
reader, err := db.NodeReader(stateRoot)
if err != nil {
return nil, &MissingNodeError{Owner: owner, NodeHash: stateRoot, err: err}
}
- return &trieReader{owner: owner, reader: reader}, nil
+ return &TrieReader{owner: owner, reader: reader}, nil
}
// newEmptyReader initializes the pure in-memory reader. All read operations
// should be forbidden and returns the MissingNodeError.
-func newEmptyReader() *trieReader {
- return &trieReader{}
+func newEmptyReader() *TrieReader {
+ return &TrieReader{}
}
// node retrieves the rlp-encoded trie node with the provided trie node
@@ -54,7 +54,7 @@ func newEmptyReader() *trieReader {
//
// Don't modify the returned byte slice since it's not deep-copied and
// still be referenced by database.
-func (r *trieReader) node(path []byte, hash common.Hash) ([]byte, error) {
+func (r *TrieReader) Node(path []byte, hash common.Hash) ([]byte, error) {
// Perform the logics in tests for preventing trie node access.
if r.banned != nil {
if _, ok := r.banned[string(path)]; ok {
diff --git a/trie/verkle.go b/trie/verkle.go
index e00ea21602..8233aded38 100644
--- a/trie/verkle.go
+++ b/trie/verkle.go
@@ -41,13 +41,13 @@ var (
type VerkleTrie struct {
root verkle.VerkleNode
cache *utils.PointCache
- reader *trieReader
+ reader *TrieReader
tracer *prevalueTracer
}
// NewVerkleTrie constructs a verkle tree based on the specified root hash.
func NewVerkleTrie(root common.Hash, db database.NodeDatabase, cache *utils.PointCache) (*VerkleTrie, error) {
- reader, err := newTrieReader(root, common.Hash{}, db)
+ reader, err := NewTrieReader(root, common.Hash{}, db)
if err != nil {
return nil, err
}
@@ -72,6 +72,10 @@ func NewVerkleTrie(root common.Hash, db database.NodeDatabase, cache *utils.Poin
return t, nil
}
+func (t *VerkleTrie) FlatdbNodeResolver(path []byte) ([]byte, error) {
+ return t.reader.Node(path, common.Hash{})
+}
+
// GetKey returns the sha3 preimage of a hashed key that was previously used
// to store a value.
func (t *VerkleTrie) GetKey(key []byte) []byte {
@@ -443,7 +447,7 @@ func (t *VerkleTrie) ToDot() string {
}
func (t *VerkleTrie) nodeResolver(path []byte) ([]byte, error) {
- blob, err := t.reader.node(path, common.Hash{})
+ blob, err := t.reader.Node(path, common.Hash{})
if err != nil {
return nil, err
}