mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-06 13:26:23 +00:00
* Update rpc client header
(cherry picked from commit 2ff11e5bc2)
Co-authored-by: samricotta <37125168+samricotta@users.noreply.github.com>
This commit is contained in:
@@ -36,6 +36,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi
|
|||||||
|
|
||||||
### IMPROVEMENTS
|
### IMPROVEMENTS
|
||||||
|
|
||||||
|
- [rpc] \#9276 Added `header` and `header_by_hash` queries to the RPC client (@samricotta)
|
||||||
- [abci] \#5706 Added `AbciVersion` to `RequestInfo` allowing applications to check ABCI version when connecting to Tendermint. (@marbar3778)
|
- [abci] \#5706 Added `AbciVersion` to `RequestInfo` allowing applications to check ABCI version when connecting to Tendermint. (@marbar3778)
|
||||||
|
|
||||||
### BUG FIXES
|
### BUG FIXES
|
||||||
|
|||||||
@@ -1164,6 +1164,7 @@ func (bs *mockBlockStore) LoadBlock(height int64) *types.Block { return bs.chain
|
|||||||
func (bs *mockBlockStore) LoadBlockByHash(hash []byte) *types.Block {
|
func (bs *mockBlockStore) LoadBlockByHash(hash []byte) *types.Block {
|
||||||
return bs.chain[int64(len(bs.chain))-1]
|
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 {
|
func (bs *mockBlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
|
||||||
block := bs.chain[height-1]
|
block := bs.chain[height-1]
|
||||||
bps, err := block.MakePartSet(types.BlockPartSizeBytes)
|
bps, err := block.MakePartSet(types.BlockPartSizeBytes)
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ func RPCRoutes(c *lrpc.Client) map[string]*rpcserver.RPCFunc {
|
|||||||
"genesis": rpcserver.NewRPCFunc(makeGenesisFunc(c), ""),
|
"genesis": rpcserver.NewRPCFunc(makeGenesisFunc(c), ""),
|
||||||
"genesis_chunked": rpcserver.NewRPCFunc(makeGenesisChunkedFunc(c), ""),
|
"genesis_chunked": rpcserver.NewRPCFunc(makeGenesisChunkedFunc(c), ""),
|
||||||
"block": rpcserver.NewRPCFunc(makeBlockFunc(c), "height"),
|
"block": rpcserver.NewRPCFunc(makeBlockFunc(c), "height"),
|
||||||
|
"header": rpcserver.NewRPCFunc(makeHeaderFunc(c), "height"),
|
||||||
|
"header_by_hash": rpcserver.NewRPCFunc(makeHeaderByHashFunc(c), "hash"),
|
||||||
"block_by_hash": rpcserver.NewRPCFunc(makeBlockByHashFunc(c), "hash"),
|
"block_by_hash": rpcserver.NewRPCFunc(makeBlockByHashFunc(c), "hash"),
|
||||||
"block_results": rpcserver.NewRPCFunc(makeBlockResultsFunc(c), "height"),
|
"block_results": rpcserver.NewRPCFunc(makeBlockResultsFunc(c), "height"),
|
||||||
"commit": rpcserver.NewRPCFunc(makeCommitFunc(c), "height"),
|
"commit": rpcserver.NewRPCFunc(makeCommitFunc(c), "height"),
|
||||||
@@ -108,6 +110,22 @@ func makeBlockFunc(c *lrpc.Client) rpcBlockFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type rpcHeaderFunc func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultHeader, error)
|
||||||
|
|
||||||
|
func makeHeaderFunc(c *lrpc.Client) rpcHeaderFunc {
|
||||||
|
return func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultHeader, error) {
|
||||||
|
return c.Header(ctx.Context(), height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type rpcHeaderByHashFunc func(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultHeader, error)
|
||||||
|
|
||||||
|
func makeHeaderByHashFunc(c *lrpc.Client) rpcHeaderByHashFunc {
|
||||||
|
return func(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultHeader, error) {
|
||||||
|
return c.HeaderByHash(ctx.Context(), hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type rpcBlockByHashFunc func(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error)
|
type rpcBlockByHashFunc func(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error)
|
||||||
|
|
||||||
func makeBlockByHashFunc(c *lrpc.Client) rpcBlockByHashFunc {
|
func makeBlockByHashFunc(c *lrpc.Client) rpcBlockByHashFunc {
|
||||||
|
|||||||
@@ -441,6 +441,40 @@ func (c *Client) BlockResults(ctx context.Context, height *int64) (*ctypes.Resul
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Header fetches and verifies the header directly via the light client
|
||||||
|
func (c *Client) Header(ctx context.Context, height *int64) (*ctypes.ResultHeader, error) {
|
||||||
|
lb, err := c.updateLightClientIfNeededTo(ctx, height)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ctypes.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) (*ctypes.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) (*ctypes.ResultCommit, error) {
|
func (c *Client) Commit(ctx context.Context, height *int64) (*ctypes.ResultCommit, error) {
|
||||||
// Update the light client if we're behind and retrieve the light block at the requested height
|
// 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.
|
// or at the latest height if no height is provided.
|
||||||
|
|||||||
@@ -98,9 +98,11 @@ type baseRPCClient struct {
|
|||||||
caller jsonrpcclient.Caller
|
caller jsonrpcclient.Caller
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ rpcClient = (*HTTP)(nil)
|
var (
|
||||||
var _ rpcClient = (*BatchHTTP)(nil)
|
_ rpcClient = (*HTTP)(nil)
|
||||||
var _ rpcClient = (*baseRPCClient)(nil)
|
_ rpcClient = (*BatchHTTP)(nil)
|
||||||
|
_ rpcClient = (*baseRPCClient)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// HTTP
|
// HTTP
|
||||||
@@ -444,6 +446,31 @@ func (c *baseRPCClient) BlockResults(
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *baseRPCClient) Header(ctx context.Context, height *int64) (*ctypes.ResultHeader, error) {
|
||||||
|
result := new(ctypes.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) (*ctypes.ResultHeader, error) {
|
||||||
|
result := new(ctypes.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) (*ctypes.ResultCommit, error) {
|
func (c *baseRPCClient) Commit(ctx context.Context, height *int64) (*ctypes.ResultCommit, error) {
|
||||||
result := new(ctypes.ResultCommit)
|
result := new(ctypes.ResultCommit)
|
||||||
params := make(map[string]interface{})
|
params := make(map[string]interface{})
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ type SignClient interface {
|
|||||||
Block(ctx context.Context, height *int64) (*ctypes.ResultBlock, error)
|
Block(ctx context.Context, height *int64) (*ctypes.ResultBlock, error)
|
||||||
BlockByHash(ctx context.Context, hash []byte) (*ctypes.ResultBlock, error)
|
BlockByHash(ctx context.Context, hash []byte) (*ctypes.ResultBlock, error)
|
||||||
BlockResults(ctx context.Context, height *int64) (*ctypes.ResultBlockResults, error)
|
BlockResults(ctx context.Context, height *int64) (*ctypes.ResultBlockResults, error)
|
||||||
|
Header(ctx context.Context, height *int64) (*ctypes.ResultHeader, error)
|
||||||
|
HeaderByHash(ctx context.Context, hash bytes.HexBytes) (*ctypes.ResultHeader, error)
|
||||||
Commit(ctx context.Context, height *int64) (*ctypes.ResultCommit, error)
|
Commit(ctx context.Context, height *int64) (*ctypes.ResultCommit, error)
|
||||||
Validators(ctx context.Context, height *int64, page, perPage *int) (*ctypes.ResultValidators, error)
|
Validators(ctx context.Context, height *int64, page, perPage *int) (*ctypes.ResultValidators, error)
|
||||||
Tx(ctx context.Context, hash []byte, prove bool) (*ctypes.ResultTx, error)
|
Tx(ctx context.Context, hash []byte, prove bool) (*ctypes.ResultTx, error)
|
||||||
|
|||||||
@@ -169,6 +169,14 @@ func (c *Local) BlockResults(ctx context.Context, height *int64) (*ctypes.Result
|
|||||||
return core.BlockResults(c.ctx, height)
|
return core.BlockResults(c.ctx, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Local) Header(ctx context.Context, height *int64) (*ctypes.ResultHeader, error) {
|
||||||
|
return core.Header(c.ctx, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Local) HeaderByHash(ctx context.Context, hash bytes.HexBytes) (*ctypes.ResultHeader, error) {
|
||||||
|
return core.HeaderByHash(c.ctx, hash)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Local) Commit(ctx context.Context, height *int64) (*ctypes.ResultCommit, error) {
|
func (c *Local) Commit(ctx context.Context, height *int64) (*ctypes.ResultCommit, error) {
|
||||||
return core.Commit(c.ctx, height)
|
return core.Commit(c.ctx, height)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -458,6 +458,51 @@ func (_m *Client) GenesisChunked(_a0 context.Context, _a1 uint) (*coretypes.Resu
|
|||||||
|
|
||||||
return r0, r1
|
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
|
// Health provides a mock function with given fields: _a0
|
||||||
func (_m *Client) Health(_a0 context.Context) (*coretypes.ResultHealth, error) {
|
func (_m *Client) Health(_a0 context.Context) (*coretypes.ResultHealth, error) {
|
||||||
|
|||||||
@@ -285,6 +285,15 @@ func TestAppCalls(t *testing.T) {
|
|||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
require.Equal(block, blockByHash)
|
require.Equal(block, blockByHash)
|
||||||
|
|
||||||
|
// check that the header matches the block hash
|
||||||
|
header, err := c.Header(context.Background(), &apph)
|
||||||
|
require.NoError(err)
|
||||||
|
require.Equal(block.Block.Header, *header.Header)
|
||||||
|
|
||||||
|
headerByHash, err := c.HeaderByHash(context.Background(), block.BlockID.Hash)
|
||||||
|
require.NoError(err)
|
||||||
|
require.Equal(header, headerByHash)
|
||||||
|
|
||||||
// now check the results
|
// now check the results
|
||||||
blockResults, err := c.BlockResults(context.Background(), &txh)
|
blockResults, err := c.BlockResults(context.Background(), &txh)
|
||||||
require.Nil(err, "%d: %+v", i, err)
|
require.Nil(err, "%d: %+v", i, err)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/tendermint/tendermint/libs/bytes"
|
||||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||||
tmquery "github.com/tendermint/tendermint/libs/pubsub/query"
|
tmquery "github.com/tendermint/tendermint/libs/pubsub/query"
|
||||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||||
@@ -76,6 +77,38 @@ func filterMinMax(base, height, min, max, limit int64) (int64, int64, error) {
|
|||||||
return min, max, nil
|
return min, max, 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 Header(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultHeader, error) {
|
||||||
|
height, err := getHeight(env.BlockStore.Height(), heightPtr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
blockMeta := env.BlockStore.LoadBlockMeta(height)
|
||||||
|
if blockMeta == nil {
|
||||||
|
return &ctypes.ResultHeader{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ctypes.ResultHeader{Header: &blockMeta.Header}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeaderByHash gets header by hash.
|
||||||
|
// More: https://docs.tendermint.com/master/rpc/#/Info/header_by_hash
|
||||||
|
func HeaderByHash(ctx *rpctypes.Context, hash bytes.HexBytes) (*ctypes.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 &ctypes.ResultHeader{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ctypes.ResultHeader{Header: &blockMeta.Header}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Block gets block at a given height.
|
// Block gets block at a given height.
|
||||||
// If no height is provided, it will fetch the latest block.
|
// If no height is provided, it will fetch the latest block.
|
||||||
// More: https://docs.tendermint.com/v0.37/rpc/#/Info/block
|
// More: https://docs.tendermint.com/v0.37/rpc/#/Info/block
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||||
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
||||||
sm "github.com/tendermint/tendermint/state"
|
sm "github.com/tendermint/tendermint/state"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/state/mocks"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBlockchainInfo(t *testing.T) {
|
func TestBlockchainInfo(t *testing.T) {
|
||||||
@@ -86,7 +86,10 @@ func TestBlockResults(t *testing.T) {
|
|||||||
})
|
})
|
||||||
err := env.StateStore.SaveABCIResponses(100, results)
|
err := env.StateStore.SaveABCIResponses(100, results)
|
||||||
require.NoError(t, err)
|
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 {
|
testCases := []struct {
|
||||||
height int64
|
height int64
|
||||||
@@ -116,21 +119,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(height int64) *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) {
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ var Routes = map[string]*rpc.RPCFunc{
|
|||||||
"block_by_hash": rpc.NewRPCFunc(BlockByHash, "hash"),
|
"block_by_hash": rpc.NewRPCFunc(BlockByHash, "hash"),
|
||||||
"block_results": rpc.NewRPCFunc(BlockResults, "height"),
|
"block_results": rpc.NewRPCFunc(BlockResults, "height"),
|
||||||
"commit": rpc.NewRPCFunc(Commit, "height"),
|
"commit": rpc.NewRPCFunc(Commit, "height"),
|
||||||
|
"header": rpc.NewRPCFunc(Header, "height"),
|
||||||
|
"header_by_hash": rpc.NewRPCFunc(HeaderByHash, "hash"),
|
||||||
"check_tx": rpc.NewRPCFunc(CheckTx, "tx"),
|
"check_tx": rpc.NewRPCFunc(CheckTx, "tx"),
|
||||||
"tx": rpc.NewRPCFunc(Tx, "hash,prove"),
|
"tx": rpc.NewRPCFunc(Tx, "hash,prove"),
|
||||||
"tx_search": rpc.NewRPCFunc(TxSearch, "query,prove,page,per_page,order_by"),
|
"tx_search": rpc.NewRPCFunc(TxSearch, "query,prove,page,per_page,order_by"),
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ type ResultBlock struct {
|
|||||||
Block *types.Block `json:"block"`
|
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
|
// Commit and Header
|
||||||
type ResultCommit struct {
|
type ResultCommit struct {
|
||||||
types.SignedHeader `json:"signed_header"`
|
types.SignedHeader `json:"signed_header"`
|
||||||
|
|||||||
@@ -500,7 +500,7 @@ paths:
|
|||||||
$ref: "#/components/schemas/ErrorResponse"
|
$ref: "#/components/schemas/ErrorResponse"
|
||||||
/net_info:
|
/net_info:
|
||||||
get:
|
get:
|
||||||
summary: Network informations
|
summary: Network information
|
||||||
operationId: net_info
|
operationId: net_info
|
||||||
tags:
|
tags:
|
||||||
- Info
|
- Info
|
||||||
@@ -637,6 +637,64 @@ paths:
|
|||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: "#/components/schemas/ErrorResponse"
|
$ref: "#/components/schemas/ErrorResponse"
|
||||||
|
/header:
|
||||||
|
get:
|
||||||
|
summary: Get 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 header.
|
||||||
|
tags:
|
||||||
|
- Info
|
||||||
|
description: |
|
||||||
|
Get Header.
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Header informations.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/BlockHeader"
|
||||||
|
"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: |
|
||||||
|
Get Header By Hash.
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Header informations.
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/BlockHeader"
|
||||||
|
"500":
|
||||||
|
description: Error
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/ErrorResponse"
|
||||||
/block:
|
/block:
|
||||||
get:
|
get:
|
||||||
summary: Get block at a specified height
|
summary: Get block at a specified height
|
||||||
|
|||||||
@@ -121,6 +121,22 @@ func (_m *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
|
|||||||
return r0
|
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
|
// LoadBlockPart provides a mock function with given fields: height, index
|
||||||
func (_m *BlockStore) LoadBlockPart(height int64, index int) *types.Part {
|
func (_m *BlockStore) LoadBlockPart(height int64, index int) *types.Part {
|
||||||
ret := _m.Called(height, index)
|
ret := _m.Called(height, index)
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ type BlockStore interface {
|
|||||||
PruneBlocks(height int64) (uint64, error)
|
PruneBlocks(height int64) (uint64, error)
|
||||||
|
|
||||||
LoadBlockByHash(hash []byte) *types.Block
|
LoadBlockByHash(hash []byte) *types.Block
|
||||||
|
LoadBlockMetaByHash(hash []byte) *types.BlockMeta
|
||||||
LoadBlockPart(height int64, index int) *types.Part
|
LoadBlockPart(height int64, index int) *types.Part
|
||||||
|
|
||||||
LoadBlockCommit(height int64) *types.Commit
|
LoadBlockCommit(height int64) *types.Commit
|
||||||
|
|||||||
@@ -196,6 +196,26 @@ func (bs *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
|
|||||||
return blockMeta
|
return blockMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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(calcBlockHashKey(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)
|
||||||
|
}
|
||||||
|
|
||||||
// LoadBlockCommit returns the Commit for the given height.
|
// LoadBlockCommit returns the Commit for the given height.
|
||||||
// This commit consists of the +2/3 and other Precommit-votes for block at `height`,
|
// This commit consists of the +2/3 and other Precommit-votes for block at `height`,
|
||||||
// and it comes from the block.LastCommit for `height+1`.
|
// and it comes from the block.LastCommit for `height+1`.
|
||||||
|
|||||||
@@ -476,6 +476,7 @@ func TestPruneBlocks(t *testing.T) {
|
|||||||
require.Nil(t, bs.LoadBlockByHash(prunedBlock.Hash()))
|
require.Nil(t, bs.LoadBlockByHash(prunedBlock.Hash()))
|
||||||
require.Nil(t, bs.LoadBlockCommit(1199))
|
require.Nil(t, bs.LoadBlockCommit(1199))
|
||||||
require.Nil(t, bs.LoadBlockMeta(1199))
|
require.Nil(t, bs.LoadBlockMeta(1199))
|
||||||
|
require.Nil(t, bs.LoadBlockMetaByHash(prunedBlock.Hash()))
|
||||||
require.Nil(t, bs.LoadBlockPart(1199, 1))
|
require.Nil(t, bs.LoadBlockPart(1199, 1))
|
||||||
|
|
||||||
for i := int64(1); i < 1200; i++ {
|
for i := int64(1); i < 1200; i++ {
|
||||||
@@ -552,6 +553,28 @@ func TestLoadBlockMeta(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoadBlockMetaByHash(t *testing.T) {
|
||||||
|
config := cfg.ResetTestRoot("blockchain_reactor_test")
|
||||||
|
defer os.RemoveAll(config.RootDir)
|
||||||
|
stateStore := sm.NewStore(dbm.NewMemDB(), sm.StoreOptions{
|
||||||
|
DiscardABCIResponses: false,
|
||||||
|
})
|
||||||
|
state, err := stateStore.LoadFromDBOrGenesisFile(config.GenesisFile())
|
||||||
|
require.NoError(t, err)
|
||||||
|
bs := NewBlockStore(dbm.NewMemDB())
|
||||||
|
|
||||||
|
b1 := state.MakeBlock(state.LastBlockHeight+1, test.MakeNTxs(state.LastBlockHeight+1, 10), new(types.Commit), nil, state.Validators.GetProposer().Address)
|
||||||
|
partSet, err := b1.MakePartSet(2)
|
||||||
|
require.NoError(t, err)
|
||||||
|
seenCommit := makeTestCommit(1, tmtime.Now())
|
||||||
|
bs.SaveBlock(b1, partSet, seenCommit)
|
||||||
|
|
||||||
|
baseBlock := bs.LoadBlockMetaByHash(b1.Hash())
|
||||||
|
assert.EqualValues(t, b1.Header.Height, baseBlock.Header.Height)
|
||||||
|
assert.EqualValues(t, b1.Header.LastBlockID, baseBlock.Header.LastBlockID)
|
||||||
|
assert.EqualValues(t, b1.Header.ChainID, baseBlock.Header.ChainID)
|
||||||
|
}
|
||||||
|
|
||||||
func TestBlockFetchAtHeight(t *testing.T) {
|
func TestBlockFetchAtHeight(t *testing.T) {
|
||||||
state, bs, cleanup := makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer)))
|
state, bs, cleanup := makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer)))
|
||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|||||||
Reference in New Issue
Block a user