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  }