Browse Source

rpc: implement header and header_by_hash queries (backport #7270) (#7367)

pull/7374/head
mergify[bot] 2 years ago
committed by GitHub
parent
commit
b4396d79f2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1064 additions and 32 deletions
  1. +2
    -0
      CHANGELOG_PENDING.md
  2. +1
    -0
      internal/consensus/replay_test.go
  3. +34
    -1
      internal/rpc/core/blocks.go
  4. +5
    -20
      internal/rpc/core/blocks_test.go
  5. +2
    -0
      internal/rpc/core/routes.go
  6. +16
    -0
      internal/state/mocks/block_store.go
  7. +1
    -0
      internal/state/services.go
  8. +20
    -0
      internal/store/store.go
  9. +18
    -0
      light/proxy/routes.go
  10. +36
    -1
      light/rpc/client.go
  11. +30
    -3
      rpc/client/http/http.go
  12. +2
    -0
      rpc/client/interface.go
  13. +8
    -0
      rpc/client/local/local.go
  14. +802
    -0
      rpc/client/mocks/client.go
  15. +9
    -0
      rpc/client/rpc_test.go
  16. +5
    -0
      rpc/coretypes/responses.go
  17. +73
    -7
      rpc/openapi/openapi.yaml

+ 2
- 0
CHANGELOG_PENDING.md View File

@ -26,6 +26,8 @@ Special thanks to external contributors on this release:
### FEATURES
- [rpc] [\#7270](https://github.com/tendermint/tendermint/pull/7270) Add `header` and `header_by_hash` RPC Client queries. (@fedekunze) (@cmwaters)
### IMPROVEMENTS
- [\#7338](https://github.com/tendermint/tendermint/pull/7338) pubsub: Performance improvements for the event query API (backport of #7319) (@creachadair)


+ 1
- 0
internal/consensus/replay_test.go View File

@ -1195,6 +1195,7 @@ func (bs *mockBlockStore) LoadBlock(height int64) *types.Block { return bs.chain
func (bs *mockBlockStore) LoadBlockByHash(hash []byte) *types.Block {
return bs.chain[int64(len(bs.chain))-1]
}
func (bs *mockBlockStore) LoadBlockMetaByHash(hash []byte) *types.BlockMeta { return nil }
func (bs *mockBlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
block := bs.chain[height-1]
return &types.BlockMeta{


+ 34
- 1
internal/rpc/core/blocks.go View File

@ -51,7 +51,8 @@ func (env *Environment) BlockchainInfo(
return &coretypes.ResultBlockchainInfo{
LastHeight: env.BlockStore.Height(),
BlockMetas: blockMetas}, nil
BlockMetas: blockMetas,
}, nil
}
// error if either min or max are negative or min > max
@ -122,6 +123,38 @@ func (env *Environment) BlockByHash(ctx *rpctypes.Context, hash bytes.HexBytes)
return &coretypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil
}
// Header gets block header at a given height.
// If no height is provided, it will fetch the latest header.
// More: https://docs.tendermint.com/master/rpc/#/Info/header
func (env *Environment) Header(ctx *rpctypes.Context, heightPtr *int64) (*coretypes.ResultHeader, error) {
height, err := env.getHeight(env.BlockStore.Height(), heightPtr)
if err != nil {
return nil, err
}
blockMeta := env.BlockStore.LoadBlockMeta(height)
if blockMeta == nil {
return &coretypes.ResultHeader{}, nil
}
return &coretypes.ResultHeader{Header: &blockMeta.Header}, nil
}
// HeaderByHash gets header by hash.
// More: https://docs.tendermint.com/master/rpc/#/Info/header_by_hash
func (env *Environment) HeaderByHash(ctx *rpctypes.Context, hash bytes.HexBytes) (*coretypes.ResultHeader, error) {
// N.B. The hash parameter is HexBytes so that the reflective parameter
// decoding logic in the HTTP service will correctly translate from JSON.
// See https://github.com/tendermint/tendermint/issues/6802 for context.
blockMeta := env.BlockStore.LoadBlockMetaByHash(hash)
if blockMeta == nil {
return &coretypes.ResultHeader{}, nil
}
return &coretypes.ResultHeader{Header: &blockMeta.Header}, nil
}
// Commit gets block commit at a given height.
// If no height is provided, it will fetch the commit for the latest block.
// More: https://docs.tendermint.com/master/rpc/#/Info/commit


+ 5
- 20
internal/rpc/core/blocks_test.go View File

@ -11,10 +11,10 @@ import (
abci "github.com/tendermint/tendermint/abci/types"
sm "github.com/tendermint/tendermint/internal/state"
"github.com/tendermint/tendermint/internal/state/mocks"
tmstate "github.com/tendermint/tendermint/proto/tendermint/state"
"github.com/tendermint/tendermint/rpc/coretypes"
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
"github.com/tendermint/tendermint/types"
)
func TestBlockchainInfo(t *testing.T) {
@ -84,7 +84,10 @@ func TestBlockResults(t *testing.T) {
env.StateStore = sm.NewStore(dbm.NewMemDB())
err := env.StateStore.SaveABCIResponses(100, results)
require.NoError(t, err)
env.BlockStore = mockBlockStore{height: 100}
mockstore := &mocks.BlockStore{}
mockstore.On("Height").Return(int64(100))
mockstore.On("Base").Return(int64(1))
env.BlockStore = mockstore
testCases := []struct {
height int64
@ -115,21 +118,3 @@ func TestBlockResults(t *testing.T) {
}
}
}
type mockBlockStore struct {
height int64
}
func (mockBlockStore) Base() int64 { return 1 }
func (store mockBlockStore) Height() int64 { return store.height }
func (store mockBlockStore) Size() int64 { return store.height }
func (mockBlockStore) LoadBaseMeta() *types.BlockMeta { return nil }
func (mockBlockStore) LoadBlockMeta(height int64) *types.BlockMeta { return nil }
func (mockBlockStore) LoadBlock(height int64) *types.Block { return nil }
func (mockBlockStore) LoadBlockByHash(hash []byte) *types.Block { return nil }
func (mockBlockStore) LoadBlockPart(height int64, index int) *types.Part { return nil }
func (mockBlockStore) LoadBlockCommit(height int64) *types.Commit { return nil }
func (mockBlockStore) LoadSeenCommit() *types.Commit { return nil }
func (mockBlockStore) PruneBlocks(height int64) (uint64, error) { return 0, nil }
func (mockBlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) {
}

+ 2
- 0
internal/rpc/core/routes.go View File

@ -23,6 +23,8 @@ func (env *Environment) GetRoutes() RoutesMap {
"blockchain": rpc.NewRPCFunc(env.BlockchainInfo, "minHeight,maxHeight", true),
"genesis": rpc.NewRPCFunc(env.Genesis, "", true),
"genesis_chunked": rpc.NewRPCFunc(env.GenesisChunked, "chunk", true),
"header": rpc.NewRPCFunc(env.Header, "height", true),
"header_by_hash": rpc.NewRPCFunc(env.HeaderByHash, "hash", true),
"block": rpc.NewRPCFunc(env.Block, "height", true),
"block_by_hash": rpc.NewRPCFunc(env.BlockByHash, "hash", true),
"block_results": rpc.NewRPCFunc(env.BlockResults, "height", true),


+ 16
- 0
internal/state/mocks/block_store.go View File

@ -121,6 +121,22 @@ func (_m *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
return r0
}
// LoadBlockMetaByHash provides a mock function with given fields: hash
func (_m *BlockStore) LoadBlockMetaByHash(hash []byte) *types.BlockMeta {
ret := _m.Called(hash)
var r0 *types.BlockMeta
if rf, ok := ret.Get(0).(func([]byte) *types.BlockMeta); ok {
r0 = rf(hash)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.BlockMeta)
}
}
return r0
}
// LoadBlockPart provides a mock function with given fields: height, index
func (_m *BlockStore) LoadBlockPart(height int64, index int) *types.Part {
ret := _m.Called(height, index)


+ 1
- 0
internal/state/services.go View File

@ -29,6 +29,7 @@ type BlockStore interface {
PruneBlocks(height int64) (uint64, error)
LoadBlockByHash(hash []byte) *types.Block
LoadBlockMetaByHash(hash []byte) *types.BlockMeta
LoadBlockPart(height int64, index int) *types.Part
LoadBlockCommit(height int64) *types.Commit


+ 20
- 0
internal/store/store.go View File

@ -181,6 +181,26 @@ func (bs *BlockStore) LoadBlockByHash(hash []byte) *types.Block {
return bs.LoadBlock(height)
}
// LoadBlockMetaByHash returns the blockmeta who's header corresponds to the given
// hash. If none is found, returns nil.
func (bs *BlockStore) LoadBlockMetaByHash(hash []byte) *types.BlockMeta {
bz, err := bs.db.Get(blockHashKey(hash))
if err != nil {
panic(err)
}
if len(bz) == 0 {
return nil
}
s := string(bz)
height, err := strconv.ParseInt(s, 10, 64)
if err != nil {
panic(fmt.Sprintf("failed to extract height from %s: %v", s, err))
}
return bs.LoadBlockMeta(height)
}
// LoadBlockPart returns the Part at the given index
// from the block at the given height.
// If no part is found for the given height and index, it returns nil.


+ 18
- 0
light/proxy/routes.go View File

@ -24,6 +24,8 @@ func RPCRoutes(c *lrpc.Client) map[string]*rpcserver.RPCFunc {
"blockchain": rpcserver.NewRPCFunc(makeBlockchainInfoFunc(c), "minHeight,maxHeight", true),
"genesis": rpcserver.NewRPCFunc(makeGenesisFunc(c), "", true),
"genesis_chunked": rpcserver.NewRPCFunc(makeGenesisChunkedFunc(c), "", true),
"header": rpcserver.NewRPCFunc(makeHeaderFunc(c), "height", true),
"header_by_hash": rpcserver.NewRPCFunc(makeHeaderByHashFunc(c), "hash", true),
"block": rpcserver.NewRPCFunc(makeBlockFunc(c), "height", true),
"block_by_hash": rpcserver.NewRPCFunc(makeBlockByHashFunc(c), "hash", true),
"block_results": rpcserver.NewRPCFunc(makeBlockResultsFunc(c), "height", true),
@ -101,6 +103,22 @@ func makeGenesisChunkedFunc(c *lrpc.Client) rpcGenesisChunkedFunc {
}
}
type rpcHeaderFunc func(ctx *rpctypes.Context, height *int64) (*coretypes.ResultHeader, error)
func makeHeaderFunc(c *lrpc.Client) rpcHeaderFunc {
return func(ctx *rpctypes.Context, height *int64) (*coretypes.ResultHeader, error) {
return c.Header(ctx.Context(), height)
}
}
type rpcHeaderByHashFunc func(ctx *rpctypes.Context, hash []byte) (*coretypes.ResultHeader, error)
func makeHeaderByHashFunc(c *lrpc.Client) rpcHeaderByHashFunc {
return func(ctx *rpctypes.Context, hash []byte) (*coretypes.ResultHeader, error) {
return c.HeaderByHash(ctx.Context(), hash)
}
}
type rpcBlockFunc func(ctx *rpctypes.Context, height *int64) (*coretypes.ResultBlock, error)
func makeBlockFunc(c *lrpc.Client) rpcBlockFunc {


+ 36
- 1
light/rpc/client.go View File

@ -442,6 +442,40 @@ func (c *Client) BlockResults(ctx context.Context, height *int64) (*coretypes.Re
return res, nil
}
// Header fetches and verifies the header directly via the light client
func (c *Client) Header(ctx context.Context, height *int64) (*coretypes.ResultHeader, error) {
lb, err := c.updateLightClientIfNeededTo(ctx, height)
if err != nil {
return nil, err
}
return &coretypes.ResultHeader{Header: lb.Header}, nil
}
// HeaderByHash calls rpcclient#HeaderByHash and updates the client if it's falling behind.
func (c *Client) HeaderByHash(ctx context.Context, hash tmbytes.HexBytes) (*coretypes.ResultHeader, error) {
res, err := c.next.HeaderByHash(ctx, hash)
if err != nil {
return nil, err
}
if err := res.Header.ValidateBasic(); err != nil {
return nil, err
}
lb, err := c.updateLightClientIfNeededTo(ctx, &res.Header.Height)
if err != nil {
return nil, err
}
if !bytes.Equal(lb.Header.Hash(), res.Header.Hash()) {
return nil, fmt.Errorf("primary header hash does not match trusted header hash. (%X != %X)",
lb.Header.Hash(), res.Header.Hash())
}
return res, nil
}
func (c *Client) Commit(ctx context.Context, height *int64) (*coretypes.ResultCommit, error) {
// Update the light client if we're behind and retrieve the light block at the requested height
// or at the latest height if no height is provided.
@ -526,7 +560,8 @@ func (c *Client) Validators(
BlockHeight: l.Height,
Validators: v,
Count: len(v),
Total: totalCount}, nil
Total: totalCount,
}, nil
}
func (c *Client) BroadcastEvidence(ctx context.Context, ev types.Evidence) (*coretypes.ResultBroadcastEvidence, error) {


+ 30
- 3
rpc/client/http/http.go View File

@ -91,9 +91,11 @@ type baseRPCClient struct {
caller jsonrpcclient.Caller
}
var _ rpcClient = (*HTTP)(nil)
var _ rpcClient = (*BatchHTTP)(nil)
var _ rpcClient = (*baseRPCClient)(nil)
var (
_ rpcClient = (*HTTP)(nil)
_ rpcClient = (*BatchHTTP)(nil)
_ rpcClient = (*baseRPCClient)(nil)
)
//-----------------------------------------------------------------------------
// HTTP
@ -449,6 +451,31 @@ func (c *baseRPCClient) BlockResults(
return result, nil
}
func (c *baseRPCClient) Header(ctx context.Context, height *int64) (*coretypes.ResultHeader, error) {
result := new(coretypes.ResultHeader)
params := make(map[string]interface{})
if height != nil {
params["height"] = height
}
_, err := c.caller.Call(ctx, "header", params, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) HeaderByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultHeader, error) {
result := new(coretypes.ResultHeader)
params := map[string]interface{}{
"hash": hash,
}
_, err := c.caller.Call(ctx, "header_by_hash", params, result)
if err != nil {
return nil, err
}
return result, nil
}
func (c *baseRPCClient) Commit(ctx context.Context, height *int64) (*coretypes.ResultCommit, error) {
result := new(coretypes.ResultCommit)
params := make(map[string]interface{})


+ 2
- 0
rpc/client/interface.go View File

@ -78,6 +78,8 @@ type SignClient interface {
Block(ctx context.Context, height *int64) (*coretypes.ResultBlock, error)
BlockByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultBlock, error)
BlockResults(ctx context.Context, height *int64) (*coretypes.ResultBlockResults, error)
Header(ctx context.Context, height *int64) (*coretypes.ResultHeader, error)
HeaderByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultHeader, error)
Commit(ctx context.Context, height *int64) (*coretypes.ResultCommit, error)
Validators(ctx context.Context, height *int64, page, perPage *int) (*coretypes.ResultValidators, error)
Tx(ctx context.Context, hash bytes.HexBytes, prove bool) (*coretypes.ResultTx, error)


+ 8
- 0
rpc/client/local/local.go View File

@ -178,6 +178,14 @@ func (c *Local) BlockResults(ctx context.Context, height *int64) (*coretypes.Res
return c.env.BlockResults(c.ctx, height)
}
func (c *Local) Header(ctx context.Context, height *int64) (*coretypes.ResultHeader, error) {
return c.env.Header(c.ctx, height)
}
func (c *Local) HeaderByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultHeader, error) {
return c.env.HeaderByHash(c.ctx, hash)
}
func (c *Local) Commit(ctx context.Context, height *int64) (*coretypes.ResultCommit, error) {
return c.env.Commit(c.ctx, height)
}


+ 802
- 0
rpc/client/mocks/client.go View File

@ -0,0 +1,802 @@
// Code generated by mockery. DO NOT EDIT.
package mocks
import (
bytes "github.com/tendermint/tendermint/libs/bytes"
client "github.com/tendermint/tendermint/rpc/client"
context "context"
coretypes "github.com/tendermint/tendermint/rpc/coretypes"
mock "github.com/stretchr/testify/mock"
types "github.com/tendermint/tendermint/types"
)
// Client is an autogenerated mock type for the Client type
type Client struct {
mock.Mock
}
// ABCIInfo provides a mock function with given fields: _a0
func (_m *Client) ABCIInfo(_a0 context.Context) (*coretypes.ResultABCIInfo, error) {
ret := _m.Called(_a0)
var r0 *coretypes.ResultABCIInfo
if rf, ok := ret.Get(0).(func(context.Context) *coretypes.ResultABCIInfo); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultABCIInfo)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ABCIQuery provides a mock function with given fields: ctx, path, data
func (_m *Client) ABCIQuery(ctx context.Context, path string, data bytes.HexBytes) (*coretypes.ResultABCIQuery, error) {
ret := _m.Called(ctx, path, data)
var r0 *coretypes.ResultABCIQuery
if rf, ok := ret.Get(0).(func(context.Context, string, bytes.HexBytes) *coretypes.ResultABCIQuery); ok {
r0 = rf(ctx, path, data)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultABCIQuery)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, string, bytes.HexBytes) error); ok {
r1 = rf(ctx, path, data)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ABCIQueryWithOptions provides a mock function with given fields: ctx, path, data, opts
func (_m *Client) ABCIQueryWithOptions(ctx context.Context, path string, data bytes.HexBytes, opts client.ABCIQueryOptions) (*coretypes.ResultABCIQuery, error) {
ret := _m.Called(ctx, path, data, opts)
var r0 *coretypes.ResultABCIQuery
if rf, ok := ret.Get(0).(func(context.Context, string, bytes.HexBytes, client.ABCIQueryOptions) *coretypes.ResultABCIQuery); ok {
r0 = rf(ctx, path, data, opts)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultABCIQuery)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, string, bytes.HexBytes, client.ABCIQueryOptions) error); ok {
r1 = rf(ctx, path, data, opts)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Block provides a mock function with given fields: ctx, height
func (_m *Client) Block(ctx context.Context, height *int64) (*coretypes.ResultBlock, error) {
ret := _m.Called(ctx, height)
var r0 *coretypes.ResultBlock
if rf, ok := ret.Get(0).(func(context.Context, *int64) *coretypes.ResultBlock); ok {
r0 = rf(ctx, height)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultBlock)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *int64) error); ok {
r1 = rf(ctx, height)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// BlockByHash provides a mock function with given fields: ctx, hash
func (_m *Client) BlockByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultBlock, error) {
ret := _m.Called(ctx, hash)
var r0 *coretypes.ResultBlock
if rf, ok := ret.Get(0).(func(context.Context, bytes.HexBytes) *coretypes.ResultBlock); ok {
r0 = rf(ctx, hash)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultBlock)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, bytes.HexBytes) error); ok {
r1 = rf(ctx, hash)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// BlockResults provides a mock function with given fields: ctx, height
func (_m *Client) BlockResults(ctx context.Context, height *int64) (*coretypes.ResultBlockResults, error) {
ret := _m.Called(ctx, height)
var r0 *coretypes.ResultBlockResults
if rf, ok := ret.Get(0).(func(context.Context, *int64) *coretypes.ResultBlockResults); ok {
r0 = rf(ctx, height)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultBlockResults)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *int64) error); ok {
r1 = rf(ctx, height)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// BlockSearch provides a mock function with given fields: ctx, query, page, perPage, orderBy
func (_m *Client) BlockSearch(ctx context.Context, query string, page *int, perPage *int, orderBy string) (*coretypes.ResultBlockSearch, error) {
ret := _m.Called(ctx, query, page, perPage, orderBy)
var r0 *coretypes.ResultBlockSearch
if rf, ok := ret.Get(0).(func(context.Context, string, *int, *int, string) *coretypes.ResultBlockSearch); ok {
r0 = rf(ctx, query, page, perPage, orderBy)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultBlockSearch)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, string, *int, *int, string) error); ok {
r1 = rf(ctx, query, page, perPage, orderBy)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// BlockchainInfo provides a mock function with given fields: ctx, minHeight, maxHeight
func (_m *Client) BlockchainInfo(ctx context.Context, minHeight int64, maxHeight int64) (*coretypes.ResultBlockchainInfo, error) {
ret := _m.Called(ctx, minHeight, maxHeight)
var r0 *coretypes.ResultBlockchainInfo
if rf, ok := ret.Get(0).(func(context.Context, int64, int64) *coretypes.ResultBlockchainInfo); ok {
r0 = rf(ctx, minHeight, maxHeight)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultBlockchainInfo)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int64, int64) error); ok {
r1 = rf(ctx, minHeight, maxHeight)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// BroadcastEvidence provides a mock function with given fields: _a0, _a1
func (_m *Client) BroadcastEvidence(_a0 context.Context, _a1 types.Evidence) (*coretypes.ResultBroadcastEvidence, error) {
ret := _m.Called(_a0, _a1)
var r0 *coretypes.ResultBroadcastEvidence
if rf, ok := ret.Get(0).(func(context.Context, types.Evidence) *coretypes.ResultBroadcastEvidence); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultBroadcastEvidence)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, types.Evidence) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// BroadcastTxAsync provides a mock function with given fields: _a0, _a1
func (_m *Client) BroadcastTxAsync(_a0 context.Context, _a1 types.Tx) (*coretypes.ResultBroadcastTx, error) {
ret := _m.Called(_a0, _a1)
var r0 *coretypes.ResultBroadcastTx
if rf, ok := ret.Get(0).(func(context.Context, types.Tx) *coretypes.ResultBroadcastTx); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultBroadcastTx)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, types.Tx) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// BroadcastTxCommit provides a mock function with given fields: _a0, _a1
func (_m *Client) BroadcastTxCommit(_a0 context.Context, _a1 types.Tx) (*coretypes.ResultBroadcastTxCommit, error) {
ret := _m.Called(_a0, _a1)
var r0 *coretypes.ResultBroadcastTxCommit
if rf, ok := ret.Get(0).(func(context.Context, types.Tx) *coretypes.ResultBroadcastTxCommit); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultBroadcastTxCommit)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, types.Tx) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// BroadcastTxSync provides a mock function with given fields: _a0, _a1
func (_m *Client) BroadcastTxSync(_a0 context.Context, _a1 types.Tx) (*coretypes.ResultBroadcastTx, error) {
ret := _m.Called(_a0, _a1)
var r0 *coretypes.ResultBroadcastTx
if rf, ok := ret.Get(0).(func(context.Context, types.Tx) *coretypes.ResultBroadcastTx); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultBroadcastTx)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, types.Tx) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CheckTx provides a mock function with given fields: _a0, _a1
func (_m *Client) CheckTx(_a0 context.Context, _a1 types.Tx) (*coretypes.ResultCheckTx, error) {
ret := _m.Called(_a0, _a1)
var r0 *coretypes.ResultCheckTx
if rf, ok := ret.Get(0).(func(context.Context, types.Tx) *coretypes.ResultCheckTx); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultCheckTx)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, types.Tx) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Commit provides a mock function with given fields: ctx, height
func (_m *Client) Commit(ctx context.Context, height *int64) (*coretypes.ResultCommit, error) {
ret := _m.Called(ctx, height)
var r0 *coretypes.ResultCommit
if rf, ok := ret.Get(0).(func(context.Context, *int64) *coretypes.ResultCommit); ok {
r0 = rf(ctx, height)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultCommit)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *int64) error); ok {
r1 = rf(ctx, height)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ConsensusParams provides a mock function with given fields: ctx, height
func (_m *Client) ConsensusParams(ctx context.Context, height *int64) (*coretypes.ResultConsensusParams, error) {
ret := _m.Called(ctx, height)
var r0 *coretypes.ResultConsensusParams
if rf, ok := ret.Get(0).(func(context.Context, *int64) *coretypes.ResultConsensusParams); ok {
r0 = rf(ctx, height)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultConsensusParams)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *int64) error); ok {
r1 = rf(ctx, height)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ConsensusState provides a mock function with given fields: _a0
func (_m *Client) ConsensusState(_a0 context.Context) (*coretypes.ResultConsensusState, error) {
ret := _m.Called(_a0)
var r0 *coretypes.ResultConsensusState
if rf, ok := ret.Get(0).(func(context.Context) *coretypes.ResultConsensusState); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultConsensusState)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DumpConsensusState provides a mock function with given fields: _a0
func (_m *Client) DumpConsensusState(_a0 context.Context) (*coretypes.ResultDumpConsensusState, error) {
ret := _m.Called(_a0)
var r0 *coretypes.ResultDumpConsensusState
if rf, ok := ret.Get(0).(func(context.Context) *coretypes.ResultDumpConsensusState); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultDumpConsensusState)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Genesis provides a mock function with given fields: _a0
func (_m *Client) Genesis(_a0 context.Context) (*coretypes.ResultGenesis, error) {
ret := _m.Called(_a0)
var r0 *coretypes.ResultGenesis
if rf, ok := ret.Get(0).(func(context.Context) *coretypes.ResultGenesis); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultGenesis)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GenesisChunked provides a mock function with given fields: _a0, _a1
func (_m *Client) GenesisChunked(_a0 context.Context, _a1 uint) (*coretypes.ResultGenesisChunk, error) {
ret := _m.Called(_a0, _a1)
var r0 *coretypes.ResultGenesisChunk
if rf, ok := ret.Get(0).(func(context.Context, uint) *coretypes.ResultGenesisChunk); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultGenesisChunk)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, uint) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Header provides a mock function with given fields: ctx, height
func (_m *Client) Header(ctx context.Context, height *int64) (*coretypes.ResultHeader, error) {
ret := _m.Called(ctx, height)
var r0 *coretypes.ResultHeader
if rf, ok := ret.Get(0).(func(context.Context, *int64) *coretypes.ResultHeader); ok {
r0 = rf(ctx, height)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultHeader)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *int64) error); ok {
r1 = rf(ctx, height)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// HeaderByHash provides a mock function with given fields: ctx, hash
func (_m *Client) HeaderByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultHeader, error) {
ret := _m.Called(ctx, hash)
var r0 *coretypes.ResultHeader
if rf, ok := ret.Get(0).(func(context.Context, bytes.HexBytes) *coretypes.ResultHeader); ok {
r0 = rf(ctx, hash)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultHeader)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, bytes.HexBytes) error); ok {
r1 = rf(ctx, hash)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Health provides a mock function with given fields: _a0
func (_m *Client) Health(_a0 context.Context) (*coretypes.ResultHealth, error) {
ret := _m.Called(_a0)
var r0 *coretypes.ResultHealth
if rf, ok := ret.Get(0).(func(context.Context) *coretypes.ResultHealth); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultHealth)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// IsRunning provides a mock function with given fields:
func (_m *Client) IsRunning() bool {
ret := _m.Called()
var r0 bool
if rf, ok := ret.Get(0).(func() bool); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(bool)
}
return r0
}
// NetInfo provides a mock function with given fields: _a0
func (_m *Client) NetInfo(_a0 context.Context) (*coretypes.ResultNetInfo, error) {
ret := _m.Called(_a0)
var r0 *coretypes.ResultNetInfo
if rf, ok := ret.Get(0).(func(context.Context) *coretypes.ResultNetInfo); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultNetInfo)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// NumUnconfirmedTxs provides a mock function with given fields: _a0
func (_m *Client) NumUnconfirmedTxs(_a0 context.Context) (*coretypes.ResultUnconfirmedTxs, error) {
ret := _m.Called(_a0)
var r0 *coretypes.ResultUnconfirmedTxs
if rf, ok := ret.Get(0).(func(context.Context) *coretypes.ResultUnconfirmedTxs); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultUnconfirmedTxs)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RemoveTx provides a mock function with given fields: _a0, _a1
func (_m *Client) RemoveTx(_a0 context.Context, _a1 types.TxKey) error {
ret := _m.Called(_a0, _a1)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, types.TxKey) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}
// Start provides a mock function with given fields: _a0
func (_m *Client) Start(_a0 context.Context) error {
ret := _m.Called(_a0)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context) error); ok {
r0 = rf(_a0)
} else {
r0 = ret.Error(0)
}
return r0
}
// Status provides a mock function with given fields: _a0
func (_m *Client) Status(_a0 context.Context) (*coretypes.ResultStatus, error) {
ret := _m.Called(_a0)
var r0 *coretypes.ResultStatus
if rf, ok := ret.Get(0).(func(context.Context) *coretypes.ResultStatus); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultStatus)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Stop provides a mock function with given fields:
func (_m *Client) Stop() error {
ret := _m.Called()
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
} else {
r0 = ret.Error(0)
}
return r0
}
// Subscribe provides a mock function with given fields: ctx, subscriber, query, outCapacity
func (_m *Client) Subscribe(ctx context.Context, subscriber string, query string, outCapacity ...int) (<-chan coretypes.ResultEvent, error) {
_va := make([]interface{}, len(outCapacity))
for _i := range outCapacity {
_va[_i] = outCapacity[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, subscriber, query)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 <-chan coretypes.ResultEvent
if rf, ok := ret.Get(0).(func(context.Context, string, string, ...int) <-chan coretypes.ResultEvent); ok {
r0 = rf(ctx, subscriber, query, outCapacity...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(<-chan coretypes.ResultEvent)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, string, string, ...int) error); ok {
r1 = rf(ctx, subscriber, query, outCapacity...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Tx provides a mock function with given fields: ctx, hash, prove
func (_m *Client) Tx(ctx context.Context, hash bytes.HexBytes, prove bool) (*coretypes.ResultTx, error) {
ret := _m.Called(ctx, hash, prove)
var r0 *coretypes.ResultTx
if rf, ok := ret.Get(0).(func(context.Context, bytes.HexBytes, bool) *coretypes.ResultTx); ok {
r0 = rf(ctx, hash, prove)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultTx)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, bytes.HexBytes, bool) error); ok {
r1 = rf(ctx, hash, prove)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// TxSearch provides a mock function with given fields: ctx, query, prove, page, perPage, orderBy
func (_m *Client) TxSearch(ctx context.Context, query string, prove bool, page *int, perPage *int, orderBy string) (*coretypes.ResultTxSearch, error) {
ret := _m.Called(ctx, query, prove, page, perPage, orderBy)
var r0 *coretypes.ResultTxSearch
if rf, ok := ret.Get(0).(func(context.Context, string, bool, *int, *int, string) *coretypes.ResultTxSearch); ok {
r0 = rf(ctx, query, prove, page, perPage, orderBy)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultTxSearch)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, string, bool, *int, *int, string) error); ok {
r1 = rf(ctx, query, prove, page, perPage, orderBy)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// UnconfirmedTxs provides a mock function with given fields: ctx, limit
func (_m *Client) UnconfirmedTxs(ctx context.Context, limit *int) (*coretypes.ResultUnconfirmedTxs, error) {
ret := _m.Called(ctx, limit)
var r0 *coretypes.ResultUnconfirmedTxs
if rf, ok := ret.Get(0).(func(context.Context, *int) *coretypes.ResultUnconfirmedTxs); ok {
r0 = rf(ctx, limit)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultUnconfirmedTxs)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *int) error); ok {
r1 = rf(ctx, limit)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Unsubscribe provides a mock function with given fields: ctx, subscriber, query
func (_m *Client) Unsubscribe(ctx context.Context, subscriber string, query string) error {
ret := _m.Called(ctx, subscriber, query)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
r0 = rf(ctx, subscriber, query)
} else {
r0 = ret.Error(0)
}
return r0
}
// UnsubscribeAll provides a mock function with given fields: ctx, subscriber
func (_m *Client) UnsubscribeAll(ctx context.Context, subscriber string) error {
ret := _m.Called(ctx, subscriber)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
r0 = rf(ctx, subscriber)
} else {
r0 = ret.Error(0)
}
return r0
}
// Validators provides a mock function with given fields: ctx, height, page, perPage
func (_m *Client) Validators(ctx context.Context, height *int64, page *int, perPage *int) (*coretypes.ResultValidators, error) {
ret := _m.Called(ctx, height, page, perPage)
var r0 *coretypes.ResultValidators
if rf, ok := ret.Get(0).(func(context.Context, *int64, *int, *int) *coretypes.ResultValidators); ok {
r0 = rf(ctx, height, page, perPage)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*coretypes.ResultValidators)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *int64, *int, *int) error); ok {
r1 = rf(ctx, height, page, perPage)
} else {
r1 = ret.Error(1)
}
return r0, r1
}

