Skip to content

Commit

Permalink
Merge PR 2210: Judge if trust-node predefined before create certifier…
Browse files Browse the repository at this point in the history
… add verification for getBlock, queryTx and getValidators (cosmos#2210)

Replace trust-node option with distrust-node option, and add varification in getting blocks, transactions and validator sets.
  • Loading branch information
HaoyangLiu authored and cwgoes committed Sep 14, 2018
1 parent b5f8350 commit 7dc09d0
Show file tree
Hide file tree
Showing 14 changed files with 143 additions and 53 deletions.
1 change: 1 addition & 0 deletions Gopkg.lock

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

19 changes: 14 additions & 5 deletions client/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
tmlite "github.com/tendermint/tendermint/lite"
tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
rpcclient "github.com/tendermint/tendermint/rpc/client"
"os"
)

const ctxAccStoreName = "acc"
Expand Down Expand Up @@ -68,32 +69,40 @@ func NewCLIContext() CLIContext {
}

func createCertifier() tmlite.Certifier {
trustNodeDefined := viper.IsSet(client.FlagTrustNode)
if !trustNodeDefined {
return nil
}

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 ")
errMsg.WriteString("--chain-id ")
}
if home == "" {
errMsg.WriteString("home ")
errMsg.WriteString("--home ")
}
if nodeURI == "" {
errMsg.WriteString("node ")
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()))
fmt.Printf("must specify these options: %s when --trust-node is false\n", errMsg.String())
os.Exit(1)
}

certifier, err := tmliteProxy.GetCertifier(chainID, home, nodeURI)
if err != nil {
panic(err)
}

return certifier
}

Expand Down
8 changes: 8 additions & 0 deletions client/context/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,11 @@ func ErrInvalidAccount(addr sdk.AccAddress) error {
return errors.Errorf(`No account with address %s was found in the state.
Are you sure there has been a transaction involving it?`, addr)
}

