diff --git a/CHANGELOG.md b/CHANGELOG.md index a0bbca185..ff09e4e9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,12 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (types) [\#5581](https://github.com/cosmos/cosmos-sdk/pull/5581) Add convenience functions {,Must}Bech32ifyAddressBytes. * (staking) [\#5584](https://github.com/cosmos/cosmos-sdk/pull/5584) Add util function `ToTmValidator` that converts a `staking.Validator` type to `*tmtypes.Validator`. +* (client) [\#5585](https://github.com/cosmos/cosmos-sdk/pull/5585) IBC additions: + * Added `prove` flag for commitment proof verification. + * Added `queryABCI` function that returns the full `abci.ResponseQuery` with inclusion merkle proofs. +* (types) [\#5585](https://github.com/cosmos/cosmos-sdk/pull/5585) IBC additions: + * `Coin` denomination max lenght has been increased to 32. + * Added `CapabilityKey` alias for `StoreKey` to match IBC spec. ## [v0.38.0] - 2020-01-23 diff --git a/client/context/query.go b/client/context/query.go index dcf2c92b7..b5686f864 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -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. @@ -72,40 +78,51 @@ func (ctx CLIContext) GetFromName() string { return ctx.FromName } +func (ctx CLIContext) queryABCI(req abci.RequestQuery) (abci.ResponseQuery, error) { + node, err := ctx.GetNode() + if err != nil { + return abci.ResponseQuery{}, err + } + + opts := rpcclient.ABCIQueryOptions{ + Height: ctx.Height, + Prove: req.Prove || !ctx.TrustNode, + } + + result, err := node.ABCIQueryWithOptions(req.Path, req.Data, opts) + if err != nil { + return abci.ResponseQuery{}, err + } + + 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(req.Path) { + return result.Response, nil + } + + err = ctx.verifyProof(req.Path, result.Response) + if 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) (res []byte, height int64, err error) { - node, err := ctx.GetNode() +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, height, err - } - - opts := rpcclient.ABCIQueryOptions{ - Height: ctx.Height, - Prove: !ctx.TrustNode, - } - - result, err := node.ABCIQueryWithOptions(path, key, opts) - if err != nil { - return res, height, err - } - - resp := result.Response - if !resp.IsOK() { - return res, resp.Height, errors.New(resp.Log) - } - - // data from trusted node or subspace query doesn't need verification - if ctx.TrustNode || !isQueryStoreWithProof(path) { - return resp.Value, resp.Height, nil - } - - err = ctx.verifyProof(path, resp) - if err != nil { - return res, resp.Height, err + return nil, 0, err } return resp.Value, resp.Height, nil diff --git a/client/flags/flags.go b/client/flags/flags.go index 2cdaa5f21..64dc2f25a 100644 --- a/client/flags/flags.go +++ b/client/flags/flags.go @@ -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" @@ -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" @@ -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" diff --git a/store/types/store.go b/store/types/store.go index 377b43909..d48585f8f 100644 --- a/store/types/store.go +++ b/store/types/store.go @@ -277,6 +277,10 @@ type StoreKey interface { String() string } +// CapabilityKey represent the Cosmos SDK keys for object-capability +// generation in the IBC protocol as defined in https://github.com/cosmos/ics/tree/master/spec/ics-005-port-allocation#data-structures +type CapabilityKey StoreKey + // KVStoreKey is used for accessing substores. // Only the pointer value should ever be used - it functions as a capabilities key. type KVStoreKey struct { diff --git a/types/coin.go b/types/coin.go index 12d82a6e0..6a759d569 100644 --- a/types/coin.go +++ b/types/coin.go @@ -580,8 +580,8 @@ func (coins Coins) Sort() Coins { // Parsing var ( - // Denominations can be 3 ~ 16 characters long. - reDnmString = `[a-z][a-z0-9]{2,15}` + // Denominations can be 3 ~ 32 characters long. + reDnmString = `[a-z][a-z0-9/]{2,31}` reAmt = `[[:digit:]]+` reDecAmt = `[[:digit:]]*\.[[:digit:]]+` reSpc = `[[:space:]]*` diff --git a/types/rest/rest.go b/types/rest/rest.go index 89770c944..a58a56252 100644 --- a/types/rest/rest.go +++ b/types/rest/rest.go @@ -375,3 +375,14 @@ func ParseHTTPArgsWithLimit(r *http.Request, defaultLimit int) (tags []string, p func ParseHTTPArgs(r *http.Request) (tags []string, page, limit int, err error) { return ParseHTTPArgsWithLimit(r, DefaultLimit) } + +// ParseQueryParamBool parses the given param to a boolean. It returns false by +// default if the string is not parseable to bool. +func ParseQueryParamBool(r *http.Request, param string) bool { + valueStr := r.FormValue(param) + value := false + if ok, err := strconv.ParseBool(valueStr); err == nil { + value = ok + } + return value +} diff --git a/types/store.go b/types/store.go index ce890f2f6..6057526c8 100644 --- a/types/store.go +++ b/types/store.go @@ -81,6 +81,7 @@ const ( // nolint - reexport type ( StoreKey = types.StoreKey + CapabilityKey = types.CapabilityKey KVStoreKey = types.KVStoreKey TransientStoreKey = types.TransientStoreKey )