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 }