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