github.com/okex/exchain@v1.8.0/libs/tendermint/store/store_test.go (about) 1 package store 2 3 import ( 4 "bytes" 5 "fmt" 6 "math" 7 "math/rand" 8 "os" 9 "runtime/debug" 10 "strings" 11 "testing" 12 "time" 13 14 db "github.com/okex/exchain/libs/tm-db" 15 dbm "github.com/okex/exchain/libs/tm-db" 16 "github.com/pkg/errors" 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 20 cfg "github.com/okex/exchain/libs/tendermint/config" 21 "github.com/okex/exchain/libs/tendermint/libs/log" 22 sm "github.com/okex/exchain/libs/tendermint/state" 23 24 "github.com/okex/exchain/libs/tendermint/types" 25 tmtime "github.com/okex/exchain/libs/tendermint/types/time" 26 ) 27 28 // A cleanupFunc cleans up any config / test files created for a particular 29 // test. 30 type cleanupFunc func() 31 32 // make a Commit with a single vote containing just the height and a timestamp 33 func makeTestCommit(height int64, timestamp time.Time) *types.Commit { 34 commitSigs := []types.CommitSig{{ 35 BlockIDFlag: types.BlockIDFlagCommit, 36 ValidatorAddress: []byte("ValidatorAddress"), 37 Timestamp: timestamp, 38 Signature: []byte("Signature"), 39 }} 40 return types.NewCommit(height, 0, types.BlockID{}, commitSigs) 41 } 42 43 func makeTxs(height int64) (txs []types.Tx) { 44 for i := 0; i < 10; i++ { 45 txs = append(txs, types.Tx([]byte{byte(height), byte(i)})) 46 } 47 return txs 48 } 49 50 func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block { 51 block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address) 52 return block 53 } 54 55 func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore, cleanupFunc) { 56 config := cfg.ResetTestRoot("blockchain_reactor_test") 57 // blockDB := dbm.NewDebugDB("blockDB", dbm.NewMemDB()) 58 // stateDB := dbm.NewDebugDB("stateDB", dbm.NewMemDB()) 59 blockDB := dbm.NewMemDB() 60 stateDB := dbm.NewMemDB() 61 state, err := sm.LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile()) 62 if err != nil { 63 panic(errors.Wrap(err, "error constructing state from genesis file")) 64 } 65 return state, NewBlockStore(blockDB), func() { os.RemoveAll(config.RootDir) } 66 } 67 68 func TestLoadBlockStoreStateJSON(t *testing.T) { 69 db := db.NewMemDB() 70 bsj := &BlockStoreStateJSON{Base: 100, Height: 1000} 71 bsj.Save(db) 72 73 retrBSJ := LoadBlockStoreStateJSON(db) 74 assert.Equal(t, *bsj, retrBSJ, "expected the retrieved DBs to match") 75 } 76 77 func TestLoadBlockStoreStateJSON_Empty(t *testing.T) { 78 db := db.NewMemDB() 79 80 bsj := &BlockStoreStateJSON{} 81 bsj.Save(db) 82 83 retrBSJ := LoadBlockStoreStateJSON(db) 84 assert.Equal(t, BlockStoreStateJSON{}, retrBSJ, "expected the retrieved DBs to match") 85 } 86 87 func TestLoadBlockStoreStateJSON_NoBase(t *testing.T) { 88 db := db.NewMemDB() 89 90 bsj := &BlockStoreStateJSON{Height: 1000} 91 bsj.Save(db) 92 93 retrBSJ := LoadBlockStoreStateJSON(db) 94 assert.Equal(t, BlockStoreStateJSON{Base: 1, Height: 1000}, retrBSJ, "expected the retrieved DBs to match") 95 } 96 97 func TestNewBlockStore(t *testing.T) { 98 db := db.NewMemDB() 99 err := db.Set(blockStoreKey, []byte(`{"base": "100", "height": "10000"}`)) 100 require.NoError(t, err) 101 bs := NewBlockStore(db) 102 require.Equal(t, int64(100), bs.Base(), "failed to properly parse blockstore") 103 require.Equal(t, int64(10000), bs.Height(), "failed to properly parse blockstore") 104 105 panicCausers := []struct { 106 data []byte 107 wantErr string 108 }{ 109 {[]byte("artful-doger"), "not unmarshal bytes"}, 110 {[]byte(" "), "unmarshal bytes"}, 111 } 112 113 for i, tt := range panicCausers { 114 tt := tt 115 // Expecting a panic here on trying to parse an invalid blockStore 116 _, _, panicErr := doFn(func() (interface{}, error) { 117 err := db.Set(blockStoreKey, tt.data) 118 require.NoError(t, err) 119 _ = NewBlockStore(db) 120 return nil, nil 121 }) 122 require.NotNil(t, panicErr, "#%d panicCauser: %q expected a panic", i, tt.data) 123 assert.Contains(t, fmt.Sprintf("%#v", panicErr), tt.wantErr, "#%d data: %q", i, tt.data) 124 } 125 126 err = db.Set(blockStoreKey, nil) 127 require.NoError(t, err) 128 bs = NewBlockStore(db) 129 assert.Equal(t, bs.Height(), int64(0), "expecting nil bytes to be unmarshaled alright") 130 } 131 132 func freshBlockStore() (*BlockStore, db.DB) { 133 db := db.NewMemDB() 134 return NewBlockStore(db), db 135 } 136 137 var ( 138 state sm.State 139 block *types.Block 140 partSet *types.PartSet 141 part1 *types.Part 142 part2 *types.Part 143 seenCommit1 *types.Commit 144 ) 145 146 func TestMain(m *testing.M) { 147 var cleanup cleanupFunc 148 state, _, cleanup = makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer))) 149 block = makeBlock(1, state, new(types.Commit)) 150 partSet = block.MakePartSet(2) 151 part1 = partSet.GetPart(0) 152 part2 = partSet.GetPart(1) 153 seenCommit1 = makeTestCommit(10, tmtime.Now()) 154 code := m.Run() 155 cleanup() 156 os.Exit(code) 157 } 158 159 // TODO: This test should be simplified ... 160 161 func TestBlockStoreSaveLoadBlock(t *testing.T) { 162 state, bs, cleanup := makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer))) 163 defer cleanup() 164 require.Equal(t, bs.Base(), int64(0), "initially the base should be zero") 165 require.Equal(t, bs.Height(), int64(0), "initially the height should be zero") 166 167 // check there are no blocks at various heights 168 noBlockHeights := []int64{0, -1, 100, 1000, 2} 169 for i, height := range noBlockHeights { 170 if g := bs.LoadBlock(height); g != nil { 171 t.Errorf("#%d: height(%d) got a block; want nil", i, height) 172 } 173 } 174 175 // save a block 176 block := makeBlock(bs.Height()+1, state, new(types.Commit)) 177 validPartSet := block.MakePartSet(2) 178 seenCommit := makeTestCommit(10, tmtime.Now()) 179 bs.SaveBlock(block, partSet, seenCommit) 180 require.EqualValues(t, 1, bs.Base(), "expecting the new height to be changed") 181 require.EqualValues(t, block.Header.Height, bs.Height(), "expecting the new height to be changed") 182 183 incompletePartSet := types.NewPartSetFromHeader(types.PartSetHeader{Total: 2}) 184 uncontiguousPartSet := types.NewPartSetFromHeader(types.PartSetHeader{Total: 0}) 185 uncontiguousPartSet.AddPart(part2) 186 187 header1 := types.Header{ 188 Height: 1, 189 ChainID: "block_test", 190 Time: tmtime.Now(), 191 } 192 193 // End of setup, test data 194 195 commitAtH10 := makeTestCommit(10, tmtime.Now()) 196 tuples := []struct { 197 block *types.Block 198 parts *types.PartSet 199 seenCommit *types.Commit 200 wantPanic string 201 wantErr bool 202 203 corruptBlockInDB bool 204 corruptCommitInDB bool 205 corruptSeenCommitInDB bool 206 eraseCommitInDB bool 207 eraseSeenCommitInDB bool 208 }{ 209 { 210 block: newBlock(header1, commitAtH10), 211 parts: validPartSet, 212 seenCommit: seenCommit1, 213 }, 214 215 { 216 block: nil, 217 wantPanic: "only save a non-nil block", 218 }, 219 220 { 221 block: newBlock( // New block at height 5 in empty block store is fine 222 types.Header{Height: 5, ChainID: "block_test", Time: tmtime.Now()}, 223 makeTestCommit(5, tmtime.Now()), 224 ), 225 parts: validPartSet, 226 seenCommit: makeTestCommit(5, tmtime.Now()), 227 }, 228 229 { 230 block: newBlock(header1, commitAtH10), 231 parts: incompletePartSet, 232 wantPanic: "only save complete block", // incomplete parts 233 }, 234 235 { 236 block: newBlock(header1, commitAtH10), 237 parts: validPartSet, 238 seenCommit: seenCommit1, 239 corruptCommitInDB: true, // Corrupt the DB's commit entry 240 wantPanic: "unmarshal to types.Commit failed", 241 }, 242 243 { 244 block: newBlock(header1, commitAtH10), 245 parts: validPartSet, 246 seenCommit: seenCommit1, 247 wantPanic: "unmarshal to types.BlockMeta failed", 248 corruptBlockInDB: true, // Corrupt the DB's block entry 249 }, 250 251 { 252 block: newBlock(header1, commitAtH10), 253 parts: validPartSet, 254 seenCommit: seenCommit1, 255 256 // Expecting no error and we want a nil back 257 eraseSeenCommitInDB: true, 258 }, 259 260 { 261 block: newBlock(header1, commitAtH10), 262 parts: validPartSet, 263 seenCommit: seenCommit1, 264 265 corruptSeenCommitInDB: true, 266 wantPanic: "unmarshal to types.Commit failed", 267 }, 268 269 { 270 block: newBlock(header1, commitAtH10), 271 parts: validPartSet, 272 seenCommit: seenCommit1, 273 274 // Expecting no error and we want a nil back 275 eraseCommitInDB: true, 276 }, 277 } 278 279 type quad struct { 280 block *types.Block 281 commit *types.Commit 282 meta *types.BlockMeta 283 284 seenCommit *types.Commit 285 } 286 287 for i, tuple := range tuples { 288 tuple := tuple 289 bs, db := freshBlockStore() 290 // SaveBlock 291 res, err, panicErr := doFn(func() (interface{}, error) { 292 bs.SaveBlock(tuple.block, tuple.parts, tuple.seenCommit) 293 if tuple.block == nil { 294 return nil, nil 295 } 296 297 if tuple.corruptBlockInDB { 298 err := db.Set(calcBlockMetaKey(tuple.block.Height), []byte("block-bogus")) 299 require.NoError(t, err) 300 } 301 bBlock := bs.LoadBlock(tuple.block.Height) 302 bBlockMeta := bs.LoadBlockMeta(tuple.block.Height) 303 304 if tuple.eraseSeenCommitInDB { 305 db.Delete(calcSeenCommitKey(tuple.block.Height)) 306 } 307 if tuple.corruptSeenCommitInDB { 308 err := db.Set(calcSeenCommitKey(tuple.block.Height), []byte("bogus-seen-commit")) 309 require.NoError(t, err) 310 } 311 bSeenCommit := bs.LoadSeenCommit(tuple.block.Height) 312 313 commitHeight := tuple.block.Height - 1 314 if tuple.eraseCommitInDB { 315 db.Delete(calcBlockCommitKey(commitHeight)) 316 } 317 if tuple.corruptCommitInDB { 318 err := db.Set(calcBlockCommitKey(commitHeight), []byte("foo-bogus")) 319 require.NoError(t, err) 320 } 321 bCommit := bs.LoadBlockCommit(commitHeight) 322 return &quad{block: bBlock, seenCommit: bSeenCommit, commit: bCommit, 323 meta: bBlockMeta}, nil 324 }) 325 326 if subStr := tuple.wantPanic; subStr != "" { 327 if panicErr == nil { 328 t.Errorf("#%d: want a non-nil panic", i) 329 } else if got := fmt.Sprintf("%#v", panicErr); !strings.Contains(got, subStr) { 330 t.Errorf("#%d:\n\tgotErr: %q\nwant substring: %q", i, got, subStr) 331 } 332 continue 333 } 334 335 if tuple.wantErr { 336 if err == nil { 337 t.Errorf("#%d: got nil error", i) 338 } 339 continue 340 } 341 342 assert.Nil(t, panicErr, "#%d: unexpected panic", i) 343 assert.Nil(t, err, "#%d: expecting a non-nil error", i) 344 qua, ok := res.(*quad) 345 if !ok || qua == nil { 346 t.Errorf("#%d: got nil quad back; gotType=%T", i, res) 347 continue 348 } 349 if tuple.eraseSeenCommitInDB { 350 assert.Nil(t, qua.seenCommit, 351 "erased the seenCommit in the DB hence we should get back a nil seenCommit") 352 } 353 if tuple.eraseCommitInDB { 354 assert.Nil(t, qua.commit, 355 "erased the commit in the DB hence we should get back a nil commit") 356 } 357 } 358 } 359 360 func TestLoadBlockPart(t *testing.T) { 361 bs, db := freshBlockStore() 362 height, index := int64(10), 1 363 loadPart := func() (interface{}, error) { 364 part := bs.LoadBlockPart(height, index) 365 return part, nil 366 } 367 368 // Initially no contents. 369 // 1. Requesting for a non-existent block shouldn't fail 370 res, _, panicErr := doFn(loadPart) 371 require.Nil(t, panicErr, "a non-existent block part shouldn't cause a panic") 372 require.Nil(t, res, "a non-existent block part should return nil") 373 374 // 2. Next save a corrupted block then try to load it 375 err := db.Set(calcBlockPartKey(height, index), []byte("Tendermint")) 376 require.NoError(t, err) 377 res, _, panicErr = doFn(loadPart) 378 require.NotNil(t, panicErr, "expecting a non-nil panic") 379 require.Contains(t, panicErr.Error(), "unmarshal to types.Part failed") 380 381 // 3. A good block serialized and saved to the DB should be retrievable 382 err = db.Set(calcBlockPartKey(height, index), cdc.MustMarshalBinaryBare(part1)) 383 require.NoError(t, err) 384 gotPart, _, panicErr := doFn(loadPart) 385 require.Nil(t, panicErr, "an existent and proper block should not panic") 386 require.Nil(t, res, "a properly saved block should return a proper block") 387 require.Equal(t, gotPart.(*types.Part), part1, 388 "expecting successful retrieval of previously saved block") 389 } 390 391 func TestPruneBlocks(t *testing.T) { 392 config := cfg.ResetTestRoot("blockchain_reactor_test") 393 defer os.RemoveAll(config.RootDir) 394 state, err := sm.LoadStateFromDBOrGenesisFile(dbm.NewMemDB(), config.GenesisFile()) 395 require.NoError(t, err) 396 db := dbm.NewMemDB() 397 bs := NewBlockStore(db) 398 assert.EqualValues(t, 0, bs.Base()) 399 assert.EqualValues(t, 0, bs.Height()) 400 assert.EqualValues(t, 0, bs.Size()) 401 402 // pruning an empty store should error, even when pruning to 0 403 _, err = bs.PruneBlocks(1) 404 require.Error(t, err) 405 406 _, err = bs.PruneBlocks(0) 407 require.Error(t, err) 408 409 // make more than 1000 blocks, to test batch deletions 410 for h := int64(1); h <= 1500; h++ { 411 block := makeBlock(h, state, new(types.Commit)) 412 partSet := block.MakePartSet(2) 413 seenCommit := makeTestCommit(h, tmtime.Now()) 414 bs.SaveBlock(block, partSet, seenCommit) 415 } 416 417 assert.EqualValues(t, 1, bs.Base()) 418 assert.EqualValues(t, 1500, bs.Height()) 419 assert.EqualValues(t, 1500, bs.Size()) 420 421 prunedBlock := bs.LoadBlock(1199) 422 423 // Check that basic pruning works 424 pruned, err := bs.PruneBlocks(1200) 425 require.NoError(t, err) 426 assert.EqualValues(t, 1199, pruned) 427 assert.EqualValues(t, 1200, bs.Base()) 428 assert.EqualValues(t, 1500, bs.Height()) 429 assert.EqualValues(t, 301, bs.Size()) 430 assert.EqualValues(t, BlockStoreStateJSON{ 431 Base: 1200, 432 Height: 1500, 433 }, LoadBlockStoreStateJSON(db)) 434 435 require.NotNil(t, bs.LoadBlock(1200)) 436 require.Nil(t, bs.LoadBlock(1199)) 437 require.Nil(t, bs.LoadBlockByHash(prunedBlock.Hash())) 438 require.Nil(t, bs.LoadBlockCommit(1199)) 439 require.Nil(t, bs.LoadBlockMeta(1199)) 440 require.Nil(t, bs.LoadBlockPart(1199, 1)) 441 442 for i := int64(1); i < 1200; i++ { 443 require.Nil(t, bs.LoadBlock(i)) 444 } 445 for i := int64(1200); i <= 1500; i++ { 446 require.NotNil(t, bs.LoadBlock(i)) 447 } 448 449 // Pruning below the current base should error 450 _, err = bs.PruneBlocks(1199) 451 require.Error(t, err) 452 453 // Pruning to the current base should work 454 pruned, err = bs.PruneBlocks(1200) 455 require.NoError(t, err) 456 assert.EqualValues(t, 0, pruned) 457 458 // Pruning again should work 459 pruned, err = bs.PruneBlocks(1300) 460 require.NoError(t, err) 461 assert.EqualValues(t, 100, pruned) 462 assert.EqualValues(t, 1300, bs.Base()) 463 464 // Pruning beyond the current height should error 465 _, err = bs.PruneBlocks(1501) 466 require.Error(t, err) 467 468 // Pruning to the current height should work 469 pruned, err = bs.PruneBlocks(1500) 470 require.NoError(t, err) 471 assert.EqualValues(t, 200, pruned) 472 assert.Nil(t, bs.LoadBlock(1499)) 473 assert.NotNil(t, bs.LoadBlock(1500)) 474 assert.Nil(t, bs.LoadBlock(1501)) 475 } 476 477 func TestDeleteBlocksFromTop(t *testing.T) { 478 config := cfg.ResetTestRoot("blockchain_reactor_test") 479 defer os.RemoveAll(config.RootDir) 480 state, err := sm.LoadStateFromDBOrGenesisFile(dbm.NewMemDB(), config.GenesisFile()) 481 require.NoError(t, err) 482 db := dbm.NewMemDB() 483 bs := NewBlockStore(db) 484 assert.EqualValues(t, 0, bs.Base()) 485 assert.EqualValues(t, 0, bs.Height()) 486 assert.EqualValues(t, 0, bs.Size()) 487 488 // deleting an empty store should error, even when deleting to 0 489 _, err = bs.DeleteBlocksFromTop(1) 490 require.Error(t, err) 491 492 _, err = bs.DeleteBlocksFromTop(0) 493 require.Error(t, err) 494 495 // make more than 1000 blocks, to test batch deletions 496 for h := int64(1); h <= 1500; h++ { 497 block := makeBlock(h, state, new(types.Commit)) 498 partSet := block.MakePartSet(2) 499 seenCommit := makeTestCommit(h, tmtime.Now()) 500 bs.SaveBlock(block, partSet, seenCommit) 501 } 502 503 assert.EqualValues(t, 1, bs.Base()) 504 assert.EqualValues(t, 1500, bs.Height()) 505 assert.EqualValues(t, 1500, bs.Size()) 506 507 deletedBlock := bs.LoadBlock(1201) 508 509 // Check that basic pruning works 510 deleted, err := bs.DeleteBlocksFromTop(1200) 511 require.NoError(t, err) 512 assert.EqualValues(t, 300, deleted) 513 assert.EqualValues(t, 1, bs.Base()) 514 assert.EqualValues(t, 1200, bs.Height()) 515 assert.EqualValues(t, 1200, bs.Size()) 516 assert.EqualValues(t, BlockStoreStateJSON{ 517 Base: 1, 518 Height: 1200, 519 }, LoadBlockStoreStateJSON(db)) 520 521 require.NotNil(t, bs.LoadBlock(1200)) 522 require.Nil(t, bs.LoadBlock(1201)) 523 require.Nil(t, bs.LoadBlockByHash(deletedBlock.Hash())) 524 require.Nil(t, bs.LoadBlockCommit(1201)) 525 require.Nil(t, bs.LoadBlockMeta(1201)) 526 require.Nil(t, bs.LoadBlockPart(1201, 1)) 527 528 for i := int64(1201); i <= 1500; i++ { 529 require.Nil(t, bs.LoadBlock(i)) 530 } 531 for i := int64(1); i <= 1200; i++ { 532 require.NotNil(t, bs.LoadBlock(i)) 533 } 534 535 // Deleting up the current height should error 536 _, err = bs.DeleteBlocksFromTop(1201) 537 require.Error(t, err) 538 539 // Deleting down to the current height should work 540 deleted, err = bs.DeleteBlocksFromTop(1200) 541 require.NoError(t, err) 542 assert.EqualValues(t, 0, deleted) 543 544 // Deleting again should work 545 deleted, err = bs.DeleteBlocksFromTop(1100) 546 require.NoError(t, err) 547 assert.EqualValues(t, 100, deleted) 548 assert.EqualValues(t, 1100, bs.Height()) 549 550 // Deleting beyond the current height should error 551 _, err = bs.DeleteBlocksFromTop(1101) 552 require.Error(t, err) 553 554 // Deleting to the current base should work 555 deleted, err = bs.DeleteBlocksFromTop(1) 556 require.NoError(t, err) 557 assert.EqualValues(t, 1099, deleted) 558 assert.Nil(t, bs.LoadBlock(2)) 559 assert.NotNil(t, bs.LoadBlock(1)) 560 assert.Nil(t, bs.LoadBlock(2)) 561 } 562 563 func TestLoadBlockMeta(t *testing.T) { 564 bs, db := freshBlockStore() 565 height := int64(10) 566 loadMeta := func() (interface{}, error) { 567 meta := bs.LoadBlockMeta(height) 568 return meta, nil 569 } 570 571 // Initially no contents. 572 // 1. Requesting for a non-existent blockMeta shouldn't fail 573 res, _, panicErr := doFn(loadMeta) 574 require.Nil(t, panicErr, "a non-existent blockMeta shouldn't cause a panic") 575 require.Nil(t, res, "a non-existent blockMeta should return nil") 576 577 // 2. Next save a corrupted blockMeta then try to load it 578 err := db.Set(calcBlockMetaKey(height), []byte("Tendermint-Meta")) 579 require.NoError(t, err) 580 res, _, panicErr = doFn(loadMeta) 581 require.NotNil(t, panicErr, "expecting a non-nil panic") 582 require.Contains(t, panicErr.Error(), "unmarshal to types.BlockMeta") 583 584 // 3. A good blockMeta serialized and saved to the DB should be retrievable 585 meta := &types.BlockMeta{} 586 err = db.Set(calcBlockMetaKey(height), cdc.MustMarshalBinaryBare(meta)) 587 require.NoError(t, err) 588 gotMeta, _, panicErr := doFn(loadMeta) 589 require.Nil(t, panicErr, "an existent and proper block should not panic") 590 require.Nil(t, res, "a properly saved blockMeta should return a proper blocMeta ") 591 require.Equal(t, cdc.MustMarshalBinaryBare(meta), cdc.MustMarshalBinaryBare(gotMeta), 592 "expecting successful retrieval of previously saved blockMeta") 593 } 594 595 func TestBlockFetchAtHeight(t *testing.T) { 596 state, bs, cleanup := makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer))) 597 defer cleanup() 598 require.Equal(t, bs.Height(), int64(0), "initially the height should be zero") 599 block := makeBlock(bs.Height()+1, state, new(types.Commit)) 600 601 partSet := block.MakePartSet(2) 602 seenCommit := makeTestCommit(10, tmtime.Now()) 603 bs.SaveBlock(block, partSet, seenCommit) 604 require.Equal(t, bs.Height(), block.Header.Height, "expecting the new height to be changed") 605 606 blockAtHeight := bs.LoadBlock(bs.Height()) 607 bz1 := cdc.MustMarshalBinaryBare(block) 608 bz2 := cdc.MustMarshalBinaryBare(blockAtHeight) 609 require.Equal(t, bz1, bz2) 610 require.Equal(t, block.Hash(), blockAtHeight.Hash(), 611 "expecting a successful load of the last saved block") 612 613 blockAtHeightPlus1 := bs.LoadBlock(bs.Height() + 1) 614 require.Nil(t, blockAtHeightPlus1, "expecting an unsuccessful load of Height()+1") 615 blockAtHeightPlus2 := bs.LoadBlock(bs.Height() + 2) 616 require.Nil(t, blockAtHeightPlus2, "expecting an unsuccessful load of Height()+2") 617 } 618 619 func TestBlockFetchAtHeightWithExInfo(t *testing.T) { 620 types.BlockCompressThreshold = 0 621 state, bs, cleanup := makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer))) 622 defer cleanup() 623 require.Equal(t, bs.Height(), int64(0), "initially the height should be zero") 624 block := makeBlock(bs.Height()+1, state, new(types.Commit)) 625 626 exInfo1 := &types.BlockExInfo{BlockCompressType: 2, BlockCompressFlag: 1, BlockPartSize: 2} 627 partSet := block.MakePartSetByExInfo(exInfo1) 628 seenCommit := makeTestCommit(10, tmtime.Now()) 629 bs.SaveBlock(block, partSet, seenCommit) 630 require.Equal(t, bs.Height(), block.Header.Height, "expecting the new height to be changed") 631 632 blockAtHeight, exInfo2 := bs.LoadBlockWithExInfo(bs.Height()) 633 bz1 := cdc.MustMarshalBinaryBare(block) 634 bz2 := cdc.MustMarshalBinaryBare(blockAtHeight) 635 require.Equal(t, bz1, bz2) 636 require.Equal(t, block.Hash(), blockAtHeight.Hash(), 637 "expecting a successful load of the last saved block") 638 require.EqualValues(t, exInfo1, exInfo2) 639 640 blockAtHeightPlus1, exInfoPlus1 := bs.LoadBlockWithExInfo(bs.Height() + 1) 641 require.Nil(t, blockAtHeightPlus1, "expecting an unsuccessful load of Height()+1") 642 require.Nil(t, exInfoPlus1) 643 blockAtHeightPlus2, exInfoPlus2 := bs.LoadBlockWithExInfo(bs.Height() + 2) 644 require.Nil(t, blockAtHeightPlus2, "expecting an unsuccessful load of Height()+2") 645 require.Nil(t, exInfoPlus2) 646 647 partSet2 := block.MakePartSetByExInfo(exInfo2) 648 require.EqualValues(t, partSet, partSet2) 649 } 650 651 func doFn(fn func() (interface{}, error)) (res interface{}, err error, panicErr error) { 652 defer func() { 653 if r := recover(); r != nil { 654 switch e := r.(type) { 655 case error: 656 panicErr = e 657 case string: 658 panicErr = fmt.Errorf("%s", e) 659 default: 660 if st, ok := r.(fmt.Stringer); ok { 661 panicErr = fmt.Errorf("%s", st) 662 } else { 663 panicErr = fmt.Errorf("%s", debug.Stack()) 664 } 665 } 666 } 667 }() 668 669 res, err = fn() 670 return res, err, panicErr 671 } 672 673 func newBlock(hdr types.Header, lastCommit *types.Commit) *types.Block { 674 return &types.Block{ 675 Header: hdr, 676 LastCommit: lastCommit, 677 } 678 } 679 680 func calcBlockMetaKeyOld(height int64) []byte { 681 return []byte(fmt.Sprintf("H:%v", height)) 682 } 683 684 func calcBlockPartKeyOld(height int64, partIndex int) []byte { 685 return []byte(fmt.Sprintf("P:%v:%v", height, partIndex)) 686 } 687 688 func calcBlockCommitKeyOld(height int64) []byte { 689 return []byte(fmt.Sprintf("C:%v", height)) 690 } 691 692 func calcSeenCommitKeyOld(height int64) []byte { 693 return []byte(fmt.Sprintf("SC:%v", height)) 694 } 695 696 func calcBlockHashKeyOld(hash []byte) []byte { 697 return []byte(fmt.Sprintf("BH:%x", hash)) 698 } 699 700 func TestCalcKey(t *testing.T) { 701 for _, tc := range []int64{ 702 0, 1, -2, math.MaxInt64, math.MinInt64, 12345, -12345, 703 } { 704 require.Equal(t, calcBlockMetaKey(tc), calcBlockMetaKeyOld(tc)) 705 require.Equal(t, calcBlockCommitKey(tc), calcBlockCommitKeyOld(tc)) 706 require.Equal(t, calcSeenCommitKey(tc), calcSeenCommitKeyOld(tc)) 707 } 708 709 for _, tc := range []struct { 710 height int64 711 partIndex int 712 }{ 713 {}, 714 {-1, -1}, 715 { 716 height: 12345, 717 partIndex: 23456, 718 }, 719 { 720 height: math.MaxInt64, 721 partIndex: math.MaxInt, 722 }, 723 { 724 height: math.MinInt64, 725 partIndex: math.MinInt, 726 }, 727 } { 728 require.Equal(t, calcBlockPartKey(tc.height, tc.partIndex), calcBlockPartKeyOld(tc.height, tc.partIndex)) 729 } 730 731 for _, tc := range [][]byte{ 732 nil, 733 []byte{}, 734 make([]byte, 100), 735 make([]byte, 1024), 736 } { 737 _, err := rand.Read(tc) 738 require.NoError(t, err) 739 require.Equal(t, calcBlockHashKey(tc), calcBlockHashKeyOld(tc)) 740 } 741 } 742 743 func BenchmarkCalcKey(b *testing.B) { 744 b.Run("calcBlockMetaKey", func(b *testing.B) { 745 b.ReportAllocs() 746 for i := 0; i < b.N; i++ { 747 calcBlockMetaKey(int64(i)) 748 } 749 }) 750 b.Run("calcBlockMetaKeyOld", func(b *testing.B) { 751 b.ReportAllocs() 752 for i := 0; i < b.N; i++ { 753 calcBlockMetaKeyOld(int64(i)) 754 } 755 }) 756 }