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 }