|
17 | 17 | package state |
18 | 18 |
|
19 | 19 | import ( |
20 | | - "bytes" |
21 | | - "encoding/binary" |
22 | 20 | "encoding/json" |
23 | 21 | "fmt" |
24 | 22 | "time" |
25 | 23 |
|
26 | 24 | "github.com/ethereum/go-ethereum/common" |
27 | 25 | "github.com/ethereum/go-ethereum/common/hexutil" |
28 | 26 | "github.com/ethereum/go-ethereum/core/types" |
29 | | - "github.com/ethereum/go-ethereum/crypto" |
30 | 27 | "github.com/ethereum/go-ethereum/log" |
31 | 28 | "github.com/ethereum/go-ethereum/rlp" |
32 | 29 | "github.com/ethereum/go-ethereum/trie" |
33 | 30 | "github.com/ethereum/go-ethereum/trie/bintrie" |
34 | | - "github.com/holiman/uint256" |
35 | 31 | ) |
36 | 32 |
|
37 | 33 | // DumpConfig is a set of options to control what portions of the state will be |
@@ -138,12 +134,6 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey [] |
138 | 134 | if err != nil { |
139 | 135 | return nil |
140 | 136 | } |
141 | | - |
142 | | - // Check if this is a Binary Trie and handle it specially |
143 | | - if btrie, ok := tr.(*bintrie.BinaryTrie); ok { |
144 | | - return s.dumpBinaryTrieToCollector(btrie, c, conf) |
145 | | - } |
146 | | - |
147 | 137 | trieIt, err := tr.NodeIterator(conf.Start) |
148 | 138 | if err != nil { |
149 | 139 | log.Error("Trie dumping error", "err", err) |
@@ -232,166 +222,6 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey [] |
232 | 222 | return nextKey |
233 | 223 | } |
234 | 224 |
|
235 | | -// dumpBinaryTrieToCollector handles dumping Binary Trie state to the collector. |
236 | | -// This is necessary because Binary Trie stores account data in a completely different format |
237 | | -// than MPT. While MPT uses RLP-encoded StateAccount structures, Binary Trie stores raw bytes |
238 | | -// with specific offsets as defined in EIP-7864. |
239 | | -// |
240 | | -// Binary Trie storage layout for account data: |
241 | | -// - BasicDataLeafKey (suffix byte 0): Contains nonce and balance |
242 | | -// - Bytes 0-7: Code size (4 bytes) + padding |
243 | | -// - Bytes 8-15: Nonce (8 bytes, big-endian) |
244 | | -// - Bytes 16-31: Balance (16 bytes, big-endian) |
245 | | -// |
246 | | -// - CodeHashLeafKey (suffix byte 1): Contains the code hash (32 bytes) |
247 | | -// - Storage slots (suffix bytes 64+): Contains storage values |
248 | | -// |
249 | | -// This function needs to: |
250 | | -// 1. Iterate through Binary Trie nodes to find account data |
251 | | -// 2. Extract and decode account information from raw bytes |
252 | | -// 3. Map Binary Trie keys back to Ethereum addresses |
253 | | -// 4. Reconstruct the full account state for the collector |
254 | | -func (s *StateDB) dumpBinaryTrieToCollector(btrie *bintrie.BinaryTrie, c DumpCollector, conf *DumpConfig) (nextKey []byte) { |
255 | | - var ( |
256 | | - accounts uint64 |
257 | | - // Map to track processed stems to avoid duplicate accounts |
258 | | - // This prevents dumping the same account when iterating through |
259 | | - // since nultiple leaves can belong to the same account (basic data, code hash, storage). |
260 | | - processedStems = make(map[string]bool) |
261 | | - ) |
262 | | - |
263 | | - // Step 1: Create an iterator to traverse the Binary Trie |
264 | | - // The iterator will visit all nodes in the trie, allowing us to find leaf nodes |
265 | | - // that contain actual account data |
266 | | - it, err := btrie.NodeIterator(nil) |
267 | | - if err != nil { |
268 | | - log.Error("Failed to create Binary Trie iterator", "err", err) |
269 | | - return nil |
270 | | - } |
271 | | - |
272 | | - // Step 2: Iterate through all nodes in the Binary Trie |
273 | | - for it.Next(true) { |
274 | | - // Skip non-leaf nodes as they don't contain account data |
275 | | - if !it.Leaf() { |
276 | | - continue |
277 | | - } |
278 | | - |
279 | | - // Step 3: Extract the leaf's key and value |
280 | | - // The key is 32 bytes: 31-byte stem + 1-byte suffix |
281 | | - // The stem encodes the account address, the suffix indicates the data type |
282 | | - leafKey := it.LeafKey() |
283 | | - leafValue := it.LeafBlob() |
284 | | - |
285 | | - // Step 4: Parse the key structure |
286 | | - // First 31 bytes: stem (encodes the account address) |
287 | | - // Last byte: suffix (indicates the type of data) |
288 | | - stem := string(leafKey[:31]) |
289 | | - suffixByte := leafKey[31] |
290 | | - |
291 | | - // Step 5: Check if this leaf contains BasicData (nonce + balance) |
292 | | - // BasicDataLeafKey = 0 is the suffix for account basic data |
293 | | - if suffixByte == bintrie.BasicDataLeafKey { |
294 | | - // Step 6: Ensure we only process each account once |
295 | | - // Multiple leaves can belong to the same account (basic data, code hash, storage) |
296 | | - // We only want to dump each account once |
297 | | - if processedStems[stem] { |
298 | | - continue |
299 | | - } |
300 | | - processedStems[stem] = true |
301 | | - |
302 | | - // Step 7: Extract nonce from the Binary Trie format |
303 | | - // Nonce is stored at offset 8 as an 8-byte big-endian integer |
304 | | - var nonce uint64 |
305 | | - if len(leafValue) > bintrie.BasicDataNonceOffset+8 { |
306 | | - nonce = binary.BigEndian.Uint64(leafValue[bintrie.BasicDataNonceOffset:]) |
307 | | - } |
308 | | - |
309 | | - // Step 8: Extract balance from the Binary Trie format |
310 | | - // Balance is stored at offset 16 as a 16-byte big-endian integer |
311 | | - var balance = new(uint256.Int) |
312 | | - if len(leafValue) > bintrie.BasicDataBalanceOffset+16 { |
313 | | - balanceBytes := make([]byte, 16) |
314 | | - copy(balanceBytes, leafValue[bintrie.BasicDataBalanceOffset:bintrie.BasicDataBalanceOffset+16]) |
315 | | - balance.SetBytes(balanceBytes) |
316 | | - } |
317 | | - |
318 | | - // Step 9: Map the Binary Trie key back to an Ethereum address |
319 | | - // This is the challenging part: Binary Trie keys are hashed versions of addresses |
320 | | - // We need to find which address maps to this particular key |
321 | | - // |
322 | | - // Current approach: (Made up by Claude) -> |
323 | | - // Iterate through known addresses in stateObjects |
324 | | - // and check if their Binary Trie key matches our leaf key |
325 | | - var foundAddr *common.Address |
326 | | - for addr := range s.stateObjects { |
327 | | - // Generate the Binary Trie key for this address |
328 | | - testKey := bintrie.GetBinaryTreeKeyBasicData(addr) |
329 | | - if bytes.Equal(testKey, leafKey) { |
330 | | - a := addr // Create a copy to avoid reference issues |
331 | | - foundAddr = &a |
332 | | - break |
333 | | - } |
334 | | - } |
335 | | - |
336 | | - // Step 10: Error if we couldn't find the corresponding address |
337 | | - // This might happen for accounts not in the current state cache |
338 | | - if foundAddr == nil { |
339 | | - // TODO(@CPerezz): Figure out how to proceed. |
340 | | - panic("Binary Trie dump error: Cannot recover address from hash.") |
341 | | - } |
342 | | - |
343 | | - // Step 11: Create the dump account structure with basic data |
344 | | - addr := *foundAddr |
345 | | - dumpAccount := DumpAccount{ |
346 | | - Balance: balance.ToBig().String(), |
347 | | - Nonce: nonce, |
348 | | - Address: &addr, |
349 | | - AddressHash: crypto.Keccak256(addr[:]), |
350 | | - } |
351 | | - |
352 | | - // Step 12: Fetch the code hash from a separate Binary Trie leaf |
353 | | - // Code hash is stored at suffix byte 1 (CodeHashLeafKey) |
354 | | - codeHashKey := bintrie.GetBinaryTreeKeyCodeHash(addr) |
355 | | - if codeHashData, err := btrie.GetWithHashedKey(codeHashKey); err == nil && codeHashData != nil { |
356 | | - dumpAccount.CodeHash = codeHashData |
357 | | - // Step 13: Fetch the actual code if needed and not empty |
358 | | - if !conf.SkipCode && !bytes.Equal(codeHashData, types.EmptyCodeHash.Bytes()) { |
359 | | - dumpAccount.Code = s.GetCode(addr) |
360 | | - } |
361 | | - } |
362 | | - |
363 | | - // Step 14: Fetch storage values if needed |
364 | | - if !conf.SkipStorage { |
365 | | - dumpAccount.Storage = make(map[common.Hash]string) |
366 | | - // TODO(CPerezz): Properly iterate through Binary Trie storage slots |
367 | | - // Storage slots are at suffix bytes 64+ in the Binary Trie |
368 | | - // Idea from Claude: |
369 | | - // Use the cached dirty storage from state objects |
370 | | - if obj := s.getStateObject(addr); obj != nil { |
371 | | - for key, value := range obj.dirtyStorage { |
372 | | - dumpAccount.Storage[key] = common.Bytes2Hex(value[:]) |
373 | | - } |
374 | | - } |
375 | | - } |
376 | | - |
377 | | - // Step 15: Send the account to the collector |
378 | | - c.OnAccount(&addr, dumpAccount) |
379 | | - accounts++ |
380 | | - |
381 | | - // Step 17: Check if we've reached the maximum number of accounts |
382 | | - if conf.Max > 0 && accounts >= conf.Max { |
383 | | - // Save the next key for resumption if there are more accounts |
384 | | - if it.Next(true) { |
385 | | - nextKey = it.LeafKey() |
386 | | - } |
387 | | - break |
388 | | - } |
389 | | - } |
390 | | - } |
391 | | - |
392 | | - return nextKey |
393 | | -} |
394 | | - |
395 | 225 | // DumpBinTrieLeaves collects all binary trie leaf nodes into the provided map. |
396 | 226 | func (s *StateDB) DumpBinTrieLeaves(collector map[common.Hash]hexutil.Bytes) error { |
397 | 227 | if s.trie == nil { |
|
0 commit comments