github.com/core-coin/go-core/v2@v2.1.9/trie/sync_test.go (about) 1 // Copyright 2015 by the Authors 2 // This file is part of the go-core library. 3 // 4 // The go-core 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-core 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-core library. If not, see <http://www.gnu.org/licenses/>. 16 17 package trie 18 19 import ( 20 "bytes" 21 "testing" 22 23 "github.com/core-coin/go-core/v2/xcbdb/memorydb" 24 25 "github.com/core-coin/go-core/v2/common" 26 "github.com/core-coin/go-core/v2/crypto" 27 ) 28 29 // makeTestTrie create a sample test trie to test node-wise reconstruction. 30 func makeTestTrie() (*Database, *SecureTrie, map[string][]byte) { 31 // Create an empty trie 32 triedb := NewDatabase(memorydb.New()) 33 trie, _ := NewSecure(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 trie.Commit(nil) 55 56 // Return the generated trie 57 return triedb, trie, content 58 } 59 60 // checkTrieContents cross references a reconstructed trie with an expected data 61 // content map. 62 func checkTrieContents(t *testing.T, db *Database, root []byte, content map[string][]byte) { 63 // Check root availability and trie contents 64 trie, err := NewSecure(common.BytesToHash(root), db) 65 if err != nil { 66 t.Fatalf("failed to create trie at %x: %v", root, err) 67 } 68 if err := checkTrieConsistency(db, common.BytesToHash(root)); err != nil { 69 t.Fatalf("inconsistent trie at %x: %v", root, err) 70 } 71 for key, val := range content { 72 if have := trie.Get([]byte(key)); !bytes.Equal(have, val) { 73 t.Errorf("entry %x: content mismatch: have %x, want %x", key, have, val) 74 } 75 } 76 } 77 78 // checkTrieConsistency checks that all nodes in a trie are indeed present. 79 func checkTrieConsistency(db *Database, root common.Hash) error { 80 // Create and iterate a trie rooted in a subnode 81 trie, err := NewSecure(root, db) 82 if err != nil { 83 return nil // Consider a non existent state consistent 84 } 85 it := trie.NodeIterator(nil) 86 for it.Next(true) { 87 } 88 return it.Error() 89 } 90 91 // Tests that an empty trie is not scheduled for syncing. 92 func TestEmptySync(t *testing.T) { 93 dbA := NewDatabase(memorydb.New()) 94 dbB := NewDatabase(memorydb.New()) 95 emptyA, _ := New(common.Hash{}, dbA) 96 emptyB, _ := New(emptyRoot, dbB) 97 98 for i, trie := range []*Trie{emptyA, emptyB} { 99 sync := NewSync(trie.Hash(), memorydb.New(), nil, NewSyncBloom(1, memorydb.New())) 100 if nodes, paths, codes := sync.Missing(1); len(nodes) != 0 || len(paths) != 0 || len(codes) != 0 { 101 t.Errorf("test %d: content requested for empty trie: %v, %v, %v", i, nodes, paths, codes) 102 } 103 } 104 } 105 106 // Tests that given a root hash, a trie can sync iteratively on a single thread, 107 // requesting retrieval tasks and returning all of them in one go. 108 func TestIterativeSyncIndividual(t *testing.T) { testIterativeSync(t, 1, false) } 109 func TestIterativeSyncBatched(t *testing.T) { testIterativeSync(t, 100, false) } 110 func TestIterativeSyncIndividualByPath(t *testing.T) { testIterativeSync(t, 1, true) } 111 func TestIterativeSyncBatchedByPath(t *testing.T) { testIterativeSync(t, 100, true) } 112 113 func testIterativeSync(t *testing.T, count int, bypath bool) { 114 // Create a random trie to copy 115 srcDb, srcTrie, srcData := makeTestTrie() 116 117 // Create a destination trie and sync with the scheduler 118 diskdb := memorydb.New() 119 triedb := NewDatabase(diskdb) 120 sched := NewSync(srcTrie.Hash(), diskdb, nil, NewSyncBloom(1, diskdb)) 121 122 nodes, paths, codes := sched.Missing(count) 123 var ( 124 hashQueue []common.Hash 125 pathQueue []SyncPath 126 ) 127 if !bypath { 128 hashQueue = append(append(hashQueue[:0], nodes...), codes...) 129 } else { 130 hashQueue = append(hashQueue[:0], codes...) 131 pathQueue = append(pathQueue[:0], paths...) 132 } 133 for len(hashQueue)+len(pathQueue) > 0 { 134 results := make([]SyncResult, len(hashQueue)+len(pathQueue)) 135 for i, hash := range hashQueue { 136 data, err := srcDb.Node(hash) 137 if err != nil { 138 t.Fatalf("failed to retrieve node data for hash %x: %v", hash, err) 139 } 140 results[i] = SyncResult{hash, data} 141 } 142 for i, path := range pathQueue { 143 data, _, err := srcTrie.TryGetNode(path[0]) 144 if err != nil { 145 t.Fatalf("failed to retrieve node data for path %x: %v", path, err) 146 } 147 results[len(hashQueue)+i] = SyncResult{crypto.SHA3Hash(data), data} 148 } 149 for _, result := range results { 150 if err := sched.Process(result); err != nil { 151 t.Fatalf("failed to process result %v", err) 152 } 153 } 154 batch := diskdb.NewBatch() 155 if err := sched.Commit(batch); err != nil { 156 t.Fatalf("failed to commit data: %v", err) 157 } 158 batch.Write() 159 160 nodes, paths, codes = sched.Missing(count) 161 if !bypath { 162 hashQueue = append(append(hashQueue[:0], nodes...), codes...) 163 } else { 164 hashQueue = append(hashQueue[:0], codes...) 165 pathQueue = append(pathQueue[:0], paths...) 166 } 167 } 168 // Cross check that the two tries are in sync 169 checkTrieContents(t, triedb, srcTrie.Hash().Bytes(), srcData) 170 } 171 172 // Tests that the trie scheduler can correctly reconstruct the state even if only 173 // partial results are returned, and the others sent only later. 174 func TestIterativeDelayedSync(t *testing.T) { 175 // Create a random trie to copy 176 srcDb, srcTrie, srcData := makeTestTrie() 177 178 // Create a destination trie and sync with the scheduler 179 diskdb := memorydb.New() 180 triedb := NewDatabase(diskdb) 181 sched := NewSync(srcTrie.Hash(), diskdb, nil, NewSyncBloom(1, diskdb)) 182 183 nodes, _, codes := sched.Missing(10000) 184 queue := append(append([]common.Hash{}, nodes...), codes...) 185 186 for len(queue) > 0 { 187 // Sync only half of the scheduled nodes 188 results := make([]SyncResult, len(queue)/2+1) 189 for i, hash := range queue[:len(results)] { 190 data, err := srcDb.Node(hash) 191 if err != nil { 192 t.Fatalf("failed to retrieve node data for %x: %v", hash, err) 193 } 194 results[i] = SyncResult{hash, data} 195 } 196 for _, result := range results { 197 if err := sched.Process(result); err != nil { 198 t.Fatalf("failed to process result %v", err) 199 } 200 } 201 batch := diskdb.NewBatch() 202 if err := sched.Commit(batch); err != nil { 203 t.Fatalf("failed to commit data: %v", err) 204 } 205 batch.Write() 206 207 nodes, _, codes = sched.Missing(10000) 208 queue = append(append(queue[len(results):], nodes...), codes...) 209 } 210 // Cross check that the two tries are in sync 211 checkTrieContents(t, triedb, srcTrie.Hash().Bytes(), srcData) 212 } 213 214 // Tests that given a root hash, a trie can sync iteratively on a single thread, 215 // requesting retrieval tasks and returning all of them in one go, however in a 216 // random order. 217 func TestIterativeRandomSyncIndividual(t *testing.T) { testIterativeRandomSync(t, 1) } 218 func TestIterativeRandomSyncBatched(t *testing.T) { testIterativeRandomSync(t, 100) } 219 220 func testIterativeRandomSync(t *testing.T, count int) { 221 // Create a random trie to copy 222 srcDb, srcTrie, srcData := makeTestTrie() 223 224 // Create a destination trie and sync with the scheduler 225 diskdb := memorydb.New() 226 triedb := NewDatabase(diskdb) 227 sched := NewSync(srcTrie.Hash(), diskdb, nil, NewSyncBloom(1, diskdb)) 228 229 queue := make(map[common.Hash]struct{}) 230 nodes, _, codes := sched.Missing(count) 231 for _, hash := range append(nodes, codes...) { 232 queue[hash] = struct{}{} 233 } 234 for len(queue) > 0 { 235 // Fetch all the queued nodes in a random order 236 results := make([]SyncResult, 0, len(queue)) 237 for hash := range queue { 238 data, err := srcDb.Node(hash) 239 if err != nil { 240 t.Fatalf("failed to retrieve node data for %x: %v", hash, err) 241 } 242 results = append(results, SyncResult{hash, data}) 243 } 244 // Feed the retrieved results back and queue new tasks 245 for _, result := range results { 246 if err := sched.Process(result); err != nil { 247 t.Fatalf("failed to process result %v", err) 248 } 249 } 250 batch := diskdb.NewBatch() 251 if err := sched.Commit(batch); err != nil { 252 t.Fatalf("failed to commit data: %v", err) 253 } 254 batch.Write() 255 256 queue = make(map[common.Hash]struct{}) 257 nodes, _, codes = sched.Missing(count) 258 for _, hash := range append(nodes, codes...) { 259 queue[hash] = struct{}{} 260 } 261 } 262 // Cross check that the two tries are in sync 263 checkTrieContents(t, triedb, srcTrie.Hash().Bytes(), srcData) 264 } 265 266 // Tests that the trie scheduler can correctly reconstruct the state even if only 267 // partial results are returned (Even those randomly), others sent only later. 268 func TestIterativeRandomDelayedSync(t *testing.T) { 269 // Create a random trie to copy 270 srcDb, srcTrie, srcData := makeTestTrie() 271 272 // Create a destination trie and sync with the scheduler 273 diskdb := memorydb.New() 274 triedb := NewDatabase(diskdb) 275 sched := NewSync(srcTrie.Hash(), diskdb, nil, NewSyncBloom(1, diskdb)) 276 277 queue := make(map[common.Hash]struct{}) 278 nodes, _, codes := sched.Missing(10000) 279 for _, hash := range append(nodes, codes...) { 280 queue[hash] = struct{}{} 281 } 282 for len(queue) > 0 { 283 // Sync only half of the scheduled nodes, even those in random order 284 results := make([]SyncResult, 0, len(queue)/2+1) 285 for hash := range queue { 286 data, err := srcDb.Node(hash) 287 if err != nil { 288 t.Fatalf("failed to retrieve node data for %x: %v", hash, err) 289 } 290 results = append(results, SyncResult{hash, data}) 291 292 if len(results) >= cap(results) { 293 break 294 } 295 } 296 // Feed the retrieved results back and queue new tasks 297 for _, result := range results { 298 if err := sched.Process(result); err != nil { 299 t.Fatalf("failed to process result %v", err) 300 } 301 } 302 batch := diskdb.NewBatch() 303 if err := sched.Commit(batch); err != nil { 304 t.Fatalf("failed to commit data: %v", err) 305 } 306 batch.Write() 307 for _, result := range results { 308 delete(queue, result.Hash) 309 } 310 nodes, _, codes = sched.Missing(10000) 311 for _, hash := range append(nodes, codes...) { 312 queue[hash] = struct{}{} 313 } 314 } 315 // Cross check that the two tries are in sync 316 checkTrieContents(t, triedb, srcTrie.Hash().Bytes(), srcData) 317 } 318 319 // Tests that a trie sync will not request nodes multiple times, even if they 320 // have such references. 321 func TestDuplicateAvoidanceSync(t *testing.T) { 322 // Create a random trie to copy 323 srcDb, srcTrie, srcData := makeTestTrie() 324 325 // Create a destination trie and sync with the scheduler 326 diskdb := memorydb.New() 327 triedb := NewDatabase(diskdb) 328 sched := NewSync(srcTrie.Hash(), diskdb, nil, NewSyncBloom(1, diskdb)) 329 330 nodes, _, codes := sched.Missing(0) 331 queue := append(append([]common.Hash{}, nodes...), codes...) 332 requested := make(map[common.Hash]struct{}) 333 334 for len(queue) > 0 { 335 results := make([]SyncResult, len(queue)) 336 for i, hash := range queue { 337 data, err := srcDb.Node(hash) 338 if err != nil { 339 t.Fatalf("failed to retrieve node data for %x: %v", hash, err) 340 } 341 if _, ok := requested[hash]; ok { 342 t.Errorf("hash %x already requested once", hash) 343 } 344 requested[hash] = struct{}{} 345 346 results[i] = SyncResult{hash, data} 347 } 348 for _, result := range results { 349 if err := sched.Process(result); err != nil { 350 t.Fatalf("failed to process result %v", err) 351 } 352 } 353 batch := diskdb.NewBatch() 354 if err := sched.Commit(batch); err != nil { 355 t.Fatalf("failed to commit data: %v", err) 356 } 357 batch.Write() 358 359 nodes, _, codes = sched.Missing(0) 360 queue = append(append(queue[:0], nodes...), codes...) 361 } 362 // Cross check that the two tries are in sync 363 checkTrieContents(t, triedb, srcTrie.Hash().Bytes(), srcData) 364 } 365 366 // Tests that at any point in time during a sync, only complete sub-tries are in 367 // the database. 368 func TestIncompleteSync(t *testing.T) { 369 // Create a random trie to copy 370 srcDb, srcTrie, _ := makeTestTrie() 371 372 // Create a destination trie and sync with the scheduler 373 diskdb := memorydb.New() 374 triedb := NewDatabase(diskdb) 375 sched := NewSync(srcTrie.Hash(), diskdb, nil, NewSyncBloom(1, diskdb)) 376 377 var added []common.Hash 378 379 nodes, _, codes := sched.Missing(1) 380 queue := append(append([]common.Hash{}, nodes...), codes...) 381 for len(queue) > 0 { 382 // Fetch a batch of trie nodes 383 results := make([]SyncResult, len(queue)) 384 for i, hash := range queue { 385 data, err := srcDb.Node(hash) 386 if err != nil { 387 t.Fatalf("failed to retrieve node data for %x: %v", hash, err) 388 } 389 results[i] = SyncResult{hash, data} 390 } 391 // Process each of the trie nodes 392 for _, result := range results { 393 if err := sched.Process(result); err != nil { 394 t.Fatalf("failed to process result %v", err) 395 } 396 } 397 batch := diskdb.NewBatch() 398 if err := sched.Commit(batch); err != nil { 399 t.Fatalf("failed to commit data: %v", err) 400 } 401 batch.Write() 402 for _, result := range results { 403 added = append(added, result.Hash) 404 // Check that all known sub-tries in the synced trie are complete 405 if err := checkTrieConsistency(triedb, result.Hash); err != nil { 406 t.Fatalf("trie inconsistent: %v", err) 407 } 408 } 409 // Fetch the next batch to retrieve 410 nodes, _, codes = sched.Missing(1) 411 queue = append(append(queue[:0], nodes...), codes...) 412 } 413 // Sanity check that removing any node from the database is detected 414 for _, node := range added[1:] { 415 key := node.Bytes() 416 value, _ := diskdb.Get(key) 417 418 diskdb.Delete(key) 419 if err := checkTrieConsistency(triedb, added[0]); err == nil { 420 t.Fatalf("trie inconsistency not caught, missing: %x", key) 421 } 422 diskdb.Put(key, value) 423 } 424 } 425 426 // Tests that trie nodes get scheduled lexicographically when having the same 427 // depth. 428 func TestSyncOrdering(t *testing.T) { 429 // Create a random trie to copy 430 srcDb, srcTrie, srcData := makeTestTrie() 431 432 // Create a destination trie and sync with the scheduler, tracking the requests 433 diskdb := memorydb.New() 434 triedb := NewDatabase(diskdb) 435 sched := NewSync(srcTrie.Hash(), diskdb, nil, NewSyncBloom(1, diskdb)) 436 437 nodes, paths, _ := sched.Missing(1) 438 queue := append([]common.Hash{}, nodes...) 439 reqs := append([]SyncPath{}, paths...) 440 441 for len(queue) > 0 { 442 results := make([]SyncResult, len(queue)) 443 for i, hash := range queue { 444 data, err := srcDb.Node(hash) 445 if err != nil { 446 t.Fatalf("failed to retrieve node data for %x: %v", hash, err) 447 } 448 results[i] = SyncResult{hash, data} 449 } 450 for _, result := range results { 451 if err := sched.Process(result); err != nil { 452 t.Fatalf("failed to process result %v", err) 453 } 454 } 455 batch := diskdb.NewBatch() 456 if err := sched.Commit(batch); err != nil { 457 t.Fatalf("failed to commit data: %v", err) 458 } 459 batch.Write() 460 461 nodes, paths, _ = sched.Missing(1) 462 queue = append(queue[:0], nodes...) 463 reqs = append(reqs, paths...) 464 } 465 // Cross check that the two tries are in sync 466 checkTrieContents(t, triedb, srcTrie.Hash().Bytes(), srcData) 467 468 // Check that the trie nodes have been requested path-ordered 469 for i := 0; i < len(reqs)-1; i++ { 470 if len(reqs[i]) > 1 || len(reqs[i+1]) > 1 { 471 // In the case of the trie tests, there's no storage so the tuples 472 // must always be single items. 2-tuples should be tested in state. 473 t.Errorf("Invalid request tuples: len(%v) or len(%v) > 1", reqs[i], reqs[i+1]) 474 } 475 if bytes.Compare(compactToHex(reqs[i][0]), compactToHex(reqs[i+1][0])) > 0 { 476 t.Errorf("Invalid request order: %v before %v", compactToHex(reqs[i][0]), compactToHex(reqs[i+1][0])) 477 } 478 } 479 }