+ 9
- 0
rpc/client/rpc_test.go View File

@ -340,6 +340,15 @@ func TestAppCalls(t *testing.T) {
require.NoError(t, err)
require.Equal(t, block, blockByHash)
// check that the header matches the block hash
header, err := c.Header(ctx, &apph)
require.NoError(t, err)
require.Equal(t, block.Block.Header, *header.Header)
headerByHash, err := c.HeaderByHash(ctx, block.BlockID.Hash)
require.NoError(t, err)
require.Equal(t, header, headerByHash)
// now check the results
blockResults, err := c.BlockResults(ctx, &txh)
require.NoError(t, err, "%d: %+v", i, err)


+ 5
- 0
rpc/coretypes/responses.go View File

@ -51,6 +51,11 @@ type ResultBlock struct {
Block *types.Block `json:"block"`
}
// ResultHeader represents the response for a Header RPC Client query
type ResultHeader struct {
Header *types.Header `json:"header"`
}
// Commit and Header
type ResultCommit struct {
types.SignedHeader `json:"signed_header"`


+ 73
- 7
rpc/openapi/openapi.yaml View File

@ -525,7 +525,7 @@ paths:
$ref: "#/components/schemas/ErrorResponse"
/net_info:
get:
summary: Network informations
summary: Network information
operationId: net_info
tags:
- Info
@ -693,6 +693,64 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/header:
get:
summary: Get the header at a specified height
operationId: header
parameters:
- in: query
name: height
schema:
type: integer
default: 0
example: 1
description: height to return. If no height is provided, it will fetch the latest height.
tags:
- Info
description: |
Retrieve the block header corresponding to a specified height.
responses:
"200":
description: Header information.
content:
application/json:
schema:
$ref: "#/components/schemas/HeaderResponse"
"500":
description: Error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/header_by_hash:
get:
summary: Get header by hash
operationId: header_by_hash
parameters:
- in: query
name: hash
description: header hash
required: true
schema:
type: string
example: "0xD70952032620CC4E2737EB8AC379806359D8E0B17B0488F627997A0B043ABDED"
tags:
- Info
description: |
Retrieve the block header corresponding to a block hash.
responses:
"200":
description: Header information.
content:
application/json:
schema:
$ref: "#/components/schemas/HeaderResponse"
"500":
description: Error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/block:
get:
summary: Get block at a specified height
@ -711,7 +769,7 @@ paths:
Get Block.
responses:
"200":
description: Block informations.
description: Block information.
content:
application/json:
schema:
@ -740,7 +798,7 @@ paths:
Get Block By Hash.
responses:
"200":
description: Block informations.
description: Block information.
content:
application/json:
schema:
@ -758,7 +816,7 @@ paths:
parameters:
- in: query
name: height
description: height to return. If no height is provided, it will fetch informations regarding the latest block.
description: height to return. If no height is provided, it will fetch information regarding the latest block.
schema:
type: integer
default: 0
@ -787,7 +845,7 @@ paths:
parameters:
- in: query
name: height
description: height to return. If no height is provided, it will fetch commit informations regarding the latest block.
description: height to return. If no height is provided, it will fetch commit information regarding the latest block.
schema:
type: integer
default: 0
@ -968,7 +1026,7 @@ paths:
parameters:
- in: query
name: height
description: height to return. If no height is provided, it will fetch commit informations regarding the latest block.
description: height to return. If no height is provided, it will fetch commit information regarding the latest block.
schema:
type: integer
default: 0
@ -1703,13 +1761,21 @@ components:
block:
$ref: "#/components/schemas/Block"
BlockResponse:
description: Blockc info
description: Block info
allOf:
- $ref: "#/components/schemas/JSONRPC"
- type: object
properties:
result:
$ref: "#/components/schemas/BlockComplete"
HeaderResponse:
description: Block Header info
allOf:
- $ref: "#/components/schemas/JSONRPC"
- type: object
properties:
result:
$ref: "#/components/schemas/BlockHeader"
################## FROM NOW ON NEEDS REFACTOR ##################
BlockResultsResponse:


Loading…
Cancel
Save