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