Skip to content

Commit

Permalink
Merge PR cosmos#2192: Split LCD implementation PR, part one
Browse files Browse the repository at this point in the history
  • Loading branch information
cwgoes authored Aug 31, 2018
2 parents ee0434e + 46172bb commit 03f79ef
Show file tree
Hide file tree
Showing 12 changed files with 387 additions and 36 deletions.
45 changes: 23 additions & 22 deletions Gopkg.lock

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

46 changes: 44 additions & 2 deletions client/context/context.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package context

import (
"io"

"bytes"
"fmt"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
"io"

"github.com/spf13/viper"

"github.com/tendermint/tendermint/libs/cli"
tmlite "github.com/tendermint/tendermint/lite"
tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
rpcclient "github.com/tendermint/tendermint/rpc/client"
)

Expand All @@ -32,6 +36,7 @@ type CLIContext struct {
Async bool
JSON bool
PrintResponse bool
Certifier tmlite.Certifier
}

// NewCLIContext returns a new initialized CLIContext with parameters from the
Expand All @@ -57,7 +62,38 @@ func NewCLIContext() CLIContext {
Async: viper.GetBool(client.FlagAsync),
JSON: viper.GetBool(client.FlagJson),
PrintResponse: viper.GetBool(client.FlagPrintResponse),
Certifier: createCertifier(),
}
}

func createCertifier() tmlite.Certifier {
trustNode := viper.GetBool(client.FlagTrustNode)
if trustNode {
return nil
}
chainID := viper.GetString(client.FlagChainID)
home := viper.GetString(cli.HomeFlag)
nodeURI := viper.GetString(client.FlagNode)

var errMsg bytes.Buffer
if chainID == "" {
errMsg.WriteString("chain-id ")
}
if home == "" {
errMsg.WriteString("home ")
}
if nodeURI == "" {
errMsg.WriteString("node ")
}
// errMsg is not empty
if errMsg.Len() != 0 {
panic(fmt.Errorf("can't create certifier for distrust mode, empty values from these options: %s", errMsg.String()))
}
certifier, err := tmliteProxy.GetCertifier(chainID, home, nodeURI)
if err != nil {
panic(err)
}
return certifier
}

// WithCodec returns a copy of the context with an updated codec.
Expand Down Expand Up @@ -117,3 +153,9 @@ func (ctx CLIContext) WithUseLedger(useLedger bool) CLIContext {
ctx.UseLedger = useLedger
return ctx
}

// WithCertifier - return a copy of the context with an updated Certifier
func (ctx CLIContext) WithCertifier(certifier tmlite.Certifier) CLIContext {
ctx.Certifier = certifier
return ctx
}
70 changes: 70 additions & 0 deletions client/context/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ import (

"github.com/pkg/errors"

"github.com/cosmos/cosmos-sdk/store"
"github.com/cosmos/cosmos-sdk/wire"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
rpcclient "github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"strings"
)

// GetNode returns an RPC client. If the context's client is not defined, an
Expand Down Expand Up @@ -304,12 +309,77 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, err erro
return res, errors.Errorf("query failed: (%d) %s", resp.Code, resp.Log)
}

// Data from trusted node or subspace query doesn't need verification
if ctx.TrustNode || !isQueryStoreWithProof(path) {
return resp.Value, nil
}

err = ctx.verifyProof(path, resp)
if err != nil {
return nil, err
}

return resp.Value, nil
}

// verifyProof perform response proof verification
func (ctx CLIContext) verifyProof(path string, resp abci.ResponseQuery) error {

if ctx.Certifier == nil {
return fmt.Errorf("missing valid certifier to verify data from untrusted node")
}

node, err := ctx.GetNode()
if err != nil {
return err
}

// AppHash for height H is in header H+1
commit, err := tmliteProxy.GetCertifiedCommit(resp.Height+1, node, ctx.Certifier)
if err != nil {
return err
}

var multiStoreProof store.MultiStoreProof
cdc := wire.NewCodec()
err = cdc.UnmarshalBinary(resp.Proof, &multiStoreProof)
if err != nil {
return errors.Wrap(err, "failed to unmarshalBinary rangeProof")
}

// Verify the substore commit hash against trusted appHash
substoreCommitHash, err := store.VerifyMultiStoreCommitInfo(multiStoreProof.StoreName,
multiStoreProof.StoreInfos, commit.Header.AppHash)
if err != nil {
return errors.Wrap(err, "failed in verifying the proof against appHash")
}
err = store.VerifyRangeProof(resp.Key, resp.Value, substoreCommitHash, &multiStoreProof.RangeProof)
if err != nil {
return errors.Wrap(err, "failed in the range proof verification")
}
return nil
}

// queryStore performs a query from a Tendermint node with the provided a store
// name and path.
func (ctx CLIContext) queryStore(key cmn.HexBytes, storeName, endPath string) ([]byte, error) {
path := fmt.Sprintf("/store/%s/%s", storeName, endPath)
return ctx.query(path, key)
}

// isQueryStoreWithProof expects a format like /<queryType>/<storeName>/<subpath>
// queryType can be app or store
func isQueryStoreWithProof(path string) bool {
if !strings.HasPrefix(path, "/") {
return false
}
paths := strings.SplitN(path[1:], "/", 3)
if len(paths) != 3 {
return false
}

if store.RequireProof("/" + paths[2]) {
return true
}
return false
}
1 change: 1 addition & 0 deletions client/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously")
c.Flags().Bool(FlagJson, false, "return output in json format")
c.Flags().Bool(FlagPrintResponse, true, "return tx response (only works with async = false)")
c.Flags().Bool(FlagTrustNode, true, "Don't verify proofs for query responses")
}
return cmds
}
Loading

0 comments on commit 03f79ef

Please sign in to comment.