From 9bf8f41ac93a64aef4fa0ff32456773bd86b4624 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 2 Mar 2020 10:51:36 +0400 Subject: [PATCH] lite2: fix tendermint lite sub command (#4505) * lite2: fix tendermint lite sub command - better logging - chainID as an argument - more examples * one more log msg * lite2: fire update right away after start * turn off auto update in verification tests Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- cmd/tendermint/commands/lite.go | 110 +++++++++++++----- docs/tendermint-core/light-client-protocol.md | 6 +- lite2/client.go | 11 +- lite2/client_test.go | 2 + 4 files changed, 94 insertions(+), 35 deletions(-) diff --git a/cmd/tendermint/commands/lite.go b/cmd/tendermint/commands/lite.go index 8477b46ed..2d1798920 100644 --- a/cmd/tendermint/commands/lite.go +++ b/cmd/tendermint/commands/lite.go @@ -2,6 +2,7 @@ package commands import ( "net/http" + "os" "strings" "time" @@ -11,6 +12,7 @@ import ( amino "github.com/tendermint/go-amino" dbm "github.com/tendermint/tm-db" + "github.com/tendermint/tendermint/libs/log" tmos "github.com/tendermint/tendermint/libs/os" lite "github.com/tendermint/tendermint/lite2" "github.com/tendermint/tendermint/lite2/provider" @@ -24,15 +26,29 @@ import ( // LiteCmd represents the base command when called without any subcommands var LiteCmd = &cobra.Command{ - Use: "lite", + Use: "lite [chainID]", Short: "Run a light client proxy server, verifying Tendermint rpc", Long: `Run a light client proxy server, verifying Tendermint rpc. All calls that can be tracked back to a block header by a proof will be verified before passing them back to the caller. Other than -that, it will present the same interface as a full Tendermint node.`, - RunE: runProxy, - SilenceUsage: true, +that, it will present the same interface as a full Tendermint node. + +Example: + +start a fresh instance: + +lite cosmoshub-3 -p 52.57.29.196:26657 -w public-seed-node.cosmoshub.certus.one:26657 + --height 962118 --hash 28B97BE9F6DE51AC69F70E0B7BFD7E5C9CD1A595B7DC31AFF27C50D4948020CD + +continue from latest state: + +lite cosmoshub-3 -p 52.57.29.196:26657 -w public-seed-node.cosmoshub.certus.one:26657 +`, + RunE: runProxy, + Args: cobra.ExactArgs(1), + Example: `lite cosmoshub-3 -p 52.57.29.196:26657 -w public-seed-node.cosmoshub.certus.one:26657 + --height 962118 --hash 28B97BE9F6DE51AC69F70E0B7BFD7E5C9CD1A595B7DC31AFF27C50D4948020CD`, } var ( @@ -46,15 +62,16 @@ var ( trustingPeriod time.Duration trustedHeight int64 trustedHash []byte + + verbose bool ) func init() { LiteCmd.Flags().StringVar(&listenAddr, "laddr", "tcp://localhost:8888", "Serve the proxy on the given address") - LiteCmd.Flags().StringVar(&chainID, "chain-id", "tendermint", "Specify the Tendermint chain ID") - LiteCmd.Flags().StringVar(&primaryAddr, "primary", "tcp://localhost:26657", + LiteCmd.Flags().StringVarP(&primaryAddr, "primary", "p", "", "Connect to a Tendermint node at this address") - LiteCmd.Flags().StringVar(&witnessesAddrs, "witnesses", "", + LiteCmd.Flags().StringVarP(&witnessesAddrs, "witnesses", "w", "", "Tendermint nodes to cross-check the primary node, comma-separated") LiteCmd.Flags().StringVar(&home, "home-dir", ".tendermint-lite", "Specify the home directory") LiteCmd.Flags().IntVar( @@ -64,21 +81,32 @@ func init() { "Maximum number of simultaneous connections (including WebSocket).") LiteCmd.Flags().DurationVar(&trustingPeriod, "trusting-period", 168*time.Hour, "Trusting period. Should be significantly less than the unbonding period") - LiteCmd.Flags().Int64Var(&trustedHeight, "trusted-height", 1, "Trusted header's height") - LiteCmd.Flags().BytesHexVar(&trustedHash, "trusted-hash", []byte{}, "Trusted header's hash") + LiteCmd.Flags().Int64Var(&trustedHeight, "height", 1, "Trusted header's height") + LiteCmd.Flags().BytesHexVar(&trustedHash, "hash", []byte{}, "Trusted header's hash") + LiteCmd.Flags().BoolVar(&verbose, "verbose", false, "Verbose output") } func runProxy(cmd *cobra.Command, args []string) error { - liteLogger := logger.With("module", "lite") + // Initialise logger. + logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + var option log.Option + if verbose { + option, _ = log.AllowLevel("debug") + } else { + option, _ = log.AllowLevel("info") + } + logger = log.NewFilter(logger, option) - logger.Info("Connecting to the primary node...") - rpcClient, err := rpcclient.NewHTTP(chainID, primaryAddr) + chainID = args[0] + + logger.Info("Creating HTTP client for primary...", "addr", primaryAddr) + rpcClient, err := rpcclient.NewHTTP(primaryAddr, "/websocket") if err != nil { return errors.Wrapf(err, "http client for %s", primaryAddr) } primary := httpp.NewWithClient(chainID, rpcClient) - logger.Info("Connecting to the witness nodes...") + // TODO: use NewNetClient once we have it addrs := strings.Split(witnessesAddrs, ",") witnesses := make([]provider.Provider, len(addrs)) for i, addr := range addrs { @@ -89,40 +117,60 @@ func runProxy(cmd *cobra.Command, args []string) error { witnesses[i] = p } - logger.Info("Creating client...") + logger.Info("Creating client...", "chainID", chainID) db, err := dbm.NewGoLevelDB("lite-client-db", home) if err != nil { return err } - c, err := lite.NewClient( - chainID, - lite.TrustOptions{ - Period: trustingPeriod, - Height: trustedHeight, - Hash: trustedHash, - }, - primary, - witnesses, - dbs.New(db, chainID), - lite.Logger(liteLogger), - ) - if err != nil { - return err + + var c *lite.Client + if trustedHeight > 0 && len(trustedHash) > 0 { // fresh installation + c, err = lite.NewClient( + chainID, + lite.TrustOptions{ + Period: trustingPeriod, + Height: trustedHeight, + Hash: trustedHash, + }, + primary, + witnesses, + dbs.New(db, chainID), + lite.Logger(logger), + ) + } else { // continue from latest state + c, err = lite.NewClientFromTrustedStore( + chainID, + trustingPeriod, + primary, + witnesses, + dbs.New(db, chainID), + lite.Logger(logger), + ) } + if err != nil { + return errors.Wrap(err, "failed to create") + } + logger.Info("Starting client...") + err = c.Start() + if err != nil { + return errors.Wrap(err, "failed to start") + } + defer c.Stop() + p := lproxy.Proxy{ Addr: listenAddr, Config: &rpcserver.Config{MaxOpenConnections: maxOpenConnections}, Codec: amino.NewCodec(), Client: lrpc.NewClient(rpcClient, c), - Logger: liteLogger, + Logger: logger, } // Stop upon receiving SIGTERM or CTRL-C. - tmos.TrapSignal(liteLogger, func() { + tmos.TrapSignal(logger, func() { p.Listener.Close() }) - logger.Info("Starting proxy...") + logger.Info("Starting proxy...", "laddr", listenAddr) if err := p.ListenAndServe(); err != http.ErrServerClosed { // Error starting or closing listener: logger.Error("proxy ListenAndServe", "err", err) diff --git a/docs/tendermint-core/light-client-protocol.md b/docs/tendermint-core/light-client-protocol.md index 4537412c1..41b7a0e07 100644 --- a/docs/tendermint-core/light-client-protocol.md +++ b/docs/tendermint-core/light-client-protocol.md @@ -55,9 +55,9 @@ passing them back to the caller. Other than that, it will present the same interface as a full Tendermint node. ```sh -$ tendermint lite --chain-id=supernova --primary=tcp://233.123.0.140:26657 \ - --witnesses=tcp://179.63.29.15:26657,tcp://144.165.223.135:26657 \ - --trusted-height=10 --trusted-hash=37E9A6DD3FA25E83B22C18835401E8E56088D0D7ABC6FD99FCDC920DD76C1C57 +$ tendermint lite supernova -p tcp://233.123.0.140:26657 \ + -w tcp://179.63.29.15:26657,tcp://144.165.223.135:26657 \ + --height=10 --hash=37E9A6DD3FA25E83B22C18835401E8E56088D0D7ABC6FD99FCDC920DD76C1C57 ``` For additional options, run `tendermint lite --help`. diff --git a/lite2/client.go b/lite2/client.go index b70123099..5272f1b3a 100644 --- a/lite2/client.go +++ b/lite2/client.go @@ -166,12 +166,14 @@ func NewClient( } if c.latestTrustedHeader != nil { + c.logger.Info("Checking trusted header using options") if err := c.checkTrustedHeaderUsingOptions(trustOptions); err != nil { return nil, err } } if c.latestTrustedHeader == nil || c.latestTrustedHeader.Height < trustOptions.Height { + c.logger.Info("Downloading trusted header using options") if err := c.initializeWithTrustOptions(trustOptions); err != nil { return nil, err } @@ -258,7 +260,7 @@ func (c *Client) restoreTrustedHeaderAndVals() error { c.latestTrustedHeader = trustedHeader c.latestTrustedVals = trustedVals - c.logger.Debug("Restored trusted header and vals", "height", lastHeight) + c.logger.Info("Restored trusted header and vals", "height", lastHeight) } return nil @@ -930,6 +932,11 @@ func (c *Client) removeWitness(idx int) { func (c *Client) autoUpdateRoutine() { defer c.routinesWaitGroup.Done() + err := c.Update(time.Now()) + if err != nil { + c.logger.Error("Error during auto update", "err", err) + } + ticker := time.NewTicker(c.updatePeriod) defer ticker.Stop() @@ -1013,6 +1020,7 @@ func (c *Client) signedHeaderFromPrimary(height int64) (*types.SignedHeader, err if err == provider.ErrSignedHeaderNotFound { return nil, err } + c.logger.Error("Failed to get signed header from primary", "attempt", attempt, "err", err) time.Sleep(backoffTimeout(attempt)) } @@ -1036,6 +1044,7 @@ func (c *Client) validatorSetFromPrimary(height int64) (*types.ValidatorSet, err if err == nil || err == provider.ErrValidatorSetNotFound { return vals, err } + c.logger.Error("Failed to get validator set from primary", "attempt", attempt, "err", err) time.Sleep(backoffTimeout(attempt)) } diff --git a/lite2/client_test.go b/lite2/client_test.go index b1079390a..383bd4c60 100644 --- a/lite2/client_test.go +++ b/lite2/client_test.go @@ -154,6 +154,7 @@ func TestClient_SequentialVerification(t *testing.T) { )}, dbs.New(dbm.NewMemDB(), chainID), SequentialVerification(), + UpdatePeriod(0), ) if tc.initErr { @@ -280,6 +281,7 @@ func TestClient_SkippingVerification(t *testing.T) { )}, dbs.New(dbm.NewMemDB(), chainID), SkippingVerification(DefaultTrustLevel), + UpdatePeriod(0), ) if tc.initErr { require.Error(t, err)