github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/ledger/complete/wal/checkpoint_v6_test.go (about) 1 package wal 2 3 import ( 4 "bufio" 5 "bytes" 6 "crypto/rand" 7 "errors" 8 "fmt" 9 "io" 10 "os" 11 "path" 12 "path/filepath" 13 "testing" 14 15 "github.com/rs/zerolog" 16 "github.com/rs/zerolog/log" 17 "github.com/stretchr/testify/require" 18 19 "github.com/onflow/flow-go/ledger" 20 "github.com/onflow/flow-go/ledger/common/hash" 21 "github.com/onflow/flow-go/ledger/common/testutils" 22 "github.com/onflow/flow-go/ledger/complete/mtrie/node" 23 "github.com/onflow/flow-go/ledger/complete/mtrie/trie" 24 "github.com/onflow/flow-go/utils/unittest" 25 ) 26 27 func TestVersion(t *testing.T) { 28 m, v, err := decodeVersion(encodeVersion(MagicBytesCheckpointHeader, VersionV6)) 29 require.NoError(t, err) 30 require.Equal(t, MagicBytesCheckpointHeader, m) 31 require.Equal(t, VersionV6, v) 32 } 33 34 func TestSubtrieCount(t *testing.T) { 35 l, err := decodeSubtrieCount(encodeSubtrieCount(subtrieCount)) 36 require.NoError(t, err) 37 require.Equal(t, uint16(subtrieCount), l) 38 } 39 40 func TestCRC32SumEncoding(t *testing.T) { 41 v := uint32(3) 42 s, err := decodeCRC32Sum(encodeCRC32Sum(v)) 43 require.NoError(t, err) 44 require.Equal(t, v, s) 45 } 46 47 func TestSubtrieNodeCountEncoding(t *testing.T) { 48 v := uint64(10000) 49 s, err := decodeNodeCount(encodeNodeCount(v)) 50 require.NoError(t, err) 51 require.Equal(t, v, s) 52 } 53 54 func TestFooterEncoding(t *testing.T) { 55 n1, r1 := uint64(40), uint16(500) 56 n2, r2, err := decodeTopLevelNodesAndTriesFooter(encodeTopLevelNodesAndTriesFooter(n1, r1)) 57 require.NoError(t, err) 58 require.Equal(t, n1, n2) 59 require.Equal(t, r1, r2) 60 } 61 62 func requireTriesEqual(t *testing.T, tries1, tries2 []*trie.MTrie) { 63 require.Equal(t, len(tries1), len(tries2), "tries have different length") 64 for i, expect := range tries1 { 65 actual := tries2[i] 66 require.True(t, expect.Equals(actual), "%v-th trie is different", i) 67 } 68 } 69 70 func createSimpleTrie(t *testing.T) []*trie.MTrie { 71 emptyTrie := trie.NewEmptyMTrie() 72 73 p1 := testutils.PathByUint8(0) 74 v1 := testutils.LightPayload8('A', 'a') 75 76 p2 := testutils.PathByUint8(1) 77 v2 := testutils.LightPayload8('B', 'b') 78 79 paths := []ledger.Path{p1, p2} 80 payloads := []ledger.Payload{*v1, *v2} 81 82 updatedTrie, _, err := trie.NewTrieWithUpdatedRegisters(emptyTrie, paths, payloads, true) 83 require.NoError(t, err) 84 tries := []*trie.MTrie{emptyTrie, updatedTrie} 85 return tries 86 } 87 88 func randPathPayload() (ledger.Path, ledger.Payload) { 89 var path ledger.Path 90 _, err := rand.Read(path[:]) 91 if err != nil { 92 panic("randomness failed") 93 } 94 payload := testutils.RandomPayload(1, 100) 95 return path, *payload 96 } 97 98 func randNPathPayloads(n int) ([]ledger.Path, []ledger.Payload) { 99 paths := make([]ledger.Path, n) 100 payloads := make([]ledger.Payload, n) 101 for i := 0; i < n; i++ { 102 path, payload := randPathPayload() 103 paths[i] = path 104 payloads[i] = payload 105 } 106 return paths, payloads 107 } 108 109 func createMultipleRandomTries(t *testing.T) []*trie.MTrie { 110 tries := make([]*trie.MTrie, 0) 111 activeTrie := trie.NewEmptyMTrie() 112 113 var err error 114 // add tries with no shared paths 115 for i := 0; i < 100; i++ { 116 paths, payloads := randNPathPayloads(100) 117 activeTrie, _, err = trie.NewTrieWithUpdatedRegisters(activeTrie, paths, payloads, false) 118 require.NoError(t, err, "update registers") 119 tries = append(tries, activeTrie) 120 } 121 122 // add trie with some shared path 123 sharedPaths, payloads1 := randNPathPayloads(100) 124 activeTrie, _, err = trie.NewTrieWithUpdatedRegisters(activeTrie, sharedPaths, payloads1, false) 125 require.NoError(t, err, "update registers") 126 tries = append(tries, activeTrie) 127 128 _, payloads2 := randNPathPayloads(100) 129 activeTrie, _, err = trie.NewTrieWithUpdatedRegisters(activeTrie, sharedPaths, payloads2, false) 130 require.NoError(t, err, "update registers") 131 tries = append(tries, activeTrie) 132 133 return tries 134 } 135 136 func createMultipleRandomTriesMini(t *testing.T) []*trie.MTrie { 137 tries := make([]*trie.MTrie, 0) 138 activeTrie := trie.NewEmptyMTrie() 139 140 var err error 141 // add tries with no shared paths 142 for i := 0; i < 5; i++ { 143 paths, payloads := randNPathPayloads(20) 144 activeTrie, _, err = trie.NewTrieWithUpdatedRegisters(activeTrie, paths, payloads, false) 145 require.NoError(t, err, "update registers") 146 tries = append(tries, activeTrie) 147 } 148 149 // add trie with some shared path 150 sharedPaths, payloads1 := randNPathPayloads(10) 151 activeTrie, _, err = trie.NewTrieWithUpdatedRegisters(activeTrie, sharedPaths, payloads1, false) 152 require.NoError(t, err, "update registers") 153 tries = append(tries, activeTrie) 154 155 _, payloads2 := randNPathPayloads(10) 156 activeTrie, _, err = trie.NewTrieWithUpdatedRegisters(activeTrie, sharedPaths, payloads2, false) 157 require.NoError(t, err, "update registers") 158 tries = append(tries, activeTrie) 159 160 return tries 161 } 162 163 func TestEncodeSubTrie(t *testing.T) { 164 file := "checkpoint" 165 logger := unittest.Logger() 166 tries := createMultipleRandomTries(t) 167 estimatedSubtrieNodeCount := estimateSubtrieNodeCount(tries[0]) 168 subtrieRoots := createSubTrieRoots(tries) 169 170 for index, roots := range subtrieRoots { 171 unittest.RunWithTempDir(t, func(dir string) { 172 uniqueIndices, nodeCount, checksum, err := storeCheckpointSubTrie( 173 index, roots, estimatedSubtrieNodeCount, dir, file, logger) 174 require.NoError(t, err) 175 176 // subtrie roots might have duplciates, that why we group the them, 177 // and store each group in different part file in order to deduplicate. 178 // the returned uniqueIndices contains the index for each unique roots. 179 // in order to verify that, we build a uniqueRoots first, and verify 180 // if any unique root is missing from the uniqueIndices 181 uniqueRoots := make(map[*node.Node]struct{}) 182 for i, root := range roots { 183 if root == nil { 184 fmt.Println(i, "-th subtrie root is nil") 185 } 186 _, ok := uniqueRoots[root] 187 if ok { 188 fmt.Println(i, "-th subtrie root is a duplicate") 189 } 190 uniqueRoots[root] = struct{}{} 191 } 192 193 // each root should be included in the uniqueIndices 194 for _, root := range roots { 195 _, ok := uniqueIndices[root] 196 require.True(t, ok, "each root should be included in the uniqueIndices") 197 } 198 199 if len(uniqueIndices) > 1 { 200 require.Len(t, uniqueIndices, len(uniqueRoots), 201 fmt.Sprintf("uniqueIndices should include all roots, uniqueIndices[nil] %v, roots[0] %v", uniqueIndices[nil], roots[0])) 202 } 203 204 logger.Info().Msgf("sub trie checkpoint stored, uniqueIndices: %v, node count: %v, checksum: %v", 205 uniqueIndices, nodeCount, checksum) 206 207 // all the nodes 208 nodes, err := readCheckpointSubTrie(dir, file, index, checksum, logger) 209 require.NoError(t, err) 210 211 for _, root := range roots { 212 if root == nil { 213 continue 214 } 215 index := uniqueIndices[root] 216 require.Equal(t, root.Hash(), nodes[index-1].Hash(), // -1 because readCheckpointSubTrie returns nodes[1:] 217 "readCheckpointSubTrie should return nodes where the root should be found "+ 218 "by the index specified by the uniqueIndices returned by storeCheckpointSubTrie") 219 } 220 }) 221 } 222 } 223 224 func randomNode() *node.Node { 225 var randomPath ledger.Path 226 _, err := rand.Read(randomPath[:]) 227 if err != nil { 228 panic("randomness failed") 229 } 230 231 var randomHashValue hash.Hash 232 _, err = rand.Read(randomHashValue[:]) 233 if err != nil { 234 panic("randomness failed") 235 } 236 237 return node.NewNode(256, nil, nil, randomPath, nil, randomHashValue) 238 } 239 func TestGetNodesByIndex(t *testing.T) { 240 n := 10 241 ns := make([]*node.Node, n) 242 for i := 0; i < n; i++ { 243 ns[i] = randomNode() 244 } 245 subtrieNodes := [][]*node.Node{ 246 {ns[0], ns[1]}, 247 {ns[2]}, 248 {}, 249 {}, 250 } 251 topLevelNodes := []*node.Node{nil, ns[3]} 252 totalSubTrieNodeCount := computeTotalSubTrieNodeCount(subtrieNodes) 253 254 for i := uint64(1); i <= 4; i++ { 255 node, err := getNodeByIndex(subtrieNodes, totalSubTrieNodeCount, topLevelNodes, i) 256 require.NoError(t, err, "cannot get node by index", i) 257 require.Equal(t, ns[i-1], node, "got wrong node by index %v", i) 258 } 259 } 260 func TestWriteAndReadCheckpointV6EmptyTrie(t *testing.T) { 261 unittest.RunWithTempDir(t, func(dir string) { 262 tries := []*trie.MTrie{trie.NewEmptyMTrie()} 263 fileName := "checkpoint-empty-trie" 264 logger := unittest.Logger() 265 require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") 266 decoded, err := OpenAndReadCheckpointV6(dir, fileName, logger) 267 require.NoErrorf(t, err, "fail to read checkpoint %v/%v", dir, fileName) 268 requireTriesEqual(t, tries, decoded) 269 }) 270 } 271 272 func TestWriteAndReadCheckpointV6SimpleTrie(t *testing.T) { 273 unittest.RunWithTempDir(t, func(dir string) { 274 tries := createSimpleTrie(t) 275 fileName := "checkpoint" 276 logger := unittest.Logger() 277 require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") 278 decoded, err := OpenAndReadCheckpointV6(dir, fileName, logger) 279 require.NoErrorf(t, err, "fail to read checkpoint %v/%v", dir, fileName) 280 requireTriesEqual(t, tries, decoded) 281 }) 282 } 283 284 func TestWriteAndReadCheckpointV6MultipleTries(t *testing.T) { 285 unittest.RunWithTempDir(t, func(dir string) { 286 tries := createMultipleRandomTries(t) 287 fileName := "checkpoint-multi-file" 288 logger := unittest.Logger() 289 require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") 290 decoded, err := OpenAndReadCheckpointV6(dir, fileName, logger) 291 require.NoErrorf(t, err, "fail to read checkpoint %v/%v", dir, fileName) 292 requireTriesEqual(t, tries, decoded) 293 }) 294 } 295 296 // test running checkpointing twice will produce the same checkpoint file 297 func TestCheckpointV6IsDeterminstic(t *testing.T) { 298 unittest.RunWithTempDir(t, func(dir string) { 299 tries := createMultipleRandomTries(t) 300 logger := unittest.Logger() 301 require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, "checkpoint1", logger), "fail to store checkpoint") 302 require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, "checkpoint2", logger), "fail to store checkpoint") 303 partFiles1 := filePaths(dir, "checkpoint1", subtrieLevel) 304 partFiles2 := filePaths(dir, "checkpoint2", subtrieLevel) 305 for i, partFile1 := range partFiles1 { 306 partFile2 := partFiles2[i] 307 require.NoError(t, compareFiles( 308 partFile1, partFile2), 309 "found difference in checkpoint files") 310 311 } 312 }) 313 } 314 315 func TestWriteAndReadCheckpointV6LeafEmptyTrie(t *testing.T) { 316 unittest.RunWithTempDir(t, func(dir string) { 317 tries := []*trie.MTrie{trie.NewEmptyMTrie()} 318 fileName := "checkpoint-empty-trie" 319 logger := unittest.Logger() 320 require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") 321 322 bufSize := 10 323 leafNodesCh := make(chan *LeafNode, bufSize) 324 go func() { 325 err := OpenAndReadLeafNodesFromCheckpointV6(leafNodesCh, dir, fileName, logger) 326 require.NoErrorf(t, err, "fail to read checkpoint %v/%v", dir, fileName) 327 }() 328 for range leafNodesCh { 329 require.Fail(t, "should not return any nodes") 330 } 331 }) 332 } 333 334 func TestWriteAndReadCheckpointV6LeafSimpleTrie(t *testing.T) { 335 unittest.RunWithTempDir(t, func(dir string) { 336 tries := createSimpleTrie(t) 337 fileName := "checkpoint" 338 logger := unittest.Logger() 339 require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") 340 bufSize := 1 341 leafNodesCh := make(chan *LeafNode, bufSize) 342 go func() { 343 err := OpenAndReadLeafNodesFromCheckpointV6(leafNodesCh, dir, fileName, logger) 344 require.NoErrorf(t, err, "fail to read checkpoint %v/%v", dir, fileName) 345 }() 346 resultPayloads := make([]*ledger.Payload, 0) 347 for leafNode := range leafNodesCh { 348 // avoid dummy payload from empty trie 349 if leafNode.Payload != nil { 350 resultPayloads = append(resultPayloads, leafNode.Payload) 351 } 352 } 353 require.EqualValues(t, tries[1].AllPayloads(), resultPayloads) 354 }) 355 } 356 357 func TestWriteAndReadCheckpointV6LeafMultipleTries(t *testing.T) { 358 unittest.RunWithTempDir(t, func(dir string) { 359 fileName := "checkpoint-multi-leaf-file" 360 tries := createMultipleRandomTriesMini(t) 361 logger := unittest.Logger() 362 require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") 363 bufSize := 5 364 leafNodesCh := make(chan *LeafNode, bufSize) 365 go func() { 366 err := OpenAndReadLeafNodesFromCheckpointV6(leafNodesCh, dir, fileName, logger) 367 require.NoErrorf(t, err, "fail to read checkpoint %v/%v", dir, fileName) 368 }() 369 resultPayloads := make([]ledger.Payload, 0) 370 for leafNode := range leafNodesCh { 371 resultPayloads = append(resultPayloads, *leafNode.Payload) 372 } 373 require.NotEmpty(t, resultPayloads) 374 }) 375 } 376 377 // compareFiles takes two files' full path, and read them bytes by bytes and compare if 378 // the two files are identical 379 // it returns nil if identical 380 // it returns error if there is difference 381 func compareFiles(file1, file2 string) error { 382 closable1, err := os.Open(file1) 383 if err != nil { 384 return fmt.Errorf("could not open file 1 %v: %w", closable1, err) 385 } 386 defer func(f *os.File) { 387 f.Close() 388 }(closable1) 389 390 closable2, err := os.Open(file1) 391 if err != nil { 392 return fmt.Errorf("could not open file 2 %v: %w", closable2, err) 393 } 394 defer func(f *os.File) { 395 f.Close() 396 }(closable2) 397 398 reader1 := bufio.NewReaderSize(closable1, defaultBufioReadSize) 399 reader2 := bufio.NewReaderSize(closable2, defaultBufioReadSize) 400 401 buf1 := make([]byte, defaultBufioReadSize) 402 buf2 := make([]byte, defaultBufioReadSize) 403 for { 404 _, err1 := reader1.Read(buf1) 405 _, err2 := reader2.Read(buf2) 406 if errors.Is(err1, io.EOF) && errors.Is(err2, io.EOF) { 407 break 408 } 409 410 if err1 != nil { 411 return err1 412 } 413 if err2 != nil { 414 return err2 415 } 416 417 if !bytes.Equal(buf1, buf2) { 418 return fmt.Errorf("bytes are different: %x, %x", buf1, buf2) 419 } 420 } 421 422 return nil 423 } 424 425 func storeCheckpointV5(tries []*trie.MTrie, dir string, fileName string, logger zerolog.Logger) error { 426 return StoreCheckpointV5(dir, fileName, logger, tries...) 427 } 428 429 func TestWriteAndReadCheckpointV5(t *testing.T) { 430 unittest.RunWithTempDir(t, func(dir string) { 431 tries := createMultipleRandomTries(t) 432 fileName := "checkpoint1" 433 logger := unittest.Logger() 434 435 require.NoErrorf(t, storeCheckpointV5(tries, dir, fileName, logger), "fail to store checkpoint") 436 decoded, err := LoadCheckpoint(filepath.Join(dir, fileName), logger) 437 require.NoErrorf(t, err, "fail to load checkpoint") 438 requireTriesEqual(t, tries, decoded) 439 }) 440 } 441 442 // test that converting a v6 back to v5 would produce the same v5 checkpoint as 443 // producing directly to v5 444 func TestWriteAndReadCheckpointV6ThenBackToV5(t *testing.T) { 445 unittest.RunWithTempDir(t, func(dir string) { 446 tries := createMultipleRandomTries(t) 447 logger := unittest.Logger() 448 449 // store tries into v6 then read back, then store into v5 450 require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, "checkpoint-v6", logger), "fail to store checkpoint") 451 decoded, err := OpenAndReadCheckpointV6(dir, "checkpoint-v6", logger) 452 require.NoErrorf(t, err, "fail to read checkpoint %v/checkpoint-v6", dir) 453 require.NoErrorf(t, storeCheckpointV5(decoded, dir, "checkpoint-v6-v5", logger), "fail to store checkpoint") 454 455 // store tries directly into v5 checkpoint 456 require.NoErrorf(t, storeCheckpointV5(tries, dir, "checkpoint-v5", logger), "fail to store checkpoint") 457 458 // compare the two v5 checkpoint files should be identical 459 require.NoError(t, compareFiles( 460 path.Join(dir, "checkpoint-v5"), 461 path.Join(dir, "checkpoint-v6-v5")), 462 "found difference in checkpoint files") 463 }) 464 } 465 466 func TestCleanupOnErrorIfNotExist(t *testing.T) { 467 t.Run("works if temp files not exist", func(t *testing.T) { 468 require.NoError(t, deleteCheckpointFiles("not-exist", "checkpoint-v6")) 469 }) 470 471 // if it can clean up all files after successful storing, then it can 472 // clean up if failed in middle. 473 t.Run("clean up after finish storing files", func(t *testing.T) { 474 unittest.RunWithTempDir(t, func(dir string) { 475 tries := createMultipleRandomTries(t) 476 logger := unittest.Logger() 477 478 // store tries into v6 then read back, then store into v5 479 require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, "checkpoint-v6", logger), "fail to store checkpoint") 480 require.NoError(t, deleteCheckpointFiles(dir, "checkpoint-v6")) 481 482 // verify all files are removed 483 files := filePaths(dir, "checkpoint-v6", subtrieLevel) 484 for _, file := range files { 485 _, err := os.Stat(file) 486 require.True(t, os.IsNotExist(err), err) 487 } 488 }) 489 }) 490 } 491 492 // verify that if a part file is missing then os.ErrNotExist should return 493 func TestAllPartFileExist(t *testing.T) { 494 unittest.RunWithTempDir(t, func(dir string) { 495 for i := 0; i < 17; i++ { 496 tries := createSimpleTrie(t) 497 fileName := fmt.Sprintf("checkpoint_missing_part_file_%v", i) 498 var fileToDelete string 499 var err error 500 if i == 16 { 501 fileToDelete, _ = filePathTopTries(dir, fileName) 502 } else { 503 fileToDelete, _, err = filePathSubTries(dir, fileName, i) 504 } 505 require.NoErrorf(t, err, "fail to find sub trie file path") 506 507 logger := unittest.Logger() 508 require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") 509 510 // delete i-th part file, then the error should mention i-th file missing 511 err = os.Remove(fileToDelete) 512 require.NoError(t, err, "fail to remove part file") 513 514 _, err = OpenAndReadCheckpointV6(dir, fileName, logger) 515 require.ErrorIs(t, err, os.ErrNotExist, "wrong error type returned") 516 } 517 }) 518 } 519 520 // verify that if a part file is missing then os.ErrNotExist should return 521 func TestAllPartFileExistLeafReader(t *testing.T) { 522 unittest.RunWithTempDir(t, func(dir string) { 523 for i := 0; i < 17; i++ { 524 tries := createSimpleTrie(t) 525 fileName := fmt.Sprintf("checkpoint_missing_part_file_%v", i) 526 var fileToDelete string 527 var err error 528 if i == 16 { 529 fileToDelete, _ = filePathTopTries(dir, fileName) 530 } else { 531 fileToDelete, _, err = filePathSubTries(dir, fileName, i) 532 } 533 require.NoErrorf(t, err, "fail to find sub trie file path") 534 535 logger := unittest.Logger() 536 require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") 537 538 // delete i-th part file, then the error should mention i-th file missing 539 err = os.Remove(fileToDelete) 540 require.NoError(t, err, "fail to remove part file") 541 542 bufSize := 10 543 leafNodesCh := make(chan *LeafNode, bufSize) 544 err = OpenAndReadLeafNodesFromCheckpointV6(leafNodesCh, dir, fileName, logger) 545 require.ErrorIs(t, err, os.ErrNotExist, "wrong error type returned") 546 } 547 }) 548 } 549 550 // verify that can't store the same checkpoint file twice, because a checkpoint already exists 551 func TestCannotStoreTwice(t *testing.T) { 552 unittest.RunWithTempDir(t, func(dir string) { 553 tries := createSimpleTrie(t) 554 fileName := "checkpoint" 555 logger := unittest.Logger() 556 require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") 557 // checkpoint already exist, can't store again 558 require.Error(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger)) 559 }) 560 } 561 562 func filePaths(dir string, fileName string, subtrieLevel uint16) []string { 563 paths := make([]string, 0) 564 565 paths = append(paths, filePathCheckpointHeader(dir, fileName)) 566 567 subtrieCount := subtrieCountByLevel(subtrieLevel) 568 for i := 0; i < subtrieCount; i++ { 569 partFile := partFileName(fileName, i) 570 paths = append(paths, path.Join(dir, partFile)) 571 } 572 573 p, _ := filePathTopTries(dir, fileName) 574 paths = append(paths, p) 575 return paths 576 } 577 578 func TestCopyCheckpointFileV6(t *testing.T) { 579 unittest.RunWithTempDir(t, func(dir string) { 580 tries := createSimpleTrie(t) 581 fileName := "checkpoint" 582 logger := unittest.Logger() 583 require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") 584 to := filepath.Join(dir, "newfolder") 585 newPaths, err := CopyCheckpointFile(fileName, dir, to) 586 require.NoError(t, err) 587 log.Info().Msgf("copied to :%v", newPaths) 588 decoded, err := OpenAndReadCheckpointV6(to, fileName, logger) 589 require.NoErrorf(t, err, "fail to read checkpoint %v/%v", dir, fileName) 590 requireTriesEqual(t, tries, decoded) 591 }) 592 } 593 594 func TestReadCheckpointRootHash(t *testing.T) { 595 unittest.RunWithTempDir(t, func(dir string) { 596 tries := createSimpleTrie(t) 597 fileName := "checkpoint" 598 logger := unittest.Logger() 599 require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") 600 601 trieRoots, err := ReadTriesRootHash(logger, dir, fileName) 602 require.NoError(t, err) 603 for i, root := range trieRoots { 604 expectedHash := tries[i].RootHash() 605 require.Equal(t, expectedHash, root) 606 } 607 require.Equal(t, len(tries), len(trieRoots)) 608 }) 609 } 610 611 func TestReadCheckpointRootHashValidateChecksum(t *testing.T) { 612 unittest.RunWithTempDir(t, func(dir string) { 613 tries := createSimpleTrie(t) 614 fileName := "checkpoint" 615 logger := unittest.Logger() 616 require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") 617 618 // add a wrong checksum to top trie file 619 topTrieFilePath, _ := filePathTopTries(dir, fileName) 620 file, err := os.OpenFile(topTrieFilePath, os.O_RDWR, 0644) 621 require.NoError(t, err) 622 623 fileInfo, err := file.Stat() 624 require.NoError(t, err) 625 fileSize := fileInfo.Size() 626 627 invalidSum := encodeCRC32Sum(10) 628 _, err = file.WriteAt(invalidSum, fileSize-crc32SumSize) 629 require.NoError(t, err) 630 require.NoError(t, file.Close()) 631 632 // ReadTriesRootHash will first validate the checksum and detect the error 633 _, err = ReadTriesRootHash(logger, dir, fileName) 634 require.Error(t, err) 635 }) 636 } 637 638 func TestReadCheckpointRootHashMulti(t *testing.T) { 639 unittest.RunWithTempDir(t, func(dir string) { 640 tries := createMultipleRandomTries(t) 641 fileName := "checkpoint" 642 logger := unittest.Logger() 643 require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") 644 645 trieRoots, err := ReadTriesRootHash(logger, dir, fileName) 646 require.NoError(t, err) 647 for i, root := range trieRoots { 648 expectedHash := tries[i].RootHash() 649 require.Equal(t, expectedHash, root) 650 } 651 require.Equal(t, len(tries), len(trieRoots)) 652 }) 653 } 654 655 func TestCheckpointHasRootHash(t *testing.T) { 656 unittest.RunWithTempDir(t, func(dir string) { 657 tries := createMultipleRandomTries(t) 658 fileName := "checkpoint" 659 logger := unittest.Logger() 660 require.NoErrorf(t, StoreCheckpointV6Concurrently(tries, dir, fileName, logger), "fail to store checkpoint") 661 662 trieRoots, err := ReadTriesRootHash(logger, dir, fileName) 663 require.NoError(t, err) 664 for _, root := range trieRoots { 665 require.NoError(t, CheckpointHasRootHash(logger, dir, fileName, root)) 666 } 667 668 nonExist := ledger.RootHash(unittest.StateCommitmentFixture()) 669 require.Error(t, CheckpointHasRootHash(logger, dir, fileName, nonExist)) 670 }) 671 }