Skip to content

Commit

Permalink
Implement BlockChainInfo RPC method (cosmos#282)
Browse files Browse the repository at this point in the history
  • Loading branch information
Raneet10 authored Feb 15, 2022
1 parent 1a42c63 commit e2929b6
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG-PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Month, DD, YYYY
- [rpc] [Implement Tx Method #272](https://github.com/celestiaorg/optimint/pull/272) [@mauriceLC92](https://github.com/mauriceLC92)
- [rpc] [Implement Commit and BlockSearch method #258](https://github.com/celestiaorg/optimint/pull/258) [@raneet10](https://github.com/Raneet10/)
- [rpc] [Remove extra variable #280](https://github.com/celestiaorg/optimint/pull/280) [@raneet10](https://github.com/Raneet10/)
- [rpc] [Implement BlockChainInfo RPC method #282](https://github.com/celestiaorg/optimint/pull/282) [@raneet10](https://github.com/Raneet10/)

### BUG FIXES

Expand Down
16 changes: 16 additions & 0 deletions conv/abci/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,22 @@ func ToABCIBlock(block *types.Block) (*tmtypes.Block, error) {
return &abciBlock, nil
}

// ToABCIBlockMeta converts Optimint block into BlockMeta format defined by ABCI
func ToABCIBlockMeta(block *types.Block) (*tmtypes.BlockMeta, error) {
tmblock, err := ToABCIBlock(block)
if err != nil {
return nil, err
}
blockId := tmtypes.BlockID{Hash: tmblock.Hash()}

return &tmtypes.BlockMeta{
BlockID: blockId,
BlockSize: tmblock.Size(),
Header: tmblock.Header,
NumTxs: len(tmblock.Txs),
}, nil
}

// ToABCICommit converts Optimint commit into commit format defined by ABCI.
// This function only converts fields that are available in Optimint commit.
// Other fields (especially ValidatorAddress and Timestamp of Signature) has to be filled by caller.
Expand Down
67 changes: 65 additions & 2 deletions rpc/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,40 @@ func (c *Client) GenesisChunked(context context.Context, id uint) (*ctypes.Resul
}

func (c *Client) BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) {
// needs block store
panic("BlockchainInfo - not implemented!")
const limit int64 = 20

// Currently blocks are not pruned and are synced linearly so the base height is 0
minHeight, maxHeight, err := filterMinMax(
0,
int64(c.node.Store.Height()),
minHeight,
maxHeight,
limit)
if err != nil {
return nil, err
}
c.Logger.Debug("BlockchainInfo", "maxHeight", maxHeight, "minHeight", minHeight)

blocks := make([]*types.BlockMeta, 0, maxHeight-minHeight+1)
for height := maxHeight; height >= minHeight; height-- {
block, err := c.node.Store.LoadBlock(uint64(height))
if err != nil {
return nil, err
}
if block != nil {
tmblockmeta, err := abciconv.ToABCIBlockMeta(block)
if err != nil {
return nil, err
}
blocks = append(blocks, tmblockmeta)
}
}

return &ctypes.ResultBlockchainInfo{
LastHeight: int64(c.node.Store.Height()),
BlockMetas: blocks,
}, nil

}

func (c *Client) NetInfo(ctx context.Context) (*ctypes.ResultNetInfo, error) {
Expand Down Expand Up @@ -749,3 +781,34 @@ func validateSkipCount(page, perPage int) int {

return skipCount
}

func filterMinMax(base, height, min, max, limit int64) (int64, int64, error) {
// filter negatives
if min < 0 || max < 0 {
return min, max, errors.New("height must be greater than zero")
}

// adjust for default values
if min == 0 {
min = 1
}
if max == 0 {
max = height
}

// limit max to the height
max = tmmath.MinInt64(height, max)

// limit min to the base
min = tmmath.MaxInt64(base, min)

// limit min to within `limit` of max
// so the total number of blocks returned will be `limit`
min = tmmath.MaxInt64(min, max-limit+1)

if min > max {
return min, max, fmt.Errorf("%w: min height %d can't be greater than max height %d",
errors.New("invalid request"), min, max)
}
return min, max, nil
}
87 changes: 87 additions & 0 deletions rpc/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
tmtypes "github.com/tendermint/tendermint/types"

"github.com/celestiaorg/optimint/config"
abciconv "github.com/celestiaorg/optimint/conv/abci"
"github.com/celestiaorg/optimint/mocks"
"github.com/celestiaorg/optimint/node"
"github.com/celestiaorg/optimint/state"
Expand Down Expand Up @@ -498,6 +499,79 @@ func TestConsensusState(t *testing.T) {
assert.ErrorIs(err, ErrConsensusStateNotAvailable)
}

func TestBlockchainInfo(t *testing.T) {
require := require.New(t)
assert := assert.New(t)
mockApp, rpc := getRPC(t)
mockApp.On("BeginBlock", mock.Anything).Return(abci.ResponseBeginBlock{})
mockApp.On("Commit", mock.Anything).Return(abci.ResponseCommit{})

heights := []int64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
for _, h := range heights {
block := getRandomBlock(uint64(h), 5)
err := rpc.node.Store.SaveBlock(block, &types.Commit{
Height: uint64(h),
HeaderHash: block.Header.Hash(),
})
require.NoError(err)
}

tests := []struct {
desc string
min int64
max int64
exp []*tmtypes.BlockMeta
err bool
}{
{
desc: "min = 1 and max = 5",
min: 1,
max: 5,
exp: []*tmtypes.BlockMeta{getBlockMeta(rpc, 1), getBlockMeta(rpc, 5)},
err: false,
}, {
desc: "min height is 0",
min: 0,
max: 10,
exp: []*tmtypes.BlockMeta{getBlockMeta(rpc, 1), getBlockMeta(rpc, 10)},
err: false,
}, {
desc: "max height is out of range",
min: 0,
max: 15,
exp: []*tmtypes.BlockMeta{getBlockMeta(rpc, 1), getBlockMeta(rpc, 10)},
err: false,
}, {
desc: "negative min height",
min: -1,
max: 11,
exp: nil,
err: true,
}, {
desc: "negative max height",
min: 1,
max: -1,
exp: nil,
err: true,
},
}

for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
result, err := rpc.BlockchainInfo(context.Background(), test.min, test.max)
if test.err {
require.Error(err)
} else {
require.NoError(err)
assert.Equal(result.LastHeight, heights[9])
assert.Contains(result.BlockMetas, test.exp[0])
assert.Contains(result.BlockMetas, test.exp[1])
}

})
}
}

// copy-pasted from store/store_test.go
func getRandomBlock(height uint64, nTxs int) *types.Block {
block := &types.Block{
Expand Down Expand Up @@ -546,6 +620,19 @@ func getRandomBytes(n int) []byte {
return data
}

func getBlockMeta(rpc *Client, n int64) *tmtypes.BlockMeta {
b, err := rpc.node.Store.LoadBlock(uint64(n))
if err != nil {
return nil
}
bmeta, err := abciconv.ToABCIBlockMeta(b)
if err != nil {
return nil
}

return bmeta
}

func getRPC(t *testing.T) (*mocks.Application, *Client) {
t.Helper()
require := require.New(t)
Expand Down

0 comments on commit e2929b6

Please sign in to comment.