github.com/line/ostracon@v1.0.10-0.20230328032236-7f20145f065d/rpc/core/blocks_test.go (about)

     1  package core
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	abci "github.com/tendermint/tendermint/abci/types"
    14  	dbm "github.com/tendermint/tm-db"
    15  
    16  	cfg "github.com/line/ostracon/config"
    17  	"github.com/line/ostracon/crypto"
    18  	tmrand "github.com/line/ostracon/libs/rand"
    19  	tmstate "github.com/line/ostracon/proto/ostracon/state"
    20  	ctypes "github.com/line/ostracon/rpc/core/types"
    21  	rpctypes "github.com/line/ostracon/rpc/jsonrpc/types"
    22  	sm "github.com/line/ostracon/state"
    23  	blockidxkv "github.com/line/ostracon/state/indexer/block/kv"
    24  	blockidxnull "github.com/line/ostracon/state/indexer/block/null"
    25  	txidxkv "github.com/line/ostracon/state/txindex/kv"
    26  	"github.com/line/ostracon/store"
    27  	"github.com/line/ostracon/types"
    28  )
    29  
    30  func TestBlockchainInfo(t *testing.T) {
    31  	cases := []struct {
    32  		min, max     int64
    33  		base, height int64
    34  		limit        int64
    35  		resultLength int64
    36  		wantErr      bool
    37  	}{
    38  
    39  		// min > max
    40  		{0, 0, 0, 0, 10, 0, true},  // min set to 1
    41  		{0, 1, 0, 0, 10, 0, true},  // max set to height (0)
    42  		{0, 0, 0, 1, 10, 1, false}, // max set to height (1)
    43  		{2, 0, 0, 1, 10, 0, true},  // max set to height (1)
    44  		{2, 1, 0, 5, 10, 0, true},
    45  
    46  		// negative
    47  		{1, 10, 0, 14, 10, 10, false}, // control
    48  		{-1, 10, 0, 14, 10, 0, true},
    49  		{1, -10, 0, 14, 10, 0, true},
    50  		{-9223372036854775808, -9223372036854775788, 0, 100, 20, 0, true},
    51  
    52  		// check base
    53  		{1, 1, 1, 1, 1, 1, false},
    54  		{2, 5, 3, 5, 5, 3, false},
    55  
    56  		// check limit and height
    57  		{1, 1, 0, 1, 10, 1, false},
    58  		{1, 1, 0, 5, 10, 1, false},
    59  		{2, 2, 0, 5, 10, 1, false},
    60  		{1, 2, 0, 5, 10, 2, false},
    61  		{1, 5, 0, 1, 10, 1, false},
    62  		{1, 5, 0, 10, 10, 5, false},
    63  		{1, 15, 0, 10, 10, 10, false},
    64  		{1, 15, 0, 15, 10, 10, false},
    65  		{1, 15, 0, 15, 20, 15, false},
    66  		{1, 20, 0, 15, 20, 15, false},
    67  		{1, 20, 0, 20, 20, 20, false},
    68  	}
    69  
    70  	for i, c := range cases {
    71  		caseString := fmt.Sprintf("test %d failed", i)
    72  		min, max, err := filterMinMax(c.base, c.height, c.min, c.max, c.limit)
    73  		if c.wantErr {
    74  			require.Error(t, err, caseString)
    75  		} else {
    76  			require.NoError(t, err, caseString)
    77  			require.Equal(t, 1+max-min, c.resultLength, caseString)
    78  		}
    79  	}
    80  }
    81  
    82  func TestBlockResults(t *testing.T) {
    83  	results := &tmstate.ABCIResponses{
    84  		DeliverTxs: []*abci.ResponseDeliverTx{
    85  			{Code: 0, Data: []byte{0x01}, Log: "ok"},
    86  			{Code: 0, Data: []byte{0x02}, Log: "ok"},
    87  			{Code: 1, Log: "not ok"},
    88  		},
    89  		EndBlock:   &abci.ResponseEndBlock{},
    90  		BeginBlock: &abci.ResponseBeginBlock{},
    91  	}
    92  
    93  	env = &Environment{}
    94  	env.StateStore = sm.NewStore(dbm.NewMemDB())
    95  	err := env.StateStore.SaveABCIResponses(100, results)
    96  	require.NoError(t, err)
    97  	env.BlockStore = mockBlockStore{height: 100}
    98  
    99  	testCases := []struct {
   100  		height  int64
   101  		wantErr bool
   102  		wantRes *ctypes.ResultBlockResults
   103  	}{
   104  		{-1, true, nil},
   105  		{0, true, nil},
   106  		{101, true, nil},
   107  		{100, false, &ctypes.ResultBlockResults{
   108  			Height:                100,
   109  			TxsResults:            results.DeliverTxs,
   110  			BeginBlockEvents:      results.BeginBlock.Events,
   111  			EndBlockEvents:        results.EndBlock.Events,
   112  			ValidatorUpdates:      results.EndBlock.ValidatorUpdates,
   113  			ConsensusParamUpdates: results.EndBlock.ConsensusParamUpdates,
   114  		}},
   115  	}
   116  
   117  	for _, tc := range testCases {
   118  		res, err := BlockResults(&rpctypes.Context{}, &tc.height)
   119  		if tc.wantErr {
   120  			assert.Error(t, err)
   121  		} else {
   122  			assert.NoError(t, err)
   123  			assert.Equal(t, tc.wantRes, res)
   124  		}
   125  	}
   126  }
   127  
   128  func TestBlockSearchByBlockHeightQuery(t *testing.T) {
   129  	height := int64(1)
   130  	ctx := &rpctypes.Context{}
   131  
   132  	q := fmt.Sprintf("%s=%d", types.BlockHeightKey, height)
   133  	page := 1
   134  	perPage := 10
   135  	orderBy := TestOrderByDefault
   136  
   137  	state, cleanup := makeTestState()
   138  	defer cleanup()
   139  
   140  	{
   141  		// Get by block.height (not search/range)
   142  		res, err := BlockSearch(ctx, q, &page, &perPage, orderBy)
   143  
   144  		require.NoError(t, err)
   145  		require.NotNil(t, res)
   146  		require.Equal(t, 0, res.TotalCount) // Don't have height in db
   147  		require.Equal(t, 0, len(res.Blocks))
   148  	}
   149  
   150  	numToMakeBlocks := 1
   151  	numToGet := 1
   152  	// Save blocks
   153  	storeTestBlocks(height, int64(numToMakeBlocks), 0, state, time.Now())
   154  
   155  	{
   156  		// Get by block.height (not search/range)
   157  		res, err := BlockSearch(ctx, q, &page, &perPage, orderBy)
   158  
   159  		require.NoError(t, err)
   160  		require.NotNil(t, res)
   161  		require.Equal(t, numToMakeBlocks, res.TotalCount) // Get
   162  		require.Equal(t, numToGet, len(res.Blocks))
   163  	}
   164  }
   165  
   166  func TestBlockSearchByRangeQuery(t *testing.T) {
   167  	height := int64(1)
   168  	ctx := &rpctypes.Context{}
   169  
   170  	q := fmt.Sprintf("%s>=%d", types.BlockHeightKey, height)
   171  	page := 1
   172  	perPage := 10
   173  	orderBy := TestOrderByDefault
   174  
   175  	state, cleanup := makeTestState()
   176  	defer cleanup()
   177  
   178  	numToMakeBlocks := 15
   179  	numToGet := perPage
   180  	// Save blocks
   181  	storeTestBlocks(height, int64(numToMakeBlocks), 0, state, time.Now())
   182  
   183  	{
   184  		// Search blocks by range query with desc (default)
   185  		res, err := BlockSearch(ctx, q, &page, &perPage, orderBy)
   186  
   187  		require.NoError(t, err)
   188  		require.NotNil(t, res)
   189  		require.Equal(t, numToMakeBlocks, res.TotalCount)
   190  		require.Equal(t, numToGet, len(res.Blocks))
   191  		require.Equal(t, int64(numToMakeBlocks), res.Blocks[0].Block.Height)
   192  		require.Equal(t, height+int64(numToMakeBlocks-numToGet), res.Blocks[numToGet-1].Block.Height)
   193  	}
   194  	{
   195  		orderBy = TestOrderByAsc
   196  		// Search blocks by range query with asc
   197  		res, err := BlockSearch(ctx, q, &page, &perPage, orderBy)
   198  
   199  		require.NoError(t, err)
   200  		require.NotNil(t, res)
   201  		require.Equal(t, numToMakeBlocks, res.TotalCount)
   202  		require.Equal(t, numToGet, len(res.Blocks))
   203  		require.Equal(t, height, res.Blocks[0].Block.Height)
   204  		require.Equal(t, int64(numToGet), res.Blocks[numToGet-1].Block.Height)
   205  	}
   206  }
   207  
   208  func TestBlockSearch_errors(t *testing.T) {
   209  	ctx := &rpctypes.Context{}
   210  
   211  	q := ""
   212  	page := 0
   213  	perPage := 1
   214  	orderBy := "error"
   215  
   216  	{
   217  		// error: env.BlockIndexer.(*blockidxnull.BlockerIndexer)
   218  		env = &Environment{}
   219  		env.BlockIndexer = &blockidxnull.BlockerIndexer{}
   220  
   221  		res, err := BlockSearch(ctx, q, &page, &perPage, orderBy)
   222  
   223  		require.Error(t, err)
   224  		require.Equal(t, errors.New("block indexing is disabled"), err)
   225  		require.Nil(t, res)
   226  	}
   227  	{
   228  		// error: tmquery.New(query)
   229  		env = &Environment{}
   230  
   231  		res, err := BlockSearch(ctx, q, &page, &perPage, orderBy)
   232  
   233  		require.Error(t, err)
   234  		require.Equal(t,
   235  			"\nparse error near Unknown (line 1 symbol 1 - line 1 symbol 1):\n\"\"\n",
   236  			err.Error())
   237  		require.Nil(t, res)
   238  	}
   239  	{
   240  		// error: switch orderBy
   241  		env = &Environment{}
   242  		env.BlockIndexer = blockidxkv.New(dbm.NewMemDB())
   243  		q = fmt.Sprintf("%s>%d", types.BlockHeightKey, 1)
   244  
   245  		res, err := BlockSearch(ctx, q, &page, &perPage, orderBy)
   246  
   247  		require.Error(t, err)
   248  		require.Equal(t,
   249  			"expected order_by to be either `asc` or `desc` or empty",
   250  			err.Error())
   251  		require.Nil(t, res)
   252  	}
   253  	{
   254  		// error: validatePage(pagePtr, perPage, totalCount)
   255  		env = &Environment{}
   256  		env.BlockIndexer = blockidxkv.New(dbm.NewMemDB())
   257  		q = fmt.Sprintf("%s>%d", types.BlockHeightKey, 1)
   258  		orderBy = TestOrderByDesc
   259  
   260  		res, err := BlockSearch(ctx, q, &page, &perPage, orderBy)
   261  
   262  		require.Error(t, err)
   263  		require.Equal(t,
   264  			"page should be within [1, 1] range, given 0",
   265  			err.Error())
   266  		require.Nil(t, res)
   267  	}
   268  }
   269  
   270  func makeTestState() (sm.State, func()) {
   271  	config := cfg.ResetTestRoot("rpc_core_test")
   272  	env = &Environment{}
   273  	env.StateStore = sm.NewStore(dbm.NewMemDB())
   274  	env.BlockStore = store.NewBlockStore(dbm.NewMemDB())
   275  	env.BlockIndexer = blockidxkv.New(dbm.NewMemDB())
   276  	env.TxIndexer = txidxkv.NewTxIndex(dbm.NewMemDB())
   277  
   278  	state, _ := env.StateStore.LoadFromDBOrGenesisFile(config.GenesisFile())
   279  	return state, func() { os.RemoveAll(config.RootDir) }
   280  }
   281  
   282  func storeTestBlocks(startHeight, numToMakeBlocks, numToMakeTxs int64, state sm.State, timestamp time.Time) {
   283  	for i := int64(0); i < numToMakeBlocks; i++ {
   284  		commitSigs := []types.CommitSig{{
   285  			BlockIDFlag:      types.BlockIDFlagCommit,
   286  			ValidatorAddress: tmrand.Bytes(crypto.AddressSize),
   287  			Timestamp:        timestamp,
   288  			Signature:        []byte("Signature"),
   289  		}}
   290  		height := startHeight + i
   291  		lastHeight := startHeight - 1
   292  		round := int32(0)
   293  		hash := []byte("")
   294  		partSize := uint32(2)
   295  		blockID := types.BlockID{Hash: hash, PartSetHeader: types.PartSetHeader{Hash: hash, Total: partSize}}
   296  		proposer := state.Validators.SelectProposer(state.LastProofHash, startHeight, round)
   297  		txs := make([]types.Tx, numToMakeTxs)
   298  		for txIndex := int64(0); txIndex < numToMakeTxs; txIndex++ {
   299  			tx := []byte{byte(height), byte(txIndex)}
   300  			txs[txIndex] = tx
   301  			// Indexing
   302  			env.TxIndexer.Index(&abci.TxResult{Height: height, Index: uint32(txIndex), Tx: tx}) // nolint:errcheck
   303  		}
   304  		lastCommit := types.NewCommit(lastHeight, round, blockID, commitSigs)
   305  		block, _ := state.MakeBlock(height, txs, lastCommit, nil, proposer.Address, round, nil)
   306  		blockPart := block.MakePartSet(partSize)
   307  		// Indexing
   308  		env.BlockIndexer.Index(types.EventDataNewBlockHeader{Header: block.Header}) // nolint:errcheck
   309  		// Save
   310  		env.BlockStore.SaveBlock(block, blockPart, lastCommit)
   311  	}
   312  }
   313  
   314  const (
   315  	TestOrderByDefault = ""
   316  	TestOrderByDesc    = "desc"
   317  	TestOrderByAsc     = "asc"
   318  )
   319  
   320  type mockBlockStore struct {
   321  	height int64
   322  }
   323  
   324  func (mockBlockStore) Base() int64                                       { return 1 }
   325  func (store mockBlockStore) Height() int64                               { return store.height }
   326  func (store mockBlockStore) Size() int64                                 { return store.height }
   327  func (mockBlockStore) LoadBaseMeta() *types.BlockMeta                    { return nil }
   328  func (mockBlockStore) LoadBlockMeta(height int64) *types.BlockMeta       { return nil }
   329  func (mockBlockStore) LoadBlock(height int64) *types.Block               { return nil }
   330  func (mockBlockStore) LoadBlockByHash(hash []byte) *types.Block          { return nil }
   331  func (mockBlockStore) LoadBlockPart(height int64, index int) *types.Part { return nil }
   332  func (mockBlockStore) LoadBlockCommit(height int64) *types.Commit        { return nil }
   333  func (mockBlockStore) LoadSeenCommit(height int64) *types.Commit         { return nil }
   334  func (mockBlockStore) PruneBlocks(height int64) (uint64, error)          { return 0, nil }
   335  func (mockBlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) {
   336  }