Skip to content

Commit

Permalink
Update rpc client header (#9276) (#9349) (#1096)
Browse files Browse the repository at this point in the history
## Description

cherry picks the cherry pick to get the header back
d95e437

closes #1095

---------

Co-authored-by: Callum Waters <cmwaters19@gmail.com>
Co-authored-by: samricotta <37125168+samricotta@users.noreply.github.com>
  • Loading branch information
3 people committed Sep 26, 2023
1 parent e2b3187 commit 36c53b8
Show file tree
Hide file tree
Showing 17 changed files with 308 additions and 7 deletions.
2 changes: 1 addition & 1 deletion consensus/replay_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1211,7 +1211,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{
Expand Down
18 changes: 18 additions & 0 deletions light/proxy/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ func RPCRoutes(c *lrpc.Client) map[string]*rpcserver.RPCFunc {
"block_by_hash": rpcserver.NewRPCFunc(makeBlockByHashFunc(c), "hash", rpcserver.Cacheable()),
"block_results": rpcserver.NewRPCFunc(makeBlockResultsFunc(c), "height", rpcserver.Cacheable("height")),
"commit": rpcserver.NewRPCFunc(makeCommitFunc(c), "height", rpcserver.Cacheable("height")),
"header": rpcserver.NewRPCFunc(makeHeaderFunc(c), "height", rpcserver.Cacheable("height")),
"header_by_hash": rpcserver.NewRPCFunc(makeHeaderByHashFunc(c), "hash"),
"tx": rpcserver.NewRPCFunc(makeTxFunc(c), "hash,prove", rpcserver.Cacheable()),
"tx_search": rpcserver.NewRPCFunc(makeTxSearchFuncMatchEvents(c), "query,prove,page,per_page,order_by,match_events"),
"block_search": rpcserver.NewRPCFunc(makeBlockSearchFuncMatchEvents(c), "query,page,per_page,order_by,match_events"),
Expand Down Expand Up @@ -109,6 +111,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)

func makeBlockByHashFunc(c *lrpc.Client) rpcBlockByHashFunc {
Expand Down
34 changes: 34 additions & 0 deletions light/rpc/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,40 @@ func (c *Client) BlockResults(ctx context.Context, height *int64) (*ctypes.Resul
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 cmtbytes.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) {
// 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.
Expand Down
33 changes: 30 additions & 3 deletions rpc/client/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,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
Expand Down Expand Up @@ -457,6 +459,31 @@ func (c *baseRPCClient) BlockResults(
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) {
result := new(ctypes.ResultCommit)
params := make(map[string]interface{})
Expand Down
2 changes: 2 additions & 0 deletions rpc/client/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ type SignClient interface {
SignedBlock(ctx context.Context, height *int64) (*ctypes.ResultSignedBlock, error)
BlockByHash(ctx context.Context, hash []byte) (*ctypes.ResultBlock, 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)

DataCommitment(ctx context.Context, start, end uint64) (*ctypes.ResultDataCommitment, error)
Expand Down
8 changes: 8 additions & 0 deletions rpc/client/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,14 @@ func (c *Local) BlockResults(ctx context.Context, height *int64) (*ctypes.Result
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) {
return core.Commit(c.ctx, height)
}
Expand Down
46 changes: 46 additions & 0 deletions rpc/client/mocks/client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions rpc/client/rpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,15 @@ func TestAppCalls(t *testing.T) {
require.NoError(err)
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
blockResults, err := c.BlockResults(context.Background(), &txh)
require.Nil(err, "%d: %+v", i, err)
Expand Down
33 changes: 33 additions & 0 deletions rpc/core/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strconv"

"github.com/tendermint/tendermint/crypto/merkle"
"github.com/tendermint/tendermint/libs/bytes"
cmtmath "github.com/tendermint/tendermint/libs/math"
cmtquery "github.com/tendermint/tendermint/libs/pubsub/query"
"github.com/tendermint/tendermint/pkg/consts"
Expand Down Expand Up @@ -87,6 +88,38 @@ func filterMinMax(base, height, min, max, limit int64) (int64, int64, error) {
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(GetEnvironment().BlockStore.Height(), heightPtr)
if err != nil {
return nil, err
}

blockMeta := GetEnvironment().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 := GetEnvironment().BlockStore.LoadBlockMetaByHash(hash)
if blockMeta == nil {
return &ctypes.ResultHeader{}, nil
}

return &ctypes.ResultHeader{Header: &blockMeta.Header}, nil
}

// Block gets block at a given height.
// If no height is provided, it will fetch the latest block.
// More: https://docs.cometbft.com/v0.34/rpc/#/Info/block
Expand Down
3 changes: 2 additions & 1 deletion rpc/core/blocks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ import (
"github.com/tendermint/tendermint/crypto/merkle"
"github.com/tendermint/tendermint/libs/pubsub/query"
cmtrand "github.com/tendermint/tendermint/libs/rand"
"github.com/tendermint/tendermint/types"

abci "github.com/tendermint/tendermint/abci/types"
cmtstate "github.com/tendermint/tendermint/proto/tendermint/state"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
)

func TestBlockchainInfo(t *testing.T) {
Expand Down Expand Up @@ -292,6 +292,7 @@ func (store mockBlockStore) Size() int64 { retur
func (mockBlockStore) LoadBaseMeta() *types.BlockMeta { return nil }
func (mockBlockStore) LoadBlockByHash(hash []byte) *types.Block { return nil }
func (mockBlockStore) LoadBlockPart(height int64, index int) *types.Part { return nil }
func (mockBlockStore) LoadBlockMetaByHash(hash []byte) *types.BlockMeta { 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 }
Expand Down
2 changes: 2 additions & 0 deletions rpc/core/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ var Routes = map[string]*rpc.RPCFunc{
"block_by_hash": rpc.NewRPCFunc(BlockByHash, "hash", rpc.Cacheable()),
"block_results": rpc.NewRPCFunc(BlockResults, "height", rpc.Cacheable("height")),
"commit": rpc.NewRPCFunc(Commit, "height", rpc.Cacheable("height")),
"header": rpc.NewRPCFunc(Header, "height", rpc.Cacheable("height")),
"header_by_hash": rpc.NewRPCFunc(HeaderByHash, "hash"),
"data_commitment": rpc.NewRPCFunc(DataCommitment, "start,end"),
"check_tx": rpc.NewRPCFunc(CheckTx, "tx"),
"tx": rpc.NewRPCFunc(Tx, "hash,prove", rpc.Cacheable()),
Expand Down
5 changes: 5 additions & 0 deletions rpc/core/types/responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ type ResultSignedBlock struct {
ValidatorSet types.ValidatorSet `json:"validator_set"`
}

// 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"`
Expand Down
60 changes: 59 additions & 1 deletion rpc/openapi/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ paths:
$ref: "#/components/schemas/ErrorResponse"
/net_info:
get:
summary: Network informations
summary: Network information
operationId: net_info
tags:
- Info
Expand Down Expand Up @@ -439,6 +439,64 @@ paths:
application/json:
schema:
$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:
get:
summary: Get block at a specified height
Expand Down
Loading

0 comments on commit 36c53b8

Please sign in to comment.