mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-07 22:05:18 +00:00
rpc: add BlockByHash to Client (#4923)
Ethermint currently has to maintain a map height-> block hash on the store (see here) as it needs to expose the eth_getBlockByHash JSON-RPC query for Web3 compatibility. This query is currently not supported by the tendermint RPC client.
This commit is contained in:
@@ -35,6 +35,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi
|
||||
- [statesync] Add state sync support, where a new node can be rapidly bootstrapped by fetching state snapshots from peers instead of replaying blocks. See the `[statesync]` config section.
|
||||
- [evidence] [\#4532](https://github.com/tendermint/tendermint/pull/4532) Handle evidence from light clients (@melekes)
|
||||
- [lite2] [\#4532](https://github.com/tendermint/tendermint/pull/4532) Submit conflicting headers, if any, to a full node & all witnesses (@melekes)
|
||||
- [rpc] [\#4532](https://github.com/tendermint/tendermint/pull/4923) Support `BlockByHash` query (@fedekunze)
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
|
||||
@@ -68,13 +68,14 @@ func RPCRoutes(c rpcclient.Client) map[string]*rpcserver.RPCFunc {
|
||||
"unsubscribe_all": rpcserver.NewWSRPCFunc(c.(Wrapper).UnsubscribeAllWS, ""),
|
||||
|
||||
// info API
|
||||
"status": rpcserver.NewRPCFunc(makeStatusFunc(c), ""),
|
||||
"blockchain": rpcserver.NewRPCFunc(makeBlockchainInfoFunc(c), "minHeight,maxHeight"),
|
||||
"genesis": rpcserver.NewRPCFunc(makeGenesisFunc(c), ""),
|
||||
"block": rpcserver.NewRPCFunc(makeBlockFunc(c), "height"),
|
||||
"commit": rpcserver.NewRPCFunc(makeCommitFunc(c), "height"),
|
||||
"tx": rpcserver.NewRPCFunc(makeTxFunc(c), "hash,prove"),
|
||||
"validators": rpcserver.NewRPCFunc(makeValidatorsFunc(c), "height"),
|
||||
"status": rpcserver.NewRPCFunc(makeStatusFunc(c), ""),
|
||||
"blockchain": rpcserver.NewRPCFunc(makeBlockchainInfoFunc(c), "minHeight,maxHeight"),
|
||||
"genesis": rpcserver.NewRPCFunc(makeGenesisFunc(c), ""),
|
||||
"block": rpcserver.NewRPCFunc(makeBlockFunc(c), "height"),
|
||||
"block_by_hash": rpcserver.NewRPCFunc(makeBlockByHashFunc(c), "hash"),
|
||||
"commit": rpcserver.NewRPCFunc(makeCommitFunc(c), "height"),
|
||||
"tx": rpcserver.NewRPCFunc(makeTxFunc(c), "hash,prove"),
|
||||
"validators": rpcserver.NewRPCFunc(makeValidatorsFunc(c), "height"),
|
||||
|
||||
// broadcast API
|
||||
"broadcast_tx_commit": rpcserver.NewRPCFunc(makeBroadcastTxCommitFunc(c), "tx"),
|
||||
@@ -115,6 +116,12 @@ func makeBlockFunc(c rpcclient.Client) func(ctx *rpctypes.Context, height *int64
|
||||
}
|
||||
}
|
||||
|
||||
func makeBlockByHashFunc(c rpcclient.Client) func(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error) {
|
||||
return func(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error) {
|
||||
return c.BlockByHash(hash)
|
||||
}
|
||||
}
|
||||
|
||||
func makeCommitFunc(c rpcclient.Client) func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultCommit, error) {
|
||||
return func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultCommit, error) {
|
||||
return c.Commit(height)
|
||||
|
||||
@@ -112,6 +112,26 @@ func (w Wrapper) Block(height *int64) (*ctypes.ResultBlock, error) {
|
||||
return resBlock, nil
|
||||
}
|
||||
|
||||
// BlockByHash returns an entire block and verifies all signatures
|
||||
func (w Wrapper) BlockByHash(hash []byte) (*ctypes.ResultBlock, error) {
|
||||
resBlock, err := w.Client.BlockByHash(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// get a checkpoint to verify from
|
||||
resCommit, err := w.Commit(&resBlock.Block.Height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sh := resCommit.SignedHeader
|
||||
|
||||
err = ValidateBlock(resBlock.Block, sh)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resBlock, nil
|
||||
}
|
||||
|
||||
// Commit downloads the Commit and certifies it with the lite.
|
||||
//
|
||||
// This is the foundation for all other verification in this module
|
||||
|
||||
@@ -23,6 +23,7 @@ func RPCRoutes(c *lrpc.Client) map[string]*rpcserver.RPCFunc {
|
||||
"blockchain": rpcserver.NewRPCFunc(makeBlockchainInfoFunc(c), "minHeight,maxHeight"),
|
||||
"genesis": rpcserver.NewRPCFunc(makeGenesisFunc(c), ""),
|
||||
"block": rpcserver.NewRPCFunc(makeBlockFunc(c), "height"),
|
||||
"block_by_hash": rpcserver.NewRPCFunc(makeBlockByHashFunc(c), "hash"),
|
||||
"block_results": rpcserver.NewRPCFunc(makeBlockResultsFunc(c), "height"),
|
||||
"commit": rpcserver.NewRPCFunc(makeCommitFunc(c), "height"),
|
||||
"tx": rpcserver.NewRPCFunc(makeTxFunc(c), "hash,prove"),
|
||||
@@ -97,6 +98,14 @@ func makeBlockFunc(c *lrpc.Client) rpcBlockFunc {
|
||||
}
|
||||
}
|
||||
|
||||
type rpcBlockByHashFunc func(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error)
|
||||
|
||||
func makeBlockByHashFunc(c *lrpc.Client) rpcBlockByHashFunc {
|
||||
return func(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error) {
|
||||
return c.BlockByHash(hash)
|
||||
}
|
||||
}
|
||||
|
||||
type rpcBlockResultsFunc func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultBlockResults, error)
|
||||
|
||||
func makeBlockResultsFunc(c *lrpc.Client) rpcBlockResultsFunc {
|
||||
|
||||
@@ -268,6 +268,40 @@ func (c *Client) Block(height *int64) (*ctypes.ResultBlock, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// BlockByHash calls rpcclient#BlockByHash and then verifies the result.
|
||||
func (c *Client) BlockByHash(hash []byte) (*ctypes.ResultBlock, error) {
|
||||
res, err := c.next.BlockByHash(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Validate res.
|
||||
if err := res.BlockID.ValidateBasic(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := res.Block.ValidateBasic(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bmH, bH := res.BlockID.Hash, res.Block.Hash(); !bytes.Equal(bmH, bH) {
|
||||
return nil, fmt.Errorf("blockID %X does not match with block %X",
|
||||
bmH, bH)
|
||||
}
|
||||
|
||||
// Update the light client if we're behind.
|
||||
h, err := c.updateLiteClientIfNeededTo(res.Block.Height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Verify block.
|
||||
if bH, tH := res.Block.Hash(), h.Hash(); !bytes.Equal(bH, tH) {
|
||||
return nil, fmt.Errorf("block header %X does not match with trusted header %X",
|
||||
bH, tH)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (c *Client) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) {
|
||||
res, err := c.next.BlockResults(height)
|
||||
if err != nil {
|
||||
|
||||
@@ -367,6 +367,18 @@ func (c *baseRPCClient) Block(height *int64) (*ctypes.ResultBlock, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *baseRPCClient) BlockByHash(hash []byte) (*ctypes.ResultBlock, error) {
|
||||
result := new(ctypes.ResultBlock)
|
||||
params := map[string]interface{}{
|
||||
"hash": hash,
|
||||
}
|
||||
_, err := c.caller.Call("block_by_hash", params, result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *baseRPCClient) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) {
|
||||
result := new(ctypes.ResultBlockResults)
|
||||
params := make(map[string]interface{})
|
||||
|
||||
@@ -65,6 +65,7 @@ type ABCIClient interface {
|
||||
// and prove anything about the chain.
|
||||
type SignClient interface {
|
||||
Block(height *int64) (*ctypes.ResultBlock, error)
|
||||
BlockByHash(hash []byte) (*ctypes.ResultBlock, error)
|
||||
BlockResults(height *int64) (*ctypes.ResultBlockResults, error)
|
||||
Commit(height *int64) (*ctypes.ResultCommit, error)
|
||||
Validators(height *int64, page, perPage int) (*ctypes.ResultValidators, error)
|
||||
|
||||
@@ -144,6 +144,10 @@ func (c *Local) Block(height *int64) (*ctypes.ResultBlock, error) {
|
||||
return core.Block(c.ctx, height)
|
||||
}
|
||||
|
||||
func (c *Local) BlockByHash(hash []byte) (*ctypes.ResultBlock, error) {
|
||||
return core.BlockByHash(c.ctx, hash)
|
||||
}
|
||||
|
||||
func (c *Local) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) {
|
||||
return core.BlockResults(c.ctx, height)
|
||||
}
|
||||
|
||||
@@ -150,6 +150,10 @@ func (c Client) Block(height *int64) (*ctypes.ResultBlock, error) {
|
||||
return core.Block(&rpctypes.Context{}, height)
|
||||
}
|
||||
|
||||
func (c Client) BlockByHash(hash []byte) (*ctypes.ResultBlock, error) {
|
||||
return core.BlockByHash(&rpctypes.Context{}, hash)
|
||||
}
|
||||
|
||||
func (c Client) Commit(height *int64) (*ctypes.ResultCommit, error) {
|
||||
return core.Commit(&rpctypes.Context{}, height)
|
||||
}
|
||||
|
||||
@@ -249,6 +249,10 @@ func TestAppCalls(t *testing.T) {
|
||||
assert.True(len(appHash) > 0)
|
||||
assert.EqualValues(apph, block.Block.Header.Height)
|
||||
|
||||
blockByHash, err := c.BlockByHash(block.BlockID.Hash)
|
||||
require.NoError(err)
|
||||
require.Equal(block, blockByHash)
|
||||
|
||||
// now check the results
|
||||
blockResults, err := c.BlockResults(&txh)
|
||||
require.Nil(err, "%d: %+v", i, err)
|
||||
|
||||
Reference in New Issue
Block a user