Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

R4R: Judge if trust-node predefined before create certifier add verification for getBlock, queryTx and getValidators #2210

Merged
merged 21 commits into from
Sep 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ref #2322

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this change?

Copy link
Author

@abelliumnt abelliumnt Sep 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added a comment about the reason above. When gaia-lite is launched, it will fetch the latest height as its trust basement. Then it can only verify blocks or transactions in later height.
Here gaia-lite is launched after a new block is created, please refer to the code. The wait is necessary. If the height of gaia node is zero, gaia-lite will be aborted for fetching block failure. So here gaia-lite will be start later and it will get block 2 as its trust basement. As a result, it can't verify block 1. As I clear here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, we should update gaia lite so that you can easily start it with a separate root-of-trust.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ref #2323

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
}

Copy link
Author

@abelliumnt abelliumnt Sep 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remove this because if rest-server is stared with trust-node option, then we can't verify tx proof even if the request requires proof.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK 👍

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