Skip to content

Commit

Permalink
client: backport IBC additions (#6682)
Browse files Browse the repository at this point in the history
* launchpad: backport cliCtx.QueryABCI

* add prove flag
  • Loading branch information
fedekunze authored Jul 10, 2020
1 parent 232e720 commit b7f9914
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 20 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ pruning setting `syncable` used `KeepEvery:100`.

* (deps) Bump Tendermint version to [v0.33.6](https://github.com/tendermint/tendermint/releases/tag/v0.33.6)
* (deps) Bump IAVL version to [v0.14.0](https://github.com/cosmos/iavl/releases/tag/v0.14.0)
* (client) [\#5585](https://github.com/cosmos/cosmos-sdk/pull/5585) `CLIContext` additions:
* Introduce `QueryABCI` that returns the full `abci.ResponseQuery` with inclusion Merkle proofs.
* Added `prove` flag for Merkle proof verification.

### API Breaking Changes

Expand Down
60 changes: 40 additions & 20 deletions client/context/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
// error is returned.
func (ctx CLIContext) GetNode() (rpcclient.Client, error) {
if ctx.Client == nil {
return nil, errors.New("no RPC client defined")
return nil, errors.New("no RPC client is defined in offline mode")
}

return ctx.Client, nil
Expand All @@ -49,6 +49,12 @@ func (ctx CLIContext) QueryStore(key tmbytes.HexBytes, storeName string) ([]byte
return ctx.queryStore(key, storeName, "key")
}

// QueryABCI performs a query to a Tendermint node with the provide RequestQuery.
// It returns the ResultQuery obtained from the query.
func (ctx CLIContext) QueryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) {
return ctx.queryABCI(req)
}

// QuerySubspace performs a query to a Tendermint node with the provided
// store name and subspace. It returns key value pair and height of the query
// upon success or an error if the query fails.
Expand All @@ -72,40 +78,50 @@ func (ctx CLIContext) GetFromName() string {
return ctx.FromName
}

// query performs a query to a Tendermint node with the provided store name
// and path. It returns the result and height of the query upon success
// or an error if the query fails. In addition, it will verify the returned
// proof if TrustNode is disabled. If proof verification fails or the query
// height is invalid, an error will be returned.
func (ctx CLIContext) query(path string, key tmbytes.HexBytes) (res []byte, height int64, err error) {
func (ctx CLIContext) queryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) {
node, err := ctx.GetNode()
if err != nil {
return res, height, err
return abci.ResponseQuery{}, err
}

opts := rpcclient.ABCIQueryOptions{
Height: ctx.Height,
Prove: !ctx.TrustNode,
Prove: req.Prove || !ctx.TrustNode,
}

result, err := node.ABCIQueryWithOptions(path, key, opts)
result, err := node.ABCIQueryWithOptions(req.Path, req.Data, opts)
if err != nil {
return res, height, err
return abci.ResponseQuery{}, err
}

resp := result.Response
if !resp.IsOK() {
return res, resp.Height, errors.New(resp.Log)
if !result.Response.IsOK() {
return abci.ResponseQuery{}, errors.New(result.Response.Log)
}

// data from trusted node or subspace query doesn't need verification
if ctx.TrustNode || !isQueryStoreWithProof(path) {
return resp.Value, resp.Height, nil
if !opts.Prove || !isQueryStoreWithProof(req.Path) {
return result.Response, nil
}

err = ctx.verifyProof(path, resp)
if err := ctx.verifyProof(req.Path, result.Response); err != nil {
return abci.ResponseQuery{}, err
}

return result.Response, nil
}

// query performs a query to a Tendermint node with the provided store name
// and path. It returns the result and height of the query upon success
// or an error if the query fails. In addition, it will verify the returned
// proof if TrustNode is disabled. If proof verification fails or the query
// height is invalid, an error will be returned.
func (ctx CLIContext) query(path string, key tmbytes.HexBytes) ([]byte, int64, error) {
resp, err := ctx.queryABCI(abci.RequestQuery{
Path: path,
Data: key,
})
if err != nil {
return res, resp.Height, err
return nil, 0, err
}

return resp.Value, resp.Height, nil
Expand All @@ -116,7 +132,9 @@ func (ctx CLIContext) Verify(height int64) (tmtypes.SignedHeader, error) {
if ctx.Verifier == nil {
return tmtypes.SignedHeader{}, fmt.Errorf("missing valid certifier to verify data from distrusted node")
}

check, err := tmliteProxy.GetCertifiedCommit(height, ctx.Client, ctx.Verifier)

switch {
case tmliteErr.IsErrCommitNotFound(err):
return tmtypes.SignedHeader{}, ErrVerifyCommit(height)
Expand Down Expand Up @@ -159,8 +177,8 @@ func (ctx CLIContext) verifyProof(queryPath string, resp abci.ResponseQuery) err
}
return nil
}
err = prt.VerifyValue(resp.Proof, commit.Header.AppHash, kp.String(), resp.Value)
if err != nil {

if err := prt.VerifyValue(resp.Proof, commit.Header.AppHash, kp.String(), resp.Value); err != nil {
return errors.Wrap(err, "failed to prove merkle proof")
}

Expand All @@ -183,6 +201,7 @@ func isQueryStoreWithProof(path string) bool {
}

paths := strings.SplitN(path[1:], "/", 3)

switch {
case len(paths) != 3:
return false
Expand All @@ -202,6 +221,7 @@ func parseQueryStorePath(path string) (storeName string, err error) {
}

paths := strings.SplitN(path[1:], "/", 3)

switch {
case len(paths) != 3:
return "", errors.New("expected format like /store/<storeName>/key")
Expand Down
6 changes: 6 additions & 0 deletions client/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ const (

// DefaultKeyringBackend
DefaultKeyringBackend = keys.BackendOS
)

const (
// BroadcastBlock defines a tx broadcasting mode where the client waits for
// the tx to be committed in a block.
BroadcastBlock = "block"
Expand All @@ -34,7 +36,10 @@ const (
// BroadcastAsync defines a tx broadcasting mode where the client returns
// immediately.
BroadcastAsync = "async"
)

// List of CLI flags
const (
FlagHome = tmcli.HomeFlag
FlagUseLedger = "ledger"
FlagChainID = "chain-id"
Expand All @@ -59,6 +64,7 @@ const (
FlagRPCWriteTimeout = "write-timeout"
FlagOutputDocument = "output-document" // inspired by wget -O
FlagSkipConfirmation = "yes"
FlagProve = "prove"
FlagKeyringBackend = "keyring-backend"
FlagPage = "page"
FlagLimit = "limit"
Expand Down

0 comments on commit b7f9914

Please sign in to comment.