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