Skip to content

Commit 35532a2

Browse files
jsvisarjl493456442
authored andcommitted
doc
Signed-off-by: jsvisa <[email protected]>
1 parent 7e571fd commit 35532a2

File tree

3 files changed

+47
-145
lines changed

3 files changed

+47
-145
lines changed

triedb/pathdb/layertree.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,8 @@ func (tree *layerTree) lookupStorage(accountHash common.Hash, slotHash common.Ha
340340
return l, nil
341341
}
342342

343+
// lookupNode returns the layer that is guaranteed to contain the trie node
344+
// data corresponding to the specified state root being queried.
343345
func (tree *layerTree) lookupNode(accountHash common.Hash, path string, state common.Hash) (layer, error) {
344346
// Hold the read lock to prevent the unexpected layer changes
345347
tree.lock.RLock()

triedb/pathdb/lookup.go

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"golang.org/x/sync/errgroup"
2828
)
2929

30+
// storageNodesShardCount is the number of shards used for storage nodes.
3031
const storageNodesShardCount = 16
3132

3233
// storageKey returns a key for uniquely identifying the storage slot.
@@ -52,10 +53,17 @@ type lookup struct {
5253
// where the slot was modified, with the order from oldest to newest.
5354
storages map[[64]byte][]common.Hash
5455

56+
// accountNodes represents the mutation history for specific account
57+
// trie nodes. The key is the trie path of the node, and the value is a slice
58+
// of **diff layer** IDs indicating where the account was modified,
59+
// with the order from oldest to newest.
5560
accountNodes map[string][]common.Hash
5661

57-
// Key is accountHash.Hex() + path, distributed across 16 shards
58-
// This eliminates the need for two-level map lookups
62+
// storageNodes represents the mutation history for specific storage
63+
// slot trie nodes, distributed across 16 shards for efficiency.
64+
// The key is the account address hash and the trie path of the node,
65+
// the value is a slice of **diff layer** IDs indicating where the
66+
// slot was modified, with the order from oldest to newest.
5967
storageNodes [storageNodesShardCount]map[string][]common.Hash
6068

6169
// descendant is the callback indicating whether the layer with
@@ -186,6 +194,17 @@ func (l *lookup) storageTip(accountHash common.Hash, slotHash common.Hash, state
186194
return common.Hash{}
187195
}
188196

197+
// nodeTip traverses the layer list associated with the given account and path
198+
// in reverse order to locate the first entry that either matches
199+
// the specified stateID or is a descendant of it.
200+
//
201+
// If found, the trie node data corresponding to the supplied stateID resides
202+
// in that layer. Otherwise, two scenarios are possible:
203+
//
204+
// (a) the trie node remains unmodified from the current disk layer up to
205+
// the state layer specified by the stateID: fallback to the disk layer for
206+
// data retrieval, (b) or the layer specified by the stateID is stale: reject
207+
// the data retrieval.
189208
func (l *lookup) nodeTip(accountHash common.Hash, path string, stateID common.Hash, base common.Hash) common.Hash {
190209
var list []common.Hash
191210
if accountHash == (common.Hash{}) {
@@ -448,17 +467,16 @@ func (l *lookup) removeStorageNodes(state common.Hash, nodes map[common.Hash]map
448467
for accountHash, slots := range nodes {
449468
accountHex := accountHash.Hex()
450469
for path := range slots {
451-
// Construct the combined key and find the correct shard
470+
shard := l.storageNodes[getStorageShardIndex(path)]
452471
key := accountHex + path
453-
shardIndex := getStorageShardIndex(path)
454-
found, list := removeFromList(l.storageNodes[shardIndex][key], state)
472+
found, list := removeFromList(shard[key], state)
455473
if !found {
456474
return fmt.Errorf("storage lookup is not found, %x %x, state: %x", accountHash, path, state)
457475
}
458476
if len(list) != 0 {
459-
l.storageNodes[shardIndex][key] = list
477+
shard[key] = list
460478
} else {
461-
delete(l.storageNodes[shardIndex], key)
479+
delete(shard, key)
462480
}
463481
}
464482
}

triedb/pathdb/lookup_test.go

Lines changed: 20 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package pathdb
22

33
import (
44
"crypto/rand"
5-
"fmt"
65
"testing"
76

87
"github.com/ethereum/go-ethereum/common"
@@ -43,173 +42,56 @@ func generateRandomStorageNodes(accountCount, nodesPerAccount int) map[common.Ha
4342
return storageNodes
4443
}
4544

46-
// addNodes is a helper method for testing that adds nodes to the lookup structure
47-
func (l *lookup) addNodes(stateHash common.Hash, accountNodes map[string]*trienode.Node, storageNodes map[common.Hash]map[string]*trienode.Node) {
48-
// Add account nodes
49-
for path := range accountNodes {
50-
list, exists := l.accountNodes[path]
51-
if !exists {
52-
list = make([]common.Hash, 0, 16)
53-
}
54-
list = append(list, stateHash)
55-
l.accountNodes[path] = list
56-
}
57-
58-
// Add storage nodes using single-level sharded structure
59-
for accountHash, slots := range storageNodes {
60-
accountHex := accountHash.Hex()
61-
62-
for path := range slots {
63-
// Construct the combined key but use only path for shard calculation
64-
key := accountHex + path
65-
shardIndex := getStorageShardIndex(path) // Use only path for sharding
66-
shardMap := l.storageNodes[shardIndex]
67-
68-
list, exists := shardMap[key]
69-
if !exists {
70-
list = make([]common.Hash, 0, 16)
71-
}
72-
list = append(list, stateHash)
73-
shardMap[key] = list
74-
}
75-
}
76-
}
77-
7845
func BenchmarkAddNodes(b *testing.B) {
7946
tests := []struct {
80-
name string
81-
accountNodeCount int
82-
storageAccountCount int
83-
nodesPerAccount int
47+
name string
48+
accountNodeCount int
49+
nodesPerAccount int
8450
}{
8551
{
86-
name: "Small-100-accounts-10-nodes",
87-
accountNodeCount: 100,
88-
storageAccountCount: 100,
89-
nodesPerAccount: 10,
52+
name: "Small-100-accounts-10-nodes",
53+
accountNodeCount: 100,
54+
nodesPerAccount: 10,
9055
},
9156
{
92-
name: "Medium-500-accounts-20-nodes",
93-
accountNodeCount: 500,
94-
storageAccountCount: 500,
95-
nodesPerAccount: 20,
57+
name: "Medium-500-accounts-20-nodes",
58+
accountNodeCount: 500,
59+
nodesPerAccount: 20,
9660
},
9761
{
98-
name: "Large-2000-accounts-40-nodes",
99-
accountNodeCount: 2000,
100-
storageAccountCount: 2000,
101-
nodesPerAccount: 40,
62+
name: "Large-2000-accounts-40-nodes",
63+
accountNodeCount: 2000,
64+
nodesPerAccount: 40,
10265
},
10366
}
10467

10568
for _, tc := range tests {
10669
b.Run(tc.name, func(b *testing.B) {
107-
accountNodes := generateRandomAccountNodes(tc.accountNodeCount)
108-
storageNodes := generateRandomStorageNodes(tc.storageAccountCount, tc.nodesPerAccount)
70+
storageNodes := generateRandomStorageNodes(tc.accountNodeCount, tc.nodesPerAccount)
10971

11072
lookup := &lookup{
11173
accountNodes: make(map[string][]common.Hash),
11274
}
75+
11376
// Initialize all 16 storage node shards
114-
for i := 0; i < 16; i++ {
77+
for i := 0; i < storageNodesShardCount; i++ {
11578
lookup.storageNodes[i] = make(map[string][]common.Hash)
11679
}
11780

118-
var stateHash common.Hash
119-
rand.Read(stateHash[:])
81+
var state common.Hash
82+
rand.Read(state[:])
12083

12184
b.ResetTimer()
12285
b.ReportAllocs()
12386

12487
for i := 0; i < b.N; i++ {
125-
// Clear the nodes maps for each iteration
126-
lookup.accountNodes = make(map[string][]common.Hash)
127-
// Reinitialize all 16 storage node shards
128-
for j := 0; j < 16; j++ {
88+
// Reset the lookup instance for each benchmark iteration
89+
for j := 0; j < storageNodesShardCount; j++ {
12990
lookup.storageNodes[j] = make(map[string][]common.Hash)
13091
}
13192

132-
lookup.addNodes(stateHash, accountNodes, storageNodes)
93+
lookup.addStorageNodes(state, storageNodes)
13394
}
13495
})
13596
}
13697
}
137-
138-
func TestConcurrentStorageNodesUpdate(b *testing.T) {
139-
// Create a lookup instance
140-
lookup := &lookup{
141-
accountNodes: make(map[string][]common.Hash),
142-
}
143-
// Initialize all storage node shards
144-
for i := 0; i < storageNodesShardCount; i++ {
145-
lookup.storageNodes[i] = make(map[string][]common.Hash)
146-
}
147-
148-
// Create test data with known shard distribution
149-
testData := map[common.Hash]map[string]*trienode.Node{}
150-
151-
// Create accounts that will distribute across different shards
152-
for i := 0; i < 100; i++ {
153-
var accountHash common.Hash
154-
accountHash[0] = byte(i)
155-
156-
testData[accountHash] = make(map[string]*trienode.Node)
157-
158-
// Create paths that will hash to different shards
159-
for j := 0; j < 10; j++ {
160-
path := fmt.Sprintf("path_%d_%d", i, j)
161-
var nodeHash common.Hash
162-
nodeHash[0] = byte(j)
163-
164-
testData[accountHash][path] = &trienode.Node{Hash: nodeHash}
165-
}
166-
}
167-
168-
// Add nodes using the concurrent method
169-
var stateHash common.Hash
170-
stateHash[0] = 0x42
171-
lookup.addNodes(stateHash, nil, testData)
172-
173-
// Verify that all nodes were added correctly
174-
totalNodes := 0
175-
for accountHash, slots := range testData {
176-
accountHex := accountHash.Hex()
177-
for path := range slots {
178-
key := accountHex + path
179-
shardIndex := getStorageShardIndex(path)
180-
181-
list, exists := lookup.storageNodes[shardIndex][key]
182-
if !exists {
183-
b.Errorf("Node not found: account=%x, path=%s, shard=%d", accountHash, path, shardIndex)
184-
continue
185-
}
186-
187-
if len(list) != 1 {
188-
b.Errorf("Expected 1 state hash, got %d: account=%x, path=%s", len(list), accountHash, path)
189-
continue
190-
}
191-
192-
if list[0] != stateHash {
193-
b.Errorf("Expected state hash %x, got %x: account=%x, path=%s", stateHash, list[0], accountHash, path)
194-
continue
195-
}
196-
197-
totalNodes++
198-
}
199-
}
200-
201-
expectedTotal := 100 * 10 // 100 accounts * 10 nodes each
202-
if totalNodes != expectedTotal {
203-
b.Errorf("Expected %d total nodes, got %d", expectedTotal, totalNodes)
204-
}
205-
206-
// Verify shard distribution
207-
for i := 0; i < storageNodesShardCount; i++ {
208-
shardSize := len(lookup.storageNodes[i])
209-
if shardSize == 0 {
210-
b.Logf("Shard %d is empty", i)
211-
} else {
212-
b.Logf("Shard %d has %d entries", i, shardSize)
213-
}
214-
}
215-
}

0 commit comments

Comments
 (0)