// ErrVerifyCommit returns a common error reflecting that the blockchain commit at a given
// height can't be verified. The reason is that the base checkpoint of the certifier is
// newer than the given height
func ErrVerifyCommit(height int64) error {
return errors.Errorf(`The height of base truststore in gaia-lite is higher than height %d.
Can't verify blockchain proof at this height. Please set --trust-node to true and try again`, height)
}
28 changes: 19 additions & 9 deletions client/context/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (
"github.com/cosmos/cosmos-sdk/store"
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/lite"
tmliteErr "github.com/tendermint/tendermint/lite/errors"
tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
rpcclient "github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
Expand Down Expand Up @@ -310,7 +312,7 @@ 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
// Data from trusted node or subspace query doesn't need verification.
if ctx.TrustNode || !isQueryStoreWithProof(path) {
return resp.Value, nil
}
Expand All @@ -323,6 +325,17 @@ func (ctx CLIContext) query(path string, key cmn.HexBytes) (res []byte, err erro
return resp.Value, nil
}

// Certify verifies the consensus proof at given height
func (ctx CLIContext) Certify(height int64) (lite.Commit, error) {
check, err := tmliteProxy.GetCertifiedCommit(height, ctx.Client, ctx.Certifier)
if tmliteErr.IsCommitNotFoundErr(err) {
return lite.Commit{}, ErrVerifyCommit(height)
} else if err != nil {
return lite.Commit{}, err
}
return check, nil
}

// verifyProof perform response proof verification
// nolint: unparam
func (ctx CLIContext) verifyProof(path string, resp abci.ResponseQuery) error {
Expand All @@ -331,13 +344,8 @@ func (ctx CLIContext) verifyProof(path string, resp abci.ResponseQuery) error {
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)
commit, err := ctx.Certify(resp.Height + 1)
if err != nil {
return err
}
Expand All @@ -350,15 +358,17 @@ func (ctx CLIContext) verifyProof(path string, resp abci.ResponseQuery) error {
}

// Verify the substore commit hash against trusted appHash
substoreCommitHash, err := store.VerifyMultiStoreCommitInfo(multiStoreProof.StoreName,
multiStoreProof.StoreInfos, commit.Header.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
}

Expand Down
5 changes: 2 additions & 3 deletions client/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ var (
// GetCommands adds common flags to query commands
func GetCommands(cmds ...*cobra.Command) []*cobra.Command {
for _, c := range cmds {
// TODO: make this default false when we support proofs
c.Flags().Bool(FlagTrustNode, true, "Don't verify proofs for responses")
c.Flags().Bool(FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)")
c.Flags().Bool(FlagUseLedger, false, "Use a connected Ledger device")
c.Flags().String(FlagChainID, "", "Chain ID of tendermint node")
c.Flags().String(FlagNode, "tcp://localhost:26657", "<host>:<port> to tendermint rpc interface for this chain")
Expand All @@ -71,7 +70,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")
c.Flags().Bool(FlagTrustNode, true, "Trust connected full node (don't verify proofs for responses)")
c.Flags().Bool(FlagDryRun, false, "ignore the --gas flag and perform a simulation of a transaction, but don't broadcast it")
c.Flags().Bool(FlagGenerateOnly, false, "build an unsigned transaction and write it to STDOUT")
// --gas can accept integers and "simulate"
Expand Down
4 changes: 2 additions & 2 deletions client/lcd/lcd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func TestBlock(t *testing.T) {

// --

res, body = Request(t, port, "GET", "/blocks/1", nil)
res, body = Request(t, port, "GET", "/blocks/2", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)

err = codec.Cdc.UnmarshalJSON([]byte(body), &resultBlock)
Expand Down Expand Up @@ -210,7 +210,7 @@ func TestValidators(t *testing.T) {

// --

res, body = Request(t, port, "GET", "/validatorsets/1", nil)
res, body = Request(t, port, "GET", "/validatorsets/2", nil)
require.Equal(t, http.StatusOK, res.StatusCode, body)

err = cdc.UnmarshalJSON([]byte(body), &resultVals)
Expand Down
4 changes: 2 additions & 2 deletions client/lcd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command {

cmd.Flags().String(flagListenAddr, "tcp://localhost:1317", "The address for the server to listen on")
cmd.Flags().String(flagCORS, "", "Set the domains that can make CORS requests (* for all)")
cmd.Flags().String(client.FlagChainID, "", "The chain ID to connect to")
cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node")
cmd.Flags().String(client.FlagNode, "tcp://localhost:26657", "Address of the node to connect to")
cmd.Flags().Int(flagMaxOpenConnections, 1000, "The number of maximum open connections")
cmd.Flags().Bool(client.FlagTrustNode, false, "Whether trust connected full node")
cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)")

return cmd
}
Expand Down
4 changes: 4 additions & 0 deletions client/lcd/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress
// XXX: Need to set this so LCD knows the tendermint node address!
viper.Set(client.FlagNode, config.RPC.ListenAddress)
viper.Set(client.FlagChainID, genDoc.ChainID)
viper.Set(client.FlagTrustNode, false)
dir, err := ioutil.TempDir("", "lcd_test")
require.NoError(t, err)
viper.Set(cli.HomeFlag, dir)

node, err := startTM(config, logger, genDoc, privVal, app)
require.NoError(t, err)
Expand Down
22 changes: 20 additions & 2 deletions client/rpc/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/gorilla/mux"
"github.com/spf13/cobra"
tmliteProxy "github.com/tendermint/tendermint/lite/proxy"
)

//BlockCommand returns the verified block data for a given heights
Expand All @@ -21,8 +22,8 @@ func BlockCommand() *cobra.Command {
RunE: printBlock,
}
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
// TODO: change this to false when we can
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)")
cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node")
return cmd
}

Expand All @@ -41,6 +42,23 @@ func getBlock(cliCtx context.CLIContext, height *int64) ([]byte, error) {
return nil, err
}

if !cliCtx.TrustNode {
check, err := cliCtx.Certify(*height)
if err != nil {
return nil, err
}

err = tmliteProxy.ValidateBlockMeta(res.BlockMeta, check)
if err != nil {
return nil, err
}

err = tmliteProxy.ValidateBlock(res.Block, check)
if err != nil {
return nil, err
}
}

// TODO move maarshalling into cmd/rest functions
// output, err := tmcodec.MarshalJSON(res)
output, err := cdc.MarshalJSON(res)
Expand Down
17 changes: 15 additions & 2 deletions client/rpc/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import (
"github.com/gorilla/mux"
"github.com/spf13/cobra"

"bytes"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
tmTypes "github.com/tendermint/tendermint/types"
tmtypes "github.com/tendermint/tendermint/types"
)

Expand All @@ -25,8 +27,8 @@ func ValidatorCommand() *cobra.Command {
RunE: printValidators,
}
cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")
// TODO: change this to false when we can
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)")
cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node")
return cmd
}

Expand Down Expand Up @@ -70,6 +72,17 @@ func getValidators(cliCtx context.CLIContext, height *int64) ([]byte, error) {
return nil, err
}

if !cliCtx.TrustNode {
check, err := cliCtx.Certify(*height)
if err != nil {
return nil, err
}

if !bytes.Equal(check.ValidatorsHash(), tmTypes.NewValidatorSet(validatorsRes.Validators).Hash()) {
return nil, fmt.Errorf("got invalid validatorset")
}
}

outputValidatorsRes := ResultValidatorsOutput{
BlockHeight: validatorsRes.BlockHeight,
Validators: make([]ValidatorOutput, len(validatorsRes.Validators)),
Expand Down
46 changes: 28 additions & 18 deletions client/tx/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,11 @@ package tx
import (
"encoding/hex"
"fmt"
"net/http"
"strconv"

"github.com/tendermint/tendermint/libs/common"
"net/http"

"github.com/gorilla/mux"
"github.com/spf13/cobra"
"github.com/spf13/viper"
abci "github.com/tendermint/tendermint/abci/types"
ctypes "github.com/tendermint/tendermint/rpc/core/types"

Expand All @@ -30,11 +27,10 @@ func QueryTxCmd(cdc *codec.Codec) *cobra.Command {
RunE: func(cmd *cobra.Command, args []string) error {
// find the key to look up the account
hashHexStr := args[0]
trustNode := viper.GetBool(client.FlagTrustNode)

cliCtx := context.NewCLIContext().WithCodec(cdc)

output, err := queryTx(cdc, cliCtx, hashHexStr, trustNode)
output, err := queryTx(cdc, cliCtx, hashHexStr)
if err != nil {
return err
}
Expand All @@ -45,13 +41,12 @@ func QueryTxCmd(cdc *codec.Codec) *cobra.Command {
}

cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to")

// TODO: change this to false when we can
cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses")
cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)")
cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node")
return cmd
}

func queryTx(cdc *codec.Codec, cliCtx context.CLIContext, hashHexStr string, trustNode bool) ([]byte, error) {
func queryTx(cdc *codec.Codec, cliCtx context.CLIContext, hashHexStr string) ([]byte, error) {
hash, err := hex.DecodeString(hashHexStr)
if err != nil {
return nil, err
Expand All @@ -62,11 +57,18 @@ func queryTx(cdc *codec.Codec, cliCtx context.CLIContext, hashHexStr string, tru
return nil, err
}

res, err := node.Tx(hash, !trustNode)
res, err := node.Tx(hash, !cliCtx.TrustNode)
if err != nil {
return nil, err
}

if !cliCtx.TrustNode {
err := ValidateTxResult(cliCtx, res)
if err != nil {
return nil, err
}
}

info, err := formatTxResult(cdc, res)
if err != nil {
return nil, err
Expand All @@ -75,8 +77,21 @@ func queryTx(cdc *codec.Codec, cliCtx context.CLIContext, hashHexStr string, tru
return codec.MarshalJSONIndent(cdc, info)
}

// ValidateTxResult performs transaction verification
func ValidateTxResult(cliCtx context.CLIContext, res *ctypes.ResultTx) error {
check, err := cliCtx.Certify(res.Height)
if err != nil {
return err
}

err = res.Proof.Validate(check.Header.DataHash)
if err != nil {
return err
}
return nil
}

func formatTxResult(cdc *codec.Codec, res *ctypes.ResultTx) (Info, error) {
// TODO: verify the proof if requested
tx, err := parseTx(cdc, res.Tx)
if err != nil {
return Info{}, err
Expand Down Expand Up @@ -116,13 +131,8 @@ func QueryTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.H
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
hashHexStr := vars["hash"]
trustNode, err := strconv.ParseBool(r.FormValue("trust_node"))
// trustNode defaults to true
if err != nil {
trustNode = true
}

output, err := queryTx(cdc, cliCtx, hashHexStr, trustNode)
output, err := queryTx(cdc, cliCtx, hashHexStr)
if err != nil {
w.WriteHeader(500)
w.Write([]byte(err.Error()))
Expand Down
Loading

0 comments on commit 7dc09d0

Please sign in to comment.