github.com/ethw3/go-ethereuma@v0.0.0-20221013053120-c14602a4c23c/trie/sync_test.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package trie 18 19 import ( 20 "bytes" 21 "fmt" 22 "testing" 23 24 "github.com/ethw3/go-ethereuma/common" 25 "github.com/ethw3/go-ethereuma/crypto" 26 "github.com/ethw3/go-ethereuma/ethdb/memorydb" 27 ) 28 29 // makeTestTrie create a sample test trie to test node-wise reconstruction. 30 func makeTestTrie() (*Database, *StateTrie, map[string][]byte) { 31 // Create an empty trie 32 triedb := NewDatabase(memorydb.New()) 33 trie, _ := NewStateTrie(common.Hash{}, common.Hash{}, triedb) 34 35 // Fill it with some arbitrary data 36 content := make(map[string][]byte) 37 for i := byte(0); i < 255; i++ { 38 // Map the same data under multiple keys 39 key, val := common.LeftPadBytes([]byte{1, i}, 32), []byte{i} 40 content[string(key)] = val 41 trie.Update(key, val) 42 43 key, val = common.LeftPadBytes([]byte{2, i}, 32), []byte{i} 44 content[string(key)] = val 45 trie.Update(key, val) 46 47 // Add some other data to inflate the trie 48 for j := byte(3); j < 13; j++ { 49 key, val = common.LeftPadBytes([]byte{j, i}, 32), []byte{j, i} 50 content[string(key)] = val 51 trie.Update(key, val) 52 } 53 } 54 root, nodes, err := trie.Commit(false) 55 if err != nil { 56 panic(fmt.Errorf("failed to commit trie %v", err)) 57 } 58 if err := triedb.Update(NewWithNodeSet(nodes)); err != nil { 59 panic(fmt.Errorf("failed to commit db %v", err)) 60 } 61 // Re-create the trie based on the new state 62 trie, _ = NewSecure(common.Hash{}, root, triedb) 63 return triedb, trie, content 64 } 65 66 // checkTrieContents cross references a reconstructed trie with an expected data 67 // content map. 68 func checkTrieContents(t *testing.T, db *Database, root []byte, content map[string][]byte) { 69 // Check root availability and trie contents 70 trie, err := NewStateTrie(common.Hash{}, common.BytesToHash(root), db) 71 if err != nil { 72 t.Fatalf("failed to create trie at %x: %v", root, err) 73 } 74 if err := checkTrieConsistency(db, common.BytesToHash(root)); err != nil { 75 t.Fatalf("inconsistent trie at %x: %v", root, err) 76 } 77 for key, val := range content { 78 if have := trie.Get([]byte(key)); !bytes.Equal(have, val) { 79 t.Errorf("entry %x: content mismatch: have %x, want %x", key, have, val) 80 } 81 } 82 } 83 84 // checkTrieConsistency checks that all nodes in a trie are indeed present. 85 func checkTrieConsistency(db *Database, root common.Hash) error { 86 // Create and iterate a trie rooted in a subnode 87 trie, err := NewStateTrie(common.Hash{}, root, db) 88 if err != nil { 89 return nil // Consider a non existent state consistent 90 } 91 it := trie.NodeIterator(nil) 92 for it.Next(true) { 93 } 94 return it.Error() 95 } 96 97 // trieElement represents the element in the state trie(bytecode or trie node). 98 type trieElement struct { 99 path string 100 hash common.Hash 101 syncPath SyncPath 102 } 103 104 // Tests that an empty trie is not scheduled for syncing. 105 func TestEmptySync(t *testing.T) { 106 dbA := NewDatabase(memorydb.New()) 107 dbB := NewDatabase(memorydb.New()) 108 emptyA := NewEmpty(dbA) 109 emptyB, _ := New(common.Hash{}, emptyRoot, dbB) 110 111 for i, trie := range []*Trie{emptyA, emptyB} { 112 sync := NewSync(trie.Hash(), memorydb.New(), nil) 113 if paths, nodes, codes := sync.Missing(1); len(paths) != 0 || len(nodes) != 0 || len(codes) != 0 { 114 t.Errorf("test %d: content requested for empty trie: %v, %v, %v", i, paths, nodes, codes) 115 } 116 } 117 } 118 119 // Tests that given a root hash, a trie can sync iteratively on a single thread, 120 // requesting retrieval tasks and returning all of them in one go. 121 func TestIterativeSyncIndividual(t *testing.T) { testIterativeSync(t, 1, false) } 122 func TestIterativeSyncBatched(t *testing.T) { testIterativeSync(t, 100, false) } 123 func TestIterativeSyncIndividualByPath(t *testing.T) { testIterativeSync(t, 1, true) } 124 func TestIterativeSyncBatchedByPath(t *testing.T) { testIterativeSync(t, 100, true) } 125 126 func testIterativeSync(t *testing.T, count int, bypath bool) { 127 // Create a random trie to copy 128 srcDb, srcTrie, srcData := makeTestTrie() 129 130 // Create a destination trie and sync with the scheduler 131 diskdb := memorydb.New() 132 triedb := NewDatabase(diskdb) 133 sched := NewSync(srcTrie.Hash(), diskdb, nil) 134 135 // The code requests are ignored here since there is no code 136 // at the testing trie. 137 paths, nodes, _ := sched.Missing(count) 138 var elements []trieElement 139 for i := 0; i < len(paths); i++ { 140 elements = append(elements, trieElement{ 141 path: paths[i], 142 hash: nodes[i], 143 syncPath: NewSyncPath([]byte(paths[i])), 144 }) 145 } 146 for len(elements) > 0 { 147 results := make([]NodeSyncResult, len(elements)) 148 if !bypath { 149 for i, element := range elements { 150 data, err := srcDb.Node(element.hash) 151 if err != nil { 152 t.Fatalf("failed to retrieve node data for hash %x: %v", element.hash, err) 153 } 154 results[i] = NodeSyncResult{element.path, data} 155 } 156 } else { 157 for i, element := range elements { 158 data, _, err := srcTrie.TryGetNode(element.syncPath[len(element.syncPath)-1]) 159 if err != nil { 160 t.Fatalf("failed to retrieve node data for path %x: %v", element.path, err) 161 } 162 results[i] = NodeSyncResult{element.path, data} 163 } 164 } 165 for _, result := range results { 166 if err := sched.ProcessNode(result); err != nil { 167 t.Fatalf("failed to process result %v", err) 168 } 169 } 170 batch := diskdb.NewBatch() 171 if err := sched.Commit(batch); err != nil { 172 t.Fatalf("failed to commit data: %v", err) 173 } 174 batch.Write() 175 176 paths, nodes, _ = sched.Missing(count) 177 elements = elements[:0] 178 for i := 0; i < len(paths); i++ { 179 elements = append(elements, trieElement{ 180 path: paths[i], 181 hash: nodes[i], 182 syncPath: NewSyncPath([]byte(paths[i])), 183 }) 184 } 185 } 186 // Cross check that the two tries are in sync 187 checkTrieContents(t, triedb, srcTrie.Hash().Bytes(), srcData) 188 } 189 190 // Tests that the trie scheduler can correctly reconstruct the state even if only 191 // partial results are returned, and the others sent only later. 192 func TestIterativeDelayedSync(t *testing.T) { 193 // Create a random trie to copy 194 srcDb, srcTrie, srcData := makeTestTrie() 195 196 // Create a destination trie and sync with the scheduler 197 diskdb := memorydb.New() 198 triedb := NewDatabase(diskdb) 199 sched := NewSync(srcTrie.Hash(), diskdb, nil) 200 201 // The code requests are ignored here since there is no code 202 // at the testing trie. 203 paths, nodes, _ := sched.Missing(10000) 204 var elements []trieElement 205 for i := 0; i < len(paths); i++ { 206 elements = append(elements, trieElement{ 207 path: paths[i], 208 hash: nodes[i], 209 syncPath: NewSyncPath([]byte(paths[i])), 210 }) 211 } 212 for len(elements) > 0 { 213 // Sync only half of the scheduled nodes 214 results := make([]NodeSyncResult, len(elements)/2+1) 215 for i, element := range elements[:len(results)] { 216 data, err := srcDb.Node(element.hash) 217 if err != nil { 218 t.Fatalf("failed to retrieve node data for %x: %v", element.hash, err) 219 } 220 results[i] = NodeSyncResult{element.path, data} 221 } 222 for _, result := range results { 223 if err := sched.ProcessNode(result); err != nil { 224 t.Fatalf("failed to process result %v", err) 225 } 226 } 227 batch := diskdb.NewBatch() 228 if err := sched.Commit(batch); err != nil { 229 t.Fatalf("failed to commit data: %v", err) 230 } 231 batch.Write() 232 233 paths, nodes, _ = sched.Missing(10000) 234 elements = elements[len(results):] 235 for i := 0; i < len(paths); i++ { 236 elements = append(elements, trieElement{ 237 path: paths[i], 238 hash: nodes[i], 239 syncPath: NewSyncPath([]byte(paths[i])), 240 }) 241 } 242 } 243 // Cross check that the two tries are in sync 244 checkTrieContents(t, triedb, srcTrie.Hash().Bytes(), srcData) 245 } 246 247 // Tests that given a root hash, a trie can sync iteratively on a single thread, 248 // requesting retrieval tasks and returning all of them in one go, however in a 249 // random order. 250 func TestIterativeRandomSyncIndividual(t *testing.T) { testIterativeRandomSync(t, 1) } 251 func TestIterativeRandomSyncBatched(t *testing.T) { testIterativeRandomSync(t, 100) } 252 253 func testIterativeRandomSync(t *testing.T, count int) { 254 // Create a random trie to copy 255 srcDb, srcTrie, srcData := makeTestTrie() 256 257 // Create a destination trie and sync with the scheduler 258 diskdb := memorydb.New() 259 triedb := NewDatabase(diskdb) 260 sched := NewSync(srcTrie.Hash(), diskdb, nil) 261 262 // The code requests are ignored here since there is no code 263 // at the testing trie. 264 paths, nodes, _ := sched.Missing(count) 265 queue := make(map[string]trieElement) 266 for i, path := range paths { 267 queue[path] = trieElement{ 268 path: paths[i], 269 hash: nodes[i], 270 syncPath: NewSyncPath([]byte(paths[i])), 271 } 272 } 273 for len(queue) > 0 { 274 // Fetch all the queued nodes in a random order 275 results := make([]NodeSyncResult, 0, len(queue)) 276 for path, element := range queue { 277 data, err := srcDb.Node(element.hash) 278 if err != nil { 279 t.Fatalf("failed to retrieve node data for %x: %v", element.hash, err) 280 } 281 results = append(results, NodeSyncResult{path, data}) 282 } 283 // Feed the retrieved results back and queue new tasks 284 for _, result := range results { 285 if err := sched.ProcessNode(result); err != nil { 286 t.Fatalf("failed to process result %v", err) 287 } 288 } 289 batch := diskdb.NewBatch() 290 if err := sched.Commit(batch); err != nil { 291 t.Fatalf("failed to commit data: %v", err) 292 } 293 batch.Write() 294 295 paths, nodes, _ = sched.Missing(count) 296 queue = make(map[string]trieElement) 297 for i, path := range paths { 298 queue[path] = trieElement{ 299 path: path, 300 hash: nodes[i], 301 syncPath: NewSyncPath([]byte(path)), 302 } 303 } 304 } 305 // Cross check that the two tries are in sync 306 checkTrieContents(t, triedb, srcTrie.Hash().Bytes(), srcData) 307 } 308 309 // Tests that the trie scheduler can correctly reconstruct the state even if only 310 // partial results are returned (Even those randomly), others sent only later. 311 func TestIterativeRandomDelayedSync(t *testing.T) { 312 // Create a random trie to copy 313 srcDb, srcTrie, srcData := makeTestTrie() 314 315 // Create a destination trie and sync with the scheduler 316 diskdb := memorydb.New() 317 triedb := NewDatabase(diskdb) 318 sched := NewSync(srcTrie.Hash(), diskdb, nil) 319 320 // The code requests are ignored here since there is no code 321 // at the testing trie. 322 paths, nodes, _ := sched.Missing(10000) 323 queue := make(map[string]trieElement) 324 for i, path := range paths { 325 queue[path] = trieElement{ 326 path: path, 327 hash: nodes[i], 328 syncPath: NewSyncPath([]byte(path)), 329 } 330 } 331 for len(queue) > 0 { 332 // Sync only half of the scheduled nodes, even those in random order 333 results := make([]NodeSyncResult, 0, len(queue)/2+1) 334 for path, element := range queue { 335 data, err := srcDb.Node(element.hash) 336 if err != nil { 337 t.Fatalf("failed to retrieve node data for %x: %v", element.hash, err) 338 } 339 results = append(results, NodeSyncResult{path, data}) 340 341 if len(results) >= cap(results) { 342 break 343 } 344 } 345 // Feed the retrieved results back and queue new tasks 346 for _, result := range results { 347 if err := sched.ProcessNode(result); err != nil { 348 t.Fatalf("failed to process result %v", err) 349 } 350 } 351 batch := diskdb.NewBatch() 352 if err := sched.Commit(batch); err != nil { 353 t.Fatalf("failed to commit data: %v", err) 354 } 355 batch.Write() 356 for _, result := range results { 357 delete(queue, result.Path) 358 } 359 paths, nodes, _ = sched.Missing(10000) 360 for i, path := range paths { 361 queue[path] = trieElement{ 362 path: path, 363 hash: nodes[i], 364 syncPath: NewSyncPath([]byte(path)), 365 } 366 } 367 } 368 // Cross check that the two tries are in sync 369 checkTrieContents(t, triedb, srcTrie.Hash().Bytes(), srcData) 370 } 371 372 // Tests that a trie sync will not request nodes multiple times, even if they 373 // have such references. 374 func TestDuplicateAvoidanceSync(t *testing.T) { 375 // Create a random trie to copy 376 srcDb, srcTrie, srcData := makeTestTrie() 377 378 // Create a destination trie and sync with the scheduler 379 diskdb := memorydb.New() 380 triedb := NewDatabase(diskdb) 381 sched := NewSync(srcTrie.Hash(), diskdb, nil) 382 383 // The code requests are ignored here since there is no code 384 // at the testing trie. 385 paths, nodes, _ := sched.Missing(0) 386 var elements []trieElement 387 for i := 0; i < len(paths); i++ { 388 elements = append(elements, trieElement{ 389 path: paths[i], 390 hash: nodes[i], 391 syncPath: NewSyncPath([]byte(paths[i])), 392 }) 393 } 394 requested := make(map[common.Hash]struct{}) 395 396 for len(elements) > 0 { 397 results := make([]NodeSyncResult, len(elements)) 398 for i, element := range elements { 399 data, err := srcDb.Node(element.hash) 400 if err != nil { 401 t.Fatalf("failed to retrieve node data for %x: %v", element.hash, err) 402 } 403 if _, ok := requested[element.hash]; ok { 404 t.Errorf("hash %x already requested once", element.hash) 405 } 406 requested[element.hash] = struct{}{} 407 408 results[i] = NodeSyncResult{element.path, data} 409 } 410 for _, result := range results { 411 if err := sched.ProcessNode(result); err != nil { 412 t.Fatalf("failed to process result %v", err) 413 } 414 } 415 batch := diskdb.NewBatch() 416 if err := sched.Commit(batch); err != nil { 417 t.Fatalf("failed to commit data: %v", err) 418 } 419 batch.Write() 420 421 paths, nodes, _ = sched.Missing(0) 422 elements = elements[:0] 423 for i := 0; i < len(paths); i++ { 424 elements = append(elements, trieElement{ 425 path: paths[i], 426 hash: nodes[i], 427 syncPath: NewSyncPath([]byte(paths[i])), 428 }) 429 } 430 } 431 // Cross check that the two tries are in sync 432 checkTrieContents(t, triedb, srcTrie.Hash().Bytes(), srcData) 433 } 434 435 // Tests that at any point in time during a sync, only complete sub-tries are in 436 // the database. 437 func TestIncompleteSync(t *testing.T) { 438 // Create a random trie to copy 439 srcDb, srcTrie, _ := makeTestTrie() 440 441 // Create a destination trie and sync with the scheduler 442 diskdb := memorydb.New() 443 triedb := NewDatabase(diskdb) 444 sched := NewSync(srcTrie.Hash(), diskdb, nil) 445 446 // The code requests are ignored here since there is no code 447 // at the testing trie. 448 var ( 449 added []common.Hash 450 elements []trieElement 451 root = srcTrie.Hash() 452 ) 453 paths, nodes, _ := sched.Missing(1) 454 for i := 0; i < len(paths); i++ { 455 elements = append(elements, trieElement{ 456 path: paths[i], 457 hash: nodes[i], 458 syncPath: NewSyncPath([]byte(paths[i])), 459 }) 460 } 461 for len(elements) > 0 { 462 // Fetch a batch of trie nodes 463 results := make([]NodeSyncResult, len(elements)) 464 for i, element := range elements { 465 data, err := srcDb.Node(element.hash) 466 if err != nil { 467 t.Fatalf("failed to retrieve node data for %x: %v", element.hash, err) 468 } 469 results[i] = NodeSyncResult{element.path, data} 470 } 471 // Process each of the trie nodes 472 for _, result := range results { 473 if err := sched.ProcessNode(result); err != nil { 474 t.Fatalf("failed to process result %v", err) 475 } 476 } 477 batch := diskdb.NewBatch() 478 if err := sched.Commit(batch); err != nil { 479 t.Fatalf("failed to commit data: %v", err) 480 } 481 batch.Write() 482 483 for _, result := range results { 484 hash := crypto.Keccak256Hash(result.Data) 485 if hash != root { 486 added = append(added, hash) 487 } 488 // Check that all known sub-tries in the synced trie are complete 489 if err := checkTrieConsistency(triedb, hash); err != nil { 490 t.Fatalf("trie inconsistent: %v", err) 491 } 492 } 493 // Fetch the next batch to retrieve 494 paths, nodes, _ = sched.Missing(1) 495 elements = elements[:0] 496 for i := 0; i < len(paths); i++ { 497 elements = append(elements, trieElement{ 498 path: paths[i], 499 hash: nodes[i], 500 syncPath: NewSyncPath([]byte(paths[i])), 501 }) 502 } 503 } 504 // Sanity check that removing any node from the database is detected 505 for _, hash := range added { 506 value, _ := diskdb.Get(hash.Bytes()) 507 diskdb.Delete(hash.Bytes()) 508 if err := checkTrieConsistency(triedb, root); err == nil { 509 t.Fatalf("trie inconsistency not caught, missing: %x", hash) 510 } 511 diskdb.Put(hash.Bytes(), value) 512 } 513 } 514 515 // Tests that trie nodes get scheduled lexicographically when having the same 516 // depth. 517 func TestSyncOrdering(t *testing.T) { 518 // Create a random trie to copy 519 srcDb, srcTrie, srcData := makeTestTrie() 520 521 // Create a destination trie and sync with the scheduler, tracking the requests 522 diskdb := memorydb.New() 523 triedb := NewDatabase(diskdb) 524 sched := NewSync(srcTrie.Hash(), diskdb, nil) 525 526 // The code requests are ignored here since there is no code 527 // at the testing trie. 528 var ( 529 reqs []SyncPath 530 elements []trieElement 531 ) 532 paths, nodes, _ := sched.Missing(1) 533 for i := 0; i < len(paths); i++ { 534 elements = append(elements, trieElement{ 535 path: paths[i], 536 hash: nodes[i], 537 syncPath: NewSyncPath([]byte(paths[i])), 538 }) 539 reqs = append(reqs, NewSyncPath([]byte(paths[i]))) 540 } 541 542 for len(elements) > 0 { 543 results := make([]NodeSyncResult, len(elements)) 544 for i, element := range elements { 545 data, err := srcDb.Node(element.hash) 546 if err != nil { 547 t.Fatalf("failed to retrieve node data for %x: %v", element.hash, err) 548 } 549 results[i] = NodeSyncResult{element.path, data} 550 } 551 for _, result := range results { 552 if err := sched.ProcessNode(result); err != nil { 553 t.Fatalf("failed to process result %v", err) 554 } 555 } 556 batch := diskdb.NewBatch() 557 if err := sched.Commit(batch); err != nil { 558 t.Fatalf("failed to commit data: %v", err) 559 } 560 batch.Write() 561 562 paths, nodes, _ = sched.Missing(1) 563 elements = elements[:0] 564 for i := 0; i < len(paths); i++ { 565 elements = append(elements, trieElement{ 566 path: paths[i], 567 hash: nodes[i], 568 syncPath: NewSyncPath([]byte(paths[i])), 569 }) 570 reqs = append(reqs, NewSyncPath([]byte(paths[i]))) 571 } 572 } 573 // Cross check that the two tries are in sync 574 checkTrieContents(t, triedb, srcTrie.Hash().Bytes(), srcData) 575 576 // Check that the trie nodes have been requested path-ordered 577 for i := 0; i < len(reqs)-1; i++ { 578 if len(reqs[i]) > 1 || len(reqs[i+1]) > 1 { 579 // In the case of the trie tests, there's no storage so the tuples 580 // must always be single items. 2-tuples should be tested in state. 581 t.Errorf("Invalid request tuples: len(%v) or len(%v) > 1", reqs[i], reqs[i+1]) 582 } 583 if bytes.Compare(compactToHex(reqs[i][0]), compactToHex(reqs[i+1][0])) > 0 { 584 t.Errorf("Invalid request order: %v before %v", compactToHex(reqs[i][0]), compactToHex(reqs[i+1][0])) 585 } 586 } 587 }