From c33576e72ae876291cb73570b4498d807596d1e2 Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Sun, 1 Mar 2020 21:03:28 +0100 Subject: [PATCH 01/77] example/kvstore: return ABCI query height (#4509) * example/kvstore: return ABCI query height * CHANGELOG: added PR link * Updated tests --- CHANGELOG_PENDING.md | 5 +++-- abci/example/kvstore/kvstore.go | 2 ++ abci/example/kvstore/kvstore_test.go | 20 ++++++++++++++++++++ abci/tests/test_cli/ex1.abci.out | 4 ++-- docs/app-dev/abci-cli.md | 4 ++-- 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 8877d31ec..f85b63307 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -4,8 +4,7 @@ Special thanks to external contributors on this release: -Friendly reminder, we have a [bug bounty -program](https://hackerone.com/tendermint). +Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint). ### BREAKING CHANGES: @@ -21,6 +20,8 @@ program](https://hackerone.com/tendermint). - [types] [\#4417](https://github.com/tendermint/tendermint/issues/4417) VerifyCommitX() functions should return as soon as +2/3 threashold is reached. +- [examples/kvstore] [\#4509](https://github.com/tendermint/tendermint/pull/4509) ABCI query now returns the proper height (@erikgrinaker) + ### BUG FIXES: - [rpc] [\#4493](https://github.com/tendermint/tendermint/pull/4493) Keep the original subscription "id" field when new RPCs come in (@michaelfig) diff --git a/abci/example/kvstore/kvstore.go b/abci/example/kvstore/kvstore.go index f856519f3..4e7449938 100644 --- a/abci/example/kvstore/kvstore.go +++ b/abci/example/kvstore/kvstore.go @@ -136,6 +136,7 @@ func (app *Application) Query(reqQuery types.RequestQuery) (resQuery types.Respo resQuery.Index = -1 // TODO make Proof return index resQuery.Key = reqQuery.Data resQuery.Value = value + resQuery.Height = app.state.Height return } @@ -151,6 +152,7 @@ func (app *Application) Query(reqQuery types.RequestQuery) (resQuery types.Respo resQuery.Log = "exists" } resQuery.Value = value + resQuery.Height = app.state.Height return resQuery } diff --git a/abci/example/kvstore/kvstore_test.go b/abci/example/kvstore/kvstore_test.go index e8dee67d9..4d8c829ad 100644 --- a/abci/example/kvstore/kvstore_test.go +++ b/abci/example/kvstore/kvstore_test.go @@ -30,6 +30,11 @@ func testKVStore(t *testing.T, app types.Application, tx []byte, key, value stri // repeating tx doesn't raise error ar = app.DeliverTx(req) require.False(t, ar.IsErr(), ar) + // commit + app.Commit() + + info := app.Info(types.RequestInfo{}) + require.NotZero(t, info.LastBlockHeight) // make sure query is fine resQuery := app.Query(types.RequestQuery{ @@ -37,7 +42,9 @@ func testKVStore(t *testing.T, app types.Application, tx []byte, key, value stri Data: []byte(key), }) require.Equal(t, code.CodeTypeOK, resQuery.Code) + require.Equal(t, key, string(resQuery.Key)) require.Equal(t, value, string(resQuery.Value)) + require.EqualValues(t, info.LastBlockHeight, resQuery.Height) // make sure proof is fine resQuery = app.Query(types.RequestQuery{ @@ -46,7 +53,9 @@ func testKVStore(t *testing.T, app types.Application, tx []byte, key, value stri Prove: true, }) require.EqualValues(t, code.CodeTypeOK, resQuery.Code) + require.Equal(t, key, string(resQuery.Key)) require.Equal(t, value, string(resQuery.Value)) + require.EqualValues(t, info.LastBlockHeight, resQuery.Height) } func TestKVStoreKV(t *testing.T) { @@ -300,6 +309,13 @@ func testClient(t *testing.T, app abcicli.Client, tx []byte, key, value string) ar, err = app.DeliverTxSync(types.RequestDeliverTx{Tx: tx}) require.NoError(t, err) require.False(t, ar.IsErr(), ar) + // commit + _, err = app.CommitSync() + require.NoError(t, err) + + info, err := app.InfoSync(types.RequestInfo{}) + require.NoError(t, err) + require.NotZero(t, info.LastBlockHeight) // make sure query is fine resQuery, err := app.QuerySync(types.RequestQuery{ @@ -308,7 +324,9 @@ func testClient(t *testing.T, app abcicli.Client, tx []byte, key, value string) }) require.Nil(t, err) require.Equal(t, code.CodeTypeOK, resQuery.Code) + require.Equal(t, key, string(resQuery.Key)) require.Equal(t, value, string(resQuery.Value)) + require.EqualValues(t, info.LastBlockHeight, resQuery.Height) // make sure proof is fine resQuery, err = app.QuerySync(types.RequestQuery{ @@ -318,5 +336,7 @@ func testClient(t *testing.T, app abcicli.Client, tx []byte, key, value string) }) require.Nil(t, err) require.Equal(t, code.CodeTypeOK, resQuery.Code) + require.Equal(t, key, string(resQuery.Key)) require.Equal(t, value, string(resQuery.Value)) + require.EqualValues(t, info.LastBlockHeight, resQuery.Height) } diff --git a/abci/tests/test_cli/ex1.abci.out b/abci/tests/test_cli/ex1.abci.out index 0cdd43df6..9e702b5ce 100644 --- a/abci/tests/test_cli/ex1.abci.out +++ b/abci/tests/test_cli/ex1.abci.out @@ -27,7 +27,7 @@ > query "abc" -> code: OK -> log: exists --> height: 0 +-> height: 2 -> key: abc -> key.hex: 616263 -> value: abc @@ -43,7 +43,7 @@ > query "def" -> code: OK -> log: exists --> height: 0 +-> height: 3 -> key: def -> key.hex: 646566 -> value: xyz diff --git a/docs/app-dev/abci-cli.md b/docs/app-dev/abci-cli.md index a72dc220f..e747e6d48 100644 --- a/docs/app-dev/abci-cli.md +++ b/docs/app-dev/abci-cli.md @@ -192,7 +192,7 @@ Try running these commands: > query "abc" -> code: OK -> log: exists --> height: 0 +-> height: 2 -> value: abc -> value.hex: 616263 @@ -206,7 +206,7 @@ Try running these commands: > query "def" -> code: OK -> log: exists --> height: 0 +-> height: 3 -> value: xyz -> value.hex: 78797A ``` From 9bf8f41ac93a64aef4fa0ff32456773bd86b4624 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 2 Mar 2020 10:51:36 +0400 Subject: [PATCH 02/77] 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) From 67224fd174eba42cb3efedea9474cb96bdbeab77 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 2 Mar 2020 17:09:55 +0400 Subject: [PATCH 03/77] docs: `tendermint node --help` dumps all supported flags (#4511) Closes #4211 --- docs/tendermint-core/using-tendermint.md | 33 ++++++++++++------------ 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/docs/tendermint-core/using-tendermint.md b/docs/tendermint-core/using-tendermint.md index 2c6370e84..b1098f54f 100644 --- a/docs/tendermint-core/using-tendermint.md +++ b/docs/tendermint-core/using-tendermint.md @@ -104,36 +104,37 @@ definition](https://github.com/tendermint/tendermint/blob/master/types/genesis.g ## Run -To run a Tendermint node, use +To run a Tendermint node, use: -``` +```sh tendermint node ``` By default, Tendermint will try to connect to an ABCI application on -[127.0.0.1:26658](127.0.0.1:26658). If you have the `kvstore` ABCI app -installed, run it in another window. If you don't, kill Tendermint and -run an in-process version of the `kvstore` app: +`127.0.0.1:26658`. If you have the `kvstore` ABCI app installed, run it in +another window. If you don't, kill Tendermint and run an in-process version of +the `kvstore` app: -``` +```sh tendermint node --proxy_app=kvstore ``` -After a few seconds you should see blocks start streaming in. Note that -blocks are produced regularly, even if there are no transactions. See -_No Empty Blocks_, below, to modify this setting. +After a few seconds, you should see blocks start streaming in. Note that blocks +are produced regularly, even if there are no transactions. See _No Empty +Blocks_, below, to modify this setting. -Tendermint supports in-process versions of the `counter`, `kvstore` and -`noop` apps that ship as examples with `abci-cli`. It's easy to compile -your own app in-process with Tendermint if it's written in Go. If your -app is not written in Go, simply run it in another process, and use the -`--proxy_app` flag to specify the address of the socket it is listening -on, for instance: +Tendermint supports in-process versions of the `counter`, `kvstore`, and `noop` +apps that ship as examples with `abci-cli`. It's easy to compile your app +in-process with Tendermint if it's written in Go. If your app is not written in +Go, run it in another process, and use the `--proxy_app` flag to specify the +address of the socket it is listening on, for instance: -``` +```sh tendermint node --proxy_app=/var/run/abci.sock ``` +You can find out what flags are supported by running `tendermint node --help`. + ## Transactions To send a transaction, use `curl` to make requests to the Tendermint RPC From 51e1b4e42acbb410265efd409d29ffecb4049e93 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 2 Mar 2020 17:55:50 +0400 Subject: [PATCH 04/77] rpc: remove BlockStoreRPC in favor of BlockStore (#4510) * rpc: use BlockStoreRPC instead of BlockStore BlockStoreRPC is a limited version of BlockStore interface, which does not include SaveBlock method. Closes #4159 * remove BlockStoreRPC interface in favor of single BlockStore interface Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- state/services.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/state/services.go b/state/services.go index bf9942811..d83a410c9 100644 --- a/state/services.go +++ b/state/services.go @@ -12,12 +12,15 @@ import ( //------------------------------------------------------ // blockstore -// BlockStoreRPC is the block store interface used by the RPC. -type BlockStoreRPC interface { +// BlockStore defines the interface used by the ConsensusState. +type BlockStore interface { Height() int64 LoadBlockMeta(height int64) *types.BlockMeta LoadBlock(height int64) *types.Block + + SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) + LoadBlockByHash(hash []byte) *types.Block LoadBlockPart(height int64, index int) *types.Part @@ -25,13 +28,7 @@ type BlockStoreRPC interface { LoadSeenCommit(height int64) *types.Commit } -// BlockStore defines the BlockStore interface used by the ConsensusState. -type BlockStore interface { - BlockStoreRPC - SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) -} - -//----------------------------------------------------------------------------------------------------- +//----------------------------------------------------------------------------- // evidence pool // EvidencePool defines the EvidencePool interface used by the ConsensusState. From 3b2e1f22e4484092be26aa9a3911a710c270c981 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 2 Mar 2020 19:59:38 +0400 Subject: [PATCH 05/77] cmd: show useful error when tm not initialised (#4512) Closes #4303 Before: ``` TMHOME="/Users/daniel/go/src/github.com/tendermint/thome" tendermint node --proxy_app=unix:///Users/daniel/go/src/github.com/kvstore/example.sock ERROR: 1 error(s) decoding: * error decoding 'consensus.create_empty_blocks_interval': time: invalid duration ``` After: ``` $ TMHOME=/tmp ./build/tendermint node --proxy_app=kvstore ERROR: failed to create node: Couldn't read GenesisDoc file: open /tmp/config/genesis.json: no such file or directory ``` --- cmd/tendermint/commands/run_node.go | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index 6d9f575d0..628a0d173 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -78,7 +78,7 @@ func AddNodeFlags(cmd *cobra.Command) { "Set this to false to only produce blocks when there are txs or when the AppHash changes") cmd.Flags().String( "consensus.create_empty_blocks_interval", - string(config.Consensus.CreateEmptyBlocksInterval), + config.Consensus.CreateEmptyBlocksInterval.String(), "The possible interval between empty blocks") // db flags @@ -99,11 +99,21 @@ func NewRunNodeCmd(nodeProvider nm.Provider) *cobra.Command { Use: "node", Short: "Run the tendermint node", RunE: func(cmd *cobra.Command, args []string) error { + if err := checkGenesisHash(config); err != nil { + return err + } + n, err := nodeProvider(config, logger) if err != nil { - return fmt.Errorf("failed to create node: %v", err) + return fmt.Errorf("failed to create node: %w", err) } + if err := n.Start(); err != nil { + return fmt.Errorf("failed to start node: %w", err) + } + + logger.Info("Started node", "nodeInfo", n.Switch().NodeInfo()) + // Stop upon receiving SIGTERM or CTRL-C. tmos.TrapSignal(logger, func() { if n.IsRunning() { @@ -111,15 +121,6 @@ func NewRunNodeCmd(nodeProvider nm.Provider) *cobra.Command { } }) - if err := checkGenesisHash(config); err != nil { - return err - } - - if err := n.Start(); err != nil { - return fmt.Errorf("failed to start node: %v", err) - } - logger.Info("Started node", "nodeInfo", n.Switch().NodeInfo()) - // Run forever. select {} }, From a60d032b07fd6f5ee5687b0f7d206fb4ab16228d Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 3 Mar 2020 12:37:29 +0400 Subject: [PATCH 06/77] docs: write about debug kill and dump (#4516) * docs: write about debug kill and dump Closes #4325 * wrap file tree in code blocks --- docs/tendermint-core/running-in-production.md | 62 +++++++++---------- docs/tools/README.md | 13 ++-- docs/tools/debugging.md | 57 +++++++++++++++++ 3 files changed, 94 insertions(+), 38 deletions(-) create mode 100644 docs/tools/debugging.md diff --git a/docs/tendermint-core/running-in-production.md b/docs/tendermint-core/running-in-production.md index 7a436ec95..02a0eae58 100644 --- a/docs/tendermint-core/running-in-production.md +++ b/docs/tendermint-core/running-in-production.md @@ -111,54 +111,44 @@ to achieve the same things. ## Debugging Tendermint -If you ever have to debug Tendermint, the first thing you should -probably do is to check out the logs. See [How to read -logs](./how-to-read-logs.md), where we explain what certain log -statements mean. +If you ever have to debug Tendermint, the first thing you should probably do is +check out the logs. See [How to read logs](./how-to-read-logs.md), where we +explain what certain log statements mean. -If, after skimming through the logs, things are not clear still, the -next thing to try is query the /status RPC endpoint. It provides the -necessary info: whenever the node is syncing or not, what height it is -on, etc. +If, after skimming through the logs, things are not clear still, the next thing +to try is querying the `/status` RPC endpoint. It provides the necessary info: +whenever the node is syncing or not, what height it is on, etc. -``` +```sh curl http(s)://{ip}:{rpcPort}/status ``` -`dump_consensus_state` will give you a detailed overview of the -consensus state (proposer, lastest validators, peers states). From it, -you should be able to figure out why, for example, the network had -halted. +`/dump_consensus_state` will give you a detailed overview of the consensus +state (proposer, latest validators, peers states). From it, you should be able +to figure out why, for example, the network had halted. -``` +```sh curl http(s)://{ip}:{rpcPort}/dump_consensus_state ``` -There is a reduced version of this endpoint - `consensus_state`, which -returns just the votes seen at the current height. +There is a reduced version of this endpoint - `/consensus_state`, which returns +just the votes seen at the current height. -- [Github Issues](https://github.com/tendermint/tendermint/issues) -- [StackOverflow - questions](https://stackoverflow.com/questions/tagged/tendermint) +If, after consulting with the logs and above endpoints, you still have no idea +what's happening, consider using `tendermint debug kill` sub-command. This +command will scrap all the available info and kill the process. See +[Debugging](../tools/debugging.md) for the exact format. -### Debug Utility - -Tendermint also ships with a `debug` sub-command that allows you to kill a live -Tendermint process while collecting useful information in a compressed archive -such as the configuration used, consensus state, network state, the node' status, -the WAL, and even the stacktrace of the process before exit. These files can be -useful to examine when debugging a faulty Tendermint process. - -In addition, the `debug` sub-command also allows you to dump debugging data into -compressed archives at a regular interval. These archives contain the goroutine -and heap profiles in addition to the consensus state, network info, node status, -and even the WAL. +You can inspect the resulting archive yourself or create an issue on +[Github](https://github.com/tendermint/tendermint). Before opening an issue +however, be sure to check if there's [no existing +issue](https://github.com/tendermint/tendermint/issues) already. ## Monitoring Tendermint -Each Tendermint instance has a standard `/health` RPC endpoint, which -responds with 200 (OK) if everything is fine and 500 (or no response) - -if something is wrong. +Each Tendermint instance has a standard `/health` RPC endpoint, which responds +with 200 (OK) if everything is fine and 500 (or no response) - if something is +wrong. Other useful endpoints include mentioned earlier `/status`, `/net_info` and `/validators`. @@ -166,6 +156,10 @@ Other useful endpoints include mentioned earlier `/status`, `/net_info` and Tendermint also can report and serve Prometheus metrics. See [Metrics](./metrics.md). +`tendermint debug dump` sub-command can be used to periodically dump useful +information into an archive. See [Debugging](../tools/debugging.md) for more +information. + ## What happens when my app dies? You are supposed to run Tendermint under a [process diff --git a/docs/tools/README.md b/docs/tools/README.md index c326cde5b..bf9dd1f97 100644 --- a/docs/tools/README.md +++ b/docs/tools/README.md @@ -9,16 +9,21 @@ parent: Tendermint has some tools that are associated with it for: +- [Debugging](./debugging.md) - [Benchmarking](#benchmarking) -- [Validation of remote signers](./remote-signer-validation.md) - [Testnets](#testnets) - +- [Validation of remote signers](./remote-signer-validation.md) ## Benchmarking -Benchmarking is done with tm-load-test, for information on how to use the tool please visit the docs: https://github.com/interchainio/tm-load-test +- https://github.com/interchainio/tm-load-test +`tm-load-test` is a distributed load testing tool (and framework) for load +testing Tendermint networks. ## Testnets -The testnets tool is aimed at testing Tendermint with different configurations. For more information please visit: https://github.com/interchainio/testnets. +- https://github.com/interchainio/testnets + +This repository contains various different configurations of test networks for, +and relating to, Tendermint. diff --git a/docs/tools/debugging.md b/docs/tools/debugging.md new file mode 100644 index 000000000..50961dd3b --- /dev/null +++ b/docs/tools/debugging.md @@ -0,0 +1,57 @@ +# Debugging + +## tendermint debug kill + +Tendermint comes with a `debug` sub-command that allows you to kill a live +Tendermint process while collecting useful information in a compressed archive. +The information includes the configuration used, consensus state, network +state, the node' status, the WAL, and even the stack trace of the process +before exit. These files can be useful to examine when debugging a faulty +Tendermint process. + +```sh +tendermint debug kill --home= +``` + +will write debug info into a compressed archive. The archive will contain the +following: + +``` +├── config.toml +├── consensus_state.json +├── net_info.json +├── stacktrace.out +├── status.json +└── wal +``` + +Under the hood, `debug kill` fetches info from `/status`, `/net_info`, and +`/dump_consensus_state` HTTP endpoints, and kills the process with `-6`, which +catches the go-routine dump. + +## tendermint debug dump + +Also, the `debug dump` sub-command allows you to dump debugging data into +compressed archives at a regular interval. These archives contain the goroutine +and heap profiles in addition to the consensus state, network info, node +status, and even the WAL. + +```sh +tendermint debug dump --home= +``` + +will perform similarly to `kill` except it only polls the node and +dumps debugging data every frequency seconds to a compressed archive under a +given destination directory. Each archive will contain: + +``` +├── consensus_state.json +├── goroutine.out +├── heap.out +├── net_info.json +├── status.json +└── wal +``` + +Note: goroutine.out and heap.out will only be written if a profile address is +provided and is operational. This command is blocking and will log any error. From c518a062a7b39fe8427b47270bd71e34cb480576 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2020 15:52:36 +0400 Subject: [PATCH 07/77] deps: bump github.com/Workiva/go-datastructures (#4519) Bumps [github.com/Workiva/go-datastructures](https://github.com/Workiva/go-datastructures) from 1.0.50 to 1.0.51. - [Release notes](https://github.com/Workiva/go-datastructures/releases) - [Commits](https://github.com/Workiva/go-datastructures/compare/v1.0.50...v1.0.51) Signed-off-by: dependabot-preview[bot] --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index dd2b444fc..817b13cab 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.13 require ( github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f - github.com/Workiva/go-datastructures v1.0.50 + github.com/Workiva/go-datastructures v1.0.51 github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a github.com/fortytw2/leaktest v1.3.0 diff --git a/go.sum b/go.sum index c8eb10010..3833da4ca 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrd github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/Workiva/go-datastructures v1.0.50 h1:slDmfW6KCHcC7U+LP3DDBbm4fqTwZGn1beOFPfGaLvo= github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= +github.com/Workiva/go-datastructures v1.0.51 h1:LJHjjfcv+1gH+1D1SgrjcrF8iSZkgsAdCjclvHvVecQ= +github.com/Workiva/go-datastructures v1.0.51/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= From ff786515b8cc56dae299326040dd1bd65c829fbf Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Tue, 3 Mar 2020 13:00:06 +0100 Subject: [PATCH 08/77] lite: add helper functions for initiating the light client (#4486) * add new net client * hijack example test * lint fixes * reorganised file structure * renamed funcs and added documentation * implemented suggested changes * restored example tests * edited comments * Update lite2/setup.go Co-Authored-By: Bot from GolangCI <42910462+golangcibot@users.noreply.github.com> * Update lite2/client.go * lite cmds use HTTP client * better naming * Delete go.sum * Delete go.mod * restore go mod * restore go.sum * fix double import Co-authored-by: Anton Kaliaev Co-authored-by: Bot from GolangCI <42910462+golangcibot@users.noreply.github.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- cmd/tendermint/commands/lite.go | 46 +++++++------------- lite2/client.go | 7 +-- lite2/example_test.go | 2 +- lite2/setup.go | 77 +++++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+), 36 deletions(-) create mode 100644 lite2/setup.go diff --git a/cmd/tendermint/commands/lite.go b/cmd/tendermint/commands/lite.go index 2d1798920..447c6d92b 100644 --- a/cmd/tendermint/commands/lite.go +++ b/cmd/tendermint/commands/lite.go @@ -9,14 +9,12 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" - amino "github.com/tendermint/go-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" - httpp "github.com/tendermint/tendermint/lite2/provider/http" lproxy "github.com/tendermint/tendermint/lite2/proxy" lrpc "github.com/tendermint/tendermint/lite2/rpc" dbs "github.com/tendermint/tendermint/lite2/store/db" @@ -54,9 +52,9 @@ lite cosmoshub-3 -p 52.57.29.196:26657 -w public-seed-node.cosmoshub.certus.one: var ( listenAddr string primaryAddr string + witnessAddrsJoined string chainID string home string - witnessesAddrs string maxOpenConnections int trustingPeriod time.Duration @@ -71,7 +69,7 @@ func init() { "Serve the proxy on the given address") LiteCmd.Flags().StringVarP(&primaryAddr, "primary", "p", "", "Connect to a Tendermint node at this address") - LiteCmd.Flags().StringVarP(&witnessesAddrs, "witnesses", "w", "", + LiteCmd.Flags().StringVarP(&witnessAddrsJoined, "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( @@ -98,26 +96,10 @@ func runProxy(cmd *cobra.Command, args []string) error { logger = log.NewFilter(logger, option) 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) - - // TODO: use NewNetClient once we have it - addrs := strings.Split(witnessesAddrs, ",") - witnesses := make([]provider.Provider, len(addrs)) - for i, addr := range addrs { - p, err := httpp.New(chainID, addr) - if err != nil { - return errors.Wrapf(err, "http provider for %s", addr) - } - witnesses[i] = p - } - logger.Info("Creating client...", "chainID", chainID) + + witnessesAddrs := strings.Split(witnessAddrsJoined, ",") + db, err := dbm.NewGoLevelDB("lite-client-db", home) if err != nil { return err @@ -125,24 +107,24 @@ func runProxy(cmd *cobra.Command, args []string) error { var c *lite.Client if trustedHeight > 0 && len(trustedHash) > 0 { // fresh installation - c, err = lite.NewClient( + c, err = lite.NewHTTPClient( chainID, lite.TrustOptions{ Period: trustingPeriod, Height: trustedHeight, Hash: trustedHash, }, - primary, - witnesses, + primaryAddr, + witnessesAddrs, dbs.New(db, chainID), lite.Logger(logger), ) } else { // continue from latest state - c, err = lite.NewClientFromTrustedStore( + c, err = lite.NewHTTPClientFromTrustedStore( chainID, trustingPeriod, - primary, - witnesses, + primaryAddr, + witnessesAddrs, dbs.New(db, chainID), lite.Logger(logger), ) @@ -158,6 +140,10 @@ func runProxy(cmd *cobra.Command, args []string) error { } defer c.Stop() + rpcClient, err := rpcclient.NewHTTP(primaryAddr, "/websocket") + if err != nil { + return errors.Wrapf(err, "http client for %s", primaryAddr) + } p := lproxy.Proxy{ Addr: listenAddr, Config: &rpcserver.Config{MaxOpenConnections: maxOpenConnections}, diff --git a/lite2/client.go b/lite2/client.go index 5272f1b3a..d65ed8ddb 100644 --- a/lite2/client.go +++ b/lite2/client.go @@ -953,11 +953,8 @@ func (c *Client) autoUpdateRoutine() { } } -// Update attempts to advance the state making exponential steps (note: -// when SequentialVerification is being used, the client will still be -// downloading all intermediate headers). -// -// Exposed for testing. +// Update attempts to advance the state by downloading the latest header and +// comparing it with the existing one. func (c *Client) Update(now time.Time) error { lastTrustedHeight, err := c.LastTrustedHeight() if err != nil { diff --git a/lite2/example_test.go b/lite2/example_test.go index 65997f906..e6839386f 100644 --- a/lite2/example_test.go +++ b/lite2/example_test.go @@ -135,7 +135,7 @@ func ExampleClient_VerifyHeaderAtHeight() { Hash: header.Hash(), }, primary, - []provider.Provider{primary}, // TODO: primary should not be used here + []provider.Provider{primary}, // NOTE: primary should not be used here dbs.New(db, chainID), UpdatePeriod(0), // Logger(log.TestingLogger()), diff --git a/lite2/setup.go b/lite2/setup.go new file mode 100644 index 000000000..50a4a9d21 --- /dev/null +++ b/lite2/setup.go @@ -0,0 +1,77 @@ +package lite + +import ( + "time" + + "github.com/tendermint/tendermint/lite2/provider" + "github.com/tendermint/tendermint/lite2/provider/http" + "github.com/tendermint/tendermint/lite2/store" +) + +// NewHTTPClient initiates an instance of a lite client using HTTP addresses +// for both the primary provider and witnesses of the lite client. A trusted +// header and hash must be passed to initialize the client. +// +// See all Option(s) for the additional configuration. +// See NewClient. +func NewHTTPClient( + chainID string, + trustOptions TrustOptions, + primaryAddress string, + witnessesAddresses []string, + trustedStore store.Store, + options ...Option) (*Client, error) { + + providers, err := providersFromAddresses(append(witnessesAddresses, primaryAddress), chainID) + if err != nil { + return nil, err + } + + return NewClient( + chainID, + trustOptions, + providers[len(providers)-1], + providers[:len(providers)-1], + trustedStore, + options...) +} + +// NewHTTPClientFromTrustedStore initiates an instance of a lite client using +// HTTP addresses for both the primary provider and witnesses and uses a +// trusted store as the root of trust. +// +// See all Option(s) for the additional configuration. +// See NewClientFromTrustedStore. +func NewHTTPClientFromTrustedStore( + chainID string, + trustingPeriod time.Duration, + primaryAddress string, + witnessesAddresses []string, + trustedStore store.Store, + options ...Option) (*Client, error) { + + providers, err := providersFromAddresses(append(witnessesAddresses, primaryAddress), chainID) + if err != nil { + return nil, err + } + + return NewClientFromTrustedStore( + chainID, + trustingPeriod, + providers[len(providers)-1], + providers[:len(providers)-1], + trustedStore, + options...) +} + +func providersFromAddresses(addrs []string, chainID string) ([]provider.Provider, error) { + providers := make([]provider.Provider, len(addrs)) + for idx, address := range addrs { + p, err := http.New(chainID, address) + if err != nil { + return nil, err + } + providers[idx] = p + } + return providers, nil +} From 891afc22352585697fb7baa6e448235bce4bfdb1 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 3 Mar 2020 16:41:59 +0400 Subject: [PATCH 09/77] cmd: fix debug kill and change debug dump archive filename format (#4517) * cmd: change archive filename format before: Mar 3 08:44:43.zip after: 2020-03-03T08:44:43Z.zip Refs #4515 * cmd: kill pid, not the debug process itself Fixes #4515 * update changelog Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- CHANGELOG_PENDING.md | 5 ++--- cmd/tendermint/commands/debug/dump.go | 2 +- cmd/tendermint/commands/debug/kill.go | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index f85b63307..47ff5dfb1 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -19,13 +19,12 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi ### IMPROVEMENTS: - [types] [\#4417](https://github.com/tendermint/tendermint/issues/4417) VerifyCommitX() functions should return as soon as +2/3 threashold is reached. - - [examples/kvstore] [\#4509](https://github.com/tendermint/tendermint/pull/4509) ABCI query now returns the proper height (@erikgrinaker) +- [cmd] \#4515 Change `tendermint debug dump` sub-command archives filename's format (@melekes) ### BUG FIXES: - [rpc] [\#4493](https://github.com/tendermint/tendermint/pull/4493) Keep the original subscription "id" field when new RPCs come in (@michaelfig) - - [rpc] [\#4437](https://github.com/tendermint/tendermint/pull/4437) Fix tx_search pagination with ordered results (@erikgrinaker) - - [rpc] [\#4406](https://github.com/tendermint/tendermint/pull/4406) Fix issue with multiple subscriptions on the websocket (@antho1404) +- [cmd] \#4515 Fix `tendermint debug kill` sub-command (@melekes) diff --git a/cmd/tendermint/commands/debug/dump.go b/cmd/tendermint/commands/debug/dump.go index 80ca15c6b..33cb3e24d 100644 --- a/cmd/tendermint/commands/debug/dump.go +++ b/cmd/tendermint/commands/debug/dump.go @@ -126,7 +126,7 @@ func dumpDebugData(outDir string, conf *cfg.Config, rpc *rpcclient.HTTP) { } } - outFile := filepath.Join(outDir, fmt.Sprintf("%s.zip", start.Format(time.Stamp))) + outFile := filepath.Join(outDir, fmt.Sprintf("%s.zip", start.Format(time.RFC3339))) if err := zipDir(tmpDir, outFile); err != nil { logger.Error("failed to create and compress archive", "file", outFile, "error", err) } diff --git a/cmd/tendermint/commands/debug/kill.go b/cmd/tendermint/commands/debug/kill.go index 52defc69c..8d9df1161 100644 --- a/cmd/tendermint/commands/debug/kill.go +++ b/cmd/tendermint/commands/debug/kill.go @@ -124,7 +124,7 @@ func killProc(pid uint64, dir string) error { go func() { // Killing the Tendermint process with the '-ABRT|-6' signal will result in // a goroutine stacktrace. - p, err := os.FindProcess(os.Getpid()) + p, err := os.FindProcess(int(pid)) if err != nil { fmt.Fprintf(os.Stderr, "failed to find PID to kill Tendermint process: %s", err) } else if err = p.Signal(syscall.SIGABRT); err != nil { From 61380ffa3f43a88ae721fc205799d62c9c8dbcfc Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 4 Mar 2020 11:58:45 +0000 Subject: [PATCH 10/77] build(deps): bump github.com/prometheus/client_golang (#4525) Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.4.1 to 1.5.0. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/master/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.4.1...v1.5.0) Signed-off-by: dependabot-preview[bot] --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 817b13cab..5af1190cf 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/libp2p/go-buffer-pool v0.0.2 github.com/magiconair/properties v1.8.1 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.4.1 + github.com/prometheus/client_golang v1.5.0 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a github.com/rs/cors v1.7.0 github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa diff --git a/go.sum b/go.sum index 3833da4ca..886eadee3 100644 --- a/go.sum +++ b/go.sum @@ -306,6 +306,8 @@ github.com/prometheus/client_golang v1.3.0 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0 github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.4.1 h1:FFSuS004yOQEtDdTq+TAOLP5xUq63KqAFYyOi8zA+Y8= github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.5.0 h1:Ctq0iGpCmr3jeP77kbF2UxgvRwzWWz+4Bh9/vJTyg1A= +github.com/prometheus/client_golang v1.5.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= From 9e5b4afbf1da48d57643b3ccaeb6890b7c16af40 Mon Sep 17 00:00:00 2001 From: Tess Rinearson Date: Wed, 4 Mar 2020 15:48:17 +0100 Subject: [PATCH 11/77] .github: add markdown link checker (#4513) co-authored-by: Marko --- .github/workflows/action.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/workflows/action.yml diff --git a/.github/workflows/action.yml b/.github/workflows/action.yml new file mode 100644 index 000000000..1b6f70730 --- /dev/null +++ b/.github/workflows/action.yml @@ -0,0 +1,10 @@ +name: Check Markdown links +on: push +jobs: + markdown-link-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - uses: gaurav-nelson/github-action-markdown-link-check@0.6.0 + with: + folder-path: "docs" From 4936b23dae289422171d0575eb6d18957b598372 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 5 Mar 2020 11:28:32 +0400 Subject: [PATCH 12/77] adr-047: evidence handling (#4429) Closes #4213 and #4182 --- docs/architecture/README.md | 1 + ...047-handling-evidence-from-light-client.md | 186 ++++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 docs/architecture/adr-047-handling-evidence-from-light-client.md diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 9b3c2f661..6eb6ebc4f 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -67,5 +67,6 @@ Note the context/background should be written in the present tense. - [ADR-044-Lite-Client-With-Weak-Subjectivity](./adr-044-lite-client-with-weak-subjectivity.md) - [ADR-045-ABCI-Evidence](./adr-045-abci-evidence.md) - [ADR-046-Light-Client-Implementation](./adr-046-light-client-implementation.md) +- [ADR-047-Handling-Evidence-From-Light-Client](./adr-047-handling-evidence-from-light-client.md) - [ADR-051-Double-Signing-Risk-Reduction](./adr-051-double-signing-risk-reduction.md) - [ADR-052-Tendermint-Mode](./adr-052-tendermint-mode.md) diff --git a/docs/architecture/adr-047-handling-evidence-from-light-client.md b/docs/architecture/adr-047-handling-evidence-from-light-client.md new file mode 100644 index 000000000..8b3a850ba --- /dev/null +++ b/docs/architecture/adr-047-handling-evidence-from-light-client.md @@ -0,0 +1,186 @@ +# ADR 047: Handling evidence from light client + +## Changelog +* 18-02-2020: Initial draft +* 24-02-2020: Second version + +## Context + +If the light client is under attack, either directly -> lunatic/phantom +validators (light fork) or indirectly -> full fork, it's supposed to halt and +send evidence of misbehavior to a correct full node. Upon receiving an +evidence, the full node should punish malicious validators (if possible). + +## Decision + +When a light client sees two conflicting headers (`H1.Hash() != H2.Hash()`, +`H1.Height == H2.Height`), both having 1/3+ of the voting power of the +currently trusted validator set, it will submit a `ConflictingHeadersEvidence` +to all full nodes it's connected to. Evidence needs to be submitted to all full +nodes since there's no way to determine which full node is correct (honest). + +```go +type ConflictingHeadersEvidence struct { + H1 types.SignedHeader + H2 types.SignedHeader +} +``` + +When a full node receives the `ConflictingHeadersEvidence` evidence, it should +a) validate it b) figure out if malicious behaviour is obvious (immediately +slashable) or the fork accountability protocol needs to be started. + +### Validating headers + +Check both headers are valid (`ValidateBasic`), have the same height, and +signed by 1/3+ of the validator set that the full node had at height +`H1.Height-1`. + +- Q: What if light client validator set is not equal to full node's validator + set (i.e. from full node's point of view both headers are not properly signed; + this includes the case where none of the two headers were committed on the + main chain) + + Reject the evidence. It means light client is following a fork, but, hey, at + least it will halt. + +- Q: Don't we want to punish validators who signed something else even if they + have less or equal than 1/3? + + No consensus so far. Ethan said no, Zarko said yes. + https://github.com/tendermint/spec/pull/71#discussion_r374210533 + +### Figuring out if malicious behaviour is immediately slashable + +Let's say H1 was committed from this full node's perspective (see Appendix A). +Intersect validator sets of H1 and H2. + +* if there are signers(H2) that are not part of validators(H1), they misbehaved as +they are signing protocol messages in heights they are not validators => +immediately slashable (#F4). + +* if `H1.Round == H2.Round`, and some signers signed different precommit +messages in both commits, then it is an equivocation misbehavior => immediately +slashable (#F1). + +* if `H1.Round != H2.Round` we need to run full detection procedure => not +immediately slashable. + +* if `ValidatorsHash`, `NextValidatorsHash`, `ConsensusHash`, +`AppHash`, and `LastResultsHash` in H2 are different (incorrect application +state transition), then it is a lunatic misbehavior => immediately slashable (#F5). + +If evidence is not immediately slashable, fork accountability needs to invoked +(ADR does not yet exist). + +It's unclear if we should further break up `ConflictingHeadersEvidence` or +gossip and commit it directly. See +https://github.com/tendermint/tendermint/issues/4182#issuecomment-590339233 + +If we'd go without breaking evidence, all we'll need to do is to strip the +committed header from `ConflictingHeadersEvidence` (H1) and leave only the +uncommitted header (H2): + +```go +type ConflictingHeaderEvidence struct { + H types.SignedHeader +} +``` + +If we'd go with breaking evidence, here are the types we'll need: + +### F1. Equivocation + +Existing `DuplicateVoteEvidence` needs to be created and gossiped. + +### F4. Phantom validators + +A new type of evidence needs to be created: + +```go +type PhantomValidatorEvidence struct { + PubKey crypto.PubKey + Vote types.Vote +} +``` + +It contains a validator's public key and a vote for a block, where this +validator is not part of the validator set. + +### F5. Lunatic validator + +```go +type LunaticValidatorEvidence struct { + Header types.Header + Vote types.Vote +} +``` + +To punish this attack, we need support for a new Evidence type - +`LunaticValidatorEvidence`. This type includes a vote and a header. The header +must contain fields that are invalid with respect to the previous block, and a +vote for that header by a validator that was in a validator set within the +unbonding period. While the attack is only possible if +1/3 of some validator +set colludes, the evidence should be verifiable independently for each +individual validator. This means the total evidence can be split into one piece +of evidence per attacking validator and gossipped to nodes to be verified one +piece at a time, reducing the DoS attack surface at the peer layer. + +Note it is not sufficient to simply compare this header with that committed for +the corresponding height, as an honest node may vote for a header that is not +ultimately committed. Certain fields may also be variable, for instance the +`LastCommitHash` and the `Time` may depend on which votes the proposer includes. +Thus, the header must be explicitly checked for invalid data. + +For the attack to succeed, VC must sign a header that changes the validator set +to consist of something they control. Without doing this, they can not +otherwise attack the light client, since the client verifies commits according +to validator sets. Thus, it should be sufficient to check only that +`ValidatorsHash` and `NextValidatorsHash` are correct with respect to the +header that was committed at the corresponding height. + +That said, if the attack is conducted by +2/3 of the validator set, they don't +need to make an invalid change to the validator set, since they already control +it. Instead they would make invalid changes to the `AppHash`, or possibly other +fields. In order to punish them, then, we would have to check all header +fields. + +Note some header fields require the block itself to verify, which the light +client, by definition, does not possess, so it may not be possible to check +these fields. For now, then, `LunaticValidatorEvidence` must be checked against +all header fields which are a function of the application at previous blocks. +This includes `ValidatorsHash`, `NextValidatorsHash`, `ConsensusHash`, +`AppHash`, and `LastResultsHash`. These should all match what's in the header +for the block that was actually committed at the corresponding height, and +should thus be easy to check. + +## Status + +Proposed. + +## Consequences + +### Positive + +* Tendermint will be able to detect & punish new types of misbehavior +* light clients connected to multiple full nodes can help full nodes notice a + fork faster + +### Negative + +* Accepting `ConflictingHeadersEvidence` from light clients opens up a DDOS +attack vector (same is fair for any RPC endpoint open to public; remember that +RPC is not open by default). + +### Neutral + +## References + +* [Fork accountability spec](https://github.com/tendermint/spec/blob/master/spec/consensus/light-client/accountability.md) + +## Appendix A + +If there is an actual fork (full fork), a full node may follow either one or +another branch. So both H1 or H2 can be considered committed depending on which +branch the full node is following. It's supposed to halt if it notices an +actual fork, but there's a small chance it doesn't. From 4c8e3c814521bef5e9040fb1e9ae67907fd699e1 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Mar 2020 08:41:36 +0000 Subject: [PATCH 13/77] fix: proto-breakage (#4506) * fix: fix proto-breakage - this is amed to fix proto breakage for consumers Signed-off-by: Marko Baricevic * fix for importing third_party everywhere * undo change * test breakage change * test ssh * test https * change ssh to https * fix phony --- .circleci/config.yml | 5 +---- Makefile | 7 ++++++- abci/types/types.pb.go | 2 +- buf.yaml | 1 - libs/kv/types.pb.go | 4 ++-- scripts/protocgen.sh | 2 +- tools.mk | 23 ++++------------------- 7 files changed, 15 insertions(+), 29 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 97b0653bc..b9d535da6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -87,7 +87,7 @@ jobs: steps: - checkout - run: - command: make proto-check-breaking + command: make proto-check-breaking-ci test_abci_apps: executor: golang @@ -378,13 +378,10 @@ jobs: # command: | # set -x # export PATH=~/.local/bin:$PATH - # # install node and dredd # ./scripts/get_nodejs.sh - # # build the binaries with a proper version of Go # docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint golang make build-linux build-contract-tests-hooks - # # This docker image works with go 1.7, we can install here the hook handler that contract-tests is going to use # go get github.com/snikch/goodman/cmd/goodman # make contract-tests diff --git a/Makefile b/Makefile index 5557a87ae..da4d440e7 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ OUTPUT?=build/tendermint BUILD_TAGS?='tendermint' LD_FLAGS = -X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD` -s -w BUILD_FLAGS = -mod=readonly -ldflags "$(LD_FLAGS)" +HTTPS_GIT := https://github.com/tendermint/tendermint.git all: check build test install .PHONY: all @@ -57,9 +58,13 @@ proto-lint: .PHONY: proto-lint proto-check-breaking: - @buf check breaking --against-input '.git#branch=master' + @buf check breaking --against-input ".git#branch=master" .PHONY: proto-check-breaking +proto-check-breaking-ci: + @buf check breaking --against-input "$(HTTPS_GIT)#branch=master" +.PHONY: proto-check-breaking-ci + ############################################################################### ### Build ABCI ### ############################################################################### diff --git a/abci/types/types.pb.go b/abci/types/types.pb.go index d8445f469..6c18cfbd1 100644 --- a/abci/types/types.pb.go +++ b/abci/types/types.pb.go @@ -2571,7 +2571,7 @@ func (m *PartSetHeader) GetHash() []byte { // Validator type Validator struct { Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - //PubKey pub_key = 2 [(gogoproto.nullable)=false]; + // PubKey pub_key = 2 [(gogoproto.nullable)=false]; Power int64 `protobuf:"varint,3,opt,name=power,proto3" json:"power,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` diff --git a/buf.yaml b/buf.yaml index 1b97487f4..7349a3516 100644 --- a/buf.yaml +++ b/buf.yaml @@ -13,4 +13,3 @@ lint: breaking: use: - FILE - - PACKAGE diff --git a/libs/kv/types.pb.go b/libs/kv/types.pb.go index b572ac205..c197d5791 100644 --- a/libs/kv/types.pb.go +++ b/libs/kv/types.pb.go @@ -26,7 +26,7 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package -// Define these here for compatibility but use tmlibs/common.KVPair. +// Define these here for compatibility but use tmlibs/kv.Pair. type Pair struct { Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` @@ -82,7 +82,7 @@ func (m *Pair) GetValue() []byte { return nil } -// Define these here for compatibility but use tmlibs/common.KI64Pair. +// Define these here for compatibility but use tmlibs/kv.KI64Pair. type KI64Pair struct { Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` Value int64 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"` diff --git a/scripts/protocgen.sh b/scripts/protocgen.sh index 922512ace..e6226dc1a 100644 --- a/scripts/protocgen.sh +++ b/scripts/protocgen.sh @@ -8,4 +8,4 @@ for dir in $proto_dirs; do -I. \ --gogo_out=Mgoogle/protobuf/timestamp.proto=github.com/golang/protobuf/ptypes/timestamp,Mgoogle/protobuf/duration.proto=github.com/golang/protobuf/ptypes/duration,plugins=grpc,paths=source_relative:. \ $(find "${dir}" -name '*.proto') -done \ No newline at end of file +done diff --git a/tools.mk b/tools.mk index 66c6f2978..516fc494e 100644 --- a/tools.mk +++ b/tools.mk @@ -77,29 +77,14 @@ $(PROTOBUF): @go get github.com/gogo/protobuf/protoc-gen-gogo@v1.3.1 .PHONY: protobuf -buf: protoc-gen-buf-check-breaking protoc-gen-buf-check-lint +buf: @echo "Installing buf..." @curl -sSL \ - "https://github.com/bufbuild/buf/releases/download/v${BUF_VERSION}/buf-${UNAME_S}-${UNAME_M}" \ - -o "${BIN}/buf" && \ - chmod +x "${BIN}/buf" + "https://github.com/bufbuild/buf/releases/download/v$(BUF_VERSION)/buf-$(UNAME_S)-$(UNAME_M)" \ + -o "$(BIN)/buf" && \ + chmod +x "$(BIN)/buf" .PHONY: buf -protoc-gen-buf-check-breaking: - @echo "Installing protoc-gen-buf-check-breaking..." - @curl -sSL \ - "https://github.com/bufbuild/buf/releases/download/v${BUF_VERSION}/protoc-gen-buf-check-breaking-${UNAME_S}-${UNAME_M}" \ - -o "${BIN}/protoc-gen-buf-check-breaking" && \ - chmod +x "${BIN}/protoc-gen-buf-check-breaking" - -protoc-gen-buf-check-lint: - @echo "Installing protoc-gen-buf-check-lint..." - @curl -sSL \ - "https://github.com/bufbuild/buf/releases/download/v${BUF_VERSION}/protoc-gen-buf-check-lint-${UNAME_S}-${UNAME_M}" \ - -o "${BIN}/protoc-gen-buf-check-lint" && \ - chmod +x "${BIN}/protoc-gen-buf-check-lint" -.PHONY: protoc-gen-buf-check-lint - goodman: $(GOODMAN) $(GOODMAN): @echo "Get Goodman" From 632b53d6535e2edb4cefe84bdbecfa3736143b2d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Mar 2020 09:29:26 +0000 Subject: [PATCH 14/77] fix addrbook tests (#4526) Co-authored-by: Anton Kaliaev Co-authored-by: Erik Grinaker --- p2p/pex/addrbook_test.go | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/p2p/pex/addrbook_test.go b/p2p/pex/addrbook_test.go index 4942b403b..363958c44 100644 --- a/p2p/pex/addrbook_test.go +++ b/p2p/pex/addrbook_test.go @@ -60,15 +60,15 @@ func TestAddrBookSaveLoad(t *testing.T) { defer deleteTempFile(fname) // 0 addresses - book := NewAddrBook(fname, true).(*addrBook) + book := NewAddrBook(fname, true) book.SetLogger(log.TestingLogger()) - book.saveToFile(fname) + book.Save() - book = NewAddrBook(fname, true).(*addrBook) + book = NewAddrBook(fname, true) book.SetLogger(log.TestingLogger()) - book.loadFromFile(fname) + book.Start() - assert.Zero(t, book.Size()) + assert.True(t, book.Empty()) // 100 addresses randAddrs := randNetAddressPairs(t, 100) @@ -78,11 +78,11 @@ func TestAddrBookSaveLoad(t *testing.T) { } assert.Equal(t, 100, book.Size()) - book.saveToFile(fname) + book.Save() - book = NewAddrBook(fname, true).(*addrBook) + book = NewAddrBook(fname, true) book.SetLogger(log.TestingLogger()) - book.loadFromFile(fname) + book.Start() assert.Equal(t, 100, book.Size()) } @@ -93,19 +93,15 @@ func TestAddrBookLookup(t *testing.T) { randAddrs := randNetAddressPairs(t, 100) - book := NewAddrBook(fname, true).(*addrBook) + book := NewAddrBook(fname, true) book.SetLogger(log.TestingLogger()) for _, addrSrc := range randAddrs { addr := addrSrc.addr src := addrSrc.src book.AddAddress(addr, src) - ka := book.addrLookup[addr.ID] - assert.NotNil(t, ka, "Expected to find KnownAddress %v but wasn't there.", addr) - - if !(ka.Addr.Equals(addr) && ka.Src.Equals(src)) { - t.Fatalf("KnownAddress doesn't match addr & src") - } + ka := book.HasAddress(addr) + assert.True(t, ka, "Expected to find KnownAddress %v but wasn't there.", addr) } } From 3a695d4b6f92b61a1fa612ceba971414edc2c108 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Mar 2020 10:48:29 +0000 Subject: [PATCH 15/77] docs: fix links (#4531) * docs: fix links - fix broken links closes #4522 Signed-off-by: Marko Baricevic * fix more links * add enable and disable * remvoe deadlink --- docs/DOCS_README.md | 9 ++- docs/README.md | 2 +- docs/app-dev/abci-cli.md | 2 +- docs/app-dev/app-development.md | 2 +- docs/app-dev/indexing-transactions.md | 2 +- .../subscribing-to-events-via-websocket.md | 2 +- docs/architecture/README.md | 4 +- .../adr-002-event-subscription.md | 1 - docs/architecture/adr-011-monitoring.md | 2 +- docs/architecture/adr-018-ABCI-Validators.md | 2 +- .../adr-043-blockchain-riri-org.md | 66 ++++++++++--------- docs/tendermint-core/how-to-read-logs.md | 5 +- docs/tendermint-core/running-in-production.md | 2 +- docs/tendermint-core/using-tendermint.md | 4 ++ 14 files changed, 57 insertions(+), 48 deletions(-) diff --git a/docs/DOCS_README.md b/docs/DOCS_README.md index 0da81aa21..ae16c18c9 100644 --- a/docs/DOCS_README.md +++ b/docs/DOCS_README.md @@ -2,8 +2,7 @@ The documentation for Tendermint Core is hosted at: -- https://docs.tendermint.com/master/ and -- https://tendermint-staging.interblock.io/docs/ +- https://docs.tendermint.com/master/ built from the files in this (`/docs`) directory for [master](https://github.com/tendermint/tendermint/tree/master/docs) respectively. @@ -78,9 +77,13 @@ Install the theme and all dependencies. npm run serve ``` + + Run `pre` and `post` hooks and start a hot-reloading web-server. See output of this command for the URL (it is often https://localhost:8080). -To build documentation as a static website run `npm run build`. You will find the website in `.vuepress/dist` directory. + + +To build documentation as a static website run `npm run build`. You will find the website in `.vuepress/dist` directory. ## Search diff --git a/docs/README.md b/docs/README.md index b1b876045..1b7d691e3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -14,7 +14,7 @@ Tendermint?](introduction/what-is-tendermint.md). To get started quickly with an example application, see the [quick start guide](introduction/quick-start.md). -To learn about application development on Tendermint, see the [Application Blockchain Interface](spec/abci/). +To learn about application development on Tendermint, see the [Application Blockchain Interface](https://github.com/tendermint/spec/tree/master/spec/abci). For more details on using Tendermint, see the respective documentation for [Tendermint Core](tendermint-core/), [benchmarking and monitoring](tools/), and [network deployments](networks/). diff --git a/docs/app-dev/abci-cli.md b/docs/app-dev/abci-cli.md index e747e6d48..ec8b0abf3 100644 --- a/docs/app-dev/abci-cli.md +++ b/docs/app-dev/abci-cli.md @@ -142,7 +142,7 @@ response. The server may be generic for a particular language, and we provide a [reference implementation in Golang](https://github.com/tendermint/tendermint/tree/master/abci/server). See the -[list of other ABCI implementations](./ecosystem.md) for servers in +[list of other ABCI implementations](https://github.com/tendermint/awesome#ecosystem) for servers in other languages. The handler is specific to the application, and may be arbitrary, so diff --git a/docs/app-dev/app-development.md b/docs/app-dev/app-development.md index 44a2ea623..9c1acc289 100644 --- a/docs/app-dev/app-development.md +++ b/docs/app-dev/app-development.md @@ -7,7 +7,7 @@ order: 4 ## XXX This page is undergoing deprecation. All content is being moved to the new [home -of the ABCI specification](../spec/abci/README.md). +of the ABCI specification](https://github.com/tendermint/spec/tree/master/spec/abci). ## ABCI Design diff --git a/docs/app-dev/indexing-transactions.md b/docs/app-dev/indexing-transactions.md index 4afca5775..3e3fcd551 100644 --- a/docs/app-dev/indexing-transactions.md +++ b/docs/app-dev/indexing-transactions.md @@ -14,7 +14,7 @@ type, only the key-value pairs defined in `EndBlock` are used. Each event contains a type and a list of attributes, which are key-value pairs denoting something about what happened during the method's execution. For more -details on `Events`, see the [ABCI](../spec/abci/abci.md) documentation. +details on `Events`, see the [ABCI]https://github.com/tendermint/spec/blob/master/spec/abci/abci.md#events) documentation. An Event has a composite key associated with it. A `compositeKey` is constructed by its type and key separated by a dot. For example: diff --git a/docs/app-dev/subscribing-to-events-via-websocket.md b/docs/app-dev/subscribing-to-events-via-websocket.md index 5f5cc8921..6e4f0d207 100644 --- a/docs/app-dev/subscribing-to-events-via-websocket.md +++ b/docs/app-dev/subscribing-to-events-via-websocket.md @@ -36,7 +36,7 @@ transactions](./indexing-transactions.md) for details. When validator set changes, ValidatorSetUpdates event is published. The event carries a list of pubkey/power pairs. The list is the same Tendermint receives from ABCI application (see [EndBlock -section](../spec/abci/abci.md#endblock) in +section](https://github.com/tendermint/spec/blob/master/spec/abci/abci.md#endblock) in the ABCI spec). Response: diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 6eb6ebc4f..b571564d5 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -37,7 +37,7 @@ Note the context/background should be written in the present tense. - [ADR-006-Trust-Metric](./adr-006-trust-metric.md) - [ADR-007-Trust-Metric-Usage](./adr-007-trust-metric-usage.md) - [ADR-008-Priv-Validator](./adr-008-priv-validator.md) -- [ADR-009-ABCI-Design](./adr-009-abci-design.md) +- [ADR-009-ABCI-Design](./adr-009-ABCI-design.md) - [ADR-010-Crypto-Changes](./adr-010-crypto-changes.md) - [ADR-011-Monitoring](./adr-011-monitoring.md) - [ADR-012-Peer-Transport](./adr-012-peer-transport.md) @@ -46,7 +46,7 @@ Note the context/background should be written in the present tense. - [ADR-015-Crypto-Encoding](./adr-015-crypto-encoding.md) - [ADR-016-Protocol-Versions](./adr-016-protocol-versions.md) - [ADR-017-Chain-Versions](./adr-017-chain-versions.md) -- [ADR-018-ABCI-Validators](./adr-018-abci-validators.md) +- [ADR-018-ABCI-Validators](./adr-018-ABCI-Validators.md) - [ADR-019-Multisigs](./adr-019-multisigs.md) - [ADR-020-Block-Size](./adr-020-block-size.md) - [ADR-021-ABCI-Events](./adr-021-abci-events.md) diff --git a/docs/architecture/adr-002-event-subscription.md b/docs/architecture/adr-002-event-subscription.md index a73d584ab..e5c98637f 100644 --- a/docs/architecture/adr-002-event-subscription.md +++ b/docs/architecture/adr-002-event-subscription.md @@ -65,7 +65,6 @@ For historic queries we will need a indexing storage (Postgres, SQLite, ...). ### Issues -- https://github.com/tendermint/basecoin/issues/91 - https://github.com/tendermint/tendermint/issues/376 - https://github.com/tendermint/tendermint/issues/287 - https://github.com/tendermint/tendermint/issues/525 (related) diff --git a/docs/architecture/adr-011-monitoring.md b/docs/architecture/adr-011-monitoring.md index 8f2d009dd..4c79507a1 100644 --- a/docs/architecture/adr-011-monitoring.md +++ b/docs/architecture/adr-011-monitoring.md @@ -19,7 +19,7 @@ A few solutions were considered: b) [go-kit metrics package](https://github.com/go-kit/kit/tree/master/metrics) as an interface plus Prometheus c) [telegraf](https://github.com/influxdata/telegraf) d) new service, which will listen to events emitted by pubsub and report metrics -2. [OpenCensus](https://opencensus.io/go/index.html) +2. [OpenCensus](https://opencensus.io/introduction/) ### 1. Prometheus diff --git a/docs/architecture/adr-018-ABCI-Validators.md b/docs/architecture/adr-018-ABCI-Validators.md index f40efca15..f5ffdccaa 100644 --- a/docs/architecture/adr-018-ABCI-Validators.md +++ b/docs/architecture/adr-018-ABCI-Validators.md @@ -32,7 +32,7 @@ message ValidatorUpdate { } ``` -As noted in ADR-009[https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-009-ABCI-design.md], +As noted in [ADR-009](adr-009-ABCI-design.md), the `Validator` does not contain a pubkey because quantum public keys are quite large and it would be wasteful to send them all over ABCI with every block. Thus, applications that want to take advantage of the information in BeginBlock diff --git a/docs/architecture/adr-043-blockchain-riri-org.md b/docs/architecture/adr-043-blockchain-riri-org.md index 6bb018f51..303def716 100644 --- a/docs/architecture/adr-043-blockchain-riri-org.md +++ b/docs/architecture/adr-043-blockchain-riri-org.md @@ -1,25 +1,26 @@ # ADR 043: Blockhchain Reactor Riri-Org ## Changelog -* 18-06-2019: Initial draft -* 08-07-2019: Reviewed -* 29-11-2019: Implemented -* 14-02-2020: Updated with the implementation details + +- 18-06-2019: Initial draft +- 08-07-2019: Reviewed +- 29-11-2019: Implemented +- 14-02-2020: Updated with the implementation details ## Context -The blockchain reactor is responsible for two high level processes:sending/receiving blocks from peers and FastSync-ing blocks to catch upnode who is far behind. The goal of [ADR-40](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-040-blockchain-reactor-refactor.md) was to refactor these two processes by separating business logic currently wrapped up in go-channels into pure `handle*` functions. While the ADR specified what the final form of the reactor might look like it lacked guidance on intermediary steps to get there. -The following diagram illustrates the state of the [blockchain-reorg](https://github.com/tendermint/tendermint/pull/35610) reactor which will be referred to as `v1`. +The blockchain reactor is responsible for two high level processes:sending/receiving blocks from peers and FastSync-ing blocks to catch upnode who is far behind. The goal of [ADR-40](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-040-blockchain-reactor-refactor.md) was to refactor these two processes by separating business logic currently wrapped up in go-channels into pure `handle*` functions. While the ADR specified what the final form of the reactor might look like it lacked guidance on intermediary steps to get there. +The following diagram illustrates the state of the [blockchain-reorg](https://github.com/tendermint/tendermint/pull/3561) reactor which will be referred to as `v1`. ![v1 Blockchain Reactor Architecture Diagram](https://github.com/tendermint/tendermint/blob/f9e556481654a24aeb689bdadaf5eab3ccd66829/docs/architecture/img/blockchain-reactor-v1.png) While `v1` of the blockchain reactor has shown significant improvements in terms of simplifying the concurrency model, the current PR has run into few roadblocks. -* The current PR large and difficult to review. -* Block gossiping and fast sync processes are highly coupled to the shared `Pool` data structure. -* Peer communication is spread over multiple components creating complex dependency graph which must be mocked out during testing. -* Timeouts modeled as stateful tickers introduce non-determinism in tests +- The current PR large and difficult to review. +- Block gossiping and fast sync processes are highly coupled to the shared `Pool` data structure. +- Peer communication is spread over multiple components creating complex dependency graph which must be mocked out during testing. +- Timeouts modeled as stateful tickers introduce non-determinism in tests This ADR is meant to specify the missing components and control necessary to achieve [ADR-40](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-040-blockchain-reactor-refactor.md). @@ -32,7 +33,7 @@ Diagram](https://github.com/tendermint/tendermint/blob/584e67ac3fac220c5c3e0652e ### Fast Sync Related Communication Channels -The diagram below shows the fast sync routines and the types of channels and queues used to communicate with each other. +The diagram below shows the fast sync routines and the types of channels and queues used to communicate with each other. In addition the per reactor channels used by the sendRoutine to send messages over the Peer MConnection are shown. ![v2 Blockchain Channels and Queues @@ -42,7 +43,6 @@ Diagram](https://github.com/tendermint/tendermint/blob/5cf570690f989646fb3b615b7 The reactor will include a demultiplexing routine which will send each message to each sub routine for independent processing. Each sub routine will then select the messages it's interested in and call the handle specific function specified in [ADR-40](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-040-blockchain-reactor-refactor.md). The demuxRoutine acts as "pacemaker" setting the time in which events are expected to be handled. - ```go func demuxRoutine(msgs, scheduleMsgs, processorMsgs, ioMsgs) { timer := time.NewTicker(interval) @@ -143,6 +143,7 @@ func (r *BlockchainReactor) AddPeer(peer p2p.Peer) { ``` ## IO handling + An io handling routine within the reactor will isolate peer communication. Message going through the ioRoutine will usually be one way, using `p2p` APIs. In the case in which the `p2p` API such as `trySend` return errors, the ioRoutine can funnel those message back to the demuxRoutine for distribution to the other routines. For instance errors from the ioRoutine can be consumed by the scheduler to inform better peer selection implementations. ```go @@ -169,6 +170,7 @@ func (r *BlockchainReacor) ioRoutine(ioMesgs chan Message, outMsgs chan Message) } ``` + ### Processor Internals The processor is responsible for ordering, verifying and executing blocks. The Processor will maintain an internal cursor `height` refering to the last processed block. As a set of blocks arrive unordered, the Processor will check if it has `height+1` necessary to process the next block. The processor also maintains the map `blockPeers` of peers to height, to keep track of which peer provided the block at `height`. `blockPeers` can be used in`handleRemovePeer(...)` to reschedule all unprocessed blocks provided by a peer who has errored. @@ -232,10 +234,10 @@ func handleTimeCheckEv(time) { The Schedule maintains the internal state used for scheduling blockRequestMessages based on some scheduling algorithm. The schedule needs to maintain state on: -* The state `blockState` of every block seem up to height of maxHeight -* The set of peers and their peer state `peerState` -* which peers have which blocks -* which blocks have been requested from which peers +- The state `blockState` of every block seem up to height of maxHeight +- The set of peers and their peer state `peerState` +- which peers have which blocks +- which blocks have been requested from which peers ```go type blockState int @@ -256,7 +258,7 @@ type schedule { // a map of peerID to schedule specific peer struct `scPeer` peers map[p2p.ID]scPeer - + // a map of heights to the peer we are waiting for a response from pending map[height]scPeer @@ -309,6 +311,7 @@ type scPeer struct { ``` # Scheduler + The scheduler is configured to maintain a target `n` of in flight messages and will use feedback from `_blockResponseMessage`, `_statusResponseMessage` and `_peerError` produce an optimal assignment @@ -342,7 +345,7 @@ func handleTimeCheckEv(time) { events = [] for peerID := range schedule.peersNotTouchedSince(time) { - pending = schedule.pendingFrom(peerID) + pending = schedule.pendingFrom(peerID) schedule.setPeerState(peerID, timedout) schedule.resetBlocks(pending) events = append(events, peerTimeout{peerID}) @@ -355,6 +358,7 @@ func handleTimeCheckEv(time) { ``` ## Peer + The Peer Stores per peer state based on messages received by the scheduler. ```go @@ -376,19 +380,19 @@ type Peer struct { This design is under active development. The Implementation has been staged in the following PRs: -* [Routine](https://github.com/tendermint/tendermint/pull/3878) -* [Processor](https://github.com/tendermint/tendermint/pull/4012) -* [Scheduler](https://github.com/tendermint/tendermint/pull/4043) -* [Reactor](https://github.com/tendermint/tendermint/pull/4067) +- [Routine](https://github.com/tendermint/tendermint/pull/3878) +- [Processor](https://github.com/tendermint/tendermint/pull/4012) +- [Scheduler](https://github.com/tendermint/tendermint/pull/4043) +- [Reactor](https://github.com/tendermint/tendermint/pull/4067) ## Consequences ### Positive -* Test become deterministic -* Simulation becomes a-termporal: no need wait for a wall-time timeout -* Peer Selection can be independently tested/simulated -* Develop a general approach to refactoring reactors +- Test become deterministic +- Simulation becomes a-termporal: no need wait for a wall-time timeout +- Peer Selection can be independently tested/simulated +- Develop a general approach to refactoring reactors ### Negative @@ -396,11 +400,11 @@ staged in the following PRs: ### Implementation Path -* Implement the scheduler, test the scheduler, review the rescheduler -* Implement the processor, test the processor, review the processor -* Implement the demuxer, write integration test, review integration tests +- Implement the scheduler, test the scheduler, review the rescheduler +- Implement the processor, test the processor, review the processor +- Implement the demuxer, write integration test, review integration tests ## References -* [ADR-40](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-040-blockchain-reactor-refactor.md): The original blockchain reactor re-org proposal -* [Blockchain re-org](https://github.com/tendermint/tendermint/pull/3561): The current blockchain reactor re-org implementation (v1) +- [ADR-40](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-040-blockchain-reactor-refactor.md): The original blockchain reactor re-org proposal +- [Blockchain re-org](https://github.com/tendermint/tendermint/pull/3561): The current blockchain reactor re-org implementation (v1) diff --git a/docs/tendermint-core/how-to-read-logs.md b/docs/tendermint-core/how-to-read-logs.md index 4031a178a..195a515a5 100644 --- a/docs/tendermint-core/how-to-read-logs.md +++ b/docs/tendermint-core/how-to-read-logs.md @@ -66,9 +66,8 @@ I[10-04|13:54:30.392] Started node module=main n Next follows a standard block creation cycle, where we enter a new round, propose a block, receive more than 2/3 of prevotes, then precommits and finally have a chance to commit a block. For details, -please refer to [Consensus -Overview](../introduction/introduction.md#consensus-overview) or [Byzantine Consensus -Algorithm](../spec/consensus/consensus.md). +please refer to [Byzantine Consensus +Algorithm](https://github.com/tendermint/spec/blob/master/spec/consensus/consensus.md). ``` I[10-04|13:54:30.393] enterNewRound(91/0). Current: 91/0/RoundStepNewHeight module=consensus diff --git a/docs/tendermint-core/running-in-production.md b/docs/tendermint-core/running-in-production.md index 02a0eae58..d386308de 100644 --- a/docs/tendermint-core/running-in-production.md +++ b/docs/tendermint-core/running-in-production.md @@ -106,7 +106,7 @@ Rate-limiting and authentication are another key aspects to help protect against DOS attacks. While in the future we may implement these features, for now, validators are supposed to use external tools like [NGINX](https://www.nginx.com/blog/rate-limiting-nginx/) or -[traefik](https://docs.traefik.io/configuration/commons/#rate-limiting) +[traefik](https://docs.traefik.io/middlewares/ratelimit/) to achieve the same things. ## Debugging Tendermint diff --git a/docs/tendermint-core/using-tendermint.md b/docs/tendermint-core/using-tendermint.md index b1098f54f..b33e770d7 100644 --- a/docs/tendermint-core/using-tendermint.md +++ b/docs/tendermint-core/using-tendermint.md @@ -156,10 +156,14 @@ and the `latest_app_hash` in particular: curl http://localhost:26657/status | json_pp | grep latest_app_hash ``` + + Visit http://localhost:26657 in your browser to see the list of other endpoints. Some take no arguments (like `/status`), while others specify the argument name and use `_` as a placeholder. + + ::: tip Find the RPC Documentation [here](https://docs.tendermint.com/master/rpc/) ::: From 7d001177e3b27d96f732a3627fe847c248b3b172 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 5 Mar 2020 13:25:18 +0000 Subject: [PATCH 16/77] readme: add discord to readme (#4533) * readme: add discord to readme Signed-off-by: Marko Baricevic * add our id --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 61391f51e..f3346a75c 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Or [Blockchain](), for shor [![version](https://img.shields.io/github/tag/tendermint/tendermint.svg)](https://github.com/tendermint/tendermint/releases/latest) [![API Reference](https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667)](https://godoc.org/github.com/tendermint/tendermint) [![Go version](https://img.shields.io/badge/go-1.13-blue.svg)](https://github.com/moovweb/gvm) -[![riot.im](https://img.shields.io/badge/riot.im-JOIN%20CHAT-green.svg)](https://riot.im/app/#/room/#tendermint:matrix.org) +[![Discord](https://img.shields.io/discord/669268347736686612.svg)](https://discord.gg/AzefAFd) [![license](https://img.shields.io/github/license/tendermint/tendermint.svg)](https://github.com/tendermint/tendermint/blob/master/LICENSE) [![](https://tokei.rs/b1/github/tendermint/tendermint?category=lines)](https://github.com/tendermint/tendermint) From 431618cef6e09c24b81fba535e7cb2b7342c3af0 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Fri, 6 Mar 2020 13:33:07 +0400 Subject: [PATCH 17/77] lite2: remove auto update (#4535) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We first introduced auto-update as a separate struct AutoClient, which was wrapping Client and calling Update periodically. // AutoClient can auto update itself by fetching headers every N seconds. type AutoClient struct { base *Client updatePeriod time.Duration quit chan struct{} trustedHeaders chan *types.SignedHeader errs chan error } // NewAutoClient creates a new client and starts a polling goroutine. func NewAutoClient(base *Client, updatePeriod time.Duration) *AutoClient { c := &AutoClient{ base: base, updatePeriod: updatePeriod, quit: make(chan struct{}), trustedHeaders: make(chan *types.SignedHeader), errs: make(chan error), } go c.autoUpdate() return c } // TrustedHeaders returns a channel onto which new trusted headers are posted. func (c *AutoClient) TrustedHeaders() <-chan *types.SignedHeader { return c.trustedHeaders } // Err returns a channel onto which errors are posted. func (c *AutoClient) Errs() <-chan error { return c.errs } // Stop stops the client. func (c *AutoClient) Stop() { close(c.quit) } func (c *AutoClient) autoUpdate() { ticker := time.NewTicker(c.updatePeriod) defer ticker.Stop() for { select { case <-ticker.C: lastTrustedHeight, err := c.base.LastTrustedHeight() if err != nil { c.errs <- err continue } if lastTrustedHeight == -1 { // no headers yet => wait continue } newTrustedHeader, err := c.base.Update(time.Now()) if err != nil { c.errs <- err continue } if newTrustedHeader != nil { c.trustedHeaders <- newTrustedHeader } case <-c.quit: return } } } Later we merged it into the Client itself with the assumption that most clients will want it. But now I am not sure. Neither IBC nor cosmos/relayer are using it. It increases complexity (Start/Stop methods). That said, I think it makes sense to remove it until we see a need for it (until we better understand usage behavior). We can always introduce it later 😅. Maybe in the form of AutoClient. --- cmd/tendermint/commands/lite.go | 11 +--- .../adr-046-light-client-implementation.md | 17 ++---- lite2/client.go | 59 ------------------- lite2/client_test.go | 34 ----------- lite2/doc.go | 28 ++++----- lite2/example_test.go | 8 +-- lite2/rpc/client.go | 16 +---- 7 files changed, 21 insertions(+), 152 deletions(-) diff --git a/cmd/tendermint/commands/lite.go b/cmd/tendermint/commands/lite.go index 447c6d92b..dae72266d 100644 --- a/cmd/tendermint/commands/lite.go +++ b/cmd/tendermint/commands/lite.go @@ -102,7 +102,7 @@ func runProxy(cmd *cobra.Command, args []string) error { db, err := dbm.NewGoLevelDB("lite-client-db", home) if err != nil { - return err + return errors.Wrap(err, "new goleveldb") } var c *lite.Client @@ -129,16 +129,9 @@ func runProxy(cmd *cobra.Command, args []string) error { lite.Logger(logger), ) } - if err != nil { - return errors.Wrap(err, "failed to create") + return err } - logger.Info("Starting client...") - err = c.Start() - if err != nil { - return errors.Wrap(err, "failed to start") - } - defer c.Stop() rpcClient, err := rpcclient.NewHTTP(primaryAddr, "/websocket") if err != nil { diff --git a/docs/architecture/adr-046-light-client-implementation.md b/docs/architecture/adr-046-light-client-implementation.md index 6058da8f8..aa7df3450 100644 --- a/docs/architecture/adr-046-light-client-implementation.md +++ b/docs/architecture/adr-046-light-client-implementation.md @@ -8,23 +8,13 @@ ## Context A `Client` struct represents a light client, connected to a single blockchain. -As soon as it's started (via `Start`), it tries to update to the latest header -(using bisection algorithm by default). -Cleaning routine is also started to remove headers outside of trusting period. -NOTE: since it's periodic, we still need to check header is not expired in -`TrustedHeader`, `TrustedValidatorSet` methods (and others which are using the -latest trusted header). - -The user has an option to manually verify headers using `VerifyHeader` and -`VerifyHeaderAtHeight` methods. To avoid races, `UpdatePeriod(0)` needs to be -passed when initializing the light client (it turns off the auto update). +The user has an option to verify headers using `VerifyHeader` or +`VerifyHeaderAtHeight` or `Update` methods. The latter method downloads the +latest header from primary and compares it with the currently trusted one. ```go type Client interface { - // start and stop updating & cleaning goroutines - Start() error - Stop() Cleanup() error // get trusted headers & validators @@ -41,6 +31,7 @@ type Client interface { // verify new headers VerifyHeaderAtHeight(height int64, now time.Time) (*types.SignedHeader, error) VerifyHeader(newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error + Update(now time.Time) error } ``` diff --git a/lite2/client.go b/lite2/client.go index d65ed8ddb..b353f70d8 100644 --- a/lite2/client.go +++ b/lite2/client.go @@ -22,7 +22,6 @@ const ( sequential mode = iota + 1 skipping - defaultUpdatePeriod = 5 * time.Second defaultPruningSize = 1000 defaultMaxRetryAttempts = 10 ) @@ -55,13 +54,6 @@ func SkippingVerification(trustLevel tmmath.Fraction) Option { } } -// UpdatePeriod option can be used to change default polling period (5s). -func UpdatePeriod(d time.Duration) Option { - return func(c *Client) { - c.updatePeriod = d - } -} - // PruningSize option sets the maximum amount of headers & validator set pairs // that the light client stores. When Prune() is run, all headers (along with // the associated validator sets) that are earlier than the h amount of headers @@ -101,9 +93,6 @@ func MaxRetryAttempts(max uint16) Option { // headers from a primary provider, verifies them either sequentially or by // skipping some and stores them in a trusted store (usually, a local FS). // -// By default, the client will poll the primary provider for new headers every -// 5s (UpdatePeriod). If there are any, it will try to advance the state. -// // Default verification: SkippingVerification(DefaultTrustLevel) type Client struct { chainID string @@ -126,8 +115,6 @@ type Client struct { // Highest validator set from the store (height=H). latestTrustedVals *types.ValidatorSet - // See UpdatePeriod option - updatePeriod time.Duration // See RemoveNoLongerTrustedHeadersPeriod option pruningSize uint16 // See ConfirmationFunction option @@ -202,7 +189,6 @@ func NewClientFromTrustedStore( primary: primary, witnesses: witnesses, trustedStore: trustedStore, - updatePeriod: defaultUpdatePeriod, pruningSize: defaultPruningSize, confirmationFn: func(action string) bool { return true }, quit: make(chan struct{}), @@ -390,27 +376,6 @@ func (c *Client) initializeWithTrustOptions(options TrustOptions) error { return c.updateTrustedHeaderAndVals(h, vals) } -// Start starts two processes: 1) auto updating 2) removing outdated headers. -func (c *Client) Start() error { - c.logger.Info("Starting light client") - - if c.updatePeriod > 0 { - c.routinesWaitGroup.Add(1) - go c.autoUpdateRoutine() - } - - return nil -} - -// Stop stops two processes: 1) auto updating 2) removing outdated headers. -// Stop only returns after both of them are finished running. If you wish to -// remove all the data, call Cleanup. -func (c *Client) Stop() { - c.logger.Info("Stopping light client") - close(c.quit) - c.routinesWaitGroup.Wait() -} - // TrustedHeader returns a trusted header at the given height (0 - the latest). // // Headers along with validator sets, which can't be trusted anymore, are @@ -929,30 +894,6 @@ 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() - - for { - select { - case <-ticker.C: - err := c.Update(time.Now()) - if err != nil { - c.logger.Error("Error during auto update", "err", err) - } - case <-c.quit: - return - } - } -} - // Update attempts to advance the state by downloading the latest header and // comparing it with the existing one. func (c *Client) Update(now time.Time) error { diff --git a/lite2/client_test.go b/lite2/client_test.go index 383bd4c60..e9023c3ad 100644 --- a/lite2/client_test.go +++ b/lite2/client_test.go @@ -154,7 +154,6 @@ func TestClient_SequentialVerification(t *testing.T) { )}, dbs.New(dbm.NewMemDB(), chainID), SequentialVerification(), - UpdatePeriod(0), ) if tc.initErr { @@ -163,9 +162,6 @@ func TestClient_SequentialVerification(t *testing.T) { } require.NoError(t, err) - err = c.Start() - require.NoError(t, err) - defer c.Stop() _, err = c.VerifyHeaderAtHeight(3, bTime.Add(3*time.Hour)) if tc.verifyErr { @@ -281,7 +277,6 @@ func TestClient_SkippingVerification(t *testing.T) { )}, dbs.New(dbm.NewMemDB(), chainID), SkippingVerification(DefaultTrustLevel), - UpdatePeriod(0), ) if tc.initErr { require.Error(t, err) @@ -289,9 +284,6 @@ func TestClient_SkippingVerification(t *testing.T) { } require.NoError(t, err) - err = c.Start() - require.NoError(t, err) - defer c.Stop() _, err = c.VerifyHeaderAtHeight(3, bTime.Add(3*time.Hour)) if tc.verifyErr { @@ -429,9 +421,6 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) { Logger(log.TestingLogger()), ) require.NoError(t, err) - err = c.Start() - require.NoError(t, err) - defer c.Stop() // Check we still have the 1st header (+header+). h, err := c.TrustedHeader(1) @@ -483,9 +472,6 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) { Logger(log.TestingLogger()), ) require.NoError(t, err) - err = c.Start() - require.NoError(t, err) - defer c.Stop() // Check we no longer have the invalid 1st header (+header+). h, err := c.TrustedHeader(1) @@ -520,9 +506,6 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) { Logger(log.TestingLogger()), ) require.NoError(t, err) - err = c.Start() - require.NoError(t, err) - defer c.Stop() // Check we still have the 1st header (+header+). h, err := c.TrustedHeader(1) @@ -584,9 +567,6 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) { Logger(log.TestingLogger()), ) require.NoError(t, err) - err = c.Start() - require.NoError(t, err) - defer c.Stop() // Check we have swapped invalid 1st header (+header+) with correct one (+header1+). h, err := c.TrustedHeader(1) @@ -622,9 +602,6 @@ func TestClient_Update(t *testing.T) { Logger(log.TestingLogger()), ) require.NoError(t, err) - err = c.Start() - require.NoError(t, err) - defer c.Stop() // should result in downloading & verifying header #3 err = c.Update(bTime.Add(2 * time.Hour)) @@ -649,13 +626,9 @@ func TestClient_Concurrency(t *testing.T) { fullNode, []provider.Provider{fullNode}, dbs.New(dbm.NewMemDB(), chainID), - UpdatePeriod(0), Logger(log.TestingLogger()), ) require.NoError(t, err) - err = c.Start() - require.NoError(t, err) - defer c.Stop() _, err = c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour)) require.NoError(t, err) @@ -697,7 +670,6 @@ func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) { deadNode, []provider.Provider{fullNode, fullNode}, dbs.New(dbm.NewMemDB(), chainID), - UpdatePeriod(0), Logger(log.TestingLogger()), MaxRetryAttempts(1), ) @@ -722,7 +694,6 @@ func TestClient_BackwardsVerification(t *testing.T) { fullNode, []provider.Provider{fullNode}, dbs.New(dbm.NewMemDB(), chainID), - UpdatePeriod(0), Logger(log.TestingLogger()), ) require.NoError(t, err) @@ -755,7 +726,6 @@ func TestClient_BackwardsVerification(t *testing.T) { fullNode, []provider.Provider{fullNode}, dbs.New(dbm.NewMemDB(), chainID), - UpdatePeriod(0), Logger(log.TestingLogger()), ) require.NoError(t, err) @@ -807,7 +777,6 @@ func TestClient_BackwardsVerification(t *testing.T) { tc.provider, []provider.Provider{tc.provider}, dbs.New(dbm.NewMemDB(), chainID), - UpdatePeriod(0), Logger(log.TestingLogger()), ) require.NoError(t, err) @@ -854,7 +823,6 @@ func TestNewClientErrorsIfAllWitnessesUnavailable(t *testing.T) { fullNode, []provider.Provider{deadNode, deadNode}, dbs.New(dbm.NewMemDB(), chainID), - UpdatePeriod(0), Logger(log.TestingLogger()), MaxRetryAttempts(1), ) @@ -898,7 +866,6 @@ func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) { fullNode, []provider.Provider{badProvider1, badProvider2}, dbs.New(dbm.NewMemDB(), chainID), - UpdatePeriod(0), Logger(log.TestingLogger()), MaxRetryAttempts(1), ) @@ -926,7 +893,6 @@ func TestClientTrustedValidatorSet(t *testing.T) { fullNode, []provider.Provider{fullNode}, dbs.New(dbm.NewMemDB(), chainID), - UpdatePeriod(0), Logger(log.TestingLogger()), ) diff --git a/lite2/doc.go b/lite2/doc.go index aa280e64f..b61f5453f 100644 --- a/lite2/doc.go +++ b/lite2/doc.go @@ -65,33 +65,29 @@ Example usage: db, err := dbm.NewGoLevelDB("lite-client-db", dbDir) if err != nil { - // return err - t.Fatal(err) + // handle error } - c, err := NewClient( + + c, err := NewHTTPClient( chainID, TrustOptions{ Period: 504 * time.Hour, // 21 days Height: 100, Hash: header.Hash(), }, - httpp.New(chainID, "tcp://localhost:26657"), - []provider.Provider{httpp.New(chainID, "tcp://witness1:26657")}, - dbs.New(db, chainID), + "http://localhost:26657", + []string{"http://witness1:26657"}, + dbs.New(db, ""), ) - - err = c.Start() - if err != nil { - // return err - t.Fatal(err) - } - defer c.Stop() - - h, err := c.TrustedHeader(101) if err != nil { // handle error } - fmt.Println("got header", h) + + h, err := c.TrustedHeader(100) + if err != nil { + // handle error + } + fmt.Println("header", h) Check out other examples in example_test.go diff --git a/lite2/example_test.go b/lite2/example_test.go index e6839386f..34889ef98 100644 --- a/lite2/example_test.go +++ b/lite2/example_test.go @@ -58,18 +58,12 @@ func ExampleClient_Update() { primary, []provider.Provider{primary}, // NOTE: primary should not be used here dbs.New(db, chainID), - UpdatePeriod(0), // NOTE: value should be greater than zero // Logger(log.TestingLogger()), ) if err != nil { stdlog.Fatal(err) } - err = c.Start() - if err != nil { - stdlog.Fatal(err) - } defer func() { - c.Stop() c.Cleanup() }() @@ -78,6 +72,7 @@ func ExampleClient_Update() { // XXX: 30 * time.Minute clock drift is needed because a) Tendermint strips // monotonic component (see types/time/time.go) b) single instance is being // run. + // https://github.com/tendermint/tendermint/issues/4489 err = c.Update(time.Now().Add(30 * time.Minute)) if err != nil { stdlog.Fatal(err) @@ -137,7 +132,6 @@ func ExampleClient_VerifyHeaderAtHeight() { primary, []provider.Provider{primary}, // NOTE: primary should not be used here dbs.New(db, chainID), - UpdatePeriod(0), // Logger(log.TestingLogger()), ) if err != nil { diff --git a/lite2/rpc/client.go b/lite2/rpc/client.go index b59d459bc..abd15adc2 100644 --- a/lite2/rpc/client.go +++ b/lite2/rpc/client.go @@ -322,20 +322,8 @@ func (c *Client) UnsubscribeAll(ctx context.Context, subscriber string) error { } func (c *Client) updateLiteClientIfNeededTo(height int64) (*types.SignedHeader, error) { - lastTrustedHeight, err := c.lc.LastTrustedHeight() - if err != nil { - return nil, errors.Wrap(err, "LastTrustedHeight") - } - - if lastTrustedHeight < height { - return c.lc.VerifyHeaderAtHeight(height, time.Now()) - } - - h, err := c.lc.TrustedHeader(height) - if err != nil { - return nil, errors.Wrapf(err, "TrustedHeader(#%d)", height) - } - return h, nil + h, err := c.lc.VerifyHeaderAtHeight(height, time.Now()) + return h, errors.Wrapf(err, "failed to update light client to %d", height) } func (c *Client) RegisterOpDecoder(typ string, dec merkle.OpDecoder) { From d3f965ba68fa39af56793bc78a9d4bcdd6c57551 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Fri, 6 Mar 2020 17:10:28 +0400 Subject: [PATCH 18/77] lite2: indicate success/failure of Update (#4536) error itself is not enough since it only signals if there were any errors. Either (types.SignedHeader) or (success bool) is needed to indicate the status of the operation. Returning a header is optimal since most of the clients will want to get a newly verified header anyway. --- docs/architecture/adr-042-state-sync.md | 9 ++++----- .../adr-046-light-client-implementation.md | 10 +++++----- lite2/client.go | 17 +++++++++-------- lite2/client_test.go | 12 +++++------- lite2/example_test.go | 9 ++------- 5 files changed, 25 insertions(+), 32 deletions(-) diff --git a/docs/architecture/adr-042-state-sync.md b/docs/architecture/adr-042-state-sync.md index d525a4974..89d95f2e4 100644 --- a/docs/architecture/adr-042-state-sync.md +++ b/docs/architecture/adr-042-state-sync.md @@ -34,7 +34,7 @@ across different criteria: ### Implementation Question * What is the format of a snapshot - * Complete snapshot + * Complete snapshot * Ordered IAVL key ranges * Compressed individually chunks which can be validated * How is data validated @@ -58,7 +58,7 @@ request time. This solution would create an auxiliary data structure optimized for batch read/writes. Additionally the propsosals tend to vary on how they provide safety -properties. +properties. **LightClient** Where a client can aquire the merkle root from the block headers synchronized from a trusted validator set. Subsets of the application state, @@ -70,7 +70,7 @@ downloaded and compared against versions provided by a majority of peers. #### Lazy StateSync -An [initial specification](https://docs.google.com/document/d/15MFsQtNA0MGBv7F096FFWRDzQ1vR6_dics5Y49vF8JU/edit?ts=5a0f3629) was published by Alexis Sellier. +An initial specification was published by Alexis Sellier. In this design, the state has a given `size` of primitive elements (like keys or nodes), each element is assigned a number from 0 to `size-1`, and chunks consists of a range of such elements. Ackratos raised @@ -104,7 +104,7 @@ chunks and snappy compressed. Hashes of snappy compressed chunks are stored in a manifest file which co-ordinates the state-sync. Obtaining a correct manifest file seems to require an honest majority of peers. This means you may not find out the state is incorrect until you download the whole thing and compare it -with a verified block header. +with a verified block header. A similar solution was implemented by Binance in [#3594](https://github.com/tendermint/tendermint/pull/3594) @@ -229,7 +229,6 @@ Proposed ## References [sync: Sync current state without full replay for Applications](https://github.com/tendermint/tendermint/issues/828) - original issue -[tendermint state sync proposal](https://docs.google.com/document/d/15MFsQtNA0MGBv7F096FFWRDzQ1vR6_dics5Y49vF8JU/edit?ts=5a0f3629) - Cloudhead proposal [tendermint state sync proposal 2](https://docs.google.com/document/d/1npGTAa1qxe8EQZ1wG0a0Sip9t5oX2vYZNUDwr_LVRR4/edit) - ackratos proposal [proposal 2 implementation](https://github.com/tendermint/tendermint/pull/3243) - ackratos implementation [WIP General/Lazy State-Sync pseudo-spec](https://github.com/tendermint/tendermint/issues/3639) - Jae Proposal diff --git a/docs/architecture/adr-046-light-client-implementation.md b/docs/architecture/adr-046-light-client-implementation.md index aa7df3450..37a7c83c5 100644 --- a/docs/architecture/adr-046-light-client-implementation.md +++ b/docs/architecture/adr-046-light-client-implementation.md @@ -15,7 +15,10 @@ latest header from primary and compares it with the currently trusted one. ```go type Client interface { - Cleanup() error + // verify new headers + VerifyHeaderAtHeight(height int64, now time.Time) (*types.SignedHeader, error) + VerifyHeader(newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error + Update(now time.Time) (*types.SignedHeader, error) // get trusted headers & validators TrustedHeader(height int64) (*types.SignedHeader, error) @@ -28,10 +31,7 @@ type Client interface { Primary() provider.Provider Witnesses() []provider.Provider - // verify new headers - VerifyHeaderAtHeight(height int64, now time.Time) (*types.SignedHeader, error) - VerifyHeader(newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error - Update(now time.Time) error + Cleanup() error } ``` diff --git a/lite2/client.go b/lite2/client.go index b353f70d8..2eefd472d 100644 --- a/lite2/client.go +++ b/lite2/client.go @@ -895,33 +895,34 @@ func (c *Client) removeWitness(idx int) { } // Update attempts to advance the state by downloading the latest header and -// comparing it with the existing one. -func (c *Client) Update(now time.Time) error { +// comparing it with the existing one. It returns a new header on a successful +// update. Otherwise, it returns nil (plus an error, if any). +func (c *Client) Update(now time.Time) (*types.SignedHeader, error) { lastTrustedHeight, err := c.LastTrustedHeight() if err != nil { - return errors.Wrap(err, "can't get last trusted height") + return nil, errors.Wrap(err, "can't get last trusted height") } if lastTrustedHeight == -1 { // no headers yet => wait - return nil + return nil, nil } latestHeader, latestVals, err := c.fetchHeaderAndValsAtHeight(0) if err != nil { - return errors.Wrapf(err, "can't get latest header and vals") + return nil, errors.Wrapf(err, "can't get latest header and vals") } if latestHeader.Height > lastTrustedHeight { err = c.VerifyHeader(latestHeader, latestVals, now) if err != nil { - return err + return nil, err } - c.logger.Info("Advanced to new state", "height", latestHeader.Height, "hash", hash2str(latestHeader.Hash())) + return latestHeader, nil } - return nil + return nil, nil } // replaceProvider takes the first alternative provider and promotes it as the diff --git a/lite2/client_test.go b/lite2/client_test.go index e9023c3ad..65ea55122 100644 --- a/lite2/client_test.go +++ b/lite2/client_test.go @@ -604,13 +604,11 @@ func TestClient_Update(t *testing.T) { require.NoError(t, err) // should result in downloading & verifying header #3 - err = c.Update(bTime.Add(2 * time.Hour)) - require.NoError(t, err) - - h, err := c.TrustedHeader(3) + h, err := c.Update(bTime.Add(2 * time.Hour)) assert.NoError(t, err) - require.NotNil(t, h) - assert.EqualValues(t, 3, h.Height) + if assert.NotNil(t, h) { + assert.EqualValues(t, 3, h.Height) + } valSet, _, err := c.TrustedValidatorSet(3) assert.NoError(t, err) @@ -675,7 +673,7 @@ func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) { ) require.NoError(t, err) - err = c.Update(bTime.Add(2 * time.Hour)) + _, err = c.Update(bTime.Add(2 * time.Hour)) require.NoError(t, err) assert.NotEqual(t, c.Primary(), deadNode) diff --git a/lite2/example_test.go b/lite2/example_test.go index 34889ef98..e8c3b8bb3 100644 --- a/lite2/example_test.go +++ b/lite2/example_test.go @@ -73,17 +73,12 @@ func ExampleClient_Update() { // monotonic component (see types/time/time.go) b) single instance is being // run. // https://github.com/tendermint/tendermint/issues/4489 - err = c.Update(time.Now().Add(30 * time.Minute)) + h, err := c.Update(time.Now().Add(30 * time.Minute)) if err != nil { stdlog.Fatal(err) } - h, err := c.TrustedHeader(0) - if err != nil { - stdlog.Fatal(err) - } - - if h.Height > 2 { + if h != nil && h.Height > 2 { fmt.Println("successful update") } else { fmt.Println("update failed") From bc89aad16280a4b2429f3dd51b3a836042dbed66 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Fri, 6 Mar 2020 17:23:44 +0400 Subject: [PATCH 19/77] rpc: create buffered subscriptions on /subscribe (#4521) Closes #3935 --- CHANGELOG_PENDING.md | 1 + rpc/core/README.md | 11 +++++++++++ rpc/core/events.go | 7 ++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 47ff5dfb1..31fa4dfe7 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -24,6 +24,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi ### BUG FIXES: +- [rpc] \#3935 Create buffered subscriptions on `/subscribe` (@melekes) - [rpc] [\#4493](https://github.com/tendermint/tendermint/pull/4493) Keep the original subscription "id" field when new RPCs come in (@michaelfig) - [rpc] [\#4437](https://github.com/tendermint/tendermint/pull/4437) Fix tx_search pagination with ordered results (@erikgrinaker) - [rpc] [\#4406](https://github.com/tendermint/tendermint/pull/4406) Fix issue with multiple subscriptions on the websocket (@antho1404) diff --git a/rpc/core/README.md b/rpc/core/README.md index d767c5f71..f62d2dbf4 100644 --- a/rpc/core/README.md +++ b/rpc/core/README.md @@ -5,3 +5,14 @@ Requests that return multiple items will be paginated to 30 items by default. You can specify further pages with the ?page parameter. You can also set a custom page size up to 100 with the ?per_page parameter. + +## Subscribing to events + +The user can subscribe to events emitted by Tendermint, using `/subscribe`. If +the maximum number of clients is reached or the client has too many +subscriptions, an error will be returned. The subscription timeout is 5 sec. +Each subscription has a buffer to accommodate short bursts of events or some +slowness in clients. If the buffer gets full, the subscription will be canceled +("client is not pulling messages fast enough"). If Tendermint exits, all +subscriptions are canceled ("Tendermint exited"). The user can unsubscribe +using either `/unsubscribe` or `/unsubscribe_all`. diff --git a/rpc/core/events.go b/rpc/core/events.go index 165aa6e54..7802f160e 100644 --- a/rpc/core/events.go +++ b/rpc/core/events.go @@ -12,6 +12,11 @@ import ( rpctypes "github.com/tendermint/tendermint/rpc/lib/types" ) +const ( + // Buffer on the Tendermint (server) side to allow some slowness in clients. + subBufferSize = 100 +) + // Subscribe for events via WebSocket. // More: https://docs.tendermint.com/master/rpc/#/Websocket/subscribe func Subscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, error) { @@ -33,7 +38,7 @@ func Subscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, er subCtx, cancel := context.WithTimeout(ctx.Context(), SubscribeTimeout) defer cancel() - sub, err := eventBus.Subscribe(subCtx, addr, q) + sub, err := eventBus.Subscribe(subCtx, addr, q, subBufferSize) if err != nil { return nil, err } From b6f0aa3a8874877771415d40f49798b16e86c3e5 Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Fri, 6 Mar 2020 20:05:20 +0100 Subject: [PATCH 20/77] lite2: replace primary when providing invalid header (#4523) Closes: #4420 Created a new error ErrInvalidHeaderwhich can be formed during the verification process verifier.go and will result in the replacement of the primary provider with a witness by executing: replacePrimaryProvider() Co-authored-by: Anton Kaliaev --- go.sum | 10 ------- lite2/client.go | 67 ++++++++++++++++++++++++++++++------------ lite2/errors.go | 10 +++++++ lite2/verifier.go | 61 ++++++++++++++++++++++++++++---------- lite2/verifier_test.go | 4 +-- 5 files changed, 106 insertions(+), 46 deletions(-) diff --git a/go.sum b/go.sum index 886eadee3..21cbdfc57 100644 --- a/go.sum +++ b/go.sum @@ -10,8 +10,6 @@ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWX github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/Workiva/go-datastructures v1.0.50 h1:slDmfW6KCHcC7U+LP3DDBbm4fqTwZGn1beOFPfGaLvo= -github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/Workiva/go-datastructures v1.0.51 h1:LJHjjfcv+1gH+1D1SgrjcrF8iSZkgsAdCjclvHvVecQ= github.com/Workiva/go-datastructures v1.0.51/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= @@ -132,8 +130,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -386,22 +382,16 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.0 h1:DMOzIV76tmoDNE9pX6RSN0aDtCYeCg5VueieJaAo1uw= -github.com/stretchr/testify v1.5.0/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d h1:gZZadD8H+fF+n9CmNhYL1Y0dJB+kLOmKd7FbPJLeGHs= github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= -github.com/tecbot/gorocksdb v0.0.0-20191017175515-d217d93fd4c5 h1:gVwAW5OwaZlDB5/CfqcGFM9p9C+KxvQKyNOltQ8orj0= -github.com/tecbot/gorocksdb v0.0.0-20191017175515-d217d93fd4c5/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzHWCjJB1zZfXPIAaDpzXIEJ0eS6B5Ok= github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= github.com/tendermint/go-amino v0.14.1 h1:o2WudxNfdLNBwMyl2dqOJxiro5rfrEaU0Ugs6offJMk= github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= -github.com/tendermint/tm-db v0.4.0 h1:iPbCcLbf4nwDFhS39Zo1lpdS1X/cT9CkTlUx17FHQgA= -github.com/tendermint/tm-db v0.4.0/go.mod h1:+Cwhgowrf7NBGXmsqFMbwEtbo80XmyrlY5Jsk95JubQ= github.com/tendermint/tm-db v0.4.1 h1:TvX7JWjJOVZ+N3y+I86wddrGttOdMmmBxXcu0/Y7ZJ0= github.com/tendermint/tm-db v0.4.1/go.mod h1:JsJ6qzYkCGiGwm5GHl/H5GLI9XLb6qZX7PRe425dHAY= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= diff --git a/lite2/client.go b/lite2/client.go index 2eefd472d..22c080949 100644 --- a/lite2/client.go +++ b/lite2/client.go @@ -476,9 +476,10 @@ func (c *Client) ChainID() string { // and calls VerifyHeader. It returns header immediately if such exists in // trustedStore (no verification is needed). // +// height must be > 0. +// // It returns provider.ErrSignedHeaderNotFound if header is not found by // primary. -// It returns ErrOldHeaderExpired if header expired. func (c *Client) VerifyHeaderAtHeight(height int64, now time.Time) (*types.SignedHeader, error) { if height <= 0 { return nil, errors.New("negative or zero height") @@ -515,7 +516,10 @@ func (c *Client) VerifyHeaderAtHeight(height int64, now time.Time) (*types.Signe // intermediate headers will be requested. See the specification for details. // https://github.com/tendermint/spec/blob/master/spec/consensus/light-client.md // -// It returns ErrOldHeaderExpired if newHeader expired. +// It returns ErrOldHeaderExpired if the latest trusted header expired. +// +// If the primary provides an invalid header (ErrInvalidHeader), it is rejected +// and replaced by another provider until all are exhausted. // // If, at any moment, SignedHeader or ValidatorSet are not found by the primary // provider, provider.ErrSignedHeaderNotFound / @@ -676,7 +680,23 @@ func (c *Client) sequence( err = VerifyAdjacent(c.chainID, trustedHeader, interimHeader, interimVals, c.trustingPeriod, now) if err != nil { - return errors.Wrapf(err, "failed to verify the header #%d", height) + err = errors.Wrapf(err, "verify adjacent from #%d to #%d failed", + trustedHeader.Height, interimHeader.Height) + + switch errors.Cause(err).(type) { + case ErrInvalidHeader: + c.logger.Error("primary sent invalid header -> replacing", "err", err) + replaceErr := c.replacePrimaryProvider() + if replaceErr != nil { + c.logger.Error("Can't replace primary", "err", replaceErr) + return err // return original error + } + // attempt to verify header again + height-- + continue + default: + return err + } } // 3) Update trustedHeader @@ -729,8 +749,21 @@ func (c *Client) bisection( return err } + case ErrInvalidHeader: + c.logger.Error("primary sent invalid header -> replacing", "err", err) + replaceErr := c.replacePrimaryProvider() + if replaceErr != nil { + c.logger.Error("Can't replace primary", "err", replaceErr) + // return original error + return errors.Wrapf(err, "verify from #%d to #%d failed", + trustedHeader.Height, interimHeader.Height) + } + // attempt to verify the header again + continue + default: - return errors.Wrapf(err, "failed to verify the header #%d", newHeader.Height) + return errors.Wrapf(err, "verify from #%d to #%d failed", + trustedHeader.Height, interimHeader.Height) } } } @@ -772,7 +805,9 @@ func (c *Client) fetchHeaderAndValsAtHeight(height int64) (*types.SignedHeader, return h, vals, nil } -// Backwards verification (see VerifyHeaderBackwards func in the spec) +// backwards verification (see VerifyHeaderBackwards func in the spec) verifies +// headers before a trusted header. If a sent header is invalid the primary is +// replaced with another provider and the operation is repeated. func (c *Client) backwards( initiallyTrustedHeader *types.SignedHeader, newHeader *types.SignedHeader, @@ -794,20 +829,14 @@ func (c *Client) backwards( return errors.Wrapf(err, "failed to obtain the header at height #%d", trustedHeader.Height-1) } - if err := interimHeader.ValidateBasic(c.chainID); err != nil { - return errors.Wrap(err, "untrustedHeader.ValidateBasic failed") - } - - if !interimHeader.Time.Before(trustedHeader.Time) { - return errors.Errorf("expected older header time %v to be before newer header time %v", - interimHeader.Time, - trustedHeader.Time) - } - - if !bytes.Equal(interimHeader.Hash(), trustedHeader.LastBlockID.Hash) { - return errors.Errorf("older header hash %X does not match trusted header's last block %X", - interimHeader.Hash(), - trustedHeader.LastBlockID.Hash) + if err := VerifyBackwards(c.chainID, interimHeader, trustedHeader); err != nil { + c.logger.Error("primary sent invalid header -> replacing", "err", err) + if replaceErr := c.replacePrimaryProvider(); replaceErr != nil { + c.logger.Error("Can't replace primary", "err", replaceErr) + // return original error + return errors.Wrapf(err, "verify backwards from %d to %d failed", + trustedHeader.Height, interimHeader.Height) + } } trustedHeader = interimHeader diff --git a/lite2/errors.go b/lite2/errors.go index d0b5d2d31..13a6cf29d 100644 --- a/lite2/errors.go +++ b/lite2/errors.go @@ -28,3 +28,13 @@ type ErrNewValSetCantBeTrusted struct { func (e ErrNewValSetCantBeTrusted) Error() string { return fmt.Sprintf("cant trust new val set: %v", e.Reason) } + +// ErrInvalidHeader means the header either failed the basic validation or +// commit is not signed by 2/3+. +type ErrInvalidHeader struct { + Reason error +} + +func (e ErrInvalidHeader) Error() string { + return fmt.Sprintf("invalid header: %v", e.Reason) +} diff --git a/lite2/verifier.go b/lite2/verifier.go index 250c8b204..6d8459ab6 100644 --- a/lite2/verifier.go +++ b/lite2/verifier.go @@ -23,12 +23,12 @@ var ( // VerifyNonAdjacent verifies non-adjacent untrustedHeader against // trustedHeader. It ensures that: // -// a) trustedHeader can still be trusted (if not, ErrOldHeaderExpired is returned); -// b) untrustedHeader is valid; +// a) trustedHeader can still be trusted (if not, ErrOldHeaderExpired is returned) +// b) untrustedHeader is valid (if not, ErrInvalidHeader is returned) // c) trustLevel ([1/3, 1]) of trustedHeaderVals (or trustedHeaderNextVals) -// signed correctly (if not, ErrNewValSetCantBeTrusted is returned); -// d) more than 2/3 of untrustedVals have signed h2 (if not, -// ErrNotEnoughVotingPowerSigned is returned); +// signed correctly (if not, ErrNewValSetCantBeTrusted is returned) +// d) more than 2/3 of untrustedVals have signed h2 +// (otherwise, ErrInvalidHeader is returned) // e) headers are non-adjacent. func VerifyNonAdjacent( chainID string, @@ -49,7 +49,7 @@ func VerifyNonAdjacent( } if err := verifyNewHeaderAndVals(chainID, untrustedHeader, untrustedVals, trustedHeader, now); err != nil { - return err + return ErrInvalidHeader{err} } // Ensure that +`trustLevel` (default 1/3) or more of last trusted validators signed correctly. @@ -67,11 +67,11 @@ func VerifyNonAdjacent( // Ensure that +2/3 of new validators signed correctly. // // NOTE: this should always be the last check because untrustedVals can be - // intentionaly made very large to DOS the light client. not the case for + // intentionally made very large to DOS the light client. not the case for // VerifyAdjacent, where validator set is known in advance. if err := untrustedVals.VerifyCommit(chainID, untrustedHeader.Commit.BlockID, untrustedHeader.Height, untrustedHeader.Commit); err != nil { - return err + return ErrInvalidHeader{err} } return nil @@ -80,11 +80,11 @@ func VerifyNonAdjacent( // VerifyAdjacent verifies directly adjacent untrustedHeader against // trustedHeader. It ensures that: // -// a) trustedHeader can still be trusted (if not, ErrOldHeaderExpired is returned); -// b) untrustedHeader is valid; -// c) untrustedHeader.ValidatorsHash equals trustedHeader.NextValidatorsHash; -// d) more than 2/3 of new validators (untrustedVals) have signed h2 (if not, -// ErrNotEnoughVotingPowerSigned is returned); +// a) trustedHeader can still be trusted (if not, ErrOldHeaderExpired is returned) +// b) untrustedHeader is valid (if not, ErrInvalidHeader is returned) +// c) untrustedHeader.ValidatorsHash equals trustedHeader.NextValidatorsHash +// d) more than 2/3 of new validators (untrustedVals) have signed h2 +// (otherwise, ErrInvalidHeader is returned) // e) headers are adjacent. func VerifyAdjacent( chainID string, @@ -103,7 +103,7 @@ func VerifyAdjacent( } if err := verifyNewHeaderAndVals(chainID, untrustedHeader, untrustedVals, trustedHeader, now); err != nil { - return err + return ErrInvalidHeader{err} } // Check the validator hashes are the same @@ -118,7 +118,7 @@ func VerifyAdjacent( // Ensure that +2/3 of new validators signed correctly. if err := untrustedVals.VerifyCommit(chainID, untrustedHeader.Commit.BlockID, untrustedHeader.Height, untrustedHeader.Commit); err != nil { - return err + return ErrInvalidHeader{err} } return nil @@ -200,3 +200,34 @@ func HeaderExpired(h *types.SignedHeader, trustingPeriod time.Duration, now time expirationTime := h.Time.Add(trustingPeriod) return !expirationTime.After(now) } + +// VerifyBackwards verifies an untrusted header with a height one less than +// that of an adjacent trusted header. It ensures that: +// +// a) untrusted header is valid +// b) untrusted header has a time before the trusted header +// c) that the LastBlockID hash of the trusted header is the same as the hash +// of the trusted header +// +// For any of these cases ErrInvalidHeader is returned. +func VerifyBackwards(chainID string, untrustedHeader, trustedHeader *types.SignedHeader) error { + if err := untrustedHeader.ValidateBasic(chainID); err != nil { + return ErrInvalidHeader{err} + } + + if !untrustedHeader.Time.Before(trustedHeader.Time) { + return ErrInvalidHeader{ + errors.Errorf("expected older header time %v to be before new header time %v", + untrustedHeader.Time, + trustedHeader.Time)} + } + + if !bytes.Equal(untrustedHeader.Hash(), trustedHeader.LastBlockID.Hash) { + return ErrInvalidHeader{ + errors.Errorf("older header hash %X does not match trusted header's last block %X", + untrustedHeader.Hash(), + trustedHeader.LastBlockID.Hash)} + } + + return nil +} diff --git a/lite2/verifier_test.go b/lite2/verifier_test.go index 241d33b05..adc671516 100644 --- a/lite2/verifier_test.go +++ b/lite2/verifier_test.go @@ -113,7 +113,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) { vals, 3 * time.Hour, bTime.Add(2 * time.Hour), - types.ErrNotEnoughVotingPowerSigned{Got: 50, Needed: 93}, + ErrInvalidHeader{Reason: types.ErrNotEnoughVotingPowerSigned{Got: 50, Needed: 93}}, "", }, // vals does not match with what we have -> error @@ -227,7 +227,7 @@ func TestVerifyNonAdjacentHeaders(t *testing.T) { vals, 3 * time.Hour, bTime.Add(2 * time.Hour), - types.ErrNotEnoughVotingPowerSigned{Got: 50, Needed: 93}, + ErrInvalidHeader{types.ErrNotEnoughVotingPowerSigned{Got: 50, Needed: 93}}, "", }, // 3/3 new vals signed, 2/3 old vals present -> no error From f17717f3a3044d70ac36059a230e7e1eb22e9b9e Mon Sep 17 00:00:00 2001 From: Marko Date: Mon, 9 Mar 2020 09:44:37 +0100 Subject: [PATCH 21/77] adr: crypto encoding for proto (#4481) * adr: crypto encoding for proto work - this adr is meant to help with deciding on how to move forward with keys in tendermint. * minor change * fix gomod * add a third option * fix spelling * add first part of descision * breakdown keys and where they are used * add some wording * minor wording fix * question * change proto messages * minor update * undo go.mod changes * add a few things based on comemnts * push, push it real good * minor explanation on interface type * touch up --- docs/architecture/README.md | 2 + .../architecture/adr-054-crypto-encoding-2.md | 83 +++++++++++++++++++ docs/architecture/adr-template.md | 16 ++-- go.sum | 3 + 4 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 docs/architecture/adr-054-crypto-encoding-2.md diff --git a/docs/architecture/README.md b/docs/architecture/README.md index b571564d5..9f754fd37 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -70,3 +70,5 @@ Note the context/background should be written in the present tense. - [ADR-047-Handling-Evidence-From-Light-Client](./adr-047-handling-evidence-from-light-client.md) - [ADR-051-Double-Signing-Risk-Reduction](./adr-051-double-signing-risk-reduction.md) - [ADR-052-Tendermint-Mode](./adr-052-tendermint-mode.md) +- [ADR-053-State-Sync-Prototype](./adr-053-state-sync-prototype.md) +- [ADR-054-crypto-encoding-2](./adr-054-crypto-encoding-2.md) diff --git a/docs/architecture/adr-054-crypto-encoding-2.md b/docs/architecture/adr-054-crypto-encoding-2.md new file mode 100644 index 000000000..9ec05f229 --- /dev/null +++ b/docs/architecture/adr-054-crypto-encoding-2.md @@ -0,0 +1,83 @@ +# ADR 054: Crypto encoding (part 2) + +## Changelog + +\*2020-2-27: Created + +## Context + +Amino has been a pain point of many users in the ecosystem. While Tendermint does not suffer greatly from the performance degradation introduced by amino, we are making an effort in moving the encoding format to a widely adopted format, [Protocol Buffers](https://developers.google.com/protocol-buffers). With this migration a new standard is needed for the encoding of keys. This will cause ecosystem wide breaking changes. + +Currently amino encodes keys as ` `. + +## Decision + +When using the `oneof` protobuf type there are many times where one will have to manually switch over the possible messages and then pass them to the interface which is needed. By transitioning from a fixed size byte array (`[size]byte`) to byte slice's (`[]byte`) then this would enable the usage of the [cosmos-proto's](hhttps://github.com/regen-network/cosmos-proto#interface_type) interface type, which will generate these switch statements. + +The approach that will be taken to minimize headaches for users is one where all encoding of keys will shift to protobuf and where amino encoding is relied on, there will be custom marshal and unmarshal functions. + +Protobuf messages: + +```proto +message PubKey { + option (cosmos_proto.interface_type) = "*github.com/tendermint/tendermint/crypto.PubKey"; + oneof key { + bytes ed25519 = 1 + [(gogoproto.casttype) = "github.com/tendermint/tendermint/crypto/ed25519.PubKey"]; + bytes secp256k1 = 2 + [(gogoproto.casttype) = "github.com/tendermint/tendermint/crypto/secp256k1.PubKey"]; + bytes sr25519 = 3 + [(gogoproto.casttype) = "github.com/tendermint/tendermint/crypto/sr25519.PubKey"]; + PubKeyMultiSigThreshold multisig = 4 + [(gogoproto.casttype) = "github.com/tendermint/tendermint/crypto/multisig.PubKeyMultisigThreshold"];; + } + +message PrivKey { + option (cosmos_proto.interface_type) = "github.com/tendermint/tendermint/crypto.PrivKey"; + oneof sum { + bytes ed25519 = 1 + [(gogoproto.casttype) = "github.com/tendermint/tendermint/crypto/ed25519.PrivKey"]; + bytes secp256k1 = 2 + [(gogoproto.casttype) = "github.com/tendermint/tendermint/crypto/secp256k1.PrivKey"]; + bytes sr25519 = 3 + [(gogoproto.casttype) = "github.com/tendermint/tendermint/crypto/sr25519.PrivKey"];; + } +} +``` + +> Note: The places where backwards compatibility is needed is still unclear. + +All modules currently do not rely on amino encoded bytes and keys are not amino encoded for genesis, therefore a hardfork upgrade is what will be needed to adopt these changes. + +This work will be broken out into a few PRs, this work will be merged into a proto-breakage branch, all PRs will be reviewed prior to being merged: + +1. Encoding of keys to protobuf and protobuf messages +2. Move Tendermint types to protobuf, mainly the ones that are being encoded. +3. Go one by one through the reactors and transition amino encoded messages to protobuf. +4. Test with cosmos-sdk and/or testnets repo. + +## Status + +Proposed + +## Consequences + +- Move keys to protobuf encoding, where backwards compatibility is needed, amino marshal and unmarshal functions will be used. + +### Positive + +- Protocol Buffer encoding will not change going forward. +- Removing amino overhead from keys will help with the KSM. +- Have a large ecosystem of supported languages. + +### Negative + +- Hardfork is required to integrate this into running chains. + +### Neutral + +## References + +> Are there any relevant PR comments, issues that led up to this, or articles referenced for why we made the given design choice? If so link them here! + +- {reference link} diff --git a/docs/architecture/adr-template.md b/docs/architecture/adr-template.md index 28a5ecfbb..759fc6d72 100644 --- a/docs/architecture/adr-template.md +++ b/docs/architecture/adr-template.md @@ -1,17 +1,19 @@ # ADR {ADR-NUMBER}: {TITLE} ## Changelog -* {date}: {changelog} + +- {date}: {changelog} ## Context -> This section contains all the context one needs to understand the current state, and why there is a problem. It should be as succinct as possible and introduce the high level idea behind the solution. +> This section contains all the context one needs to understand the current state, and why there is a problem. It should be as succinct as possible and introduce the high level idea behind the solution. + ## Decision > This section explains all of the details of the proposed solution, including implementation details. -It should also describe affects / corollary items that may need to be changed as a part of this. -If the proposed change will be large, please also indicate a way to do the change to maximize ease of review. -(e.g. the optimal split of things to do between separate PR's) +> It should also describe affects / corollary items that may need to be changed as a part of this. +> If the proposed change will be large, please also indicate a way to do the change to maximize ease of review. +> (e.g. the optimal split of things to do between separate PR's) ## Status @@ -31,6 +33,6 @@ If the proposed change will be large, please also indicate a way to do the chang ## References -> Are there any relevant PR comments, issues that led up to this, or articles referrenced for why we made the given design choice? If so link them here! +> Are there any relevant PR comments, issues that led up to this, or articles referenced for why we made the given design choice? If so link them here! -* {reference link} +- {reference link} diff --git a/go.sum b/go.sum index 21cbdfc57..1fa4c0054 100644 --- a/go.sum +++ b/go.sum @@ -34,6 +34,7 @@ github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d h1:xG8Pj6Y6J760xwETNmMzmlt38QSwz0BLp1cZ09g27uw= github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= @@ -152,6 +153,7 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= @@ -238,6 +240,7 @@ github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= From 73c19bd6ac367e1a4a473f56fea4cfdbe0a37039 Mon Sep 17 00:00:00 2001 From: Tess Rinearson Date: Mon, 9 Mar 2020 18:52:08 +0100 Subject: [PATCH 22/77] CONTRIBUTING: include instructions for installing protobuf --- CONTRIBUTING.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 623cfb53a..6e6897ffa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -100,7 +100,25 @@ specify exactly the dependency you want to update, eg. ## Protobuf -When working with [protobuf](https://developers.google.com/protocol-buffers) there are a few things you should know. We use [buf](https://buf.build/) for our linting and breaking changes checking. If you would like to run linting and check if the changes you have made are breaking then you will have to install the needed dependencies with `make buf`. Then the linting cmd will be `make proto-lint` and the breaking changes check will be `make proto-check-breaking`. To generate new stubs based off of your changes you can run `make proto-gen` (you can do this outside of GOPATH). +We use [Protocol Buffers](https://developers.google.com/protocol-buffers) along with [gogoproto](https://github.com/gogo/protobuf) to generate code for use across Tendermint Core. + +For linting and checking breaking changes, we use [buf](https://buf.build/). If you would like to run linting and check if the changes you have made are breaking then you will have to install the needed dependencies with `make buf`. Then the linting cmd will be `make proto-lint` and the breaking changes check will be `make proto-check-breaking`. + +To generate new stubs based off of your changes you can run `make proto-gen` after installing `protoc` and gogoproto. + +### Installation Instructions + +To install `protoc`, download an appropriate release (https://github.com/protocolbuffers/protobuf) and then move the provided binaries into your PATH (follow instructions in README included with the download). + +To install `gogoproto`, do the following: + +```sh +$ go get github.com/gogo/protobuf/gogoproto +$ cd $GOPATH/pkg/mod/github.com/gogo/protobuf@v1.3.1 # or wherever go get installs things +$ make install +``` + +You should now be able to run `make proto-gen` from inside the root Tendermint directory to generate new files from proto files. ## Vagrant From cb8a2cc67f5d7f34d3d83634b8c1760225256e42 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2020 15:29:31 +0000 Subject: [PATCH 23/77] build(deps): bump github.com/Workiva/go-datastructures (#4545) Bumps [github.com/Workiva/go-datastructures](https://github.com/Workiva/go-datastructures) from 1.0.51 to 1.0.52. - [Release notes](https://github.com/Workiva/go-datastructures/releases) - [Commits](https://github.com/Workiva/go-datastructures/compare/v1.0.51...v1.0.52) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 5af1190cf..9e916e7e8 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.13 require ( github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f - github.com/Workiva/go-datastructures v1.0.51 + github.com/Workiva/go-datastructures v1.0.52 github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a github.com/fortytw2/leaktest v1.3.0 diff --git a/go.sum b/go.sum index 1fa4c0054..6b5a1cf49 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrd github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/Workiva/go-datastructures v1.0.51 h1:LJHjjfcv+1gH+1D1SgrjcrF8iSZkgsAdCjclvHvVecQ= github.com/Workiva/go-datastructures v1.0.51/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= +github.com/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9abU0yMQt0NI= +github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= From 7466f353451eae68183f86e042034b98a6755001 Mon Sep 17 00:00:00 2001 From: Callum Michael Waters Date: Tue, 10 Mar 2020 18:13:45 +0100 Subject: [PATCH 24/77] mvp blacklist alg --- p2p/pex/addrbook.go | 44 +++++++++++++++++++++++++++++++++++----- p2p/pex/known_address.go | 12 +++++++++++ 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index dbba71345..3bf331172 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -22,8 +22,9 @@ import ( ) const ( - bucketTypeNew = 0x01 - bucketTypeOld = 0x02 + bucketTypeNew = 0x01 + bucketTypeOld = 0x02 + defaultBanTime = 24 * time.Hour ) // AddrBook is an address book used for tracking peers @@ -87,6 +88,7 @@ type addrBook struct { ourAddrs map[string]struct{} privateIDs map[p2p.ID]struct{} addrLookup map[p2p.ID]*knownAddress // new & old + badPeers map[p2p.ID]*knownAddress // blacklisted peers bucketsOld []map[string]*knownAddress bucketsNew []map[string]*knownAddress nOld int @@ -108,6 +110,7 @@ func NewAddrBook(filePath string, routabilityStrict bool) AddrBook { ourAddrs: make(map[string]struct{}), privateIDs: make(map[p2p.ID]struct{}), addrLookup: make(map[p2p.ID]*knownAddress), + badPeers: make(map[p2p.ID]*knownAddress), filePath: filePath, routabilityStrict: routabilityStrict, } @@ -233,6 +236,7 @@ func (a *addrBook) HasAddress(addr *p2p.NetAddress) bool { // NeedMoreAddrs implements AddrBook - returns true if there are not have enough addresses in the book. func (a *addrBook) NeedMoreAddrs() bool { + a.ReinstateBadPeers() return a.Size() < needAddressThreshold } @@ -324,10 +328,24 @@ func (a *addrBook) MarkAttempt(addr *p2p.NetAddress) { ka.markAttempt() } -// MarkBad implements AddrBook. Currently it just ejects the address. -// TODO: black list for some amount of time +// MarkBad implements AddrBook. Kicks address out from book, places +// the address in the badPeers pool. func (a *addrBook) MarkBad(addr *p2p.NetAddress) { - a.RemoveAddress(addr) + if a.addBadPeer(addr) { + a.RemoveAddress(addr) + } +} + +func (a *addrBook) ReinstateBadPeers() { + for _, ka := range a.badPeers { + if !ka.isBanned(defaultBanTime) { + a.mtx.Lock() + bucket := a.calcNewBucket(ka.Addr, ka.Src) + a.addToNewBucket(ka, bucket) + delete(a.badPeers, ka.ID()) + a.mtx.Unlock() + } + } } // GetSelection implements AddrBook. @@ -725,6 +743,22 @@ func (a *addrBook) moveToOld(ka *knownAddress) { } } +func (a *addrBook) addBadPeer(addr *p2p.NetAddress) bool { + a.mtx.Lock() + defer a.mtx.Unlock() + + // check it exists in addrbook + ka := a.addrLookup[addr.ID] + // check address is not already there + if _, alreadyBadPeer := a.badPeers[addr.ID]; ka != nil && !alreadyBadPeer { + // add to bad peer list + ka.ban() + a.badPeers[addr.ID] = ka + return true + } + return false +} + //--------------------------------------------------------------------- // calculate bucket placements diff --git a/p2p/pex/known_address.go b/p2p/pex/known_address.go index af40d6ff0..72729a5d5 100644 --- a/p2p/pex/known_address.go +++ b/p2p/pex/known_address.go @@ -16,6 +16,7 @@ type knownAddress struct { BucketType byte `json:"bucket_type"` LastAttempt time.Time `json:"last_attempt"` LastSuccess time.Time `json:"last_success"` + LastBanTime time.Time `json:"last_ban_time"` } func newKnownAddress(addr *p2p.NetAddress, src *p2p.NetAddress) *knownAddress { @@ -54,6 +55,17 @@ func (ka *knownAddress) markGood() { ka.LastSuccess = now } +func (ka *knownAddress) ban() { + ka.LastBanTime = time.Now() +} + +func (ka *knownAddress) isBanned(banTime time.Duration) bool { + if ka.LastBanTime.Add(banTime).Before(time.Now()) { + return false + } + return true +} + func (ka *knownAddress) addBucketRef(bucketIdx int) int { for _, bucket := range ka.Buckets { if bucket == bucketIdx { From 6ccd3324c2b58a0ff79c0085d2a6d1c87025d954 Mon Sep 17 00:00:00 2001 From: Callum Michael Waters Date: Tue, 10 Mar 2020 19:01:56 +0100 Subject: [PATCH 25/77] move reinstatement after if statement in pex reactor --- p2p/pex/addrbook.go | 5 +++-- p2p/pex/pex_reactor.go | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index 3bf331172..15019f5cd 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -60,7 +60,9 @@ type AddrBook interface { // Mark address MarkGood(p2p.ID) MarkAttempt(*p2p.NetAddress) - MarkBad(*p2p.NetAddress) + MarkBad(*p2p.NetAddress) // Move peer to bad peers list + // Add bad peers back to addrBook + ReinstateBadPeers() IsGood(*p2p.NetAddress) bool @@ -236,7 +238,6 @@ func (a *addrBook) HasAddress(addr *p2p.NetAddress) bool { // NeedMoreAddrs implements AddrBook - returns true if there are not have enough addresses in the book. func (a *addrBook) NeedMoreAddrs() bool { - a.ReinstateBadPeers() return a.Size() < needAddressThreshold } diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index 3a3d2d7de..66ec77503 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -8,7 +8,7 @@ import ( "github.com/pkg/errors" - amino "github.com/tendermint/go-amino" + "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/libs/cmap" tmmath "github.com/tendermint/tendermint/libs/math" "github.com/tendermint/tendermint/libs/rand" @@ -494,6 +494,8 @@ func (r *Reactor) ensurePeers() { } if r.book.NeedMoreAddrs() { + // 0) Check if banned nodes can be reinstated + r.book.ReinstateBadPeers() // 1) Pick a random peer and ask for more. peers := r.Switch.Peers().List() peersCount := len(peers) From e18636ef1fcbee64f6df3751a79a7323089eaec9 Mon Sep 17 00:00:00 2001 From: Callum Michael Waters Date: Tue, 10 Mar 2020 19:04:45 +0100 Subject: [PATCH 26/77] separate reinstatement of peers from discovery of new peers --- p2p/pex/pex_reactor.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index 66ec77503..bce9b40ff 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -494,8 +494,12 @@ func (r *Reactor) ensurePeers() { } if r.book.NeedMoreAddrs() { - // 0) Check if banned nodes can be reinstated + // Check if banned nodes can be reinstated r.book.ReinstateBadPeers() + } + + if r.book.NeedMoreAddrs() { + // 1) Pick a random peer and ask for more. peers := r.Switch.Peers().List() peersCount := len(peers) From eeb0b0d1123358b3ead49d234840087fe22b9a17 Mon Sep 17 00:00:00 2001 From: Callum Michael Waters Date: Tue, 10 Mar 2020 19:06:48 +0100 Subject: [PATCH 27/77] lint fix --- p2p/pex/known_address.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/p2p/pex/known_address.go b/p2p/pex/known_address.go index 72729a5d5..9d4d060a5 100644 --- a/p2p/pex/known_address.go +++ b/p2p/pex/known_address.go @@ -60,10 +60,7 @@ func (ka *knownAddress) ban() { } func (ka *knownAddress) isBanned(banTime time.Duration) bool { - if ka.LastBanTime.Add(banTime).Before(time.Now()) { - return false - } - return true + return ka.LastBanTime.Add(banTime).Before(time.Now()) } func (ka *knownAddress) addBucketRef(bucketIdx int) int { From 31bea92d127a40a14174611c27bb7e6dd66ca3c9 Mon Sep 17 00:00:00 2001 From: Tess Rinearson Date: Tue, 10 Mar 2020 21:34:44 +0100 Subject: [PATCH 28/77] libs/kv: remove unused type KI64Pair (#4542) --- CHANGELOG_PENDING.md | 1 + libs/kv/kvpair.go | 29 ---- libs/kv/result.go | 11 -- libs/kv/types.pb.go | 296 ++-------------------------------------- libs/kv/types.proto | 7 - libs/kv/typespb_test.go | 124 ----------------- 6 files changed, 13 insertions(+), 455 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 31fa4dfe7..aa0c0c41f 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -21,6 +21,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi - [types] [\#4417](https://github.com/tendermint/tendermint/issues/4417) VerifyCommitX() functions should return as soon as +2/3 threashold is reached. - [examples/kvstore] [\#4509](https://github.com/tendermint/tendermint/pull/4509) ABCI query now returns the proper height (@erikgrinaker) - [cmd] \#4515 Change `tendermint debug dump` sub-command archives filename's format (@melekes) +- [libs/kv]: [\#4542](https://github.com/tendermint/tendermint/pull/4542) remove unused type KI64Pair (@tessr) ### BUG FIXES: diff --git a/libs/kv/kvpair.go b/libs/kv/kvpair.go index 3007c0272..8eebae606 100644 --- a/libs/kv/kvpair.go +++ b/libs/kv/kvpair.go @@ -36,32 +36,3 @@ func (kvs Pairs) Less(i, j int) bool { func (kvs Pairs) Swap(i, j int) { kvs[i], kvs[j] = kvs[j], kvs[i] } func (kvs Pairs) Sort() { sort.Sort(kvs) } -//---------------------------------------- -// KI64Pair - -/* -Defined in types.proto -type KI64Pair struct { - Key []byte - Value int64 -} -*/ - -type KI64Pairs []KI64Pair - -// Sorting -func (kvs KI64Pairs) Len() int { return len(kvs) } -func (kvs KI64Pairs) Less(i, j int) bool { - switch bytes.Compare(kvs[i].Key, kvs[j].Key) { - case -1: - return true - case 0: - return kvs[i].Value < kvs[j].Value - case 1: - return false - default: - panic("invalid comparison result") - } -} -func (kvs KI64Pairs) Swap(i, j int) { kvs[i], kvs[j] = kvs[j], kvs[i] } -func (kvs KI64Pairs) Sort() { sort.Sort(kvs) } diff --git a/libs/kv/result.go b/libs/kv/result.go index b2d855cbc..fd40450b1 100644 --- a/libs/kv/result.go +++ b/libs/kv/result.go @@ -30,16 +30,6 @@ func (r *Pair) UnmarshalJSON(b []byte) error { return jsonpbUnmarshaller.Unmarshal(reader, r) } -func (r *KI64Pair) MarshalJSON() ([]byte, error) { - s, err := jsonpbMarshaller.MarshalToString(r) - return []byte(s), err -} - -func (r *KI64Pair) UnmarshalJSON(b []byte) error { - reader := bytes.NewBuffer(b) - return jsonpbUnmarshaller.Unmarshal(reader, r) -} - // Some compile time assertions to ensure we don't // have accidental runtime surprises later on. // jsonEncodingRoundTripper ensures that asserted @@ -51,4 +41,3 @@ type jsonRoundTripper interface { } var _ jsonRoundTripper = (*Pair)(nil) -var _ jsonRoundTripper = (*KI64Pair)(nil) diff --git a/libs/kv/types.pb.go b/libs/kv/types.pb.go index c197d5791..7a6e6e12e 100644 --- a/libs/kv/types.pb.go +++ b/libs/kv/types.pb.go @@ -26,7 +26,6 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package -// Define these here for compatibility but use tmlibs/kv.Pair. type Pair struct { Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` @@ -82,74 +81,16 @@ func (m *Pair) GetValue() []byte { return nil } -// Define these here for compatibility but use tmlibs/kv.KI64Pair. -type KI64Pair struct { - Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` - Value int64 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *KI64Pair) Reset() { *m = KI64Pair{} } -func (m *KI64Pair) String() string { return proto.CompactTextString(m) } -func (*KI64Pair) ProtoMessage() {} -func (*KI64Pair) Descriptor() ([]byte, []int) { - return fileDescriptor_31432671d164f444, []int{1} -} -func (m *KI64Pair) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *KI64Pair) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_KI64Pair.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *KI64Pair) XXX_Merge(src proto.Message) { - xxx_messageInfo_KI64Pair.Merge(m, src) -} -func (m *KI64Pair) XXX_Size() int { - return m.Size() -} -func (m *KI64Pair) XXX_DiscardUnknown() { - xxx_messageInfo_KI64Pair.DiscardUnknown(m) -} - -var xxx_messageInfo_KI64Pair proto.InternalMessageInfo - -func (m *KI64Pair) GetKey() []byte { - if m != nil { - return m.Key - } - return nil -} - -func (m *KI64Pair) GetValue() int64 { - if m != nil { - return m.Value - } - return 0 -} - func init() { proto.RegisterType((*Pair)(nil), "tendermint.libs.kv.Pair") golang_proto.RegisterType((*Pair)(nil), "tendermint.libs.kv.Pair") - proto.RegisterType((*KI64Pair)(nil), "tendermint.libs.kv.KI64Pair") - golang_proto.RegisterType((*KI64Pair)(nil), "tendermint.libs.kv.KI64Pair") } func init() { proto.RegisterFile("libs/kv/types.proto", fileDescriptor_31432671d164f444) } func init() { golang_proto.RegisterFile("libs/kv/types.proto", fileDescriptor_31432671d164f444) } var fileDescriptor_31432671d164f444 = []byte{ - // 196 bytes of a gzipped FileDescriptorProto + // 182 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xce, 0xc9, 0x4c, 0x2a, 0xd6, 0xcf, 0x2e, 0xd3, 0x2f, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x2a, 0x49, 0xcd, 0x4b, 0x49, 0x2d, 0xca, 0xcd, 0xcc, 0x2b, 0xd1, 0x03, 0xc9, 0xeb, 0x65, @@ -157,12 +98,11 @@ var fileDescriptor_31432671d164f444 = []byte{ 0x95, 0xe9, 0xa7, 0xe7, 0xa7, 0xe7, 0x23, 0x58, 0x10, 0xbd, 0x4a, 0x7a, 0x5c, 0x2c, 0x01, 0x89, 0x99, 0x45, 0x42, 0x02, 0x5c, 0xcc, 0xd9, 0xa9, 0x95, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x3c, 0x41, 0x20, 0xa6, 0x90, 0x08, 0x17, 0x6b, 0x59, 0x62, 0x4e, 0x69, 0xaa, 0x04, 0x13, 0x58, 0x0c, 0xc2, - 0x51, 0x32, 0xe2, 0xe2, 0xf0, 0xf6, 0x34, 0x33, 0x21, 0x46, 0x0f, 0x33, 0x54, 0x8f, 0x93, 0xdb, - 0x8f, 0x87, 0x72, 0x8c, 0x2b, 0x1e, 0xc9, 0x31, 0xee, 0x78, 0x24, 0xc7, 0x78, 0xe2, 0x91, 0x1c, - 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x1e, 0x78, 0x2c, 0xc7, 0x18, 0xa5, 0x91, - 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x8f, 0xf0, 0x08, 0x32, 0x13, 0xea, - 0xe7, 0x24, 0x36, 0xb0, 0x93, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xc5, 0x5f, 0x67, 0xcb, - 0x05, 0x01, 0x00, 0x00, + 0x71, 0x72, 0xfb, 0xf1, 0x50, 0x8e, 0x71, 0xc5, 0x23, 0x39, 0xc6, 0x1d, 0x8f, 0xe4, 0x18, 0x4f, + 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x03, 0x8f, 0xe5, 0x18, + 0xa3, 0x34, 0xd2, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0x11, 0x8e, 0x42, + 0x66, 0x42, 0xdd, 0x9f, 0xc4, 0x06, 0xb6, 0xde, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x5d, 0x3a, + 0xdc, 0xba, 0xd1, 0x00, 0x00, 0x00, } func (this *Pair) Equal(that interface{}) bool { @@ -195,36 +135,6 @@ func (this *Pair) Equal(that interface{}) bool { } return true } -func (this *KI64Pair) Equal(that interface{}) bool { - if that == nil { - return this == nil - } - - that1, ok := that.(*KI64Pair) - if !ok { - that2, ok := that.(KI64Pair) - if ok { - that1 = &that2 - } else { - return false - } - } - if that1 == nil { - return this == nil - } else if this == nil { - return false - } - if !bytes.Equal(this.Key, that1.Key) { - return false - } - if this.Value != that1.Value { - return false - } - if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { - return false - } - return true -} func (m *Pair) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -266,45 +176,6 @@ func (m *Pair) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *KI64Pair) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *KI64Pair) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *KI64Pair) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.XXX_unrecognized != nil { - i -= len(m.XXX_unrecognized) - copy(dAtA[i:], m.XXX_unrecognized) - } - if m.Value != 0 { - i = encodeVarintTypes(dAtA, i, uint64(m.Value)) - i-- - dAtA[i] = 0x10 - } - if len(m.Key) > 0 { - i -= len(m.Key) - copy(dAtA[i:], m.Key) - i = encodeVarintTypes(dAtA, i, uint64(len(m.Key))) - i-- - dAtA[i] = 0xa - } - return len(dAtA) - i, nil -} - func encodeVarintTypes(dAtA []byte, offset int, v uint64) int { offset -= sovTypes(v) base := offset @@ -334,23 +205,6 @@ func NewPopulatedPair(r randyTypes, easy bool) *Pair { return this } -func NewPopulatedKI64Pair(r randyTypes, easy bool) *KI64Pair { - this := &KI64Pair{} - v3 := r.Intn(100) - this.Key = make([]byte, v3) - for i := 0; i < v3; i++ { - this.Key[i] = byte(r.Intn(256)) - } - this.Value = int64(r.Int63()) - if r.Intn(2) == 0 { - this.Value *= -1 - } - if !easy && r.Intn(10) != 0 { - this.XXX_unrecognized = randUnrecognizedTypes(r, 3) - } - return this -} - type randyTypes interface { Float32() float32 Float64() float64 @@ -370,9 +224,9 @@ func randUTF8RuneTypes(r randyTypes) rune { return rune(ru + 61) } func randStringTypes(r randyTypes) string { - v4 := r.Intn(100) - tmps := make([]rune, v4) - for i := 0; i < v4; i++ { + v3 := r.Intn(100) + tmps := make([]rune, v3) + for i := 0; i < v3; i++ { tmps[i] = randUTF8RuneTypes(r) } return string(tmps) @@ -394,11 +248,11 @@ func randFieldTypes(dAtA []byte, r randyTypes, fieldNumber int, wire int) []byte switch wire { case 0: dAtA = encodeVarintPopulateTypes(dAtA, uint64(key)) - v5 := r.Int63() + v4 := r.Int63() if r.Intn(2) == 0 { - v5 *= -1 + v4 *= -1 } - dAtA = encodeVarintPopulateTypes(dAtA, uint64(v5)) + dAtA = encodeVarintPopulateTypes(dAtA, uint64(v4)) case 1: dAtA = encodeVarintPopulateTypes(dAtA, uint64(key)) dAtA = append(dAtA, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256))) @@ -443,25 +297,6 @@ func (m *Pair) Size() (n int) { return n } -func (m *KI64Pair) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = len(m.Key) - if l > 0 { - n += 1 + l + sovTypes(uint64(l)) - } - if m.Value != 0 { - n += 1 + sovTypes(uint64(m.Value)) - } - if m.XXX_unrecognized != nil { - n += len(m.XXX_unrecognized) - } - return n -} - func sovTypes(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -590,113 +425,6 @@ func (m *Pair) Unmarshal(dAtA []byte) error { } return nil } -func (m *KI64Pair) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: KI64Pair: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: KI64Pair: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthTypes - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthTypes - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) - if m.Key == nil { - m.Key = []byte{} - } - iNdEx = postIndex - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) - } - m.Value = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Value |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - default: - iNdEx = preIndex - skippy, err := skipTypes(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthTypes - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthTypes - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func skipTypes(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/libs/kv/types.proto b/libs/kv/types.proto index 7e1375c21..1b6a7a58d 100644 --- a/libs/kv/types.proto +++ b/libs/kv/types.proto @@ -16,14 +16,7 @@ option (gogoproto.testgen_all) = true; //---------------------------------------- // Abstract types -// Define these here for compatibility but use tmlibs/kv.Pair. message Pair { bytes key = 1; bytes value = 2; } - -// Define these here for compatibility but use tmlibs/kv.KI64Pair. -message KI64Pair { - bytes key = 1; - int64 value = 2; -} diff --git a/libs/kv/typespb_test.go b/libs/kv/typespb_test.go index 38656ecf9..dc45bf7f2 100644 --- a/libs/kv/typespb_test.go +++ b/libs/kv/typespb_test.go @@ -78,62 +78,6 @@ func TestPairMarshalTo(t *testing.T) { } } -func TestKI64PairProto(t *testing.T) { - seed := time.Now().UnixNano() - popr := math_rand.New(math_rand.NewSource(seed)) - p := NewPopulatedKI64Pair(popr, false) - dAtA, err := github_com_gogo_protobuf_proto.Marshal(p) - if err != nil { - t.Fatalf("seed = %d, err = %v", seed, err) - } - msg := &KI64Pair{} - if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil { - t.Fatalf("seed = %d, err = %v", seed, err) - } - littlefuzz := make([]byte, len(dAtA)) - copy(littlefuzz, dAtA) - for i := range dAtA { - dAtA[i] = byte(popr.Intn(256)) - } - if !p.Equal(msg) { - t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) - } - if len(littlefuzz) > 0 { - fuzzamount := 100 - for i := 0; i < fuzzamount; i++ { - littlefuzz[popr.Intn(len(littlefuzz))] = byte(popr.Intn(256)) - littlefuzz = append(littlefuzz, byte(popr.Intn(256))) - } - // shouldn't panic - _ = github_com_gogo_protobuf_proto.Unmarshal(littlefuzz, msg) - } -} - -func TestKI64PairMarshalTo(t *testing.T) { - seed := time.Now().UnixNano() - popr := math_rand.New(math_rand.NewSource(seed)) - p := NewPopulatedKI64Pair(popr, false) - size := p.Size() - dAtA := make([]byte, size) - for i := range dAtA { - dAtA[i] = byte(popr.Intn(256)) - } - _, err := p.MarshalTo(dAtA) - if err != nil { - t.Fatalf("seed = %d, err = %v", seed, err) - } - msg := &KI64Pair{} - if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil { - t.Fatalf("seed = %d, err = %v", seed, err) - } - for i := range dAtA { - dAtA[i] = byte(popr.Intn(256)) - } - if !p.Equal(msg) { - t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) - } -} - func TestPairJSON(t *testing.T) { seed := time.Now().UnixNano() popr := math_rand.New(math_rand.NewSource(seed)) @@ -152,24 +96,6 @@ func TestPairJSON(t *testing.T) { t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p) } } -func TestKI64PairJSON(t *testing.T) { - seed := time.Now().UnixNano() - popr := math_rand.New(math_rand.NewSource(seed)) - p := NewPopulatedKI64Pair(popr, true) - marshaler := github_com_gogo_protobuf_jsonpb.Marshaler{} - jsondata, err := marshaler.MarshalToString(p) - if err != nil { - t.Fatalf("seed = %d, err = %v", seed, err) - } - msg := &KI64Pair{} - err = github_com_gogo_protobuf_jsonpb.UnmarshalString(jsondata, msg) - if err != nil { - t.Fatalf("seed = %d, err = %v", seed, err) - } - if !p.Equal(msg) { - t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p) - } -} func TestPairProtoText(t *testing.T) { seed := time.Now().UnixNano() popr := math_rand.New(math_rand.NewSource(seed)) @@ -198,34 +124,6 @@ func TestPairProtoCompactText(t *testing.T) { } } -func TestKI64PairProtoText(t *testing.T) { - seed := time.Now().UnixNano() - popr := math_rand.New(math_rand.NewSource(seed)) - p := NewPopulatedKI64Pair(popr, true) - dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p) - msg := &KI64Pair{} - if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil { - t.Fatalf("seed = %d, err = %v", seed, err) - } - if !p.Equal(msg) { - t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) - } -} - -func TestKI64PairProtoCompactText(t *testing.T) { - seed := time.Now().UnixNano() - popr := math_rand.New(math_rand.NewSource(seed)) - p := NewPopulatedKI64Pair(popr, true) - dAtA := github_com_gogo_protobuf_proto.CompactTextString(p) - msg := &KI64Pair{} - if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil { - t.Fatalf("seed = %d, err = %v", seed, err) - } - if !p.Equal(msg) { - t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) - } -} - func TestPairSize(t *testing.T) { seed := time.Now().UnixNano() popr := math_rand.New(math_rand.NewSource(seed)) @@ -248,26 +146,4 @@ func TestPairSize(t *testing.T) { } } -func TestKI64PairSize(t *testing.T) { - seed := time.Now().UnixNano() - popr := math_rand.New(math_rand.NewSource(seed)) - p := NewPopulatedKI64Pair(popr, true) - size2 := github_com_gogo_protobuf_proto.Size(p) - dAtA, err := github_com_gogo_protobuf_proto.Marshal(p) - if err != nil { - t.Fatalf("seed = %d, err = %v", seed, err) - } - size := p.Size() - if len(dAtA) != size { - t.Errorf("seed = %d, size %v != marshalled size %v", seed, size, len(dAtA)) - } - if size2 != size { - t.Errorf("seed = %d, size %v != before marshal proto.Size %v", seed, size, size2) - } - size3 := github_com_gogo_protobuf_proto.Size(p) - if size3 != size { - t.Errorf("seed = %d, size %v != after marshal proto.Size %v", seed, size, size3) - } -} - //These tests are generated by github.com/gogo/protobuf/plugin/testgen From c534291a5652ddb8d9c2b89f2fdb18485ef2ccd1 Mon Sep 17 00:00:00 2001 From: Marko Date: Wed, 11 Mar 2020 11:57:42 +0100 Subject: [PATCH 29/77] rc1/v0.33.2(#4541) * rc1/v0.33.2 - release candiadte for 0.33.2 Signed-off-by: Marko Baricevic Co-authored-by: Anton Kaliaev --- CHANGELOG.md | 52 ++++++++++++++++++++++++++++++++++++++++++++ CHANGELOG_PENDING.md | 13 +---------- version/version.go | 2 +- 3 files changed, 54 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6040849c1..89315a1b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,57 @@ # Changelog +## v0.33.2 + +*March 11, 2020* + +Special thanks to external contributors on this release: +@antho1404, @michaelfig, @gterzian, @tau3, @Shivani912 + +Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint). + +### BREAKING CHANGES: + +- CLI/RPC/Config + - [cli] [\#4505](https://github.com/tendermint/tendermint/pull/4505) `tendermint lite` sub-command new syntax (@melekes): + `lite cosmoshub-3 -p 52.57.29.196:26657 -w public-seed-node.cosmoshub.certus.one:26657 + --height 962118 --hash 28B97BE9F6DE51AC69F70E0B7BFD7E5C9CD1A595B7DC31AFF27C50D4948` + +- Go API + - [lite2] [\#4535](https://github.com/tendermint/tendermint/pull/4535) Remove `Start/Stop` (@melekes) + - [lite2] [\#4469](https://github.com/tendermint/tendermint/issues/4469) Remove `RemoveNoLongerTrustedHeaders` and `RemoveNoLongerTrustedHeadersPeriod` option (@cmwaters) + - [lite2] [\#4473](https://github.com/tendermint/tendermint/issues/4473) Return height as a 2nd param in `TrustedValidatorSet` (@melekes) + - [lite2] [\#4536](https://github.com/tendermint/tendermint/pull/4536) `Update` returns a signed header (1st param) (@melekes) + + +### IMPROVEMENTS: + +- [blockchain/v2] [\#4361](https://github.com/tendermint/tendermint/pull/4361) Add reactor (@brapse) +- [cmd] [\#4515](https://github.com/tendermint/tendermint/issues/4515) Change `tendermint debug dump` sub-command archives filename's format (@melekes) +- [consensus] [\#3583](https://github.com/tendermint/tendermint/issues/3583) Reduce `non-deterministic signature` log noise (@tau3) +- [examples/kvstore] [\#4507](https://github.com/tendermint/tendermint/issues/4507) ABCI query now returns the proper height (@erikgrinaker) +- [lite2] [\#4462](https://github.com/tendermint/tendermint/issues/4462) Add `NewHTTPClient` and `NewHTTPClientFromTrustedStore` (@cmwaters) +- [lite2] [\#4329](https://github.com/tendermint/tendermint/issues/4329) modified bisection to loop (@cmwaters) +- [lite2] [\#4385](https://github.com/tendermint/tendermint/issues/4385) Disconnect from bad nodes (@melekes) +- [lite2] [\#4398](https://github.com/tendermint/tendermint/issues/4398) Add `VerifyAdjacent` and `VerifyNonAdjacent` funcs (@cmwaters) +- [lite2] [\#4426](https://github.com/tendermint/tendermint/issues/4426) Don't save intermediate headers (@cmwaters) +- [lite2] [\#4464](https://github.com/tendermint/tendermint/issues/4464) Cross-check first header (@cmwaters) +- [lite2] [\#4470](https://github.com/tendermint/tendermint/issues/4470) Fix inconsistent header-validatorset pairing (@melekes) +- [lite2] [\#4488](https://github.com/tendermint/tendermint/issues/4488) Allow local clock drift -10 sec. (@melekes) +- [p2p] [\#4449](https://github.com/tendermint/tendermint/pull/4449) Use `curve25519.X25519()` instead of `ScalarMult` (@erikgrinaker) +- [types] [\#4417](https://github.com/tendermint/tendermint/issues/4417) **VerifyCommitX() functions should return as soon as +2/3 threshold is reached** (@alessio). +- [libs/kv] [\#4542](https://github.com/tendermint/tendermint/pull/4542) remove unused type KI64Pair (@tessr) + +### BUG FIXES: + +- [cmd] [\#4303](https://github.com/tendermint/tendermint/issues/4303) Show useful error when Tendermint is not initialized (@melekes) +- [cmd] [\#4515](https://github.com/tendermint/tendermint/issues/4515) **Fix `tendermint debug kill` sub-command** (@melekes) +- [rpc] [\#3935](https://github.com/tendermint/tendermint/issues/3935) **Create buffered subscriptions on `/subscribe`** (@melekes) +- [rpc] [\#4375](https://github.com/tendermint/tendermint/issues/4375) Stop searching for txs in `/tx_search` upon client timeout (@gterzian) +- [rpc] [\#4406](https://github.com/tendermint/tendermint/pull/4406) Fix issue with multiple subscriptions on the websocket (@antho1404) +- [rpc] [\#4432](https://github.com/tendermint/tendermint/issues/4432) Fix `/tx_search` pagination with ordered results (@erikgrinaker) +- [rpc] [\#4492](https://github.com/tendermint/tendermint/issues/4492) Keep the original subscription "id" field when new RPCs come in (@michaelfig) + + ## v0.33.1 *Feburary 13, 2020* diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index aa0c0c41f..42609c19b 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,4 +1,4 @@ -## v0.33.2 +## v0.33.3 \*\* @@ -18,15 +18,4 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi ### IMPROVEMENTS: -- [types] [\#4417](https://github.com/tendermint/tendermint/issues/4417) VerifyCommitX() functions should return as soon as +2/3 threashold is reached. -- [examples/kvstore] [\#4509](https://github.com/tendermint/tendermint/pull/4509) ABCI query now returns the proper height (@erikgrinaker) -- [cmd] \#4515 Change `tendermint debug dump` sub-command archives filename's format (@melekes) -- [libs/kv]: [\#4542](https://github.com/tendermint/tendermint/pull/4542) remove unused type KI64Pair (@tessr) - ### BUG FIXES: - -- [rpc] \#3935 Create buffered subscriptions on `/subscribe` (@melekes) -- [rpc] [\#4493](https://github.com/tendermint/tendermint/pull/4493) Keep the original subscription "id" field when new RPCs come in (@michaelfig) -- [rpc] [\#4437](https://github.com/tendermint/tendermint/pull/4437) Fix tx_search pagination with ordered results (@erikgrinaker) -- [rpc] [\#4406](https://github.com/tendermint/tendermint/pull/4406) Fix issue with multiple subscriptions on the websocket (@antho1404) -- [cmd] \#4515 Fix `tendermint debug kill` sub-command (@melekes) diff --git a/version/version.go b/version/version.go index 57ef21f1f..9a16e5d58 100644 --- a/version/version.go +++ b/version/version.go @@ -20,7 +20,7 @@ const ( // Must be a string because scripts like dist.sh read this file. // XXX: Don't change the name of this variable or you will break // automation :) - TMCoreSemVer = "0.33.1" + TMCoreSemVer = "0.33.2" // ABCISemVer is the semantic version of the ABCI library ABCISemVer = "0.16.1" From 8c749dc518896ffaddbdf90a7db0dda7ce41e669 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 11 Mar 2020 12:25:37 +0000 Subject: [PATCH 30/77] build(deps): bump google.golang.org/grpc from 1.27.1 to 1.28.0 (#4551) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.27.1 to 1.28.0. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.27.1...v1.28.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 9e916e7e8..bdf8f61b7 100644 --- a/go.mod +++ b/go.mod @@ -28,5 +28,5 @@ require ( github.com/tendermint/tm-db v0.4.1 golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 - google.golang.org/grpc v1.27.1 + google.golang.org/grpc v1.28.0 ) diff --git a/go.sum b/go.sum index 6b5a1cf49..201495da8 100644 --- a/go.sum +++ b/go.sum @@ -57,6 +57,7 @@ github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -83,7 +84,9 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= @@ -133,6 +136,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -531,10 +535,13 @@ google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From cd5a987f6eb74381599ff503ddcd25c1ea9d8bea Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 11 Mar 2020 13:29:40 +0000 Subject: [PATCH 31/77] build(deps): bump github.com/tendermint/tm-db from 0.4.1 to 0.5.0 (#4554) Bumps [github.com/tendermint/tm-db](https://github.com/tendermint/tm-db) from 0.4.1 to 0.5.0. - [Release notes](https://github.com/tendermint/tm-db/releases) - [Changelog](https://github.com/tendermint/tm-db/blob/master/CHANGELOG.md) - [Commits](https://github.com/tendermint/tm-db/compare/v0.4.1...v0.5.0) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index bdf8f61b7..04802b716 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/spf13/viper v1.6.2 github.com/stretchr/testify v1.5.1 github.com/tendermint/go-amino v0.14.1 - github.com/tendermint/tm-db v0.4.1 + github.com/tendermint/tm-db v0.5.0 golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 google.golang.org/grpc v1.28.0 diff --git a/go.sum b/go.sum index 201495da8..97f97b763 100644 --- a/go.sum +++ b/go.sum @@ -143,6 +143,7 @@ github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8l github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -403,6 +404,8 @@ github.com/tendermint/go-amino v0.14.1 h1:o2WudxNfdLNBwMyl2dqOJxiro5rfrEaU0Ugs6o github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= github.com/tendermint/tm-db v0.4.1 h1:TvX7JWjJOVZ+N3y+I86wddrGttOdMmmBxXcu0/Y7ZJ0= github.com/tendermint/tm-db v0.4.1/go.mod h1:JsJ6qzYkCGiGwm5GHl/H5GLI9XLb6qZX7PRe425dHAY= +github.com/tendermint/tm-db v0.5.0 h1:qtM5UTr1dlRnHtDY6y7MZO5Di8XAE2j3lc/pCnKJ5hQ= +github.com/tendermint/tm-db v0.5.0/go.mod h1:lSq7q5WRR/njf1LnhiZ/lIJHk2S8Y1Zyq5oP/3o9C2U= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= From 429febde801348f961d64d8ea82d67287e057e5c Mon Sep 17 00:00:00 2001 From: Callum Michael Waters Date: Wed, 11 Mar 2020 15:00:22 +0100 Subject: [PATCH 32/77] reconfigure mutexes --- p2p/pex/addrbook.go | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index 15019f5cd..c99ea75e5 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -210,12 +210,7 @@ func (a *addrBook) RemoveAddress(addr *p2p.NetAddress) { a.mtx.Lock() defer a.mtx.Unlock() - ka := a.addrLookup[addr.ID] - if ka == nil { - return - } - a.Logger.Info("Remove address from book", "addr", addr) - a.removeFromAllBuckets(ka) + a.removeAddress(addr) } // IsGood returns true if peer was ever marked as good and haven't @@ -332,19 +327,22 @@ func (a *addrBook) MarkAttempt(addr *p2p.NetAddress) { // MarkBad implements AddrBook. Kicks address out from book, places // the address in the badPeers pool. func (a *addrBook) MarkBad(addr *p2p.NetAddress) { + a.mtx.Lock() + defer a.mtx.Unlock() + if a.addBadPeer(addr) { - a.RemoveAddress(addr) + a.removeAddress(addr) } } func (a *addrBook) ReinstateBadPeers() { + a.mtx.Lock() + defer a.mtx.Unlock() for _, ka := range a.badPeers { if !ka.isBanned(defaultBanTime) { - a.mtx.Lock() bucket := a.calcNewBucket(ka.Addr, ka.Src) a.addToNewBucket(ka, bucket) delete(a.badPeers, ka.ID()) - a.mtx.Unlock() } } } @@ -744,6 +742,15 @@ func (a *addrBook) moveToOld(ka *knownAddress) { } } +func (a *addrBook) removeAddress(addr *p2p.NetAddress) { + ka := a.addrLookup[addr.ID] + if ka == nil { + return + } + a.Logger.Info("Remove address from book", "addr", addr) + a.removeFromAllBuckets(ka) +} + func (a *addrBook) addBadPeer(addr *p2p.NetAddress) bool { a.mtx.Lock() defer a.mtx.Unlock() @@ -751,10 +758,12 @@ func (a *addrBook) addBadPeer(addr *p2p.NetAddress) bool { // check it exists in addrbook ka := a.addrLookup[addr.ID] // check address is not already there - if _, alreadyBadPeer := a.badPeers[addr.ID]; ka != nil && !alreadyBadPeer { - // add to bad peer list - ka.ban() - a.badPeers[addr.ID] = ka + if ka != nil { + if _, alreadyBadPeer := a.badPeers[addr.ID]; !alreadyBadPeer { + // add to bad peer list + ka.ban() + a.badPeers[addr.ID] = ka + } return true } return false From 08ccbdcb43c214f2f349d6b2f2d6f6c271985836 Mon Sep 17 00:00:00 2001 From: Callum Michael Waters Date: Wed, 11 Mar 2020 15:06:42 +0100 Subject: [PATCH 33/77] ban function requires a ban duration as an argument --- p2p/pex/addrbook.go | 4 ++-- p2p/pex/known_address.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index c99ea75e5..c3351a600 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -339,7 +339,7 @@ func (a *addrBook) ReinstateBadPeers() { a.mtx.Lock() defer a.mtx.Unlock() for _, ka := range a.badPeers { - if !ka.isBanned(defaultBanTime) { + if !ka.isBanned() { bucket := a.calcNewBucket(ka.Addr, ka.Src) a.addToNewBucket(ka, bucket) delete(a.badPeers, ka.ID()) @@ -761,7 +761,7 @@ func (a *addrBook) addBadPeer(addr *p2p.NetAddress) bool { if ka != nil { if _, alreadyBadPeer := a.badPeers[addr.ID]; !alreadyBadPeer { // add to bad peer list - ka.ban() + ka.ban(defaultBanTime) a.badPeers[addr.ID] = ka } return true diff --git a/p2p/pex/known_address.go b/p2p/pex/known_address.go index 9d4d060a5..fdd055a13 100644 --- a/p2p/pex/known_address.go +++ b/p2p/pex/known_address.go @@ -55,12 +55,12 @@ func (ka *knownAddress) markGood() { ka.LastSuccess = now } -func (ka *knownAddress) ban() { - ka.LastBanTime = time.Now() +func (ka *knownAddress) ban(banTime time.Duration) { + ka.LastBanTime = time.Now().Add(banTime) } -func (ka *knownAddress) isBanned(banTime time.Duration) bool { - return ka.LastBanTime.Add(banTime).Before(time.Now()) +func (ka *knownAddress) isBanned() bool { + return ka.LastBanTime.Before(time.Now()) } func (ka *knownAddress) addBucketRef(bucketIdx int) int { From 4110c252af6ba4202fe948d1a4fc7c35fa84e3e5 Mon Sep 17 00:00:00 2001 From: Callum Michael Waters Date: Wed, 11 Mar 2020 15:44:22 +0100 Subject: [PATCH 34/77] make banTime an argument and set default in PEX reactor instead of AddrBook --- p2p/pex/addrbook.go | 15 +++++++-------- p2p/pex/addrbook_test.go | 28 ++++++++++++++++++++++++---- p2p/pex/pex_reactor.go | 7 +++++-- 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index c3351a600..66a9118e3 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -22,9 +22,8 @@ import ( ) const ( - bucketTypeNew = 0x01 - bucketTypeOld = 0x02 - defaultBanTime = 24 * time.Hour + bucketTypeNew = 0x01 + bucketTypeOld = 0x02 ) // AddrBook is an address book used for tracking peers @@ -60,7 +59,7 @@ type AddrBook interface { // Mark address MarkGood(p2p.ID) MarkAttempt(*p2p.NetAddress) - MarkBad(*p2p.NetAddress) // Move peer to bad peers list + MarkBad(*p2p.NetAddress, time.Duration) // Move peer to bad peers list // Add bad peers back to addrBook ReinstateBadPeers() @@ -326,11 +325,11 @@ func (a *addrBook) MarkAttempt(addr *p2p.NetAddress) { // MarkBad implements AddrBook. Kicks address out from book, places // the address in the badPeers pool. -func (a *addrBook) MarkBad(addr *p2p.NetAddress) { +func (a *addrBook) MarkBad(addr *p2p.NetAddress, banTime time.Duration) { a.mtx.Lock() defer a.mtx.Unlock() - if a.addBadPeer(addr) { + if a.addBadPeer(addr, banTime) { a.removeAddress(addr) } } @@ -751,7 +750,7 @@ func (a *addrBook) removeAddress(addr *p2p.NetAddress) { a.removeFromAllBuckets(ka) } -func (a *addrBook) addBadPeer(addr *p2p.NetAddress) bool { +func (a *addrBook) addBadPeer(addr *p2p.NetAddress, banTime time.Duration) bool { a.mtx.Lock() defer a.mtx.Unlock() @@ -761,7 +760,7 @@ func (a *addrBook) addBadPeer(addr *p2p.NetAddress) bool { if ka != nil { if _, alreadyBadPeer := a.badPeers[addr.ID]; !alreadyBadPeer { // add to bad peer list - ka.ban(defaultBanTime) + ka.ban(banTime) a.badPeers[addr.ID] = ka } return true diff --git a/p2p/pex/addrbook_test.go b/p2p/pex/addrbook_test.go index 363958c44..eaa3280f2 100644 --- a/p2p/pex/addrbook_test.go +++ b/p2p/pex/addrbook_test.go @@ -3,14 +3,13 @@ package pex import ( "encoding/hex" "fmt" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "io/ioutil" "math" "os" "testing" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/libs/log" tmmath "github.com/tendermint/tendermint/libs/math" tmrand "github.com/tendermint/tendermint/libs/rand" @@ -343,7 +342,7 @@ func TestAddrBookGetSelectionWithBias(t *testing.T) { } } - got, expected := int((float64(good)/float64(len(selection)))*100), (100 - biasTowardsNewAddrs) + got, expected := int((float64(good)/float64(len(selection)))*100), 100-biasTowardsNewAddrs // compute some slack to protect against small differences due to rounding: slack := int(math.Round(float64(100) / float64(len(selection)))) @@ -544,6 +543,27 @@ func TestMultipleAddrBookAddressSelection(t *testing.T) { } } +//func TestBanBadPeers(t *testing.T) { +// fname := createTempFileName("addrbook_test") +// defer deleteTempFile(fname) +// +// book := NewAddrBook(fname, true) +// book.SetLogger(log.TestingLogger()) +// +// addr := randIPv4Address(t) +// book.AddAddress(addr, addr) +// +// book.MarkBad(addr, 1 * time.Second) +// +// assert.False(t, book.HasAddress(addr)) +// +// time.Sleep(1 * time.Second) +// +// book.ReinstateBadPeers() +// +// assert.True(t, book.HasAddress(addr)) +//} + func assertMOldAndNNewAddrsInSelection(t *testing.T, m, n int, addrs []*p2p.NetAddress, book *addrBook) { nOld, nNew := countOldAndNewAddrsInSelection(addrs, book) assert.Equal(t, m, nOld, "old addresses") diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index bce9b40ff..afe29e16e 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -50,6 +50,9 @@ const ( // Especially in the beginning, node should have more trusted peers than // untrusted. biasToSelectNewPeers = 30 // 70 to select good peers + + // if a peer is marked bad, it will be banned for at least this time period + defaultBanTime = 24 * time.Hour ) type errMaxAttemptsToDial struct { @@ -535,7 +538,7 @@ func (r *Reactor) dialPeer(addr *p2p.NetAddress) error { // failed to connect to. Then we can clean up attemptsToDial, which acts as // a blacklist currently. // https://github.com/tendermint/tendermint/issues/3572 - r.book.MarkBad(addr) + r.book.MarkBad(addr, defaultBanTime) return errMaxAttemptsToDial{} } @@ -747,7 +750,7 @@ func markAddrInBookBasedOnErr(addr *p2p.NetAddress, book AddrBook, err error) { // TODO: detect more "bad peer" scenarios switch err.(type) { case p2p.ErrSwitchAuthenticationFailure: - book.MarkBad(addr) + book.MarkBad(addr, defaultBanTime) default: book.MarkAttempt(addr) } From 60d375eba62ef8b609cd12ed89fb6ece8cd12be4 Mon Sep 17 00:00:00 2001 From: Callum Michael Waters Date: Wed, 11 Mar 2020 16:27:49 +0100 Subject: [PATCH 35/77] basic test for banning peers --- p2p/pex/addrbook.go | 3 --- p2p/pex/addrbook_test.go | 57 ++++++++++++++++++++++------------------ p2p/pex/known_address.go | 2 +- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index 66a9118e3..00835977e 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -751,9 +751,6 @@ func (a *addrBook) removeAddress(addr *p2p.NetAddress) { } func (a *addrBook) addBadPeer(addr *p2p.NetAddress, banTime time.Duration) bool { - a.mtx.Lock() - defer a.mtx.Unlock() - // check it exists in addrbook ka := a.addrLookup[addr.ID] // check address is not already there diff --git a/p2p/pex/addrbook_test.go b/p2p/pex/addrbook_test.go index eaa3280f2..454b0070f 100644 --- a/p2p/pex/addrbook_test.go +++ b/p2p/pex/addrbook_test.go @@ -5,15 +5,15 @@ import ( "fmt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "io/ioutil" - "math" - "os" - "testing" - "github.com/tendermint/tendermint/libs/log" tmmath "github.com/tendermint/tendermint/libs/math" tmrand "github.com/tendermint/tendermint/libs/rand" "github.com/tendermint/tendermint/p2p" + "io/ioutil" + "math" + "os" + "testing" + "time" ) // FIXME These tests should not rely on .(*addrBook) assertions @@ -395,6 +395,32 @@ func testCreatePrivateAddrs(t *testing.T, numAddrs int) ([]*p2p.NetAddress, []st return addrs, private } +func TestBanBadPeers(t *testing.T) { + fname := createTempFileName("addrbook_test") + defer deleteTempFile(fname) + + book := NewAddrBook(fname, true) + book.SetLogger(log.TestingLogger()) + + addr := randIPv4Address(t) + _ = book.AddAddress(addr, addr) + + book.MarkBad(addr, 1*time.Second) + // addr should not reachable + assert.False(t, book.HasAddress(addr)) + + err := book.AddAddress(addr, addr) + // book should not add address from the blacklist + assert.Error(t, err) + + time.Sleep(1 * time.Second) + book.ReinstateBadPeers() + // address should be reinstated in the new bucket + assert.EqualValues(t, 1, book.Size()) + assert.True(t, book.HasAddress(addr)) + assert.False(t, book.IsGood(addr)) +} + func TestAddrBookEmpty(t *testing.T) { fname := createTempFileName("addrbook_test") defer deleteTempFile(fname) @@ -543,27 +569,6 @@ func TestMultipleAddrBookAddressSelection(t *testing.T) { } } -//func TestBanBadPeers(t *testing.T) { -// fname := createTempFileName("addrbook_test") -// defer deleteTempFile(fname) -// -// book := NewAddrBook(fname, true) -// book.SetLogger(log.TestingLogger()) -// -// addr := randIPv4Address(t) -// book.AddAddress(addr, addr) -// -// book.MarkBad(addr, 1 * time.Second) -// -// assert.False(t, book.HasAddress(addr)) -// -// time.Sleep(1 * time.Second) -// -// book.ReinstateBadPeers() -// -// assert.True(t, book.HasAddress(addr)) -//} - func assertMOldAndNNewAddrsInSelection(t *testing.T, m, n int, addrs []*p2p.NetAddress, book *addrBook) { nOld, nNew := countOldAndNewAddrsInSelection(addrs, book) assert.Equal(t, m, nOld, "old addresses") diff --git a/p2p/pex/known_address.go b/p2p/pex/known_address.go index fdd055a13..674b66553 100644 --- a/p2p/pex/known_address.go +++ b/p2p/pex/known_address.go @@ -60,7 +60,7 @@ func (ka *knownAddress) ban(banTime time.Duration) { } func (ka *knownAddress) isBanned() bool { - return ka.LastBanTime.Before(time.Now()) + return ka.LastBanTime.After(time.Now()) } func (ka *knownAddress) addBucketRef(bucketIdx int) int { From 65d86bcad192574bb8fa0b359e935bff3965df83 Mon Sep 17 00:00:00 2001 From: Callum Michael Waters Date: Wed, 11 Mar 2020 16:31:03 +0100 Subject: [PATCH 36/77] added banned address error --- p2p/pex/errors.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/p2p/pex/errors.go b/p2p/pex/errors.go index 911389a9e..1ea5a3b3e 100644 --- a/p2p/pex/errors.go +++ b/p2p/pex/errors.go @@ -63,3 +63,11 @@ type ErrAddrBookInvalidAddr struct { func (err ErrAddrBookInvalidAddr) Error() string { return fmt.Sprintf("Cannot add invalid address %v: %v", err.Addr, err.AddrErr) } + +type ErrAddressBanned struct { + Addr *p2p.NetAddress +} + +func (err ErrAddressBanned) Error() string { + return fmt.Sprintf("Address: %v is currently banned", err.Addr) +} From 2f2d62efed379bab2b2873e8c1d4ea3d7b1fa0e2 Mon Sep 17 00:00:00 2001 From: Callum Michael Waters Date: Wed, 11 Mar 2020 16:33:45 +0100 Subject: [PATCH 37/77] banned addresses can't be added again --- p2p/pex/addrbook.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index 00835977e..4913373ae 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -608,6 +608,10 @@ func (a *addrBook) addAddress(addr, src *p2p.NetAddress) error { return ErrAddrBookInvalidAddr{Addr: addr, AddrErr: err} } + if _, ok := a.badPeers[addr.ID]; ok { + return ErrAddressBanned{addr} + } + if _, ok := a.privateIDs[addr.ID]; ok { return ErrAddrBookPrivate{addr} } From 1e37a1f3e489eb97d132d6d92277e5f341e1ba24 Mon Sep 17 00:00:00 2001 From: Callum Michael Waters Date: Wed, 11 Mar 2020 16:43:32 +0100 Subject: [PATCH 38/77] added isBanned check in addrbook --- p2p/pex/addrbook.go | 9 +++++++++ p2p/pex/addrbook_test.go | 1 + 2 files changed, 10 insertions(+) diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index 4913373ae..1637129c4 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -64,6 +64,7 @@ type AddrBook interface { ReinstateBadPeers() IsGood(*p2p.NetAddress) bool + IsBanned(*p2p.NetAddress) bool // Send a selection of addresses to peers GetSelection() []*p2p.NetAddress @@ -221,6 +222,14 @@ func (a *addrBook) IsGood(addr *p2p.NetAddress) bool { return a.addrLookup[addr.ID].isOld() } +func (a *addrBook) IsBanned(addr *p2p.NetAddress) bool { + a.mtx.Lock() + defer a.mtx.Unlock() + + _, ok := a.badPeers[addr.ID] + return ok +} + // HasAddress returns true if the address is in the book. func (a *addrBook) HasAddress(addr *p2p.NetAddress) bool { a.mtx.Lock() diff --git a/p2p/pex/addrbook_test.go b/p2p/pex/addrbook_test.go index 454b0070f..51e968693 100644 --- a/p2p/pex/addrbook_test.go +++ b/p2p/pex/addrbook_test.go @@ -408,6 +408,7 @@ func TestBanBadPeers(t *testing.T) { book.MarkBad(addr, 1*time.Second) // addr should not reachable assert.False(t, book.HasAddress(addr)) + assert.True(t, book.IsBanned(addr)) err := book.AddAddress(addr, addr) // book should not add address from the blacklist From 5de6ec78e58544b7febb55da6ae523edab8bdac1 Mon Sep 17 00:00:00 2001 From: Callum Michael Waters Date: Wed, 11 Mar 2020 16:48:06 +0100 Subject: [PATCH 39/77] added logs for more information --- p2p/pex/addrbook.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index 1637129c4..207b6a1c3 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -351,6 +351,7 @@ func (a *addrBook) ReinstateBadPeers() { bucket := a.calcNewBucket(ka.Addr, ka.Src) a.addToNewBucket(ka, bucket) delete(a.badPeers, ka.ID()) + a.Logger.Info("Reinstated Address", "addr", ka.Addr) } } } @@ -772,6 +773,7 @@ func (a *addrBook) addBadPeer(addr *p2p.NetAddress, banTime time.Duration) bool // add to bad peer list ka.ban(banTime) a.badPeers[addr.ID] = ka + a.Logger.Info("Add address to blacklist", "addr", addr) } return true } From dbf02200dea000eb8d7fa18952569c305005a2cc Mon Sep 17 00:00:00 2001 From: Callum Michael Waters Date: Wed, 11 Mar 2020 17:16:46 +0100 Subject: [PATCH 40/77] lint fix --- p2p/pex/addrbook_test.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/p2p/pex/addrbook_test.go b/p2p/pex/addrbook_test.go index 51e968693..e5a226daf 100644 --- a/p2p/pex/addrbook_test.go +++ b/p2p/pex/addrbook_test.go @@ -3,17 +3,18 @@ package pex import ( "encoding/hex" "fmt" + "io/ioutil" + "math" + "os" + "testing" + "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/log" tmmath "github.com/tendermint/tendermint/libs/math" tmrand "github.com/tendermint/tendermint/libs/rand" "github.com/tendermint/tendermint/p2p" - "io/ioutil" - "math" - "os" - "testing" - "time" ) // FIXME These tests should not rely on .(*addrBook) assertions From 4ef506f7baf77ba427d7c840a2134d8fb786c85b Mon Sep 17 00:00:00 2001 From: Marko Date: Wed, 11 Mar 2020 18:02:41 +0100 Subject: [PATCH 41/77] deps: bump deps that bot cant (#4555) * deps: bump deps that bot cant - bumping deps that dependat bot does not do. Signed-off-by: Marko Baricevic * run go mod tidy * fix go.sum --- go.mod | 14 +++++++------- go.sum | 41 +++++++++++++++++++++++++++-------------- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 04802b716..c86c1be2d 100644 --- a/go.mod +++ b/go.mod @@ -3,22 +3,22 @@ module github.com/tendermint/tendermint go 1.13 require ( - github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f + github.com/ChainSafe/go-schnorrkel v0.0.0-20200115165343-aa45d48b5ed6 github.com/Workiva/go-datastructures v1.0.52 - github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d - github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a + github.com/btcsuite/btcd v0.20.1-beta + github.com/btcsuite/btcutil v1.0.1 github.com/fortytw2/leaktest v1.3.0 github.com/go-kit/kit v0.10.0 github.com/go-logfmt/logfmt v0.5.0 github.com/gogo/protobuf v1.3.1 github.com/golang/protobuf v1.3.4 github.com/gorilla/websocket v1.4.1 - github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f + github.com/gtank/merlin v0.1.1 github.com/libp2p/go-buffer-pool v0.0.2 github.com/magiconair/properties v1.8.1 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.5.0 - github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a + github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563 github.com/rs/cors v1.7.0 github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa github.com/spf13/cobra v0.0.6 @@ -26,7 +26,7 @@ require ( github.com/stretchr/testify v1.5.1 github.com/tendermint/go-amino v0.14.1 github.com/tendermint/tm-db v0.5.0 - golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 - golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 + golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 + golang.org/x/net v0.0.0-20200301022130-244492dfa37a google.golang.org/grpc v1.28.0 ) diff --git a/go.sum b/go.sum index 97f97b763..6c7ab4b19 100644 --- a/go.sum +++ b/go.sum @@ -2,18 +2,17 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f h1:4O1om+UVU+Hfcihr1timk8YNXHxzZWgCo7ofnrZRApw= -github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= +github.com/ChainSafe/go-schnorrkel v0.0.0-20200115165343-aa45d48b5ed6 h1:wfM1NefWZdwihyFmcpWjBSP2lMEIWj+WHpbjNi+jIQo= +github.com/ChainSafe/go-schnorrkel v0.0.0-20200115165343-aa45d48b5ed6/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/Workiva/go-datastructures v1.0.51 h1:LJHjjfcv+1gH+1D1SgrjcrF8iSZkgsAdCjclvHvVecQ= -github.com/Workiva/go-datastructures v1.0.51/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9abU0yMQt0NI= github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= +github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -38,15 +37,23 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d h1:xG8Pj6Y6J760xwETNmMzmlt38QSwz0BLp1cZ09g27uw= -github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= +github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a h1:RQMUrEILyYJEoAT34XS/kLu40vC0+po/UfxrBBA4qZE= -github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v1.0.1 h1:GKOz8BnRjYrb/JTKgaOk+zh26NWNdSNvdvv0xoAZMSA= +github.com/btcsuite/btcutil v1.0.1/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd h1:qdGvebPBDuYDPGi1WCPjy1tGyMpmDK8IEapSsszn7HE= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723 h1:ZA/jbKoGcVAnER6pCHPEkGdZOV7U1oLUedErBHCUMs0= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= @@ -173,6 +180,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f h1:8N8XWLZelZNibkhM1FuF+3Ad3YIbgirjdMiVA0eUkaM= github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= +github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc= github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= @@ -203,11 +212,13 @@ github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmK github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89 h1:12K8AlpT0/6QUXSfV0yi4Q0jkbq8NDtIKFtF61AoqV0= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -219,6 +230,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= @@ -310,8 +322,6 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0XyUVEcDsc= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= -github.com/prometheus/client_golang v1.4.1 h1:FFSuS004yOQEtDdTq+TAOLP5xUq63KqAFYyOi8zA+Y8= -github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.5.0 h1:Ctq0iGpCmr3jeP77kbF2UxgvRwzWWz+4Bh9/vJTyg1A= github.com/prometheus/client_golang v1.5.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= @@ -345,6 +355,8 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563 h1:dY6ETXrvDG7Sa4vE8ZQG4yqWg6UnOcbqTAahkV813vQ= +github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= @@ -402,8 +414,6 @@ github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzH github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= github.com/tendermint/go-amino v0.14.1 h1:o2WudxNfdLNBwMyl2dqOJxiro5rfrEaU0Ugs6offJMk= github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= -github.com/tendermint/tm-db v0.4.1 h1:TvX7JWjJOVZ+N3y+I86wddrGttOdMmmBxXcu0/Y7ZJ0= -github.com/tendermint/tm-db v0.4.1/go.mod h1:JsJ6qzYkCGiGwm5GHl/H5GLI9XLb6qZX7PRe425dHAY= github.com/tendermint/tm-db v0.5.0 h1:qtM5UTr1dlRnHtDY6y7MZO5Di8XAE2j3lc/pCnKJ5hQ= github.com/tendermint/tm-db v0.5.0/go.mod h1:lSq7q5WRR/njf1LnhiZ/lIJHk2S8Y1Zyq5oP/3o9C2U= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -438,6 +448,9 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -467,6 +480,8 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8 golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= @@ -541,8 +556,6 @@ google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= From 864ce4be7996670ef9016bb2b1baf4d72c1aa9cb Mon Sep 17 00:00:00 2001 From: Tess Rinearson Date: Thu, 12 Mar 2020 07:08:09 +0100 Subject: [PATCH 42/77] tools/build: delete stale tools (#4558) --- tools/build/Makefile | 289 ------------------------------------ tools/build/tendermint.list | 1 - 2 files changed, 290 deletions(-) delete mode 100644 tools/build/Makefile delete mode 100644 tools/build/tendermint.list diff --git a/tools/build/Makefile b/tools/build/Makefile deleted file mode 100644 index 1c2094427..000000000 --- a/tools/build/Makefile +++ /dev/null @@ -1,289 +0,0 @@ -## -# Extra checks, because we do not use autoconf. -## - -requirements_check = true -gpg_check = false -go_min_version = 1.13 -gpg_key = 2122CBE9 - -ifeq ($(requirements_check),true) -ifndef GOPATH -$(error GOPATH not set) -else -go_version := $(shell go version | sed "s/^.* go\([0-9\.]*\) .*$$/\1/" ) -$(info Found go version $(go_version)) -go_version_check := $(shell echo -e "$(go_min_version)\n$(go_version)" | sort -V | head -1) -ifneq ($(go_min_version),$(go_version_check)) -$(error go version go_min_version or above is required) -endif -endif -ifeq ($(gpg_check),true) -gpg_check := $(shell gpg -K | grep '/$(gpg_key) ' | sed 's,^.*/\($(gpg_key)\) .*$$,\1,') -ifneq ($(gpg_check),$(gpg_key)) -$(error GPG key $(gpg_key) not found.) -else -$(info GPG key $(gpg_key) found) -endif -ifndef GPG_PASSPHRASE -$(error GPG_PASSPHRASE not set) -endif -endif -endif - -### -# Here comes the real deal -### - -binaries = tendermint basecoind ethermint gaia -build-binaries = build-tendermint build-basecoind build-ethermint build-gaia -package-rpm = package-rpm-tendermint package-rpm-basecoind package-rpm-ethermint package-rpm-gaia -install-rpm = install-rpm-tendermint install-rpm-basecoind install-rpm-ethermint install-rpm-gaia -package-deb = package-deb-tendermint package-deb-basecoind package-deb-ethermint package-deb-gaia -install-deb = install-deb-tendermint install-deb-basecoind install-deb-ethermint install-deb-gaia - -all: $(binaries) -build: $(build-binaries) -package: $(package-rpm) $(package-deb) -install: $(install-rpm) $(install-deb) -$(binaries): %: build-% package-rpm-% package-deb-% - -### -# Build the binaries -### - -git-branch: - $(eval GIT_BRANCH=$(shell echo $${GIT_BRANCH:-master})) - -gopath-setup: - test -d $(GOPATH) || mkdir -p $(GOPATH) - test -d $(GOPATH)/bin || mkdir -p $(GOPATH)/bin - test -d $(GOPATH)/src || mkdir -p $(GOPATH)/src - -build-tendermint: git-branch gopath-setup - @echo "*** Building tendermint" - go get -d -u github.com/tendermint/tendermint/cmd/tendermint - cd $(GOPATH)/src/github.com/tendermint/tendermint && git checkout "$(GIT_BRANCH)" && git pull - export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/tendermint/tendermint tools build - cp $(GOPATH)/src/github.com/tendermint/tendermint/build/tendermint $(GOPATH)/bin - @echo "*** Built tendermint" - -build-ethermint: git-branch gopath-setup - @echo "*** Building ethermint" - go get -d -u github.com/tendermint/ethermint/cmd/ethermint - cd $(GOPATH)/src/github.com/tendermint/ethermint && git checkout "$(GIT_BRANCH)" && git pull - export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/tendermint/ethermint build - cp $(GOPATH)/src/github.com/tendermint/ethermint/build/ethermint $(GOPATH)/bin - @echo "*** Built ethermint" - -build-gaia: git-branch gopath-setup - @echo "*** Building gaia" - go get -d -u go github.com/cosmos/gaia || echo "Workaround for go downloads." - cd $(GOPATH)/src/github.com/cosmos/gaia && git checkout "$(GIT_BRANCH)" && git pull - export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/cosmos/gaia install - @echo "*** Built gaia" - -build-basecoind: git-branch gopath-setup - @echo "*** Building basecoind from cosmos-sdk" - go get -d -u github.com/cosmos/cosmos-sdk/examples/basecoin/cmd/basecoind - cd $(GOPATH)/src/github.com/cosmos/cosmos-sdk && git checkout "$(GIT_BRANCH)" && git pull - export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/cosmos/cosmos-sdk tools build - cp $(GOPATH)/src/github.com/cosmos/cosmos-sdk/build/basecoind $(GOPATH)/bin/basecoind - @echo "*** Built basecoind from cosmos-sdk" - -### -# Prepare package files -### - -# set app_version -version-%: - @echo "Checking if binary exists" - test -f $(GOPATH)/bin/$* - @echo "BUILD_NUMBER is $(BUILD_NUMBER)" - test -n "$(BUILD_NUMBER)" - $(eval $*_version=$(shell $(GOPATH)/bin/$* version | head -1 | cut -d- -f1 | sed 's/^\(ethermint:\s*\|\)\(v\|\)//' | tr -d '\t ' )) - -# set build_folder -folder-%: version-% - $(eval build_folder=BUILD/$*-$($*_version)-$(BUILD_NUMBER)) - -# clean up folder structure for package files -prepare-files = rm -rf $(build_folder) && mkdir -p $(build_folder) && cp -r ./$(1)/* $(build_folder) && mkdir -p $(build_folder)/usr/bin && cp $(GOPATH)/bin/$(1) $(build_folder)/usr/bin - -## -## Package customizations for the different applications -## - -prepare-tendermint = -prepare-ethermint = mkdir -p $(build_folder)/etc/ethermint && \ - cp $(GOPATH)/src/github.com/tendermint/ethermint/setup/genesis.json $(build_folder)/etc/ethermint/genesis.json && \ - cp -r $(GOPATH)/src/github.com/tendermint/ethermint/setup/keystore $(build_folder)/etc/ethermint -prepare-gaia = -prepare-basecoind = cp $(GOPATH)/bin/basecoind $(build_folder)/usr/bin - -### -# Package the binary for CentOS/RedHat (RPM) and Debian/Ubuntu (DEB) -### - -# Depends on rpmbuild, sorry, this can only be built on CentOS/RedHat machines. -package-rpm-%: folder-% - @echo "*** Packaging RPM $* version $($*_version)" - - $(call prepare-files,$*) - $(call prepare-$*) - - rm -rf $(build_folder)/DEBIAN - mkdir -p $(build_folder)/usr/share/licenses/$* - cp ./LICENSE $(build_folder)/usr/share/licenses/$*/LICENSE - chmod -Rf a+rX,u+w,g-w,o-w $(build_folder) - - mkdir -p {SPECS,tmp} - - ./generate-spec $* spectemplates SPECS - sed -i "s/@VERSION@/$($*_version)/" SPECS/$*.spec - sed -i "s/@BUILD_NUMBER@/$(BUILD_NUMBER)/" SPECS/$*.spec - sed -i "s/@PACKAGE_NAME@/$*/" SPECS/$*.spec - - rpmbuild -bb SPECS/$*.spec --define "_topdir `pwd`" --define "_tmppath `pwd`/tmp" - ./sign RPMS/x86_64/$*-$($*_version)-$(BUILD_NUMBER).x86_64.rpm "$(gpg_key)" "`which gpg`" - rpm -Kv RPMS/x86_64/$*-$($*_version)-$(BUILD_NUMBER).x86_64.rpm || echo "rpm returns non-zero exist for some reason. ($?)" - @echo "*** Packaged RPM $* version $($*_version)" - -package-deb-%: folder-% - @echo "*** Packaging DEB $* version $($*_version)-$(BUILD_NUMBER)" - - $(call prepare-files,$*) - $(call prepare-$*) - - mkdir -p $(build_folder)/usr/share/doc/$* - cp $(build_folder)/DEBIAN/copyright $(build_folder)/usr/share/doc/$* - chmod -Rf a+rX,u+w,g-w,o-w $(build_folder) - - sed -i "s/@VERSION@/$($*_version)-$(BUILD_NUMBER)/" $(build_folder)/DEBIAN/changelog - sed -i "s/@STABILITY@/stable/" $(build_folder)/DEBIAN/changelog - sed -i "s/@DATETIMESTAMP@/`date +%a,\ %d\ %b\ %Y\ %T\ %z`/" $(build_folder)/DEBIAN/changelog - sed -i "s/@VERSION@/$($*_version)-$(BUILD_NUMBER)/" $(build_folder)/DEBIAN/control - - gzip -c $(build_folder)/DEBIAN/changelog > $(build_folder)/usr/share/doc/$*/changelog.Debian.gz - gzip -c $(build_folder)/DEBIAN/changelog > $(build_folder)/usr/share/doc/$*/changelog.Debian.amd64.gz - sed -i "s/@INSTALLEDSIZE@/`du -ks $(build_folder) | cut -f 1`/" $(build_folder)/DEBIAN/control - - cd $(build_folder) && tar --owner=root --group=root -cvJf ../../tmp/data.tar.xz --exclude DEBIAN * - cd $(build_folder)/DEBIAN && tar --owner=root --group=root -cvzf ../../../tmp/control.tar.gz * - echo "2.0" > tmp/debian-binary - - cp ./_gpg tmp/ - cd tmp && sed -i "s/@DATETIMESTAMP@/`date +%a\ %b\ %d\ %T\ %Y`/" _gpg - cd tmp && sed -i "s/@BINMD5@/`md5sum debian-binary | cut -d\ -f1`/" _gpg - cd tmp && sed -i "s/@BINSHA1@/`sha1sum debian-binary | cut -d\ -f1`/" _gpg - cd tmp && sed -i "s/@BINSIZE@/`stat -c %s debian-binary | cut -d\ -f1`/" _gpg - cd tmp && sed -i "s/@CONMD5@/`md5sum control.tar.gz | cut -d\ -f1`/" _gpg - cd tmp && sed -i "s/@CONSHA1@/`sha1sum control.tar.gz | cut -d\ -f1`/" _gpg - cd tmp && sed -i "s/@CONSIZE@/`stat -c %s control.tar.gz | cut -d\ -f1`/" _gpg - cd tmp && sed -i "s/@DATMD5@/`md5sum data.tar.xz | cut -d\ -f1`/" _gpg - cd tmp && sed -i "s/@DATSHA1@/`sha1sum data.tar.xz | cut -d\ -f1`/" _gpg - cd tmp && sed -i "s/@DATSIZE@/`stat -c %s data.tar.xz | cut -d\ -f1`/" _gpg - gpg --batch --passphrase "$(GPG_PASSPHRASE)" --clearsign tmp/_gpg - mv tmp/_gpg.asc tmp/_gpgbuilder - ar r tmp/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb tmp/debian-binary tmp/control.tar.gz tmp/data.tar.xz tmp/_gpgbuilder - mv tmp/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb RPMS/ - rm tmp/debian-binary tmp/control.tar.gz tmp/data.tar.xz tmp/_gpgbuilder tmp/_gpg - @echo "*** Packaged DEB $* version $($*_version)-$(BUILD_NUMBER)" - -install-rpm-%: version-% -#Make sure your host has the IAM role to read/write the S3 bucket OR that you set up ~/.boto - @echo "*** Uploading $*-$($*_version)-$(BUILD_NUMBER).x86_64.rpm to AWS $(DEVOPS_PATH)CentOS repository" - aws s3 sync s3://tendermint-packages/$(DEVOPS_PATH)centos/ tmp/s3/ --delete - mkdir -p tmp/s3/7/os/x86_64/Packages - cp RPMS/x86_64/$*-$($*_version)-$(BUILD_NUMBER).x86_64.rpm tmp/s3/7/os/x86_64/Packages - cp ./RPM-GPG-KEY-Tendermint tmp/s3/7/os/x86_64/ - cp ./tendermint.repo tmp/s3/7/os/x86_64/ - rm -f tmp/s3/7/os/x86_64/repodata/*.bz2 tmp/s3/7/os/x86_64/repodata/*.gz tmp/s3/7/os/x86_64/repodata/repomd.xml.asc - createrepo tmp/s3/7/os/x86_64/Packages -u https://tendermint-packages.interblock.io/$(DEVOPS_PATH)centos/7/os/x86_64/Packages -o tmp/s3/7/os/x86_64 --update -S --repo Tendermint --content tendermint --content basecoind --content ethermint - gpg --batch --passphrase "$(GPG_PASSPHRASE)" --detach-sign -a tmp/s3/7/os/x86_64/repodata/repomd.xml - aws s3 sync tmp/s3/ s3://tendermint-packages/$(DEVOPS_PATH)centos/ --delete --acl public-read - @echo "*** Uploaded $* to AWS $(DEVOPS_PATH)CentOS repository" - -install-deb-%: version-% - @echo "*** Uploading $*-$($*_version)-$(BUILD_NUMBER)_amd64.deb to AWS $(DEVOPS_PATH)Debian repository" - @echo "Testing if $*-$($*_version)-$(BUILD_NUMBER)_amd64.deb is already uploaded" - test ! -f tmp/debian-s3/pool/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb - aws s3 sync s3://tendermint-packages/$(DEVOPS_PATH)debian/ tmp/debian-s3/ --delete - @echo "Testing if $*-$($*_version)-$(BUILD_NUMBER)_amd64.deb is already uploaded" - test ! -f tmp/debian-s3/pool/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb - cp ./tendermint.list tmp/debian-s3/ - mkdir -p tmp/debian-s3/pool tmp/debian-s3/dists/stable/main/binary-amd64 - cp RPMS/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb tmp/debian-s3/pool - cp ./Release_amd64 tmp/debian-s3/dists/stable/main/binary-amd64/Release - - #Packages / Packages.gz - - echo > tmp/Package - echo "Filename: pool/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb" >> tmp/Package - echo "MD5sum: `md5sum RPMS/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb | cut -d\ -f 1`" >> tmp/Package - echo "SHA1: `sha1sum RPMS/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb | cut -d\ -f 1`" >> tmp/Package - echo "SHA256: `sha256sum RPMS/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb | cut -d\ -f 1`" >> tmp/Package - echo "Size: `stat -c %s RPMS/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb | cut -d\ -f 1`" >> tmp/Package - cat BUILD/$*-$($*_version)-$(BUILD_NUMBER)/DEBIAN/control >> tmp/Package - - cat tmp/Package >> tmp/debian-s3/dists/stable/main/binary-amd64/Packages - rm -f tmp/debian-s3/dists/stable/main/binary-amd64/Packages.gz - gzip -c tmp/debian-s3/dists/stable/main/binary-amd64/Packages > tmp/debian-s3/dists/stable/main/binary-amd64/Packages.gz - rm -f tmp/Package - - #main / Release / InRelease / Release.gpg - - cp ./Release tmp/debian-s3/dists/stable/main/Release - rm -f tmp/debian-s3/dists/stable/main/InRelease - rm -f tmp/debian-s3/dists/stable/main/Release.gpg - - echo "MD5Sum:" >> tmp/debian-s3/dists/stable/main/Release - cd tmp/debian-s3/dists/stable/main && for f in `find . -type f | sed 's/^.\///'` ; do test "$$f" == "Release" && continue ; echo -n " " ; md5sum $$f | sed "s/ / `stat -c %s $$f` /" ; done >> Release - echo "SHA1:" >> tmp/debian-s3/dists/stable/main/Release - cd tmp/debian-s3/dists/stable/main && for f in `find . -type f | sed 's/^.\///'` ; do test "$$f" == "Release" && continue ; echo -n " " ; sha1sum $$f | sed "s/ / `stat -c %s $$f` /" ; done >> Release - echo "SHA256:" >> tmp/debian-s3/dists/stable/main/Release - cd tmp/debian-s3/dists/stable/main && for f in `find . -type f | sed 's/^.\///'` ; do test "$$f" == "Release" && continue ; echo -n " " ; sha256sum $$f | sed "s/ / `stat -c %s $$f` /" ; done >> Release - - gpg --batch --passphrase "$(GPG_PASSPHRASE)" --digest-algo SHA256 -b -a tmp/debian-s3/dists/stable/main/Release - mv tmp/debian-s3/dists/stable/main/Release.asc tmp/debian-s3/dists/stable/main/Release.gpg - gpg --batch --passphrase "$(GPG_PASSPHRASE)" --digest-algo SHA512 --clearsign tmp/debian-s3/dists/stable/main/Release - mv tmp/debian-s3/dists/stable/main/Release.asc tmp/debian-s3/dists/stable/main/InRelease - - #stable / Release / InRelease / Release.gpg - - cp ./Release tmp/debian-s3/dists/stable/Release - rm -f tmp/debian-s3/dists/stable/InRelease - rm -f tmp/debian-s3/dists/stable/Release.gpg - - echo "MD5Sum:" >> tmp/debian-s3/dists/stable/Release - cd tmp/debian-s3/dists/stable && for f in `find . -type f | sed 's/^.\///'` ; do test "$$f" == "Release" && continue ; echo -n " " ; md5sum $$f | sed "s/ / `stat -c %s $$f` /" ; done >> Release - echo "SHA1:" >> tmp/debian-s3/dists/stable/Release - cd tmp/debian-s3/dists/stable && for f in `find . -type f | sed 's/^.\///'` ; do test "$$f" == "Release" && continue ; echo -n " " ; sha1sum $$f | sed "s/ / `stat -c %s $$f` /" ; done >> Release - echo "SHA256:" >> tmp/debian-s3/dists/stable/Release - cd tmp/debian-s3/dists/stable && for f in `find . -type f | sed 's/^.\///'` ; do test "$$f" == "Release" && continue ; echo -n " " ; sha256sum $$f | sed "s/ / `stat -c %s $$f` /" ; done >> Release - - gpg --batch --passphrase "$(GPG_PASSPHRASE)" --digest-algo SHA256 -b -a tmp/debian-s3/dists/stable/Release - mv tmp/debian-s3/dists/stable/Release.asc tmp/debian-s3/dists/stable/Release.gpg - gpg --batch --passphrase "$(GPG_PASSPHRASE)" --digest-algo SHA512 --clearsign tmp/debian-s3/dists/stable/Release - mv tmp/debian-s3/dists/stable/Release.asc tmp/debian-s3/dists/stable/InRelease - - aws s3 sync tmp/debian-s3/ s3://tendermint-packages/$(DEVOPS_PATH)debian/ --delete --acl public-read - @echo "*** Uploaded $*-$($*_version)-$(BUILD_NUMBER)_amd64.deb to AWS $(DEVOPS_PATH)Debian repository" - -mostlyclean: - rm -rf {BUILDROOT,SOURCES,SPECS,SRPMS,tmp} - -clean: mostlyclean - rm -rf {BUILD,RPMS} - -distclean: clean - rm -rf $(GOPATH)/src/github.com/tendermint/tendermint - rm -rf $(GOPATH)/src/github.com/cosmos/cosmos-sdk - rm -rf $(GOPATH)/src/github.com/tendermint/ethermint - rm -rf $(GOPATH)/bin/tendermint - rm -rf $(GOPATH)/bin/basecoind - rm -rf $(GOPATH)/bin/ethermint - rm -rf $(GOPATH)/bin/gaia - -.PHONY : clean - diff --git a/tools/build/tendermint.list b/tools/build/tendermint.list deleted file mode 100644 index bba521af5..000000000 --- a/tools/build/tendermint.list +++ /dev/null @@ -1 +0,0 @@ -deb http://tendermint-packages.s3-website-us-west-1.amazonaws.com/debian stable main From 038aff1fdbee661196a420818bfd75b208c025ba Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Thu, 12 Mar 2020 07:57:06 +0100 Subject: [PATCH 43/77] lite2: add benchmarking tests (#4514) Closes #4392 Refs #4504 --- lite2/client_benchmark_test.go | 104 +++++++++++++++++++++++++++++++++ lite2/test_helpers.go | 58 ++++++++++++++++++ lite2/verifier.go | 3 +- 3 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 lite2/client_benchmark_test.go diff --git a/lite2/client_benchmark_test.go b/lite2/client_benchmark_test.go new file mode 100644 index 000000000..823ff1156 --- /dev/null +++ b/lite2/client_benchmark_test.go @@ -0,0 +1,104 @@ +package lite + +import ( + "testing" + "time" + + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/lite2/provider" + mockp "github.com/tendermint/tendermint/lite2/provider/mock" + dbs "github.com/tendermint/tendermint/lite2/store/db" + dbm "github.com/tendermint/tm-db" +) + +// NOTE: block is produced every minute. Make sure the verification time +// provided in the function call is correct for the size of the blockchain. The +// benchmarking may take some time hence it can be more useful to set the time +// or the amount of iterations use the flag -benchtime t -> i.e. -benchtime 5m +// or -benchtime 100x. +// +// Remember that none of these benchmarks account for network latency. +var ( + largeFullNode = mockp.New(GenMockNode(chainID, 1000, 100, 1, bTime)) + genesisHeader, _ = largeFullNode.SignedHeader(1) +) + +func BenchmarkSequence(b *testing.B) { + c, err := NewClient( + chainID, + TrustOptions{ + Period: 24 * time.Hour, + Height: 1, + Hash: genesisHeader.Hash(), + }, + largeFullNode, + []provider.Provider{largeFullNode}, + dbs.New(dbm.NewMemDB(), chainID), + Logger(log.TestingLogger()), + SequentialVerification(), + ) + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + + for n := 0; n < b.N; n++ { + _, err = c.VerifyHeaderAtHeight(1000, bTime.Add(1000*time.Minute)) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkBisection(b *testing.B) { + c, err := NewClient( + chainID, + TrustOptions{ + Period: 24 * time.Hour, + Height: 1, + Hash: genesisHeader.Hash(), + }, + largeFullNode, + []provider.Provider{largeFullNode}, + dbs.New(dbm.NewMemDB(), chainID), + Logger(log.TestingLogger()), + ) + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + + for n := 0; n < b.N; n++ { + _, err = c.VerifyHeaderAtHeight(1000, bTime.Add(1000*time.Minute)) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkBackwards(b *testing.B) { + trustedHeader, _ := largeFullNode.SignedHeader(0) + c, err := NewClient( + chainID, + TrustOptions{ + Period: 24 * time.Hour, + Height: trustedHeader.Height, + Hash: trustedHeader.Hash(), + }, + largeFullNode, + []provider.Provider{largeFullNode}, + dbs.New(dbm.NewMemDB(), chainID), + Logger(log.TestingLogger()), + ) + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + + for n := 0; n < b.N; n++ { + _, err = c.VerifyHeaderAtHeight(1, bTime) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/lite2/test_helpers.go b/lite2/test_helpers.go index cc1bf4eb9..504e9569e 100644 --- a/lite2/test_helpers.go +++ b/lite2/test_helpers.go @@ -160,3 +160,61 @@ func (pkz privKeys) GenSignedHeaderLastBlockID(chainID string, height int64, bTi Commit: pkz.signHeader(header, first, last), } } + +func (pkz privKeys) ChangeKeys(delta int) privKeys { + newKeys := pkz[delta:] + return newKeys.Extend(delta) +} + +// Generates the header and validator set to create a full entire mock node with blocks to height ( +// blockSize) and with variation in validator sets. BlockIntervals are in per minute. +// NOTE: Expected to have a large validator set size ~ 100 validators. +func GenMockNode( + chainID string, + blockSize int64, + valSize int, + valVariation float32, + bTime time.Time) ( + string, + map[int64]*types.SignedHeader, + map[int64]*types.ValidatorSet) { + + var ( + headers = make(map[int64]*types.SignedHeader, blockSize) + valset = make(map[int64]*types.ValidatorSet, blockSize) + keys = genPrivKeys(valSize) + totalVariation = valVariation + valVariationInt int + newKeys privKeys + ) + + valVariationInt = int(totalVariation) + totalVariation = -float32(valVariationInt) + newKeys = keys.ChangeKeys(valVariationInt) + + // genesis header and vals + lastHeader := keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Minute), nil, + keys.ToValidators(2, 2), newKeys.ToValidators(2, 2), []byte("app_hash"), []byte("cons_hash"), + []byte("results_hash"), 0, len(keys)) + currentHeader := lastHeader + headers[1] = currentHeader + valset[1] = keys.ToValidators(2, 2) + keys = newKeys + + for height := int64(2); height <= blockSize; height++ { + totalVariation += valVariation + valVariationInt = int(totalVariation) + totalVariation = -float32(valVariationInt) + newKeys = keys.ChangeKeys(valVariationInt) + currentHeader = keys.GenSignedHeaderLastBlockID(chainID, height, bTime.Add(time.Duration(height)*time.Minute), + nil, + keys.ToValidators(2, 2), newKeys.ToValidators(2, 2), []byte("app_hash"), []byte("cons_hash"), + []byte("results_hash"), 0, len(keys), types.BlockID{Hash: lastHeader.Hash()}) + headers[height] = currentHeader + valset[height] = keys.ToValidators(2, 2) + lastHeader = currentHeader + keys = newKeys + } + + return chainID, headers, valset +} diff --git a/lite2/verifier.go b/lite2/verifier.go index 6d8459ab6..422e0398e 100644 --- a/lite2/verifier.go +++ b/lite2/verifier.go @@ -174,9 +174,10 @@ func verifyNewHeaderAndVals( } if !bytes.Equal(untrustedHeader.ValidatorsHash, untrustedVals.Hash()) { - return errors.Errorf("expected new header validators (%X) to match those that were supplied (%X)", + return errors.Errorf("expected new header validators (%X) to match those that were supplied (%X) at height %d", untrustedHeader.ValidatorsHash, untrustedVals.Hash(), + untrustedHeader.Height, ) } From 48f073d796ca4d1063b5f02c5d1b35c2c7e77afc Mon Sep 17 00:00:00 2001 From: Marko Date: Thu, 12 Mar 2020 08:10:36 +0100 Subject: [PATCH 44/77] privval: return error on getpubkey (#4534) closes #3602 Co-authored-by: Anton Kaliaev --- CHANGELOG_PENDING.md | 2 + blockchain/v1/reactor_test.go | 30 +++-- cmd/tendermint/commands/init.go | 11 +- cmd/tendermint/commands/show_validator.go | 8 +- cmd/tendermint/commands/testnet.go | 11 +- consensus/common_test.go | 36 ++++- consensus/reactor_test.go | 29 +++-- consensus/replay_test.go | 42 ++++-- consensus/state.go | 108 +++++++++++---- consensus/state_test.go | 64 ++++++--- consensus/types/height_vote_set_test.go | 10 +- node/node.go | 18 +-- privval/file.go | 4 +- privval/file_deprecated_test.go | 4 +- privval/signer_client.go | 12 +- privval/signer_client_test.go | 15 ++- privval/signer_requestHandler.go | 10 +- scripts/privValUpgrade_test.go | 4 +- state/state_test.go | 3 +- state/validation_test.go | 6 +- .../internal/test_harness.go | 27 +++- types/block_test.go | 5 +- types/evidence_test.go | 63 ++++----- types/priv_validator.go | 22 +++- types/proposal_test.go | 10 +- types/protobuf_test.go | 8 +- types/test_util.go | 17 ++- types/validator.go | 5 +- types/vote_set_test.go | 123 ++++++++++++------ types/vote_test.go | 10 +- 30 files changed, 501 insertions(+), 216 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 42609c19b..849c26950 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -18,4 +18,6 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi ### IMPROVEMENTS: +- [privval] \#4534 Add `error` as a return value on`GetPubKey()` + ### BUG FIXES: diff --git a/blockchain/v1/reactor_test.go b/blockchain/v1/reactor_test.go index deb73ad6d..d0e55695b 100644 --- a/blockchain/v1/reactor_test.go +++ b/blockchain/v1/reactor_test.go @@ -10,6 +10,8 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/log" @@ -46,15 +48,19 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G } func makeVote( + t *testing.T, header *types.Header, blockID types.BlockID, valset *types.ValidatorSet, privVal types.PrivValidator) *types.Vote { - addr := privVal.GetPubKey().Address() - idx, _ := valset.GetByAddress(addr) + + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) + + valIdx, _ := valset.GetByAddress(pubKey.Address()) vote := &types.Vote{ - ValidatorAddress: addr, - ValidatorIndex: idx, + ValidatorAddress: pubKey.Address(), + ValidatorIndex: valIdx, Height: header.Height, Round: 1, Timestamp: tmtime.Now(), @@ -73,6 +79,7 @@ type BlockchainReactorPair struct { } func newBlockchainReactor( + t *testing.T, logger log.Logger, genDoc *types.GenesisDoc, privVals []types.PrivValidator, @@ -114,7 +121,7 @@ func newBlockchainReactor( lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1) lastBlock := blockStore.LoadBlock(blockHeight - 1) - vote := makeVote(&lastBlock.Header, lastBlockMeta.BlockID, state.Validators, privVals[0]) + vote := makeVote(t, &lastBlock.Header, lastBlockMeta.BlockID, state.Validators, privVals[0]) lastCommit = types.NewCommit(vote.Height, vote.Round, lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()}) } @@ -138,6 +145,7 @@ func newBlockchainReactor( } func newBlockchainReactorPair( + t *testing.T, logger log.Logger, genDoc *types.GenesisDoc, privVals []types.PrivValidator, @@ -147,7 +155,7 @@ func newBlockchainReactorPair( consensusReactor.BaseReactor = *p2p.NewBaseReactor("Consensus reactor", consensusReactor) return BlockchainReactorPair{ - newBlockchainReactor(logger, genDoc, privVals, maxBlockHeight), + newBlockchainReactor(t, logger, genDoc, privVals, maxBlockHeight), consensusReactor} } @@ -174,8 +182,8 @@ func TestFastSyncNoBlockResponse(t *testing.T) { reactorPairs := make([]BlockchainReactorPair, 2) logger := log.TestingLogger() - reactorPairs[0] = newBlockchainReactorPair(logger, genDoc, privVals, maxBlockHeight) - reactorPairs[1] = newBlockchainReactorPair(logger, genDoc, privVals, 0) + reactorPairs[0] = newBlockchainReactorPair(t, logger, genDoc, privVals, maxBlockHeight) + reactorPairs[1] = newBlockchainReactorPair(t, logger, genDoc, privVals, 0) p2p.MakeConnectedSwitches(config.P2P, 2, func(i int, s *p2p.Switch) *p2p.Switch { s.AddReactor("BLOCKCHAIN", reactorPairs[i].bcR) @@ -239,7 +247,7 @@ func TestFastSyncBadBlockStopsPeer(t *testing.T) { defer os.RemoveAll(config.RootDir) genDoc, privVals := randGenesisDoc(1, false, 30) - otherChain := newBlockchainReactorPair(log.TestingLogger(), genDoc, privVals, maxBlockHeight) + otherChain := newBlockchainReactorPair(t, log.TestingLogger(), genDoc, privVals, maxBlockHeight) defer func() { _ = otherChain.bcR.Stop() _ = otherChain.conR.Stop() @@ -254,7 +262,7 @@ func TestFastSyncBadBlockStopsPeer(t *testing.T) { if i == 0 { height = maxBlockHeight } - reactorPairs[i] = newBlockchainReactorPair(logger[i], genDoc, privVals, height) + reactorPairs[i] = newBlockchainReactorPair(t, logger[i], genDoc, privVals, height) } switches := p2p.MakeConnectedSwitches(config.P2P, numNodes, func(i int, s *p2p.Switch) *p2p.Switch { @@ -296,7 +304,7 @@ outerFor: reactorPairs[numNodes-1].bcR.store = otherChain.bcR.store lastLogger := log.TestingLogger() - lastReactorPair := newBlockchainReactorPair(lastLogger, genDoc, privVals, 0) + lastReactorPair := newBlockchainReactorPair(t, lastLogger, genDoc, privVals, 0) reactorPairs = append(reactorPairs, lastReactorPair) switches = append(switches, p2p.MakeConnectedSwitches(config.P2P, 1, func(i int, s *p2p.Switch) *p2p.Switch { diff --git a/cmd/tendermint/commands/init.go b/cmd/tendermint/commands/init.go index eedf6f2b5..1ece45132 100644 --- a/cmd/tendermint/commands/init.go +++ b/cmd/tendermint/commands/init.go @@ -3,7 +3,9 @@ package commands import ( "fmt" + "github.com/pkg/errors" "github.com/spf13/cobra" + cfg "github.com/tendermint/tendermint/config" tmos "github.com/tendermint/tendermint/libs/os" tmrand "github.com/tendermint/tendermint/libs/rand" @@ -60,10 +62,13 @@ func initFilesWithConfig(config *cfg.Config) error { GenesisTime: tmtime.Now(), ConsensusParams: types.DefaultConsensusParams(), } - key := pv.GetPubKey() + pubKey, err := pv.GetPubKey() + if err != nil { + return errors.Wrap(err, "can't get pubkey") + } genDoc.Validators = []types.GenesisValidator{{ - Address: key.Address(), - PubKey: key, + Address: pubKey.Address(), + PubKey: pubKey, Power: 10, }} diff --git a/cmd/tendermint/commands/show_validator.go b/cmd/tendermint/commands/show_validator.go index b0c673373..4b885a5c3 100644 --- a/cmd/tendermint/commands/show_validator.go +++ b/cmd/tendermint/commands/show_validator.go @@ -24,7 +24,13 @@ func showValidator(cmd *cobra.Command, args []string) error { } pv := privval.LoadFilePV(keyFilePath, config.PrivValidatorStateFile()) - bz, err := cdc.MarshalJSON(pv.GetPubKey()) + + pubKey, err := pv.GetPubKey() + if err != nil { + return errors.Wrap(err, "can't get pubkey") + } + + bz, err := cdc.MarshalJSON(pubKey) if err != nil { return errors.Wrap(err, "failed to marshal private validator pubkey") } diff --git a/cmd/tendermint/commands/testnet.go b/cmd/tendermint/commands/testnet.go index ddf320622..54bb1363e 100644 --- a/cmd/tendermint/commands/testnet.go +++ b/cmd/tendermint/commands/testnet.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" + "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -137,11 +138,15 @@ func testnetFiles(cmd *cobra.Command, args []string) error { pvKeyFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidatorKey) pvStateFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidatorState) - pv := privval.LoadFilePV(pvKeyFile, pvStateFile) + + pubKey, err := pv.GetPubKey() + if err != nil { + return errors.Wrap(err, "can't get pubkey") + } genVals[i] = types.GenesisValidator{ - Address: pv.GetPubKey().Address(), - PubKey: pv.GetPubKey(), + Address: pubKey.Address(), + PubKey: pubKey, Power: 1, Name: nodeDirName, } diff --git a/consensus/common_test.go b/consensus/common_test.go index 0403ccaf3..1b2c818ae 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -13,6 +13,8 @@ import ( "time" "github.com/go-kit/kit/log/term" + "github.com/pkg/errors" + "github.com/stretchr/testify/require" "path" @@ -82,17 +84,23 @@ func (vs *validatorStub) signVote( voteType types.SignedMsgType, hash []byte, header types.PartSetHeader) (*types.Vote, error) { - addr := vs.PrivValidator.GetPubKey().Address() + + pubKey, err := vs.PrivValidator.GetPubKey() + if err != nil { + return nil, errors.Wrap(err, "can't get pubkey") + } + vote := &types.Vote{ ValidatorIndex: vs.Index, - ValidatorAddress: addr, + ValidatorAddress: pubKey.Address(), Height: vs.Height, Round: vs.Round, Timestamp: tmtime.Now(), Type: voteType, BlockID: types.BlockID{Hash: hash, PartsHeader: header}, } - err := vs.PrivValidator.SignVote(config.ChainID(), vote) + + err = vs.PrivValidator.SignVote(config.ChainID(), vote) return vote, err } @@ -136,7 +144,15 @@ func (vss ValidatorStubsByAddress) Len() int { } func (vss ValidatorStubsByAddress) Less(i, j int) bool { - return bytes.Compare(vss[i].GetPubKey().Address(), vss[j].GetPubKey().Address()) == -1 + vssi, err := vss[i].GetPubKey() + if err != nil { + panic(err) + } + vssj, err := vss[j].GetPubKey() + if err != nil { + panic(err) + } + return bytes.Compare(vssi.Address(), vssj.Address()) == -1 } func (vss ValidatorStubsByAddress) Swap(i, j int) { @@ -199,7 +215,9 @@ func signAddVotes( func validatePrevote(t *testing.T, cs *State, round int, privVal *validatorStub, blockHash []byte) { prevotes := cs.Votes.Prevotes(round) - address := privVal.GetPubKey().Address() + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) + address := pubKey.Address() var vote *types.Vote if vote = prevotes.GetByAddress(address); vote == nil { panic("Failed to find prevote from validator") @@ -217,7 +235,9 @@ func validatePrevote(t *testing.T, cs *State, round int, privVal *validatorStub, func validateLastPrecommit(t *testing.T, cs *State, privVal *validatorStub, blockHash []byte) { votes := cs.LastCommit - address := privVal.GetPubKey().Address() + pv, err := privVal.GetPubKey() + require.NoError(t, err) + address := pv.Address() var vote *types.Vote if vote = votes.GetByAddress(address); vote == nil { panic("Failed to find precommit from validator") @@ -237,7 +257,9 @@ func validatePrecommit( lockedBlockHash []byte, ) { precommits := cs.Votes.Precommits(thisRound) - address := privVal.GetPubKey().Address() + pv, err := privVal.GetPubKey() + require.NoError(t, err) + address := pv.Address() var vote *types.Vote if vote = precommits.GetByAddress(address); vote == nil { panic("Failed to find precommit from validator") diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index 32c638d3a..95ee6cdc5 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -154,8 +154,9 @@ func TestReactorWithEvidence(t *testing.T) { // mock the evidence pool // everyone includes evidence of another double signing vIdx := (i + 1) % nValidators - addr := privVals[vIdx].GetPubKey().Address() - evpool := newMockEvidencePool(addr) + pubKey, err := privVals[vIdx].GetPubKey() + require.NoError(t, err) + evpool := newMockEvidencePool(pubKey.Address()) // Make State blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyAppConnCon, mempool, evpool) @@ -331,7 +332,9 @@ func TestReactorVotingPowerChange(t *testing.T) { // map of active validators activeVals := make(map[string]struct{}) for i := 0; i < nVals; i++ { - addr := css[i].privValidator.GetPubKey().Address() + pubKey, err := css[i].privValidator.GetPubKey() + require.NoError(t, err) + addr := pubKey.Address() activeVals[string(addr)] = struct{}{} } @@ -343,7 +346,8 @@ func TestReactorVotingPowerChange(t *testing.T) { //--------------------------------------------------------------------------- logger.Debug("---------------------------- Testing changing the voting power of one validator a few times") - val1PubKey := css[0].privValidator.GetPubKey() + val1PubKey, err := css[0].privValidator.GetPubKey() + require.NoError(t, err) val1PubKeyABCI := types.TM2PB.PubKey(val1PubKey) updateValidatorTx := kvstore.MakeValSetChangeTx(val1PubKeyABCI, 25) previousTotalVotingPower := css[0].GetRoundState().LastValidators.TotalVotingPower() @@ -410,8 +414,9 @@ func TestReactorValidatorSetChanges(t *testing.T) { // map of active validators activeVals := make(map[string]struct{}) for i := 0; i < nVals; i++ { - addr := css[i].privValidator.GetPubKey().Address() - activeVals[string(addr)] = struct{}{} + pubKey, err := css[i].privValidator.GetPubKey() + require.NoError(t, err) + activeVals[string(pubKey.Address())] = struct{}{} } // wait till everyone makes block 1 @@ -422,7 +427,8 @@ func TestReactorValidatorSetChanges(t *testing.T) { //--------------------------------------------------------------------------- logger.Info("---------------------------- Testing adding one validator") - newValidatorPubKey1 := css[nVals].privValidator.GetPubKey() + newValidatorPubKey1, err := css[nVals].privValidator.GetPubKey() + require.NoError(t, err) valPubKey1ABCI := types.TM2PB.PubKey(newValidatorPubKey1) newValidatorTx1 := kvstore.MakeValSetChangeTx(valPubKey1ABCI, testMinPower) @@ -449,7 +455,8 @@ func TestReactorValidatorSetChanges(t *testing.T) { //--------------------------------------------------------------------------- logger.Info("---------------------------- Testing changing the voting power of one validator") - updateValidatorPubKey1 := css[nVals].privValidator.GetPubKey() + updateValidatorPubKey1, err := css[nVals].privValidator.GetPubKey() + require.NoError(t, err) updatePubKey1ABCI := types.TM2PB.PubKey(updateValidatorPubKey1) updateValidatorTx1 := kvstore.MakeValSetChangeTx(updatePubKey1ABCI, 25) previousTotalVotingPower := css[nVals].GetRoundState().LastValidators.TotalVotingPower() @@ -469,11 +476,13 @@ func TestReactorValidatorSetChanges(t *testing.T) { //--------------------------------------------------------------------------- logger.Info("---------------------------- Testing adding two validators at once") - newValidatorPubKey2 := css[nVals+1].privValidator.GetPubKey() + newValidatorPubKey2, err := css[nVals+1].privValidator.GetPubKey() + require.NoError(t, err) newVal2ABCI := types.TM2PB.PubKey(newValidatorPubKey2) newValidatorTx2 := kvstore.MakeValSetChangeTx(newVal2ABCI, testMinPower) - newValidatorPubKey3 := css[nVals+2].privValidator.GetPubKey() + newValidatorPubKey3, err := css[nVals+2].privValidator.GetPubKey() + require.NoError(t, err) newVal3ABCI := types.TM2PB.PubKey(newValidatorPubKey3) newValidatorTx3 := kvstore.MakeValSetChangeTx(newVal3ABCI, testMinPower) diff --git a/consensus/replay_test.go b/consensus/replay_test.go index aca6a9f10..24903927d 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -344,10 +344,11 @@ func TestSimulateValidatorsChange(t *testing.T) { //height 2 height++ incrementHeight(vss...) - newValidatorPubKey1 := css[nVals].privValidator.GetPubKey() + newValidatorPubKey1, err := css[nVals].privValidator.GetPubKey() + require.NoError(t, err) valPubKey1ABCI := types.TM2PB.PubKey(newValidatorPubKey1) newValidatorTx1 := kvstore.MakeValSetChangeTx(valPubKey1ABCI, testMinPower) - err := assertMempool(css[0].txNotifier).CheckTx(newValidatorTx1, nil, mempl.TxInfo{}) + err = assertMempool(css[0].txNotifier).CheckTx(newValidatorTx1, nil, mempl.TxInfo{}) assert.Nil(t, err) propBlock, _ := css[0].createProposalBlock() //changeProposer(t, cs1, vs2) propBlockParts := propBlock.MakePartSet(partSize) @@ -369,7 +370,8 @@ func TestSimulateValidatorsChange(t *testing.T) { //height 3 height++ incrementHeight(vss...) - updateValidatorPubKey1 := css[nVals].privValidator.GetPubKey() + updateValidatorPubKey1, err := css[nVals].privValidator.GetPubKey() + require.NoError(t, err) updatePubKey1ABCI := types.TM2PB.PubKey(updateValidatorPubKey1) updateValidatorTx1 := kvstore.MakeValSetChangeTx(updatePubKey1ABCI, 25) err = assertMempool(css[0].txNotifier).CheckTx(updateValidatorTx1, nil, mempl.TxInfo{}) @@ -394,12 +396,14 @@ func TestSimulateValidatorsChange(t *testing.T) { //height 4 height++ incrementHeight(vss...) - newValidatorPubKey2 := css[nVals+1].privValidator.GetPubKey() + newValidatorPubKey2, err := css[nVals+1].privValidator.GetPubKey() + require.NoError(t, err) newVal2ABCI := types.TM2PB.PubKey(newValidatorPubKey2) newValidatorTx2 := kvstore.MakeValSetChangeTx(newVal2ABCI, testMinPower) err = assertMempool(css[0].txNotifier).CheckTx(newValidatorTx2, nil, mempl.TxInfo{}) assert.Nil(t, err) - newValidatorPubKey3 := css[nVals+2].privValidator.GetPubKey() + newValidatorPubKey3, err := css[nVals+2].privValidator.GetPubKey() + require.NoError(t, err) newVal3ABCI := types.TM2PB.PubKey(newValidatorPubKey3) newValidatorTx3 := kvstore.MakeValSetChangeTx(newVal3ABCI, testMinPower) err = assertMempool(css[0].txNotifier).CheckTx(newValidatorTx3, nil, mempl.TxInfo{}) @@ -412,7 +416,13 @@ func TestSimulateValidatorsChange(t *testing.T) { sort.Sort(ValidatorStubsByAddress(newVss)) selfIndex := 0 for i, vs := range newVss { - if vs.GetPubKey().Equals(css[0].privValidator.GetPubKey()) { + vsPubKey, err := vs.GetPubKey() + require.NoError(t, err) + + css0PubKey, err := css[0].privValidator.GetPubKey() + require.NoError(t, err) + + if vsPubKey.Equals(css0PubKey) { selfIndex = i break } @@ -469,7 +479,13 @@ func TestSimulateValidatorsChange(t *testing.T) { copy(newVss, vss[:nVals+3]) sort.Sort(ValidatorStubsByAddress(newVss)) for i, vs := range newVss { - if vs.GetPubKey().Equals(css[0].privValidator.GetPubKey()) { + vsKeyKey, err := vs.GetPubKey() + require.NoError(t, err) + + css0PubKey, err := css[0].privValidator.GetPubKey() + require.NoError(t, err) + + if vsKeyKey.Equals(css0PubKey) { selfIndex = i break } @@ -642,7 +658,9 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin chain, commits, err = makeBlockchainFromWAL(wal) require.NoError(t, err) - stateDB, genisisState, store = stateAndStore(config, privVal.GetPubKey(), kvstore.ProtocolVersion) + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) + stateDB, genisisState, store = stateAndStore(config, pubKey, kvstore.ProtocolVersion) } store.chain = chain store.commits = commits @@ -811,7 +829,9 @@ func TestHandshakePanicsIfAppReturnsWrongAppHash(t *testing.T) { defer os.RemoveAll(config.RootDir) privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()) const appVersion = 0x0 - stateDB, state, store := stateAndStore(config, privVal.GetPubKey(), appVersion) + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) + stateDB, state, store := stateAndStore(config, pubKey, appVersion) genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile()) state.LastValidators = state.Validators.Copy() // mode = 0 for committing all the blocks @@ -1095,7 +1115,9 @@ func TestHandshakeUpdatesValidators(t *testing.T) { config := ResetConfig("handshake_test_") defer os.RemoveAll(config.RootDir) privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()) - stateDB, state, store := stateAndStore(config, privVal.GetPubKey(), 0x0) + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) + stateDB, state, store := stateAndStore(config, pubKey, 0x0) oldValAddr := state.Validators.Validators[0].Address diff --git a/consensus/state.go b/consensus/state.go index cd25aa32f..cd3dbeccb 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -921,19 +921,27 @@ func (cs *State) enterPropose(height int64, round int) { logger.Debug("This node is not a validator") return } + logger.Debug("This node is a validator") + + pubKey, err := cs.privValidator.GetPubKey() + if err != nil { + // If this node is a validator & proposer in the current round, it will + // miss the opportunity to create a block. + logger.Error("Error on retrival of pubkey", "err", err) + return + } + address := pubKey.Address() // if not a validator, we're done - address := cs.privValidator.GetPubKey().Address() if !cs.Validators.HasAddress(address) { logger.Debug("This node is not a validator", "addr", address, "vals", cs.Validators) return } - logger.Debug("This node is a validator") if cs.isProposer(address) { logger.Info("enterPropose: Our turn to propose", "proposer", - cs.Validators.GetProposer().Address, + address, "privValidator", cs.privValidator) cs.decideProposal(height, round) @@ -961,7 +969,7 @@ func (cs *State) defaultDecideProposal(height int64, round int) { } else { // Create a new proposal block from state/txs from the mempool. block, blockParts = cs.createProposalBlock() - if block == nil { // on error + if block == nil { return } } @@ -1004,11 +1012,13 @@ func (cs *State) isProposalComplete() bool { } -// Create the next block to propose and return it. -// We really only need to return the parts, but the block -// is returned for convenience so we can log the proposal block. -// Returns nil block upon error. +// Create the next block to propose and return it. Returns nil block upon error. +// +// We really only need to return the parts, but the block is returned for +// convenience so we can log the proposal block. +// // NOTE: keep it side-effect free for clarity. +// CONTRACT: cs.privValidator is not nil. func (cs *State) createProposalBlock() (block *types.Block, blockParts *types.PartSet) { var commit *types.Commit switch { @@ -1019,13 +1029,23 @@ func (cs *State) createProposalBlock() (block *types.Block, blockParts *types.Pa case cs.LastCommit.HasTwoThirdsMajority(): // Make the commit from LastCommit commit = cs.LastCommit.MakeCommit() - default: - // This shouldn't happen. - cs.Logger.Error("enterPropose: Cannot propose anything: No commit for the previous block.") + default: // This shouldn't happen. + cs.Logger.Error("enterPropose: Cannot propose anything: No commit for the previous block") return } - proposerAddr := cs.privValidator.GetPubKey().Address() + if cs.privValidator == nil { + panic("entered createProposalBlock with privValidator being nil") + } + pubKey, err := cs.privValidator.GetPubKey() + if err != nil { + // If this node is a validator & proposer in the current round, it will + // miss the opportunity to create a block. + cs.Logger.Error("Error on retrival of pubkey", "err", err) + return + } + proposerAddr := pubKey.Address() + return cs.blockExec.CreateProposalBlock(cs.Height, cs.state, commit, proposerAddr) } @@ -1491,15 +1511,24 @@ func (cs *State) recordMetrics(height int64, block *types.Block) { missingValidatorsPower += val.VotingPower } - if cs.privValidator != nil && bytes.Equal(val.Address, cs.privValidator.GetPubKey().Address()) { - label := []string{ - "validator_address", val.Address.String(), + if cs.privValidator != nil { + pubKey, err := cs.privValidator.GetPubKey() + if err != nil { + // Metrics won't be updated, but it's not critical. + cs.Logger.Error("Error on retrival of pubkey", "err", err) + continue } - cs.metrics.ValidatorPower.With(label...).Set(float64(val.VotingPower)) - if commitSig.ForBlock() { - cs.metrics.ValidatorLastSignedHeight.With(label...).Set(float64(height)) - } else { - cs.metrics.ValidatorMissedBlocks.With(label...).Add(float64(1)) + + if bytes.Equal(val.Address, pubKey.Address()) { + label := []string{ + "validator_address", val.Address.String(), + } + cs.metrics.ValidatorPower.With(label...).Set(float64(val.VotingPower)) + if commitSig.ForBlock() { + cs.metrics.ValidatorLastSignedHeight.With(label...).Set(float64(height)) + } else { + cs.metrics.ValidatorMissedBlocks.With(label...).Add(float64(1)) + } } } } @@ -1648,8 +1677,12 @@ func (cs *State) tryAddVote(vote *types.Vote, peerID p2p.ID) (bool, error) { if err == ErrVoteHeightMismatch { return added, err } else if voteErr, ok := err.(*types.ErrVoteConflictingVotes); ok { - addr := cs.privValidator.GetPubKey().Address() - if bytes.Equal(vote.ValidatorAddress, addr) { + pubKey, err := cs.privValidator.GetPubKey() + if err != nil { + return false, errors.Wrap(err, "can't get pubkey") + } + + if bytes.Equal(vote.ValidatorAddress, pubKey.Address()) { cs.Logger.Error( "Found conflicting vote from ourselves. Did you unsafe_reset a validator?", "height", @@ -1838,6 +1871,7 @@ func (cs *State) addVote( return added, err } +// CONTRACT: cs.privValidator is not nil. func (cs *State) signVote( msgType types.SignedMsgType, hash []byte, @@ -1847,19 +1881,24 @@ func (cs *State) signVote( // and the privValidator will refuse to sign anything. cs.wal.FlushAndSync() - addr := cs.privValidator.GetPubKey().Address() - valIndex, _ := cs.Validators.GetByAddress(addr) + pubKey, err := cs.privValidator.GetPubKey() + if err != nil { + return nil, errors.Wrap(err, "can't get pubkey") + } + addr := pubKey.Address() + valIdx, _ := cs.Validators.GetByAddress(addr) vote := &types.Vote{ ValidatorAddress: addr, - ValidatorIndex: valIndex, + ValidatorIndex: valIdx, Height: cs.Height, Round: cs.Round, Timestamp: cs.voteTime(), Type: msgType, BlockID: types.BlockID{Hash: hash, PartsHeader: header}, } - err := cs.privValidator.SignVote(cs.state.ChainID, vote) + + err = cs.privValidator.SignVote(cs.state.ChainID, vote) return vote, err } @@ -1884,10 +1923,23 @@ func (cs *State) voteTime() time.Time { // sign the vote and publish on internalMsgQueue func (cs *State) signAddVote(msgType types.SignedMsgType, hash []byte, header types.PartSetHeader) *types.Vote { - // if we don't have a key or we're not in the validator set, do nothing - if cs.privValidator == nil || !cs.Validators.HasAddress(cs.privValidator.GetPubKey().Address()) { + if cs.privValidator == nil { // the node does not have a key return nil } + + pubKey, err := cs.privValidator.GetPubKey() + if err != nil { + // Vote won't be signed, but it's not critical. + cs.Logger.Error("Error on retrival of pubkey", "err", err) + return nil + } + + // If the node not in the validator set, do nothing. + if !cs.Validators.HasAddress(pubKey.Address()) { + return nil + } + + // TODO: pass pubKey to signVote vote, err := cs.signVote(msgType, hash, header) if err == nil { cs.sendInternalMessage(msgInfo{&VoteMessage{vote}, ""}) diff --git a/consensus/state_test.go b/consensus/state_test.go index e8df64981..3de5a37d1 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -65,7 +65,9 @@ func TestStateProposerSelection0(t *testing.T) { // Commit a block and ensure proposer for the next height is correct. prop := cs1.GetRoundState().Validators.GetProposer() - address := cs1.privValidator.GetPubKey().Address() + pv, err := cs1.privValidator.GetPubKey() + require.NoError(t, err) + address := pv.Address() if !bytes.Equal(prop.Address, address) { t.Fatalf("expected proposer to be validator %d. Got %X", 0, prop.Address) } @@ -80,7 +82,9 @@ func TestStateProposerSelection0(t *testing.T) { ensureNewRound(newRoundCh, height+1, 0) prop = cs1.GetRoundState().Validators.GetProposer() - addr := vss[1].GetPubKey().Address() + pv1, err := vss[1].GetPubKey() + require.NoError(t, err) + addr := pv1.Address() if !bytes.Equal(prop.Address, addr) { panic(fmt.Sprintf("expected proposer to be validator %d. Got %X", 1, prop.Address)) } @@ -104,7 +108,9 @@ func TestStateProposerSelection2(t *testing.T) { // everyone just votes nil. we get a new proposer each round for i := 0; i < len(vss); i++ { prop := cs1.GetRoundState().Validators.GetProposer() - addr := vss[(i+round)%len(vss)].GetPubKey().Address() + pvk, err := vss[(i+round)%len(vss)].GetPubKey() + require.NoError(t, err) + addr := pvk.Address() correctProposer := addr if !bytes.Equal(prop.Address, correctProposer) { panic(fmt.Sprintf( @@ -516,7 +522,9 @@ func TestStateLockPOLRelock(t *testing.T) { timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) - addr := cs1.privValidator.GetPubKey().Address() + pv1, err := cs1.privValidator.GetPubKey() + require.NoError(t, err) + addr := pv1.Address() voteCh := subscribeToVoter(cs1, addr) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlockHeader) @@ -608,7 +616,9 @@ func TestStateLockPOLUnlock(t *testing.T) { timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock) - addr := cs1.privValidator.GetPubKey().Address() + pv1, err := cs1.privValidator.GetPubKey() + require.NoError(t, err) + addr := pv1.Address() voteCh := subscribeToVoter(cs1, addr) // everything done from perspective of cs1 @@ -700,7 +710,9 @@ func TestStateLockPOLSafety1(t *testing.T) { timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) - addr := cs1.privValidator.GetPubKey().Address() + pv1, err := cs1.privValidator.GetPubKey() + require.NoError(t, err) + addr := pv1.Address() voteCh := subscribeToVoter(cs1, addr) // start round and wait for propose and prevote @@ -817,7 +829,9 @@ func TestStateLockPOLSafety2(t *testing.T) { timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock) - addr := cs1.privValidator.GetPubKey().Address() + pv1, err := cs1.privValidator.GetPubKey() + require.NoError(t, err) + addr := pv1.Address() voteCh := subscribeToVoter(cs1, addr) // the block for R0: gets polkad but we miss it @@ -909,7 +923,9 @@ func TestProposeValidBlock(t *testing.T) { timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock) - addr := cs1.privValidator.GetPubKey().Address() + pv1, err := cs1.privValidator.GetPubKey() + require.NoError(t, err) + addr := pv1.Address() voteCh := subscribeToVoter(cs1, addr) // start round and wait for propose and prevote @@ -996,7 +1012,9 @@ func TestSetValidBlockOnDelayedPrevote(t *testing.T) { timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock) - addr := cs1.privValidator.GetPubKey().Address() + pv1, err := cs1.privValidator.GetPubKey() + require.NoError(t, err) + addr := pv1.Address() voteCh := subscribeToVoter(cs1, addr) // start round and wait for propose and prevote @@ -1056,7 +1074,9 @@ func TestSetValidBlockOnDelayedProposal(t *testing.T) { timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock) - addr := cs1.privValidator.GetPubKey().Address() + pv1, err := cs1.privValidator.GetPubKey() + require.NoError(t, err) + addr := pv1.Address() voteCh := subscribeToVoter(cs1, addr) proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) @@ -1127,7 +1147,9 @@ func TestWaitingTimeoutProposeOnNewRound(t *testing.T) { timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) - addr := cs1.privValidator.GetPubKey().Address() + pv1, err := cs1.privValidator.GetPubKey() + require.NoError(t, err) + addr := pv1.Address() voteCh := subscribeToVoter(cs1, addr) // start round @@ -1161,7 +1183,9 @@ func TestRoundSkipOnNilPolkaFromHigherRound(t *testing.T) { timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) - addr := cs1.privValidator.GetPubKey().Address() + pv1, err := cs1.privValidator.GetPubKey() + require.NoError(t, err) + addr := pv1.Address() voteCh := subscribeToVoter(cs1, addr) // start round @@ -1195,7 +1219,9 @@ func TestWaitTimeoutProposeOnNilPolkaForTheCurrentRound(t *testing.T) { timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) - addr := cs1.privValidator.GetPubKey().Address() + pv1, err := cs1.privValidator.GetPubKey() + require.NoError(t, err) + addr := pv1.Address() voteCh := subscribeToVoter(cs1, addr) // start round in which PO is not proposer @@ -1310,7 +1336,9 @@ func TestStartNextHeightCorrectly(t *testing.T) { newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) newBlockHeader := subscribe(cs1.eventBus, types.EventQueryNewBlockHeader) - addr := cs1.privValidator.GetPubKey().Address() + pv1, err := cs1.privValidator.GetPubKey() + require.NoError(t, err) + addr := pv1.Address() voteCh := subscribeToVoter(cs1, addr) // start round and wait for propose and prevote @@ -1365,7 +1393,9 @@ func TestResetTimeoutPrecommitUponNewHeight(t *testing.T) { newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) newBlockHeader := subscribe(cs1.eventBus, types.EventQueryNewBlockHeader) - addr := cs1.privValidator.GetPubKey().Address() + pv1, err := cs1.privValidator.GetPubKey() + require.NoError(t, err) + addr := pv1.Address() voteCh := subscribeToVoter(cs1, addr) // start round and wait for propose and prevote @@ -1502,7 +1532,9 @@ func TestStateHalt1(t *testing.T) { timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlock) - addr := cs1.privValidator.GetPubKey().Address() + pv1, err := cs1.privValidator.GetPubKey() + require.NoError(t, err) + addr := pv1.Address() voteCh := subscribeToVoter(cs1, addr) // start round and wait for propose and prevote diff --git a/consensus/types/height_vote_set_test.go b/consensus/types/height_vote_set_test.go index 93c73f1a1..654880d27 100644 --- a/consensus/types/height_vote_set_test.go +++ b/consensus/types/height_vote_set_test.go @@ -54,9 +54,13 @@ func TestPeerCatchupRounds(t *testing.T) { func makeVoteHR(t *testing.T, height int64, round int, privVals []types.PrivValidator, valIndex int) *types.Vote { privVal := privVals[valIndex] - addr := privVal.GetPubKey().Address() + pubKey, err := privVal.GetPubKey() + if err != nil { + panic(err) + } + vote := &types.Vote{ - ValidatorAddress: addr, + ValidatorAddress: pubKey.Address(), ValidatorIndex: valIndex, Height: height, Round: round, @@ -65,7 +69,7 @@ func makeVoteHR(t *testing.T, height int64, round int, privVals []types.PrivVali BlockID: types.BlockID{Hash: []byte("fakehash"), PartsHeader: types.PartSetHeader{}}, } chainID := config.ChainID() - err := privVal.SignVote(chainID, vote) + err = privVal.SignVote(chainID, vote) if err != nil { panic(fmt.Sprintf("Error signing vote: %v", err)) } diff --git a/node/node.go b/node/node.go index 485709120..e9cd02c30 100644 --- a/node/node.go +++ b/node/node.go @@ -309,12 +309,12 @@ func logNodeStartupInfo(state sm.State, pubKey crypto.PubKey, logger, consensusL } } -func onlyValidatorIsUs(state sm.State, privVal types.PrivValidator) bool { +func onlyValidatorIsUs(state sm.State, pubKey crypto.PubKey) bool { if state.Validators.Size() > 1 { return false } addr, _ := state.Validators.GetByIndex(0) - return bytes.Equal(privVal.GetPubKey().Address(), addr) + return bytes.Equal(pubKey.Address(), addr) } func createMempoolAndMempoolReactor(config *cfg.Config, proxyApp proxy.AppConns, @@ -613,17 +613,16 @@ func NewNode(config *cfg.Config, } } - pubKey := privValidator.GetPubKey() - if pubKey == nil { - // TODO: GetPubKey should return errors - https://github.com/tendermint/tendermint/issues/3602 - return nil, errors.New("could not retrieve public key from private validator") + pubKey, err := privValidator.GetPubKey() + if err != nil { + return nil, errors.Wrap(err, "can't get pubkey") } logNodeStartupInfo(state, pubKey, logger, consensusLogger) // Decide whether to fast-sync or not // We don't fast-sync when the only validator is us. - fastSync := config.FastSyncMode && !onlyValidatorIsUs(state, privValidator) + fastSync := config.FastSyncMode && !onlyValidatorIsUs(state, pubKey) csMetrics, p2pMetrics, memplMetrics, smMetrics := metricsProvider(genDoc.ChainID) @@ -856,7 +855,10 @@ func (n *Node) ConfigureRPC() { rpccore.SetEvidencePool(n.evidencePool) rpccore.SetP2PPeers(n.sw) rpccore.SetP2PTransport(n) - pubKey := n.privValidator.GetPubKey() + pubKey, err := n.privValidator.GetPubKey() + if err != nil { + panic(err) + } rpccore.SetPubKey(pubKey) rpccore.SetGenesisDoc(n.genesisDoc) rpccore.SetProxyAppQuery(n.proxyApp.Query()) diff --git a/privval/file.go b/privval/file.go index c31e95a6a..5f07ac525 100644 --- a/privval/file.go +++ b/privval/file.go @@ -237,8 +237,8 @@ func (pv *FilePV) GetAddress() types.Address { // GetPubKey returns the public key of the validator. // Implements PrivValidator. -func (pv *FilePV) GetPubKey() crypto.PubKey { - return pv.Key.PubKey +func (pv *FilePV) GetPubKey() (crypto.PubKey, error) { + return pv.Key.PubKey, nil } // SignVote signs a canonical representation of the vote, along with the diff --git a/privval/file_deprecated_test.go b/privval/file_deprecated_test.go index ca0e1e508..f850c23f1 100644 --- a/privval/file_deprecated_test.go +++ b/privval/file_deprecated_test.go @@ -61,7 +61,9 @@ func assertEqualPV(t *testing.T, oldPV *privval.OldFilePV, newPV *privval.FilePV assert.Equal(t, oldPV.Address, newPV.Key.Address) assert.Equal(t, oldPV.Address, newPV.GetAddress()) assert.Equal(t, oldPV.PubKey, newPV.Key.PubKey) - assert.Equal(t, oldPV.PubKey, newPV.GetPubKey()) + npv, err := newPV.GetPubKey() + require.NoError(t, err) + assert.Equal(t, oldPV.PubKey, npv) assert.Equal(t, oldPV.PrivKey, newPV.Key.PrivKey) assert.Equal(t, oldPV.LastHeight, newPV.LastSignState.Height) diff --git a/privval/signer_client.go b/privval/signer_client.go index 0885ee4aa..3e69c6c08 100644 --- a/privval/signer_client.go +++ b/privval/signer_client.go @@ -1,6 +1,7 @@ package privval import ( + "fmt" "time" "github.com/pkg/errors" @@ -66,25 +67,26 @@ func (sc *SignerClient) Ping() error { } // GetPubKey retrieves a public key from a remote signer -func (sc *SignerClient) GetPubKey() crypto.PubKey { +// returns an error if client is not able to provide the key +func (sc *SignerClient) GetPubKey() (crypto.PubKey, error) { response, err := sc.endpoint.SendRequest(&PubKeyRequest{}) if err != nil { sc.endpoint.Logger.Error("SignerClient::GetPubKey", "err", err) - return nil + return nil, errors.Wrap(err, "send") } pubKeyResp, ok := response.(*PubKeyResponse) if !ok { sc.endpoint.Logger.Error("SignerClient::GetPubKey", "err", "response != PubKeyResponse") - return nil + return nil, errors.Errorf("unexpected response type %T", response) } if pubKeyResp.Error != nil { sc.endpoint.Logger.Error("failed to get private validator's public key", "err", pubKeyResp.Error) - return nil + return nil, fmt.Errorf("remote error: %w", pubKeyResp.Error) } - return pubKeyResp.PubKey + return pubKeyResp.PubKey, nil } // SignVote requests a remote signer to sign a vote diff --git a/privval/signer_client_test.go b/privval/signer_client_test.go index 3c578f401..2156a8cf2 100644 --- a/privval/signer_client_test.go +++ b/privval/signer_client_test.go @@ -74,15 +74,20 @@ func TestSignerGetPubKey(t *testing.T) { defer tc.signerServer.Stop() defer tc.signerClient.Close() - pubKey := tc.signerClient.GetPubKey() - expectedPubKey := tc.mockPV.GetPubKey() + pubKey, err := tc.signerClient.GetPubKey() + require.NoError(t, err) + expectedPubKey, err := tc.mockPV.GetPubKey() + require.NoError(t, err) assert.Equal(t, expectedPubKey, pubKey) - addr := tc.signerClient.GetPubKey().Address() - expectedAddr := tc.mockPV.GetPubKey().Address() + pubKey, err = tc.signerClient.GetPubKey() + require.NoError(t, err) + expectedpk, err := tc.mockPV.GetPubKey() + require.NoError(t, err) + expectedAddr := expectedpk.Address() - assert.Equal(t, expectedAddr, addr) + assert.Equal(t, expectedAddr, pubKey.Address()) } } diff --git a/privval/signer_requestHandler.go b/privval/signer_requestHandler.go index 2562764ba..c658abdfd 100644 --- a/privval/signer_requestHandler.go +++ b/privval/signer_requestHandler.go @@ -17,9 +17,13 @@ func DefaultValidationRequestHandler( switch r := req.(type) { case *PubKeyRequest: - var p crypto.PubKey - p = privVal.GetPubKey() - res = &PubKeyResponse{p, nil} + var pubKey crypto.PubKey + pubKey, err = privVal.GetPubKey() + if err != nil { + res = &PubKeyResponse{nil, &RemoteSignerError{0, err.Error()}} + } else { + res = &PubKeyResponse{pubKey, nil} + } case *SignVoteRequest: err = privVal.SignVote(chainID, r.Vote) diff --git a/scripts/privValUpgrade_test.go b/scripts/privValUpgrade_test.go index d62d4ceee..248ac7a24 100644 --- a/scripts/privValUpgrade_test.go +++ b/scripts/privValUpgrade_test.go @@ -100,7 +100,9 @@ func TestLoadAndUpgrade(t *testing.T) { assert.Equal(t, oldPV.Address, upgradedPV.Key.Address) assert.Equal(t, oldPV.Address, upgradedPV.GetAddress()) assert.Equal(t, oldPV.PubKey, upgradedPV.Key.PubKey) - assert.Equal(t, oldPV.PubKey, upgradedPV.GetPubKey()) + upv, err := upgradedPV.GetPubKey() + require.NoError(t, err) + assert.Equal(t, oldPV.PubKey, upv) assert.Equal(t, oldPV.PrivKey, upgradedPV.Key.PrivKey) assert.Equal(t, oldPV.LastHeight, upgradedPV.LastSignState.Height) diff --git a/state/state_test.go b/state/state_test.go index b015a4e17..d08792033 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -326,7 +326,8 @@ func TestProposerFrequency(t *testing.T) { votePower := int64(tmrand.Int()%maxPower) + 1 totalVotePower += votePower privVal := types.NewMockPV() - pubKey := privVal.GetPubKey() + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) val := types.NewValidator(pubKey, votePower) val.ProposerPriority = tmrand.Int64() vals[j] = val diff --git a/state/validation_test.go b/state/validation_test.go index da975351c..d7d875f41 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -171,8 +171,12 @@ func TestValidateBlockCommit(t *testing.T) { time.Now(), ) require.NoError(t, err, "height %d", height) + + bpvPubKey, err := badPrivVal.GetPubKey() + require.NoError(t, err) + badVote := &types.Vote{ - ValidatorAddress: badPrivVal.GetPubKey().Address(), + ValidatorAddress: bpvPubKey.Address(), ValidatorIndex: 0, Height: height, Round: 0, diff --git a/tools/tm-signer-harness/internal/test_harness.go b/tools/tm-signer-harness/internal/test_harness.go index c489a2fd4..f9d48fdcb 100644 --- a/tools/tm-signer-harness/internal/test_harness.go +++ b/tools/tm-signer-harness/internal/test_harness.go @@ -190,9 +190,17 @@ func (th *TestHarness) Run() { // local Tendermint version. func (th *TestHarness) TestPublicKey() error { th.logger.Info("TEST: Public key of remote signer") - th.logger.Info("Local", "pubKey", th.fpv.GetPubKey()) - th.logger.Info("Remote", "pubKey", th.signerClient.GetPubKey()) - if th.fpv.GetPubKey() != th.signerClient.GetPubKey() { + fpvk, err := th.fpv.GetPubKey() + if err != nil { + return err + } + th.logger.Info("Local", "pubKey", fpvk) + sck, err := th.signerClient.GetPubKey() + if err != nil { + return err + } + th.logger.Info("Remote", "pubKey", sck) + if fpvk != sck { th.logger.Error("FAILED: Local and remote public keys do not match") return newTestHarnessError(ErrTestPublicKeyFailed, nil, "") } @@ -230,8 +238,12 @@ func (th *TestHarness) TestSignProposal() error { th.logger.Error("FAILED: Signed proposal is invalid", "err", err) return newTestHarnessError(ErrTestSignProposalFailed, err, "") } + sck, err := th.signerClient.GetPubKey() + if err != nil { + return err + } // now validate the signature on the proposal - if th.signerClient.GetPubKey().VerifyBytes(propBytes, prop.Signature) { + if sck.VerifyBytes(propBytes, prop.Signature) { th.logger.Info("Successfully validated proposal signature") } else { th.logger.Error("FAILED: Proposal signature validation failed") @@ -274,8 +286,13 @@ func (th *TestHarness) TestSignVote() error { th.logger.Error("FAILED: Signed vote is invalid", "err", err) return newTestHarnessError(ErrTestSignVoteFailed, err, fmt.Sprintf("voteType=%d", voteType)) } + sck, err := th.signerClient.GetPubKey() + if err != nil { + return err + } + // now validate the signature on the proposal - if th.signerClient.GetPubKey().VerifyBytes(voteBytes, vote.Signature) { + if sck.VerifyBytes(voteBytes, vote.Signature) { th.logger.Info("Successfully validated vote signature", "type", voteType) } else { th.logger.Error("FAILED: Vote signature validation failed", "type", voteType) diff --git a/types/block_test.go b/types/block_test.go index a7ae31e36..fe3da920c 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -475,9 +475,10 @@ func TestCommitToVoteSetWithVotesForNilBlock(t *testing.T) { vi := 0 for n := range tc.blockIDs { for i := 0; i < tc.numVotes[n]; i++ { - addr := vals[vi].GetPubKey().Address() + pubKey, err := vals[vi].GetPubKey() + require.NoError(t, err) vote := &Vote{ - ValidatorAddress: addr, + ValidatorAddress: pubKey.Address(), ValidatorIndex: vi, Height: height - 1, Round: round, diff --git a/types/evidence_test.go b/types/evidence_test.go index bb04d9a4b..40e096fcd 100644 --- a/types/evidence_test.go +++ b/types/evidence_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/secp256k1" "github.com/tendermint/tendermint/crypto/tmhash" ) @@ -17,17 +18,20 @@ type voteData struct { valid bool } -func makeVote(val PrivValidator, chainID string, valIndex int, height int64, round, step int, blockID BlockID) *Vote { - addr := val.GetPubKey().Address() +func makeVote( + t *testing.T, val PrivValidator, chainID string, valIndex int, height int64, round, step int, blockID BlockID, +) *Vote { + pubKey, err := val.GetPubKey() + require.NoError(t, err) v := &Vote{ - ValidatorAddress: addr, + ValidatorAddress: pubKey.Address(), ValidatorIndex: valIndex, Height: height, Round: round, Type: SignedMsgType(step), BlockID: blockID, } - err := val.SignVote(chainID, v) + err = val.SignVote(chainID, v) if err != nil { panic(err) } @@ -45,28 +49,27 @@ func TestEvidence(t *testing.T) { const chainID = "mychain" - vote1 := makeVote(val, chainID, 0, 10, 2, 1, blockID) - badVote := makeVote(val, chainID, 0, 10, 2, 1, blockID) + vote1 := makeVote(t, val, chainID, 0, 10, 2, 1, blockID) + badVote := makeVote(t, val, chainID, 0, 10, 2, 1, blockID) err := val2.SignVote(chainID, badVote) - if err != nil { - panic(err) - } + assert.NoError(t, err) cases := []voteData{ - {vote1, makeVote(val, chainID, 0, 10, 2, 1, blockID2), true}, // different block ids - {vote1, makeVote(val, chainID, 0, 10, 2, 1, blockID3), true}, - {vote1, makeVote(val, chainID, 0, 10, 2, 1, blockID4), true}, - {vote1, makeVote(val, chainID, 0, 10, 2, 1, blockID), false}, // wrong block id - {vote1, makeVote(val, "mychain2", 0, 10, 2, 1, blockID2), false}, // wrong chain id - {vote1, makeVote(val, chainID, 1, 10, 2, 1, blockID2), false}, // wrong val index - {vote1, makeVote(val, chainID, 0, 11, 2, 1, blockID2), false}, // wrong height - {vote1, makeVote(val, chainID, 0, 10, 3, 1, blockID2), false}, // wrong round - {vote1, makeVote(val, chainID, 0, 10, 2, 2, blockID2), false}, // wrong step - {vote1, makeVote(val2, chainID, 0, 10, 2, 1, blockID), false}, // wrong validator + {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID2), true}, // different block ids + {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID3), true}, + {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID4), true}, + {vote1, makeVote(t, val, chainID, 0, 10, 2, 1, blockID), false}, // wrong block id + {vote1, makeVote(t, val, "mychain2", 0, 10, 2, 1, blockID2), false}, // wrong chain id + {vote1, makeVote(t, val, chainID, 1, 10, 2, 1, blockID2), false}, // wrong val index + {vote1, makeVote(t, val, chainID, 0, 11, 2, 1, blockID2), false}, // wrong height + {vote1, makeVote(t, val, chainID, 0, 10, 3, 1, blockID2), false}, // wrong round + {vote1, makeVote(t, val, chainID, 0, 10, 2, 2, blockID2), false}, // wrong step + {vote1, makeVote(t, val2, chainID, 0, 10, 2, 1, blockID), false}, // wrong validator {vote1, badVote, false}, // signed by wrong key } - pubKey := val.GetPubKey() + pubKey, err := val.GetPubKey() + require.NoError(t, err) for _, c := range cases { ev := &DuplicateVoteEvidence{ VoteA: c.vote1, @@ -81,14 +84,14 @@ func TestEvidence(t *testing.T) { } func TestDuplicatedVoteEvidence(t *testing.T) { - ev := randomDuplicatedVoteEvidence() + ev := randomDuplicatedVoteEvidence(t) assert.True(t, ev.Equal(ev)) assert.False(t, ev.Equal(&DuplicateVoteEvidence{})) } func TestEvidenceList(t *testing.T) { - ev := randomDuplicatedVoteEvidence() + ev := randomDuplicatedVoteEvidence(t) evl := EvidenceList([]Evidence{ev}) assert.NotNil(t, evl.Hash()) @@ -103,8 +106,8 @@ func TestMaxEvidenceBytes(t *testing.T) { const chainID = "mychain" ev := &DuplicateVoteEvidence{ PubKey: secp256k1.GenPrivKey().PubKey(), // use secp because it's pubkey is longer - VoteA: makeVote(val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64, blockID), - VoteB: makeVote(val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64, blockID2), + VoteA: makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64, blockID), + VoteB: makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64, blockID2), } bz, err := cdc.MarshalBinaryLengthPrefixed(ev) @@ -113,14 +116,14 @@ func TestMaxEvidenceBytes(t *testing.T) { assert.EqualValues(t, MaxEvidenceBytes, len(bz)) } -func randomDuplicatedVoteEvidence() *DuplicateVoteEvidence { +func randomDuplicatedVoteEvidence(t *testing.T) *DuplicateVoteEvidence { val := NewMockPV() blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash")) blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash")) const chainID = "mychain" return &DuplicateVoteEvidence{ - VoteA: makeVote(val, chainID, 0, 10, 2, 1, blockID), - VoteB: makeVote(val, chainID, 0, 10, 2, 1, blockID2), + VoteA: makeVote(t, val, chainID, 0, 10, 2, 1, blockID), + VoteB: makeVote(t, val, chainID, 0, 10, 2, 1, blockID2), } } @@ -143,7 +146,7 @@ func TestDuplicateVoteEvidenceValidation(t *testing.T) { ev.VoteB = nil }, true}, {"Invalid vote type", func(ev *DuplicateVoteEvidence) { - ev.VoteA = makeVote(val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, 0, blockID2) + ev.VoteA = makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, 0, blockID2) }, true}, {"Invalid vote order", func(ev *DuplicateVoteEvidence) { swap := ev.VoteA.Copy() @@ -155,8 +158,8 @@ func TestDuplicateVoteEvidenceValidation(t *testing.T) { tc := tc t.Run(tc.testName, func(t *testing.T) { pk := secp256k1.GenPrivKey().PubKey() - vote1 := makeVote(val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, 0x02, blockID) - vote2 := makeVote(val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, 0x02, blockID2) + vote1 := makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, 0x02, blockID) + vote2 := makeVote(t, val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, 0x02, blockID2) ev := NewDuplicateVoteEvidence(pk, vote1, vote2) tc.malleateEvidence(ev) assert.Equal(t, tc.expectErr, ev.ValidateBasic() != nil, "Validate Basic had an unexpected result") diff --git a/types/priv_validator.go b/types/priv_validator.go index f2af88750..fbe8cebf0 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -12,8 +12,7 @@ import ( // PrivValidator defines the functionality of a local Tendermint validator // that signs votes and proposals, and never double signs. type PrivValidator interface { - // TODO: Extend the interface to return errors too. Issue: https://github.com/tendermint/tendermint/issues/3602 - GetPubKey() crypto.PubKey + GetPubKey() (crypto.PubKey, error) SignVote(chainID string, vote *Vote) error SignProposal(chainID string, proposal *Proposal) error @@ -29,7 +28,16 @@ func (pvs PrivValidatorsByAddress) Len() int { } func (pvs PrivValidatorsByAddress) Less(i, j int) bool { - return bytes.Compare(pvs[i].GetPubKey().Address(), pvs[j].GetPubKey().Address()) == -1 + pvi, err := pvs[i].GetPubKey() + if err != nil { + panic(err) + } + pvj, err := pvs[j].GetPubKey() + if err != nil { + panic(err) + } + + return bytes.Compare(pvi.Address(), pvj.Address()) == -1 } func (pvs PrivValidatorsByAddress) Swap(i, j int) { @@ -61,8 +69,8 @@ func NewMockPVWithParams(privKey crypto.PrivKey, breakProposalSigning, breakVote } // Implements PrivValidator. -func (pv MockPV) GetPubKey() crypto.PubKey { - return pv.PrivKey.PubKey() +func (pv MockPV) GetPubKey() (crypto.PubKey, error) { + return pv.PrivKey.PubKey(), nil } // Implements PrivValidator. @@ -97,8 +105,8 @@ func (pv MockPV) SignProposal(chainID string, proposal *Proposal) error { // String returns a string representation of the MockPV. func (pv MockPV) String() string { - addr := pv.GetPubKey().Address() - return fmt.Sprintf("MockPV{%v}", addr) + mpv, _ := pv.GetPubKey() // mockPV will never return an error, ignored here + return fmt.Sprintf("MockPV{%v}", mpv.Address()) } // XXX: Implement. diff --git a/types/proposal_test.go b/types/proposal_test.go index 1b30a7286..a1d0fac37 100644 --- a/types/proposal_test.go +++ b/types/proposal_test.go @@ -45,7 +45,8 @@ func TestProposalString(t *testing.T) { func TestProposalVerifySignature(t *testing.T) { privVal := NewMockPV() - pubKey := privVal.GetPubKey() + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) prop := NewProposal( 4, 2, 2, @@ -53,7 +54,7 @@ func TestProposalVerifySignature(t *testing.T) { signBytes := prop.SignBytes("test_chain_id") // sign it - err := privVal.SignProposal("test_chain_id", prop) + err = privVal.SignProposal("test_chain_id", prop) require.NoError(t, err) // verify the same proposal @@ -93,8 +94,9 @@ func BenchmarkProposalSign(b *testing.B) { func BenchmarkProposalVerifySignature(b *testing.B) { privVal := NewMockPV() err := privVal.SignProposal("test_chain_id", testProposal) - require.Nil(b, err) - pubKey := privVal.GetPubKey() + require.NoError(b, err) + pubKey, err := privVal.GetPubKey() + require.NoError(b, err) for i := 0; i < b.N; i++ { pubKey.VerifyBytes(testProposal.SignBytes("test_chain_id"), testProposal.Signature) diff --git a/types/protobuf_test.go b/types/protobuf_test.go index 7d5434be6..3f4cef9ce 100644 --- a/types/protobuf_test.go +++ b/types/protobuf_test.go @@ -6,6 +6,7 @@ import ( "github.com/golang/protobuf/proto" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" amino "github.com/tendermint/go-amino" abci "github.com/tendermint/tendermint/abci/types" @@ -131,11 +132,12 @@ func TestABCIEvidence(t *testing.T) { blockID := makeBlockID([]byte("blockhash"), 1000, []byte("partshash")) blockID2 := makeBlockID([]byte("blockhash2"), 1000, []byte("partshash")) const chainID = "mychain" - pubKey := val.GetPubKey() + pubKey, err := val.GetPubKey() + require.NoError(t, err) ev := &DuplicateVoteEvidence{ PubKey: pubKey, - VoteA: makeVote(val, chainID, 0, 10, 2, 1, blockID), - VoteB: makeVote(val, chainID, 0, 10, 2, 1, blockID2), + VoteA: makeVote(t, val, chainID, 0, 10, 2, 1, blockID), + VoteB: makeVote(t, val, chainID, 0, 10, 2, 1, blockID2), } abciEv := TM2PB.Evidence( ev, diff --git a/types/test_util.go b/types/test_util.go index 48913f308..377c965a8 100644 --- a/types/test_util.go +++ b/types/test_util.go @@ -2,6 +2,8 @@ package types import ( "time" + + "github.com/pkg/errors" ) func MakeCommit(blockID BlockID, height int64, round int, @@ -9,9 +11,12 @@ func MakeCommit(blockID BlockID, height int64, round int, // all sign for i := 0; i < len(validators); i++ { - addr := validators[i].GetPubKey().Address() + pubKey, err := validators[i].GetPubKey() + if err != nil { + return nil, errors.Wrap(err, "can't get pubkey") + } vote := &Vote{ - ValidatorAddress: addr, + ValidatorAddress: pubKey.Address(), ValidatorIndex: i, Height: height, Round: round, @@ -20,7 +25,7 @@ func MakeCommit(blockID BlockID, height int64, round int, Timestamp: now, } - _, err := signAddVote(validators[i], vote, voteSet) + _, err = signAddVote(validators[i], vote, voteSet) if err != nil { return nil, err } @@ -45,7 +50,11 @@ func MakeVote( chainID string, now time.Time, ) (*Vote, error) { - addr := privVal.GetPubKey().Address() + pubKey, err := privVal.GetPubKey() + if err != nil { + return nil, errors.Wrap(err, "can't get pubkey") + } + addr := pubKey.Address() idx, _ := valSet.GetByAddress(addr) vote := &Vote{ ValidatorAddress: addr, diff --git a/types/validator.go b/types/validator.go index c3cadc4d3..359a19114 100644 --- a/types/validator.go +++ b/types/validator.go @@ -105,7 +105,10 @@ func RandValidator(randPower bool, minPower int64) (*Validator, PrivValidator) { if randPower { votePower += int64(tmrand.Uint32()) } - pubKey := privVal.GetPubKey() + pubKey, err := privVal.GetPubKey() + if err != nil { + panic(fmt.Errorf("could not retrieve pubkey %w", err)) + } val := NewValidator(pubKey, votePower) return val, privVal } diff --git a/types/vote_set_test.go b/types/vote_set_test.go index ab4433a39..fc4eb76f3 100644 --- a/types/vote_set_test.go +++ b/types/vote_set_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto" tmrand "github.com/tendermint/tendermint/libs/rand" @@ -73,7 +74,10 @@ func TestAddVote(t *testing.T) { // t.Logf(">> %v", voteSet) - val0Addr := val0.GetPubKey().Address() + val0p, err := val0.GetPubKey() + require.NoError(t, err) + val0Addr := val0p.Address() + if voteSet.GetByAddress(val0Addr) != nil { t.Errorf("expected GetByAddress(val0.Address) to be nil") } @@ -94,7 +98,7 @@ func TestAddVote(t *testing.T) { Timestamp: tmtime.Now(), BlockID: BlockID{nil, PartSetHeader{}}, } - _, err := signAddVote(val0, vote, voteSet) + _, err = signAddVote(val0, vote, voteSet) if err != nil { t.Error(err) } @@ -126,9 +130,11 @@ func Test2_3Majority(t *testing.T) { } // 6 out of 10 voted for nil. for i := 0; i < 6; i++ { - addr := privValidators[i].GetPubKey().Address() + pubKey, err := privValidators[i].GetPubKey() + require.NoError(t, err) + addr := pubKey.Address() vote := withValidator(voteProto, addr, i) - _, err := signAddVote(privValidators[i], vote, voteSet) + _, err = signAddVote(privValidators[i], vote, voteSet) if err != nil { t.Error(err) } @@ -140,9 +146,11 @@ func Test2_3Majority(t *testing.T) { // 7th validator voted for some blockhash { - addr := privValidators[6].GetPubKey().Address() + pubKey, err := privValidators[6].GetPubKey() + require.NoError(t, err) + addr := pubKey.Address() vote := withValidator(voteProto, addr, 6) - _, err := signAddVote(privValidators[6], withBlockHash(vote, tmrand.Bytes(32)), voteSet) + _, err = signAddVote(privValidators[6], withBlockHash(vote, tmrand.Bytes(32)), voteSet) if err != nil { t.Error(err) } @@ -154,9 +162,11 @@ func Test2_3Majority(t *testing.T) { // 8th validator voted for nil. { - addr := privValidators[7].GetPubKey().Address() + pubKey, err := privValidators[7].GetPubKey() + require.NoError(t, err) + addr := pubKey.Address() vote := withValidator(voteProto, addr, 7) - _, err := signAddVote(privValidators[7], vote, voteSet) + _, err = signAddVote(privValidators[7], vote, voteSet) if err != nil { t.Error(err) } @@ -187,9 +197,11 @@ func Test2_3MajorityRedux(t *testing.T) { // 66 out of 100 voted for nil. for i := 0; i < 66; i++ { - addr := privValidators[i].GetPubKey().Address() + pubKey, err := privValidators[i].GetPubKey() + require.NoError(t, err) + addr := pubKey.Address() vote := withValidator(voteProto, addr, i) - _, err := signAddVote(privValidators[i], vote, voteSet) + _, err = signAddVote(privValidators[i], vote, voteSet) if err != nil { t.Error(err) } @@ -201,9 +213,11 @@ func Test2_3MajorityRedux(t *testing.T) { // 67th validator voted for nil { - adrr := privValidators[66].GetPubKey().Address() + pubKey, err := privValidators[66].GetPubKey() + require.NoError(t, err) + adrr := pubKey.Address() vote := withValidator(voteProto, adrr, 66) - _, err := signAddVote(privValidators[66], withBlockHash(vote, nil), voteSet) + _, err = signAddVote(privValidators[66], withBlockHash(vote, nil), voteSet) if err != nil { t.Error(err) } @@ -215,10 +229,12 @@ func Test2_3MajorityRedux(t *testing.T) { // 68th validator voted for a different BlockParts PartSetHeader { - addr := privValidators[67].GetPubKey().Address() + pubKey, err := privValidators[67].GetPubKey() + require.NoError(t, err) + addr := pubKey.Address() vote := withValidator(voteProto, addr, 67) blockPartsHeader := PartSetHeader{blockPartsTotal, crypto.CRandBytes(32)} - _, err := signAddVote(privValidators[67], withBlockPartsHeader(vote, blockPartsHeader), voteSet) + _, err = signAddVote(privValidators[67], withBlockPartsHeader(vote, blockPartsHeader), voteSet) if err != nil { t.Error(err) } @@ -230,10 +246,12 @@ func Test2_3MajorityRedux(t *testing.T) { // 69th validator voted for different BlockParts Total { - addr := privValidators[68].GetPubKey().Address() + pubKey, err := privValidators[68].GetPubKey() + require.NoError(t, err) + addr := pubKey.Address() vote := withValidator(voteProto, addr, 68) blockPartsHeader := PartSetHeader{blockPartsTotal + 1, blockPartsHeader.Hash} - _, err := signAddVote(privValidators[68], withBlockPartsHeader(vote, blockPartsHeader), voteSet) + _, err = signAddVote(privValidators[68], withBlockPartsHeader(vote, blockPartsHeader), voteSet) if err != nil { t.Error(err) } @@ -245,9 +263,11 @@ func Test2_3MajorityRedux(t *testing.T) { // 70th validator voted for different BlockHash { - addr := privValidators[69].GetPubKey().Address() + pubKey, err := privValidators[69].GetPubKey() + require.NoError(t, err) + addr := pubKey.Address() vote := withValidator(voteProto, addr, 69) - _, err := signAddVote(privValidators[69], withBlockHash(vote, tmrand.Bytes(32)), voteSet) + _, err = signAddVote(privValidators[69], withBlockHash(vote, tmrand.Bytes(32)), voteSet) if err != nil { t.Error(err) } @@ -259,9 +279,11 @@ func Test2_3MajorityRedux(t *testing.T) { // 71st validator voted for the right BlockHash & BlockPartsHeader { - addr := privValidators[70].GetPubKey().Address() + pubKey, err := privValidators[70].GetPubKey() + require.NoError(t, err) + addr := pubKey.Address() vote := withValidator(voteProto, addr, 70) - _, err := signAddVote(privValidators[70], vote, voteSet) + _, err = signAddVote(privValidators[70], vote, voteSet) if err != nil { t.Error(err) } @@ -288,7 +310,9 @@ func TestBadVotes(t *testing.T) { // val0 votes for nil. { - addr := privValidators[0].GetPubKey().Address() + pubKey, err := privValidators[0].GetPubKey() + require.NoError(t, err) + addr := pubKey.Address() vote := withValidator(voteProto, addr, 0) added, err := signAddVote(privValidators[0], vote, voteSet) if !added || err != nil { @@ -298,7 +322,9 @@ func TestBadVotes(t *testing.T) { // val0 votes again for some block. { - addr := privValidators[0].GetPubKey().Address() + pubKey, err := privValidators[0].GetPubKey() + require.NoError(t, err) + addr := pubKey.Address() vote := withValidator(voteProto, addr, 0) added, err := signAddVote(privValidators[0], withBlockHash(vote, tmrand.Bytes(32)), voteSet) if added || err == nil { @@ -308,7 +334,9 @@ func TestBadVotes(t *testing.T) { // val1 votes on another height { - addr := privValidators[1].GetPubKey().Address() + pubKey, err := privValidators[1].GetPubKey() + require.NoError(t, err) + addr := pubKey.Address() vote := withValidator(voteProto, addr, 1) added, err := signAddVote(privValidators[1], withHeight(vote, height+1), voteSet) if added || err == nil { @@ -318,7 +346,9 @@ func TestBadVotes(t *testing.T) { // val2 votes on another round { - addr := privValidators[2].GetPubKey().Address() + pubKey, err := privValidators[2].GetPubKey() + require.NoError(t, err) + addr := pubKey.Address() vote := withValidator(voteProto, addr, 2) added, err := signAddVote(privValidators[2], withRound(vote, round+1), voteSet) if added || err == nil { @@ -328,7 +358,9 @@ func TestBadVotes(t *testing.T) { // val3 votes of another type. { - addr := privValidators[3].GetPubKey().Address() + pubKey, err := privValidators[3].GetPubKey() + require.NoError(t, err) + addr := pubKey.Address() vote := withValidator(voteProto, addr, 3) added, err := signAddVote(privValidators[3], withType(vote, byte(PrecommitType)), voteSet) if added || err == nil { @@ -353,7 +385,10 @@ func TestConflicts(t *testing.T) { BlockID: BlockID{nil, PartSetHeader{}}, } - val0Addr := privValidators[0].GetPubKey().Address() + val0, err := privValidators[0].GetPubKey() + require.NoError(t, err) + val0Addr := val0.Address() + // val0 votes for nil. { vote := withValidator(voteProto, val0Addr, 0) @@ -407,7 +442,9 @@ func TestConflicts(t *testing.T) { // val1 votes for blockHash1. { - addr := privValidators[1].GetPubKey().Address() + pv, err := privValidators[1].GetPubKey() + assert.NoError(t, err) + addr := pv.Address() vote := withValidator(voteProto, addr, 1) added, err := signAddVote(privValidators[1], withBlockHash(vote, blockHash1), voteSet) if !added || err != nil { @@ -425,7 +462,9 @@ func TestConflicts(t *testing.T) { // val2 votes for blockHash2. { - addr := privValidators[2].GetPubKey().Address() + pv, err := privValidators[2].GetPubKey() + assert.NoError(t, err) + addr := pv.Address() vote := withValidator(voteProto, addr, 2) added, err := signAddVote(privValidators[2], withBlockHash(vote, blockHash2), voteSet) if !added || err != nil { @@ -446,7 +485,9 @@ func TestConflicts(t *testing.T) { // val2 votes for blockHash1. { - addr := privValidators[2].GetPubKey().Address() + pv, err := privValidators[2].GetPubKey() + assert.NoError(t, err) + addr := pv.Address() vote := withValidator(voteProto, addr, 2) added, err := signAddVote(privValidators[2], withBlockHash(vote, blockHash1), voteSet) if !added { @@ -488,9 +529,11 @@ func TestMakeCommit(t *testing.T) { // 6 out of 10 voted for some block. for i := 0; i < 6; i++ { - addr := privValidators[i].GetPubKey().Address() + pv, err := privValidators[i].GetPubKey() + assert.NoError(t, err) + addr := pv.Address() vote := withValidator(voteProto, addr, i) - _, err := signAddVote(privValidators[i], vote, voteSet) + _, err = signAddVote(privValidators[i], vote, voteSet) if err != nil { t.Error(err) } @@ -501,12 +544,14 @@ func TestMakeCommit(t *testing.T) { // 7th voted for some other block. { - addr := privValidators[6].GetPubKey().Address() + pv, err := privValidators[6].GetPubKey() + assert.NoError(t, err) + addr := pv.Address() vote := withValidator(voteProto, addr, 6) vote = withBlockHash(vote, tmrand.Bytes(32)) vote = withBlockPartsHeader(vote, PartSetHeader{123, tmrand.Bytes(32)}) - _, err := signAddVote(privValidators[6], vote, voteSet) + _, err = signAddVote(privValidators[6], vote, voteSet) if err != nil { t.Error(err) } @@ -514,9 +559,11 @@ func TestMakeCommit(t *testing.T) { // The 8th voted like everyone else. { - addr := privValidators[7].GetPubKey().Address() + pv, err := privValidators[7].GetPubKey() + assert.NoError(t, err) + addr := pv.Address() vote := withValidator(voteProto, addr, 7) - _, err := signAddVote(privValidators[7], vote, voteSet) + _, err = signAddVote(privValidators[7], vote, voteSet) if err != nil { t.Error(err) } @@ -524,11 +571,13 @@ func TestMakeCommit(t *testing.T) { // The 9th voted for nil. { - addr := privValidators[8].GetPubKey().Address() + pv, err := privValidators[8].GetPubKey() + assert.NoError(t, err) + addr := pv.Address() vote := withValidator(voteProto, addr, 8) vote.BlockID = BlockID{} - _, err := signAddVote(privValidators[8], vote, voteSet) + _, err = signAddVote(privValidators[8], vote, voteSet) if err != nil { t.Error(err) } diff --git a/types/vote_test.go b/types/vote_test.go index 40a9d650a..eb4f6a955 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -143,13 +143,14 @@ func TestVoteProposalNotEq(t *testing.T) { func TestVoteVerifySignature(t *testing.T) { privVal := NewMockPV() - pubkey := privVal.GetPubKey() + pubkey, err := privVal.GetPubKey() + require.NoError(t, err) vote := examplePrecommit() signBytes := vote.SignBytes("test_chain_id") // sign it - err := privVal.SignVote("test_chain_id", vote) + err = privVal.SignVote("test_chain_id", vote) require.NoError(t, err) // verify the same vote @@ -193,12 +194,13 @@ func TestIsVoteTypeValid(t *testing.T) { func TestVoteVerify(t *testing.T) { privVal := NewMockPV() - pubkey := privVal.GetPubKey() + pubkey, err := privVal.GetPubKey() + require.NoError(t, err) vote := examplePrevote() vote.ValidatorAddress = pubkey.Address() - err := vote.Verify("test_chain_id", ed25519.GenPrivKey().PubKey()) + err = vote.Verify("test_chain_id", ed25519.GenPrivKey().PubKey()) if assert.Error(t, err) { assert.Equal(t, ErrVoteInvalidValidatorAddress, err) } From c8bb1cc8b7d20573280528b61a1beb3efa80b531 Mon Sep 17 00:00:00 2001 From: Callum Michael Waters Date: Thu, 12 Mar 2020 12:35:28 +0100 Subject: [PATCH 45/77] made suggested changes --- p2p/pex/addrbook.go | 7 ++++--- p2p/pex/errors.go | 1 + p2p/pex/pex_reactor.go | 4 ---- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index 207b6a1c3..6e8397cd6 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -222,11 +222,12 @@ func (a *addrBook) IsGood(addr *p2p.NetAddress) bool { return a.addrLookup[addr.ID].isOld() } +// IsBanned returns true if the peer is currently banned func (a *addrBook) IsBanned(addr *p2p.NetAddress) bool { a.mtx.Lock() - defer a.mtx.Unlock() - _, ok := a.badPeers[addr.ID] + a.mtx.Unlock() + return ok } @@ -351,7 +352,7 @@ func (a *addrBook) ReinstateBadPeers() { bucket := a.calcNewBucket(ka.Addr, ka.Src) a.addToNewBucket(ka, bucket) delete(a.badPeers, ka.ID()) - a.Logger.Info("Reinstated Address", "addr", ka.Addr) + a.Logger.Info("Reinstated address", "addr", ka.Addr) } } } diff --git a/p2p/pex/errors.go b/p2p/pex/errors.go index 1ea5a3b3e..1892b5fff 100644 --- a/p2p/pex/errors.go +++ b/p2p/pex/errors.go @@ -64,6 +64,7 @@ func (err ErrAddrBookInvalidAddr) Error() string { return fmt.Sprintf("Cannot add invalid address %v: %v", err.Addr, err.AddrErr) } +// Err is thrown when the address is banned and therefore cannot be used type ErrAddressBanned struct { Addr *p2p.NetAddress } diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index afe29e16e..b0a1c02bb 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -534,10 +534,6 @@ func (r *Reactor) dialAttemptsInfo(addr *p2p.NetAddress) (attempts int, lastDial func (r *Reactor) dialPeer(addr *p2p.NetAddress) error { attempts, lastDialed := r.dialAttemptsInfo(addr) if !r.Switch.IsPeerPersistent(addr) && attempts > maxAttemptsToDial { - // TODO(melekes): have a blacklist in the addrbook with peers whom we've - // failed to connect to. Then we can clean up attemptsToDial, which acts as - // a blacklist currently. - // https://github.com/tendermint/tendermint/issues/3572 r.book.MarkBad(addr, defaultBanTime) return errMaxAttemptsToDial{} } From 7e6b1a87dae105c45d0efa644ec11a8fd6abebb0 Mon Sep 17 00:00:00 2001 From: Callum Michael Waters Date: Thu, 12 Mar 2020 15:29:31 +0100 Subject: [PATCH 46/77] cannot decrease ban time --- p2p/pex/addrbook.go | 19 ++++++++++--------- p2p/pex/errors.go | 2 +- p2p/pex/known_address.go | 4 +++- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index 6e8397cd6..c9c372638 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -769,16 +769,17 @@ func (a *addrBook) addBadPeer(addr *p2p.NetAddress, banTime time.Duration) bool // check it exists in addrbook ka := a.addrLookup[addr.ID] // check address is not already there - if ka != nil { - if _, alreadyBadPeer := a.badPeers[addr.ID]; !alreadyBadPeer { - // add to bad peer list - ka.ban(banTime) - a.badPeers[addr.ID] = ka - a.Logger.Info("Add address to blacklist", "addr", addr) - } - return true + if ka == nil { + return false } - return false + + if _, alreadyBadPeer := a.badPeers[addr.ID]; !alreadyBadPeer { + // add to bad peer list + ka.ban(banTime) + a.badPeers[addr.ID] = ka + a.Logger.Info("Add address to blacklist", "addr", addr) + } + return true } //--------------------------------------------------------------------- diff --git a/p2p/pex/errors.go b/p2p/pex/errors.go index 1892b5fff..1fc54ea50 100644 --- a/p2p/pex/errors.go +++ b/p2p/pex/errors.go @@ -64,7 +64,7 @@ func (err ErrAddrBookInvalidAddr) Error() string { return fmt.Sprintf("Cannot add invalid address %v: %v", err.Addr, err.AddrErr) } -// Err is thrown when the address is banned and therefore cannot be used +// ErrAddressBanned is thrown when the address has been banned and therefore cannot be used type ErrAddressBanned struct { Addr *p2p.NetAddress } diff --git a/p2p/pex/known_address.go b/p2p/pex/known_address.go index 674b66553..e98a9e97e 100644 --- a/p2p/pex/known_address.go +++ b/p2p/pex/known_address.go @@ -56,7 +56,9 @@ func (ka *knownAddress) markGood() { } func (ka *knownAddress) ban(banTime time.Duration) { - ka.LastBanTime = time.Now().Add(banTime) + if ka.LastBanTime.Before(time.Now().Add(banTime)) { + ka.LastBanTime = time.Now().Add(banTime) + } } func (ka *knownAddress) isBanned() bool { From c42d5305077c57247c8d486daa71e3f5652e94f1 Mon Sep 17 00:00:00 2001 From: Callum Date: Thu, 12 Mar 2020 18:03:44 +0100 Subject: [PATCH 47/77] update Changelog with PR #4548 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89315a1b7..d1a6c16a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## v0.33.2 -*March 11, 2020* +*March 12, 2020* Special thanks to external contributors on this release: @antho1404, @michaelfig, @gterzian, @tau3, @Shivani912 @@ -38,6 +38,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi - [lite2] [\#4470](https://github.com/tendermint/tendermint/issues/4470) Fix inconsistent header-validatorset pairing (@melekes) - [lite2] [\#4488](https://github.com/tendermint/tendermint/issues/4488) Allow local clock drift -10 sec. (@melekes) - [p2p] [\#4449](https://github.com/tendermint/tendermint/pull/4449) Use `curve25519.X25519()` instead of `ScalarMult` (@erikgrinaker) +- [p2p] [\#4548](https://github.com/tendermint/tendermint/pull/4548) Add ban list to address book (@cmwaters) - [types] [\#4417](https://github.com/tendermint/tendermint/issues/4417) **VerifyCommitX() functions should return as soon as +2/3 threshold is reached** (@alessio). - [libs/kv] [\#4542](https://github.com/tendermint/tendermint/pull/4542) remove unused type KI64Pair (@tessr) From 586d29999144c10fa3a0177d87804c4dd99928d3 Mon Sep 17 00:00:00 2001 From: Callum Date: Thu, 12 Mar 2020 18:39:13 +0100 Subject: [PATCH 48/77] moved to changelog_pending --- CHANGELOG.md | 3 +-- CHANGELOG_PENDING.md | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1a6c16a1..89315a1b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## v0.33.2 -*March 12, 2020* +*March 11, 2020* Special thanks to external contributors on this release: @antho1404, @michaelfig, @gterzian, @tau3, @Shivani912 @@ -38,7 +38,6 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi - [lite2] [\#4470](https://github.com/tendermint/tendermint/issues/4470) Fix inconsistent header-validatorset pairing (@melekes) - [lite2] [\#4488](https://github.com/tendermint/tendermint/issues/4488) Allow local clock drift -10 sec. (@melekes) - [p2p] [\#4449](https://github.com/tendermint/tendermint/pull/4449) Use `curve25519.X25519()` instead of `ScalarMult` (@erikgrinaker) -- [p2p] [\#4548](https://github.com/tendermint/tendermint/pull/4548) Add ban list to address book (@cmwaters) - [types] [\#4417](https://github.com/tendermint/tendermint/issues/4417) **VerifyCommitX() functions should return as soon as +2/3 threshold is reached** (@alessio). - [libs/kv] [\#4542](https://github.com/tendermint/tendermint/pull/4542) remove unused type KI64Pair (@tessr) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 849c26950..07b4541a2 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -18,6 +18,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi ### IMPROVEMENTS: +- [p2p] [\#4548](https://github.com/tendermint/tendermint/pull/4548) Add ban list to address book (@cmwaters) - [privval] \#4534 Add `error` as a return value on`GetPubKey()` ### BUG FIXES: From 53b660ef103b17b050322e0105234d87ed152f3b Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Fri, 13 Mar 2020 01:28:37 +0500 Subject: [PATCH 49/77] Use docs-staging branch (#4561) Co-authored-by: Marko --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b9d535da6..95a4bd354 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -404,7 +404,7 @@ workflows: filters: branches: only: - - docs-theme-latest + - docs-staging - setup_dependencies - test_abci_apps: requires: From ab9cb0e1332ced4fafdae59dda7e887009e469de Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 13 Mar 2020 11:48:50 +0000 Subject: [PATCH 50/77] build(deps): bump github.com/golang/protobuf from 1.3.4 to 1.3.5 (#4563) Bumps [github.com/golang/protobuf](https://github.com/golang/protobuf) from 1.3.4 to 1.3.5. - [Release notes](https://github.com/golang/protobuf/releases) - [Commits](https://github.com/golang/protobuf/compare/v1.3.4...v1.3.5) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index c86c1be2d..11a150579 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/go-kit/kit v0.10.0 github.com/go-logfmt/logfmt v0.5.0 github.com/gogo/protobuf v1.3.1 - github.com/golang/protobuf v1.3.4 + github.com/golang/protobuf v1.3.5 github.com/gorilla/websocket v1.4.1 github.com/gtank/merlin v0.1.1 github.com/libp2p/go-buffer-pool v0.0.2 diff --git a/go.sum b/go.sum index 6c7ab4b19..aa9ec1eff 100644 --- a/go.sum +++ b/go.sum @@ -146,6 +146,8 @@ github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= From c06341e100486f982a7a69b42873d138ff705cb4 Mon Sep 17 00:00:00 2001 From: Marko Date: Fri, 13 Mar 2020 15:20:00 +0100 Subject: [PATCH 51/77] removal: remove build folder (#4565) - remove tools/build folder Signed-off-by: Marko Baricevic --- tools/build/.gitignore | 4 -- tools/build/RPM-GPG-KEY-Tendermint | 19 ------ tools/build/Release | 7 --- tools/build/Release_amd64 | 5 -- tools/build/_gpg | 8 --- tools/build/basecoind/DEBIAN/changelog | 6 -- tools/build/basecoind/DEBIAN/compat | 1 - tools/build/basecoind/DEBIAN/control | 14 ----- tools/build/basecoind/DEBIAN/copyright | 21 ------- tools/build/basecoind/DEBIAN/postinst | 41 ------------- tools/build/basecoind/DEBIAN/postrm | 41 ------------- tools/build/basecoind/DEBIAN/preinst | 38 ------------ tools/build/basecoind/DEBIAN/prerm | 38 ------------ .../systemd/system-preset/50-basecoind.preset | 2 - .../etc/systemd/system/basecoind.service | 18 ------ .../basecoind/usr/share/basecoind/key.json | 12 ---- .../basecoind/usr/share/basecoind/key2.json | 12 ---- tools/build/ethermint/DEBIAN/changelog | 6 -- tools/build/ethermint/DEBIAN/compat | 1 - tools/build/ethermint/DEBIAN/control | 15 ----- tools/build/ethermint/DEBIAN/copyright | 21 ------- tools/build/ethermint/DEBIAN/postinst | 46 -------------- tools/build/ethermint/DEBIAN/postrm | 41 ------------- tools/build/ethermint/DEBIAN/preinst | 38 ------------ tools/build/ethermint/DEBIAN/prerm | 38 ------------ .../systemd/system-preset/50-ethermint.preset | 2 - .../etc/systemd/system/ethermint.service | 17 ------ tools/build/gaia/DEBIAN/changelog | 6 -- tools/build/gaia/DEBIAN/compat | 1 - tools/build/gaia/DEBIAN/control | 14 ----- tools/build/gaia/DEBIAN/copyright | 21 ------- tools/build/gaia/DEBIAN/postinst | 41 ------------- tools/build/gaia/DEBIAN/postrm | 41 ------------- tools/build/gaia/DEBIAN/preinst | 38 ------------ tools/build/gaia/DEBIAN/prerm | 38 ------------ .../etc/systemd/system-preset/50-gaia.preset | 2 - .../gaia/etc/systemd/system/gaia.service | 17 ------ tools/build/gaia/usr/share/gaia/key.json | 12 ---- tools/build/gaia/usr/share/gaia/key2.json | 12 ---- tools/build/generate-spec | 36 ----------- tools/build/sign | 26 -------- tools/build/spectemplates/app-template.spec | 55 ----------------- tools/build/spectemplates/basecoind.data | 5 -- tools/build/spectemplates/ethermint.data | 5 -- tools/build/spectemplates/ethermint.spec | 60 ------------------- tools/build/spectemplates/gaia.data | 5 -- tools/build/spectemplates/tendermint.spec | 31 ---------- tools/build/tendermint.repo | 12 ---- tools/build/tendermint/DEBIAN/changelog | 6 -- tools/build/tendermint/DEBIAN/compat | 1 - tools/build/tendermint/DEBIAN/control | 14 ----- tools/build/tendermint/DEBIAN/copyright | 21 ------- 52 files changed, 1032 deletions(-) delete mode 100644 tools/build/.gitignore delete mode 100644 tools/build/RPM-GPG-KEY-Tendermint delete mode 100644 tools/build/Release delete mode 100644 tools/build/Release_amd64 delete mode 100644 tools/build/_gpg delete mode 100644 tools/build/basecoind/DEBIAN/changelog delete mode 100644 tools/build/basecoind/DEBIAN/compat delete mode 100644 tools/build/basecoind/DEBIAN/control delete mode 100644 tools/build/basecoind/DEBIAN/copyright delete mode 100644 tools/build/basecoind/DEBIAN/postinst delete mode 100644 tools/build/basecoind/DEBIAN/postrm delete mode 100644 tools/build/basecoind/DEBIAN/preinst delete mode 100644 tools/build/basecoind/DEBIAN/prerm delete mode 100644 tools/build/basecoind/etc/systemd/system-preset/50-basecoind.preset delete mode 100644 tools/build/basecoind/etc/systemd/system/basecoind.service delete mode 100644 tools/build/basecoind/usr/share/basecoind/key.json delete mode 100644 tools/build/basecoind/usr/share/basecoind/key2.json delete mode 100644 tools/build/ethermint/DEBIAN/changelog delete mode 100644 tools/build/ethermint/DEBIAN/compat delete mode 100644 tools/build/ethermint/DEBIAN/control delete mode 100644 tools/build/ethermint/DEBIAN/copyright delete mode 100644 tools/build/ethermint/DEBIAN/postinst delete mode 100644 tools/build/ethermint/DEBIAN/postrm delete mode 100644 tools/build/ethermint/DEBIAN/preinst delete mode 100644 tools/build/ethermint/DEBIAN/prerm delete mode 100644 tools/build/ethermint/etc/systemd/system-preset/50-ethermint.preset delete mode 100644 tools/build/ethermint/etc/systemd/system/ethermint.service delete mode 100644 tools/build/gaia/DEBIAN/changelog delete mode 100644 tools/build/gaia/DEBIAN/compat delete mode 100644 tools/build/gaia/DEBIAN/control delete mode 100644 tools/build/gaia/DEBIAN/copyright delete mode 100644 tools/build/gaia/DEBIAN/postinst delete mode 100644 tools/build/gaia/DEBIAN/postrm delete mode 100644 tools/build/gaia/DEBIAN/preinst delete mode 100644 tools/build/gaia/DEBIAN/prerm delete mode 100644 tools/build/gaia/etc/systemd/system-preset/50-gaia.preset delete mode 100644 tools/build/gaia/etc/systemd/system/gaia.service delete mode 100644 tools/build/gaia/usr/share/gaia/key.json delete mode 100644 tools/build/gaia/usr/share/gaia/key2.json delete mode 100755 tools/build/generate-spec delete mode 100755 tools/build/sign delete mode 100644 tools/build/spectemplates/app-template.spec delete mode 100644 tools/build/spectemplates/basecoind.data delete mode 100644 tools/build/spectemplates/ethermint.data delete mode 100644 tools/build/spectemplates/ethermint.spec delete mode 100644 tools/build/spectemplates/gaia.data delete mode 100644 tools/build/spectemplates/tendermint.spec delete mode 100644 tools/build/tendermint.repo delete mode 100644 tools/build/tendermint/DEBIAN/changelog delete mode 100644 tools/build/tendermint/DEBIAN/compat delete mode 100644 tools/build/tendermint/DEBIAN/control delete mode 100644 tools/build/tendermint/DEBIAN/copyright diff --git a/tools/build/.gitignore b/tools/build/.gitignore deleted file mode 100644 index 9974388f1..000000000 --- a/tools/build/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -BUILD -RPMS -SPECS -tmp diff --git a/tools/build/RPM-GPG-KEY-Tendermint b/tools/build/RPM-GPG-KEY-Tendermint deleted file mode 100644 index e6f200d87..000000000 --- a/tools/build/RPM-GPG-KEY-Tendermint +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN PGP PUBLIC KEY BLOCK----- -Version: GnuPG v2.0.22 (GNU/Linux) - -mQENBFk97ngBCADaiPQFKJI7zWYdUKqC490DzY9g9LatsWoJErK5LuMXwEnF5i+a -UkygueukA4C5U7L71l5EeOB9rtb6AbkF4IEZsmmp93APec/3Vfbac9xvK4dBdiht -F8SrazPdHeR6AKcZH8ZpG/+mdONvGb/gEgtxVjaeIJFpCbjKLlKEXazh2zamhhth -q+Nn/17QmI3KBiaGqQK5w4kGZ4mZPy6fXMQhW5dDMq9f4anlGIAYi9O53dVxsx2S -5d+NHuGer5Ps0u6WMJi/e+UT2EGwzP6ygOxkIjyhMFuVftabOtSSrRHHetw8UAaI -N/RPn2gSbQtOQ7unzHDXp3/o6/r2nDEErPyJABEBAAG0LkdyZWcgU3phYm8gKFRl -bmRlcm1pbnQpIDxncmVnQHBoaWxvc29iZWFyLmNvbT6JATkEEwECACMFAlk97ngC -GwMHCwkIBwMCAQYVCAIJCgsEFgIDAQIeAQIXgAAKCRDIkIHIISLL6bX/CACXTKmO -u5XgvJICH0pHNeVS5/4Om1Rsg1xNmEkGFBP8N2fqn576exbOLgWLSyNHTEyrJNoc -iTeUtod2qqbVGwRgWm1zeiP8NBYiQ9SUbqskIqcPavJNGWIxsCB0p/odoZah8xSj -tGrkoyoxrc+7z2JgKYK8SVSkJXQkzuc5/ZlY85ci5gPKQhlo5YDqGo+4U9n/Ieo5 -nkF8LBalFC2j7A7sQNroEicpulpGhIq3jyUHtadX01z3pNzuX+wfHX9futoet0YS -tG2007WoPGV0whGnoKxmk0JhwzhscC2XNtJl1GZcwqOOlPU9eGtZuPKj/HBAlRtz -4xTOAcklpg8soqRA -=jNDW ------END PGP PUBLIC KEY BLOCK----- diff --git a/tools/build/Release b/tools/build/Release deleted file mode 100644 index 9003d1320..000000000 --- a/tools/build/Release +++ /dev/null @@ -1,7 +0,0 @@ -Origin: Tendermint -Label: Tendermint -Suite: stable -Date: Fri, 16 Jun 2017 19:44:00 UTC -Architectures: amd64 -Components: main -Description: Tendermint repository diff --git a/tools/build/Release_amd64 b/tools/build/Release_amd64 deleted file mode 100644 index 1f2ecbfe2..000000000 --- a/tools/build/Release_amd64 +++ /dev/null @@ -1,5 +0,0 @@ -Archive: stable -Component: main -Origin: Tendermint -Label: Tendermint -Architecture: amd64 diff --git a/tools/build/_gpg b/tools/build/_gpg deleted file mode 100644 index 73742b5d8..000000000 --- a/tools/build/_gpg +++ /dev/null @@ -1,8 +0,0 @@ -Version: 4 -Signer: -Date: @DATETIMESTAMP@ -Role: builder -Files: - @BINMD5@ @BINSHA1@ @BINSIZE@ debian-binary - @CONMD5@ @CONSHA1@ @CONSIZE@ control.tar.gz - @DATMD5@ @DATSHA1@ @DATSIZE@ data.tar.xz diff --git a/tools/build/basecoind/DEBIAN/changelog b/tools/build/basecoind/DEBIAN/changelog deleted file mode 100644 index 260718eaf..000000000 --- a/tools/build/basecoind/DEBIAN/changelog +++ /dev/null @@ -1,6 +0,0 @@ -basecoind (@VERSION@) @STABILITY@; urgency=medium - - * Automatic build. See https://github.com/cosmos/cosmos-sdk for more information. - - -- Greg Szabo @DATETIMESTAMP@ - diff --git a/tools/build/basecoind/DEBIAN/compat b/tools/build/basecoind/DEBIAN/compat deleted file mode 100644 index ec635144f..000000000 --- a/tools/build/basecoind/DEBIAN/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/tools/build/basecoind/DEBIAN/control b/tools/build/basecoind/DEBIAN/control deleted file mode 100644 index c15d49110..000000000 --- a/tools/build/basecoind/DEBIAN/control +++ /dev/null @@ -1,14 +0,0 @@ -Source: basecoind -Section: net -Priority: optional -Maintainer: Greg Szabo -Build-Depends: debhelper (>=9) -Standards-Version: 3.9.6 -Homepage: https://tendermint.com -Package: basecoind -Architecture: amd64 -Version: @VERSION@ -Installed-Size: @INSTALLEDSIZE@ -Description: basecoind is a Proof-of-Stake cryptocurrency and framework - Basecoind is an ABCI application designed to be used with the Tendermint consensus engine to form a Proof-of-Stake cryptocurrency. It also provides a general purpose framework for extending the feature-set of the cryptocurrency by implementing plugins. - diff --git a/tools/build/basecoind/DEBIAN/copyright b/tools/build/basecoind/DEBIAN/copyright deleted file mode 100644 index fe449650c..000000000 --- a/tools/build/basecoind/DEBIAN/copyright +++ /dev/null @@ -1,21 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: basecoind -Source: https://github.com/cosmos/cosmos-sdk - -Files: * -Copyright: 2017 All In Bits, Inc. -License: Apache-2.0 - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - . - http://www.apache.org/licenses/LICENSE-2.0 - . - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - . - On Debian systems, the full text of the Apache License 2.0 can be found - in the file `/usr/share/common-licenses/Apache-2.0'. diff --git a/tools/build/basecoind/DEBIAN/postinst b/tools/build/basecoind/DEBIAN/postinst deleted file mode 100644 index d7d8f4413..000000000 --- a/tools/build/basecoind/DEBIAN/postinst +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh -# postinst script for basecoind -# - -set -e - -# summary of how this script can be called: -# * `configure' -# * `abort-upgrade' -# * `abort-remove' `in-favour' -# -# * `abort-remove' -# * `abort-deconfigure' `in-favour' -# `removing' -# -# for details, see https://www.debian.org/doc/debian-policy/ or -# the debian-policy package - - -case "$1" in - configure) - chown basecoind.basecoind /etc/basecoind - sudo -Hu basecoind basecoind node init --home /etc/basecoind 2B24DEE2364762300168DF19B6C18BCE2D399EA2 - systemctl daemon-reload - ;; - - abort-upgrade|abort-remove|abort-deconfigure) - ;; - - *) - echo "postinst called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff --git a/tools/build/basecoind/DEBIAN/postrm b/tools/build/basecoind/DEBIAN/postrm deleted file mode 100644 index b84c9f2a4..000000000 --- a/tools/build/basecoind/DEBIAN/postrm +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh -# postrm script for basecoin -# - -set -e - -# summary of how this script can be called: -# * `remove' -# * `purge' -# * `upgrade' -# * `failed-upgrade' -# * `abort-install' -# * `abort-install' -# * `abort-upgrade' -# * `disappear' -# -# for details, see https://www.debian.org/doc/debian-policy/ or -# the debian-policy package - - -case "$1" in - upgrade|failed-upgrade|abort-upgrade) - systemctl daemon-reload - ;; - - purge|remove|abort-install|disappear) - systemctl daemon-reload - ;; - - *) - echo "postrm called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff --git a/tools/build/basecoind/DEBIAN/preinst b/tools/build/basecoind/DEBIAN/preinst deleted file mode 100644 index 53124c0ce..000000000 --- a/tools/build/basecoind/DEBIAN/preinst +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh -# preinst script for basecoind -# - -set -e - -# summary of how this script can be called: -# * `install' -# * `install' -# * `upgrade' -# * `abort-upgrade' -# for details, see https://www.debian.org/doc/debian-policy/ or -# the debian-policy package - - -case "$1" in - install|upgrade) - if ! grep -q '^basecoind:' /etc/passwd ; then - useradd -k /dev/null -r -m -b /etc basecoind - chmod 755 /etc/basecoind - fi - ;; - - abort-upgrade) - ;; - - *) - echo "preinst called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff --git a/tools/build/basecoind/DEBIAN/prerm b/tools/build/basecoind/DEBIAN/prerm deleted file mode 100644 index 18ef42079..000000000 --- a/tools/build/basecoind/DEBIAN/prerm +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh -# prerm script for basecoin -# - -set -e - -# summary of how this script can be called: -# * `remove' -# * `upgrade' -# * `failed-upgrade' -# * `remove' `in-favour' -# * `deconfigure' `in-favour' -# `removing' -# -# for details, see https://www.debian.org/doc/debian-policy/ or -# the debian-policy package - - -case "$1" in - remove|upgrade|deconfigure) - systemctl stop basecoind 2> /dev/null || : - ;; - - failed-upgrade) - ;; - - *) - echo "prerm called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff --git a/tools/build/basecoind/etc/systemd/system-preset/50-basecoind.preset b/tools/build/basecoind/etc/systemd/system-preset/50-basecoind.preset deleted file mode 100644 index 358334fc3..000000000 --- a/tools/build/basecoind/etc/systemd/system-preset/50-basecoind.preset +++ /dev/null @@ -1,2 +0,0 @@ -disable basecoind.service - diff --git a/tools/build/basecoind/etc/systemd/system/basecoind.service b/tools/build/basecoind/etc/systemd/system/basecoind.service deleted file mode 100644 index 68b46d84f..000000000 --- a/tools/build/basecoind/etc/systemd/system/basecoind.service +++ /dev/null @@ -1,18 +0,0 @@ -[Unit] -Description=Basecoind -Requires=network-online.target -After=network-online.target - -[Service] -Environment="BCHOME=/etc/basecoind" -Restart=on-failure -User=basecoind -Group=basecoind -PermissionsStartOnly=true -ExecStart=/usr/bin/basecoind start -ExecReload=/bin/kill -HUP $MAINPID -KillSignal=SIGTERM - -[Install] -WantedBy=multi-user.target - diff --git a/tools/build/basecoind/usr/share/basecoind/key.json b/tools/build/basecoind/usr/share/basecoind/key.json deleted file mode 100644 index bdefe8fd4..000000000 --- a/tools/build/basecoind/usr/share/basecoind/key.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "address": "1B1BE55F969F54064628A63B9559E7C21C925165", - "priv_key": { - "type": "ed25519", - "data": "C70D6934B4F55F1B7BC33B56B9CA8A2061384AFC19E91E44B40C4BBA182953D1619D3678599971ED29C7529DDD4DA537B97129893598A17C82E3AC9A8BA95279" - }, - "pub_key": { - "type": "ed25519", - "data": "619D3678599971ED29C7529DDD4DA537B97129893598A17C82E3AC9A8BA95279" - } -} - diff --git a/tools/build/basecoind/usr/share/basecoind/key2.json b/tools/build/basecoind/usr/share/basecoind/key2.json deleted file mode 100644 index ddfc6809b..000000000 --- a/tools/build/basecoind/usr/share/basecoind/key2.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "address": "1DA7C74F9C219229FD54CC9F7386D5A3839F0090", - "priv_key": { - "type": "ed25519", - "data": "34BAE9E65CE8245FAD035A0E3EED9401BDE8785FFB3199ACCF8F5B5DDF7486A8352195DA90CB0B90C24295B90AEBA25A5A71BC61BAB2FE2387241D439698B7B8" - }, - "pub_key": { - "type": "ed25519", - "data": "352195DA90CB0B90C24295B90AEBA25A5A71BC61BAB2FE2387241D439698B7B8" - } -} - diff --git a/tools/build/ethermint/DEBIAN/changelog b/tools/build/ethermint/DEBIAN/changelog deleted file mode 100644 index 76a1fb154..000000000 --- a/tools/build/ethermint/DEBIAN/changelog +++ /dev/null @@ -1,6 +0,0 @@ -ethermint (@VERSION@) @STABILITY@; urgency=medium - - * Automatic build. See https://github.com/tendermint/tendermint for more information. - - -- Greg Szabo @DATETIMESTAMP@ - diff --git a/tools/build/ethermint/DEBIAN/compat b/tools/build/ethermint/DEBIAN/compat deleted file mode 100644 index ec635144f..000000000 --- a/tools/build/ethermint/DEBIAN/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/tools/build/ethermint/DEBIAN/control b/tools/build/ethermint/DEBIAN/control deleted file mode 100644 index 2d8b3b002..000000000 --- a/tools/build/ethermint/DEBIAN/control +++ /dev/null @@ -1,15 +0,0 @@ -Source: ethermint -Section: net -Priority: optional -Maintainer: Greg Szabo -Build-Depends: debhelper (>=9) -Depends: tendermint (>=0.11.0) -Standards-Version: 3.9.6 -Homepage: https://tendermint.com -Package: ethermint -Architecture: amd64 -Version: @VERSION@ -Installed-Size: @INSTALLEDSIZE@ -Description: ethermint enables ethereum as an ABCI application on tendermint and the COSMOS hub - Ethermint enables ethereum to run as an ABCI application on tendermint and the COSMOS hub. This application allows you to get all the benefits of ethereum without having to run your own miners. - diff --git a/tools/build/ethermint/DEBIAN/copyright b/tools/build/ethermint/DEBIAN/copyright deleted file mode 100644 index 6d1bab01b..000000000 --- a/tools/build/ethermint/DEBIAN/copyright +++ /dev/null @@ -1,21 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: ethermint -Source: https://github.com/tendermint/ethermint - -Files: * -Copyright: 2017 All In Bits, Inc. -License: Apache-2.0 - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - . - http://www.apache.org/licenses/LICENSE-2.0 - . - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - . - On Debian systems, the full text of the Apache License 2.0 can be found - in the file `/usr/share/common-licenses/Apache-2.0'. diff --git a/tools/build/ethermint/DEBIAN/postinst b/tools/build/ethermint/DEBIAN/postinst deleted file mode 100644 index 439fdc395..000000000 --- a/tools/build/ethermint/DEBIAN/postinst +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh -# postinst script for ethermint -# - -set -e - -# summary of how this script can be called: -# * `configure' -# * `abort-upgrade' -# * `abort-remove' `in-favour' -# -# * `abort-remove' -# * `abort-deconfigure' `in-favour' -# `removing' -# -# for details, see https://www.debian.org/doc/debian-policy/ or -# the debian-policy package - - -case "$1" in - configure) - chown ethermint.ethermint /etc/ethermint - chown ethermint.ethermint /etc/ethermint/genesis.json - chown ethermint.ethermint /etc/ethermint/keystore - chown ethermint.ethermint /etc/ethermint/keystore/UTC--2016-10-21T22-30-03.071787745Z--7eff122b94897ea5b0e2a9abf47b86337fafebdc - - sudo -Hu ethermint /usr/bin/ethermint --datadir /etc/ethermint init /etc/ethermint/genesis.json - sudo -Hu ethermint tendermint init --home /etc/ethermint - systemctl daemon-reload - ;; - - abort-upgrade|abort-remove|abort-deconfigure) - ;; - - *) - echo "postinst called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff --git a/tools/build/ethermint/DEBIAN/postrm b/tools/build/ethermint/DEBIAN/postrm deleted file mode 100644 index f1d9d6afc..000000000 --- a/tools/build/ethermint/DEBIAN/postrm +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh -# postrm script for ethermint -# - -set -e - -# summary of how this script can be called: -# * `remove' -# * `purge' -# * `upgrade' -# * `failed-upgrade' -# * `abort-install' -# * `abort-install' -# * `abort-upgrade' -# * `disappear' -# -# for details, see https://www.debian.org/doc/debian-policy/ or -# the debian-policy package - - -case "$1" in - upgrade|failed-upgrade|abort-upgrade) - systemctl daemon-reload - ;; - - purge|remove|abort-install|disappear) - systemctl daemon-reload - ;; - - *) - echo "postrm called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff --git a/tools/build/ethermint/DEBIAN/preinst b/tools/build/ethermint/DEBIAN/preinst deleted file mode 100644 index 829112e6b..000000000 --- a/tools/build/ethermint/DEBIAN/preinst +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh -# preinst script for ethermint -# - -set -e - -# summary of how this script can be called: -# * `install' -# * `install' -# * `upgrade' -# * `abort-upgrade' -# for details, see https://www.debian.org/doc/debian-policy/ or -# the debian-policy package - - -case "$1" in - install|upgrade) - if ! grep -q '^ethermint:' /etc/passwd ; then - useradd -k /dev/null -r -m -b /etc ethermint - chmod 755 /etc/ethermint - fi - ;; - - abort-upgrade) - ;; - - *) - echo "preinst called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff --git a/tools/build/ethermint/DEBIAN/prerm b/tools/build/ethermint/DEBIAN/prerm deleted file mode 100644 index 00a775cef..000000000 --- a/tools/build/ethermint/DEBIAN/prerm +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh -# prerm script for ethermint -# - -set -e - -# summary of how this script can be called: -# * `remove' -# * `upgrade' -# * `failed-upgrade' -# * `remove' `in-favour' -# * `deconfigure' `in-favour' -# `removing' -# -# for details, see https://www.debian.org/doc/debian-policy/ or -# the debian-policy package - - -case "$1" in - remove|upgrade|deconfigure) - systemctl stop ethermint 2> /dev/null || : - ;; - - failed-upgrade) - ;; - - *) - echo "prerm called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff --git a/tools/build/ethermint/etc/systemd/system-preset/50-ethermint.preset b/tools/build/ethermint/etc/systemd/system-preset/50-ethermint.preset deleted file mode 100644 index 836a28c30..000000000 --- a/tools/build/ethermint/etc/systemd/system-preset/50-ethermint.preset +++ /dev/null @@ -1,2 +0,0 @@ -disable ethermint.service - diff --git a/tools/build/ethermint/etc/systemd/system/ethermint.service b/tools/build/ethermint/etc/systemd/system/ethermint.service deleted file mode 100644 index f71a074ea..000000000 --- a/tools/build/ethermint/etc/systemd/system/ethermint.service +++ /dev/null @@ -1,17 +0,0 @@ -[Unit] -Description=Ethermint -Requires=network-online.target -After=network-online.target - -[Service] -Restart=on-failure -User=ethermint -Group=ethermint -PermissionsStartOnly=true -ExecStart=/usr/bin/ethermint --datadir /etc/ethermint -ExecReload=/bin/kill -HUP $MAINPID -KillSignal=SIGTERM - -[Install] -WantedBy=multi-user.target - diff --git a/tools/build/gaia/DEBIAN/changelog b/tools/build/gaia/DEBIAN/changelog deleted file mode 100644 index eca5fbc3d..000000000 --- a/tools/build/gaia/DEBIAN/changelog +++ /dev/null @@ -1,6 +0,0 @@ -gaia (@VERSION@) @STABILITY@; urgency=medium - - * Automatic build. See https://github.com/tendermint/basecoin for more information. - - -- Greg Szabo @DATETIMESTAMP@ - diff --git a/tools/build/gaia/DEBIAN/compat b/tools/build/gaia/DEBIAN/compat deleted file mode 100644 index ec635144f..000000000 --- a/tools/build/gaia/DEBIAN/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/tools/build/gaia/DEBIAN/control b/tools/build/gaia/DEBIAN/control deleted file mode 100644 index 55d1cd5dd..000000000 --- a/tools/build/gaia/DEBIAN/control +++ /dev/null @@ -1,14 +0,0 @@ -Source: gaia -Section: net -Priority: optional -Maintainer: Greg Szabo -Build-Depends: debhelper (>=9) -Standards-Version: 3.9.6 -Homepage: https://cosmos.network -Package: gaia -Architecture: amd64 -Version: @VERSION@ -Installed-Size: @INSTALLEDSIZE@ -Description: gaia - Tendermint Cosmos delegation game chain - Gaia description comes later. - diff --git a/tools/build/gaia/DEBIAN/copyright b/tools/build/gaia/DEBIAN/copyright deleted file mode 100644 index ffc230134..000000000 --- a/tools/build/gaia/DEBIAN/copyright +++ /dev/null @@ -1,21 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: gaia -Source: https://github.com/cosmos/gaia - -Files: * -Copyright: 2017 All In Bits, Inc. -License: Apache-2.0 - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - . - http://www.apache.org/licenses/LICENSE-2.0 - . - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - . - On Debian systems, the full text of the Apache License 2.0 can be found - in the file `/usr/share/common-licenses/Apache-2.0'. diff --git a/tools/build/gaia/DEBIAN/postinst b/tools/build/gaia/DEBIAN/postinst deleted file mode 100644 index 427b7c493..000000000 --- a/tools/build/gaia/DEBIAN/postinst +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh -# postinst script for gaia -# - -set -e - -# summary of how this script can be called: -# * `configure' -# * `abort-upgrade' -# * `abort-remove' `in-favour' -# -# * `abort-remove' -# * `abort-deconfigure' `in-favour' -# `removing' -# -# for details, see https://www.debian.org/doc/debian-policy/ or -# the debian-policy package - - -case "$1" in - configure) - chown gaia.gaia /etc/gaia - sudo -Hu gaia gaia node init --home /etc/gaia 2B24DEE2364762300168DF19B6C18BCE2D399EA2 - systemctl daemon-reload - ;; - - abort-upgrade|abort-remove|abort-deconfigure) - ;; - - *) - echo "postinst called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff --git a/tools/build/gaia/DEBIAN/postrm b/tools/build/gaia/DEBIAN/postrm deleted file mode 100644 index da526ec30..000000000 --- a/tools/build/gaia/DEBIAN/postrm +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh -# postrm script for gaia -# - -set -e - -# summary of how this script can be called: -# * `remove' -# * `purge' -# * `upgrade' -# * `failed-upgrade' -# * `abort-install' -# * `abort-install' -# * `abort-upgrade' -# * `disappear' -# -# for details, see https://www.debian.org/doc/debian-policy/ or -# the debian-policy package - - -case "$1" in - upgrade|failed-upgrade|abort-upgrade) - systemctl daemon-reload - ;; - - purge|remove|abort-install|disappear) - systemctl daemon-reload - ;; - - *) - echo "postrm called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff --git a/tools/build/gaia/DEBIAN/preinst b/tools/build/gaia/DEBIAN/preinst deleted file mode 100644 index 382fa419f..000000000 --- a/tools/build/gaia/DEBIAN/preinst +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh -# preinst script for gaia -# - -set -e - -# summary of how this script can be called: -# * `install' -# * `install' -# * `upgrade' -# * `abort-upgrade' -# for details, see https://www.debian.org/doc/debian-policy/ or -# the debian-policy package - - -case "$1" in - install|upgrade) - if ! grep -q '^gaia:' /etc/passwd ; then - useradd -k /dev/null -r -m -b /etc gaia - chmod 755 /etc/gaia - fi - ;; - - abort-upgrade) - ;; - - *) - echo "preinst called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff --git a/tools/build/gaia/DEBIAN/prerm b/tools/build/gaia/DEBIAN/prerm deleted file mode 100644 index 165c1ab6a..000000000 --- a/tools/build/gaia/DEBIAN/prerm +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh -# prerm script for gaia -# - -set -e - -# summary of how this script can be called: -# * `remove' -# * `upgrade' -# * `failed-upgrade' -# * `remove' `in-favour' -# * `deconfigure' `in-favour' -# `removing' -# -# for details, see https://www.debian.org/doc/debian-policy/ or -# the debian-policy package - - -case "$1" in - remove|upgrade|deconfigure) - systemctl stop gaia 2> /dev/null || : - ;; - - failed-upgrade) - ;; - - *) - echo "prerm called with unknown argument \`$1'" >&2 - exit 1 - ;; -esac - -# dh_installdeb will replace this with shell code automatically -# generated by other debhelper scripts. - -#DEBHELPER# - -exit 0 diff --git a/tools/build/gaia/etc/systemd/system-preset/50-gaia.preset b/tools/build/gaia/etc/systemd/system-preset/50-gaia.preset deleted file mode 100644 index dfbf0bc06..000000000 --- a/tools/build/gaia/etc/systemd/system-preset/50-gaia.preset +++ /dev/null @@ -1,2 +0,0 @@ -disable gaia.service - diff --git a/tools/build/gaia/etc/systemd/system/gaia.service b/tools/build/gaia/etc/systemd/system/gaia.service deleted file mode 100644 index 372fe9343..000000000 --- a/tools/build/gaia/etc/systemd/system/gaia.service +++ /dev/null @@ -1,17 +0,0 @@ -[Unit] -Description=Gaia -Requires=network-online.target -After=network-online.target - -[Service] -Restart=on-failure -User=gaia -Group=gaia -PermissionsStartOnly=true -ExecStart=/usr/bin/gaia node start --home=/etc/gaia -ExecReload=/bin/kill -HUP $MAINPID -KillSignal=SIGTERM - -[Install] -WantedBy=multi-user.target - diff --git a/tools/build/gaia/usr/share/gaia/key.json b/tools/build/gaia/usr/share/gaia/key.json deleted file mode 100644 index bdefe8fd4..000000000 --- a/tools/build/gaia/usr/share/gaia/key.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "address": "1B1BE55F969F54064628A63B9559E7C21C925165", - "priv_key": { - "type": "ed25519", - "data": "C70D6934B4F55F1B7BC33B56B9CA8A2061384AFC19E91E44B40C4BBA182953D1619D3678599971ED29C7529DDD4DA537B97129893598A17C82E3AC9A8BA95279" - }, - "pub_key": { - "type": "ed25519", - "data": "619D3678599971ED29C7529DDD4DA537B97129893598A17C82E3AC9A8BA95279" - } -} - diff --git a/tools/build/gaia/usr/share/gaia/key2.json b/tools/build/gaia/usr/share/gaia/key2.json deleted file mode 100644 index ddfc6809b..000000000 --- a/tools/build/gaia/usr/share/gaia/key2.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "address": "1DA7C74F9C219229FD54CC9F7386D5A3839F0090", - "priv_key": { - "type": "ed25519", - "data": "34BAE9E65CE8245FAD035A0E3EED9401BDE8785FFB3199ACCF8F5B5DDF7486A8352195DA90CB0B90C24295B90AEBA25A5A71BC61BAB2FE2387241D439698B7B8" - }, - "pub_key": { - "type": "ed25519", - "data": "352195DA90CB0B90C24295B90AEBA25A5A71BC61BAB2FE2387241D439698B7B8" - } -} - diff --git a/tools/build/generate-spec b/tools/build/generate-spec deleted file mode 100755 index 4ca60a1d4..000000000 --- a/tools/build/generate-spec +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -if [ $# -ne 3 ]; then - echo "Usage: $0 " - exit 1 -fi - -app=$1 -src=$2 -dst=$3 - -# Find spectemplate -if [ ! -f "$src/$app.spec" ]; then - if [ ! -f "$src/app-template.spec" ]; then - echo "Source template not found." - exit 1 - else - srcfile="$src/app-template.spec" - fi -else - srcfile="$src/$app.spec" -fi - -# Copy spectemplate to SPECS -cp "$srcfile" "$dst/$app.spec" - -# Apply any variables defined in .data -if [ -f "$src/$app.data" ]; then - srcdata="$src/$app.data" - source "$srcdata" - for var in `grep -v -e ^# -e ^\s*$ "$srcdata" | grep = | sed 's/\s*=.*$//'` - do - sed -i "s\\@${var}@\\${!var}\\g" "$dst/$app.spec" - done -fi - diff --git a/tools/build/sign b/tools/build/sign deleted file mode 100755 index 0371b5d4b..000000000 --- a/tools/build/sign +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/expect -f -set timeout 3 -set PACKAGE [lindex $argv 0] -set GPG_NAME [lindex $argv 1] -set GPG_PATH [lindex $argv 2] -set GPG_PASSPHRASE $env(GPG_PASSPHRASE) - -if {[llength $argv] == 0} { - send_user "Usage: ./sign \n" - exit 1 -} - -send_user "\nSigning $PACKAGE\n" -spawn rpmsign --resign $PACKAGE --define "_signature gpg" --define "_gpg_name $GPG_NAME" --define "_gpgbin $GPG_PATH" -expect { - timeout { send_user "\nTimeout signing $PACKAGE\n"; exit 1 } - "Enter pass phrase:" -} -send "$GPG_PASSPHRASE\r" -expect { - timeout { send_user "\nTimeout signing $PACKAGE\n"; exit 1 } - "Pass phrase is good." -} -interact -sleep 3 - diff --git a/tools/build/spectemplates/app-template.spec b/tools/build/spectemplates/app-template.spec deleted file mode 100644 index 6cb8145bb..000000000 --- a/tools/build/spectemplates/app-template.spec +++ /dev/null @@ -1,55 +0,0 @@ -Version: @VERSION@ -Release: @BUILD_NUMBER@ - -%define __spec_install_post %{nil} -%define debug_package %{nil} -%define __os_install_post %{nil} - -Name: @PACKAGE_NAME@ -Summary: @PACKAGE_SUMMARY@ -License: Apache 2.0 -URL: @PACKAGE_URL@ -Packager: Greg Szabo -@PACKAGE_ADDITIONAL_HEADER@ - -%description -@PACKAGE_DESCRIPTION@ - -%pre -if ! %{__grep} -q '^%{name}:' /etc/passwd ; then - useradd -r -b %{_sysconfdir} %{name} - mkdir -p %{_sysconfdir}/%{name} - chmod 755 %{_sysconfdir}/%{name} - chown %{name}.%{name} %{_sysconfdir}/%{name} -fi - -%prep -# Nothing to do here. - It is done in the Makefile. - -%build -# Nothing to do here. - -%install -cd %{name}-%{version}-%{release} -%{__cp} -a * %{buildroot} - -%post -sudo -Hu %{name} %{name} node init --home %{_sysconfdir}/%{name} 2B24DEE2364762300168DF19B6C18BCE2D399EA2 -systemctl daemon-reload - -%preun -systemctl stop %{name} 2> /dev/null || : - -%postun -systemctl daemon-reload - -%files -%ghost %attr(0755, %{name}, %{name}) %dir %{_sysconfdir}/%{name} -%{_bindir}/* -%{_sysconfdir}/systemd/system/* -%{_sysconfdir}/systemd/system-preset/* -%dir %{_datadir}/%{name} -%{_datadir}/%{name}/* -%dir %{_defaultlicensedir}/%{name} -%doc %{_defaultlicensedir}/%{name}/LICENSE - diff --git a/tools/build/spectemplates/basecoind.data b/tools/build/spectemplates/basecoind.data deleted file mode 100644 index 36b172ecf..000000000 --- a/tools/build/spectemplates/basecoind.data +++ /dev/null @@ -1,5 +0,0 @@ -PACKAGE_SUMMARY="basecoind is a Proof-of-Stake cryptocurrency and framework" -PACKAGE_URL="https://cosmos.network/" -PACKAGE_ADDITIONAL_HEADER="Provides: basecoind" -PACKAGE_DESCRIPTION="Basecoind is an ABCI application designed to be used with the Tendermint consensus engine to form a Proof-of-Stake cryptocurrency. It also provides a general purpose framework for extending the feature-set of the cryptocurrency by implementing plugins." - diff --git a/tools/build/spectemplates/ethermint.data b/tools/build/spectemplates/ethermint.data deleted file mode 100644 index e9d403db7..000000000 --- a/tools/build/spectemplates/ethermint.data +++ /dev/null @@ -1,5 +0,0 @@ -PACKAGE_SUMMARY="ethermint enables ethereum as an ABCI application on tendermint and the COSMOS hub" -PACKAGE_URL="https://tendermint.com/" -PACKAGE_ADDITIONAL_HEADER="Provides: ethermint" -PACKAGE_DESCRIPTION="Ethermint enables ethereum to run as an ABCI application on tendermint and the COSMOS hub. This application allows you to get all the benefits of ethereum without having to run your own miners." - diff --git a/tools/build/spectemplates/ethermint.spec b/tools/build/spectemplates/ethermint.spec deleted file mode 100644 index fc443e35b..000000000 --- a/tools/build/spectemplates/ethermint.spec +++ /dev/null @@ -1,60 +0,0 @@ -Version: @VERSION@ -Release: @BUILD_NUMBER@ - -%define __spec_install_post %{nil} -%define debug_package %{nil} -%define __os_install_post %{nil} - -Name: @PACKAGE_NAME@ -Summary: @PACKAGE_SUMMARY@ -License: Apache 2.0 -URL: @PACKAGE_URL@ -Packager: Greg Szabo -Requires: tendermint >= 0.11.0 -@PACKAGE_ADDITIONAL_HEADER@ - -%description -@PACKAGE_DESCRIPTION@ - -%pre -if ! %{__grep} -q '^%{name}:' /etc/passwd ; then - useradd -r -b %{_sysconfdir} %{name} - mkdir -p %{_sysconfdir}/%{name} - chmod 755 %{_sysconfdir}/%{name} - chown %{name}.%{name} %{_sysconfdir}/%{name} -fi - -%prep -# Nothing to do here. - It is done in the Makefile. - -%build -# Nothing to do here. - -%install -cd %{name}-%{version}-%{release} -%{__cp} -a * %{buildroot} - -%post -sudo -Hu %{name} tendermint init --home %{_sysconfdir}/%{name} -sudo -Hu %{name} %{name} --datadir %{_sysconfdir}/%{name} init %{_sysconfdir}/%{name}/genesis.json - -systemctl daemon-reload - -%preun -systemctl stop %{name} 2> /dev/null || : -systemctl stop %{name}-service 2> /dev/null || : - -%postun -systemctl daemon-reload - -%files -%attr(0755, %{name}, %{name}) %dir %{_sysconfdir}/%{name} -%config(noreplace) %attr(0644, %{name}, %{name}) %{_sysconfdir}/%{name}/genesis.json -%attr(0755, %{name}, %{name}) %dir %{_sysconfdir}/%{name}/keystore -%attr(0644, %{name}, %{name}) %{_sysconfdir}/%{name}/keystore/* -%{_bindir}/* -%{_sysconfdir}/systemd/system/* -%{_sysconfdir}/systemd/system-preset/* -%dir %{_defaultlicensedir}/%{name} -%doc %{_defaultlicensedir}/%{name}/LICENSE - diff --git a/tools/build/spectemplates/gaia.data b/tools/build/spectemplates/gaia.data deleted file mode 100644 index 7152b1b51..000000000 --- a/tools/build/spectemplates/gaia.data +++ /dev/null @@ -1,5 +0,0 @@ -PACKAGE_SUMMARY="gaia - Tendermint Cosmos delegation game chain" -PACKAGE_URL="https://cosmos.network/" -PACKAGE_ADDITIONAL_HEADER="" -PACKAGE_DESCRIPTION="Gaia description comes later." - diff --git a/tools/build/spectemplates/tendermint.spec b/tools/build/spectemplates/tendermint.spec deleted file mode 100644 index 68902a170..000000000 --- a/tools/build/spectemplates/tendermint.spec +++ /dev/null @@ -1,31 +0,0 @@ -Version: @VERSION@ -Release: @BUILD_NUMBER@ - -%define __spec_install_post %{nil} -%define debug_package %{nil} -%define __os_install_post %{nil} - -Name: tendermint -Summary: securely and consistently replicate an application on many machines -License: Apache 2.0 -URL: https://tendermint.com/ -Packager: Greg Szabo - -%description -Tendermint is software for securely and consistently replicating an application on many machines. By securely, we mean that Tendermint works even if up to 1/3 of machines fail in arbitrary ways. By consistently, we mean that every non-faulty machine sees the same transaction log and computes the same state. - -%prep -# Nothing to do here. - It is done in the Makefile. - -%build -# Nothing to do here. - -%install -cd %{name}-%{version}-%{release} -%{__cp} -a * %{buildroot} - -%files -%{_bindir}/tendermint -%dir %{_defaultlicensedir}/%{name} -%doc %{_defaultlicensedir}/%{name}/LICENSE - diff --git a/tools/build/tendermint.repo b/tools/build/tendermint.repo deleted file mode 100644 index 439f98ecb..000000000 --- a/tools/build/tendermint.repo +++ /dev/null @@ -1,12 +0,0 @@ -#This is the .repo file for the Tendermint CentOS repositories. -#Although it has only been tested under CentOS 7, it should work under Fedora and RedHat 7 too. -#Currently only 64-bit packages are built. - -[tendermint] -name=Tendermint stable releases repository -baseurl=https://do9rmxapsag1v.cloudfront.net/centos/7/os/x86_64 -gpgcheck=1 -gpgkey=https://do9rmxapsag1v.cloudfront.net/centos/7/os/x86_64/RPM-GPG-KEY-Tendermint -enabled=1 -#sslverify = 1 - diff --git a/tools/build/tendermint/DEBIAN/changelog b/tools/build/tendermint/DEBIAN/changelog deleted file mode 100644 index 4b016f845..000000000 --- a/tools/build/tendermint/DEBIAN/changelog +++ /dev/null @@ -1,6 +0,0 @@ -tendermint (@VERSION@) @STABILITY@; urgency=medium - - * Automatic build. See https://github.com/tendermint/tendermint for more information. - - -- Greg Szabo @DATETIMESTAMP@ - diff --git a/tools/build/tendermint/DEBIAN/compat b/tools/build/tendermint/DEBIAN/compat deleted file mode 100644 index ec635144f..000000000 --- a/tools/build/tendermint/DEBIAN/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/tools/build/tendermint/DEBIAN/control b/tools/build/tendermint/DEBIAN/control deleted file mode 100644 index d9da17dd1..000000000 --- a/tools/build/tendermint/DEBIAN/control +++ /dev/null @@ -1,14 +0,0 @@ -Source: tendermint -Section: net -Priority: optional -Maintainer: Greg Szabo -Build-Depends: debhelper (>=9) -Standards-Version: 3.9.6 -Homepage: https://tendermint.com -Package: tendermint -Architecture: amd64 -Version: @VERSION@ -Installed-Size: @INSTALLEDSIZE@ -Description: securely and consistently replicate an application on many machines - Tendermint is software for securely and consistently replicating an application on many machines. By securely, we mean that Tendermint works even if up to 1/3 of machines fail in arbitrary ways. By consistently, we mean that every non-faulty machine sees the same transaction log and computes the same state. - diff --git a/tools/build/tendermint/DEBIAN/copyright b/tools/build/tendermint/DEBIAN/copyright deleted file mode 100644 index 15ee960dd..000000000 --- a/tools/build/tendermint/DEBIAN/copyright +++ /dev/null @@ -1,21 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: tendermint -Source: https://github.com/tendermint/tendermint - -Files: * -Copyright: 2017 All In Bits, Inc. -License: Apache-2.0 - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - . - http://www.apache.org/licenses/LICENSE-2.0 - . - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - . - On Debian systems, the full text of the Apache License 2.0 can be found - in the file `/usr/share/common-licenses/Apache-2.0'. From 033cac2b759a7dd133d0721789f3e969d9d140aa Mon Sep 17 00:00:00 2001 From: Denis Fadeev Date: Fri, 13 Mar 2020 19:34:31 +0500 Subject: [PATCH 52/77] Update docs website (#4564) * update theme * Update version * Updated Questions section in the footer * Remove links to Riot chat * Typo * Add Discord link * Update docs theme to the latest version * Use docs-staging branch for staging website * Resolve merge conflicts * Update version * Add google analytics Co-authored-by: Marko --- docs/.vuepress/styles/index.styl | 2 +- docs/package-lock.json | 1155 +++++++++++++++--------------- docs/package.json | 5 +- 3 files changed, 586 insertions(+), 576 deletions(-) diff --git a/docs/.vuepress/styles/index.styl b/docs/.vuepress/styles/index.styl index 0b40a6f9b..0ca835191 100644 --- a/docs/.vuepress/styles/index.styl +++ b/docs/.vuepress/styles/index.styl @@ -1,3 +1,3 @@ :root - --accent-color #00BB00 + --accent-color #018A01 --background #222222 \ No newline at end of file diff --git a/docs/package-lock.json b/docs/package-lock.json index a5e8c0880..fd136c226 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -12,18 +12,35 @@ "@babel/highlight": "^7.8.3" } }, + "@babel/compat-data": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.8.6.tgz", + "integrity": "sha512-CurCIKPTkS25Mb8mz267vU95vy+TyUpnctEX2lV33xWNmHAfjruztgiPBbXZRh3xZZy1CYvGx6XfxyTVS+sk7Q==", + "requires": { + "browserslist": "^4.8.5", + "invariant": "^2.2.4", + "semver": "^5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, "@babel/core": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.4.tgz", - "integrity": "sha512-0LiLrB2PwrVI+a2/IEskBopDYSd8BCb3rOvH7D5tzoWd696TBEduBvuLVm4Nx6rltrLZqvI3MCalB2K2aVzQjA==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.7.tgz", + "integrity": "sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==", "requires": { "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.8.4", + "@babel/generator": "^7.8.7", "@babel/helpers": "^7.8.4", - "@babel/parser": "^7.8.4", - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.8.4", - "@babel/types": "^7.8.3", + "@babel/parser": "^7.8.7", + "@babel/template": "^7.8.6", + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.7", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.1", @@ -68,11 +85,11 @@ } }, "@babel/generator": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.4.tgz", - "integrity": "sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.7.tgz", + "integrity": "sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew==", "requires": { - "@babel/types": "^7.8.3", + "@babel/types": "^7.8.7", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" @@ -103,33 +120,53 @@ } }, "@babel/helper-call-delegate": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.8.3.tgz", - "integrity": "sha512-6Q05px0Eb+N4/GTyKPPvnkig7Lylw+QzihMpws9iiZQv7ZImf84ZsZpQH7QoWN4n4tm81SnSzPgHw2qtO0Zf3A==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.8.7.tgz", + "integrity": "sha512-doAA5LAKhsFCR0LAFIf+r2RSMmC+m8f/oQ+URnUET/rWeEzC0yTRmAGyWkD4sSu3xwbS7MYQ2u+xlt1V5R56KQ==", "requires": { "@babel/helper-hoist-variables": "^7.8.3", "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/types": "^7.8.7" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.7.tgz", + "integrity": "sha512-4mWm8DCK2LugIS+p1yArqvG1Pf162upsIsjE7cNBjez+NjliQpVhj20obE520nao0o14DaTnFJv+Fw5a0JpoUw==", + "requires": { + "@babel/compat-data": "^7.8.6", + "browserslist": "^4.9.1", + "invariant": "^2.2.4", + "levenary": "^1.1.1", + "semver": "^5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } } }, "@babel/helper-create-class-features-plugin": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.8.3.tgz", - "integrity": "sha512-qmp4pD7zeTxsv0JNecSBsEmG1ei2MqwJq4YQcK3ZWm/0t07QstWfvuV/vm3Qt5xNMFETn2SZqpMx2MQzbtq+KA==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.8.6.tgz", + "integrity": "sha512-klTBDdsr+VFFqaDHm5rR69OpEQtO2Qv8ECxHS1mNhJJvaHArR6a1xTf5K/eZW7eZpJbhCx3NW1Yt/sKsLXLblg==", "requires": { "@babel/helper-function-name": "^7.8.3", "@babel/helper-member-expression-to-functions": "^7.8.3", "@babel/helper-optimise-call-expression": "^7.8.3", "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.6", "@babel/helper-split-export-declaration": "^7.8.3" } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.3.tgz", - "integrity": "sha512-Gcsm1OHCUr9o9TcJln57xhWHtdXbA2pgQ58S0Lxlks0WMGNXuki4+GLfX0p+L2ZkINUGZvfkz8rzoqJQSthI+Q==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.6.tgz", + "integrity": "sha512-bPyujWfsHhV/ztUkwGHz/RPV1T1TDEsSZDsN42JPehndA+p1KKTh3npvTadux0ZhCrytx9tvjpWNowKby3tM6A==", "requires": { + "@babel/helper-annotate-as-pure": "^7.8.3", "@babel/helper-regex": "^7.8.3", "regexpu-core": "^4.6.0" } @@ -196,15 +233,16 @@ } }, "@babel/helper-module-transforms": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.8.3.tgz", - "integrity": "sha512-C7NG6B7vfBa/pwCOshpMbOYUmrYQDfCpVL/JCRu0ek8B5p8kue1+BCXpg2vOYs7w5ACB9GTOBYQ5U6NwrMg+3Q==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.8.6.tgz", + "integrity": "sha512-RDnGJSR5EFBJjG3deY0NiL0K9TO8SXxS9n/MPsbPK/s9LbQymuLNtlzvDiNS7IpecuL45cMeLVkA+HfmlrnkRg==", "requires": { "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.6", "@babel/helper-simple-access": "^7.8.3", "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3", + "@babel/template": "^7.8.6", + "@babel/types": "^7.8.6", "lodash": "^4.17.13" } }, @@ -242,14 +280,14 @@ } }, "@babel/helper-replace-supers": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.3.tgz", - "integrity": "sha512-xOUssL6ho41U81etpLoT2RTdvdus4VfHamCuAm4AHxGr+0it5fnwoVdwUJ7GFEqCsQYzJUhcbsN9wB9apcYKFA==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz", + "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==", "requires": { "@babel/helper-member-expression-to-functions": "^7.8.3", "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/traverse": "^7.8.6", + "@babel/types": "^7.8.6" } }, "@babel/helper-simple-access": { @@ -301,9 +339,9 @@ } }, "@babel/parser": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz", - "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==" + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", + "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==" }, "@babel/plugin-proposal-async-generator-functions": { "version": "7.8.3", @@ -334,6 +372,15 @@ "@babel/plugin-syntax-decorators": "^7.8.3" } }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz", + "integrity": "sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-dynamic-import": "^7.8.0" + } + }, "@babel/plugin-proposal-json-strings": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz", @@ -343,6 +390,15 @@ "@babel/plugin-syntax-json-strings": "^7.8.0" } }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + } + }, "@babel/plugin-proposal-object-rest-spread": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.8.3.tgz", @@ -361,6 +417,15 @@ "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" } }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.8.3.tgz", + "integrity": "sha512-QIoIR9abkVn+seDE3OjA08jWcs3eZ9+wJCKSRgo3WdEU2csFYgdScb+8qHB3+WXsGJD55u+5hWCISI7ejXS+kg==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.0" + } + }, "@babel/plugin-proposal-unicode-property-regex": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.3.tgz", @@ -410,6 +475,14 @@ "@babel/helper-plugin-utils": "^7.8.3" } }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, "@babel/plugin-syntax-object-rest-spread": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", @@ -426,6 +499,22 @@ "@babel/helper-plugin-utils": "^7.8.0" } }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz", + "integrity": "sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, "@babel/plugin-transform-arrow-functions": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz", @@ -462,16 +551,16 @@ } }, "@babel/plugin-transform-classes": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.8.3.tgz", - "integrity": "sha512-SjT0cwFJ+7Rbr1vQsvphAHwUHvSUPmMjMU/0P59G8U2HLFqSa082JO7zkbDNWs9kH/IUqpHI6xWNesGf8haF1w==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.8.6.tgz", + "integrity": "sha512-k9r8qRay/R6v5aWZkrEclEhKO6mc1CCQr2dLsVHBmOQiMpN6I2bpjX3vgnldUWeEI1GHVNByULVxZ4BdP4Hmdg==", "requires": { "@babel/helper-annotate-as-pure": "^7.8.3", "@babel/helper-define-map": "^7.8.3", "@babel/helper-function-name": "^7.8.3", "@babel/helper-optimise-call-expression": "^7.8.3", "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.6", "@babel/helper-split-export-declaration": "^7.8.3", "globals": "^11.1.0" } @@ -519,9 +608,9 @@ } }, "@babel/plugin-transform-for-of": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.4.tgz", - "integrity": "sha512-iAXNlOWvcYUYoV8YIxwS7TxGRJcxyl8eQCfT+A5j8sKUzRFvJdcyjp97jL2IghWSRDaL2PU2O2tX8Cu9dTBq5A==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.6.tgz", + "integrity": "sha512-M0pw4/1/KI5WAxPsdcUL/w2LJ7o89YHN3yLkzNjg7Yl15GlVGgzHyCU+FMeAxevHGsLVmUqbirlUIKTafPmzdw==", "requires": { "@babel/helper-plugin-utils": "^7.8.3" } @@ -543,6 +632,14 @@ "@babel/helper-plugin-utils": "^7.8.3" } }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz", + "integrity": "sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, "@babel/plugin-transform-modules-amd": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.8.3.tgz", @@ -610,21 +707,37 @@ } }, "@babel/plugin-transform-parameters": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.4.tgz", - "integrity": "sha512-IsS3oTxeTsZlE5KqzTbcC2sV0P9pXdec53SU+Yxv7o/6dvGM5AkTotQKhoSffhNgZ/dftsSiOoxy7evCYJXzVA==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.7.tgz", + "integrity": "sha512-brYWaEPTRimOctz2NDA3jnBbDi7SVN2T4wYuu0aqSzxC3nozFZngGaw29CJ9ZPweB7k+iFmZuoG3IVPIcXmD2g==", "requires": { - "@babel/helper-call-delegate": "^7.8.3", + "@babel/helper-call-delegate": "^7.8.7", "@babel/helper-get-function-arity": "^7.8.3", "@babel/helper-plugin-utils": "^7.8.3" } }, - "@babel/plugin-transform-regenerator": { + "@babel/plugin-transform-property-literals": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.3.tgz", - "integrity": "sha512-qt/kcur/FxrQrzFR432FGZznkVAjiyFtCOANjkAKwCbt465L6ZCiUQh2oMYGU3Wo8LRFJxNDFwWn106S5wVUNA==", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz", + "integrity": "sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg==", "requires": { - "regenerator-transform": "^0.14.0" + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz", + "integrity": "sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA==", + "requires": { + "regenerator-transform": "^0.14.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz", + "integrity": "sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-runtime": { @@ -697,53 +810,67 @@ } }, "@babel/preset-env": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.3.4.tgz", - "integrity": "sha512-2mwqfYMK8weA0g0uBKOt4FE3iEodiHy9/CW0b+nWXcbL+pGzLx8ESYc+j9IIxr6LTDHWKgPm71i9smo02bw+gA==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.8.7.tgz", + "integrity": "sha512-BYftCVOdAYJk5ASsznKAUl53EMhfBbr8CJ1X+AJLfGPscQkwJFiaV/Wn9DPH/7fzm2v6iRYJKYHSqyynTGw0nw==", "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-async-generator-functions": "^7.2.0", - "@babel/plugin-proposal-json-strings": "^7.2.0", - "@babel/plugin-proposal-object-rest-spread": "^7.3.4", - "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.2.0", - "@babel/plugin-syntax-async-generators": "^7.2.0", - "@babel/plugin-syntax-json-strings": "^7.2.0", - "@babel/plugin-syntax-object-rest-spread": "^7.2.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.2.0", - "@babel/plugin-transform-arrow-functions": "^7.2.0", - "@babel/plugin-transform-async-to-generator": "^7.3.4", - "@babel/plugin-transform-block-scoped-functions": "^7.2.0", - "@babel/plugin-transform-block-scoping": "^7.3.4", - "@babel/plugin-transform-classes": "^7.3.4", - "@babel/plugin-transform-computed-properties": "^7.2.0", - "@babel/plugin-transform-destructuring": "^7.2.0", - "@babel/plugin-transform-dotall-regex": "^7.2.0", - "@babel/plugin-transform-duplicate-keys": "^7.2.0", - "@babel/plugin-transform-exponentiation-operator": "^7.2.0", - "@babel/plugin-transform-for-of": "^7.2.0", - "@babel/plugin-transform-function-name": "^7.2.0", - "@babel/plugin-transform-literals": "^7.2.0", - "@babel/plugin-transform-modules-amd": "^7.2.0", - "@babel/plugin-transform-modules-commonjs": "^7.2.0", - "@babel/plugin-transform-modules-systemjs": "^7.3.4", - "@babel/plugin-transform-modules-umd": "^7.2.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.3.0", - "@babel/plugin-transform-new-target": "^7.0.0", - "@babel/plugin-transform-object-super": "^7.2.0", - "@babel/plugin-transform-parameters": "^7.2.0", - "@babel/plugin-transform-regenerator": "^7.3.4", - "@babel/plugin-transform-shorthand-properties": "^7.2.0", - "@babel/plugin-transform-spread": "^7.2.0", - "@babel/plugin-transform-sticky-regex": "^7.2.0", - "@babel/plugin-transform-template-literals": "^7.2.0", - "@babel/plugin-transform-typeof-symbol": "^7.2.0", - "@babel/plugin-transform-unicode-regex": "^7.2.0", - "browserslist": "^4.3.4", + "@babel/compat-data": "^7.8.6", + "@babel/helper-compilation-targets": "^7.8.7", + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-proposal-async-generator-functions": "^7.8.3", + "@babel/plugin-proposal-dynamic-import": "^7.8.3", + "@babel/plugin-proposal-json-strings": "^7.8.3", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-proposal-object-rest-spread": "^7.8.3", + "@babel/plugin-proposal-optional-catch-binding": "^7.8.3", + "@babel/plugin-proposal-optional-chaining": "^7.8.3", + "@babel/plugin-proposal-unicode-property-regex": "^7.8.3", + "@babel/plugin-syntax-async-generators": "^7.8.0", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.0", + "@babel/plugin-syntax-top-level-await": "^7.8.3", + "@babel/plugin-transform-arrow-functions": "^7.8.3", + "@babel/plugin-transform-async-to-generator": "^7.8.3", + "@babel/plugin-transform-block-scoped-functions": "^7.8.3", + "@babel/plugin-transform-block-scoping": "^7.8.3", + "@babel/plugin-transform-classes": "^7.8.6", + "@babel/plugin-transform-computed-properties": "^7.8.3", + "@babel/plugin-transform-destructuring": "^7.8.3", + "@babel/plugin-transform-dotall-regex": "^7.8.3", + "@babel/plugin-transform-duplicate-keys": "^7.8.3", + "@babel/plugin-transform-exponentiation-operator": "^7.8.3", + "@babel/plugin-transform-for-of": "^7.8.6", + "@babel/plugin-transform-function-name": "^7.8.3", + "@babel/plugin-transform-literals": "^7.8.3", + "@babel/plugin-transform-member-expression-literals": "^7.8.3", + "@babel/plugin-transform-modules-amd": "^7.8.3", + "@babel/plugin-transform-modules-commonjs": "^7.8.3", + "@babel/plugin-transform-modules-systemjs": "^7.8.3", + "@babel/plugin-transform-modules-umd": "^7.8.3", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3", + "@babel/plugin-transform-new-target": "^7.8.3", + "@babel/plugin-transform-object-super": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.8.7", + "@babel/plugin-transform-property-literals": "^7.8.3", + "@babel/plugin-transform-regenerator": "^7.8.7", + "@babel/plugin-transform-reserved-words": "^7.8.3", + "@babel/plugin-transform-shorthand-properties": "^7.8.3", + "@babel/plugin-transform-spread": "^7.8.3", + "@babel/plugin-transform-sticky-regex": "^7.8.3", + "@babel/plugin-transform-template-literals": "^7.8.3", + "@babel/plugin-transform-typeof-symbol": "^7.8.4", + "@babel/plugin-transform-unicode-regex": "^7.8.3", + "@babel/types": "^7.8.7", + "browserslist": "^4.8.5", + "core-js-compat": "^3.6.2", "invariant": "^2.2.2", - "js-levenshtein": "^1.1.3", - "semver": "^5.3.0" + "levenary": "^1.1.1", + "semver": "^5.5.0" }, "dependencies": { "semver": { @@ -754,57 +881,41 @@ } }, "@babel/runtime": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.4.tgz", - "integrity": "sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.7.tgz", + "integrity": "sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg==", "requires": { - "regenerator-runtime": "^0.13.2" + "regenerator-runtime": "^0.13.4" }, "dependencies": { "regenerator-runtime": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" - } - } - }, - "@babel/runtime-corejs2": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.8.4.tgz", - "integrity": "sha512-7jU2FgNqNHX6yTuU/Dr/vH5/O8eVL9U85MG5aDw1LzGfCvvhXC1shdXfVzCQDsoY967yrAKeLujRv7l8BU+dZA==", - "requires": { - "core-js": "^2.6.5", - "regenerator-runtime": "^0.13.2" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", + "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==" } } }, "@babel/template": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", - "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", "requires": { "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" } }, "@babel/traverse": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.4.tgz", - "integrity": "sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==", + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", + "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", "requires": { "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.8.4", + "@babel/generator": "^7.8.6", "@babel/helper-function-name": "^7.8.3", "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/parser": "^7.8.4", - "@babel/types": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" @@ -826,9 +937,9 @@ } }, "@babel/types": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", - "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "version": "7.8.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", + "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", "requires": { "esutils": "^2.0.2", "lodash": "^4.17.13", @@ -843,10 +954,13 @@ } }, "@cosmos-ui/vue": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/@cosmos-ui/vue/-/vue-0.5.21.tgz", - "integrity": "sha512-Y60AMxFKgHrgE/EHxnGKaTcYUN1nJa5m3SylhsCe/d0AvzF9RSYGSPwVgDxmW4KiufBKXkv4PmiNG9WDNWwdxw==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@cosmos-ui/vue/-/vue-0.10.0.tgz", + "integrity": "sha512-dQySi+cjICuEl7OnFHMTY1ZKiVQ/dkZ22oiVFQRzfrhRLYa3HgEgM22EMBSzmhGVg6J52yS18OWpIruftWIW1Q==", "requires": { + "clipboard-copy": "^3.1.0", + "js-base64": "^2.5.2", + "prismjs": "^1.19.0", "tiny-cookie": "^2.3.1", "vue": "^2.6.10" } @@ -917,9 +1031,9 @@ "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" }, "@types/node": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.0.tgz", - "integrity": "sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ==" + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.0.tgz", + "integrity": "sha512-0ARSQootUG1RljH2HncpsY2TJBfGQIKOOi7kxzUY6z54ePu/ZD+wJA8zI2Q6v8rol2qpG/rvqsReco8zNMPvhQ==" }, "@types/q": { "version": "1.5.2", @@ -945,23 +1059,31 @@ } }, "@vue/babel-preset-app": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/@vue/babel-preset-app/-/babel-preset-app-3.12.1.tgz", - "integrity": "sha512-Zjy5jQaikV1Pz+ri0YgXFS7q4/5wCxB5tRkDOEIt5+4105u0Feb/pvH20nVL6nx9GyXrECFfcm7Yxr/z++OaPQ==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@vue/babel-preset-app/-/babel-preset-app-4.2.3.tgz", + "integrity": "sha512-Xlc8d9Ebgu9pNZMUxKZWVP2CctVZzfX3LAxjBDWAAIiVpdXX4IkQQCevDhgiANFzlmE3KXtiSgPGs57Sso2g7Q==", "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/plugin-proposal-class-properties": "^7.0.0", - "@babel/plugin-proposal-decorators": "^7.1.0", - "@babel/plugin-syntax-dynamic-import": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.0.0", - "@babel/plugin-transform-runtime": "^7.4.0", - "@babel/preset-env": "^7.0.0 < 7.4.0", - "@babel/runtime": "^7.0.0", - "@babel/runtime-corejs2": "^7.2.0", - "@vue/babel-preset-jsx": "^1.0.0", - "babel-plugin-dynamic-import-node": "^2.2.0", - "babel-plugin-module-resolver": "3.2.0", - "core-js": "^2.6.5" + "@babel/core": "^7.8.4", + "@babel/helper-compilation-targets": "^7.8.4", + "@babel/helper-module-imports": "^7.8.3", + "@babel/plugin-proposal-class-properties": "^7.8.3", + "@babel/plugin-proposal-decorators": "^7.8.3", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-jsx": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.8.3", + "@babel/preset-env": "^7.8.4", + "@babel/runtime": "^7.8.4", + "@vue/babel-preset-jsx": "^1.1.2", + "babel-plugin-dynamic-import-node": "^2.3.0", + "core-js": "^3.6.4", + "core-js-compat": "^3.6.4" + }, + "dependencies": { + "core-js": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz", + "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==" + } } }, "@vue/babel-preset-jsx": { @@ -1063,23 +1185,24 @@ } }, "@vuepress/core": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@vuepress/core/-/core-1.3.0.tgz", - "integrity": "sha512-/KaH10ggZeEnwh/i8A02VtGHfuIfTEf/pIPV9BBVjK5M6ToPhF2pkcXlPk5PbCWam2dKm7ZDQddJzev1dY5TNA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vuepress/core/-/core-1.3.1.tgz", + "integrity": "sha512-BBtM3imJUPwCTz0Fzl++ZLgf1afcsas4jo/wbVvroIdI0R6GEbXdivnisVGD48tZ10WcwvY94tlL1jWO8xV6bg==", "requires": { - "@babel/core": "^7.0.0", - "@vue/babel-preset-app": "^3.1.1", - "@vuepress/markdown": "^1.3.0", - "@vuepress/markdown-loader": "^1.3.0", - "@vuepress/plugin-last-updated": "^1.3.0", - "@vuepress/plugin-register-components": "^1.3.0", - "@vuepress/shared-utils": "^1.3.0", + "@babel/core": "^7.8.4", + "@vue/babel-preset-app": "^4.1.2", + "@vuepress/markdown": "^1.3.1", + "@vuepress/markdown-loader": "^1.3.1", + "@vuepress/plugin-last-updated": "^1.3.1", + "@vuepress/plugin-register-components": "^1.3.1", + "@vuepress/shared-utils": "^1.3.1", "autoprefixer": "^9.5.1", "babel-loader": "^8.0.4", "cache-loader": "^3.0.0", "chokidar": "^2.0.3", "connect-history-api-fallback": "^1.5.0", "copy-webpack-plugin": "^5.0.2", + "core-js": "^3.6.4", "cross-spawn": "^6.0.5", "css-loader": "^2.1.1", "file-loader": "^3.0.1", @@ -1104,14 +1227,21 @@ "webpack-dev-server": "^3.5.1", "webpack-merge": "^4.1.2", "webpackbar": "3.2.0" + }, + "dependencies": { + "core-js": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz", + "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==" + } } }, "@vuepress/markdown": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@vuepress/markdown/-/markdown-1.3.0.tgz", - "integrity": "sha512-h4FCAxcYLSGuoftbumsesqquRuQksb98sygiP/EV1J7z3qVj8r/1YdRRoUoE0Yd9hw0izN52KJRYZC7tlUmBnw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vuepress/markdown/-/markdown-1.3.1.tgz", + "integrity": "sha512-UJoGHR9GsFnPk+Jot8tieO4M6WJQ5CkdIWlQfbpC1+Z0ETJjlNIel23BKLNzqfo3NhLq+/i33RnzMVzkBKlVvQ==", "requires": { - "@vuepress/shared-utils": "^1.3.0", + "@vuepress/shared-utils": "^1.3.1", "markdown-it": "^8.4.1", "markdown-it-anchor": "^5.0.2", "markdown-it-chain": "^1.3.0", @@ -1140,56 +1270,61 @@ } }, "@vuepress/markdown-loader": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@vuepress/markdown-loader/-/markdown-loader-1.3.0.tgz", - "integrity": "sha512-20J9+wuyCxhwOWfb7aDY0F/+j2oQYaoDE1VbH3zaqI9XesPl42DsEwA1Nw1asEm3yXdh+uC2scBCiNcv94tsHg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vuepress/markdown-loader/-/markdown-loader-1.3.1.tgz", + "integrity": "sha512-JxjQgSClW51hE0bCrcAqnG0yrvVURzcZwP2zbWkcCMD7vomHbvkHyPmuf6oa8Jk4S//RQUYINrzC/KrDjVuzIQ==", "requires": { - "@vuepress/markdown": "^1.3.0", + "@vuepress/markdown": "^1.3.1", "loader-utils": "^1.1.0", "lru-cache": "^5.1.1" } }, "@vuepress/plugin-active-header-links": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-active-header-links/-/plugin-active-header-links-1.3.0.tgz", - "integrity": "sha512-C+EhZefAOxN83jVZebRWqFUBUklTsTtWRiDFczxcxqH995ZZumi1UFKj9TurOjrZppUDr4ftfxIqGkj4QSUeWw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-active-header-links/-/plugin-active-header-links-1.3.1.tgz", + "integrity": "sha512-mrawXXAv2K1GrD1JNoFHxF8xX3KiphVcwvf+58GXpsyAQ5ag5X1BZG3gCA1JdNFUe3SXRh5jF6HTBuM2dc6Ovg==", "requires": { "lodash.debounce": "^4.0.8" } }, + "@vuepress/plugin-google-analytics": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-google-analytics/-/plugin-google-analytics-1.3.1.tgz", + "integrity": "sha512-Xb7g86JT/LD1sLYG2txvpp4ztDTOqN5yNRZK5OtzEekCh0NWxIxz0fEYKqVlaskIFnzoA1dfizudyjGCKY+hMw==" + }, "@vuepress/plugin-last-updated": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-last-updated/-/plugin-last-updated-1.3.0.tgz", - "integrity": "sha512-zCg98YiCFzBo7hHh5CE4H7lO13QaexeNXKC8SC7aNopjhg1/+rzFKEWt5frARnYqhMrkhEqcegSuB4xWxNV+zQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-last-updated/-/plugin-last-updated-1.3.1.tgz", + "integrity": "sha512-n1EhhFcaWxQtbC9ICyLg8kmSULjV18wYMbHCyaKRYAvyhlPau95zbSpQfG2Nl3ZgFR6kRodK6AmZUOgho0zh/g==", "requires": { "cross-spawn": "^6.0.5" } }, "@vuepress/plugin-nprogress": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-nprogress/-/plugin-nprogress-1.3.0.tgz", - "integrity": "sha512-PuBDAhaYLvwG63LamIc1fMk+s4kUqPuvNYKfZjQlF3LtXjlCMvd6YEQyogfB9cZnFOg1nryeHJwWoAdFvzw29Q==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-nprogress/-/plugin-nprogress-1.3.1.tgz", + "integrity": "sha512-vDBnIhTgGZbADwhaatSLsFnuj+MDDpCWQ79m9o+8RtMZO2HemedcCRNIj/ZLRJSBFjXrDdnXF5lpW4EEIeRaew==", "requires": { "nprogress": "^0.2.0" } }, "@vuepress/plugin-register-components": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-register-components/-/plugin-register-components-1.3.0.tgz", - "integrity": "sha512-IkBacuTDHSHhI3qWXPQtVWTEAL+wprrbaYrD+g2n9xV3dzMkhHJxbpRpw7eAbvsP85a03rVouwRukZ+YlhYPPQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-register-components/-/plugin-register-components-1.3.1.tgz", + "integrity": "sha512-ae/94omRTPZkJKuVic8Rvzfnu2NtqsyVPYTL6qcnjDgxieR3L7EAYLNEvYpg1jof+QTHoEDCaVU2c63chZcfEQ==", "requires": { - "@vuepress/shared-utils": "^1.3.0" + "@vuepress/shared-utils": "^1.3.1" } }, "@vuepress/plugin-search": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-search/-/plugin-search-1.3.0.tgz", - "integrity": "sha512-buoQ6gQ2MLbLQ7Nhg5KJWPzKo7NtvdK/e6Fo1ig/kbOG5HyYKHCyqLjbQ/ZqT+fGbaSeEjH3DaVYTNx55GRX5A==" + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-search/-/plugin-search-1.3.1.tgz", + "integrity": "sha512-iOIvMWUTPHrGxjDprFoGTcuI8Y8/6e6JjLO4mO6qe6qVqR1yCQ8cJzVYXIizjEHUFYJ04uZ3jF9gBV8npS+3ZQ==" }, "@vuepress/shared-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@vuepress/shared-utils/-/shared-utils-1.3.0.tgz", - "integrity": "sha512-n1AFgt8SiMDdc5aIj5yOqS3E6+dAZ+9tPw6qf1mBiqvdZzwaUtlydvXqVkskrwUo18znLrUr55VYwubMOaxFnQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vuepress/shared-utils/-/shared-utils-1.3.1.tgz", + "integrity": "sha512-MlIAlnptjDC9+l0SJKW6BpkuwtxfKDzq4Rmag75RdyIqkkNv4EsCXZ8Y3HSuzENWFBwoD31jLC+nCZ3hULcvSg==", "requires": { "chalk": "^2.3.2", "diacritics": "^1.3.0", @@ -1203,13 +1338,13 @@ } }, "@vuepress/theme-default": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@vuepress/theme-default/-/theme-default-1.3.0.tgz", - "integrity": "sha512-0KKTIQQAyO3xE9Gn5vdQYWY+B1onzMm2i3Td610FiLsCRqeHsWs/stl6tlP3nV75OUHwBRH/w0ITrIF4kMR7GQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vuepress/theme-default/-/theme-default-1.3.1.tgz", + "integrity": "sha512-CihkB6/+5vfgeTI5HRDs4+QgTkIN4/K54OpQCGLW51OinXuz4rjMVQW2uSlSqSeKEr+MERHa+Jc5deIpA0opoA==", "requires": { - "@vuepress/plugin-active-header-links": "^1.3.0", - "@vuepress/plugin-nprogress": "^1.3.0", - "@vuepress/plugin-search": "^1.3.0", + "@vuepress/plugin-active-header-links": "^1.3.1", + "@vuepress/plugin-nprogress": "^1.3.1", + "@vuepress/plugin-search": "^1.3.1", "docsearch.js": "^2.5.2", "lodash": "^4.17.15", "stylus": "^0.54.5", @@ -1426,9 +1561,9 @@ "integrity": "sha1-xdG9SxKQCPEWPyNvhuX66iAm4u8=" }, "ajv": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", - "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", + "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1557,11 +1692,11 @@ "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==" }, "ansi-escapes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", - "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", "requires": { - "type-fest": "^0.8.1" + "type-fest": "^0.11.0" } }, "ansi-html": { @@ -1796,18 +1931,6 @@ "object.assign": "^4.1.0" } }, - "babel-plugin-module-resolver": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-module-resolver/-/babel-plugin-module-resolver-3.2.0.tgz", - "integrity": "sha512-tjR0GvSndzPew/Iayf4uICWZqjBwnlMWjSx6brryfQ81F9rxBVqwDJtFCV8oOs0+vJeefK9TmdZtkIFdFe1UnA==", - "requires": { - "find-babel-config": "^1.1.0", - "glob": "^7.1.2", - "pkg-up": "^2.0.0", - "reselect": "^3.0.1", - "resolve": "^1.4.0" - } - }, "babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", @@ -2081,6 +2204,11 @@ "requires": { "has-flag": "^4.0.0" } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" } } }, @@ -2181,13 +2309,13 @@ } }, "browserslist": { - "version": "4.8.6", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.6.tgz", - "integrity": "sha512-ZHao85gf0eZ0ESxLfCp73GG9O/VTytYDIkIiZDlURppLTI9wErSM/5yAKEq6rcUdxBLjMELmrYUJGg5sxGKMHg==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.9.1.tgz", + "integrity": "sha512-Q0DnKq20End3raFulq6Vfp1ecB9fh8yUNV55s8sekaDDeqBaCtWlRHCUdaWyUeSSBJM7IbM6HcsyaeYqgeDhnw==", "requires": { - "caniuse-lite": "^1.0.30001023", - "electron-to-chromium": "^1.3.341", - "node-releases": "^1.1.47" + "caniuse-lite": "^1.0.30001030", + "electron-to-chromium": "^1.3.363", + "node-releases": "^1.1.50" } }, "buffer": { @@ -2231,9 +2359,9 @@ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" }, "cac": { - "version": "6.5.6", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.5.6.tgz", - "integrity": "sha512-8jsGLeBiYEVYTDExaj/rDPG4tyra4yjjacIL10TQ+MobPcg9/IST+dkKLu6sOzq0GcIC6fQqX1nkH9HoskQLAw==" + "version": "6.5.7", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.5.7.tgz", + "integrity": "sha512-DjjOqLvoX/oO/snovTNm553kRYWTmIIQBfHQ2UqktbCudoHJuxzvRhjwdCHkXrQwp/lnu3bYyZ+LfaHtwk0Wjw==" }, "cacache": { "version": "12.0.3", @@ -2372,9 +2500,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001027", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001027.tgz", - "integrity": "sha512-7xvKeErvXZFtUItTHgNtLgS9RJpVnwBlWX8jSo/BO8VsF6deszemZSkJJJA1KOKrXuzZH4WALpAJdq5EyfgMLg==" + "version": "1.0.30001033", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001033.tgz", + "integrity": "sha512-8Ibzxee6ibc5q88cM1usPsMpJOG5CTq0s/dKOmlekPbDGKt+UrnOOTPSjQz3kVo6yL7N4SB5xd+FGLHQmbzh6A==" }, "caseless": { "version": "0.12.0", @@ -2448,9 +2576,9 @@ } }, "chownr": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", - "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, "chrome-trace-event": { "version": "1.0.2", @@ -2509,9 +2637,9 @@ "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==" }, "clipboard": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz", - "integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.6.tgz", + "integrity": "sha512-g5zbiixBRk/wyKakSwCKd7vQXDjFnAMGHoEyBogG/bw9kTD9GvdAvaoRR1ALcEzt3pVKxZR0pViekPMIS0QyGg==", "optional": true, "requires": { "good-listener": "^1.2.2", @@ -2703,11 +2831,11 @@ } }, "configstore": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.0.tgz", - "integrity": "sha512-eE/hvMs7qw7DlcB5JPRnthmrITuHMmACUJAp89v6PT6iOqzoLS7HRWhBtuHMlhNHo2AhUSA/3Dh1bKNJHcublQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", "requires": { - "dot-prop": "^5.1.0", + "dot-prop": "^5.2.0", "graceful-fs": "^4.1.2", "make-dir": "^3.0.0", "unique-string": "^2.0.0", @@ -2715,23 +2843,10 @@ "xdg-basedir": "^4.0.0" }, "dependencies": { - "dot-prop": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", - "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", - "requires": { - "is-obj": "^2.0.0" - } - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" - }, "make-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz", - "integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.2.tgz", + "integrity": "sha512-rYKABKutXa6vXTXhoV18cBE7PaewPXHe/Bdq4v+ZLMhxbWApkFFplT0LcbMW+6BbjnQXzZ/sAvSE/JdguApG5w==", "requires": { "semver": "^6.0.0" } @@ -2877,19 +2992,6 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==" }, - "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -2907,6 +3009,22 @@ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" }, + "core-js-compat": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.4.tgz", + "integrity": "sha512-zAa3IZPvsJ0slViBQ2z+vgyyTuhd3MFn1rBQjZSKVEgB0UMYhUkCj9jJUVPgGTGqWvsBVmfnruXgTcNyTlEiSA==", + "requires": { + "browserslist": "^4.8.3", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" + } + } + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -3087,11 +3205,6 @@ "source-map": "^0.6.1" } }, - "css-unit-converter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.1.tgz", - "integrity": "sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY=" - }, "css-what": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", @@ -3498,11 +3611,11 @@ } }, "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", + "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", "requires": { - "is-obj": "^1.0.0" + "is-obj": "^2.0.0" } }, "duplexer3": { @@ -3565,9 +3678,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.3.346", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.346.tgz", - "integrity": "sha512-Yy4jF5hJd57BWmGPt0KjaXc25AmWZeQK75kdr4zIzksWVtiT6DwaNtvTb9dt+LkQKwUpvBfCyyPsXXtbY/5GYw==" + "version": "1.3.375", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.375.tgz", + "integrity": "sha512-zmaFnYVBtfpF8bGRYxgPeVAlXB7N3On8rjBE2ROc6wOpTPpzRWaiHo6KkbJMvlH07CH33uks/TEb6kuMMn8q6A==" }, "elliptic": { "version": "6.5.2", @@ -3589,9 +3702,9 @@ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" }, "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" }, "encodeurl": { "version": "1.0.2", @@ -3722,6 +3835,11 @@ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -4005,9 +4123,9 @@ "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==" }, "figures": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz", - "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "requires": { "escape-string-regexp": "^1.0.5" } @@ -4062,22 +4180,6 @@ } } }, - "find-babel-config": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/find-babel-config/-/find-babel-config-1.2.0.tgz", - "integrity": "sha512-jB2CHJeqy6a820ssiqwrKMeyC6nNdmrcgkKWJWmpoxpE8RKciYJXCcXRq1h2AzCo5I5BJeN2tkGEO3hLTuePRA==", - "requires": { - "json5": "^0.5.1", - "path-exists": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" - } - } - }, "find-cache-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", @@ -4089,11 +4191,11 @@ } }, "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "requires": { - "locate-path": "^2.0.0" + "locate-path": "^3.0.0" } }, "flush-write-stream": { @@ -4774,9 +4876,9 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "fuse.js": { - "version": "3.4.6", - "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.4.6.tgz", - "integrity": "sha512-H6aJY4UpLFwxj1+5nAvufom5b2BT2v45P1MkPvdGIK8fWjQx/7o6tTT1+ALV0yawQvbmvCF0ufl2et8eJ7v7Cg==" + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.6.1.tgz", + "integrity": "sha512-hT9yh/tiinkmirKrlv4KWOjztdoZo1mx9Qh4KvWqC7isoXwdUY3PNWUxceF4/qO9R6riA2C29jdTOeQOIROjgw==" }, "gensync": { "version": "1.0.0-beta.1", @@ -5187,9 +5289,9 @@ } }, "http-cache-semantics": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz", - "integrity": "sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew==" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" }, "http-deceiver": { "version": "1.2.7", @@ -5403,9 +5505,9 @@ "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" }, "ipaddr.js": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", - "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, "is-absolute-url": { "version": "2.1.0", @@ -5610,9 +5712,9 @@ } }, "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" }, "is-path-cwd": { "version": "2.2.0", @@ -5732,10 +5834,10 @@ "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-1.6.0.tgz", "integrity": "sha1-FC0RHzpuPa6PSpr9d9RYVbWpzOM=" }, - "js-levenshtein": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", - "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==" + "js-base64": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz", + "integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ==" }, "js-stringify": { "version": "1.0.2", @@ -5880,6 +5982,19 @@ "invert-kv": "^2.0.0" } }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" + }, + "levenary": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", + "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", + "requires": { + "leven": "^3.1.0" + } + }, "linkify-it": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", @@ -5899,21 +6014,21 @@ "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==" }, "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", "requires": { "big.js": "^5.2.2", - "emojis-list": "^2.0.0", + "emojis-list": "^3.0.0", "json5": "^1.0.1" } }, "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "requires": { - "p-locate": "^2.0.0", + "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, @@ -5985,9 +6100,9 @@ "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" }, "loglevel": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.6.tgz", - "integrity": "sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ==" + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.7.tgz", + "integrity": "sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A==" }, "longest": { "version": "1.0.1", @@ -6328,9 +6443,9 @@ } }, "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.4.tgz", + "integrity": "sha512-wTiNDqe4D2rbTJGZk1qcdZgFtY0/r+iuE6GDT7V0/+Gu5MLpIDm4+CssDECR79OJs/OxLPXMzdxy153b5Qy3hg==" }, "mississippi": { "version": "3.0.0", @@ -6553,9 +6668,9 @@ } }, "node-releases": { - "version": "1.1.48", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.48.tgz", - "integrity": "sha512-Hr8BbmUl1ujAST0K0snItzEA5zkJTQup8VNTKNfT6Zw8vTJkIiagUPNfxHmgDOyfFYNfKAul40sD0UEYTvwebw==", + "version": "1.1.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.51.tgz", + "integrity": "sha512-1eQEs6HFYY1kMXQPOLzCf7HdjReErmvn85tZESMczdCNVWP3Y7URYLBAyYynuI7yef1zj4HN5q+oB2x67QU0lw==", "requires": { "semver": "^6.3.0" } @@ -6811,19 +6926,19 @@ "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==" }, "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", + "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", "requires": { - "p-try": "^1.0.0" + "p-try": "^2.0.0" } }, "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "requires": { - "p-limit": "^1.1.0" + "p-limit": "^2.0.0" } }, "p-map": { @@ -6840,9 +6955,9 @@ } }, "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, "package-json": { "version": "6.5.0", @@ -7043,54 +7158,6 @@ "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", "requires": { "find-up": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - } - } - }, - "pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", - "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", - "requires": { - "find-up": "^2.1.0" } }, "portfinder": { @@ -7124,9 +7191,9 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" }, "postcss": { - "version": "7.0.26", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.26.tgz", - "integrity": "sha512-IY4oRjpXWYshuTDFxMVkJDtWIk2LhsTlu8bZnbEJA4+bYT16Lvpo8Qv6EvDumhYRgzjZl489pmsY3qVgJQ08nA==", + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz", + "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==", "requires": { "chalk": "^2.4.2", "source-map": "^0.6.1", @@ -7144,36 +7211,13 @@ } }, "postcss-calc": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.1.tgz", - "integrity": "sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.2.tgz", + "integrity": "sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ==", "requires": { - "css-unit-converter": "^1.1.1", - "postcss": "^7.0.5", - "postcss-selector-parser": "^5.0.0-rc.4", - "postcss-value-parser": "^3.3.1" - }, - "dependencies": { - "cssesc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", - "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" - }, - "postcss-selector-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", - "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", - "requires": { - "cssesc": "^2.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" - } - }, - "postcss-value-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", - "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" - } + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" } }, "postcss-colormin": { @@ -7295,11 +7339,11 @@ }, "dependencies": { "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", "requires": { - "dot-prop": "^4.1.1", + "dot-prop": "^5.2.0", "indexes-of": "^1.0.1", "uniq": "^1.0.1" } @@ -7372,11 +7416,11 @@ }, "dependencies": { "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", "requires": { - "dot-prop": "^4.1.1", + "dot-prop": "^5.2.0", "indexes-of": "^1.0.1", "uniq": "^1.0.1" } @@ -7670,9 +7714,9 @@ } }, "postcss-value-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz", - "integrity": "sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==" + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz", + "integrity": "sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==" }, "prepend-http": { "version": "2.0.0", @@ -7735,12 +7779,12 @@ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" }, "proxy-addr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", - "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", "requires": { "forwarded": "~0.1.2", - "ipaddr.js": "1.9.0" + "ipaddr.js": "1.9.1" } }, "prr": { @@ -7928,6 +7972,14 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, + "pupa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz", + "integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==", + "requires": { + "escape-goat": "^2.0.0" + } + }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -8015,9 +8067,9 @@ } }, "readable-stream": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.5.0.tgz", - "integrity": "sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -8077,9 +8129,9 @@ "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==" }, "regenerate-unicode-properties": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", - "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", "requires": { "regenerate": "^1.4.0" } @@ -8090,11 +8142,12 @@ "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" }, "regenerator-transform": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.1.tgz", - "integrity": "sha512-flVuee02C3FKRISbxhXl9mGzdbWUVHubl1SMaknjxkFB1/iqpJhArQUvRxOOPEc/9tAiX0BaQ28FJH10E4isSQ==", + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.2.tgz", + "integrity": "sha512-V4+lGplCM/ikqi5/mkkpJ06e9Bujq1NFmNLvsCs56zg3ZbzrnUzAtizZ24TXxtRX/W2jcdScwQCnbL0CICTFkQ==", "requires": { - "private": "^0.1.6" + "@babel/runtime": "^7.8.4", + "private": "^0.1.8" } }, "regex-not": { @@ -8135,16 +8188,16 @@ } }, "regexpu-core": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", - "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", + "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", "requires": { "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.1.0", - "regjsgen": "^0.5.0", - "regjsparser": "^0.6.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.1.0" + "unicode-match-property-value-ecmascript": "^1.2.0" } }, "registry-auth-token": { @@ -8169,9 +8222,9 @@ "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==" }, "regjsparser": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.2.tgz", - "integrity": "sha512-E9ghzUtoLwDekPT0DYCp+c4h+bvuUpe6rRHCTYn6eGoqj1LgKXxT6I0Il4WbjhQkOghzi/V+y03bPKvbllL93Q==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", + "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", "requires": { "jsesc": "~0.5.0" }, @@ -8216,9 +8269,9 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -8227,7 +8280,7 @@ "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~2.3.2", - "har-validator": "~5.1.0", + "har-validator": "~5.1.3", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", @@ -8237,7 +8290,7 @@ "performance-now": "^2.1.0", "qs": "~6.5.2", "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", + "tough-cookie": "~2.5.0", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" }, @@ -8264,11 +8317,6 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" }, - "reselect": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-3.0.1.tgz", - "integrity": "sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc=" - }, "resolve": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", @@ -9153,11 +9201,11 @@ }, "dependencies": { "postcss-selector-parser": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz", - "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", "requires": { - "dot-prop": "^4.1.1", + "dot-prop": "^5.2.0", "indexes-of": "^1.0.1", "uniq": "^1.0.1" } @@ -9267,9 +9315,9 @@ "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==" }, "terser": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.3.tgz", - "integrity": "sha512-Lw+ieAXmY69d09IIc/yqeBqXpEQIpDGZqT34ui1QWXIUpR2RjbqEkT8X7Lgex19hslSqcWM5iMN2kM11eMsESQ==", + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.6.tgz", + "integrity": "sha512-4lYPyeNmstjIIESr/ysHg2vUPRGf2tzF9z2yYwnowXVuVzLEamPN1Gfrz7f8I9uEPuHcbFlW4PLIAsJoxXyJ1g==", "requires": { "commander": "^2.20.0", "source-map": "~0.6.1", @@ -9474,19 +9522,12 @@ "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=" }, "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - } + "psl": "^1.1.28", + "punycode": "^2.1.1" } }, "tr46": { @@ -9498,9 +9539,9 @@ } }, "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==" }, "tty-browserify": { "version": "0.0.0", @@ -9521,9 +9562,9 @@ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==" }, "type-is": { "version": "1.6.18", @@ -9590,14 +9631,14 @@ } }, "unicode-match-property-value-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", - "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==" }, "unicode-property-aliases-ecmascript": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz", - "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==" }, "union-value": { "version": "1.0.1", @@ -9701,13 +9742,13 @@ "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" }, "update-notifier": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.0.0.tgz", - "integrity": "sha512-p9zf71hWt5GVXM4iEBujpUgx8mK9AWiCCapEJm/O1z5ntCim83Z1ATqzZFBHFYqx03laMqv8LiDgs/7ikXjf/g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.0.tgz", + "integrity": "sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew==", "requires": { "boxen": "^4.2.0", "chalk": "^3.0.0", - "configstore": "^5.0.0", + "configstore": "^5.0.1", "has-yarn": "^2.1.0", "import-lazy": "^2.1.0", "is-ci": "^2.0.0", @@ -9715,6 +9756,7 @@ "is-npm": "^4.0.0", "is-yarn-global": "^0.3.0", "latest-version": "^5.0.0", + "pupa": "^2.0.1", "semver-diff": "^3.1.1", "xdg-basedir": "^4.0.0" }, @@ -9923,9 +9965,9 @@ "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==" }, "vue-loader": { - "version": "15.8.3", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.8.3.tgz", - "integrity": "sha512-yFksTFbhp+lxlm92DrKdpVIWMpranXnTEuGSc0oW+Gk43M9LWaAmBTnfj5+FCdve715mTHvo78IdaXf5TbiTJg==", + "version": "15.9.0", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.0.tgz", + "integrity": "sha512-FeDHvTSpwyLeF7LIV1PYkvqUQgTJ8UmOxhSlCyRSxaXCKk+M6NF4tDQsLsPPNeDPyR7TfRQ8MLg6v+8PsDV9xQ==", "requires": { "@vue/component-compiler-utils": "^3.1.0", "hash-sum": "^1.0.2", @@ -9935,9 +9977,9 @@ } }, "vue-router": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.1.5.tgz", - "integrity": "sha512-BszkPvhl7I9h334GjckCh7sVFyjTPMMJFJ4Bsrem/Ik+B/9gt5tgrk8k4gGLO4ZpdvciVdg7O41gW4DisQWurg==" + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.1.6.tgz", + "integrity": "sha512-GYhn2ynaZlysZMkFE5oCHRUTqE8BWs/a9YbKpNLi0i7xD6KG1EzDqpHQmv1F5gXjr8kL5iIVS8EOtRaVUEXTqA==" }, "vue-server-renderer": { "version": "2.6.11", @@ -10007,13 +10049,13 @@ "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==" }, "vuepress": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/vuepress/-/vuepress-1.3.0.tgz", - "integrity": "sha512-TmPmHiT70aq4xqy4XczUJmUdpGlMSheOGGVwA2nhYSIS9IEd4ngPbfT9oEcAFTsGHXsr5KH8EgEU7G+3wWzY/A==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/vuepress/-/vuepress-1.3.1.tgz", + "integrity": "sha512-i0f0JB0zdmdVH8P8cO4w7PljPQpf8ObiVk/1pOidvMQCMEhFmIpYz+730Wlf0rtB/GG4QUsqQ27Ckp5Rfob+hQ==", "requires": { - "@vuepress/core": "^1.3.0", - "@vuepress/theme-default": "^1.3.0", - "cac": "^6.5.5", + "@vuepress/core": "^1.3.1", + "@vuepress/theme-default": "^1.3.1", + "cac": "^6.5.6", "envinfo": "^7.2.0", "opencollective-postinstall": "^2.0.2", "update-notifier": "^4.0.0" @@ -10038,6 +10080,11 @@ "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==" }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + }, "json5": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", @@ -10090,11 +10137,11 @@ } }, "vuepress-theme-cosmos": { - "version": "1.0.150", - "resolved": "https://registry.npmjs.org/vuepress-theme-cosmos/-/vuepress-theme-cosmos-1.0.150.tgz", - "integrity": "sha512-f4McVndkB+CqJ6mWpOG4UZSR14LJyXqwcgwoDoDUx149g2PKU3qI/AF5AcrM25+4UKMCXFKcJloQCl/aWq+1ig==", + "version": "1.0.156", + "resolved": "https://registry.npmjs.org/vuepress-theme-cosmos/-/vuepress-theme-cosmos-1.0.156.tgz", + "integrity": "sha512-g5KqkeRu8Dg+eYUwWYy4TfdMKp9yYy+BQpD+gegwqIPbHSBIvFV+0w3olk5/mFaCzXKfEmTXtLltok9arrayLg==", "requires": { - "@cosmos-ui/vue": "^0.5.20", + "@cosmos-ui/vue": "^0.10.0", "axios": "^0.19.0", "cheerio": "^1.0.0-rc.3", "clipboard-copy": "^3.1.0", @@ -10138,9 +10185,9 @@ "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" }, "webpack": { - "version": "4.41.5", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.5.tgz", - "integrity": "sha512-wp0Co4vpyumnp3KlkmpM5LWuzvZYayDwM2n17EHFr4qxBBbRokC7DJawPJC7TfSFZ9HZ6GsdH40EBj4UV0nmpw==", + "version": "4.42.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.0.tgz", + "integrity": "sha512-EzJRHvwQyBiYrYqhyjW9AqM90dE4+s1/XtCfn7uWg6cS72zH+2VPFAlsnW0+W0cDi0XRjNKUMoJtpSi50+Ph6w==", "requires": { "@webassemblyjs/ast": "1.8.5", "@webassemblyjs/helper-module-context": "1.8.5", @@ -10168,9 +10215,9 @@ }, "dependencies": { "acorn": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", - "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==" + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==" } } }, @@ -10275,54 +10322,16 @@ "ms": "^2.1.1" } }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, "is-absolute-url": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==" }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, "supports-color": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", @@ -10576,9 +10585,9 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write-file-atomic": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.1.tgz", - "integrity": "sha512-JPStrIyyVJ6oCSz/691fAjFtefZ6q+fP6tm+OS4Qw6o+TGQxNp1ziY2PgS+X/m0V8OWhZiO/m4xSj+Pr4RrZvw==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "requires": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", diff --git a/docs/package.json b/docs/package.json index 8ce869057..dfef8c2de 100644 --- a/docs/package.json +++ b/docs/package.json @@ -4,7 +4,8 @@ "description": "Welcome to the Tendermint Core documentation!", "main": "index.js", "dependencies": { - "vuepress-theme-cosmos": "^1.0.150" + "@vuepress/plugin-google-analytics": "^1.3.1", + "vuepress-theme-cosmos": "^1.0.156" }, "scripts": { "preserve": "./pre.sh", @@ -16,4 +17,4 @@ }, "author": "", "license": "ISC" -} \ No newline at end of file +} From fdc2efc083fd1397af62a40ae93c078a551d38a5 Mon Sep 17 00:00:00 2001 From: Marko Date: Fri, 13 Mar 2020 17:21:09 +0100 Subject: [PATCH 53/77] fix: fix spelling of comment (#4566) - :P Signed-off-by: Marko Baricevic --- types/evidence.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/evidence.go b/types/evidence.go index 199a01c70..303deeca9 100644 --- a/types/evidence.go +++ b/types/evidence.go @@ -59,7 +59,7 @@ type Evidence interface { Height() int64 // height of the equivocation Time() time.Time // time of the equivocation Address() []byte // address of the equivocating validator - Bytes() []byte // bytes which compromise the evidence + Bytes() []byte // bytes which comprise the evidence Hash() []byte // hash of the evidence Verify(chainID string, pubKey crypto.PubKey) error // verify the evidence Equal(Evidence) bool // check equality of evidence From 629dff0f0d5cb4aa738c2cb34aea1f036d5ea4e8 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2020 12:29:20 +0000 Subject: [PATCH 54/77] build(deps): bump github.com/prometheus/client_golang (#4574) Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.5.0 to 1.5.1. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/master/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.5.0...v1.5.1) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 11a150579..29a317e30 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/libp2p/go-buffer-pool v0.0.2 github.com/magiconair/properties v1.8.1 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.5.0 + github.com/prometheus/client_golang v1.5.1 github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563 github.com/rs/cors v1.7.0 github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa diff --git a/go.sum b/go.sum index aa9ec1eff..aaaf4928e 100644 --- a/go.sum +++ b/go.sum @@ -326,6 +326,8 @@ github.com/prometheus/client_golang v1.3.0 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0 github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.5.0 h1:Ctq0iGpCmr3jeP77kbF2UxgvRwzWWz+4Bh9/vJTyg1A= github.com/prometheus/client_golang v1.5.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA= +github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= From c917c2ddfb825f8e5eeb1dce36cd486afd19d433 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 16 Mar 2020 22:02:54 +0400 Subject: [PATCH 55/77] rpc: fix panic when `Subscribe` is called (#4570) but HTTP client is not running. `Subscribe`, `Unsubscribe(All)` methods return an error now. Closes #4568 --- CHANGELOG_PENDING.md | 3 ++ rpc/client/event_test.go | 20 ++++++++++ rpc/client/examples_test.go | 31 +++++++------- rpc/client/httpclient.go | 80 +++++++++++++++++++++++++++++-------- 4 files changed, 104 insertions(+), 30 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 07b4541a2..07bf9674b 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -22,3 +22,6 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi - [privval] \#4534 Add `error` as a return value on`GetPubKey()` ### BUG FIXES: + +- [rpc] \#4568 Fix panic when `Subscribe` is called, but HTTP client is not running (@melekes) + `Subscribe`, `Unsubscribe(All)` methods return an error now. diff --git a/rpc/client/event_test.go b/rpc/client/event_test.go index 5c9d902fd..a25b6ebb2 100644 --- a/rpc/client/event_test.go +++ b/rpc/client/event_test.go @@ -1,11 +1,13 @@ package client_test import ( + "context" "fmt" "reflect" "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" @@ -135,3 +137,21 @@ func testTxEventsSent(t *testing.T, broadcastMethod string) { func TestClientsResubscribe(t *testing.T) { // TODO(melekes) } + +func TestHTTPReturnsErrorIfClientIsNotRunning(t *testing.T) { + c := getHTTPClient() + + // on Subscribe + _, err := c.Subscribe(context.Background(), "TestHeaderEvents", + types.QueryForEvent(types.EventNewBlockHeader).String()) + assert.Error(t, err) + + // on Unsubscribe + err = c.Unsubscribe(context.Background(), "TestHeaderEvents", + types.QueryForEvent(types.EventNewBlockHeader).String()) + assert.Error(t, err) + + // on UnsubscribeAll + err = c.UnsubscribeAll(context.Background(), "TestHeaderEvents") + assert.Error(t, err) +} diff --git a/rpc/client/examples_test.go b/rpc/client/examples_test.go index a543de70d..bb4583506 100644 --- a/rpc/client/examples_test.go +++ b/rpc/client/examples_test.go @@ -3,6 +3,7 @@ package client_test import ( "bytes" "fmt" + "log" "github.com/tendermint/tendermint/abci/example/kvstore" "github.com/tendermint/tendermint/rpc/client" @@ -20,7 +21,7 @@ func ExampleHTTP_simple() { rpcAddr := rpctest.GetConfig().RPC.ListenAddress c, err := client.NewHTTP(rpcAddr, "/websocket") if err != nil { - panic(err) + log.Fatal(err) } // Create a transaction @@ -29,28 +30,28 @@ func ExampleHTTP_simple() { tx := append(k, append([]byte("="), v...)...) // Broadcast the transaction and wait for it to commit (rather use - // c.BroadcastTxSync though in production) + // c.BroadcastTxSync though in production). bres, err := c.BroadcastTxCommit(tx) if err != nil { - panic(err) + log.Fatal(err) } if bres.CheckTx.IsErr() || bres.DeliverTx.IsErr() { - panic("BroadcastTxCommit transaction failed") + log.Fatal("BroadcastTxCommit transaction failed") } // Now try to fetch the value for the key qres, err := c.ABCIQuery("/key", k) if err != nil { - panic(err) + log.Fatal(err) } if qres.Response.IsErr() { - panic("ABCIQuery failed") + log.Fatal("ABCIQuery failed") } if !bytes.Equal(qres.Response.Key, k) { - panic("returned key does not match queried key") + log.Fatal("returned key does not match queried key") } if !bytes.Equal(qres.Response.Value, v) { - panic("returned value does not match sent value") + log.Fatal("returned value does not match sent value") } fmt.Println("Sent tx :", string(tx)) @@ -73,7 +74,7 @@ func ExampleHTTP_batching() { rpcAddr := rpctest.GetConfig().RPC.ListenAddress c, err := client.NewHTTP(rpcAddr, "/websocket") if err != nil { - panic(err) + log.Fatal(err) } // Create our two transactions @@ -92,28 +93,30 @@ func ExampleHTTP_batching() { // Queue up our transactions for _, tx := range txs { + // Broadcast the transaction and wait for it to commit (rather use + // c.BroadcastTxSync though in production). if _, err := batch.BroadcastTxCommit(tx); err != nil { - panic(err) + log.Fatal(err) } } // Send the batch of 2 transactions if _, err := batch.Send(); err != nil { - panic(err) + log.Fatal(err) } // Now let's query for the original results as a batch keys := [][]byte{k1, k2} for _, key := range keys { if _, err := batch.ABCIQuery("/key", key); err != nil { - panic(err) + log.Fatal(err) } } // Send the 2 queries and keep the results results, err := batch.Send() if err != nil { - panic(err) + log.Fatal(err) } // Each result in the returned list is the deserialized result of each @@ -121,7 +124,7 @@ func ExampleHTTP_batching() { for _, result := range results { qr, ok := result.(*ctypes.ResultABCIQuery) if !ok { - panic("invalid result type from ABCIQuery request") + log.Fatal("invalid result type from ABCIQuery request") } fmt.Println(string(qr.Response.Key), "=", string(qr.Response.Value)) } diff --git a/rpc/client/httpclient.go b/rpc/client/httpclient.go index 98875c91e..fd8783eba 100644 --- a/rpc/client/httpclient.go +++ b/rpc/client/httpclient.go @@ -37,6 +37,27 @@ indefinitely until successful. Request batching is available for JSON RPC requests over HTTP, which conforms to the JSON RPC specification (https://www.jsonrpc.org/specification#batch). See the example for more details. + +Example: + + c, err := NewHTTP("http://192.168.1.10:26657", "/websocket") + if err != nil { + // handle error + } + + // call Start/Stop if you're subscribing to events + err = c.Start() + if err != nil { + // handle error + } + defer c.Stop() + + res, err := c.Status() + if err != nil { + // handle error + } + + // handle result */ type HTTP struct { remote string @@ -121,11 +142,16 @@ func NewHTTPWithClient(remote, wsEndpoint string, client *http.Client) (*HTTP, e ctypes.RegisterAmino(cdc) rc.SetCodec(cdc) + wsEvents, err := newWSEvents(cdc, remote, wsEndpoint) + if err != nil { + return nil, err + } + httpClient := &HTTP{ rpc: rc, remote: remote, baseRPCClient: &baseRPCClient{caller: rc}, - WSEvents: newWSEvents(cdc, remote, wsEndpoint), + WSEvents: wsEvents, } return httpClient, nil @@ -406,6 +432,9 @@ func (c *baseRPCClient) BroadcastEvidence(ev types.Evidence) (*ctypes.ResultBroa //----------------------------------------------------------------------------- // WSEvents +var errNotRunning = errors.New("client is not running. Use .Start() method to start") + +// WSEvents is a wrapper around WSClient, which implements EventsClient. type WSEvents struct { service.BaseService cdc *amino.Codec @@ -413,41 +442,41 @@ type WSEvents struct { endpoint string ws *rpcclient.WSClient - mtx sync.RWMutex - // query -> chan - subscriptions map[string]chan ctypes.ResultEvent + mtx sync.RWMutex + subscriptions map[string]chan ctypes.ResultEvent // query -> chan } -func newWSEvents(cdc *amino.Codec, remote, endpoint string) *WSEvents { - wsEvents := &WSEvents{ +func newWSEvents(cdc *amino.Codec, remote, endpoint string) (*WSEvents, error) { + w := &WSEvents{ cdc: cdc, endpoint: endpoint, remote: remote, subscriptions: make(map[string]chan ctypes.ResultEvent), } + w.BaseService = *service.NewBaseService(nil, "WSEvents", w) - wsEvents.BaseService = *service.NewBaseService(nil, "WSEvents", wsEvents) - return wsEvents -} - -// OnStart implements service.Service by starting WSClient and event loop. -func (w *WSEvents) OnStart() (err error) { + var err error w.ws, err = rpcclient.NewWSClient(w.remote, w.endpoint, rpcclient.OnReconnect(func() { // resubscribe immediately w.redoSubscriptionsAfter(0 * time.Second) })) if err != nil { - return err + return nil, err } w.ws.SetCodec(w.cdc) w.ws.SetLogger(w.Logger) - err = w.ws.Start() - if err != nil { + return w, nil +} + +// OnStart implements service.Service by starting WSClient and event loop. +func (w *WSEvents) OnStart() error { + if err := w.ws.Start(); err != nil { return err } go w.eventListener() + return nil } @@ -459,10 +488,17 @@ func (w *WSEvents) OnStop() { // Subscribe implements EventsClient by using WSClient to subscribe given // subscriber to query. By default, returns a channel with cap=1. Error is // returned if it fails to subscribe. -// Channel is never closed to prevent clients from seeing an erroneus event. +// +// Channel is never closed to prevent clients from seeing an erroneous event. +// +// It returns an error if WSEvents is not running. func (w *WSEvents) Subscribe(ctx context.Context, subscriber, query string, outCapacity ...int) (out <-chan ctypes.ResultEvent, err error) { + if !w.IsRunning() { + return nil, errNotRunning + } + if err := w.ws.Subscribe(ctx, query); err != nil { return nil, err } @@ -484,7 +520,13 @@ func (w *WSEvents) Subscribe(ctx context.Context, subscriber, query string, // Unsubscribe implements EventsClient by using WSClient to unsubscribe given // subscriber from query. +// +// It returns an error if WSEvents is not running. func (w *WSEvents) Unsubscribe(ctx context.Context, subscriber, query string) error { + if !w.IsRunning() { + return errNotRunning + } + if err := w.ws.Unsubscribe(ctx, query); err != nil { return err } @@ -501,7 +543,13 @@ func (w *WSEvents) Unsubscribe(ctx context.Context, subscriber, query string) er // UnsubscribeAll implements EventsClient by using WSClient to unsubscribe // given subscriber from all the queries. +// +// It returns an error if WSEvents is not running. func (w *WSEvents) UnsubscribeAll(ctx context.Context, subscriber string) error { + if !w.IsRunning() { + return errNotRunning + } + if err := w.ws.UnsubscribeAll(ctx); err != nil { return err } From c05b2f23849dfc5affd41ea9eb6f64b5c07ad997 Mon Sep 17 00:00:00 2001 From: Greg Szabo <16846635+greg-szabo@users.noreply.github.com> Date: Tue, 17 Mar 2020 06:31:55 -0400 Subject: [PATCH 56/77] Dockerfile updated with defaults (#4577) --- CHANGELOG_PENDING.md | 1 + DOCKER/Dockerfile | 25 ++++++++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 07bf9674b..d0446d314 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -20,6 +20,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi - [p2p] [\#4548](https://github.com/tendermint/tendermint/pull/4548) Add ban list to address book (@cmwaters) - [privval] \#4534 Add `error` as a return value on`GetPubKey()` +- [Docker] \#4569 Default configuration added to docker image (you can still mount your own config the same way) (@greg-szabo) ### BUG FIXES: diff --git a/DOCKER/Dockerfile b/DOCKER/Dockerfile index c3c186e05..49fc72fb5 100644 --- a/DOCKER/Dockerfile +++ b/DOCKER/Dockerfile @@ -22,17 +22,32 @@ RUN apk update && \ # Run the container with tmuser by default. (UID=100, GID=1000) USER tmuser -# Expose the data directory as a volume since there's mutable state in there -VOLUME [ $TMHOME ] - WORKDIR $TMHOME # p2p and rpc port -EXPOSE 26656 26657 +EXPOSE 26656 26657 26660 ENTRYPOINT ["/usr/bin/tendermint"] -CMD ["node", "--moniker=`hostname`"] +CMD ["node"] STOPSIGNAL SIGTERM ARG BINARY=tendermint COPY $BINARY /usr/bin/tendermint + +# Create default configuration for docker run. +RUN /usr/bin/tendermint init && \ + sed -i \ + -e 's/^proxy_app\s*=.*/proxy_app = "kvstore"/' \ + -e 's/^moniker\s*=.*/moniker = "dockernode"/' \ + -e 's/^addr_book_strict\s*=.*/addr_book_strict = false/' \ + -e 's/^timeout_commit\s*=.*/timeout_commit = "500ms"/' \ + -e 's/^index_all_tags\s*=.*/index_all_tags = true/' \ + -e 's/^prometheus\s*=.*/prometheus = true/' \ + $TMHOME/config/config.toml && \ + sed -i \ + -e 's/^\s*"chain_id":.*/ "chain_id": "dockerchain",/' \ + $TMHOME/config/genesis.json + +# Expose the data directory as a volume since there's mutable state in there +VOLUME [ $TMHOME ] + From a0294fe5b9d06bbc8f2e79caae83ea74ec12e0f6 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 20 Mar 2020 11:44:30 +0000 Subject: [PATCH 57/77] build(deps): bump github.com/gorilla/websocket from 1.4.1 to 1.4.2 (#4584) Bumps [github.com/gorilla/websocket](https://github.com/gorilla/websocket) from 1.4.1 to 1.4.2. - [Release notes](https://github.com/gorilla/websocket/releases) - [Commits](https://github.com/gorilla/websocket/compare/v1.4.1...v1.4.2) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 29a317e30..1889d4d03 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/go-logfmt/logfmt v0.5.0 github.com/gogo/protobuf v1.3.1 github.com/golang/protobuf v1.3.5 - github.com/gorilla/websocket v1.4.1 + github.com/gorilla/websocket v1.4.2 github.com/gtank/merlin v0.1.1 github.com/libp2p/go-buffer-pool v0.0.2 github.com/magiconair/properties v1.8.1 diff --git a/go.sum b/go.sum index aaaf4928e..0d6078aba 100644 --- a/go.sum +++ b/go.sum @@ -175,6 +175,8 @@ github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= From a116dca9905694e560528870965c111e9f68b062 Mon Sep 17 00:00:00 2001 From: Marko Date: Sat, 21 Mar 2020 10:57:18 +0100 Subject: [PATCH 58/77] deps: run go mod tidy (#4587) Signed-off-by: Marko Baricevic --- go.sum | 6 ------ 1 file changed, 6 deletions(-) diff --git a/go.sum b/go.sum index 0d6078aba..df25917db 100644 --- a/go.sum +++ b/go.sum @@ -144,8 +144,6 @@ github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -173,8 +171,6 @@ github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -326,8 +322,6 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0XyUVEcDsc= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= -github.com/prometheus/client_golang v1.5.0 h1:Ctq0iGpCmr3jeP77kbF2UxgvRwzWWz+4Bh9/vJTyg1A= -github.com/prometheus/client_golang v1.5.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA= github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= From d9c34315d2b0670e2ce3d47784c4c0adcae4d2f2 Mon Sep 17 00:00:00 2001 From: Greg Szabo <16846635+greg-szabo@users.noreply.github.com> Date: Sat, 21 Mar 2020 09:37:49 -0400 Subject: [PATCH 59/77] Docker image port fix (#4589) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- DOCKER/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DOCKER/Dockerfile b/DOCKER/Dockerfile index 49fc72fb5..e3a918749 100644 --- a/DOCKER/Dockerfile +++ b/DOCKER/Dockerfile @@ -24,7 +24,7 @@ USER tmuser WORKDIR $TMHOME -# p2p and rpc port +# p2p, rpc and prometheus port EXPOSE 26656 26657 26660 ENTRYPOINT ["/usr/bin/tendermint"] @@ -42,6 +42,7 @@ RUN /usr/bin/tendermint init && \ -e 's/^addr_book_strict\s*=.*/addr_book_strict = false/' \ -e 's/^timeout_commit\s*=.*/timeout_commit = "500ms"/' \ -e 's/^index_all_tags\s*=.*/index_all_tags = true/' \ + -e 's,^laddr = "tcp://127.0.0.1:26657",laddr = "tcp://0.0.0.0:26657",' \ -e 's/^prometheus\s*=.*/prometheus = true/' \ $TMHOME/config/config.toml && \ sed -i \ From 044f1bf288192cd49a400511189178270f02d48a Mon Sep 17 00:00:00 2001 From: Marko Date: Mon, 23 Mar 2020 09:19:26 +0100 Subject: [PATCH 60/77] format: add format cmd & goimport repo (#4586) * format: add format cmd & goimport repo - replaced format command - added goimports to format command - ran goimports Signed-off-by: Marko Baricevic * fix outliers & undo proto file changes --- Makefile | 7 ++++--- abci/example/kvstore/kvstore.go | 3 ++- abci/example/kvstore/persistent_kvstore.go | 3 ++- blockchain/v0/codec.go | 1 + blockchain/v0/reactor_test.go | 6 +++--- blockchain/v1/codec.go | 1 + blockchain/v1/reactor.go | 1 + blockchain/v1/reactor_fsm_test.go | 1 + blockchain/v1/reactor_test.go | 3 ++- blockchain/v2/codec.go | 1 + blockchain/v2/processor_test.go | 1 + blockchain/v2/reactor.go | 1 + blockchain/v2/reactor_test.go | 3 ++- blockchain/v2/routine.go | 1 + blockchain/v2/scheduler_test.go | 1 + cmd/tendermint/commands/codec.go | 1 + cmd/tendermint/commands/debug/dump.go | 1 + cmd/tendermint/commands/debug/util.go | 1 + consensus/byzantine_test.go | 1 + consensus/codec.go | 1 + consensus/common_test.go | 3 ++- consensus/mempool_test.go | 3 ++- consensus/reactor.go | 1 + consensus/reactor_test.go | 3 ++- consensus/replay_test.go | 3 ++- consensus/types/codec.go | 1 + consensus/types/round_state_test.go | 1 + consensus/wal.go | 1 + consensus/wal_generator.go | 3 ++- crypto/ed25519/ed25519_test.go | 1 + crypto/encoding/amino/amino.go | 1 + crypto/encoding/amino/encode_test.go | 1 + crypto/merkle/simple_map.go | 1 + crypto/merkle/simple_proof.go | 1 + crypto/multisig/bitarray/compact_bit_array_test.go | 1 + crypto/multisig/codec.go | 1 + crypto/secp256k1/secp256k1.go | 1 + crypto/secp256k1/secp256k1_cgo_test.go | 3 ++- crypto/sr25519/codec.go | 1 + crypto/tmhash/hash_test.go | 1 + evidence/codec.go | 1 + evidence/pool.go | 4 ++-- evidence/pool_test.go | 3 ++- evidence/reactor_test.go | 3 ++- evidence/store.go | 3 ++- evidence/store_test.go | 3 ++- libs/clist/clist_test.go | 1 + libs/kv/kvpair.go | 1 - libs/log/tmfmt_logger_test.go | 1 + libs/log/tracing_logger_test.go | 1 + libs/pubsub/pubsub.go | 1 + lite/dbprovider.go | 3 ++- lite/dynamic_verifier_test.go | 3 ++- lite/provider_test.go | 3 ++- lite/proxy/verifier.go | 3 ++- lite2/client_benchmark_test.go | 3 ++- node/codec.go | 1 + node/node.go | 3 ++- node/node_test.go | 3 ++- p2p/codec.go | 1 + p2p/conn/codec.go | 1 + p2p/conn/connection.go | 1 + p2p/conn/connection_test.go | 1 + p2p/conn/secret_connection_test.go | 1 + p2p/key_test.go | 1 + p2p/node_info_test.go | 1 + p2p/pex/addrbook_test.go | 1 + p2p/pex/pex_reactor.go | 1 + p2p/trust/store.go | 3 ++- p2p/trust/store_test.go | 3 ++- privval/codec.go | 1 + privval/file_test.go | 1 + privval/messages.go | 1 + privval/socket_dialers.go | 1 + rpc/client/codec.go | 1 + rpc/client/helpers.go | 1 + rpc/client/helpers_test.go | 1 + rpc/core/blocks_test.go | 3 ++- rpc/core/pipe.go | 3 ++- rpc/core/types/codec.go | 1 + rpc/lib/client/integration_test.go | 1 + rpc/lib/client/ws_client.go | 1 + rpc/lib/client/ws_client_test.go | 1 + rpc/lib/rpc_test.go | 1 + rpc/lib/server/http_json_handler_test.go | 1 + rpc/lib/server/parse_test.go | 1 + scripts/json2wal/main.go | 1 + scripts/privValUpgrade_test.go | 1 + scripts/wal2json/main.go | 1 + state/codec.go | 1 + state/execution.go | 3 ++- state/execution_test.go | 1 + state/export_test.go | 3 ++- state/state_test.go | 7 ++++--- state/store.go | 3 ++- state/store_test.go | 3 ++- state/tx_filter_test.go | 3 ++- state/txindex/indexer_service_test.go | 3 ++- state/txindex/kv/kv_bench_test.go | 3 ++- state/validation.go | 3 ++- state/validation_test.go | 2 +- store/codec.go | 1 + test/app/grpc_client.go | 1 + types/codec.go | 1 + types/events.go | 1 + types/evidence.go | 1 + types/params_test.go | 1 + types/proposal_test.go | 1 + types/protobuf_test.go | 1 + types/results_test.go | 1 + types/validator_set.go | 1 + 111 files changed, 152 insertions(+), 46 deletions(-) diff --git a/Makefile b/Makefile index da4d440e7..a5b7db85a 100644 --- a/Makefile +++ b/Makefile @@ -134,9 +134,10 @@ clean_certs: ### Formatting, linting, and vetting ### ############################################################################### -fmt: - @go fmt ./... -.PHONY: fmt +format: + find . -name '*.go' -type f -not -path "*.git*" -not -name '*.pb.go' -not -name '*pb_test.go' | xargs gofmt -w -s + find . -name '*.go' -type f -not -path "*.git*" -not -name '*.pb.go' -not -name '*pb_test.go' | xargs goimports -w -local github.com/tendermint/tendermint +.PHONY: format lint: @echo "--> Running linter" diff --git a/abci/example/kvstore/kvstore.go b/abci/example/kvstore/kvstore.go index 4e7449938..ef9dcee92 100644 --- a/abci/example/kvstore/kvstore.go +++ b/abci/example/kvstore/kvstore.go @@ -6,11 +6,12 @@ import ( "encoding/json" "fmt" + dbm "github.com/tendermint/tm-db" + "github.com/tendermint/tendermint/abci/example/code" "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/kv" "github.com/tendermint/tendermint/version" - dbm "github.com/tendermint/tm-db" ) var ( diff --git a/abci/example/kvstore/persistent_kvstore.go b/abci/example/kvstore/persistent_kvstore.go index 0c5498bee..fffc617be 100644 --- a/abci/example/kvstore/persistent_kvstore.go +++ b/abci/example/kvstore/persistent_kvstore.go @@ -7,12 +7,13 @@ import ( "strconv" "strings" + dbm "github.com/tendermint/tm-db" + "github.com/tendermint/tendermint/abci/example/code" "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/libs/log" tmtypes "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" ) const ( diff --git a/blockchain/v0/codec.go b/blockchain/v0/codec.go index 4494f41aa..f023bbfa1 100644 --- a/blockchain/v0/codec.go +++ b/blockchain/v0/codec.go @@ -2,6 +2,7 @@ package v0 import ( amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/types" ) diff --git a/blockchain/v0/reactor_test.go b/blockchain/v0/reactor_test.go index 5a97d3aed..74811dd0a 100644 --- a/blockchain/v0/reactor_test.go +++ b/blockchain/v0/reactor_test.go @@ -7,10 +7,10 @@ import ( "time" "github.com/pkg/errors" - "github.com/tendermint/tendermint/store" - "github.com/stretchr/testify/assert" + dbm "github.com/tendermint/tm-db" + abci "github.com/tendermint/tendermint/abci/types" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/log" @@ -18,9 +18,9 @@ import ( "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" + "github.com/tendermint/tendermint/store" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" - dbm "github.com/tendermint/tm-db" ) var config *cfg.Config diff --git a/blockchain/v1/codec.go b/blockchain/v1/codec.go index 786584435..ce4f7dfab 100644 --- a/blockchain/v1/codec.go +++ b/blockchain/v1/codec.go @@ -2,6 +2,7 @@ package v1 import ( amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/types" ) diff --git a/blockchain/v1/reactor.go b/blockchain/v1/reactor.go index 1aba26b35..d3b9b0216 100644 --- a/blockchain/v1/reactor.go +++ b/blockchain/v1/reactor.go @@ -7,6 +7,7 @@ import ( "time" amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/behaviour" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/p2p" diff --git a/blockchain/v1/reactor_fsm_test.go b/blockchain/v1/reactor_fsm_test.go index f51defb51..5980ceb08 100644 --- a/blockchain/v1/reactor_fsm_test.go +++ b/blockchain/v1/reactor_fsm_test.go @@ -6,6 +6,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/libs/log" tmmath "github.com/tendermint/tendermint/libs/math" tmrand "github.com/tendermint/tendermint/libs/rand" diff --git a/blockchain/v1/reactor_test.go b/blockchain/v1/reactor_test.go index d0e55695b..ec9707f67 100644 --- a/blockchain/v1/reactor_test.go +++ b/blockchain/v1/reactor_test.go @@ -12,6 +12,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + dbm "github.com/tendermint/tm-db" + abci "github.com/tendermint/tendermint/abci/types" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/log" @@ -22,7 +24,6 @@ import ( "github.com/tendermint/tendermint/store" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" - dbm "github.com/tendermint/tm-db" ) var config *cfg.Config diff --git a/blockchain/v2/codec.go b/blockchain/v2/codec.go index f970d115f..4e92846c4 100644 --- a/blockchain/v2/codec.go +++ b/blockchain/v2/codec.go @@ -2,6 +2,7 @@ package v2 import ( amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/types" ) diff --git a/blockchain/v2/processor_test.go b/blockchain/v2/processor_test.go index fc35c4c72..6bc36b2d3 100644 --- a/blockchain/v2/processor_test.go +++ b/blockchain/v2/processor_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/p2p" tmState "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" diff --git a/blockchain/v2/reactor.go b/blockchain/v2/reactor.go index 767e59819..e09e0adde 100644 --- a/blockchain/v2/reactor.go +++ b/blockchain/v2/reactor.go @@ -7,6 +7,7 @@ import ( "time" "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/behaviour" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/p2p" diff --git a/blockchain/v2/reactor_test.go b/blockchain/v2/reactor_test.go index ad091f582..1d3e51e77 100644 --- a/blockchain/v2/reactor_test.go +++ b/blockchain/v2/reactor_test.go @@ -10,6 +10,8 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/assert" + dbm "github.com/tendermint/tm-db" + abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/behaviour" cfg "github.com/tendermint/tendermint/config" @@ -23,7 +25,6 @@ import ( "github.com/tendermint/tendermint/store" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" - dbm "github.com/tendermint/tm-db" ) type mockPeer struct { diff --git a/blockchain/v2/routine.go b/blockchain/v2/routine.go index 1a883c3c4..ff12bfebc 100644 --- a/blockchain/v2/routine.go +++ b/blockchain/v2/routine.go @@ -5,6 +5,7 @@ import ( "sync/atomic" "github.com/Workiva/go-datastructures/queue" + "github.com/tendermint/tendermint/libs/log" ) diff --git a/blockchain/v2/scheduler_test.go b/blockchain/v2/scheduler_test.go index 445ba51a7..c91fdd366 100644 --- a/blockchain/v2/scheduler_test.go +++ b/blockchain/v2/scheduler_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/types" ) diff --git a/cmd/tendermint/commands/codec.go b/cmd/tendermint/commands/codec.go index 717f2d21e..041b9e9ce 100644 --- a/cmd/tendermint/commands/codec.go +++ b/cmd/tendermint/commands/codec.go @@ -2,6 +2,7 @@ package commands import ( amino "github.com/tendermint/go-amino" + cryptoamino "github.com/tendermint/tendermint/crypto/encoding/amino" ) diff --git a/cmd/tendermint/commands/debug/dump.go b/cmd/tendermint/commands/debug/dump.go index 33cb3e24d..7032206b4 100644 --- a/cmd/tendermint/commands/debug/dump.go +++ b/cmd/tendermint/commands/debug/dump.go @@ -10,6 +10,7 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" + cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/cli" rpcclient "github.com/tendermint/tendermint/rpc/client" diff --git a/cmd/tendermint/commands/debug/util.go b/cmd/tendermint/commands/debug/util.go index b392d23d7..0e7888121 100644 --- a/cmd/tendermint/commands/debug/util.go +++ b/cmd/tendermint/commands/debug/util.go @@ -9,6 +9,7 @@ import ( "path/filepath" "github.com/pkg/errors" + cfg "github.com/tendermint/tendermint/config" rpcclient "github.com/tendermint/tendermint/rpc/client" ) diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index bbf9cbf8f..649d47fbb 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/service" "github.com/tendermint/tendermint/p2p" sm "github.com/tendermint/tendermint/state" diff --git a/consensus/codec.go b/consensus/codec.go index 1c5bf93df..ae7dbaab2 100644 --- a/consensus/codec.go +++ b/consensus/codec.go @@ -2,6 +2,7 @@ package consensus import ( amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/types" ) diff --git a/consensus/common_test.go b/consensus/common_test.go index 1b2c818ae..f8055f01c 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -18,6 +18,8 @@ import ( "path" + dbm "github.com/tendermint/tm-db" + abcicli "github.com/tendermint/tendermint/abci/client" "github.com/tendermint/tendermint/abci/example/counter" "github.com/tendermint/tendermint/abci/example/kvstore" @@ -35,7 +37,6 @@ import ( "github.com/tendermint/tendermint/store" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" - dbm "github.com/tendermint/tm-db" ) const ( diff --git a/consensus/mempool_test.go b/consensus/mempool_test.go index c7fdbd578..8e268d444 100644 --- a/consensus/mempool_test.go +++ b/consensus/mempool_test.go @@ -9,12 +9,13 @@ import ( "github.com/stretchr/testify/assert" + dbm "github.com/tendermint/tm-db" + "github.com/tendermint/tendermint/abci/example/code" abci "github.com/tendermint/tendermint/abci/types" mempl "github.com/tendermint/tendermint/mempool" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" ) // for testing diff --git a/consensus/reactor.go b/consensus/reactor.go index 0f2dad743..61e46c7c4 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -9,6 +9,7 @@ import ( "github.com/pkg/errors" amino "github.com/tendermint/go-amino" + cstypes "github.com/tendermint/tendermint/consensus/types" "github.com/tendermint/tendermint/libs/bits" tmevents "github.com/tendermint/tendermint/libs/events" diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index 95ee6cdc5..a0a8c9732 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -14,6 +14,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + dbm "github.com/tendermint/tm-db" + abcicli "github.com/tendermint/tendermint/abci/client" "github.com/tendermint/tendermint/abci/example/kvstore" abci "github.com/tendermint/tendermint/abci/types" @@ -29,7 +31,6 @@ import ( sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/store" "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" ) //---------------------------------------------- diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 24903927d..bbb73c9be 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -17,6 +17,8 @@ import ( "sort" + dbm "github.com/tendermint/tm-db" + "github.com/tendermint/tendermint/abci/example/kvstore" abci "github.com/tendermint/tendermint/abci/types" cfg "github.com/tendermint/tendermint/config" @@ -30,7 +32,6 @@ import ( sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/version" - dbm "github.com/tendermint/tm-db" ) func TestMain(m *testing.M) { diff --git a/consensus/types/codec.go b/consensus/types/codec.go index e8a05b355..69ac8c4a5 100644 --- a/consensus/types/codec.go +++ b/consensus/types/codec.go @@ -2,6 +2,7 @@ package types import ( amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/types" ) diff --git a/consensus/types/round_state_test.go b/consensus/types/round_state_test.go index f5f5f72c0..131158f0e 100644 --- a/consensus/types/round_state_test.go +++ b/consensus/types/round_state_test.go @@ -4,6 +4,7 @@ import ( "testing" amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/tmhash" tmrand "github.com/tendermint/tendermint/libs/rand" diff --git a/consensus/wal.go b/consensus/wal.go index 989a5dc29..7b09ffa2d 100644 --- a/consensus/wal.go +++ b/consensus/wal.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" amino "github.com/tendermint/go-amino" + auto "github.com/tendermint/tendermint/libs/autofile" "github.com/tendermint/tendermint/libs/log" tmos "github.com/tendermint/tendermint/libs/os" diff --git a/consensus/wal_generator.go b/consensus/wal_generator.go index 244edd536..422c3f73b 100644 --- a/consensus/wal_generator.go +++ b/consensus/wal_generator.go @@ -11,6 +11,8 @@ import ( "github.com/pkg/errors" + db "github.com/tendermint/tm-db" + "github.com/tendermint/tendermint/abci/example/kvstore" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/log" @@ -21,7 +23,6 @@ import ( sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/store" "github.com/tendermint/tendermint/types" - db "github.com/tendermint/tm-db" ) // WALGenerateNBlocks generates a consensus WAL. It does this by spinning up a diff --git a/crypto/ed25519/ed25519_test.go b/crypto/ed25519/ed25519_test.go index 503050274..6fe2c0946 100644 --- a/crypto/ed25519/ed25519_test.go +++ b/crypto/ed25519/ed25519_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" ) diff --git a/crypto/encoding/amino/amino.go b/crypto/encoding/amino/amino.go index b63eb738d..f7a2dde77 100644 --- a/crypto/encoding/amino/amino.go +++ b/crypto/encoding/amino/amino.go @@ -4,6 +4,7 @@ import ( "reflect" amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/multisig" diff --git a/crypto/encoding/amino/encode_test.go b/crypto/encoding/amino/encode_test.go index 67a7566dd..edc54292f 100644 --- a/crypto/encoding/amino/encode_test.go +++ b/crypto/encoding/amino/encode_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/require" amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/multisig" diff --git a/crypto/merkle/simple_map.go b/crypto/merkle/simple_map.go index 36434f67f..840bebd51 100644 --- a/crypto/merkle/simple_map.go +++ b/crypto/merkle/simple_map.go @@ -4,6 +4,7 @@ import ( "bytes" amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/libs/kv" ) diff --git a/crypto/merkle/simple_proof.go b/crypto/merkle/simple_proof.go index 660bf236f..44b97f606 100644 --- a/crypto/merkle/simple_proof.go +++ b/crypto/merkle/simple_proof.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/pkg/errors" + "github.com/tendermint/tendermint/crypto/tmhash" ) diff --git a/crypto/multisig/bitarray/compact_bit_array_test.go b/crypto/multisig/bitarray/compact_bit_array_test.go index ba0949178..f086dc877 100644 --- a/crypto/multisig/bitarray/compact_bit_array_test.go +++ b/crypto/multisig/bitarray/compact_bit_array_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + tmrand "github.com/tendermint/tendermint/libs/rand" ) diff --git a/crypto/multisig/codec.go b/crypto/multisig/codec.go index 3a5869398..cc1e12f92 100644 --- a/crypto/multisig/codec.go +++ b/crypto/multisig/codec.go @@ -2,6 +2,7 @@ package multisig import ( amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/secp256k1" diff --git a/crypto/secp256k1/secp256k1.go b/crypto/secp256k1/secp256k1.go index 26dcead59..5338d10a5 100644 --- a/crypto/secp256k1/secp256k1.go +++ b/crypto/secp256k1/secp256k1.go @@ -12,6 +12,7 @@ import ( "golang.org/x/crypto/ripemd160" // nolint: staticcheck // necessary for Bitcoin address format amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/crypto" ) diff --git a/crypto/secp256k1/secp256k1_cgo_test.go b/crypto/secp256k1/secp256k1_cgo_test.go index edb207b53..96b026bc9 100644 --- a/crypto/secp256k1/secp256k1_cgo_test.go +++ b/crypto/secp256k1/secp256k1_cgo_test.go @@ -3,9 +3,10 @@ package secp256k1 import ( - "github.com/magiconair/properties/assert" "testing" + "github.com/magiconair/properties/assert" + "github.com/stretchr/testify/require" ) diff --git a/crypto/sr25519/codec.go b/crypto/sr25519/codec.go index c3e6bd646..f33b616f9 100644 --- a/crypto/sr25519/codec.go +++ b/crypto/sr25519/codec.go @@ -2,6 +2,7 @@ package sr25519 import ( amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/crypto" ) diff --git a/crypto/tmhash/hash_test.go b/crypto/tmhash/hash_test.go index 89a779801..57fd0faa5 100644 --- a/crypto/tmhash/hash_test.go +++ b/crypto/tmhash/hash_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/crypto/tmhash" ) diff --git a/evidence/codec.go b/evidence/codec.go index 135341068..650a34607 100644 --- a/evidence/codec.go +++ b/evidence/codec.go @@ -2,6 +2,7 @@ package evidence import ( amino "github.com/tendermint/go-amino" + cryptoamino "github.com/tendermint/tendermint/crypto/encoding/amino" "github.com/tendermint/tendermint/types" ) diff --git a/evidence/pool.go b/evidence/pool.go index 62b0a3325..a4d72cd2f 100644 --- a/evidence/pool.go +++ b/evidence/pool.go @@ -5,10 +5,10 @@ import ( "sync" "time" - clist "github.com/tendermint/tendermint/libs/clist" - "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" + clist "github.com/tendermint/tendermint/libs/clist" + "github.com/tendermint/tendermint/libs/log" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" ) diff --git a/evidence/pool_test.go b/evidence/pool_test.go index a39ae3eb5..844ca18af 100644 --- a/evidence/pool_test.go +++ b/evidence/pool_test.go @@ -8,10 +8,11 @@ import ( "github.com/stretchr/testify/assert" + dbm "github.com/tendermint/tm-db" + sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" - dbm "github.com/tendermint/tm-db" ) func TestMain(m *testing.M) { diff --git a/evidence/reactor_test.go b/evidence/reactor_test.go index b013b7715..135c191da 100644 --- a/evidence/reactor_test.go +++ b/evidence/reactor_test.go @@ -9,12 +9,13 @@ import ( "github.com/go-kit/kit/log/term" "github.com/stretchr/testify/assert" + dbm "github.com/tendermint/tm-db" + cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto/secp256k1" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" ) // evidenceLogger is a TestingLogger which uses a different diff --git a/evidence/store.go b/evidence/store.go index 3547b5ffc..2bfe68523 100644 --- a/evidence/store.go +++ b/evidence/store.go @@ -3,8 +3,9 @@ package evidence import ( "fmt" - "github.com/tendermint/tendermint/types" dbm "github.com/tendermint/tm-db" + + "github.com/tendermint/tendermint/types" ) /* diff --git a/evidence/store_test.go b/evidence/store_test.go index b85a6437b..351abfee2 100644 --- a/evidence/store_test.go +++ b/evidence/store_test.go @@ -5,8 +5,9 @@ import ( "time" "github.com/stretchr/testify/assert" - "github.com/tendermint/tendermint/types" dbm "github.com/tendermint/tm-db" + + "github.com/tendermint/tendermint/types" ) //------------------------------------------- diff --git a/libs/clist/clist_test.go b/libs/clist/clist_test.go index 1e25946ac..14b7e37c0 100644 --- a/libs/clist/clist_test.go +++ b/libs/clist/clist_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/stretchr/testify/assert" + tmrand "github.com/tendermint/tendermint/libs/rand" ) diff --git a/libs/kv/kvpair.go b/libs/kv/kvpair.go index 8eebae606..2474b2e47 100644 --- a/libs/kv/kvpair.go +++ b/libs/kv/kvpair.go @@ -35,4 +35,3 @@ func (kvs Pairs) Less(i, j int) bool { } func (kvs Pairs) Swap(i, j int) { kvs[i], kvs[j] = kvs[j], kvs[i] } func (kvs Pairs) Sort() { sort.Sort(kvs) } - diff --git a/libs/log/tmfmt_logger_test.go b/libs/log/tmfmt_logger_test.go index d6f039ce4..86d0e5693 100644 --- a/libs/log/tmfmt_logger_test.go +++ b/libs/log/tmfmt_logger_test.go @@ -10,6 +10,7 @@ import ( kitlog "github.com/go-kit/kit/log" "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/libs/log" ) diff --git a/libs/log/tracing_logger_test.go b/libs/log/tracing_logger_test.go index b40d2b9e0..354476755 100644 --- a/libs/log/tracing_logger_test.go +++ b/libs/log/tracing_logger_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/pkg/errors" + "github.com/tendermint/tendermint/libs/log" ) diff --git a/libs/pubsub/pubsub.go b/libs/pubsub/pubsub.go index 325403cd8..2a89e7591 100644 --- a/libs/pubsub/pubsub.go +++ b/libs/pubsub/pubsub.go @@ -39,6 +39,7 @@ import ( "sync" "github.com/pkg/errors" + "github.com/tendermint/tendermint/libs/service" ) diff --git a/lite/dbprovider.go b/lite/dbprovider.go index 35f7270ae..9b1580314 100644 --- a/lite/dbprovider.go +++ b/lite/dbprovider.go @@ -6,11 +6,12 @@ import ( "strconv" amino "github.com/tendermint/go-amino" + dbm "github.com/tendermint/tm-db" + cryptoamino "github.com/tendermint/tendermint/crypto/encoding/amino" log "github.com/tendermint/tendermint/libs/log" lerr "github.com/tendermint/tendermint/lite/errors" "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" ) var _ PersistentProvider = (*DBProvider)(nil) diff --git a/lite/dynamic_verifier_test.go b/lite/dynamic_verifier_test.go index 441010efb..fdb89052b 100644 --- a/lite/dynamic_verifier_test.go +++ b/lite/dynamic_verifier_test.go @@ -8,9 +8,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + dbm "github.com/tendermint/tm-db" + log "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" ) const testChainID = "inquiry-test" diff --git a/lite/provider_test.go b/lite/provider_test.go index 98fff8cb4..b820418ff 100644 --- a/lite/provider_test.go +++ b/lite/provider_test.go @@ -7,10 +7,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + dbm "github.com/tendermint/tm-db" + log "github.com/tendermint/tendermint/libs/log" lerr "github.com/tendermint/tendermint/lite/errors" "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" ) // missingProvider doesn't store anything, always a miss. diff --git a/lite/proxy/verifier.go b/lite/proxy/verifier.go index b5fc3af3f..5486a3ea9 100644 --- a/lite/proxy/verifier.go +++ b/lite/proxy/verifier.go @@ -3,10 +3,11 @@ package proxy import ( "github.com/pkg/errors" + dbm "github.com/tendermint/tm-db" + log "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/lite" lclient "github.com/tendermint/tendermint/lite/client" - dbm "github.com/tendermint/tm-db" ) func NewVerifier( diff --git a/lite2/client_benchmark_test.go b/lite2/client_benchmark_test.go index 823ff1156..276c962a1 100644 --- a/lite2/client_benchmark_test.go +++ b/lite2/client_benchmark_test.go @@ -4,11 +4,12 @@ import ( "testing" "time" + dbm "github.com/tendermint/tm-db" + "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/lite2/provider" mockp "github.com/tendermint/tendermint/lite2/provider/mock" dbs "github.com/tendermint/tendermint/lite2/store/db" - dbm "github.com/tendermint/tm-db" ) // NOTE: block is produced every minute. Make sure the verification time diff --git a/node/codec.go b/node/codec.go index 7607b0dd0..e172b9696 100644 --- a/node/codec.go +++ b/node/codec.go @@ -2,6 +2,7 @@ package node import ( amino "github.com/tendermint/go-amino" + cryptoamino "github.com/tendermint/tendermint/crypto/encoding/amino" ) diff --git a/node/node.go b/node/node.go index e9cd02c30..8bc6f1037 100644 --- a/node/node.go +++ b/node/node.go @@ -17,6 +17,8 @@ import ( "github.com/rs/cors" amino "github.com/tendermint/go-amino" + dbm "github.com/tendermint/tm-db" + abci "github.com/tendermint/tendermint/abci/types" bcv0 "github.com/tendermint/tendermint/blockchain/v0" bcv1 "github.com/tendermint/tendermint/blockchain/v1" @@ -45,7 +47,6 @@ import ( "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" "github.com/tendermint/tendermint/version" - dbm "github.com/tendermint/tm-db" ) //------------------------------------------------------------------------------ diff --git a/node/node_test.go b/node/node_test.go index ec8510d58..a9a43a362 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -12,6 +12,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + dbm "github.com/tendermint/tm-db" + "github.com/tendermint/tendermint/abci/example/kvstore" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto/ed25519" @@ -27,7 +29,6 @@ import ( "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" "github.com/tendermint/tendermint/version" - dbm "github.com/tendermint/tm-db" ) func TestNodeStartStop(t *testing.T) { diff --git a/p2p/codec.go b/p2p/codec.go index 6368b7d68..463276318 100644 --- a/p2p/codec.go +++ b/p2p/codec.go @@ -2,6 +2,7 @@ package p2p import ( amino "github.com/tendermint/go-amino" + cryptoamino "github.com/tendermint/tendermint/crypto/encoding/amino" ) diff --git a/p2p/conn/codec.go b/p2p/conn/codec.go index 149a09638..0625c7a38 100644 --- a/p2p/conn/codec.go +++ b/p2p/conn/codec.go @@ -2,6 +2,7 @@ package conn import ( amino "github.com/tendermint/go-amino" + cryptoamino "github.com/tendermint/tendermint/crypto/encoding/amino" ) diff --git a/p2p/conn/connection.go b/p2p/conn/connection.go index 1c2088636..0436e115c 100644 --- a/p2p/conn/connection.go +++ b/p2p/conn/connection.go @@ -16,6 +16,7 @@ import ( "github.com/pkg/errors" amino "github.com/tendermint/go-amino" + flow "github.com/tendermint/tendermint/libs/flowrate" "github.com/tendermint/tendermint/libs/log" tmmath "github.com/tendermint/tendermint/libs/math" diff --git a/p2p/conn/connection_test.go b/p2p/conn/connection_test.go index 7daa6076d..29d29fc6e 100644 --- a/p2p/conn/connection_test.go +++ b/p2p/conn/connection_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/libs/log" ) diff --git a/p2p/conn/secret_connection_test.go b/p2p/conn/secret_connection_test.go index 5ac3b8509..9044d73be 100644 --- a/p2p/conn/secret_connection_test.go +++ b/p2p/conn/secret_connection_test.go @@ -16,6 +16,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/secp256k1" diff --git a/p2p/key_test.go b/p2p/key_test.go index e0579dde6..6f8e9b0f8 100644 --- a/p2p/key_test.go +++ b/p2p/key_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + tmrand "github.com/tendermint/tendermint/libs/rand" ) diff --git a/p2p/node_info_test.go b/p2p/node_info_test.go index 6937affb8..8896efe1d 100644 --- a/p2p/node_info_test.go +++ b/p2p/node_info_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/crypto/ed25519" ) diff --git a/p2p/pex/addrbook_test.go b/p2p/pex/addrbook_test.go index e5a226daf..739fff185 100644 --- a/p2p/pex/addrbook_test.go +++ b/p2p/pex/addrbook_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/log" tmmath "github.com/tendermint/tendermint/libs/math" tmrand "github.com/tendermint/tendermint/libs/rand" diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index b0a1c02bb..b0aafe0be 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -9,6 +9,7 @@ import ( "github.com/pkg/errors" "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/libs/cmap" tmmath "github.com/tendermint/tendermint/libs/math" "github.com/tendermint/tendermint/libs/rand" diff --git a/p2p/trust/store.go b/p2p/trust/store.go index 502c88f90..166b26b1c 100644 --- a/p2p/trust/store.go +++ b/p2p/trust/store.go @@ -9,8 +9,9 @@ import ( "sync" "time" - "github.com/tendermint/tendermint/libs/service" dbm "github.com/tendermint/tm-db" + + "github.com/tendermint/tendermint/libs/service" ) const defaultStorePeriodicSaveInterval = 1 * time.Minute diff --git a/p2p/trust/store_test.go b/p2p/trust/store_test.go index 76dbaac1c..1cd83916c 100644 --- a/p2p/trust/store_test.go +++ b/p2p/trust/store_test.go @@ -10,8 +10,9 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" + + "github.com/tendermint/tendermint/libs/log" ) func TestTrustMetricStoreSaveLoad(t *testing.T) { diff --git a/privval/codec.go b/privval/codec.go index 9edcc7741..d1f2eafa2 100644 --- a/privval/codec.go +++ b/privval/codec.go @@ -2,6 +2,7 @@ package privval import ( amino "github.com/tendermint/go-amino" + cryptoamino "github.com/tendermint/tendermint/crypto/encoding/amino" ) diff --git a/privval/file_test.go b/privval/file_test.go index 38f6e6fe3..343131e1a 100644 --- a/privval/file_test.go +++ b/privval/file_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" diff --git a/privval/messages.go b/privval/messages.go index c172a5ea1..fa7a0b09d 100644 --- a/privval/messages.go +++ b/privval/messages.go @@ -2,6 +2,7 @@ package privval import ( amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/types" ) diff --git a/privval/socket_dialers.go b/privval/socket_dialers.go index 1945e7728..f9e5c7879 100644 --- a/privval/socket_dialers.go +++ b/privval/socket_dialers.go @@ -5,6 +5,7 @@ import ( "time" "github.com/pkg/errors" + "github.com/tendermint/tendermint/crypto" tmnet "github.com/tendermint/tendermint/libs/net" p2pconn "github.com/tendermint/tendermint/p2p/conn" diff --git a/rpc/client/codec.go b/rpc/client/codec.go index ef1a00ec4..2dc0f6319 100644 --- a/rpc/client/codec.go +++ b/rpc/client/codec.go @@ -2,6 +2,7 @@ package client import ( amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/types" ) diff --git a/rpc/client/helpers.go b/rpc/client/helpers.go index 756ba2818..0e54ec03b 100644 --- a/rpc/client/helpers.go +++ b/rpc/client/helpers.go @@ -5,6 +5,7 @@ import ( "time" "github.com/pkg/errors" + "github.com/tendermint/tendermint/types" ) diff --git a/rpc/client/helpers_test.go b/rpc/client/helpers_test.go index 8b843fcdb..3b78dfe5f 100644 --- a/rpc/client/helpers_test.go +++ b/rpc/client/helpers_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/rpc/client" "github.com/tendermint/tendermint/rpc/client/mock" ctypes "github.com/tendermint/tendermint/rpc/core/types" diff --git a/rpc/core/blocks_test.go b/rpc/core/blocks_test.go index d537f6c3a..1c4c3af13 100644 --- a/rpc/core/blocks_test.go +++ b/rpc/core/blocks_test.go @@ -7,12 +7,13 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + dbm "github.com/tendermint/tm-db" + abci "github.com/tendermint/tendermint/abci/types" ctypes "github.com/tendermint/tendermint/rpc/core/types" rpctypes "github.com/tendermint/tendermint/rpc/lib/types" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" ) func TestBlockchainInfo(t *testing.T) { diff --git a/rpc/core/pipe.go b/rpc/core/pipe.go index 532493451..4fb3b9b13 100644 --- a/rpc/core/pipe.go +++ b/rpc/core/pipe.go @@ -4,6 +4,8 @@ import ( "fmt" "time" + dbm "github.com/tendermint/tm-db" + cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/consensus" "github.com/tendermint/tendermint/crypto" @@ -14,7 +16,6 @@ import ( sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/state/txindex" "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" ) const ( diff --git a/rpc/core/types/codec.go b/rpc/core/types/codec.go index 82543ff6e..8e0b5303f 100644 --- a/rpc/core/types/codec.go +++ b/rpc/core/types/codec.go @@ -2,6 +2,7 @@ package coretypes import ( amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/types" ) diff --git a/rpc/lib/client/integration_test.go b/rpc/lib/client/integration_test.go index 393783c51..5fee3752b 100644 --- a/rpc/lib/client/integration_test.go +++ b/rpc/lib/client/integration_test.go @@ -14,6 +14,7 @@ import ( "time" "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/log" ) diff --git a/rpc/lib/client/ws_client.go b/rpc/lib/client/ws_client.go index 4d8a58b8e..ddddc97cf 100644 --- a/rpc/lib/client/ws_client.go +++ b/rpc/lib/client/ws_client.go @@ -14,6 +14,7 @@ import ( metrics "github.com/rcrowley/go-metrics" amino "github.com/tendermint/go-amino" + tmrand "github.com/tendermint/tendermint/libs/rand" "github.com/tendermint/tendermint/libs/service" types "github.com/tendermint/tendermint/rpc/lib/types" diff --git a/rpc/lib/client/ws_client_test.go b/rpc/lib/client/ws_client_test.go index 33a65dcbe..a4f033867 100644 --- a/rpc/lib/client/ws_client_test.go +++ b/rpc/lib/client/ws_client_test.go @@ -11,6 +11,7 @@ import ( "github.com/gorilla/websocket" "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/log" types "github.com/tendermint/tendermint/rpc/lib/types" diff --git a/rpc/lib/rpc_test.go b/rpc/lib/rpc_test.go index 5b95666a7..aef795d3e 100644 --- a/rpc/lib/rpc_test.go +++ b/rpc/lib/rpc_test.go @@ -17,6 +17,7 @@ import ( "github.com/stretchr/testify/require" amino "github.com/tendermint/go-amino" + tmbytes "github.com/tendermint/tendermint/libs/bytes" "github.com/tendermint/tendermint/libs/log" tmrand "github.com/tendermint/tendermint/libs/rand" diff --git a/rpc/lib/server/http_json_handler_test.go b/rpc/lib/server/http_json_handler_test.go index e4ae2f8bf..ef1fcc9f5 100644 --- a/rpc/lib/server/http_json_handler_test.go +++ b/rpc/lib/server/http_json_handler_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/require" amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/libs/log" types "github.com/tendermint/tendermint/rpc/lib/types" ) diff --git a/rpc/lib/server/parse_test.go b/rpc/lib/server/parse_test.go index 3780861e4..09a3d18ee 100644 --- a/rpc/lib/server/parse_test.go +++ b/rpc/lib/server/parse_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/assert" amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/libs/bytes" types "github.com/tendermint/tendermint/rpc/lib/types" ) diff --git a/scripts/json2wal/main.go b/scripts/json2wal/main.go index 1d1f6256c..9f6cdb2b6 100644 --- a/scripts/json2wal/main.go +++ b/scripts/json2wal/main.go @@ -15,6 +15,7 @@ import ( "strings" amino "github.com/tendermint/go-amino" + cs "github.com/tendermint/tendermint/consensus" "github.com/tendermint/tendermint/types" ) diff --git a/scripts/privValUpgrade_test.go b/scripts/privValUpgrade_test.go index 248ac7a24..287c4fc50 100644 --- a/scripts/privValUpgrade_test.go +++ b/scripts/privValUpgrade_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/privval" ) diff --git a/scripts/wal2json/main.go b/scripts/wal2json/main.go index 48195eead..181f40c75 100644 --- a/scripts/wal2json/main.go +++ b/scripts/wal2json/main.go @@ -13,6 +13,7 @@ import ( "os" amino "github.com/tendermint/go-amino" + cs "github.com/tendermint/tendermint/consensus" "github.com/tendermint/tendermint/types" ) diff --git a/state/codec.go b/state/codec.go index abbec6e39..df2c15545 100644 --- a/state/codec.go +++ b/state/codec.go @@ -2,6 +2,7 @@ package state import ( amino "github.com/tendermint/go-amino" + cryptoamino "github.com/tendermint/tendermint/crypto/encoding/amino" ) diff --git a/state/execution.go b/state/execution.go index 4025b6e7b..15f436463 100644 --- a/state/execution.go +++ b/state/execution.go @@ -4,13 +4,14 @@ import ( "fmt" "time" + dbm "github.com/tendermint/tm-db" + abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/fail" "github.com/tendermint/tendermint/libs/log" mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" ) //----------------------------------------------------------------------------- diff --git a/state/execution_test.go b/state/execution_test.go index 78f4d6ca4..7d1337391 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/abci/example/kvstore" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/ed25519" diff --git a/state/export_test.go b/state/export_test.go index 1f3990bbd..cba07eca3 100644 --- a/state/export_test.go +++ b/state/export_test.go @@ -1,9 +1,10 @@ package state import ( + dbm "github.com/tendermint/tm-db" + abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" ) // diff --git a/state/state_test.go b/state/state_test.go index d08792033..746f7837c 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -11,15 +11,16 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + dbm "github.com/tendermint/tm-db" + abci "github.com/tendermint/tendermint/abci/types" + cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/libs/kv" "github.com/tendermint/tendermint/libs/rand" tmrand "github.com/tendermint/tendermint/libs/rand" sm "github.com/tendermint/tendermint/state" - dbm "github.com/tendermint/tm-db" - - cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/types" ) diff --git a/state/store.go b/state/store.go index e49e289f0..efb7bddf0 100644 --- a/state/store.go +++ b/state/store.go @@ -3,11 +3,12 @@ package state import ( "fmt" + dbm "github.com/tendermint/tm-db" + abci "github.com/tendermint/tendermint/abci/types" tmmath "github.com/tendermint/tendermint/libs/math" tmos "github.com/tendermint/tendermint/libs/os" "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" ) const ( diff --git a/state/store_test.go b/state/store_test.go index 0f38f21a5..7661b631f 100644 --- a/state/store_test.go +++ b/state/store_test.go @@ -8,10 +8,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + dbm "github.com/tendermint/tm-db" + cfg "github.com/tendermint/tendermint/config" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" ) func TestStoreLoadValidators(t *testing.T) { diff --git a/state/tx_filter_test.go b/state/tx_filter_test.go index 9e666265c..2dac856bd 100644 --- a/state/tx_filter_test.go +++ b/state/tx_filter_test.go @@ -7,10 +7,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + dbm "github.com/tendermint/tm-db" + tmrand "github.com/tendermint/tendermint/libs/rand" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" ) func TestTxFilter(t *testing.T) { diff --git a/state/txindex/indexer_service_test.go b/state/txindex/indexer_service_test.go index 6df7c984a..23968dbca 100644 --- a/state/txindex/indexer_service_test.go +++ b/state/txindex/indexer_service_test.go @@ -7,12 +7,13 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + db "github.com/tendermint/tm-db" + abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/state/txindex" "github.com/tendermint/tendermint/state/txindex/kv" "github.com/tendermint/tendermint/types" - db "github.com/tendermint/tm-db" ) func TestIndexerServiceIndexesBlocks(t *testing.T) { diff --git a/state/txindex/kv/kv_bench_test.go b/state/txindex/kv/kv_bench_test.go index 34d770040..31267f54c 100644 --- a/state/txindex/kv/kv_bench_test.go +++ b/state/txindex/kv/kv_bench_test.go @@ -7,11 +7,12 @@ import ( "io/ioutil" "testing" + dbm "github.com/tendermint/tm-db" + abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/kv" "github.com/tendermint/tendermint/libs/pubsub/query" "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" ) func BenchmarkTxSearch(b *testing.B) { diff --git a/state/validation.go b/state/validation.go index 6c306e2df..41f12cc40 100644 --- a/state/validation.go +++ b/state/validation.go @@ -5,9 +5,10 @@ import ( "errors" "fmt" + dbm "github.com/tendermint/tm-db" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/types" - dbm "github.com/tendermint/tm-db" ) //----------------------------------------------------- diff --git a/state/validation_test.go b/state/validation_test.go index d7d875f41..373b77dc1 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -5,11 +5,11 @@ import ( "time" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/mock" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/mock" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" diff --git a/store/codec.go b/store/codec.go index 4895e8994..29a59948d 100644 --- a/store/codec.go +++ b/store/codec.go @@ -2,6 +2,7 @@ package store import ( amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/types" ) diff --git a/test/app/grpc_client.go b/test/app/grpc_client.go index 354443e5c..88e4650ab 100644 --- a/test/app/grpc_client.go +++ b/test/app/grpc_client.go @@ -8,6 +8,7 @@ import ( "context" amino "github.com/tendermint/go-amino" + coregrpc "github.com/tendermint/tendermint/rpc/grpc" ) diff --git a/types/codec.go b/types/codec.go index d77f2b29d..b4989d267 100644 --- a/types/codec.go +++ b/types/codec.go @@ -2,6 +2,7 @@ package types import ( amino "github.com/tendermint/go-amino" + cryptoamino "github.com/tendermint/tendermint/crypto/encoding/amino" ) diff --git a/types/events.go b/types/events.go index fb80db0f0..c257ba328 100644 --- a/types/events.go +++ b/types/events.go @@ -4,6 +4,7 @@ import ( "fmt" amino "github.com/tendermint/go-amino" + abci "github.com/tendermint/tendermint/abci/types" tmpubsub "github.com/tendermint/tendermint/libs/pubsub" tmquery "github.com/tendermint/tendermint/libs/pubsub/query" diff --git a/types/evidence.go b/types/evidence.go index 303deeca9..244244f9e 100644 --- a/types/evidence.go +++ b/types/evidence.go @@ -7,6 +7,7 @@ import ( "time" "github.com/pkg/errors" + "github.com/tendermint/tendermint/crypto/tmhash" amino "github.com/tendermint/go-amino" diff --git a/types/params_test.go b/types/params_test.go index 719d51da0..b446bda33 100644 --- a/types/params_test.go +++ b/types/params_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/stretchr/testify/assert" + abci "github.com/tendermint/tendermint/abci/types" ) diff --git a/types/proposal_test.go b/types/proposal_test.go index a1d0fac37..e4ea19183 100644 --- a/types/proposal_test.go +++ b/types/proposal_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/tmhash" ) diff --git a/types/protobuf_test.go b/types/protobuf_test.go index 3f4cef9ce..636091b5c 100644 --- a/types/protobuf_test.go +++ b/types/protobuf_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/require" amino "github.com/tendermint/go-amino" + abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" diff --git a/types/results_test.go b/types/results_test.go index a37de9ec4..9ecfe35ca 100644 --- a/types/results_test.go +++ b/types/results_test.go @@ -5,6 +5,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + abci "github.com/tendermint/tendermint/abci/types" ) diff --git a/types/validator_set.go b/types/validator_set.go index 04de50646..1a17d1e83 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/pkg/errors" + "github.com/tendermint/tendermint/crypto/merkle" tmmath "github.com/tendermint/tendermint/libs/math" ) From 130b16529d6b353fbdc0f31687f29356853836ed Mon Sep 17 00:00:00 2001 From: Marko Date: Mon, 23 Mar 2020 17:51:54 +0100 Subject: [PATCH 61/77] minor link fixing (#4598) --- CHANGELOG.md | 2 +- README.md | 2 +- docs/tools/README.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89315a1b7..69020d919 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -147,7 +147,7 @@ subjectivity interface. Refer to the [spec](https://github.com/tendermint/spec/b - Apps - - [tm-bench] Removed tm-bench in favor of [tm-load-test](https://github.com/interchainio/tm-load-test) + - [tm-bench] Removed tm-bench in favor of [tm-load-test](https://github.com/informalsystems/tm-load-test) - Go API diff --git a/README.md b/README.md index f3346a75c..edfc9220f 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ hosted at: https://docs.tendermint.com/master/ ### Tools Benchmarking is provided by `tm-load-test`. -The code for `tm-load-test` can be found [here](https://github.com/interchainio/tm-load-test) this binary needs to be built separately. +The code for `tm-load-test` can be found [here](https://github.com/informalsystems/tm-load-test) this binary needs to be built separately. Additional documentation is found [here](/docs/tools). ### Sub-projects diff --git a/docs/tools/README.md b/docs/tools/README.md index bf9dd1f97..86ba128f6 100644 --- a/docs/tools/README.md +++ b/docs/tools/README.md @@ -16,14 +16,14 @@ Tendermint has some tools that are associated with it for: ## Benchmarking -- https://github.com/interchainio/tm-load-test +- https://github.com/informalsystems/tm-load-test `tm-load-test` is a distributed load testing tool (and framework) for load testing Tendermint networks. ## Testnets -- https://github.com/interchainio/testnets +- https://github.com/informalsystems/testnets This repository contains various different configurations of test networks for, and relating to, Tendermint. From 18d44a01863faf427e420ed9c61669a982bee0fc Mon Sep 17 00:00:00 2001 From: Marko Date: Mon, 23 Mar 2020 20:12:31 +0100 Subject: [PATCH 62/77] blockchain: enable v2 to be set (#4597) * blockchain: enable v2 to be set - enable v2 to be set via config params Signed-off-by: Marko Baricevic * replace tab with space * correctly spell usability --- config/toml.go | 1 + node/node.go | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/config/toml.go b/config/toml.go index 609f1487a..3fe4d1aac 100644 --- a/config/toml.go +++ b/config/toml.go @@ -321,6 +321,7 @@ max_tx_bytes = {{ .Mempool.MaxTxBytes }} # Fast Sync version to use: # 1) "v0" (default) - the legacy fast sync implementation # 2) "v1" - refactor of v0 version for better testability +# 3) "v2" - refactor of v1 version for better usability version = "{{ .FastSync.Version }}" ##### consensus configuration options ##### diff --git a/node/node.go b/node/node.go index 8bc6f1037..70e725d42 100644 --- a/node/node.go +++ b/node/node.go @@ -22,6 +22,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" bcv0 "github.com/tendermint/tendermint/blockchain/v0" bcv1 "github.com/tendermint/tendermint/blockchain/v1" + bcv2 "github.com/tendermint/tendermint/blockchain/v2" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/consensus" cs "github.com/tendermint/tendermint/consensus" @@ -366,6 +367,8 @@ func createBlockchainReactor(config *cfg.Config, bcReactor = bcv0.NewBlockchainReactor(state.Copy(), blockExec, blockStore, fastSync) case "v1": bcReactor = bcv1.NewBlockchainReactor(state.Copy(), blockExec, blockStore, fastSync) + case "v2": + bcReactor = bcv2.NewBlockchainReactor(state.Copy(), blockExec, blockStore, fastSync) default: return nil, fmt.Errorf("unknown fastsync version %s", config.FastSync.Version) } @@ -1094,6 +1097,8 @@ func makeNodeInfo( bcChannel = bcv0.BlockchainChannel case "v1": bcChannel = bcv1.BlockchainChannel + case "v2": + bcChannel = bcv2.BlockchainChannel default: return nil, fmt.Errorf("unknown fastsync version %s", config.FastSync.Version) } From 43f3276d6f99b7e6647ff2f3dd93ce50ece53c09 Mon Sep 17 00:00:00 2001 From: Marko Date: Wed, 25 Mar 2020 07:59:52 +0100 Subject: [PATCH 63/77] change link checker to run daily (#4601) --- .github/workflows/action.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/action.yml b/.github/workflows/action.yml index 1b6f70730..5b449cffd 100644 --- a/.github/workflows/action.yml +++ b/.github/workflows/action.yml @@ -1,5 +1,7 @@ name: Check Markdown links -on: push +on: + schedule: + - cron: '* */24 * * *' jobs: markdown-link-check: runs-on: ubuntu-latest From a25faed5f073b3ad88b891521bd6849db68c4922 Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Thu, 26 Mar 2020 12:38:02 +0100 Subject: [PATCH 64/77] lite2: cache headers in bisection (#4562) Closes: #4546 The algorithm uses an array to store the headers and validators and populates it at every bisection (which is an unsuccessful verification). When a successful verification finally occurs it updates the new trusted header, trims that header from the cache (the array) and sets the depth pointer back to 0. Instead of retrieving new headers it will use the cached headers, incrementing in depth until it reaches the end of the cache which by then it will start to retrieve new headers from the provider. Mathematically, this method doesn't properly bisect after the first round but it will always choose a pivot header that is within 1/8th of the upper header's height. I.e. if we are trying to jump 128 headers, the maximum offset from bisection height (64) is 64 + 16(128/8) = 80, therefore a better heuristic would be to obtain the new pivot header height as the middle of these two numbers which would therefore mean to multiply it by 9/16ths instead of 1/2 (sorry this might be a bit more complicated in writing but I can try better explain if someone is interested). Therefore I would also, upon consensus, propose that we change the pivot height to 9/16th's of the previous height --- CHANGELOG_PENDING.md | 1 + docs/imgs/light_client_bisection_alg.png | Bin 0 -> 50560 bytes lite2/client.go | 229 +++++++++++++---------- lite2/doc.go | 15 ++ 4 files changed, 142 insertions(+), 103 deletions(-) create mode 100644 docs/imgs/light_client_bisection_alg.png diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index d0446d314..5e3dc64b0 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -21,6 +21,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi - [p2p] [\#4548](https://github.com/tendermint/tendermint/pull/4548) Add ban list to address book (@cmwaters) - [privval] \#4534 Add `error` as a return value on`GetPubKey()` - [Docker] \#4569 Default configuration added to docker image (you can still mount your own config the same way) (@greg-szabo) +- [lite2] [\#4562](https://github.com/tendermint/tendermint/pull/4562) Cache headers when using bisection (@cmwaters) ### BUG FIXES: diff --git a/docs/imgs/light_client_bisection_alg.png b/docs/imgs/light_client_bisection_alg.png new file mode 100644 index 0000000000000000000000000000000000000000..2a12c7542e5f5f26789c97e532e9063c65e1614f GIT binary patch literal 50560 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�F>OVYk4$mRbn5s2w>2Oo08zERL0}Y}_ZuJp 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 replacing", "err", err) @@ -756,18 +697,100 @@ func (c *Client) bisection( c.logger.Error("Can't replace primary", "err", replaceErr) // return original error return errors.Wrapf(err, "verify from #%d to #%d failed", - trustedHeader.Height, interimHeader.Height) + trustedHeader.Height, headerCache[depth].sh.Height) } // attempt to verify the header again continue default: return errors.Wrapf(err, "verify from #%d to #%d failed", - trustedHeader.Height, interimHeader.Height) + trustedHeader.Height, headerCache[depth].sh.Height) } } } +// LastTrustedHeight returns a last trusted height. -1 and nil are returned if +// there are no trusted headers. +// +// Safe for concurrent use by multiple goroutines. +func (c *Client) LastTrustedHeight() (int64, error) { + return c.trustedStore.LastSignedHeaderHeight() +} + +// FirstTrustedHeight returns a first trusted height. -1 and nil are returned if +// there are no trusted headers. +// +// Safe for concurrent use by multiple goroutines. +func (c *Client) FirstTrustedHeight() (int64, error) { + return c.trustedStore.FirstSignedHeaderHeight() +} + +// ChainID returns the chain ID the light client was configured with. +// +// Safe for concurrent use by multiple goroutines. +func (c *Client) ChainID() string { + return c.chainID +} + +// Primary returns the primary provider. +// +// NOTE: provider may be not safe for concurrent access. +func (c *Client) Primary() provider.Provider { + c.providerMutex.Lock() + defer c.providerMutex.Unlock() + return c.primary +} + +// Witnesses returns the witness providers. +// +// NOTE: providers may be not safe for concurrent access. +func (c *Client) Witnesses() []provider.Provider { + c.providerMutex.Lock() + defer c.providerMutex.Unlock() + return c.witnesses +} + +// Cleanup removes all the data (headers and validator sets) stored. Note: the +// client must be stopped at this point. +func (c *Client) Cleanup() error { + c.logger.Info("Removing all the data") + c.latestTrustedHeader = nil + c.latestTrustedVals = nil + return c.trustedStore.Prune(0) +} + +// cleanupAfter deletes all headers & validator sets after +height+. It also +// resets latestTrustedHeader to the latest header. +func (c *Client) cleanupAfter(height int64) error { + nextHeight := height + + for { + h, err := c.trustedStore.SignedHeaderAfter(nextHeight) + if err == store.ErrSignedHeaderNotFound { + break + } else if err != nil { + return errors.Wrapf(err, "failed to get header after %d", nextHeight) + } + + err = c.trustedStore.DeleteSignedHeaderAndValidatorSet(h.Height) + if err != nil { + c.logger.Error("can't remove a trusted header & validator set", "err", err, + "height", h.Height) + } + + nextHeight = h.Height + } + + c.latestTrustedHeader = nil + c.latestTrustedVals = nil + err := c.restoreTrustedHeaderAndVals() + if err != nil { + return err + } + + return nil +} + func (c *Client) updateTrustedHeaderAndVals(h *types.SignedHeader, vals *types.ValidatorSet) error { if !bytes.Equal(h.ValidatorsHash, vals.Hash()) { return errors.Errorf("expected validator's hash %X, but got %X", h.ValidatorsHash, vals.Hash()) diff --git a/lite2/doc.go b/lite2/doc.go index b61f5453f..f42aa64f1 100644 --- a/lite2/doc.go +++ b/lite2/doc.go @@ -97,6 +97,18 @@ Verify function verifies a new header against some trusted header. See https://github.com/tendermint/spec/blob/master/spec/consensus/light-client/verification.md for details. +There are two methods of verification: sequential and bisection + +Sequential uses the headers hashes and the validator sets to verify each adjacent header until +it reaches the target header. + +Bisection finds the middle header between a trusted and new header, reiterating the action until it +verifies a header. A cache of headers requested by the primary is kept such that when a +verification is made, and the light client tries again to verify the new header in the middle, +the light client does not need to ask for all the same headers again. + +refer to docs/imgs/light_client_bisection_alg.png + ## 3. Secure RPC proxy Tendermint RPC exposes a lot of info, but a malicious node could return any @@ -108,5 +120,8 @@ some other node. See https://docs.tendermint.com/master/tendermint-core/light-client-protocol.html for usage example. +Or see +https://github.com/tendermint/spec/tree/master/spec/consensus/light-client +for the full spec */ package lite From b14c0e579eebedc438ce90875d2896daa56a342e Mon Sep 17 00:00:00 2001 From: Marko Date: Fri, 27 Mar 2020 06:47:39 +0100 Subject: [PATCH 65/77] tools: remove need to install buf (#4605) * tools: remove need to install buf - using buf docker image instead of needing devs to install it Signed-off-by: Marko Baricevic * fix ci for lint and break checking --- .circleci/config.yml | 18 ------------------ .../workflows/{action.yml => linkchecker.yml} | 0 .github/workflows/proto.yml | 12 ++++++++++++ CONTRIBUTING.md | 9 +++++---- Makefile | 7 ++++--- tools.mk | 14 -------------- 6 files changed, 21 insertions(+), 39 deletions(-) rename .github/workflows/{action.yml => linkchecker.yml} (100%) create mode 100644 .github/workflows/proto.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index 95a4bd354..3cd01136b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -14,9 +14,6 @@ executors: - image: tendermintdev/docker-website-deployment environment: AWS_REGION: us-east-1 - protoc: - docker: - - image: tendermintdev/docker-protoc commands: run_test: @@ -75,19 +72,6 @@ jobs: root: "/tmp/bin" paths: - "." - proto-lint: - executor: protoc - steps: - - checkout - - run: - command: make proto-lint - - proto-breakage: - executor: protoc - steps: - - checkout - - run: - command: make proto-check-breaking-ci test_abci_apps: executor: golang @@ -409,8 +393,6 @@ workflows: - test_abci_apps: requires: - setup_dependencies - - proto-breakage - - proto-lint - test_abci_cli: requires: - setup_dependencies diff --git a/.github/workflows/action.yml b/.github/workflows/linkchecker.yml similarity index 100% rename from .github/workflows/action.yml rename to .github/workflows/linkchecker.yml diff --git a/.github/workflows/proto.yml b/.github/workflows/proto.yml new file mode 100644 index 000000000..ddc9ee4c4 --- /dev/null +++ b/.github/workflows/proto.yml @@ -0,0 +1,12 @@ +name: Proto check +on: [pull_request] +jobs: + proto-checks: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - uses: docker-practice/actions-setup-docker@master + - name: lint + run: make proto-lint + - name: check-breakage + run: make proto-check-breaking-ci diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6e6897ffa..2681b73ae 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ landing changes in master. All work on the code base should be motivated by a [Github Issue](https://github.com/tendermint/tendermint/issues). [Search](https://github.com/tendermint/tendermint/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) -is a good place start when looking for places to contribute. If you +is a good place start when looking for places to contribute. If you would like to work on an issue which already exists, please indicate so by leaving a comment. @@ -49,8 +49,9 @@ maintainers to take a look. ![Contributing flow](./docs/imgs/contributing.png) Each stage of the process is aimed at creating feedback cycles which align contributors and maintainers to make sure: -* Contributors don’t waste their time implementing/proposing features which won’t land in master. -* Maintainers have the necessary context in order to support and review contributions. + +- Contributors don’t waste their time implementing/proposing features which won’t land in master. +- Maintainers have the necessary context in order to support and review contributions. ## Forking @@ -102,7 +103,7 @@ specify exactly the dependency you want to update, eg. We use [Protocol Buffers](https://developers.google.com/protocol-buffers) along with [gogoproto](https://github.com/gogo/protobuf) to generate code for use across Tendermint Core. -For linting and checking breaking changes, we use [buf](https://buf.build/). If you would like to run linting and check if the changes you have made are breaking then you will have to install the needed dependencies with `make buf`. Then the linting cmd will be `make proto-lint` and the breaking changes check will be `make proto-check-breaking`. +For linting and checking breaking changes, we use [buf](https://buf.build/). If you would like to run linting and check if the changes you have made are breaking then you will need to have docker running locally. Then the linting cmd will be `make proto-lint` and the breaking changes check will be `make proto-check-breaking`. To generate new stubs based off of your changes you can run `make proto-gen` after installing `protoc` and gogoproto. diff --git a/Makefile b/Makefile index a5b7db85a..e313ce7a3 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ BUILD_TAGS?='tendermint' LD_FLAGS = -X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD` -s -w BUILD_FLAGS = -mod=readonly -ldflags "$(LD_FLAGS)" HTTPS_GIT := https://github.com/tendermint/tendermint.git +DOCKER_BUF := docker run -v $(shell pwd):/workspace --workdir /workspace bufbuild/buf all: check build test install .PHONY: all @@ -54,15 +55,15 @@ proto-gen: .PHONY: proto-gen proto-lint: - @buf check lint --error-format=json + @$(DOCKER_BUF) check lint --error-format=json .PHONY: proto-lint proto-check-breaking: - @buf check breaking --against-input ".git#branch=master" + @$(DOCKER_BUF) check breaking --against-input .git#branch=master .PHONY: proto-check-breaking proto-check-breaking-ci: - @buf check breaking --against-input "$(HTTPS_GIT)#branch=master" + @$(DOCKER_BUF) check breaking --against-input $(HTTPS_GIT)#branch=master .PHONY: proto-check-breaking-ci ############################################################################### diff --git a/tools.mk b/tools.mk index 516fc494e..de722ae89 100644 --- a/tools.mk +++ b/tools.mk @@ -38,14 +38,8 @@ mkfile_dir := $(shell cd $(shell dirname $(mkfile_path)); pwd) # Go tools ### -BIN ?= /usr/local/bin -UNAME_S ?= $(shell uname -s) -UNAME_M ?= $(shell uname -m) - TOOLS_DESTDIR ?= $(GOPATH)/bin -BUF_VERSION ?= 0.7.0 - CERTSTRAP = $(TOOLS_DESTDIR)/certstrap PROTOBUF = $(TOOLS_DESTDIR)/protoc GOODMAN = $(TOOLS_DESTDIR)/goodman @@ -77,14 +71,6 @@ $(PROTOBUF): @go get github.com/gogo/protobuf/protoc-gen-gogo@v1.3.1 .PHONY: protobuf -buf: - @echo "Installing buf..." - @curl -sSL \ - "https://github.com/bufbuild/buf/releases/download/v$(BUF_VERSION)/buf-$(UNAME_S)-$(UNAME_M)" \ - -o "$(BIN)/buf" && \ - chmod +x "$(BIN)/buf" -.PHONY: buf - goodman: $(GOODMAN) $(GOODMAN): @echo "Get Goodman" From 53aa1ceba5cc1b3b7e818dcfa44daa310f51832a Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Fri, 27 Mar 2020 16:26:39 +0100 Subject: [PATCH 66/77] create style guide and update contributors section (#4576) --- CONTRIBUTING.md | 16 ++++- README.md | 17 +++--- STYLE_GUIDE.md | 159 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 182 insertions(+), 10 deletions(-) create mode 100644 STYLE_GUIDE.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2681b73ae..db4302056 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -191,9 +191,23 @@ easy to reference the pull request where a change was introduced. - ensure pull branch is based on a recent `master` - run `make test` to ensure that all tests pass -- squash merge pull request +- [squash](https://stackoverflow.com/questions/5189560/squash-my-last-x-commits-together-using-git) merge pull request - the `unstable` branch may be used to aggregate pull merges before fixing tests +### Git Commit Style + +We follow the [Go style guide on commit messages](https://tip.golang.org/doc/contribute.html#commit_messages). Write concise commits that start with the package name and have a description that finishes the sentence "This change modifies Tendermint to...". For example, + +\``` +cmd/debug: execute p.Signal only when p is not nil + +[potentially longer description in the body] + +Fixes #nnnn +\``` + +Each PR should have one commit once it lands on `master`; this can be accomplished by using the "squash and merge" button on Github. Be sure to edit your commit message, though! + ### Release Procedure #### Major Release diff --git a/README.md b/README.md index edfc9220f..3a5a9e237 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Or [Blockchain](), for shor [![version](https://img.shields.io/github/tag/tendermint/tendermint.svg)](https://github.com/tendermint/tendermint/releases/latest) [![API Reference](https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667)](https://godoc.org/github.com/tendermint/tendermint) [![Go version](https://img.shields.io/badge/go-1.13-blue.svg)](https://github.com/moovweb/gvm) -[![Discord](https://img.shields.io/discord/669268347736686612.svg)](https://discord.gg/AzefAFd) +[![Discord](https://img.shields.io/discord/669268347736686612.svg)](https://discord.gg/AzefAFd) [![license](https://img.shields.io/github/license/tendermint/tendermint.svg)](https://github.com/tendermint/tendermint/blob/master/LICENSE) [![](https://tokei.rs/b1/github/tendermint/tendermint?category=lines)](https://github.com/tendermint/tendermint) @@ -68,17 +68,16 @@ See the [install instructions](/docs/introduction/install.md) ## Contributing -Please abide by the [Code of Conduct](CODE_OF_CONDUCT.md) in all interactions, -and the [contributing guidelines](CONTRIBUTING.md) when submitting code. +Please abide by the [Code of Conduct](CODE_OF_CONDUCT.md) in all interactions. -Join the larger community on the [forum](https://forum.cosmos.network/) and the [chat](https://riot.im/app/#/room/#tendermint:matrix.org). +Before contributing to the project, please take a look at the [contributing guidelines](CONTRIBUTING.md) +and the [style guide](STYLE_GUIDE.md). -To learn more about the structure of the software, watch the [Developer -Sessions](/docs/DEV_SESSIONS.md) and read some [Architectural Decision -Records](https://github.com/tendermint/tendermint/tree/master/docs/architecture). +To get more active, Join the wider community at [Discord](https://discord.gg/AzefAFd) or jump onto the [Forum](https://forum.cosmos.network/). -Learn more by reading the code and comparing it to the -[specification](https://github.com/tendermint/spec). +Learn more by reading the code and the +[specifications](https://github.com/tendermint/spec) or watch the [Developer Sessions](/docs/DEV_SESSIONS.md) and read up on the +[Architectural Decision Records](https://github.com/tendermint/tendermint/tree/master/docs/architecture). ## Versioning diff --git a/STYLE_GUIDE.md b/STYLE_GUIDE.md new file mode 100644 index 000000000..6d0deda6d --- /dev/null +++ b/STYLE_GUIDE.md @@ -0,0 +1,159 @@ +# Go Coding Style Guide + +In order to keep our code looking good with lots of programmers working on it, it helps to have a "style guide", so all +the code generally looks quite similar. This doesn't mean there is only one "right way" to write code, or even that this +standard is better than your style. But if we agree to a number of stylistic practices, it makes it much easier to read +and modify new code. Please feel free to make suggestions if there's something you would like to add or modify. + +We expect all contributors to be familiar with [Effective Go](https://golang.org/doc/effective_go.html) +(and it's recommended reading for all Go programmers anyways). Additionally, we generally agree with the suggestions + in [Uber's style guide](https://github.com/uber-go/guide/blob/master/style.md) and use that as a starting point. + + +## Code Structure + +Perhaps more key for code readability than good commenting is having the right structure. As a rule of thumb, try to write +in a logical order of importance, taking a little time to think how to order and divide the code such that someone could +scroll down and understand the functionality of it just as well as you do. A loose example of such order would be: +* Constants, global and package-level variables +* Main Struct +* Options (only if they are seen as critical to the struct else they should be placed in another file) +* Initialization / Start and stop of the service +* Msgs/Events +* Public Functions (In order of most important) +* Private/helper functions +* Auxiliary structs and function (can also be above private functions or in a separate file) + +## General + + * Use `gofmt` (or `goimport`) to format all code upon saving it. (If you use VIM, check out vim-go). + * Use a linter (see below) and generally try to keep the linter happy (where it makes sense). + * Think about documentation, and try to leave godoc comments, when it will help new developers. + * Every package should have a high level doc.go file to describe the purpose of that package, its main functions, and any other relevant information. + * `TODO` should not be used. If important enough should be recorded as an issue. + * `BUG` / `FIXME` should be used sparingly to guide future developers on some of the vulnerabilities of the code. + * `XXX` can be used in work-in-progress (prefixed with "WIP:" on github) branches but they must be removed before approving a PR. + * Applications (e.g. clis/servers) *should* panic on unexpected unrecoverable errors and print a stack trace. + +## Comments + + * Use a space after comment deliminter (ex. `// your comment`). + * Many comments are not sentences. These should begin with a lower case letter and end without a period. + * Conversely, sentences in comments should be sentenced-cased and end with a period. + +## Linters + +These must be applied to all (Go) repos. + + * [shellcheck](https://github.com/koalaman/shellcheck) + * [golangci-lint](https://github.com/golangci/golangci-lint) (covers all important linters) + - See the `.golangci.yml` file in each repo for linter configuration. + +## Various + + * Reserve "Save" and "Load" for long-running persistence operations. When parsing bytes, use "Encode" or "Decode". + * Maintain consistency across the codebase. + * Functions that return functions should have the suffix `Fn` + * Names should not [stutter](https://blog.golang.org/package-names). For example, a struct generally shouldn’t have + a field named after itself; e.g., this shouldn't occur: +``` golang +type middleware struct { + middleware Middleware +} +``` + * In comments, use "iff" to mean, "if and only if". + * Product names are capitalized, like "Tendermint", "Basecoin", "Protobuf", etc except in command lines: `tendermint --help` + * Acronyms are all capitalized, like "RPC", "gRPC", "API". "MyID", rather than "MyId". + * Prefer errors.New() instead of fmt.Errorf() unless you're actually using the format feature with arguments. + +## Importing Libraries + +Sometimes it's necessary to rename libraries to avoid naming collisions or ambiguity. + + * Use [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports) + * Separate imports into blocks - one for the standard lib, one for external libs and one for application libs. + * Here are some common library labels for consistency: + - dbm "github.com/tendermint/tm-db" + - tmcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" + - tmcfg "github.com/tendermint/tendermint/config/tendermint" + - tmtypes "github.com/tendermint/tendermint/types" + * Never use anonymous imports (the `.`), for example, `tmlibs/common` or anything else. + * When importing a pkg from the `tendermint/libs` directory, prefix the pkg alias with tm. + - tmbits "github.com/tendermint/tendermint/libs/bits" + * tip: Use the `_` library import to import a library for initialization effects (side effects) + +## Dependencies + + * Dependencies should be pinned by a release tag, or specific commit, to avoid breaking `go get` when external dependencies are updated. + * Refer to the [contributing](CONTRIBUTING.md) document for more details + +## Testing + + * The first rule of testing is: we add tests to our code + * The second rule of testing is: we add tests to our code + * For Golang testing: + * Make use of table driven testing where possible and not-cumbersome + - [Inspiration](https://dave.cheney.net/2013/06/09/writing-table-driven-tests-in-go) + * Make use of [assert](https://godoc.org/github.com/stretchr/testify/assert) and [require](https://godoc.org/github.com/stretchr/testify/require) + * When using mocks, it is recommended to use Testify [mock] (https://pkg.go.dev/github.com/stretchr/testify/mock + ) along with [Mockery](https://github.com/vektra/mockery) for autogeneration + +## Errors + + * Ensure that errors are concise, clear and traceable. + * Use stdlib errors package. + * For wrapping errors, use `fmt.Errorf()` with `%w`. + * Panic is appropriate when an internal invariant of a system is broken, while all other cases (in particular, + incorrect or invalid usage) should return errors. + +## Config + + * Currently the TOML filetype is being used for config files + * A good practice is to store per-user config files under `~/.[yourAppName]/config.toml` + +## CLI + + * When implementing a CLI use [Cobra](https://github.com/spf13/cobra) and [Viper](https://github.com/spf13/viper). + * Helper messages for commands and flags must be all lowercase. + * Instead of using pointer flags (eg. `FlagSet().StringVar`) use Viper to retrieve flag values (eg. `viper.GetString`) + - The flag key used when setting and getting the flag should always be stored in a + variable taking the form `FlagXxx` or `flagXxx`. + - Flag short variable descriptions should always start with a lower case character as to remain consistent with + the description provided in the default `--help` flag. + +## Version + + * Every repo should have a version/version.go file that mimics the Tendermint Core repo + * We read the value of the constant version in our build scripts and hence it has to be a string + +## Non-Go Code + + * All non-Go code (`*.proto`, `Makefile`, `*.sh`), where there is no common + agreement on style, should be formatted according to + [EditorConfig](http://editorconfig.org/) config: + + ``` + # top-most EditorConfig file + root = true + + # Unix-style newlines with a newline ending every file + [*] + charset = utf-8 + end_of_line = lf + insert_final_newline = true + trim_trailing_whitespace = true + + [Makefile] + indent_style = tab + + [*.sh] + indent_style = tab + + [*.proto] + indent_style = space + indent_size = 2 + ``` + + Make sure the file above (`.editorconfig`) are in the root directory of your + repo and you have a [plugin for your + editor](http://editorconfig.org/#download) installed. From 7164c67205b86d516447715cdce7c658cf647485 Mon Sep 17 00:00:00 2001 From: Marko Date: Fri, 27 Mar 2020 17:17:23 +0100 Subject: [PATCH 67/77] docs: validator setup & Key info (#4604) - defined what variables needed to be changed in the `config.toml` in order to run a validator. - Briefly explained how a sentry node archtecture should look - add section explaing importance of key secruity Signed-off-by: Marko Baricevic --- docs/tendermint-core/local_config.png | Bin 0 -> 43143 bytes docs/tendermint-core/sentry_layout.png | Bin 0 -> 44471 bytes docs/tendermint-core/validators.md | 75 ++++++++++++++++++++++++- 3 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 docs/tendermint-core/local_config.png create mode 100644 docs/tendermint-core/sentry_layout.png diff --git a/docs/tendermint-core/local_config.png b/docs/tendermint-core/local_config.png new file mode 100644 index 0000000000000000000000000000000000000000..050a6df2fac297550b05afe2fefe3445bc9d94a3 GIT binary patch literal 43143 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 Note: Do not forget to secure your node's firewalls when setting them up. + +More Information can be found at these links: + +- https://kb.certus.one/ +- https://forum.cosmos.network/t/sentry-node-architecture-overview/454 + +### Validator keys + +Protecting a validator's consensus key is the most important factor to take in when designing your setup. The key that a validator is given upon creation of the node is called a consensus key, it has to be online at all times in order to vote on blocks. It is **not recommended** to merely hold your private key in the default json file (`priv_validator_key.json`). Fortunately, the [Interchain Foundation](https://interchain.io/) has worked with a team to build a key management server for validators. You can find documentation on how to use it [here](https://github.com/iqlusioninc/tmkms), it is used extensively in production. You are not limited to using this tool, there are also [HSMs](https://safenet.gemalto.com/data-encryption/hardware-security-modules-hsms/), there is not a recommended HSM. + +Currently Tendermint uses [Ed25519](https://ed25519.cr.yp.to/) keys which are widely supported across the security sector and HSMs. ## Committing a Block From ccf53a22760c92a02f1f20ed24a7ec9f510d7c5d Mon Sep 17 00:00:00 2001 From: Tess Rinearson Date: Fri, 27 Mar 2020 18:30:42 +0100 Subject: [PATCH 68/77] all: name reactors when they are initialized (#4608) Previously, many reactors were initialized with the name "Reactor," which made it difficult to log which reactor was doing what. This changes those reactors' names to something more descriptive. --- CHANGELOG_PENDING.md | 1 + consensus/reactor.go | 2 +- evidence/reactor.go | 2 +- mempool/reactor.go | 2 +- p2p/mock/reactor.go | 2 +- p2p/pex/pex_reactor.go | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 5e3dc64b0..44354b5c6 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -22,6 +22,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi - [privval] \#4534 Add `error` as a return value on`GetPubKey()` - [Docker] \#4569 Default configuration added to docker image (you can still mount your own config the same way) (@greg-szabo) - [lite2] [\#4562](https://github.com/tendermint/tendermint/pull/4562) Cache headers when using bisection (@cmwaters) +- [all] [\4608](https://github.com/tendermint/tendermint/pull/4608) Give reactors descriptive names when they're initialized ### BUG FIXES: diff --git a/consensus/reactor.go b/consensus/reactor.go index 61e46c7c4..3182bba89 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -58,7 +58,7 @@ func NewReactor(consensusState *State, fastSync bool, options ...ReactorOption) metrics: NopMetrics(), } conR.updateFastSyncingMetric() - conR.BaseReactor = *p2p.NewBaseReactor("Reactor", conR) + conR.BaseReactor = *p2p.NewBaseReactor("Consensus", conR) for _, option := range options { option(conR) diff --git a/evidence/reactor.go b/evidence/reactor.go index 19ad0f135..e4dbd51ad 100644 --- a/evidence/reactor.go +++ b/evidence/reactor.go @@ -34,7 +34,7 @@ func NewReactor(evpool *Pool) *Reactor { evR := &Reactor{ evpool: evpool, } - evR.BaseReactor = *p2p.NewBaseReactor("Reactor", evR) + evR.BaseReactor = *p2p.NewBaseReactor("Evidence", evR) return evR } diff --git a/mempool/reactor.go b/mempool/reactor.go index 161fc7212..1ae3592a0 100644 --- a/mempool/reactor.go +++ b/mempool/reactor.go @@ -110,7 +110,7 @@ func NewReactor(config *cfg.MempoolConfig, mempool *CListMempool) *Reactor { mempool: mempool, ids: newMempoolIDs(), } - memR.BaseReactor = *p2p.NewBaseReactor("Reactor", memR) + memR.BaseReactor = *p2p.NewBaseReactor("Mempool", memR) return memR } diff --git a/p2p/mock/reactor.go b/p2p/mock/reactor.go index cfce12bd1..40f918e9f 100644 --- a/p2p/mock/reactor.go +++ b/p2p/mock/reactor.go @@ -12,7 +12,7 @@ type Reactor struct { func NewReactor() *Reactor { r := &Reactor{} - r.BaseReactor = *p2p.NewBaseReactor("Reactor", r) + r.BaseReactor = *p2p.NewBaseReactor("Mock-PEX", r) r.SetLogger(log.TestingLogger()) return r } diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index b0aafe0be..192e75fa1 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -141,7 +141,7 @@ func NewReactor(b AddrBook, config *ReactorConfig) *Reactor { lastReceivedRequests: cmap.NewCMap(), crawlPeerInfos: make(map[p2p.ID]crawlPeerInfo), } - r.BaseReactor = *p2p.NewBaseReactor("Reactor", r) + r.BaseReactor = *p2p.NewBaseReactor("PEX", r) return r } From 94f5321fa3d1814561155f646af400470924017c Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2020 13:49:29 +0000 Subject: [PATCH 69/77] build(deps): bump github.com/spf13/cobra from 0.0.6 to 0.0.7 (#4612) Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 0.0.6 to 0.0.7. - [Release notes](https://github.com/spf13/cobra/releases) - [Commits](https://github.com/spf13/cobra/compare/v0.0.6...0.0.7) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 1889d4d03..b7af0f90e 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563 github.com/rs/cors v1.7.0 github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa - github.com/spf13/cobra v0.0.6 + github.com/spf13/cobra v0.0.7 github.com/spf13/viper v1.6.2 github.com/stretchr/testify v1.5.1 github.com/tendermint/go-amino v0.14.1 diff --git a/go.sum b/go.sum index df25917db..64a6314db 100644 --- a/go.sum +++ b/go.sum @@ -385,6 +385,8 @@ github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs= github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v0.0.7 h1:FfTH+vuMXOas8jmfb5/M7dzEYx7LpcLb7a0LPe34uOU= +github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= From 9edf17a85caabd7d25df1750fc0de4a5dc947497 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2020 14:05:42 +0000 Subject: [PATCH 70/77] build(deps): bump github.com/tendermint/tm-db from 0.5.0 to 0.5.1 (#4613) Bumps [github.com/tendermint/tm-db](https://github.com/tendermint/tm-db) from 0.5.0 to 0.5.1. - [Release notes](https://github.com/tendermint/tm-db/releases) - [Changelog](https://github.com/tendermint/tm-db/blob/master/CHANGELOG.md) - [Commits](https://github.com/tendermint/tm-db/compare/v0.5.0...v0.5.1) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index b7af0f90e..8c2eb4637 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/spf13/viper v1.6.2 github.com/stretchr/testify v1.5.1 github.com/tendermint/go-amino v0.14.1 - github.com/tendermint/tm-db v0.5.0 + github.com/tendermint/tm-db v0.5.1 golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 golang.org/x/net v0.0.0-20200301022130-244492dfa37a google.golang.org/grpc v1.28.0 diff --git a/go.sum b/go.sum index 64a6314db..7f28d157d 100644 --- a/go.sum +++ b/go.sum @@ -418,6 +418,8 @@ github.com/tendermint/go-amino v0.14.1 h1:o2WudxNfdLNBwMyl2dqOJxiro5rfrEaU0Ugs6o github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= github.com/tendermint/tm-db v0.5.0 h1:qtM5UTr1dlRnHtDY6y7MZO5Di8XAE2j3lc/pCnKJ5hQ= github.com/tendermint/tm-db v0.5.0/go.mod h1:lSq7q5WRR/njf1LnhiZ/lIJHk2S8Y1Zyq5oP/3o9C2U= +github.com/tendermint/tm-db v0.5.1 h1:H9HDq8UEA7Eeg13kdYckkgwwkQLBnJGgX4PgLJRhieY= +github.com/tendermint/tm-db v0.5.1/go.mod h1:g92zWjHpCYlEvQXvy9M168Su8V1IBEeawpXVVBaK4f4= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= From f5651d2b616cd1c0cabed42e298fa605cf05e216 Mon Sep 17 00:00:00 2001 From: Marko Date: Mon, 30 Mar 2020 16:48:48 +0200 Subject: [PATCH 71/77] proto: use docker to generate stubs (#4615) * proto: use docker to generate stubs - provide an option to developers to use docker to generate proto stubs closes #4579 Signed-off-by: Marko Baricevic --- CHANGELOG_PENDING.md | 3 ++- CONTRIBUTING.md | 5 ++++- Makefile | 5 +++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 44354b5c6..4ec4db785 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -22,7 +22,8 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi - [privval] \#4534 Add `error` as a return value on`GetPubKey()` - [Docker] \#4569 Default configuration added to docker image (you can still mount your own config the same way) (@greg-szabo) - [lite2] [\#4562](https://github.com/tendermint/tendermint/pull/4562) Cache headers when using bisection (@cmwaters) -- [all] [\4608](https://github.com/tendermint/tendermint/pull/4608) Give reactors descriptive names when they're initialized +- [all] [\#4608](https://github.com/tendermint/tendermint/pull/4608) Give reactors descriptive names when they're initialized +- [tools] \#4615 Allow developers to use Docker to generate proto stubs, via `make proto-gen-docker`. ### BUG FIXES: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index db4302056..e057c32a8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -105,7 +105,10 @@ We use [Protocol Buffers](https://developers.google.com/protocol-buffers) along For linting and checking breaking changes, we use [buf](https://buf.build/). If you would like to run linting and check if the changes you have made are breaking then you will need to have docker running locally. Then the linting cmd will be `make proto-lint` and the breaking changes check will be `make proto-check-breaking`. -To generate new stubs based off of your changes you can run `make proto-gen` after installing `protoc` and gogoproto. +There are two ways to generate your proto stubs. + +1. Use Docker, pull an image that will generate your proto stubs with no need to install anything. `make proto-gen-docker` +2. Run `make proto-gen` after installing `protoc` and gogoproto. ### Installation Instructions diff --git a/Makefile b/Makefile index e313ce7a3..eb9626bf4 100644 --- a/Makefile +++ b/Makefile @@ -54,6 +54,11 @@ proto-gen: @sh scripts/protocgen.sh .PHONY: proto-gen +proto-gen-docker: + @echo "Generating Protobuf files" + @docker run -v $(shell pwd):/workspace --workdir /workspace tendermintdev/docker-build-proto sh ./scripts/protocgen.sh +.PHONY: proto-gen-docker + proto-lint: @$(DOCKER_BUF) check lint --error-format=json .PHONY: proto-lint From bcf27c2364b9a235151474e7cd650e881f484495 Mon Sep 17 00:00:00 2001 From: Yongwoo Lee Date: Tue, 31 Mar 2020 16:49:28 +0900 Subject: [PATCH 72/77] rpc: add codespace to ResultBroadcastTx (#4611) Closes: #4606 --- CHANGELOG_PENDING.md | 2 ++ rpc/client/mock/abci.go | 16 ++++++++++++++-- rpc/core/mempool.go | 9 +++++---- rpc/core/types/responses.go | 7 ++++--- rpc/swagger/swagger.yaml | 3 +++ 5 files changed, 28 insertions(+), 9 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 4ec4db785..6aeb1007a 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -16,6 +16,8 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi ### FEATURES: +- [rpc] [\#4611](https://github.com/tendermint/tendermint/pull/4611) Add `codespace` to `ResultBroadcastTx` (@whylee259) + ### IMPROVEMENTS: - [p2p] [\#4548](https://github.com/tendermint/tendermint/pull/4548) Add ban list to address book (@cmwaters) diff --git a/rpc/client/mock/abci.go b/rpc/client/mock/abci.go index d1f84f2b1..ebee8b4e8 100644 --- a/rpc/client/mock/abci.go +++ b/rpc/client/mock/abci.go @@ -63,7 +63,13 @@ func (a ABCIApp) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error if !c.IsErr() { go func() { a.App.DeliverTx(abci.RequestDeliverTx{Tx: tx}) }() // nolint: errcheck } - return &ctypes.ResultBroadcastTx{Code: c.Code, Data: c.Data, Log: c.Log, Hash: tx.Hash()}, nil + return &ctypes.ResultBroadcastTx{ + Code: c.Code, + Data: c.Data, + Log: c.Log, + Codespace: c.Codespace, + Hash: tx.Hash(), + }, nil } func (a ABCIApp) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { @@ -72,7 +78,13 @@ func (a ABCIApp) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) if !c.IsErr() { go func() { a.App.DeliverTx(abci.RequestDeliverTx{Tx: tx}) }() // nolint: errcheck } - return &ctypes.ResultBroadcastTx{Code: c.Code, Data: c.Data, Log: c.Log, Hash: tx.Hash()}, nil + return &ctypes.ResultBroadcastTx{ + Code: c.Code, + Data: c.Data, + Log: c.Log, + Codespace: c.Codespace, + Hash: tx.Hash(), + }, nil } // ABCIMock will send all abci related request to the named app, diff --git a/rpc/core/mempool.go b/rpc/core/mempool.go index 28b73ab33..0e6ce7a2c 100644 --- a/rpc/core/mempool.go +++ b/rpc/core/mempool.go @@ -43,10 +43,11 @@ func BroadcastTxSync(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcas res := <-resCh r := res.GetCheckTx() return &ctypes.ResultBroadcastTx{ - Code: r.Code, - Data: r.Data, - Log: r.Log, - Hash: tx.Hash(), + Code: r.Code, + Data: r.Data, + Log: r.Log, + Codespace: r.Codespace, + Hash: tx.Hash(), }, nil } diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index 9aee485e9..ed435bbe0 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -148,9 +148,10 @@ type ResultConsensusState struct { // CheckTx result type ResultBroadcastTx struct { - Code uint32 `json:"code"` - Data bytes.HexBytes `json:"data"` - Log string `json:"log"` + Code uint32 `json:"code"` + Data bytes.HexBytes `json:"data"` + Log string `json:"log"` + Codespace string `json:"codespace"` Hash bytes.HexBytes `json:"hash"` } diff --git a/rpc/swagger/swagger.yaml b/rpc/swagger/swagger.yaml index 40b6e0169..00b137bf0 100644 --- a/rpc/swagger/swagger.yaml +++ b/rpc/swagger/swagger.yaml @@ -2947,6 +2947,9 @@ components: log: type: "string" example: "" + codespace: + type: "string" + example: "ibc" hash: type: "string" example: "0D33F2F03A5234F38706E43004489E061AC40A2E" From 5c380cdacbcbd25461080a1d3724e8e1509f1766 Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Tue, 31 Mar 2020 14:20:22 +0200 Subject: [PATCH 73/77] lite2: use bisection for some of backward verification (#4575) Closes: #4537 Uses SignedHeaderBefore to find header before unverified header and then bisection to verify the header. Only when header is between first and last trusted header height else if before the first trusted header height then regular backwards verification is used. --- CHANGELOG_PENDING.md | 1 + .../adr-046-light-client-implementation.md | 5 +- lite2/client.go | 141 +++++++++++------- lite2/client_benchmark_test.go | 18 +-- lite2/client_test.go | 62 ++++---- lite2/errors.go | 8 + lite2/store/db/db.go | 12 +- lite2/store/db/db_test.go | 10 +- lite2/store/store.go | 4 +- 9 files changed, 157 insertions(+), 104 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 6aeb1007a..fe8f4a48e 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -25,6 +25,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi - [Docker] \#4569 Default configuration added to docker image (you can still mount your own config the same way) (@greg-szabo) - [lite2] [\#4562](https://github.com/tendermint/tendermint/pull/4562) Cache headers when using bisection (@cmwaters) - [all] [\#4608](https://github.com/tendermint/tendermint/pull/4608) Give reactors descriptive names when they're initialized +- [lite2] [\#4575](https://github.com/tendermint/tendermint/pull/4575) Use bisection for within-range verification (@cmwaters) - [tools] \#4615 Allow developers to use Docker to generate proto stubs, via `make proto-gen-docker`. ### BUG FIXES: diff --git a/docs/architecture/adr-046-light-client-implementation.md b/docs/architecture/adr-046-light-client-implementation.md index 37a7c83c5..a1e322a9c 100644 --- a/docs/architecture/adr-046-light-client-implementation.md +++ b/docs/architecture/adr-046-light-client-implementation.md @@ -60,8 +60,9 @@ also cross-checked with witnesses for additional security. Due to bisection algorithm nature, some headers might be skipped. If the light client does not have a header for height `X` and `VerifyHeaderAtHeight(X)` or -`VerifyHeader(H#X)` methods are called, it will perform a backwards -verification from the latest header back to the header at height `X`. +`VerifyHeader(H#X)` methods are called, these will perform either a) backwards +verification from the latest header back to the header at height `X` or b) +bisection verification from the first stored header to the header at height `X`. `TrustedHeader`, `TrustedValidatorSet` only communicate with the trusted store. If some header is not there, an error will be returned indicating that diff --git a/lite2/client.go b/lite2/client.go index 14c07e511..b93c54b8b 100644 --- a/lite2/client.go +++ b/lite2/client.go @@ -149,7 +149,7 @@ func NewClient( options ...Option) (*Client, error) { if err := trustOptions.ValidateBasic(); err != nil { - return nil, errors.Wrap(err, "invalid TrustOptions") + return nil, fmt.Errorf("invalid TrustOptions: %w", err) } c, err := NewClientFromTrustedStore(chainID, trustOptions.Period, primary, witnesses, trustedStore, options...) @@ -206,13 +206,13 @@ func NewClientFromTrustedStore( // Validate the number of witnesses. if len(c.witnesses) < 1 { - return nil, errors.New("expected at least one witness") + return nil, errNoWitnesses{} } // Verify witnesses are all on the same chain. for i, w := range witnesses { if w.ChainID() != chainID { - return nil, errors.Errorf("witness #%d: %v is on another chain %s, expected %s", + return nil, fmt.Errorf("witness #%d: %v is on another chain %s, expected %s", i, w, w.ChainID(), chainID) } } @@ -234,18 +234,18 @@ func NewClientFromTrustedStore( func (c *Client) restoreTrustedHeaderAndVals() error { lastHeight, err := c.trustedStore.LastSignedHeaderHeight() if err != nil { - return errors.Wrap(err, "can't get last trusted header height") + return fmt.Errorf("can't get last trusted header height: %w", err) } if lastHeight > 0 { trustedHeader, err := c.trustedStore.SignedHeader(lastHeight) if err != nil { - return errors.Wrap(err, "can't get last trusted header") + return fmt.Errorf("can't get last trusted header: %w", err) } trustedVals, err := c.trustedStore.ValidatorSet(lastHeight) if err != nil { - return errors.Wrap(err, "can't get last trusted validators") + return fmt.Errorf("can't get last trusted validators: %w", err) } c.latestTrustedHeader = trustedHeader @@ -300,7 +300,7 @@ func (c *Client) checkTrustedHeaderUsingOptions(options TrustOptions) error { // remove all the headers (options.Height, trustedHeader.Height] err := c.cleanupAfter(options.Height) if err != nil { - return errors.Wrapf(err, "cleanupAfter(%d)", options.Height) + return fmt.Errorf("cleanupAfter(%d): %w", options.Height, err) } c.logger.Info("Rolled back to older header (newer headers were removed)", @@ -322,7 +322,7 @@ func (c *Client) checkTrustedHeaderUsingOptions(options TrustOptions) error { if c.confirmationFn(action) { err := c.Cleanup() if err != nil { - return errors.Wrap(err, "failed to cleanup") + return fmt.Errorf("failed to cleanup: %w", err) } } else { return errors.New("refused to remove the stored headers despite hashes mismatch") @@ -350,7 +350,7 @@ func (c *Client) initializeWithTrustOptions(options TrustOptions) error { } if !bytes.Equal(h.Hash(), options.Hash) { - return errors.Errorf("expected header's hash %X, but got %X", options.Hash, h.Hash()) + return fmt.Errorf("expected header's hash %X, but got %X", options.Hash, h.Hash()) } err = c.compareNewHeaderWithWitnesses(h) @@ -365,7 +365,7 @@ func (c *Client) initializeWithTrustOptions(options TrustOptions) error { } if !bytes.Equal(h.ValidatorsHash, vals.Hash()) { - return errors.Errorf("expected header's validators (%X) to match those that were supplied (%X)", + return fmt.Errorf("expected header's validators (%X) to match those that were supplied (%X)", h.ValidatorsHash, vals.Hash(), ) @@ -374,7 +374,7 @@ func (c *Client) initializeWithTrustOptions(options TrustOptions) error { // Ensure that +2/3 of validators signed correctly. err = vals.VerifyCommit(c.chainID, h.Commit.BlockID, h.Height, h.Commit) if err != nil { - return errors.Wrap(err, "invalid commit") + return fmt.Errorf("invalid commit: %w", err) } // 3) Persist both of them and continue. @@ -436,7 +436,7 @@ func (c *Client) TrustedValidatorSet(height int64) (valSet *types.ValidatorSet, func (c *Client) compareWithLatestHeight(height int64) (int64, error) { latestHeight, err := c.LastTrustedHeight() if err != nil { - return 0, errors.Wrap(err, "can't get last trusted height") + return 0, fmt.Errorf("can't get last trusted height: %w", err) } if latestHeight == -1 { return 0, errors.New("no headers exist") @@ -444,7 +444,7 @@ func (c *Client) compareWithLatestHeight(height int64) (int64, error) { switch { case height > latestHeight: - return 0, errors.Errorf("unverified header/valset requested (latest: %d)", latestHeight) + return 0, fmt.Errorf("unverified header/valset requested (latest: %d)", latestHeight) case height == 0: return latestHeight, nil case height < 0: @@ -499,6 +499,11 @@ func (c *Client) VerifyHeaderAtHeight(height int64, now time.Time) (*types.Signe // Intermediate headers are not saved to database. // https://github.com/tendermint/spec/blob/master/spec/consensus/light-client.md // +// If the header, which is older than the currently trusted header, is +// requested and the light client does not have it, VerifyHeader will perform: +// a) bisection verification if nearest trusted header is found & not expired +// b) backwards verification in all other cases +// // It returns ErrOldHeaderExpired if the latest trusted header expired. // // If the primary provides an invalid header (ErrInvalidHeader), it is rejected @@ -517,7 +522,7 @@ func (c *Client) VerifyHeader(newHeader *types.SignedHeader, newVals *types.Vali if err == nil { // Make sure it's the same header. if !bytes.Equal(h.Hash(), newHeader.Hash()) { - return errors.Errorf("existing trusted header %X does not match newHeader %X", h.Hash(), newHeader.Hash()) + return fmt.Errorf("existing trusted header %X does not match newHeader %X", h.Hash(), newHeader.Hash()) } c.logger.Info("Header has already been verified", "height", newHeader.Height, "hash", hash2str(newHeader.Hash())) @@ -533,7 +538,7 @@ func (c *Client) verifyHeader(newHeader *types.SignedHeader, newVals *types.Vali var err error - // 1) If going forward, perform either bisection or sequential verification + // 1) If going forward, perform either bisection or sequential verification. if newHeader.Height >= c.latestTrustedHeader.Height { switch c.verificationMode { case sequential: @@ -544,26 +549,55 @@ func (c *Client) verifyHeader(newHeader *types.SignedHeader, newVals *types.Vali panic(fmt.Sprintf("Unknown verification mode: %b", c.verificationMode)) } } else { - // 2) Otherwise, perform backwards verification - // Find the closest trusted header after newHeader.Height - var closestHeader *types.SignedHeader - closestHeader, err = c.trustedStore.SignedHeaderAfter(newHeader.Height) + // 2) If verifying before the first trusted header, perform backwards + // verification. + var ( + closestHeader *types.SignedHeader + firstHeaderHeight int64 + ) + firstHeaderHeight, err = c.FirstTrustedHeight() if err != nil { - return errors.Wrapf(err, "can't get signed header after height %d", newHeader.Height) + return fmt.Errorf("can't get first header height: %w", err) + } + if newHeader.Height < firstHeaderHeight { + closestHeader, err = c.TrustedHeader(firstHeaderHeight) + if err != nil { + return fmt.Errorf("can't get first signed header: %w", err) + } + if HeaderExpired(closestHeader, c.trustingPeriod, now) { + closestHeader = c.latestTrustedHeader + } + err = c.backwards(closestHeader, newHeader, now) + } else { + // 3) OR if between trusted headers where the nearest has not expired, + // perform bisection verification, else backwards. + closestHeader, err = c.trustedStore.SignedHeaderBefore(newHeader.Height) + if err != nil { + return fmt.Errorf("can't get signed header before height %d: %w", newHeader.Height, err) + } + var closestValidatorSet *types.ValidatorSet + if c.verificationMode == sequential || HeaderExpired(closestHeader, c.trustingPeriod, now) { + err = c.backwards(c.latestTrustedHeader, newHeader, now) + } else { + closestValidatorSet, _, err = c.TrustedValidatorSet(closestHeader.Height) + if err != nil { + return fmt.Errorf("can't get validator set at height %d: %w", closestHeader.Height, err) + } + err = c.bisection(closestHeader, closestValidatorSet, newHeader, newVals, now) + } } - - err = c.backwards(closestHeader, newHeader, now) } if err != nil { c.logger.Error("Can't verify", "err", err) return err } - + // 4) Compare header with other witnesses if err := c.compareNewHeaderWithWitnesses(newHeader); err != nil { c.logger.Error("Error when comparing new header with witnesses", "err", err) return err } + // 5) Once verified, save and return return c.updateTrustedHeaderAndVals(newHeader, newVals) } @@ -590,7 +624,7 @@ func (c *Client) sequence( } else { // intermediate headers interimHeader, interimVals, err = c.fetchHeaderAndValsAtHeight(height) if err != nil { - return errors.Wrapf(err, "failed to obtain the header #%d", height) + return err } } @@ -604,10 +638,10 @@ func (c *Client) sequence( err = VerifyAdjacent(c.chainID, trustedHeader, interimHeader, interimVals, c.trustingPeriod, now) if err != nil { - err = errors.Wrapf(err, "verify adjacent from #%d to #%d failed", - trustedHeader.Height, interimHeader.Height) + err = fmt.Errorf("verify adjacent from #%d to #%d failed: %w", + trustedHeader.Height, interimHeader.Height, err) - switch errors.Cause(err).(type) { + switch errors.Unwrap(err).(type) { case ErrInvalidHeader: c.logger.Error("primary sent invalid header -> replacing", "err", err) replaceErr := c.replacePrimaryProvider() @@ -696,15 +730,15 @@ func (c *Client) bisection( if replaceErr != nil { c.logger.Error("Can't replace primary", "err", replaceErr) // return original error - return errors.Wrapf(err, "verify from #%d to #%d failed", - trustedHeader.Height, headerCache[depth].sh.Height) + return fmt.Errorf("verify non adjacent from #%d to #%d failed: %w", + trustedHeader.Height, headerCache[depth].sh.Height, err) } // attempt to verify the header again continue default: - return errors.Wrapf(err, "verify from #%d to #%d failed", - trustedHeader.Height, headerCache[depth].sh.Height) + return fmt.Errorf("verify non adjacent from #%d to #%d failed: %w", + trustedHeader.Height, headerCache[depth].sh.Height, err) } } } @@ -762,14 +796,14 @@ func (c *Client) Cleanup() error { // cleanupAfter deletes all headers & validator sets after +height+. It also // resets latestTrustedHeader to the latest header. func (c *Client) cleanupAfter(height int64) error { - nextHeight := height + prevHeight := c.latestTrustedHeader.Height for { - h, err := c.trustedStore.SignedHeaderAfter(nextHeight) - if err == store.ErrSignedHeaderNotFound { + h, err := c.trustedStore.SignedHeaderBefore(prevHeight) + if err == store.ErrSignedHeaderNotFound || (h != nil && h.Height <= height) { break } else if err != nil { - return errors.Wrapf(err, "failed to get header after %d", nextHeight) + return fmt.Errorf("failed to get header before %d: %w", prevHeight, err) } err = c.trustedStore.DeleteSignedHeaderAndValidatorSet(h.Height) @@ -778,7 +812,7 @@ func (c *Client) cleanupAfter(height int64) error { "height", h.Height) } - nextHeight = h.Height + prevHeight = h.Height } c.latestTrustedHeader = nil @@ -793,16 +827,16 @@ func (c *Client) cleanupAfter(height int64) error { func (c *Client) updateTrustedHeaderAndVals(h *types.SignedHeader, vals *types.ValidatorSet) error { if !bytes.Equal(h.ValidatorsHash, vals.Hash()) { - return errors.Errorf("expected validator's hash %X, but got %X", h.ValidatorsHash, vals.Hash()) + return fmt.Errorf("expected validator's hash %X, but got %X", h.ValidatorsHash, vals.Hash()) } if err := c.trustedStore.SaveSignedHeaderAndValidatorSet(h, vals); err != nil { - return errors.Wrap(err, "failed to save trusted header") + return fmt.Errorf("failed to save trusted header: %w", err) } if c.pruningSize > 0 { if err := c.trustedStore.Prune(c.pruningSize); err != nil { - return errors.Wrap(err, "prune") + return fmt.Errorf("prune: %w", err) } } @@ -819,11 +853,11 @@ func (c *Client) updateTrustedHeaderAndVals(h *types.SignedHeader, vals *types.V func (c *Client) fetchHeaderAndValsAtHeight(height int64) (*types.SignedHeader, *types.ValidatorSet, error) { h, err := c.signedHeaderFromPrimary(height) if err != nil { - return nil, nil, errors.Wrapf(err, "failed to obtain the header #%d", height) + return nil, nil, fmt.Errorf("failed to obtain the header #%d: %w", height, err) } vals, err := c.validatorSetFromPrimary(height) if err != nil { - return nil, nil, errors.Wrapf(err, "failed to obtain the vals #%d", height) + return nil, nil, fmt.Errorf("failed to obtain the vals #%d: %w", height, err) } return h, vals, nil } @@ -837,6 +871,7 @@ func (c *Client) backwards( now time.Time) error { if HeaderExpired(initiallyTrustedHeader, c.trustingPeriod, now) { + c.logger.Error("Header Expired") return ErrOldHeaderExpired{initiallyTrustedHeader.Time.Add(c.trustingPeriod), now} } @@ -849,16 +884,20 @@ func (c *Client) backwards( for trustedHeader.Height > newHeader.Height { interimHeader, err = c.signedHeaderFromPrimary(trustedHeader.Height - 1) if err != nil { - return errors.Wrapf(err, "failed to obtain the header at height #%d", trustedHeader.Height-1) + return fmt.Errorf("failed to obtain the header at height #%d: %w", trustedHeader.Height-1, err) } - + c.logger.Debug("Verify newHeader against trustedHeader", + "trustedHeight", trustedHeader.Height, + "trustedHash", hash2str(trustedHeader.Hash()), + "newHeight", interimHeader.Height, + "newHash", hash2str(interimHeader.Hash())) if err := VerifyBackwards(c.chainID, interimHeader, trustedHeader); err != nil { c.logger.Error("primary sent invalid header -> replacing", "err", err) if replaceErr := c.replacePrimaryProvider(); replaceErr != nil { c.logger.Error("Can't replace primary", "err", replaceErr) // return original error - return errors.Wrapf(err, "verify backwards from %d to %d failed", - trustedHeader.Height, interimHeader.Height) + return fmt.Errorf("verify backwards from %d to %d failed: %w", + trustedHeader.Height, interimHeader.Height, err) } } @@ -883,7 +922,7 @@ func (c *Client) compareNewHeaderWithWitnesses(h *types.SignedHeader) error { witnessesToRemove := make([]int, 0) for attempt := uint16(1); attempt <= c.maxRetryAttempts; attempt++ { if len(c.witnesses) == 0 { - return errors.New("could not find any witnesses. please reset the light client") + return errNoWitnesses{} } for i, witness := range c.witnesses { @@ -909,7 +948,7 @@ func (c *Client) compareNewHeaderWithWitnesses(h *types.SignedHeader) error { // TODO: send the diverged headers to primary && all witnesses - return errors.Errorf( + return fmt.Errorf( "header hash %X does not match one %X from the witness %v", h.Hash(), altH.Hash(), witness) } @@ -952,7 +991,7 @@ func (c *Client) removeWitness(idx int) { func (c *Client) Update(now time.Time) (*types.SignedHeader, error) { lastTrustedHeight, err := c.LastTrustedHeight() if err != nil { - return nil, errors.Wrap(err, "can't get last trusted height") + return nil, fmt.Errorf("can't get last trusted height: %w", err) } if lastTrustedHeight == -1 { @@ -962,7 +1001,7 @@ func (c *Client) Update(now time.Time) (*types.SignedHeader, error) { latestHeader, latestVals, err := c.fetchHeaderAndValsAtHeight(0) if err != nil { - return nil, errors.Wrapf(err, "can't get latest header and vals") + return nil, err } if latestHeader.Height > lastTrustedHeight { @@ -984,7 +1023,7 @@ func (c *Client) replacePrimaryProvider() error { defer c.providerMutex.Unlock() if len(c.witnesses) <= 1 { - return errors.Errorf("only one witness left. please reset the light client") + return errNoWitnesses{} } c.primary = c.witnesses[0] c.witnesses = c.witnesses[1:] @@ -1004,7 +1043,7 @@ func (c *Client) signedHeaderFromPrimary(height int64) (*types.SignedHeader, err if err == nil { // sanity check if height > 0 && h.Height != height { - return nil, errors.Errorf("expected %d height, got %d", height, h.Height) + return nil, fmt.Errorf("expected %d height, got %d", height, h.Height) } return h, nil } diff --git a/lite2/client_benchmark_test.go b/lite2/client_benchmark_test.go index 276c962a1..97b140ea5 100644 --- a/lite2/client_benchmark_test.go +++ b/lite2/client_benchmark_test.go @@ -20,8 +20,8 @@ import ( // // Remember that none of these benchmarks account for network latency. var ( - largeFullNode = mockp.New(GenMockNode(chainID, 1000, 100, 1, bTime)) - genesisHeader, _ = largeFullNode.SignedHeader(1) + benchmarkFullNode = mockp.New(GenMockNode(chainID, 1000, 100, 1, bTime)) + genesisHeader, _ = benchmarkFullNode.SignedHeader(1) ) func BenchmarkSequence(b *testing.B) { @@ -32,8 +32,8 @@ func BenchmarkSequence(b *testing.B) { Height: 1, Hash: genesisHeader.Hash(), }, - largeFullNode, - []provider.Provider{largeFullNode}, + benchmarkFullNode, + []provider.Provider{benchmarkFullNode}, dbs.New(dbm.NewMemDB(), chainID), Logger(log.TestingLogger()), SequentialVerification(), @@ -59,8 +59,8 @@ func BenchmarkBisection(b *testing.B) { Height: 1, Hash: genesisHeader.Hash(), }, - largeFullNode, - []provider.Provider{largeFullNode}, + benchmarkFullNode, + []provider.Provider{benchmarkFullNode}, dbs.New(dbm.NewMemDB(), chainID), Logger(log.TestingLogger()), ) @@ -78,7 +78,7 @@ func BenchmarkBisection(b *testing.B) { } func BenchmarkBackwards(b *testing.B) { - trustedHeader, _ := largeFullNode.SignedHeader(0) + trustedHeader, _ := benchmarkFullNode.SignedHeader(0) c, err := NewClient( chainID, TrustOptions{ @@ -86,8 +86,8 @@ func BenchmarkBackwards(b *testing.B) { Height: trustedHeader.Height, Hash: trustedHeader.Hash(), }, - largeFullNode, - []provider.Provider{largeFullNode}, + benchmarkFullNode, + []provider.Provider{benchmarkFullNode}, dbs.New(dbm.NewMemDB(), chainID), Logger(log.TestingLogger()), ) diff --git a/lite2/client_test.go b/lite2/client_test.go index 65ea55122..9481fb0b5 100644 --- a/lite2/client_test.go +++ b/lite2/client_test.go @@ -57,7 +57,8 @@ var ( headerSet, valSet, ) - deadNode = mockp.NewDeadMock(chainID) + deadNode = mockp.NewDeadMock(chainID) + largeFullNode = mockp.New(GenMockNode(chainID, 10, 3, 0, bTime)) ) func TestClient_SequentialVerification(t *testing.T) { @@ -682,62 +683,65 @@ func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) { func TestClient_BackwardsVerification(t *testing.T) { { + trustHeader, _ := largeFullNode.SignedHeader(6) c, err := NewClient( chainID, TrustOptions{ - Period: 1 * time.Hour, - Height: 3, - Hash: h3.Hash(), + Period: 4 * time.Minute, + Height: trustHeader.Height, + Hash: trustHeader.Hash(), }, - fullNode, - []provider.Provider{fullNode}, + largeFullNode, + []provider.Provider{largeFullNode}, dbs.New(dbm.NewMemDB(), chainID), Logger(log.TestingLogger()), ) require.NoError(t, err) - // 1) header is missing => expect no error - h, err := c.VerifyHeaderAtHeight(2, bTime.Add(1*time.Hour).Add(1*time.Second)) + // 1) verify before the trusted header using backwards => expect no error + h, err := c.VerifyHeaderAtHeight(5, bTime.Add(6*time.Minute)) require.NoError(t, err) if assert.NotNil(t, h) { - assert.EqualValues(t, 2, h.Height) + assert.EqualValues(t, 5, h.Height) } // 2) untrusted header is expired but trusted header is not => expect no error - h, err = c.VerifyHeaderAtHeight(1, bTime.Add(1*time.Hour).Add(1*time.Second)) + h, err = c.VerifyHeaderAtHeight(3, bTime.Add(8*time.Minute)) assert.NoError(t, err) assert.NotNil(t, h) // 3) already stored headers should return the header without error - h, err = c.VerifyHeaderAtHeight(2, bTime.Add(1*time.Hour).Add(1*time.Second)) + h, err = c.VerifyHeaderAtHeight(5, bTime.Add(6*time.Minute)) assert.NoError(t, err) assert.NotNil(t, h) - } - { - c, err := NewClient( - chainID, - TrustOptions{ - Period: 1 * time.Hour, - Height: 3, - Hash: h3.Hash(), - }, - fullNode, - []provider.Provider{fullNode}, - dbs.New(dbm.NewMemDB(), chainID), - Logger(log.TestingLogger()), - ) + + // 4a) First verify latest header + _, err = c.VerifyHeaderAtHeight(9, bTime.Add(9*time.Minute)) require.NoError(t, err) - // 3) trusted header has expired => expect error - _, err = c.VerifyHeaderAtHeight(1, bTime.Add(4*time.Hour).Add(1*time.Second)) + // 4b) Verify backwards using bisection => expect no error + _, err = c.VerifyHeaderAtHeight(7, bTime.Add(10*time.Minute)) + assert.NoError(t, err) + // shouldn't have verified this header in the process + _, err = c.TrustedHeader(8) assert.Error(t, err) + + // 5) trusted header has expired => expect error + _, err = c.VerifyHeaderAtHeight(1, bTime.Add(20*time.Minute)) + assert.Error(t, err) + + // 6) Try bisection method, but closest header (at 7) has expired + // so change to backwards => expect no error + _, err = c.VerifyHeaderAtHeight(8, bTime.Add(12*time.Minute)) + assert.NoError(t, err) + } { testCases := []struct { provider provider.Provider }{ { - // provides incorrect height + // 7) provides incorrect height mockp.New( chainID, map[int64]*types.SignedHeader{ @@ -750,7 +754,7 @@ func TestClient_BackwardsVerification(t *testing.T) { ), }, { - // provides incorrect hash + // 8) provides incorrect hash mockp.New( chainID, map[int64]*types.SignedHeader{ diff --git a/lite2/errors.go b/lite2/errors.go index 13a6cf29d..7bc70f698 100644 --- a/lite2/errors.go +++ b/lite2/errors.go @@ -38,3 +38,11 @@ type ErrInvalidHeader struct { func (e ErrInvalidHeader) Error() string { return fmt.Sprintf("invalid header: %v", e.Reason) } + +// errNoWitnesses means that there are not enough witnesses connected to +// continue running the light client. +type errNoWitnesses struct{} + +func (e errNoWitnesses) Error() string { + return fmt.Sprint("no witnesses connected. please reset light client") +} diff --git a/lite2/store/db/db.go b/lite2/store/db/db.go index d405b9865..8d37ace9f 100644 --- a/lite2/store/db/db.go +++ b/lite2/store/db/db.go @@ -203,18 +203,18 @@ func (s *dbs) FirstSignedHeaderHeight() (int64, error) { return -1, nil } -// SignedHeaderAfter iterates over headers until it finds a header after one at -// height. It returns ErrSignedHeaderNotFound if no such header exists. +// SignedHeaderBefore iterates over headers until it finds a header before +// the given height. It returns ErrSignedHeaderNotFound if no such header exists. // // Safe for concurrent use by multiple goroutines. -func (s *dbs) SignedHeaderAfter(height int64) (*types.SignedHeader, error) { +func (s *dbs) SignedHeaderBefore(height int64) (*types.SignedHeader, error) { if height <= 0 { panic("negative or zero height") } - itr, err := s.db.Iterator( - s.shKey(height+1), - append(s.shKey(1<<63-1), byte(0x00)), + itr, err := s.db.ReverseIterator( + s.shKey(1), + s.shKey(height), ) if err != nil { panic(err) diff --git a/lite2/store/db/db_test.go b/lite2/store/db/db_test.go index 2b82de8f3..ce45f3bcf 100644 --- a/lite2/store/db/db_test.go +++ b/lite2/store/db/db_test.go @@ -76,19 +76,19 @@ func Test_SaveSignedHeaderAndValidatorSet(t *testing.T) { assert.Nil(t, valSet) } -func Test_SignedHeaderAfter(t *testing.T) { - dbStore := New(dbm.NewMemDB(), "Test_SignedHeaderAfter") +func Test_SignedHeaderBefore(t *testing.T) { + dbStore := New(dbm.NewMemDB(), "Test_SignedHeaderBefore") assert.Panics(t, func() { - dbStore.SignedHeaderAfter(0) - dbStore.SignedHeaderAfter(100) + _, _ = dbStore.SignedHeaderBefore(0) + _, _ = dbStore.SignedHeaderBefore(100) }) err := dbStore.SaveSignedHeaderAndValidatorSet( &types.SignedHeader{Header: &types.Header{Height: 2}}, &types.ValidatorSet{}) require.NoError(t, err) - h, err := dbStore.SignedHeaderAfter(1) + h, err := dbStore.SignedHeaderBefore(3) require.NoError(t, err) if assert.NotNil(t, h) { assert.EqualValues(t, 2, h.Height) diff --git a/lite2/store/store.go b/lite2/store/store.go index 7ea6b9c6b..0d36c48b6 100644 --- a/lite2/store/store.go +++ b/lite2/store/store.go @@ -41,10 +41,10 @@ type Store interface { // If the store is empty, -1 and nil error are returned. FirstSignedHeaderHeight() (int64, error) - // SignedHeaderAfter returns the SignedHeader after the certain height. + // SignedHeaderBefore returns the SignedHeader before a certain height. // // height must be > 0 && <= LastSignedHeaderHeight. - SignedHeaderAfter(height int64) (*types.SignedHeader, error) + SignedHeaderBefore(height int64) (*types.SignedHeader, error) // Prune removes headers & the associated validator sets when Store reaches a // defined size (number of header & validator set pairs). From 6c88d2ba1f152d52728d0b5a02de12fe4872f0ba Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 31 Mar 2020 16:33:33 +0400 Subject: [PATCH 74/77] lite2: make maxClockDrift an option (#4616) Closes #4607 --- CHANGELOG_PENDING.md | 3 ++ .../adr-046-light-client-implementation.md | 17 +++++++--- lite2/client.go | 20 +++++++++-- lite2/verifier.go | 34 +++++++++++++------ lite2/verifier_test.go | 11 ++++-- 5 files changed, 66 insertions(+), 19 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index fe8f4a48e..1a00f8f75 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -14,6 +14,9 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi - Go API + - [lite2] [\#4616](https://github.com/tendermint/tendermint/pull/4616) Make `maxClockDrift` an option (@melekes). + `Verify/VerifyAdjacent/VerifyNonAdjacent` now accept `maxClockDrift time.Duration`. + ### FEATURES: - [rpc] [\#4611](https://github.com/tendermint/tendermint/pull/4611) Add `codespace` to `ResultBroadcastTx` (@whylee259) diff --git a/docs/architecture/adr-046-light-client-implementation.md b/docs/architecture/adr-046-light-client-implementation.md index a1e322a9c..7620409a0 100644 --- a/docs/architecture/adr-046-light-client-implementation.md +++ b/docs/architecture/adr-046-light-client-implementation.md @@ -4,6 +4,7 @@ * 13-02-2020: Initial draft * 26-02-2020: Cross-checking the first header * 28-02-2020: Bisection algorithm details +* 31-03-2020: Verify signature got changed ## Context @@ -100,6 +101,10 @@ type Store interface { FirstSignedHeaderHeight() (int64, error) SignedHeaderAfter(height int64) (*types.SignedHeader, error) + + Prune(size uint16) error + + Size() uint16 } ``` @@ -110,12 +115,13 @@ database, used in Tendermint). In the future, remote adapters are possible ```go func Verify( chainID string, - h1 *types.SignedHeader, - h1NextVals *types.ValidatorSet, - h2 *types.SignedHeader, - h2Vals *types.ValidatorSet, + trustedHeader *types.SignedHeader, // height=X + trustedVals *types.ValidatorSet, // height=X or height=X+1 + untrustedHeader *types.SignedHeader, // height=Y + untrustedVals *types.ValidatorSet, // height=Y trustingPeriod time.Duration, now time.Time, + maxClockDrift time.Duration, trustLevel tmmath.Fraction) error { ``` @@ -124,6 +130,9 @@ cases of adjacent and non-adjacent headers. In the former case, it compares the hashes directly (2/3+ signed transition). Otherwise, it verifies 1/3+ (`trustLevel`) of trusted validators are still present in new validators. +While `Verify` function is certainly handy, `VerifyAdjacent` and +`VerifyNonAdjacent` should be used most often to avoid logic errors. + ### Bisection algorithm details Non-recursive bisection algorithm was implemented despite the spec containing diff --git a/lite2/client.go b/lite2/client.go index b93c54b8b..215e26bdb 100644 --- a/lite2/client.go +++ b/lite2/client.go @@ -29,6 +29,12 @@ const ( // find something in between the range, 9/16 is used. bisectionNumerator = 9 bisectionDenominator = 16 + + // 10s should cover most of the clients. + // References: + // - http://vancouver-webpages.com/time/web.html + // - https://blog.codinghorror.com/keeping-time-on-the-pc/ + defaultMaxClockDrift = 10 * time.Second ) // Option sets a parameter for the light client. @@ -94,6 +100,14 @@ func MaxRetryAttempts(max uint16) Option { } } +// MaxClockDrift defines how much new (untrusted) header's Time can drift into +// the future. Default: 10s. +func MaxClockDrift(d time.Duration) Option { + return func(c *Client) { + c.maxClockDrift = d + } +} + // Client represents a light client, connected to a single chain, which gets // headers from a primary provider, verifies them either sequentially or by // skipping some and stores them in a trusted store (usually, a local FS). @@ -105,6 +119,7 @@ type Client struct { verificationMode mode trustLevel tmmath.Fraction maxRetryAttempts uint16 // see MaxRetryAttempts option + maxClockDrift time.Duration // Mutex for locking during changes of the lite clients providers providerMutex sync.Mutex @@ -191,6 +206,7 @@ func NewClientFromTrustedStore( verificationMode: skipping, trustLevel: DefaultTrustLevel, maxRetryAttempts: defaultMaxRetryAttempts, + maxClockDrift: defaultMaxClockDrift, primary: primary, witnesses: witnesses, trustedStore: trustedStore, @@ -636,7 +652,7 @@ func (c *Client) sequence( "newHash", hash2str(interimHeader.Hash())) err = VerifyAdjacent(c.chainID, trustedHeader, interimHeader, interimVals, - c.trustingPeriod, now) + c.trustingPeriod, now, c.maxClockDrift) if err != nil { err = fmt.Errorf("verify adjacent from #%d to #%d failed: %w", trustedHeader.Height, interimHeader.Height, err) @@ -697,7 +713,7 @@ func (c *Client) bisection( "newHash", hash2str(headerCache[depth].sh.Hash())) err := Verify(c.chainID, trustedHeader, trustedVals, headerCache[depth].sh, headerCache[depth].valSet, - c.trustingPeriod, now, c.trustLevel) + c.trustingPeriod, now, c.maxClockDrift, c.trustLevel) switch err.(type) { case nil: // Have we verified the last header diff --git a/lite2/verifier.go b/lite2/verifier.go index 422e0398e..1ef54677b 100644 --- a/lite2/verifier.go +++ b/lite2/verifier.go @@ -10,10 +10,6 @@ import ( "github.com/tendermint/tendermint/types" ) -const ( - maxClockDrift = 10 * time.Second -) - var ( // DefaultTrustLevel - new header can be trusted if at least one correct // validator signed it. @@ -30,6 +26,9 @@ var ( // d) more than 2/3 of untrustedVals have signed h2 // (otherwise, ErrInvalidHeader is returned) // e) headers are non-adjacent. +// +// maxClockDrift defines how much untrustedHeader.Time can drift into the +// future. func VerifyNonAdjacent( chainID string, trustedHeader *types.SignedHeader, // height=X @@ -38,6 +37,7 @@ func VerifyNonAdjacent( untrustedVals *types.ValidatorSet, // height=Y trustingPeriod time.Duration, now time.Time, + maxClockDrift time.Duration, trustLevel tmmath.Fraction) error { if untrustedHeader.Height == trustedHeader.Height+1 { @@ -48,7 +48,11 @@ func VerifyNonAdjacent( return ErrOldHeaderExpired{trustedHeader.Time.Add(trustingPeriod), now} } - if err := verifyNewHeaderAndVals(chainID, untrustedHeader, untrustedVals, trustedHeader, now); err != nil { + if err := verifyNewHeaderAndVals( + chainID, + untrustedHeader, untrustedVals, + trustedHeader, + now, maxClockDrift); err != nil { return ErrInvalidHeader{err} } @@ -86,13 +90,17 @@ func VerifyNonAdjacent( // d) more than 2/3 of new validators (untrustedVals) have signed h2 // (otherwise, ErrInvalidHeader is returned) // e) headers are adjacent. +// +// maxClockDrift defines how much untrustedHeader.Time can drift into the +// future. func VerifyAdjacent( chainID string, trustedHeader *types.SignedHeader, // height=X untrustedHeader *types.SignedHeader, // height=X+1 untrustedVals *types.ValidatorSet, // height=X+1 trustingPeriod time.Duration, - now time.Time) error { + now time.Time, + maxClockDrift time.Duration) error { if untrustedHeader.Height != trustedHeader.Height+1 { return errors.New("headers must be adjacent in height") @@ -102,7 +110,11 @@ func VerifyAdjacent( return ErrOldHeaderExpired{trustedHeader.Time.Add(trustingPeriod), now} } - if err := verifyNewHeaderAndVals(chainID, untrustedHeader, untrustedVals, trustedHeader, now); err != nil { + if err := verifyNewHeaderAndVals( + chainID, + untrustedHeader, untrustedVals, + trustedHeader, + now, maxClockDrift); err != nil { return ErrInvalidHeader{err} } @@ -133,14 +145,15 @@ func Verify( untrustedVals *types.ValidatorSet, // height=Y trustingPeriod time.Duration, now time.Time, + maxClockDrift time.Duration, trustLevel tmmath.Fraction) error { if untrustedHeader.Height != trustedHeader.Height+1 { return VerifyNonAdjacent(chainID, trustedHeader, trustedVals, untrustedHeader, untrustedVals, - trustingPeriod, now, trustLevel) + trustingPeriod, now, maxClockDrift, trustLevel) } - return VerifyAdjacent(chainID, trustedHeader, untrustedHeader, untrustedVals, trustingPeriod, now) + return VerifyAdjacent(chainID, trustedHeader, untrustedHeader, untrustedVals, trustingPeriod, now, maxClockDrift) } func verifyNewHeaderAndVals( @@ -148,7 +161,8 @@ func verifyNewHeaderAndVals( untrustedHeader *types.SignedHeader, untrustedVals *types.ValidatorSet, trustedHeader *types.SignedHeader, - now time.Time) error { + now time.Time, + maxClockDrift time.Duration) error { if err := untrustedHeader.ValidateBasic(chainID); err != nil { return errors.Wrap(err, "untrustedHeader.ValidateBasic failed") diff --git a/lite2/verifier_test.go b/lite2/verifier_test.go index adc671516..7b710d27a 100644 --- a/lite2/verifier_test.go +++ b/lite2/verifier_test.go @@ -11,6 +11,10 @@ import ( "github.com/tendermint/tendermint/types" ) +const ( + maxClockDrift = 10 * time.Second +) + func TestVerifyAdjacentHeaders(t *testing.T) { const ( chainID = "TestVerifyAdjacentHeaders" @@ -151,7 +155,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) { for i, tc := range testCases { tc := tc t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { - err := VerifyAdjacent(chainID, header, tc.newHeader, tc.newVals, tc.trustingPeriod, tc.now) + err := VerifyAdjacent(chainID, header, tc.newHeader, tc.newVals, tc.trustingPeriod, tc.now, maxClockDrift) switch { case tc.expErr != nil && assert.Error(t, err): assert.Equal(t, tc.expErr, err) @@ -265,7 +269,8 @@ func TestVerifyNonAdjacentHeaders(t *testing.T) { for i, tc := range testCases { tc := tc t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { - err := VerifyNonAdjacent(chainID, header, vals, tc.newHeader, tc.newVals, tc.trustingPeriod, tc.now, + err := VerifyNonAdjacent(chainID, header, vals, tc.newHeader, tc.newVals, tc.trustingPeriod, + tc.now, maxClockDrift, DefaultTrustLevel) switch { @@ -295,7 +300,7 @@ func TestVerifyReturnsErrorIfTrustLevelIsInvalid(t *testing.T) { []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)) ) - err := Verify(chainID, header, vals, header, vals, 2*time.Hour, time.Now(), + err := Verify(chainID, header, vals, header, vals, 2*time.Hour, time.Now(), maxClockDrift, tmmath.Fraction{Numerator: 2, Denominator: 1}) assert.Error(t, err) } From fdf9c7ae64bc60af20926ca33f4534bba008ffbd Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Thu, 2 Apr 2020 15:25:30 +0200 Subject: [PATCH 75/77] rpc/client: split out client packages (#4628) * rpc/client: initial split into directories * lite2: split out test package * rpc/client: simplify client constructurs * updated docs * updated changelog --- CHANGELOG_PENDING.md | 1 + cmd/tendermint/commands/debug/dump.go | 6 +- cmd/tendermint/commands/debug/kill.go | 4 +- cmd/tendermint/commands/debug/util.go | 8 +- cmd/tendermint/commands/lite.go | 4 +- lite/client/provider.go | 5 +- lite/proxy/query_test.go | 5 +- lite2/client_benchmark_test.go | 23 ++--- lite2/client_test.go | 93 ++++++++++--------- lite2/example_test.go | 11 ++- lite2/{test_helpers.go => helpers_test.go} | 2 +- lite2/provider/http/http.go | 21 +++-- lite2/provider/http/http_test.go | 7 +- lite2/verifier_test.go | 19 ++-- rpc/client/examples_test.go | 6 +- rpc/client/{httpclient.go => http/http.go} | 53 +++++------ rpc/client/{localclient.go => local/local.go} | 11 ++- rpc/client/rpc_test.go | 20 ++-- rpc/swagger/swagger.yaml | 5 +- 19 files changed, 159 insertions(+), 145 deletions(-) rename lite2/{test_helpers.go => helpers_test.go} (99%) rename rpc/client/{httpclient.go => http/http.go} (92%) rename rpc/client/{localclient.go => local/local.go} (96%) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 1a00f8f75..d605e36d3 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -14,6 +14,7 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi - Go API + - [rpc/client] [\#4628](https://github.com/tendermint/tendermint/pull/4628) Split out HTTP and local clients into `http` and `local` packages (@erikgrinaker). - [lite2] [\#4616](https://github.com/tendermint/tendermint/pull/4616) Make `maxClockDrift` an option (@melekes). `Verify/VerifyAdjacent/VerifyNonAdjacent` now accept `maxClockDrift time.Duration`. diff --git a/cmd/tendermint/commands/debug/dump.go b/cmd/tendermint/commands/debug/dump.go index 7032206b4..a21d8217e 100644 --- a/cmd/tendermint/commands/debug/dump.go +++ b/cmd/tendermint/commands/debug/dump.go @@ -13,7 +13,7 @@ import ( cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/cli" - rpcclient "github.com/tendermint/tendermint/rpc/client" + rpchttp "github.com/tendermint/tendermint/rpc/client/http" ) var dumpCmd = &cobra.Command{ @@ -59,7 +59,7 @@ func dumpCmdHandler(_ *cobra.Command, args []string) error { } } - rpc, err := rpcclient.NewHTTP(nodeRPCAddr, "/websocket") + rpc, err := rpchttp.New(nodeRPCAddr, "/websocket") if err != nil { return errors.Wrap(err, "failed to create new http client") } @@ -79,7 +79,7 @@ func dumpCmdHandler(_ *cobra.Command, args []string) error { return nil } -func dumpDebugData(outDir string, conf *cfg.Config, rpc *rpcclient.HTTP) { +func dumpDebugData(outDir string, conf *cfg.Config, rpc *rpchttp.HTTP) { start := time.Now().UTC() tmpDir, err := ioutil.TempDir(outDir, "tendermint_debug_tmp") diff --git a/cmd/tendermint/commands/debug/kill.go b/cmd/tendermint/commands/debug/kill.go index 8d9df1161..40e298c72 100644 --- a/cmd/tendermint/commands/debug/kill.go +++ b/cmd/tendermint/commands/debug/kill.go @@ -16,7 +16,7 @@ import ( cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/cli" - rpcclient "github.com/tendermint/tendermint/rpc/client" + rpchttp "github.com/tendermint/tendermint/rpc/client/http" ) var killCmd = &cobra.Command{ @@ -44,7 +44,7 @@ func killCmdHandler(cmd *cobra.Command, args []string) error { return errors.New("invalid output file") } - rpc, err := rpcclient.NewHTTP(nodeRPCAddr, "/websocket") + rpc, err := rpchttp.New(nodeRPCAddr, "/websocket") if err != nil { return errors.Wrap(err, "failed to create new http client") } diff --git a/cmd/tendermint/commands/debug/util.go b/cmd/tendermint/commands/debug/util.go index 0e7888121..9e5e36a87 100644 --- a/cmd/tendermint/commands/debug/util.go +++ b/cmd/tendermint/commands/debug/util.go @@ -11,12 +11,12 @@ import ( "github.com/pkg/errors" cfg "github.com/tendermint/tendermint/config" - rpcclient "github.com/tendermint/tendermint/rpc/client" + rpchttp "github.com/tendermint/tendermint/rpc/client/http" ) // dumpStatus gets node status state dump from the Tendermint RPC and writes it // to file. It returns an error upon failure. -func dumpStatus(rpc *rpcclient.HTTP, dir, filename string) error { +func dumpStatus(rpc *rpchttp.HTTP, dir, filename string) error { status, err := rpc.Status() if err != nil { return errors.Wrap(err, "failed to get node status") @@ -27,7 +27,7 @@ func dumpStatus(rpc *rpcclient.HTTP, dir, filename string) error { // dumpNetInfo gets network information state dump from the Tendermint RPC and // writes it to file. It returns an error upon failure. -func dumpNetInfo(rpc *rpcclient.HTTP, dir, filename string) error { +func dumpNetInfo(rpc *rpchttp.HTTP, dir, filename string) error { netInfo, err := rpc.NetInfo() if err != nil { return errors.Wrap(err, "failed to get node network information") @@ -38,7 +38,7 @@ func dumpNetInfo(rpc *rpcclient.HTTP, dir, filename string) error { // dumpConsensusState gets consensus state dump from the Tendermint RPC and // writes it to file. It returns an error upon failure. -func dumpConsensusState(rpc *rpcclient.HTTP, dir, filename string) error { +func dumpConsensusState(rpc *rpchttp.HTTP, dir, filename string) error { consDump, err := rpc.DumpConsensusState() if err != nil { return errors.Wrap(err, "failed to get node consensus dump") diff --git a/cmd/tendermint/commands/lite.go b/cmd/tendermint/commands/lite.go index dae72266d..e28daa6d8 100644 --- a/cmd/tendermint/commands/lite.go +++ b/cmd/tendermint/commands/lite.go @@ -18,7 +18,7 @@ import ( lproxy "github.com/tendermint/tendermint/lite2/proxy" lrpc "github.com/tendermint/tendermint/lite2/rpc" dbs "github.com/tendermint/tendermint/lite2/store/db" - rpcclient "github.com/tendermint/tendermint/rpc/client" + rpchttp "github.com/tendermint/tendermint/rpc/client/http" rpcserver "github.com/tendermint/tendermint/rpc/lib/server" ) @@ -133,7 +133,7 @@ func runProxy(cmd *cobra.Command, args []string) error { return err } - rpcClient, err := rpcclient.NewHTTP(primaryAddr, "/websocket") + rpcClient, err := rpchttp.New(primaryAddr, "/websocket") if err != nil { return errors.Wrapf(err, "http client for %s", primaryAddr) } diff --git a/lite/client/provider.go b/lite/client/provider.go index e24dbe0e4..a79a3b9fc 100644 --- a/lite/client/provider.go +++ b/lite/client/provider.go @@ -1,5 +1,5 @@ /* -Package client defines a provider that uses a rpcclient +Package client defines a provider that uses a rpchttp to get information, which is used to get new headers and validators directly from a Tendermint client. */ @@ -12,6 +12,7 @@ import ( "github.com/tendermint/tendermint/lite" lerr "github.com/tendermint/tendermint/lite/errors" rpcclient "github.com/tendermint/tendermint/rpc/client" + rpchttp "github.com/tendermint/tendermint/rpc/client/http" ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/tendermint/tendermint/types" ) @@ -40,7 +41,7 @@ func NewProvider(chainID string, client SignStatusClient) lite.Provider { // NewHTTPProvider can connect to a tendermint json-rpc endpoint // at the given url, and uses that as a read-only provider. func NewHTTPProvider(chainID, remote string) (lite.Provider, error) { - httpClient, err := rpcclient.NewHTTP(remote, "/websocket") + httpClient, err := rpchttp.New(remote, "/websocket") if err != nil { return nil, err } diff --git a/lite/proxy/query_test.go b/lite/proxy/query_test.go index 34d9d1d4c..766a86040 100644 --- a/lite/proxy/query_test.go +++ b/lite/proxy/query_test.go @@ -15,6 +15,7 @@ import ( certclient "github.com/tendermint/tendermint/lite/client" nm "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/rpc/client" + rpclocal "github.com/tendermint/tendermint/rpc/client/local" rpctest "github.com/tendermint/tendermint/rpc/test" "github.com/tendermint/tendermint/types" ) @@ -47,7 +48,7 @@ func _TestAppProofs(t *testing.T) { assert, require := assert.New(t), require.New(t) prt := defaultProofRuntime() - cl := client.NewLocal(node) + cl := rpclocal.New(node) client.WaitForHeight(cl, 1, nil) // This sets up our trust on the node based on some past point. @@ -126,7 +127,7 @@ func _TestAppProofs(t *testing.T) { func TestTxProofs(t *testing.T) { assert, require := assert.New(t), require.New(t) - cl := client.NewLocal(node) + cl := rpclocal.New(node) client.WaitForHeight(cl, 1, nil) tx := kvstoreTx([]byte("key-a"), []byte("value-a")) diff --git a/lite2/client_benchmark_test.go b/lite2/client_benchmark_test.go index 97b140ea5..5877dbc3c 100644 --- a/lite2/client_benchmark_test.go +++ b/lite2/client_benchmark_test.go @@ -1,4 +1,4 @@ -package lite +package lite_test import ( "testing" @@ -7,6 +7,7 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/libs/log" + lite "github.com/tendermint/tendermint/lite2" "github.com/tendermint/tendermint/lite2/provider" mockp "github.com/tendermint/tendermint/lite2/provider/mock" dbs "github.com/tendermint/tendermint/lite2/store/db" @@ -25,9 +26,9 @@ var ( ) func BenchmarkSequence(b *testing.B) { - c, err := NewClient( + c, err := lite.NewClient( chainID, - TrustOptions{ + lite.TrustOptions{ Period: 24 * time.Hour, Height: 1, Hash: genesisHeader.Hash(), @@ -35,8 +36,8 @@ func BenchmarkSequence(b *testing.B) { benchmarkFullNode, []provider.Provider{benchmarkFullNode}, dbs.New(dbm.NewMemDB(), chainID), - Logger(log.TestingLogger()), - SequentialVerification(), + lite.Logger(log.TestingLogger()), + lite.SequentialVerification(), ) if err != nil { b.Fatal(err) @@ -52,9 +53,9 @@ func BenchmarkSequence(b *testing.B) { } func BenchmarkBisection(b *testing.B) { - c, err := NewClient( + c, err := lite.NewClient( chainID, - TrustOptions{ + lite.TrustOptions{ Period: 24 * time.Hour, Height: 1, Hash: genesisHeader.Hash(), @@ -62,7 +63,7 @@ func BenchmarkBisection(b *testing.B) { benchmarkFullNode, []provider.Provider{benchmarkFullNode}, dbs.New(dbm.NewMemDB(), chainID), - Logger(log.TestingLogger()), + lite.Logger(log.TestingLogger()), ) if err != nil { b.Fatal(err) @@ -79,9 +80,9 @@ func BenchmarkBisection(b *testing.B) { func BenchmarkBackwards(b *testing.B) { trustedHeader, _ := benchmarkFullNode.SignedHeader(0) - c, err := NewClient( + c, err := lite.NewClient( chainID, - TrustOptions{ + lite.TrustOptions{ Period: 24 * time.Hour, Height: trustedHeader.Height, Hash: trustedHeader.Hash(), @@ -89,7 +90,7 @@ func BenchmarkBackwards(b *testing.B) { benchmarkFullNode, []provider.Provider{benchmarkFullNode}, dbs.New(dbm.NewMemDB(), chainID), - Logger(log.TestingLogger()), + lite.Logger(log.TestingLogger()), ) if err != nil { b.Fatal(err) diff --git a/lite2/client_test.go b/lite2/client_test.go index 9481fb0b5..a54786ab7 100644 --- a/lite2/client_test.go +++ b/lite2/client_test.go @@ -1,4 +1,4 @@ -package lite +package lite_test import ( "sync" @@ -11,6 +11,7 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/libs/log" + lite "github.com/tendermint/tendermint/lite2" "github.com/tendermint/tendermint/lite2/provider" mockp "github.com/tendermint/tendermint/lite2/provider/mock" dbs "github.com/tendermint/tendermint/lite2/store/db" @@ -34,7 +35,7 @@ var ( h3 = keys.GenSignedHeaderLastBlockID(chainID, 3, bTime.Add(1*time.Hour), nil, vals, vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys), types.BlockID{Hash: h2.Hash()}) trustPeriod = 4 * time.Hour - trustOptions = TrustOptions{ + trustOptions = lite.TrustOptions{ Period: 4 * time.Hour, Height: 1, Hash: h1.Hash(), @@ -140,7 +141,7 @@ func TestClient_SequentialVerification(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { - c, err := NewClient( + c, err := lite.NewClient( chainID, trustOptions, mockp.New( @@ -154,7 +155,7 @@ func TestClient_SequentialVerification(t *testing.T) { tc.vals, )}, dbs.New(dbm.NewMemDB(), chainID), - SequentialVerification(), + lite.SequentialVerification(), ) if tc.initErr { @@ -263,7 +264,7 @@ func TestClient_SkippingVerification(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.name, func(t *testing.T) { - c, err := NewClient( + c, err := lite.NewClient( chainID, trustOptions, mockp.New( @@ -277,7 +278,7 @@ func TestClient_SkippingVerification(t *testing.T) { tc.vals, )}, dbs.New(dbm.NewMemDB(), chainID), - SkippingVerification(DefaultTrustLevel), + lite.SkippingVerification(lite.DefaultTrustLevel), ) if tc.initErr { require.Error(t, err) @@ -297,13 +298,13 @@ func TestClient_SkippingVerification(t *testing.T) { } func TestClient_Cleanup(t *testing.T) { - c, err := NewClient( + c, err := lite.NewClient( chainID, trustOptions, fullNode, []provider.Provider{fullNode}, dbs.New(dbm.NewMemDB(), chainID), - Logger(log.TestingLogger()), + lite.Logger(log.TestingLogger()), ) require.NoError(t, err) _, err = c.TrustedHeader(1) @@ -330,13 +331,13 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) { err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals) require.NoError(t, err) - c, err := NewClient( + c, err := lite.NewClient( chainID, trustOptions, fullNode, []provider.Provider{fullNode}, trustedStore, - Logger(log.TestingLogger()), + lite.Logger(log.TestingLogger()), ) require.NoError(t, err) @@ -372,9 +373,9 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) { valSet, ) - c, err := NewClient( + c, err := lite.NewClient( chainID, - TrustOptions{ + lite.TrustOptions{ Period: 4 * time.Hour, Height: 1, Hash: header1.Hash(), @@ -382,7 +383,7 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) { primary, []provider.Provider{primary}, trustedStore, - Logger(log.TestingLogger()), + lite.Logger(log.TestingLogger()), ) require.NoError(t, err) @@ -409,9 +410,9 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) { err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals) require.NoError(t, err) - c, err := NewClient( + c, err := lite.NewClient( chainID, - TrustOptions{ + lite.TrustOptions{ Period: 4 * time.Hour, Height: 2, Hash: h2.Hash(), @@ -419,7 +420,7 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) { fullNode, []provider.Provider{fullNode}, trustedStore, - Logger(log.TestingLogger()), + lite.Logger(log.TestingLogger()), ) require.NoError(t, err) @@ -460,9 +461,9 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) { valSet, ) - c, err := NewClient( + c, err := lite.NewClient( chainID, - TrustOptions{ + lite.TrustOptions{ Period: 4 * time.Hour, Height: 2, Hash: diffHeader2.Hash(), @@ -470,7 +471,7 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) { primary, []provider.Provider{primary}, trustedStore, - Logger(log.TestingLogger()), + lite.Logger(log.TestingLogger()), ) require.NoError(t, err) @@ -498,13 +499,13 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) { err = trustedStore.SaveSignedHeaderAndValidatorSet(h2, vals) require.NoError(t, err) - c, err := NewClient( + c, err := lite.NewClient( chainID, trustOptions, fullNode, []provider.Provider{fullNode}, trustedStore, - Logger(log.TestingLogger()), + lite.Logger(log.TestingLogger()), ) require.NoError(t, err) @@ -555,9 +556,9 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) { valSet, ) - c, err := NewClient( + c, err := lite.NewClient( chainID, - TrustOptions{ + lite.TrustOptions{ Period: 4 * time.Hour, Height: 1, Hash: header1.Hash(), @@ -565,7 +566,7 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) { primary, []provider.Provider{primary}, trustedStore, - Logger(log.TestingLogger()), + lite.Logger(log.TestingLogger()), ) require.NoError(t, err) @@ -594,13 +595,13 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) { } func TestClient_Update(t *testing.T) { - c, err := NewClient( + c, err := lite.NewClient( chainID, trustOptions, fullNode, []provider.Provider{fullNode}, dbs.New(dbm.NewMemDB(), chainID), - Logger(log.TestingLogger()), + lite.Logger(log.TestingLogger()), ) require.NoError(t, err) @@ -619,13 +620,13 @@ func TestClient_Update(t *testing.T) { } func TestClient_Concurrency(t *testing.T) { - c, err := NewClient( + c, err := lite.NewClient( chainID, trustOptions, fullNode, []provider.Provider{fullNode}, dbs.New(dbm.NewMemDB(), chainID), - Logger(log.TestingLogger()), + lite.Logger(log.TestingLogger()), ) require.NoError(t, err) @@ -663,14 +664,14 @@ func TestClient_Concurrency(t *testing.T) { } func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) { - c, err := NewClient( + c, err := lite.NewClient( chainID, trustOptions, deadNode, []provider.Provider{fullNode, fullNode}, dbs.New(dbm.NewMemDB(), chainID), - Logger(log.TestingLogger()), - MaxRetryAttempts(1), + lite.Logger(log.TestingLogger()), + lite.MaxRetryAttempts(1), ) require.NoError(t, err) @@ -684,9 +685,9 @@ func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) { func TestClient_BackwardsVerification(t *testing.T) { { trustHeader, _ := largeFullNode.SignedHeader(6) - c, err := NewClient( + c, err := lite.NewClient( chainID, - TrustOptions{ + lite.TrustOptions{ Period: 4 * time.Minute, Height: trustHeader.Height, Hash: trustHeader.Hash(), @@ -694,7 +695,7 @@ func TestClient_BackwardsVerification(t *testing.T) { largeFullNode, []provider.Provider{largeFullNode}, dbs.New(dbm.NewMemDB(), chainID), - Logger(log.TestingLogger()), + lite.Logger(log.TestingLogger()), ) require.NoError(t, err) @@ -769,9 +770,9 @@ func TestClient_BackwardsVerification(t *testing.T) { } for _, tc := range testCases { - c, err := NewClient( + c, err := lite.NewClient( chainID, - TrustOptions{ + lite.TrustOptions{ Period: 1 * time.Hour, Height: 3, Hash: h3.Hash(), @@ -779,7 +780,7 @@ func TestClient_BackwardsVerification(t *testing.T) { tc.provider, []provider.Provider{tc.provider}, dbs.New(dbm.NewMemDB(), chainID), - Logger(log.TestingLogger()), + lite.Logger(log.TestingLogger()), ) require.NoError(t, err) @@ -795,7 +796,7 @@ func TestClient_NewClientFromTrustedStore(t *testing.T) { err := db.SaveSignedHeaderAndValidatorSet(h1, vals) require.NoError(t, err) - c, err := NewClientFromTrustedStore( + c, err := lite.NewClientFromTrustedStore( chainID, trustPeriod, deadNode, @@ -819,14 +820,14 @@ func TestClient_NewClientFromTrustedStore(t *testing.T) { } func TestNewClientErrorsIfAllWitnessesUnavailable(t *testing.T) { - _, err := NewClient( + _, err := lite.NewClient( chainID, trustOptions, fullNode, []provider.Provider{deadNode, deadNode}, dbs.New(dbm.NewMemDB(), chainID), - Logger(log.TestingLogger()), - MaxRetryAttempts(1), + lite.Logger(log.TestingLogger()), + lite.MaxRetryAttempts(1), ) if assert.Error(t, err) { assert.Contains(t, err.Error(), "awaiting response from all witnesses exceeded dropout time") @@ -862,14 +863,14 @@ func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) { }, ) - c, err := NewClient( + c, err := lite.NewClient( chainID, trustOptions, fullNode, []provider.Provider{badProvider1, badProvider2}, dbs.New(dbm.NewMemDB(), chainID), - Logger(log.TestingLogger()), - MaxRetryAttempts(1), + lite.Logger(log.TestingLogger()), + lite.MaxRetryAttempts(1), ) // witness should have behaved properly -> no error require.NoError(t, err) @@ -889,13 +890,13 @@ func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) { } func TestClientTrustedValidatorSet(t *testing.T) { - c, err := NewClient( + c, err := lite.NewClient( chainID, trustOptions, fullNode, []provider.Provider{fullNode}, dbs.New(dbm.NewMemDB(), chainID), - Logger(log.TestingLogger()), + lite.Logger(log.TestingLogger()), ) require.NoError(t, err) diff --git a/lite2/example_test.go b/lite2/example_test.go index e8c3b8bb3..0de5f1349 100644 --- a/lite2/example_test.go +++ b/lite2/example_test.go @@ -1,4 +1,4 @@ -package lite +package lite_test import ( "fmt" @@ -11,6 +11,7 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/abci/example/kvstore" + lite "github.com/tendermint/tendermint/lite2" "github.com/tendermint/tendermint/lite2/provider" httpp "github.com/tendermint/tendermint/lite2/provider/http" dbs "github.com/tendermint/tendermint/lite2/store/db" @@ -48,9 +49,9 @@ func ExampleClient_Update() { stdlog.Fatal(err) } - c, err := NewClient( + c, err := lite.NewClient( chainID, - TrustOptions{ + lite.TrustOptions{ Period: 504 * time.Hour, // 21 days Height: 2, Hash: header.Hash(), @@ -117,9 +118,9 @@ func ExampleClient_VerifyHeaderAtHeight() { stdlog.Fatal(err) } - c, err := NewClient( + c, err := lite.NewClient( chainID, - TrustOptions{ + lite.TrustOptions{ Period: 504 * time.Hour, // 21 days Height: 2, Hash: header.Hash(), diff --git a/lite2/test_helpers.go b/lite2/helpers_test.go similarity index 99% rename from lite2/test_helpers.go rename to lite2/helpers_test.go index 504e9569e..d9ab46f92 100644 --- a/lite2/test_helpers.go +++ b/lite2/helpers_test.go @@ -1,4 +1,4 @@ -package lite +package lite_test import ( "time" diff --git a/lite2/provider/http/http.go b/lite2/provider/http/http.go index 130bf0a24..dff5e4d1d 100644 --- a/lite2/provider/http/http.go +++ b/lite2/provider/http/http.go @@ -7,6 +7,7 @@ import ( "github.com/tendermint/tendermint/lite2/provider" rpcclient "github.com/tendermint/tendermint/rpc/client" + rpchttp "github.com/tendermint/tendermint/rpc/client/http" "github.com/tendermint/tendermint/types" ) @@ -21,14 +22,14 @@ type SignStatusClient interface { // http provider uses an RPC client (or SignStatusClient more generally) to // obtain the necessary information. type http struct { - chainID string - client SignStatusClient + SignStatusClient // embed so interface can be converted to SignStatusClient for tests + chainID string } -// New creates a HTTP provider, which is using the rpcclient.HTTP +// New creates a HTTP provider, which is using the rpchttp.HTTP // client under the hood. func New(chainID, remote string) (provider.Provider, error) { - httpClient, err := rpcclient.NewHTTP(remote, "/websocket") + httpClient, err := rpchttp.New(remote, "/websocket") if err != nil { return nil, err } @@ -38,8 +39,8 @@ func New(chainID, remote string) (provider.Provider, error) { // NewWithClient allows you to provide custom SignStatusClient. func NewWithClient(chainID string, client SignStatusClient) provider.Provider { return &http{ - chainID: chainID, - client: client, + SignStatusClient: client, + chainID: chainID, } } @@ -49,7 +50,7 @@ func (p *http) ChainID() string { } func (p *http) String() string { - return fmt.Sprintf("http{%s}", p.client.Remote()) + return fmt.Sprintf("http{%s}", p.Remote()) } // SignedHeader fetches a SignedHeader at the given height and checks the @@ -60,7 +61,7 @@ func (p *http) SignedHeader(height int64) (*types.SignedHeader, error) { return nil, err } - commit, err := p.client.Commit(h) + commit, err := p.SignStatusClient.Commit(h) if err != nil { // TODO: standartise errors on the RPC side if strings.Contains(err.Error(), "height must be less than or equal") { @@ -90,7 +91,7 @@ func (p *http) ValidatorSet(height int64) (*types.ValidatorSet, error) { } const maxPerPage = 100 - res, err := p.client.Validators(h, 0, maxPerPage) + res, err := p.SignStatusClient.Validators(h, 0, maxPerPage) if err != nil { // TODO: standartise errors on the RPC side if strings.Contains(err.Error(), "height must be less than or equal") { @@ -106,7 +107,7 @@ func (p *http) ValidatorSet(height int64) (*types.ValidatorSet, error) { // Check if there are more validators. for len(res.Validators) == maxPerPage { - res, err = p.client.Validators(h, page, maxPerPage) + res, err = p.SignStatusClient.Validators(h, page, maxPerPage) if err != nil { return nil, err } diff --git a/lite2/provider/http/http_test.go b/lite2/provider/http/http_test.go index 1e5f4cb2b..73706434b 100644 --- a/lite2/provider/http/http_test.go +++ b/lite2/provider/http/http_test.go @@ -1,4 +1,4 @@ -package http +package http_test import ( "os" @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/abci/example/kvstore" + litehttp "github.com/tendermint/tendermint/lite2/provider/http" rpcclient "github.com/tendermint/tendermint/rpc/client" rpctest "github.com/tendermint/tendermint/rpc/test" "github.com/tendermint/tendermint/types" @@ -33,12 +34,12 @@ func TestProvider(t *testing.T) { } chainID := genDoc.ChainID t.Log("chainID:", chainID) - p, err := New(chainID, rpcAddr) + p, err := litehttp.New(chainID, rpcAddr) require.Nil(t, err) require.NotNil(t, p) // let it produce some blocks - err = rpcclient.WaitForHeight(p.(*http).client, 6, nil) + err = rpcclient.WaitForHeight(p.(rpcclient.StatusClient), 6, nil) require.Nil(t, err) // let's get the highest block diff --git a/lite2/verifier_test.go b/lite2/verifier_test.go index 7b710d27a..5a207321d 100644 --- a/lite2/verifier_test.go +++ b/lite2/verifier_test.go @@ -1,4 +1,4 @@ -package lite +package lite_test import ( "fmt" @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" tmmath "github.com/tendermint/tendermint/libs/math" + lite "github.com/tendermint/tendermint/lite2" "github.com/tendermint/tendermint/types" ) @@ -117,7 +118,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) { vals, 3 * time.Hour, bTime.Add(2 * time.Hour), - ErrInvalidHeader{Reason: types.ErrNotEnoughVotingPowerSigned{Got: 50, Needed: 93}}, + lite.ErrInvalidHeader{Reason: types.ErrNotEnoughVotingPowerSigned{Got: 50, Needed: 93}}, "", }, // vals does not match with what we have -> error @@ -155,7 +156,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) { for i, tc := range testCases { tc := tc t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { - err := VerifyAdjacent(chainID, header, tc.newHeader, tc.newVals, tc.trustingPeriod, tc.now, maxClockDrift) + err := lite.VerifyAdjacent(chainID, header, tc.newHeader, tc.newVals, tc.trustingPeriod, tc.now, maxClockDrift) switch { case tc.expErr != nil && assert.Error(t, err): assert.Equal(t, tc.expErr, err) @@ -231,7 +232,7 @@ func TestVerifyNonAdjacentHeaders(t *testing.T) { vals, 3 * time.Hour, bTime.Add(2 * time.Hour), - ErrInvalidHeader{types.ErrNotEnoughVotingPowerSigned{Got: 50, Needed: 93}}, + lite.ErrInvalidHeader{types.ErrNotEnoughVotingPowerSigned{Got: 50, Needed: 93}}, "", }, // 3/3 new vals signed, 2/3 old vals present -> no error @@ -261,7 +262,7 @@ func TestVerifyNonAdjacentHeaders(t *testing.T) { lessThanOneThirdVals, 3 * time.Hour, bTime.Add(2 * time.Hour), - ErrNewValSetCantBeTrusted{types.ErrNotEnoughVotingPowerSigned{Got: 20, Needed: 46}}, + lite.ErrNewValSetCantBeTrusted{types.ErrNotEnoughVotingPowerSigned{Got: 20, Needed: 46}}, "", }, } @@ -269,9 +270,9 @@ func TestVerifyNonAdjacentHeaders(t *testing.T) { for i, tc := range testCases { tc := tc t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { - err := VerifyNonAdjacent(chainID, header, vals, tc.newHeader, tc.newVals, tc.trustingPeriod, + err := lite.VerifyNonAdjacent(chainID, header, vals, tc.newHeader, tc.newVals, tc.trustingPeriod, tc.now, maxClockDrift, - DefaultTrustLevel) + lite.DefaultTrustLevel) switch { case tc.expErr != nil && assert.Error(t, err): @@ -300,7 +301,7 @@ func TestVerifyReturnsErrorIfTrustLevelIsInvalid(t *testing.T) { []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)) ) - err := Verify(chainID, header, vals, header, vals, 2*time.Hour, time.Now(), maxClockDrift, + err := lite.Verify(chainID, header, vals, header, vals, 2*time.Hour, time.Now(), maxClockDrift, tmmath.Fraction{Numerator: 2, Denominator: 1}) assert.Error(t, err) } @@ -327,7 +328,7 @@ func TestValidateTrustLevel(t *testing.T) { } for _, tc := range testCases { - err := ValidateTrustLevel(tc.lvl) + err := lite.ValidateTrustLevel(tc.lvl) if !tc.valid { assert.Error(t, err) } else { diff --git a/rpc/client/examples_test.go b/rpc/client/examples_test.go index bb4583506..5d87a3a98 100644 --- a/rpc/client/examples_test.go +++ b/rpc/client/examples_test.go @@ -6,7 +6,7 @@ import ( "log" "github.com/tendermint/tendermint/abci/example/kvstore" - "github.com/tendermint/tendermint/rpc/client" + rpchttp "github.com/tendermint/tendermint/rpc/client/http" ctypes "github.com/tendermint/tendermint/rpc/core/types" rpctest "github.com/tendermint/tendermint/rpc/test" ) @@ -19,7 +19,7 @@ func ExampleHTTP_simple() { // Create our RPC client rpcAddr := rpctest.GetConfig().RPC.ListenAddress - c, err := client.NewHTTP(rpcAddr, "/websocket") + c, err := rpchttp.New(rpcAddr, "/websocket") if err != nil { log.Fatal(err) } @@ -72,7 +72,7 @@ func ExampleHTTP_batching() { // Create our RPC client rpcAddr := rpctest.GetConfig().RPC.ListenAddress - c, err := client.NewHTTP(rpcAddr, "/websocket") + c, err := rpchttp.New(rpcAddr, "/websocket") if err != nil { log.Fatal(err) } diff --git a/rpc/client/httpclient.go b/rpc/client/http/http.go similarity index 92% rename from rpc/client/httpclient.go rename to rpc/client/http/http.go index fd8783eba..9af0b6cf4 100644 --- a/rpc/client/httpclient.go +++ b/rpc/client/http/http.go @@ -1,4 +1,4 @@ -package client +package http import ( "context" @@ -15,8 +15,9 @@ import ( "github.com/tendermint/tendermint/libs/log" tmpubsub "github.com/tendermint/tendermint/libs/pubsub" "github.com/tendermint/tendermint/libs/service" + rpcclient "github.com/tendermint/tendermint/rpc/client" ctypes "github.com/tendermint/tendermint/rpc/core/types" - rpcclient "github.com/tendermint/tendermint/rpc/lib/client" + rpcclientlib "github.com/tendermint/tendermint/rpc/lib/client" "github.com/tendermint/tendermint/types" ) @@ -40,7 +41,7 @@ the example for more details. Example: - c, err := NewHTTP("http://192.168.1.10:26657", "/websocket") + c, err := New("http://192.168.1.10:26657", "/websocket") if err != nil { // handle error } @@ -61,7 +62,7 @@ Example: */ type HTTP struct { remote string - rpc *rpcclient.JSONRPCClient + rpc *rpcclientlib.JSONRPCClient *baseRPCClient *WSEvents @@ -78,7 +79,7 @@ type HTTP struct { // batch, but ordering of transactions in the batch cannot be guaranteed in such // an example. type BatchHTTP struct { - rpcBatch *rpcclient.JSONRPCRequestBatch + rpcBatch *rpcclientlib.JSONRPCRequestBatch *baseRPCClient } @@ -86,17 +87,17 @@ type BatchHTTP struct { // non-batch) must conform. Acts as an additional code-level sanity check to // make sure the implementations stay coherent. type rpcClient interface { - ABCIClient - HistoryClient - NetworkClient - SignClient - StatusClient + rpcclient.ABCIClient + rpcclient.HistoryClient + rpcclient.NetworkClient + rpcclient.SignClient + rpcclient.StatusClient } // baseRPCClient implements the basic RPC method logic without the actual // underlying RPC call functionality, which is provided by `caller`. type baseRPCClient struct { - caller rpcclient.JSONRPCCaller + caller rpcclientlib.JSONRPCCaller } var _ rpcClient = (*HTTP)(nil) @@ -106,35 +107,35 @@ var _ rpcClient = (*baseRPCClient)(nil) //----------------------------------------------------------------------------- // HTTP -// NewHTTP takes a remote endpoint in the form ://: and +// New takes a remote endpoint in the form ://: and // the websocket path (which always seems to be "/websocket") // An error is returned on invalid remote. The function panics when remote is nil. -func NewHTTP(remote, wsEndpoint string) (*HTTP, error) { - httpClient, err := rpcclient.DefaultHTTPClient(remote) +func New(remote, wsEndpoint string) (*HTTP, error) { + httpClient, err := rpcclientlib.DefaultHTTPClient(remote) if err != nil { return nil, err } - return NewHTTPWithClient(remote, wsEndpoint, httpClient) + return NewWithClient(remote, wsEndpoint, httpClient) } // Create timeout enabled http client -func NewHTTPWithTimeout(remote, wsEndpoint string, timeout uint) (*HTTP, error) { - httpClient, err := rpcclient.DefaultHTTPClient(remote) +func NewWithTimeout(remote, wsEndpoint string, timeout uint) (*HTTP, error) { + httpClient, err := rpcclientlib.DefaultHTTPClient(remote) if err != nil { return nil, err } httpClient.Timeout = time.Duration(timeout) * time.Second - return NewHTTPWithClient(remote, wsEndpoint, httpClient) + return NewWithClient(remote, wsEndpoint, httpClient) } -// NewHTTPWithClient allows for setting a custom http client (See NewHTTP). +// NewWithClient allows for setting a custom http client (See New). // An error is returned on invalid remote. The function panics when remote is nil. -func NewHTTPWithClient(remote, wsEndpoint string, client *http.Client) (*HTTP, error) { +func NewWithClient(remote, wsEndpoint string, client *http.Client) (*HTTP, error) { if client == nil { panic("nil http.Client provided") } - rc, err := rpcclient.NewJSONRPCClientWithHTTPClient(remote, client) + rc, err := rpcclientlib.NewJSONRPCClientWithHTTPClient(remote, client) if err != nil { return nil, err } @@ -157,7 +158,7 @@ func NewHTTPWithClient(remote, wsEndpoint string, client *http.Client) (*HTTP, e return httpClient, nil } -var _ Client = (*HTTP)(nil) +var _ rpcclient.Client = (*HTTP)(nil) // SetLogger sets a logger. func (c *HTTP) SetLogger(l log.Logger) { @@ -224,13 +225,13 @@ func (c *baseRPCClient) ABCIInfo() (*ctypes.ResultABCIInfo, error) { } func (c *baseRPCClient) ABCIQuery(path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) { - return c.ABCIQueryWithOptions(path, data, DefaultABCIQueryOptions) + return c.ABCIQueryWithOptions(path, data, rpcclient.DefaultABCIQueryOptions) } func (c *baseRPCClient) ABCIQueryWithOptions( path string, data bytes.HexBytes, - opts ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { + opts rpcclient.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { result := new(ctypes.ResultABCIQuery) _, err := c.caller.Call("abci_query", map[string]interface{}{"path": path, "data": data, "height": opts.Height, "prove": opts.Prove}, @@ -440,7 +441,7 @@ type WSEvents struct { cdc *amino.Codec remote string endpoint string - ws *rpcclient.WSClient + ws *rpcclientlib.WSClient mtx sync.RWMutex subscriptions map[string]chan ctypes.ResultEvent // query -> chan @@ -456,7 +457,7 @@ func newWSEvents(cdc *amino.Codec, remote, endpoint string) (*WSEvents, error) { w.BaseService = *service.NewBaseService(nil, "WSEvents", w) var err error - w.ws, err = rpcclient.NewWSClient(w.remote, w.endpoint, rpcclient.OnReconnect(func() { + w.ws, err = rpcclientlib.NewWSClient(w.remote, w.endpoint, rpcclientlib.OnReconnect(func() { // resubscribe immediately w.redoSubscriptionsAfter(0 * time.Second) })) diff --git a/rpc/client/localclient.go b/rpc/client/local/local.go similarity index 96% rename from rpc/client/localclient.go rename to rpc/client/local/local.go index e6b0eb937..a28e9f939 100644 --- a/rpc/client/localclient.go +++ b/rpc/client/local/local.go @@ -1,4 +1,4 @@ -package client +package local import ( "context" @@ -11,6 +11,7 @@ import ( tmpubsub "github.com/tendermint/tendermint/libs/pubsub" tmquery "github.com/tendermint/tendermint/libs/pubsub/query" nm "github.com/tendermint/tendermint/node" + rpcclient "github.com/tendermint/tendermint/rpc/client" "github.com/tendermint/tendermint/rpc/core" ctypes "github.com/tendermint/tendermint/rpc/core/types" rpctypes "github.com/tendermint/tendermint/rpc/lib/types" @@ -49,7 +50,7 @@ type Local struct { // you can only have one node per process. So make sure test cases // don't run in parallel, or try to simulate an entire network in // one process... -func NewLocal(node *nm.Node) *Local { +func New(node *nm.Node) *Local { node.ConfigureRPC() return &Local{ EventBus: node.EventBus(), @@ -58,7 +59,7 @@ func NewLocal(node *nm.Node) *Local { } } -var _ Client = (*Local)(nil) +var _ rpcclient.Client = (*Local)(nil) // SetLogger allows to set a logger on the client. func (c *Local) SetLogger(l log.Logger) { @@ -74,13 +75,13 @@ func (c *Local) ABCIInfo() (*ctypes.ResultABCIInfo, error) { } func (c *Local) ABCIQuery(path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) { - return c.ABCIQueryWithOptions(path, data, DefaultABCIQueryOptions) + return c.ABCIQueryWithOptions(path, data, rpcclient.DefaultABCIQueryOptions) } func (c *Local) ABCIQueryWithOptions( path string, data bytes.HexBytes, - opts ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { + opts rpcclient.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { return core.ABCIQuery(c.ctx, path, data, opts.Height, opts.Prove) } diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 9196bead0..62b9f23ef 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -23,15 +23,17 @@ import ( mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/rpc/client" + rpchttp "github.com/tendermint/tendermint/rpc/client/http" + rpclocal "github.com/tendermint/tendermint/rpc/client/local" ctypes "github.com/tendermint/tendermint/rpc/core/types" rpcclient "github.com/tendermint/tendermint/rpc/lib/client" rpctest "github.com/tendermint/tendermint/rpc/test" "github.com/tendermint/tendermint/types" ) -func getHTTPClient() *client.HTTP { +func getHTTPClient() *rpchttp.HTTP { rpcAddr := rpctest.GetConfig().RPC.ListenAddress - c, err := client.NewHTTP(rpcAddr, "/websocket") + c, err := rpchttp.New(rpcAddr, "/websocket") if err != nil { panic(err) } @@ -39,9 +41,9 @@ func getHTTPClient() *client.HTTP { return c } -func getHTTPClientWithTimeout(timeout uint) *client.HTTP { +func getHTTPClientWithTimeout(timeout uint) *rpchttp.HTTP { rpcAddr := rpctest.GetConfig().RPC.ListenAddress - c, err := client.NewHTTPWithTimeout(rpcAddr, "/websocket", timeout) + c, err := rpchttp.NewWithTimeout(rpcAddr, "/websocket", timeout) if err != nil { panic(err) } @@ -49,8 +51,8 @@ func getHTTPClientWithTimeout(timeout uint) *client.HTTP { return c } -func getLocalClient() *client.Local { - return client.NewLocal(node) +func getLocalClient() *rpclocal.Local { + return rpclocal.New(node) } // GetClients returns a slice of clients for table-driven tests @@ -63,7 +65,7 @@ func GetClients() []client.Client { func TestNilCustomHTTPClient(t *testing.T) { require.Panics(t, func() { - _, _ = client.NewHTTPWithClient("http://example.com", "/websocket", nil) + _, _ = rpchttp.NewWithClient("http://example.com", "/websocket", nil) }) require.Panics(t, func() { _, _ = rpcclient.NewJSONRPCClientWithHTTPClient("http://example.com", nil) @@ -72,7 +74,7 @@ func TestNilCustomHTTPClient(t *testing.T) { func TestCustomHTTPClient(t *testing.T) { remote := rpctest.GetConfig().RPC.ListenAddress - c, err := client.NewHTTPWithClient(remote, "/websocket", http.DefaultClient) + c, err := rpchttp.NewWithClient(remote, "/websocket", http.DefaultClient) require.Nil(t, err) status, err := c.Status() require.NoError(t, err) @@ -701,7 +703,7 @@ func TestBatchedJSONRPCCalls(t *testing.T) { testBatchedJSONRPCCalls(t, c) } -func testBatchedJSONRPCCalls(t *testing.T, c *client.HTTP) { +func testBatchedJSONRPCCalls(t *testing.T, c *rpchttp.HTTP) { k1, v1, tx1 := MakeTxKV() k2, v2, tx2 := MakeTxKV() diff --git a/rpc/swagger/swagger.yaml b/rpc/swagger/swagger.yaml index 00b137bf0..e0f40262b 100644 --- a/rpc/swagger/swagger.yaml +++ b/rpc/swagger/swagger.yaml @@ -253,9 +253,10 @@ paths: https://godoc.org/github.com/tendermint/tendermint/libs/pubsub/query. ```go + import rpchttp "github.com/tendermint/rpc/client/http" import "github.com/tendermint/tendermint/types" - client := client.NewHTTP("tcp:0.0.0.0:26657", "/websocket") + client := rpchttp.New("tcp:0.0.0.0:26657", "/websocket") err := client.Start() if err != nil { handle error @@ -309,7 +310,7 @@ paths: operationId: unsubscribe description: | ```go - client := client.NewHTTP("tcp:0.0.0.0:26657", "/websocket") + client := rpchttp.New("tcp:0.0.0.0:26657", "/websocket") err := client.Start() if err != nil { handle error From ce50dda66c1a513031c11e908c47ffdc0f8011f2 Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Thu, 2 Apr 2020 15:37:00 +0200 Subject: [PATCH 76/77] ADR-053: strengthen and simplify the state sync ABCI interface (#4610) * adr-053: strengthened and simplified ABCI interface * remove internal_error reason and open questions * add follow-up task for block backfill Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../adr-053-state-sync-prototype.md | 90 +++++++++---------- 1 file changed, 43 insertions(+), 47 deletions(-) diff --git a/docs/architecture/adr-053-state-sync-prototype.md b/docs/architecture/adr-053-state-sync-prototype.md index 79e39b24d..2848f9dd4 100644 --- a/docs/architecture/adr-053-state-sync-prototype.md +++ b/docs/architecture/adr-053-state-sync-prototype.md @@ -14,6 +14,14 @@ This ADR outlines the plan for an initial state sync prototype, and is subject t * Added experimental prototype info. * Added open questions and implementation plan. +* 2020-03-29: Strengthened and simplified ABCI interface (Erik Grinaker) + * ABCI: replaced `chunks` with `chunk_hashes` in `Snapshot`. + * ABCI: removed `SnapshotChunk` message. + * ABCI: renamed `GetSnapshotChunk` to `LoadSnapshotChunk`. + * ABCI: chunks are now exchanged simply as `bytes`. + * ABCI: chunks are now 0-indexed, for parity with `chunk_hashes` array. + * Reduced maximum chunk size to 16 MB, and increased snapshot message size to 4 MB. + ## Context State sync will allow a new node to receive a snapshot of the application state without downloading blocks or going through consensus. This bootstraps the node significantly faster than the current fast sync system, which replays all historical blocks. @@ -36,28 +44,18 @@ This describes the snapshot/restore process seen from Tendermint. The interface ### Snapshot Data Structure -A node can have multiple snapshots taken at various heights. Snapshots can be taken in different application-specified formats (e.g. MessagePack as format `1` and Protobuf as format `2`, or similarly for schema versioning). Each snapshot consists of multiple chunks containing the actual state data, allowing parallel downloads and reduced memory usage. +A node can have multiple snapshots taken at various heights. Snapshots can be taken in different application-specified formats (e.g. MessagePack as format `1` and Protobuf as format `2`, or similarly for schema versioning). Each snapshot consists of multiple chunks containing the actual state data, for parallel downloads and reduced memory usage. ```proto message Snapshot { - uint64 height = 1; // The height at which the snapshot was taken - uint32 format = 2; // The application-specific snapshot format - uint32 chunks = 3; // The number of chunks in the snapshot - bytes metadata = 4; // Arbitrary application metadata -} - -message SnapshotChunk { - uint64 height = 1; // The height of the corresponding snapshot - uint32 format = 2; // The application-specific snapshot format - uint32 chunk = 3; // The chunk index (one-based) - bytes data = 4; // Serialized application state in an arbitrary format - bytes checksum = 5; // SHA-1 checksum of data + uint64 height = 1; // The height at which the snapshot was taken + uint32 format = 2; // The application-specific snapshot format + repeated bytes chunk_hashes = 3; // SHA-256 checksums of all chunks, in order + bytes metadata = 4; // Arbitrary application metadata } ``` -Chunk verification data must be encoded along with the state data in the `data` field. - -Chunk `data` cannot be larger than 64 MB, and snapshot `metadata` cannot be larger than 64 KB. +Chunks are exchanged simply as `bytes`, and cannot be larger than 16 MB. `Snapshot` messages should be less than 4 MB. ### ABCI Interface @@ -72,41 +70,43 @@ message ResponseListSnapshots { // Offers a snapshot to the application message RequestOfferSnapshot { Snapshot snapshot = 1; - bytes app_hash = 2; + bytes app_hash = 2; } message ResponseOfferSnapshot { - bool accepted = 1; - Reason reason = 2; // Reason why snapshot was rejected - enum Reason { - unknown = 0; // Unknown or generic reason - invalid_height = 1; // Height is rejected: avoid this height - invalid_format = 2; // Format is rejected: avoid this format + bool accepted = 1; + Reason reason = 2; + + enum Reason { // Reason why snapshot was rejected + unknown = 0; // Unknown or generic reason + invalid_height = 1; // Height is rejected: avoid this height + invalid_format = 2; // Format is rejected: avoid this format } } -// Fetches a snapshot chunk -message RequestGetSnapshotChunk { +// Loads a snapshot chunk +message RequestLoadSnapshotChunk { uint64 height = 1; uint32 format = 2; - uint32 chunk = 3; + uint32 chunk = 3; // Zero-indexed } -message ResponseGetSnapshotChunk { - SnapshotChunk chunk = 1; +message ResponseLoadSnapshotChunk { + bytes chunk = 1; } // Applies a snapshot chunk message RequestApplySnapshotChunk { - SnapshotChunk chunk = 1; + bytes chunk = 1; } message ResponseApplySnapshotChunk { - bool applied = 1; - Reason reason = 2; // Reason why chunk failed - enum Reason { - unknown = 0; // Unknown or generic reason - verify_failed = 1; // Chunk verification failed + bool applied = 1; + Reason reason = 2; // Reason why chunk failed + + enum Reason { // Reason why chunk failed + unknown = 0; // Unknown or generic reason + verify_failed = 1; // Snapshot verification failed } } ``` @@ -139,19 +139,19 @@ When starting an empty node with state sync and fast sync enabled, snapshots are 3. The node contacts a set of full nodes, and verifies the trusted block header using the given hash via the light client. -4. The node requests available snapshots via `RequestListSnapshots`. Snapshots with `metadata` greater than 64 KB are rejected. +4. The node requests available snapshots via P2P from peers, via `RequestListSnapshots`. Peers will return the 10 most recent snapshots, one message per snapshot. -5. The node iterates over all snapshots in reverse order by height and format until it finds one that satisfies all of the following conditions: +5. The node aggregates snapshots from multiple peers, ordered by height and format (in reverse). If there are `chunk_hashes` mismatches between different snapshots, the one hosted by the largest amount of peers is chosen. The node iterates over all snapshots in reverse order by height and format until it finds one that satisfies all of the following conditions: * The snapshot height's block is considered trustworthy by the light client (i.e. snapshot height is greater than trusted header and within unbonding period of the latest trustworthy block). - * The snapshot's height or format hasn't been explicitly rejected by an earlier `RequestOffsetSnapshot` call (via `invalid_height` or `invalid_format`). + * The snapshot's height or format hasn't been explicitly rejected by an earlier `RequestOfferSnapshot` call (via `invalid_height` or `invalid_format`). * The application accepts the `RequestOfferSnapshot` call. -6. The node downloads chunks in parallel from multiple peers via `RequestGetSnapshotChunk`, and both the sender and receiver verifies their checksums. Chunks with `data` greater than 64 MB are rejected. +6. The node downloads chunks in parallel from multiple peers, via `RequestLoadSnapshotChunk`, and both the sender and receiver verifies their checksums. Chunk messages cannot exceed 16 MB. -7. The node passes chunks sequentially to the app via `RequestApplySnapshotChunk`, along with the chain's app hash at the snapshot height for verification. If the chunk is rejected the node should retry it. If it was rejected with `verify_failed`, it should be refetched from a different source. If an internal error occurred, `ResponseException` should be returned and state sync should be aborted. +7. The node passes chunks sequentially to the app via `RequestApplySnapshotChunk`. 8. Once all chunks have been applied, the node compares the app hash to the chain app hash, and if they do not match it either errors or discards the state and starts over. @@ -167,7 +167,7 @@ This describes the snapshot process seen from Gaia, using format version `1`. Th In the initial version there is no snapshot metadata, so it is set to an empty byte buffer. -Once all chunks have been successfully built, snapshot metadata should be serialized and stored in the file system as e.g. `snapshots///metadata`, and served via `RequestListSnapshots`. +Once all chunks have been successfully built, snapshot metadata should be stored in a database and served via `RequestListSnapshots`. ### Snapshot Chunk Format @@ -181,7 +181,7 @@ For the initial prototype, each chunk consists of a complete dump of all node da For a production version, it should be sufficient to store key/value/version for all nodes (leaf and inner) in insertion order, chunked in some appropriate way. If per-chunk verification is required, the chunk must also contain enough information to reconstruct the Merkle proofs all the way up to the root of the multistore, e.g. by storing a complete subtree's key/value/version data plus Merkle hashes of all other branches up to the multistore root. The exact approach will depend on tradeoffs between size, time, and verification. IAVL RangeProofs are not recommended, since these include redundant data such as proofs for intermediate and leaf nodes that can be derived from the above data. -Chunks should be built greedily by collecting node data up to some size limit (e.g. 32 MB) and serializing it. Chunk data is stored in the file system as `snapshots////data`, along with a SHA-1 checksum in `snapshots////checksum`, and served via `RequestGetSnapshotChunk`. +Chunks should be built greedily by collecting node data up to some size limit (e.g. 10 MB) and serializing it. Chunk data is stored in the file system as `snapshots///`, and a SHA-256 checksum is stored along with the snapshot metadata. ### Snapshot Scheduling @@ -223,12 +223,6 @@ To stop the testnet, run: $ ./tools/stop.sh ``` -## Open Questions - -* Should we have a simpler scheme for discovering snapshots? E.g. announce supported formats, and have peer supply latest available snapshot. - - Downsides: app has to announce supported formats, having a single snapshot per peer may make fewer peers available for chosen snapshot. - ## Resolved Questions * Is it OK for state-synced nodes to not have historical blocks nor historical IAVL versions? @@ -309,6 +303,8 @@ $ ./tools/stop.sh * **Tendermint:** node should go back to fast-syncing when lagging significantly [#129](https://github.com/tendermint/tendermint/issues/129) +* **Tendermint:** backfill historical blocks [#4629](https://github.com/tendermint/tendermint/issues/4629) + ## Status Accepted From 4298bbcc4e25be78e3c4f21979d6aa01aede6e87 Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Fri, 3 Apr 2020 10:38:32 +0200 Subject: [PATCH 77/77] add support for block pruning via ABCI Commit response (#4588) * Added BlockStore.DeleteBlock() * Added initial block pruner prototype * wip * Added BlockStore.PruneBlocks() * Added consensus setting for block pruning * Added BlockStore base * Error on replay if base does not have blocks * Handle missing blocks when sending VoteSetMaj23Message * Error message tweak * Properly update blockstore state * Error message fix again * blockchain: ignore peer missing blocks * Added FIXME * Added test for block replay with truncated history * Handle peer base in blockchain reactor * Improved replay error handling * Added tests for Store.PruneBlocks() * Fix non-RPC handling of truncated block history * Panic on missing block meta in needProofBlock() * Updated changelog * Handle truncated block history in RPC layer * Added info about earliest block in /status RPC * Reorder height and base in blockchain reactor messages * Updated changelog * Fix tests * Appease linter * Minor review fixes * Non-empty BlockStores should always have base > 0 * Update code to assume base > 0 invariant * Added blockstore tests for pruning to 0 * Make sure we don't prune below the current base * Added BlockStore.Size() * config: added retain_blocks recommendations * Update v1 blockchain reactor to handle blockstore base * Added state database pruning * Propagate errors on missing validator sets * Comment tweaks * Improved error message Co-Authored-By: Anton Kaliaev * use ABCI field ResponseCommit.retain_height instead of retain-blocks config option * remove State.RetainHeight, return value instead * fix minor issues * rename pruneHeights() to pruneBlocks() * noop to fix GitHub borkage Co-authored-by: Anton Kaliaev --- CHANGELOG_PENDING.md | 8 + abci/example/kvstore/kvstore.go | 10 +- abci/types/types.pb.go | 341 ++++++++++++++++------------- abci/types/types.proto | 3 +- blockchain/v0/pool.go | 17 +- blockchain/v0/pool_test.go | 15 +- blockchain/v0/reactor.go | 46 +++- blockchain/v0/reactor_test.go | 2 +- blockchain/v1/peer.go | 6 +- blockchain/v1/peer_test.go | 16 +- blockchain/v1/pool.go | 11 +- blockchain/v1/pool_test.go | 66 ++++-- blockchain/v1/reactor.go | 35 ++- blockchain/v1/reactor_fsm.go | 7 +- blockchain/v1/reactor_test.go | 2 +- blockchain/v2/io.go | 9 +- blockchain/v2/processor_context.go | 2 +- blockchain/v2/reactor.go | 26 ++- blockchain/v2/reactor_test.go | 8 +- blockchain/v2/scheduler.go | 30 ++- blockchain/v2/scheduler_test.go | 92 ++++++-- config/config_test.go | 55 +++-- consensus/reactor.go | 30 +-- consensus/replay.go | 9 +- consensus/replay_test.go | 52 ++++- consensus/state.go | 44 +++- evidence/pool.go | 9 +- rpc/core/blocks.go | 29 +-- rpc/core/blocks_test.go | 51 +++-- rpc/core/consensus.go | 4 +- rpc/core/status.go | 30 ++- rpc/core/types/responses.go | 8 +- rpc/swagger/swagger.yaml | 12 + state/errors.go | 26 ++- state/execution.go | 29 +-- state/execution_test.go | 12 +- state/helpers_test.go | 2 +- state/services.go | 4 + state/store.go | 96 ++++++++ state/store_test.go | 115 ++++++++++ store/store.go | 117 +++++++++- store/store_test.go | 115 +++++++++- 42 files changed, 1208 insertions(+), 393 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index d605e36d3..79d66bc05 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,5 +1,7 @@ ## v0.33.3 +- Nodes are no longer guaranteed to contain all blocks up to the latest height. The ABCI app can now control which blocks to retain through the ABCI field `ResponseCommit.retain_height`, all blocks and associated data below this height will be removed. + \*\* Special thanks to external contributors on this release: @@ -12,6 +14,8 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi - Apps +- P2P Protocol + - Go API - [rpc/client] [\#4628](https://github.com/tendermint/tendermint/pull/4628) Split out HTTP and local clients into `http` and `local` packages (@erikgrinaker). @@ -20,10 +24,14 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi ### FEATURES: +- [abci] Add `ResponseCommit.retain_height` field, which will automatically remove blocks below this height. +- [rpc] Add `/status` response fields for the earliest block available on the node - [rpc] [\#4611](https://github.com/tendermint/tendermint/pull/4611) Add `codespace` to `ResultBroadcastTx` (@whylee259) ### IMPROVEMENTS: +- [blockchain] Add `Base` to blockchain reactor P2P messages `StatusRequest` and `StatusResponse` +- [example/kvstore] Add `RetainBlocks` option to control block retention - [p2p] [\#4548](https://github.com/tendermint/tendermint/pull/4548) Add ban list to address book (@cmwaters) - [privval] \#4534 Add `error` as a return value on`GetPubKey()` - [Docker] \#4569 Default configuration added to docker image (you can still mount your own config the same way) (@greg-szabo) diff --git a/abci/example/kvstore/kvstore.go b/abci/example/kvstore/kvstore.go index ef9dcee92..42f00231f 100644 --- a/abci/example/kvstore/kvstore.go +++ b/abci/example/kvstore/kvstore.go @@ -64,7 +64,8 @@ var _ types.Application = (*Application)(nil) type Application struct { types.BaseApplication - state State + state State + RetainBlocks int64 // blocks to retain after commit (via ResponseCommit.RetainHeight) } func NewApplication() *Application { @@ -119,7 +120,12 @@ func (app *Application) Commit() types.ResponseCommit { app.state.AppHash = appHash app.state.Height++ saveState(app.state) - return types.ResponseCommit{Data: appHash} + + resp := types.ResponseCommit{Data: appHash} + if app.RetainBlocks > 0 && app.state.Height >= app.RetainBlocks { + resp.RetainHeight = app.state.Height - app.RetainBlocks + 1 + } + return resp } // Returns an associated value or nil if missing. diff --git a/abci/types/types.pb.go b/abci/types/types.pb.go index 6c18cfbd1..51ff4aedd 100644 --- a/abci/types/types.pb.go +++ b/abci/types/types.pb.go @@ -1865,6 +1865,7 @@ func (m *ResponseEndBlock) GetEvents() []Event { type ResponseCommit struct { // reserve 1 Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + RetainHeight int64 `protobuf:"varint,3,opt,name=retain_height,json=retainHeight,proto3" json:"retain_height,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1910,6 +1911,13 @@ func (m *ResponseCommit) GetData() []byte { return nil } +func (m *ResponseCommit) GetRetainHeight() int64 { + if m != nil { + return m.RetainHeight + } + return 0 +} + // ConsensusParams contains all consensus-relevant parameters // that can be adjusted by the abci app type ConsensusParams struct { @@ -2960,155 +2968,156 @@ func init() { proto.RegisterFile("abci/types/types.proto", fileDescriptor_9f1eaa func init() { golang_proto.RegisterFile("abci/types/types.proto", fileDescriptor_9f1eaa49c51fa1ac) } var fileDescriptor_9f1eaa49c51fa1ac = []byte{ - // 2370 bytes of a gzipped FileDescriptorProto + // 2386 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x59, 0x4d, 0x90, 0x1b, 0x47, - 0x15, 0xde, 0xd1, 0x6a, 0x57, 0xd2, 0xd3, 0xee, 0x4a, 0x69, 0x3b, 0x89, 0x22, 0x92, 0x5d, 0xd7, - 0xf8, 0x6f, 0x9d, 0x04, 0x6d, 0x58, 0x2a, 0x54, 0x8c, 0x5d, 0xa1, 0x56, 0x6b, 0x07, 0xa9, 0x62, - 0x3b, 0x9b, 0xb1, 0xbd, 0x18, 0xa8, 0xca, 0x54, 0x4b, 0xd3, 0x96, 0xa6, 0x56, 0x9a, 0x99, 0xcc, - 0xb4, 0x64, 0x89, 0xe2, 0x4e, 0x51, 0xc5, 0x81, 0x0b, 0x55, 0x5c, 0xb8, 0x73, 0xe4, 0xc0, 0x21, - 0x47, 0x8e, 0x39, 0x70, 0xe0, 0xc0, 0xd9, 0xc0, 0xc2, 0x89, 0xca, 0x91, 0xa2, 0x38, 0x52, 0xfd, - 0xba, 0xe7, 0x4f, 0x2b, 0xad, 0xc6, 0xc1, 0x37, 0x2e, 0xd2, 0x74, 0xf7, 0x7b, 0xaf, 0xbb, 0x5f, - 0xbf, 0x7e, 0xdf, 0x7b, 0xaf, 0xe1, 0x35, 0xda, 0xe9, 0xda, 0x7b, 0x7c, 0xea, 0xb1, 0x40, 0xfe, - 0x36, 0x3c, 0xdf, 0xe5, 0x2e, 0x79, 0x95, 0x33, 0xc7, 0x62, 0xfe, 0xd0, 0x76, 0x78, 0x43, 0x90, - 0x34, 0x70, 0xb0, 0x7e, 0x8d, 0xf7, 0x6d, 0xdf, 0x32, 0x3d, 0xea, 0xf3, 0xe9, 0x1e, 0x52, 0xee, - 0xf5, 0xdc, 0x9e, 0x1b, 0x7f, 0x49, 0xf6, 0x7a, 0xbd, 0xeb, 0x4f, 0x3d, 0xee, 0xee, 0x0d, 0x99, - 0x7f, 0x32, 0x60, 0xea, 0x4f, 0x8d, 0x5d, 0x18, 0xd8, 0x9d, 0x60, 0xef, 0x64, 0x9c, 0x9c, 0xaf, - 0xbe, 0xd3, 0x73, 0xdd, 0xde, 0x80, 0x49, 0x99, 0x9d, 0xd1, 0xd3, 0x3d, 0x6e, 0x0f, 0x59, 0xc0, - 0xe9, 0xd0, 0x53, 0x04, 0xdb, 0xb3, 0x04, 0xd6, 0xc8, 0xa7, 0xdc, 0x76, 0x1d, 0x39, 0xae, 0xff, - 0x7b, 0x0d, 0x0a, 0x06, 0xfb, 0x7c, 0xc4, 0x02, 0x4e, 0x3e, 0x80, 0x3c, 0xeb, 0xf6, 0xdd, 0x5a, - 0xee, 0x92, 0xb6, 0x5b, 0xde, 0xd7, 0x1b, 0x73, 0xf7, 0xd2, 0x50, 0xd4, 0x77, 0xbb, 0x7d, 0xb7, - 0xb5, 0x62, 0x20, 0x07, 0xb9, 0x05, 0x6b, 0x4f, 0x07, 0xa3, 0xa0, 0x5f, 0x5b, 0x45, 0xd6, 0xcb, - 0xe7, 0xb3, 0x7e, 0x24, 0x48, 0x5b, 0x2b, 0x86, 0xe4, 0x11, 0xd3, 0xda, 0xce, 0x53, 0xb7, 0x96, - 0xcf, 0x32, 0x6d, 0xdb, 0x79, 0x8a, 0xd3, 0x0a, 0x0e, 0xd2, 0x02, 0x08, 0x18, 0x37, 0x5d, 0x4f, - 0x6c, 0xa8, 0xb6, 0x86, 0xfc, 0xd7, 0xcf, 0xe7, 0x7f, 0xc8, 0xf8, 0x27, 0x48, 0xde, 0x5a, 0x31, - 0x4a, 0x41, 0xd8, 0x10, 0x92, 0x6c, 0xc7, 0xe6, 0x66, 0xb7, 0x4f, 0x6d, 0xa7, 0xb6, 0x9e, 0x45, - 0x52, 0xdb, 0xb1, 0xf9, 0xa1, 0x20, 0x17, 0x92, 0xec, 0xb0, 0x21, 0x54, 0xf1, 0xf9, 0x88, 0xf9, - 0xd3, 0x5a, 0x21, 0x8b, 0x2a, 0x3e, 0x15, 0xa4, 0x42, 0x15, 0xc8, 0x43, 0x3e, 0x86, 0x72, 0x87, - 0xf5, 0x6c, 0xc7, 0xec, 0x0c, 0xdc, 0xee, 0x49, 0xad, 0x88, 0x22, 0x76, 0xcf, 0x17, 0xd1, 0x14, - 0x0c, 0x4d, 0x41, 0xdf, 0x5a, 0x31, 0xa0, 0x13, 0xb5, 0x48, 0x13, 0x8a, 0xdd, 0x3e, 0xeb, 0x9e, - 0x98, 0x7c, 0x52, 0x2b, 0xa1, 0xa4, 0xab, 0xe7, 0x4b, 0x3a, 0x14, 0xd4, 0x8f, 0x26, 0xad, 0x15, - 0xa3, 0xd0, 0x95, 0x9f, 0x42, 0x2f, 0x16, 0x1b, 0xd8, 0x63, 0xe6, 0x0b, 0x29, 0x17, 0xb2, 0xe8, - 0xe5, 0x8e, 0xa4, 0x47, 0x39, 0x25, 0x2b, 0x6c, 0x90, 0xbb, 0x50, 0x62, 0x8e, 0xa5, 0x36, 0x56, - 0x46, 0x41, 0xd7, 0x96, 0x58, 0x98, 0x63, 0x85, 0xdb, 0x2a, 0x32, 0xf5, 0x4d, 0x3e, 0x84, 0xf5, - 0xae, 0x3b, 0x1c, 0xda, 0xbc, 0xb6, 0x81, 0x32, 0xae, 0x2c, 0xd9, 0x12, 0xd2, 0xb6, 0x56, 0x0c, - 0xc5, 0xd5, 0x2c, 0xc0, 0xda, 0x98, 0x0e, 0x46, 0x4c, 0xbf, 0x0e, 0xe5, 0x84, 0x25, 0x93, 0x1a, - 0x14, 0x86, 0x2c, 0x08, 0x68, 0x8f, 0xd5, 0xb4, 0x4b, 0xda, 0x6e, 0xc9, 0x08, 0x9b, 0xfa, 0x16, - 0x6c, 0x24, 0xed, 0x56, 0x1f, 0x46, 0x8c, 0xc2, 0x16, 0x05, 0xe3, 0x98, 0xf9, 0x81, 0x30, 0x40, - 0xc5, 0xa8, 0x9a, 0xe4, 0x32, 0x6c, 0xe2, 0x6e, 0xcd, 0x70, 0x5c, 0xdc, 0xab, 0xbc, 0xb1, 0x81, - 0x9d, 0xc7, 0x8a, 0x68, 0x07, 0xca, 0xde, 0xbe, 0x17, 0x91, 0xac, 0x22, 0x09, 0x78, 0xfb, 0x9e, - 0x22, 0xd0, 0xbf, 0x0b, 0xd5, 0x59, 0xd3, 0x25, 0x55, 0x58, 0x3d, 0x61, 0x53, 0x35, 0x9f, 0xf8, - 0x24, 0x17, 0xd5, 0xb6, 0x70, 0x8e, 0x92, 0xa1, 0xf6, 0xf8, 0xbb, 0x5c, 0xc4, 0x1c, 0x59, 0xab, - 0xb8, 0x6e, 0xc2, 0x49, 0x20, 0x77, 0x79, 0xbf, 0xde, 0x90, 0x0e, 0xa2, 0x11, 0x3a, 0x88, 0xc6, - 0xa3, 0xd0, 0x83, 0x34, 0x8b, 0x5f, 0x3e, 0xdf, 0x59, 0xf9, 0xe5, 0x5f, 0x76, 0x34, 0x03, 0x39, - 0xc8, 0x1b, 0xc2, 0xa0, 0xa8, 0xed, 0x98, 0xb6, 0xa5, 0xe6, 0x29, 0x60, 0xbb, 0x6d, 0x91, 0x4f, - 0xa1, 0xda, 0x75, 0x9d, 0x80, 0x39, 0xc1, 0x28, 0x10, 0x6e, 0x8e, 0x0e, 0x03, 0xe5, 0x0b, 0x16, - 0x1d, 0xf2, 0x61, 0x48, 0x7e, 0x84, 0xd4, 0x46, 0xa5, 0x9b, 0xee, 0x20, 0xf7, 0x00, 0xc6, 0x74, - 0x60, 0x5b, 0x94, 0xbb, 0x7e, 0x50, 0xcb, 0x5f, 0x5a, 0x3d, 0x47, 0xd8, 0x71, 0x48, 0xf8, 0xd8, - 0xb3, 0x28, 0x67, 0xcd, 0xbc, 0x58, 0xb9, 0x91, 0xe0, 0x27, 0xd7, 0xa0, 0x42, 0x3d, 0xcf, 0x0c, - 0x38, 0xe5, 0xcc, 0xec, 0x4c, 0x39, 0x0b, 0xd0, 0x5f, 0x6c, 0x18, 0x9b, 0xd4, 0xf3, 0x1e, 0x8a, - 0xde, 0xa6, 0xe8, 0xd4, 0xad, 0xe8, 0xb4, 0xf1, 0x6a, 0x12, 0x02, 0x79, 0x8b, 0x72, 0x8a, 0xda, - 0xda, 0x30, 0xf0, 0x5b, 0xf4, 0x79, 0x94, 0xf7, 0x95, 0x0e, 0xf0, 0x9b, 0xbc, 0x06, 0xeb, 0x7d, - 0x66, 0xf7, 0xfa, 0x1c, 0xb7, 0xbd, 0x6a, 0xa8, 0x96, 0x38, 0x18, 0xcf, 0x77, 0xc7, 0x0c, 0xbd, - 0x5b, 0xd1, 0x90, 0x0d, 0xfd, 0x57, 0x39, 0x78, 0xe5, 0xcc, 0xf5, 0x15, 0x72, 0xfb, 0x34, 0xe8, - 0x87, 0x73, 0x89, 0x6f, 0x72, 0x4b, 0xc8, 0xa5, 0x16, 0xf3, 0x95, 0x57, 0x7e, 0x6b, 0x81, 0x06, - 0x5a, 0x48, 0xa4, 0x36, 0xae, 0x58, 0xc8, 0x63, 0xa8, 0x0e, 0x68, 0xc0, 0x4d, 0x69, 0xfb, 0x26, - 0x7a, 0xd9, 0xd5, 0x73, 0x3d, 0xc1, 0x3d, 0x1a, 0xde, 0x19, 0x61, 0xdc, 0x4a, 0xdc, 0xd6, 0x20, - 0xd5, 0x4b, 0x9e, 0xc0, 0xc5, 0xce, 0xf4, 0x27, 0xd4, 0xe1, 0xb6, 0xc3, 0xcc, 0x33, 0x67, 0xb4, - 0xb3, 0x40, 0xf4, 0xdd, 0xb1, 0x6d, 0x31, 0xa7, 0x1b, 0x1e, 0xce, 0x85, 0x48, 0x44, 0x74, 0x78, - 0x81, 0xfe, 0x04, 0xb6, 0xd2, 0xbe, 0x88, 0x6c, 0x41, 0x8e, 0x4f, 0x94, 0x46, 0x72, 0x7c, 0x42, - 0xbe, 0x03, 0x79, 0x21, 0x0e, 0xb5, 0xb1, 0xb5, 0x10, 0x2c, 0x14, 0xf7, 0xa3, 0xa9, 0xc7, 0x0c, - 0xa4, 0xd7, 0xf5, 0xe8, 0x26, 0x44, 0xfe, 0x69, 0x56, 0xb6, 0x7e, 0x03, 0x2a, 0x33, 0xae, 0x27, - 0x71, 0xac, 0x5a, 0xf2, 0x58, 0xf5, 0x0a, 0x6c, 0xa6, 0x3c, 0x8c, 0xfe, 0xc7, 0x75, 0x28, 0x1a, - 0x2c, 0xf0, 0x84, 0x11, 0x93, 0x16, 0x94, 0xd8, 0xa4, 0xcb, 0x24, 0x2c, 0x69, 0x4b, 0x9c, 0xb8, - 0xe4, 0xb9, 0x1b, 0xd2, 0x0b, 0xaf, 0x19, 0x31, 0x93, 0x9b, 0x29, 0x48, 0xbe, 0xbc, 0x4c, 0x48, - 0x12, 0x93, 0x6f, 0xa7, 0x31, 0xf9, 0xca, 0x12, 0xde, 0x19, 0x50, 0xbe, 0x99, 0x02, 0xe5, 0x65, - 0x13, 0xa7, 0x50, 0xb9, 0x3d, 0x07, 0x95, 0x97, 0x6d, 0x7f, 0x01, 0x2c, 0xb7, 0xe7, 0xc0, 0xf2, - 0xee, 0xd2, 0xb5, 0xcc, 0xc5, 0xe5, 0xdb, 0x69, 0x5c, 0x5e, 0xa6, 0x8e, 0x19, 0x60, 0xbe, 0x37, - 0x0f, 0x98, 0x6f, 0x2c, 0x91, 0xb1, 0x10, 0x99, 0x0f, 0xcf, 0x20, 0xf3, 0xb5, 0x25, 0xa2, 0xe6, - 0x40, 0x73, 0x3b, 0x05, 0xcd, 0x90, 0x49, 0x37, 0x0b, 0xb0, 0xf9, 0xa3, 0xb3, 0xd8, 0x7c, 0x7d, - 0x99, 0xa9, 0xcd, 0x03, 0xe7, 0xef, 0xcd, 0x80, 0xf3, 0xd5, 0x65, 0xbb, 0x5a, 0x88, 0xce, 0x37, - 0x84, 0x7f, 0x9c, 0xb9, 0x19, 0xc2, 0x97, 0x32, 0xdf, 0x77, 0x7d, 0x05, 0x7c, 0xb2, 0xa1, 0xef, - 0x0a, 0x8f, 0x1d, 0xdb, 0xff, 0x39, 0x48, 0x8e, 0x97, 0x36, 0x61, 0xed, 0xfa, 0x17, 0x5a, 0xcc, - 0x8b, 0x9e, 0x2d, 0xe9, 0xed, 0x4b, 0xca, 0xdb, 0x27, 0x00, 0x3e, 0x97, 0x06, 0xf8, 0x1d, 0x28, - 0x0b, 0x4c, 0x99, 0xc1, 0x6e, 0xea, 0x85, 0xd8, 0x4d, 0xde, 0x86, 0x57, 0xd0, 0xff, 0xca, 0x30, - 0x40, 0x39, 0x92, 0x3c, 0x3a, 0x92, 0x8a, 0x18, 0x90, 0x1a, 0x94, 0x40, 0xf1, 0x4d, 0xb8, 0x90, - 0xa0, 0x15, 0x72, 0x11, 0x0b, 0x24, 0x48, 0x55, 0x23, 0xea, 0x03, 0xcf, 0x6b, 0xd1, 0xa0, 0xaf, - 0xdf, 0x8f, 0x15, 0x14, 0xc7, 0x05, 0x04, 0xf2, 0x5d, 0xd7, 0x92, 0xfb, 0xde, 0x34, 0xf0, 0x5b, - 0xc4, 0x0a, 0x03, 0xb7, 0x87, 0x8b, 0x2b, 0x19, 0xe2, 0x53, 0x50, 0x45, 0x57, 0xbb, 0x24, 0xef, - 0xac, 0xfe, 0x7b, 0x2d, 0x96, 0x17, 0x87, 0x0a, 0xf3, 0x50, 0x5d, 0x7b, 0x99, 0xa8, 0x9e, 0xfb, - 0xdf, 0x50, 0x5d, 0xff, 0x97, 0x16, 0x1f, 0x69, 0x84, 0xd7, 0x5f, 0x4f, 0x05, 0xc2, 0xba, 0x6c, - 0xc7, 0x62, 0x13, 0x54, 0xf9, 0xaa, 0x21, 0x1b, 0x61, 0xa8, 0xb5, 0x8e, 0xc7, 0x90, 0x0e, 0xb5, - 0x0a, 0xd8, 0x27, 0x1b, 0xe4, 0x7d, 0xc4, 0x79, 0xf7, 0xa9, 0x72, 0x0d, 0x29, 0x10, 0x94, 0x49, - 0x5d, 0x43, 0x65, 0x73, 0x47, 0x82, 0xcc, 0x90, 0xd4, 0x09, 0x7c, 0x29, 0xa5, 0xc2, 0x86, 0x37, - 0xa1, 0x24, 0x96, 0x1e, 0x78, 0xb4, 0xcb, 0xf0, 0x6e, 0x97, 0x8c, 0xb8, 0x43, 0xb7, 0x80, 0x9c, - 0xf5, 0x31, 0xe4, 0x01, 0xac, 0xb3, 0x31, 0x73, 0xb8, 0x38, 0x23, 0xa1, 0xd6, 0x37, 0x17, 0x02, - 0x31, 0x73, 0x78, 0xb3, 0x26, 0x94, 0xf9, 0xcf, 0xe7, 0x3b, 0x55, 0xc9, 0xf3, 0xae, 0x3b, 0xb4, - 0x39, 0x1b, 0x7a, 0x7c, 0x6a, 0x28, 0x29, 0xfa, 0xcf, 0x72, 0x02, 0x0f, 0x53, 0xfe, 0x67, 0xae, - 0x7a, 0xc3, 0x4b, 0x93, 0x4b, 0x84, 0x48, 0xd9, 0x54, 0xfe, 0x16, 0x40, 0x8f, 0x06, 0xe6, 0x33, - 0xea, 0x70, 0x66, 0x29, 0xbd, 0x97, 0x7a, 0x34, 0xf8, 0x01, 0x76, 0x88, 0x78, 0x53, 0x0c, 0x8f, - 0x02, 0x66, 0xe1, 0x01, 0xac, 0x1a, 0x85, 0x1e, 0x0d, 0x1e, 0x07, 0xcc, 0x4a, 0xec, 0xb5, 0xf0, - 0x32, 0xf6, 0x9a, 0xd6, 0x77, 0x71, 0x56, 0xdf, 0x3f, 0xcf, 0xc5, 0xb7, 0x23, 0x0e, 0x1f, 0xfe, - 0x3f, 0x75, 0xf1, 0x1b, 0xcc, 0x29, 0xd2, 0x20, 0x40, 0x7e, 0x08, 0xaf, 0x44, 0xb7, 0xd2, 0x1c, - 0xe1, 0x6d, 0x0d, 0xad, 0xf0, 0xc5, 0x2e, 0x77, 0x75, 0x9c, 0xee, 0x0e, 0xc8, 0x67, 0xf0, 0xfa, - 0x8c, 0x0f, 0x8a, 0x26, 0xc8, 0xbd, 0x90, 0x2b, 0x7a, 0x35, 0xed, 0x8a, 0x42, 0xf9, 0xb1, 0xf6, - 0x56, 0x5f, 0xca, 0xad, 0xb9, 0x22, 0x42, 0xd8, 0x24, 0xbc, 0xcd, 0xb3, 0x09, 0xfd, 0xcf, 0x1a, - 0x54, 0x66, 0x16, 0x48, 0x3e, 0x80, 0x35, 0x89, 0xc0, 0xda, 0xb9, 0x85, 0x10, 0xd4, 0xb8, 0xda, - 0x93, 0x64, 0x20, 0x07, 0x50, 0x64, 0x2a, 0xba, 0x56, 0x4a, 0xb9, 0xba, 0x24, 0x08, 0x57, 0xfc, - 0x11, 0x1b, 0xb9, 0x03, 0xa5, 0x48, 0xf5, 0x4b, 0x32, 0xb7, 0xe8, 0xe4, 0x94, 0x90, 0x98, 0x51, - 0x3f, 0x84, 0x72, 0x62, 0x79, 0xe4, 0x1b, 0x50, 0x1a, 0xd2, 0x89, 0x4a, 0xb7, 0x64, 0x00, 0x5d, - 0x1c, 0xd2, 0x09, 0x66, 0x5a, 0xe4, 0x75, 0x28, 0x88, 0xc1, 0x1e, 0x95, 0x07, 0xb9, 0x6a, 0xac, - 0x0f, 0xe9, 0xe4, 0xfb, 0x34, 0xd0, 0x7f, 0xa1, 0xc1, 0x56, 0x7a, 0x9d, 0xe4, 0x1d, 0x20, 0x82, - 0x96, 0xf6, 0x98, 0xe9, 0x8c, 0x86, 0x12, 0x23, 0x43, 0x89, 0x95, 0x21, 0x9d, 0x1c, 0xf4, 0xd8, - 0x83, 0xd1, 0x10, 0xa7, 0x0e, 0xc8, 0x7d, 0xa8, 0x86, 0xc4, 0x61, 0xb1, 0x4b, 0x69, 0xe5, 0x8d, - 0x33, 0xc9, 0xee, 0x1d, 0x45, 0x20, 0x73, 0xdd, 0x5f, 0x8b, 0x5c, 0x77, 0x4b, 0xca, 0x0b, 0x47, - 0xf4, 0xf7, 0xa1, 0x32, 0xb3, 0x63, 0xa2, 0xc3, 0xa6, 0x37, 0xea, 0x98, 0x27, 0x6c, 0x6a, 0xa2, - 0x4a, 0xd0, 0xd4, 0x4b, 0x46, 0xd9, 0x1b, 0x75, 0x3e, 0x66, 0x53, 0x91, 0x75, 0x04, 0x7a, 0x17, - 0xb6, 0xd2, 0xc9, 0x94, 0x00, 0x0e, 0xdf, 0x1d, 0x39, 0x16, 0xae, 0x7b, 0xcd, 0x90, 0x0d, 0x72, - 0x0b, 0xd6, 0xc6, 0xae, 0xb4, 0xe6, 0xf3, 0xb2, 0xa7, 0x63, 0x97, 0xb3, 0x44, 0x4a, 0x26, 0x79, - 0xf4, 0x00, 0xd6, 0xd0, 0x2e, 0x85, 0x8d, 0x61, 0x5a, 0xa4, 0x02, 0x17, 0xf1, 0x4d, 0x8e, 0x01, - 0x28, 0xe7, 0xbe, 0xdd, 0x19, 0xc5, 0xe2, 0x6b, 0x49, 0xf1, 0x03, 0xbb, 0x13, 0x34, 0x4e, 0xc6, - 0x8d, 0x23, 0x6a, 0xfb, 0xcd, 0x37, 0x95, 0x65, 0x5f, 0x8c, 0x79, 0x12, 0xd6, 0x9d, 0x90, 0xa4, - 0x7f, 0x95, 0x87, 0x75, 0x99, 0x6e, 0x92, 0x0f, 0xd3, 0xc5, 0x8f, 0xf2, 0xfe, 0xf6, 0xa2, 0xe5, - 0x4b, 0x2a, 0xb5, 0xfa, 0x28, 0x82, 0xba, 0x36, 0x5b, 0x51, 0x68, 0x96, 0x4f, 0x9f, 0xef, 0x14, - 0x30, 0xfa, 0x68, 0xdf, 0x89, 0xcb, 0x0b, 0x8b, 0xb2, 0xeb, 0xb0, 0x96, 0x91, 0x7f, 0xe1, 0x5a, - 0x46, 0x0b, 0x36, 0x13, 0xe1, 0x96, 0x6d, 0xa9, 0x3c, 0x65, 0xfb, 0xbc, 0x4b, 0xd7, 0xbe, 0xa3, - 0xd6, 0x5f, 0x8e, 0xc2, 0xb1, 0xb6, 0x45, 0x76, 0xd3, 0x49, 0x36, 0x46, 0x6d, 0x32, 0x5c, 0x48, - 0xe4, 0xcd, 0x22, 0x66, 0x13, 0xd7, 0x41, 0x5c, 0x7e, 0x49, 0x22, 0xa3, 0x87, 0xa2, 0xe8, 0xc0, - 0xc1, 0xeb, 0x50, 0x89, 0x03, 0x1b, 0x49, 0x52, 0x94, 0x52, 0xe2, 0x6e, 0x24, 0x7c, 0x0f, 0x2e, - 0x3a, 0x6c, 0xc2, 0xcd, 0x59, 0xea, 0x12, 0x52, 0x13, 0x31, 0x76, 0x9c, 0xe6, 0xb8, 0x0a, 0x5b, - 0xb1, 0x0b, 0x45, 0x5a, 0x90, 0xa5, 0x8f, 0xa8, 0x17, 0xc9, 0xde, 0x80, 0x62, 0x14, 0x76, 0x96, - 0x91, 0xa0, 0x40, 0x65, 0xb4, 0x19, 0x05, 0xb2, 0x3e, 0x0b, 0x46, 0x03, 0xae, 0x84, 0x6c, 0x20, - 0x0d, 0x06, 0xb2, 0x86, 0xec, 0x47, 0xda, 0xcb, 0xb0, 0x19, 0x7a, 0x15, 0x49, 0xb7, 0x89, 0x74, - 0x1b, 0x61, 0x27, 0x12, 0xdd, 0x80, 0xaa, 0xe7, 0xbb, 0x9e, 0x1b, 0x30, 0xdf, 0xa4, 0x96, 0xe5, - 0xb3, 0x20, 0xa8, 0x6d, 0x49, 0x79, 0x61, 0xff, 0x81, 0xec, 0xd6, 0xbf, 0x05, 0x85, 0x30, 0x9e, - 0xbe, 0x08, 0x6b, 0xcd, 0xc8, 0x43, 0xe6, 0x0d, 0xd9, 0x10, 0xf8, 0x7a, 0xe0, 0x79, 0xaa, 0xba, - 0x26, 0x3e, 0xf5, 0x01, 0x14, 0xd4, 0x81, 0xcd, 0xad, 0xa9, 0xdc, 0x87, 0x0d, 0x8f, 0xfa, 0x62, - 0x1b, 0xc9, 0xca, 0xca, 0xa2, 0x8c, 0xf0, 0x88, 0xfa, 0xfc, 0x21, 0xe3, 0xa9, 0x02, 0x4b, 0x19, - 0xf9, 0x65, 0x97, 0x7e, 0x13, 0x36, 0x53, 0x34, 0x62, 0x99, 0xdc, 0xe5, 0x74, 0x10, 0x5e, 0x74, - 0x6c, 0x44, 0x2b, 0xc9, 0xc5, 0x2b, 0xd1, 0x6f, 0x41, 0x29, 0x3a, 0x2b, 0x91, 0x68, 0x84, 0xaa, - 0xd0, 0x94, 0xfa, 0x65, 0x13, 0x8b, 0x48, 0xee, 0x33, 0xe6, 0x2b, 0xeb, 0x97, 0x0d, 0x9d, 0x25, - 0x1c, 0x93, 0x44, 0x33, 0x72, 0x1b, 0x0a, 0xca, 0x31, 0xa9, 0xfb, 0xb8, 0xa8, 0x5c, 0x74, 0x84, - 0x9e, 0x2a, 0x2c, 0x17, 0x49, 0xbf, 0x15, 0x4f, 0x93, 0x4b, 0x4e, 0xf3, 0x53, 0x28, 0x86, 0xce, - 0x27, 0x8d, 0x12, 0x72, 0x86, 0x4b, 0xcb, 0x50, 0x42, 0x4d, 0x12, 0x33, 0x0a, 0x6b, 0x0a, 0xec, - 0x9e, 0xc3, 0x2c, 0x33, 0xbe, 0x82, 0x38, 0x67, 0xd1, 0xa8, 0xc8, 0x81, 0x7b, 0xe1, 0xfd, 0xd2, - 0xdf, 0x83, 0x75, 0xb9, 0xd6, 0xb9, 0x2e, 0x6e, 0x1e, 0xb4, 0xfe, 0x43, 0x83, 0x62, 0x08, 0x1f, - 0x73, 0x99, 0x52, 0x9b, 0xc8, 0x7d, 0xdd, 0x4d, 0xbc, 0x7c, 0x97, 0xf4, 0x2e, 0x10, 0xb4, 0x14, - 0x73, 0xec, 0x72, 0xdb, 0xe9, 0x99, 0xf2, 0x2c, 0x64, 0x24, 0x58, 0xc5, 0x91, 0x63, 0x1c, 0x38, - 0x12, 0xfd, 0x6f, 0x5f, 0x86, 0x72, 0xa2, 0xca, 0x45, 0x0a, 0xb0, 0xfa, 0x80, 0x3d, 0xab, 0xae, - 0x90, 0x32, 0x14, 0x0c, 0x86, 0x35, 0x82, 0xaa, 0xb6, 0xff, 0x55, 0x01, 0x2a, 0x07, 0xcd, 0xc3, - 0xf6, 0x81, 0xe7, 0x0d, 0xec, 0x2e, 0xe2, 0x19, 0xf9, 0x04, 0xf2, 0x98, 0x27, 0x67, 0x78, 0xdf, - 0xa9, 0x67, 0x29, 0x38, 0x11, 0x03, 0xd6, 0x30, 0x9d, 0x26, 0x59, 0x9e, 0x7d, 0xea, 0x99, 0xea, - 0x50, 0x62, 0x91, 0x68, 0x70, 0x19, 0x5e, 0x83, 0xea, 0x59, 0x8a, 0x53, 0xe4, 0x33, 0x28, 0xc5, - 0x79, 0x72, 0xd6, 0x37, 0xa2, 0x7a, 0xe6, 0xb2, 0x95, 0x90, 0x1f, 0x67, 0x06, 0x59, 0x5f, 0x48, - 0xea, 0x99, 0xeb, 0x35, 0xe4, 0x09, 0x14, 0xc2, 0x1c, 0x2c, 0xdb, 0x2b, 0x4e, 0x3d, 0x63, 0x49, - 0x49, 0x1c, 0x9f, 0x4c, 0x9d, 0xb3, 0x3c, 0x55, 0xd5, 0x33, 0xd5, 0xcd, 0xc8, 0x63, 0x58, 0x57, - 0xc1, 0x6f, 0xa6, 0xf7, 0x99, 0x7a, 0xb6, 0x42, 0x91, 0x50, 0x72, 0x5c, 0x9c, 0xc8, 0xfa, 0x3c, - 0x57, 0xcf, 0x5c, 0x30, 0x24, 0x14, 0x20, 0x91, 0x4f, 0x67, 0x7e, 0x77, 0xab, 0x67, 0x2f, 0x04, - 0x92, 0x1f, 0x43, 0x31, 0xca, 0x9a, 0x32, 0xbe, 0x7f, 0xd5, 0xb3, 0xd6, 0xe2, 0x9a, 0xed, 0xff, - 0xfc, 0x6d, 0x5b, 0xfb, 0xed, 0xe9, 0xb6, 0xf6, 0xc5, 0xe9, 0xb6, 0xf6, 0xe5, 0xe9, 0xb6, 0xf6, - 0xa7, 0xd3, 0x6d, 0xed, 0xaf, 0xa7, 0xdb, 0xda, 0x1f, 0xfe, 0xbe, 0xad, 0xfd, 0xe8, 0x9d, 0x9e, - 0xcd, 0xfb, 0xa3, 0x4e, 0xa3, 0xeb, 0x0e, 0xf7, 0x62, 0x81, 0xc9, 0xcf, 0xf8, 0x51, 0xbb, 0xb3, - 0x8e, 0x0e, 0xeb, 0xdb, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xce, 0x64, 0xb9, 0xe4, 0xe9, 0x1e, + 0x15, 0xde, 0xd1, 0x6a, 0x57, 0xd2, 0xd3, 0xfe, 0xc8, 0x6d, 0x27, 0x91, 0x85, 0xb3, 0xeb, 0x9a, + 0x8d, 0xed, 0x75, 0x12, 0xb4, 0x61, 0xa9, 0x50, 0x31, 0x76, 0x85, 0x5a, 0xad, 0x1d, 0xa4, 0x8a, + 0xed, 0x6c, 0xc6, 0xf6, 0x62, 0xa0, 0x2a, 0x53, 0x2d, 0x4d, 0x5b, 0x9a, 0x5a, 0x69, 0x66, 0x32, + 0xd3, 0x92, 0x25, 0x8a, 0x3b, 0x45, 0x15, 0x07, 0x2e, 0x54, 0x71, 0xe1, 0xce, 0x91, 0x03, 0x87, + 0x1c, 0x39, 0xe6, 0xc0, 0x81, 0x03, 0x67, 0x03, 0x0b, 0x27, 0x2a, 0x47, 0x8a, 0xe2, 0x48, 0xf5, + 0xeb, 0x9e, 0x3f, 0xad, 0xb4, 0x1a, 0x07, 0xdf, 0xb8, 0x48, 0xd3, 0x3d, 0xef, 0xbd, 0xee, 0x7e, + 0xfd, 0xde, 0xfb, 0xde, 0x7b, 0x03, 0xaf, 0xd3, 0x76, 0xc7, 0xde, 0xe3, 0x13, 0x8f, 0x05, 0xf2, + 0xb7, 0xee, 0xf9, 0x2e, 0x77, 0xc9, 0x6b, 0x9c, 0x39, 0x16, 0xf3, 0x07, 0xb6, 0xc3, 0xeb, 0x82, + 0xa4, 0x8e, 0x2f, 0x6b, 0xd7, 0x79, 0xcf, 0xf6, 0x2d, 0xd3, 0xa3, 0x3e, 0x9f, 0xec, 0x21, 0xe5, + 0x5e, 0xd7, 0xed, 0xba, 0xf1, 0x93, 0x64, 0xaf, 0xd5, 0x3a, 0xfe, 0xc4, 0xe3, 0xee, 0xde, 0x80, + 0xf9, 0x27, 0x7d, 0xa6, 0xfe, 0xd4, 0xbb, 0x8b, 0x7d, 0xbb, 0x1d, 0xec, 0x9d, 0x8c, 0x92, 0xeb, + 0xd5, 0xb6, 0xbb, 0xae, 0xdb, 0xed, 0x33, 0x29, 0xb3, 0x3d, 0x7c, 0xb6, 0xc7, 0xed, 0x01, 0x0b, + 0x38, 0x1d, 0x78, 0x8a, 0x60, 0x6b, 0x9a, 0xc0, 0x1a, 0xfa, 0x94, 0xdb, 0xae, 0x23, 0xdf, 0xeb, + 0xff, 0x5e, 0x81, 0x82, 0xc1, 0x3e, 0x1f, 0xb2, 0x80, 0x93, 0x0f, 0x20, 0xcf, 0x3a, 0x3d, 0xb7, + 0x9a, 0xbb, 0xaa, 0xed, 0x96, 0xf7, 0xf5, 0xfa, 0xcc, 0xb3, 0xd4, 0x15, 0xf5, 0xbd, 0x4e, 0xcf, + 0x6d, 0x2e, 0x19, 0xc8, 0x41, 0x6e, 0xc3, 0xca, 0xb3, 0xfe, 0x30, 0xe8, 0x55, 0x97, 0x91, 0x75, + 0xe7, 0x7c, 0xd6, 0x8f, 0x04, 0x69, 0x73, 0xc9, 0x90, 0x3c, 0x62, 0x59, 0xdb, 0x79, 0xe6, 0x56, + 0xf3, 0x59, 0x96, 0x6d, 0x39, 0xcf, 0x70, 0x59, 0xc1, 0x41, 0x9a, 0x00, 0x01, 0xe3, 0xa6, 0xeb, + 0x89, 0x03, 0x55, 0x57, 0x90, 0xff, 0xc6, 0xf9, 0xfc, 0x8f, 0x18, 0xff, 0x04, 0xc9, 0x9b, 0x4b, + 0x46, 0x29, 0x08, 0x07, 0x42, 0x92, 0xed, 0xd8, 0xdc, 0xec, 0xf4, 0xa8, 0xed, 0x54, 0x57, 0xb3, + 0x48, 0x6a, 0x39, 0x36, 0x3f, 0x14, 0xe4, 0x42, 0x92, 0x1d, 0x0e, 0x84, 0x2a, 0x3e, 0x1f, 0x32, + 0x7f, 0x52, 0x2d, 0x64, 0x51, 0xc5, 0xa7, 0x82, 0x54, 0xa8, 0x02, 0x79, 0xc8, 0xc7, 0x50, 0x6e, + 0xb3, 0xae, 0xed, 0x98, 0xed, 0xbe, 0xdb, 0x39, 0xa9, 0x16, 0x51, 0xc4, 0xee, 0xf9, 0x22, 0x1a, + 0x82, 0xa1, 0x21, 0xe8, 0x9b, 0x4b, 0x06, 0xb4, 0xa3, 0x11, 0x69, 0x40, 0xb1, 0xd3, 0x63, 0x9d, + 0x13, 0x93, 0x8f, 0xab, 0x25, 0x94, 0x74, 0xed, 0x7c, 0x49, 0x87, 0x82, 0xfa, 0xf1, 0xb8, 0xb9, + 0x64, 0x14, 0x3a, 0xf2, 0x51, 0xe8, 0xc5, 0x62, 0x7d, 0x7b, 0xc4, 0x7c, 0x21, 0xe5, 0x62, 0x16, + 0xbd, 0xdc, 0x95, 0xf4, 0x28, 0xa7, 0x64, 0x85, 0x03, 0x72, 0x0f, 0x4a, 0xcc, 0xb1, 0xd4, 0xc1, + 0xca, 0x28, 0xe8, 0xfa, 0x02, 0x0b, 0x73, 0xac, 0xf0, 0x58, 0x45, 0xa6, 0x9e, 0xc9, 0x87, 0xb0, + 0xda, 0x71, 0x07, 0x03, 0x9b, 0x57, 0xd7, 0x50, 0xc6, 0x5b, 0x0b, 0x8e, 0x84, 0xb4, 0xcd, 0x25, + 0x43, 0x71, 0x35, 0x0a, 0xb0, 0x32, 0xa2, 0xfd, 0x21, 0xd3, 0x6f, 0x40, 0x39, 0x61, 0xc9, 0xa4, + 0x0a, 0x85, 0x01, 0x0b, 0x02, 0xda, 0x65, 0x55, 0xed, 0xaa, 0xb6, 0x5b, 0x32, 0xc2, 0xa1, 0xbe, + 0x01, 0x6b, 0x49, 0xbb, 0xd5, 0x07, 0x11, 0xa3, 0xb0, 0x45, 0xc1, 0x38, 0x62, 0x7e, 0x20, 0x0c, + 0x50, 0x31, 0xaa, 0x21, 0xd9, 0x81, 0x75, 0x3c, 0xad, 0x19, 0xbe, 0x17, 0x7e, 0x95, 0x37, 0xd6, + 0x70, 0xf2, 0x58, 0x11, 0x6d, 0x43, 0xd9, 0xdb, 0xf7, 0x22, 0x92, 0x65, 0x24, 0x01, 0x6f, 0xdf, + 0x53, 0x04, 0xfa, 0x77, 0xa1, 0x32, 0x6d, 0xba, 0xa4, 0x02, 0xcb, 0x27, 0x6c, 0xa2, 0xd6, 0x13, + 0x8f, 0xe4, 0x92, 0x3a, 0x16, 0xae, 0x51, 0x32, 0xd4, 0x19, 0x7f, 0x97, 0x8b, 0x98, 0x23, 0x6b, + 0x15, 0xee, 0x26, 0x82, 0x04, 0x72, 0x97, 0xf7, 0x6b, 0x75, 0x19, 0x20, 0xea, 0x61, 0x80, 0xa8, + 0x3f, 0x0e, 0x23, 0x48, 0xa3, 0xf8, 0xe5, 0x8b, 0xed, 0xa5, 0x5f, 0xfe, 0x65, 0x5b, 0x33, 0x90, + 0x83, 0x5c, 0x16, 0x06, 0x45, 0x6d, 0xc7, 0xb4, 0x2d, 0xb5, 0x4e, 0x01, 0xc7, 0x2d, 0x8b, 0x7c, + 0x0a, 0x95, 0x8e, 0xeb, 0x04, 0xcc, 0x09, 0x86, 0x81, 0x08, 0x73, 0x74, 0x10, 0xa8, 0x58, 0x30, + 0xef, 0x92, 0x0f, 0x43, 0xf2, 0x23, 0xa4, 0x36, 0x36, 0x3b, 0xe9, 0x09, 0x72, 0x1f, 0x60, 0x44, + 0xfb, 0xb6, 0x45, 0xb9, 0xeb, 0x07, 0xd5, 0xfc, 0xd5, 0xe5, 0x73, 0x84, 0x1d, 0x87, 0x84, 0x4f, + 0x3c, 0x8b, 0x72, 0xd6, 0xc8, 0x8b, 0x9d, 0x1b, 0x09, 0x7e, 0x72, 0x1d, 0x36, 0xa9, 0xe7, 0x99, + 0x01, 0xa7, 0x9c, 0x99, 0xed, 0x09, 0x67, 0x01, 0xc6, 0x8b, 0x35, 0x63, 0x9d, 0x7a, 0xde, 0x23, + 0x31, 0xdb, 0x10, 0x93, 0xba, 0x15, 0xdd, 0x36, 0xba, 0x26, 0x21, 0x90, 0xb7, 0x28, 0xa7, 0xa8, + 0xad, 0x35, 0x03, 0x9f, 0xc5, 0x9c, 0x47, 0x79, 0x4f, 0xe9, 0x00, 0x9f, 0xc9, 0xeb, 0xb0, 0xda, + 0x63, 0x76, 0xb7, 0xc7, 0xf1, 0xd8, 0xcb, 0x86, 0x1a, 0x89, 0x8b, 0xf1, 0x7c, 0x77, 0xc4, 0x30, + 0xba, 0x15, 0x0d, 0x39, 0xd0, 0x7f, 0x95, 0x83, 0x0b, 0x67, 0xdc, 0x57, 0xc8, 0xed, 0xd1, 0xa0, + 0x17, 0xae, 0x25, 0x9e, 0xc9, 0x6d, 0x21, 0x97, 0x5a, 0xcc, 0x57, 0x51, 0xf9, 0xcd, 0x39, 0x1a, + 0x68, 0x22, 0x91, 0x3a, 0xb8, 0x62, 0x21, 0x4f, 0xa0, 0xd2, 0xa7, 0x01, 0x37, 0xa5, 0xed, 0x9b, + 0x18, 0x65, 0x97, 0xcf, 0x8d, 0x04, 0xf7, 0x69, 0xe8, 0x33, 0xc2, 0xb8, 0x95, 0xb8, 0x8d, 0x7e, + 0x6a, 0x96, 0x3c, 0x85, 0x4b, 0xed, 0xc9, 0x4f, 0xa8, 0xc3, 0x6d, 0x87, 0x99, 0x67, 0xee, 0x68, + 0x7b, 0x8e, 0xe8, 0x7b, 0x23, 0xdb, 0x62, 0x4e, 0x27, 0xbc, 0x9c, 0x8b, 0x91, 0x88, 0xe8, 0xf2, + 0x02, 0xfd, 0x29, 0x6c, 0xa4, 0x63, 0x11, 0xd9, 0x80, 0x1c, 0x1f, 0x2b, 0x8d, 0xe4, 0xf8, 0x98, + 0x7c, 0x07, 0xf2, 0x42, 0x1c, 0x6a, 0x63, 0x63, 0x2e, 0x58, 0x28, 0xee, 0xc7, 0x13, 0x8f, 0x19, + 0x48, 0xaf, 0xeb, 0x91, 0x27, 0x44, 0xf1, 0x69, 0x5a, 0xb6, 0x7e, 0x13, 0x36, 0xa7, 0x42, 0x4f, + 0xe2, 0x5a, 0xb5, 0xe4, 0xb5, 0xea, 0x9b, 0xb0, 0x9e, 0x8a, 0x30, 0xfa, 0x1f, 0x57, 0xa1, 0x68, + 0xb0, 0xc0, 0x13, 0x46, 0x4c, 0x9a, 0x50, 0x62, 0xe3, 0x0e, 0x93, 0xb0, 0xa4, 0x2d, 0x08, 0xe2, + 0x92, 0xe7, 0x5e, 0x48, 0x2f, 0xa2, 0x66, 0xc4, 0x4c, 0x6e, 0xa5, 0x20, 0x79, 0x67, 0x91, 0x90, + 0x24, 0x26, 0xdf, 0x49, 0x63, 0xf2, 0x5b, 0x0b, 0x78, 0xa7, 0x40, 0xf9, 0x56, 0x0a, 0x94, 0x17, + 0x2d, 0x9c, 0x42, 0xe5, 0xd6, 0x0c, 0x54, 0x5e, 0x74, 0xfc, 0x39, 0xb0, 0xdc, 0x9a, 0x01, 0xcb, + 0xbb, 0x0b, 0xf7, 0x32, 0x13, 0x97, 0xef, 0xa4, 0x71, 0x79, 0x91, 0x3a, 0xa6, 0x80, 0xf9, 0xfe, + 0x2c, 0x60, 0xbe, 0xb9, 0x40, 0xc6, 0x5c, 0x64, 0x3e, 0x3c, 0x83, 0xcc, 0xd7, 0x17, 0x88, 0x9a, + 0x01, 0xcd, 0xad, 0x14, 0x34, 0x43, 0x26, 0xdd, 0xcc, 0xc1, 0xe6, 0x8f, 0xce, 0x62, 0xf3, 0x8d, + 0x45, 0xa6, 0x36, 0x0b, 0x9c, 0xbf, 0x37, 0x05, 0xce, 0xd7, 0x16, 0x9d, 0x6a, 0x2e, 0x3a, 0xdf, + 0x14, 0xf1, 0x71, 0xca, 0x33, 0x44, 0x2c, 0x65, 0xbe, 0xef, 0xfa, 0x0a, 0xf8, 0xe4, 0x40, 0xdf, + 0x15, 0x11, 0x3b, 0xb6, 0xff, 0x73, 0x90, 0x1c, 0x9d, 0x36, 0x61, 0xed, 0xfa, 0x17, 0x5a, 0xcc, + 0x8b, 0x91, 0x2d, 0x19, 0xed, 0x4b, 0x2a, 0xda, 0x27, 0x00, 0x3e, 0x97, 0x06, 0xf8, 0x6d, 0x28, + 0x0b, 0x4c, 0x99, 0xc2, 0x6e, 0xea, 0x85, 0xd8, 0x4d, 0xde, 0x86, 0x0b, 0x18, 0x7f, 0x65, 0x1a, + 0xa0, 0x02, 0x49, 0x1e, 0x03, 0xc9, 0xa6, 0x78, 0x21, 0x35, 0x28, 0x81, 0xe2, 0x9b, 0x70, 0x31, + 0x41, 0x2b, 0xe4, 0x22, 0x16, 0x48, 0x90, 0xaa, 0x44, 0xd4, 0x07, 0x9e, 0xd7, 0xa4, 0x41, 0x4f, + 0x7f, 0x10, 0x2b, 0x28, 0xce, 0x0b, 0x08, 0xe4, 0x3b, 0xae, 0x25, 0xcf, 0xbd, 0x6e, 0xe0, 0xb3, + 0xc8, 0x15, 0xfa, 0x6e, 0x17, 0x37, 0x57, 0x32, 0xc4, 0xa3, 0xa0, 0x8a, 0x5c, 0xbb, 0x24, 0x7d, + 0x56, 0xff, 0xbd, 0x16, 0xcb, 0x8b, 0x53, 0x85, 0x59, 0xa8, 0xae, 0xbd, 0x4a, 0x54, 0xcf, 0xfd, + 0x6f, 0xa8, 0xae, 0xff, 0x4b, 0x8b, 0xaf, 0x34, 0xc2, 0xeb, 0xaf, 0xa7, 0x02, 0x61, 0x5d, 0xb6, + 0x63, 0xb1, 0x31, 0xaa, 0x7c, 0xd9, 0x90, 0x83, 0x30, 0xd5, 0x5a, 0xc5, 0x6b, 0x48, 0xa7, 0x5a, + 0x05, 0x9c, 0x93, 0x03, 0xf2, 0x3e, 0xe2, 0xbc, 0xfb, 0x4c, 0x85, 0x86, 0x14, 0x08, 0xca, 0xa2, + 0xae, 0xae, 0xaa, 0xb9, 0x23, 0x41, 0x66, 0x48, 0xea, 0x04, 0xbe, 0x94, 0x52, 0x69, 0xc3, 0x15, + 0x28, 0x89, 0xad, 0x07, 0x1e, 0xed, 0x30, 0xf4, 0xed, 0x92, 0x11, 0x4f, 0xe8, 0x16, 0x90, 0xb3, + 0x31, 0x86, 0x3c, 0x84, 0x55, 0x36, 0x62, 0x0e, 0x17, 0x77, 0x24, 0xd4, 0x7a, 0x65, 0x2e, 0x10, + 0x33, 0x87, 0x37, 0xaa, 0x42, 0x99, 0xff, 0x7c, 0xb1, 0x5d, 0x91, 0x3c, 0xef, 0xba, 0x03, 0x9b, + 0xb3, 0x81, 0xc7, 0x27, 0x86, 0x92, 0xa2, 0xff, 0x2c, 0x27, 0xf0, 0x30, 0x15, 0x7f, 0x66, 0xaa, + 0x37, 0x74, 0x9a, 0x5c, 0x22, 0x45, 0xca, 0xa6, 0xf2, 0x37, 0x01, 0xba, 0x34, 0x30, 0x9f, 0x53, + 0x87, 0x33, 0x4b, 0xe9, 0xbd, 0xd4, 0xa5, 0xc1, 0x0f, 0x70, 0x42, 0xe4, 0x9b, 0xe2, 0xf5, 0x30, + 0x60, 0x16, 0x5e, 0xc0, 0xb2, 0x51, 0xe8, 0xd2, 0xe0, 0x49, 0xc0, 0xac, 0xc4, 0x59, 0x0b, 0xaf, + 0xe2, 0xac, 0x69, 0x7d, 0x17, 0xa7, 0xf5, 0xfd, 0xf3, 0x5c, 0xec, 0x1d, 0x71, 0xfa, 0xf0, 0xff, + 0xa9, 0x8b, 0xdf, 0x60, 0x4d, 0x91, 0x06, 0x01, 0xf2, 0x43, 0xb8, 0x10, 0x79, 0xa5, 0x39, 0x44, + 0x6f, 0x0d, 0xad, 0xf0, 0xe5, 0x9c, 0xbb, 0x32, 0x4a, 0x4f, 0x07, 0xe4, 0x33, 0x78, 0x63, 0x2a, + 0x06, 0x45, 0x0b, 0xe4, 0x5e, 0x2a, 0x14, 0xbd, 0x96, 0x0e, 0x45, 0xa1, 0xfc, 0x58, 0x7b, 0xcb, + 0xaf, 0xc4, 0x6b, 0x5a, 0x22, 0x85, 0x4d, 0xc2, 0xdb, 0x4c, 0x9b, 0xd8, 0x81, 0x75, 0x9f, 0x71, + 0x51, 0x4b, 0xa5, 0xaa, 0x86, 0x35, 0x39, 0x29, 0x21, 0x41, 0xff, 0xb3, 0x06, 0x9b, 0x53, 0xa7, + 0x20, 0x1f, 0xc0, 0x8a, 0x84, 0x69, 0xed, 0xdc, 0x6e, 0x09, 0x5e, 0x8b, 0x3a, 0xb8, 0x64, 0x20, + 0x07, 0x50, 0x64, 0x2a, 0x05, 0x57, 0x9a, 0xbb, 0xb6, 0x20, 0x53, 0x57, 0xfc, 0x11, 0x1b, 0xb9, + 0x0b, 0xa5, 0xe8, 0x7e, 0x16, 0x94, 0x77, 0xd1, 0xf5, 0x2a, 0x21, 0x31, 0xa3, 0x7e, 0x08, 0xe5, + 0xc4, 0xf6, 0xc8, 0x37, 0xa0, 0x34, 0xa0, 0x63, 0x55, 0x93, 0xc9, 0x2c, 0xbb, 0x38, 0xa0, 0x63, + 0x2c, 0xc7, 0xc8, 0x1b, 0x50, 0x10, 0x2f, 0xbb, 0x54, 0xde, 0xf6, 0xb2, 0xb1, 0x3a, 0xa0, 0xe3, + 0xef, 0xd3, 0x40, 0xff, 0x85, 0x06, 0x1b, 0xe9, 0x7d, 0x92, 0x77, 0x80, 0x08, 0x5a, 0xda, 0x65, + 0xa6, 0x33, 0x1c, 0x48, 0x20, 0x0d, 0x25, 0x6e, 0x0e, 0xe8, 0xf8, 0xa0, 0xcb, 0x1e, 0x0e, 0x07, + 0xb8, 0x74, 0x40, 0x1e, 0x40, 0x25, 0x24, 0x0e, 0x3b, 0x62, 0x4a, 0x2b, 0x97, 0xcf, 0x54, 0xc4, + 0x77, 0x15, 0x81, 0x2c, 0x88, 0x7f, 0x2d, 0x0a, 0xe2, 0x0d, 0x29, 0x2f, 0x7c, 0xa3, 0xbf, 0x0f, + 0x9b, 0x53, 0x27, 0x26, 0x3a, 0xac, 0x7b, 0xc3, 0xb6, 0x79, 0xc2, 0x26, 0x26, 0xaa, 0x04, 0xfd, + 0xa1, 0x64, 0x94, 0xbd, 0x61, 0xfb, 0x63, 0x36, 0x11, 0xa5, 0x49, 0xa0, 0x77, 0x60, 0x23, 0x5d, + 0x71, 0x09, 0x74, 0xf1, 0xdd, 0xa1, 0x63, 0xe1, 0xbe, 0x57, 0x0c, 0x39, 0x20, 0xb7, 0x61, 0x65, + 0xe4, 0x4a, 0x93, 0x3f, 0xaf, 0xc4, 0x3a, 0x76, 0x39, 0x4b, 0xd4, 0x6d, 0x92, 0x47, 0x0f, 0x60, + 0x05, 0x8d, 0x57, 0x18, 0x22, 0xd6, 0x4e, 0x2a, 0xbb, 0x11, 0xcf, 0xe4, 0x18, 0x80, 0x72, 0xee, + 0xdb, 0xed, 0x61, 0x2c, 0xbe, 0x9a, 0x14, 0xdf, 0xb7, 0xdb, 0x41, 0xfd, 0x64, 0x54, 0x3f, 0xa2, + 0xb6, 0xdf, 0xb8, 0xa2, 0xcc, 0xff, 0x52, 0xcc, 0x93, 0x70, 0x81, 0x84, 0x24, 0xfd, 0xab, 0x3c, + 0xac, 0xca, 0x9a, 0x94, 0x7c, 0x98, 0xee, 0x90, 0x94, 0xf7, 0xb7, 0xe6, 0x6d, 0x5f, 0x52, 0xa9, + 0xdd, 0x47, 0x69, 0xd6, 0xf5, 0xe9, 0xb6, 0x43, 0xa3, 0x7c, 0xfa, 0x62, 0xbb, 0x80, 0x29, 0x4a, + 0xeb, 0x6e, 0xdc, 0x83, 0x98, 0x57, 0x82, 0x87, 0x0d, 0x8f, 0xfc, 0x4b, 0x37, 0x3c, 0x9a, 0xb0, + 0x9e, 0xc8, 0xc9, 0x6c, 0x4b, 0x15, 0x33, 0x5b, 0xe7, 0x39, 0x5d, 0xeb, 0xae, 0xda, 0x7f, 0x39, + 0xca, 0xd9, 0x5a, 0x16, 0xd9, 0x4d, 0x57, 0xe2, 0x98, 0xda, 0xc9, 0x9c, 0x22, 0x51, 0x5c, 0x8b, + 0xc4, 0x4e, 0xb8, 0x83, 0x88, 0x10, 0x92, 0x44, 0xa6, 0x18, 0x45, 0x31, 0x81, 0x2f, 0x6f, 0xc0, + 0x66, 0x9c, 0xfd, 0x48, 0x92, 0xa2, 0x94, 0x12, 0x4f, 0x23, 0xe1, 0x7b, 0x70, 0xc9, 0x61, 0x63, + 0x6e, 0x4e, 0x53, 0x97, 0x90, 0x9a, 0x88, 0x77, 0xc7, 0x69, 0x8e, 0x6b, 0xb0, 0x11, 0xc7, 0x59, + 0xa4, 0x05, 0xd9, 0x1f, 0x89, 0x66, 0x91, 0xec, 0x32, 0x14, 0xa3, 0xdc, 0xb4, 0x8c, 0x04, 0x05, + 0x2a, 0x53, 0xd2, 0x28, 0xdb, 0xf5, 0x59, 0x30, 0xec, 0x73, 0x25, 0x64, 0x0d, 0x69, 0x30, 0xdb, + 0x35, 0xe4, 0x3c, 0xd2, 0xee, 0xc0, 0x7a, 0x18, 0x55, 0x24, 0xdd, 0x3a, 0xd2, 0xad, 0x85, 0x93, + 0x48, 0x74, 0x13, 0x2a, 0x9e, 0xef, 0x7a, 0x6e, 0xc0, 0x7c, 0x93, 0x5a, 0x96, 0xcf, 0x82, 0xa0, + 0xba, 0x21, 0xe5, 0x85, 0xf3, 0x07, 0x72, 0x5a, 0xff, 0x16, 0x14, 0xc2, 0xa4, 0xfb, 0x12, 0xac, + 0x34, 0xa2, 0x08, 0x99, 0x37, 0xe4, 0x40, 0x80, 0xf0, 0x81, 0xe7, 0xa9, 0x16, 0x9c, 0x78, 0xd4, + 0xfb, 0x50, 0x50, 0x17, 0x36, 0xb3, 0xf1, 0xf2, 0x00, 0xd6, 0x3c, 0xea, 0x8b, 0x63, 0x24, 0xdb, + 0x2f, 0xf3, 0xca, 0xc6, 0x23, 0xea, 0xf3, 0x47, 0x8c, 0xa7, 0xba, 0x30, 0x65, 0xe4, 0x97, 0x53, + 0xfa, 0x2d, 0x58, 0x4f, 0xd1, 0x88, 0x6d, 0x72, 0x97, 0xd3, 0x7e, 0xe8, 0xe8, 0x38, 0x88, 0x76, + 0x92, 0x8b, 0x77, 0xa2, 0xdf, 0x86, 0x52, 0x74, 0x57, 0xa2, 0x1a, 0x09, 0x55, 0xa1, 0x29, 0xf5, + 0xcb, 0x21, 0x76, 0x9a, 0xdc, 0xe7, 0xcc, 0x57, 0xd6, 0x2f, 0x07, 0x3a, 0x4b, 0x04, 0x26, 0x09, + 0x79, 0xe4, 0x0e, 0x14, 0x54, 0x60, 0x52, 0xfe, 0x38, 0xaf, 0xa7, 0x74, 0x84, 0x91, 0x2a, 0xec, + 0x29, 0xc9, 0xb8, 0x15, 0x2f, 0x93, 0x4b, 0x2e, 0xf3, 0x53, 0x28, 0x86, 0xc1, 0x27, 0x8d, 0x12, + 0x72, 0x85, 0xab, 0x8b, 0x50, 0x42, 0x2d, 0x12, 0x33, 0x0a, 0x6b, 0x0a, 0xec, 0xae, 0xc3, 0x2c, + 0x33, 0x76, 0x41, 0x5c, 0xb3, 0x68, 0x6c, 0xca, 0x17, 0xf7, 0x43, 0xff, 0xd2, 0xdf, 0x83, 0x55, + 0xb9, 0xd7, 0x99, 0x21, 0x6e, 0x06, 0xfe, 0xea, 0xff, 0xd0, 0xa0, 0x18, 0xc2, 0xc7, 0x4c, 0xa6, + 0xd4, 0x21, 0x72, 0x5f, 0xf7, 0x10, 0xaf, 0x3e, 0x24, 0xbd, 0x0b, 0x04, 0x2d, 0xc5, 0x1c, 0xb9, + 0xdc, 0x76, 0xba, 0xa6, 0xbc, 0x0b, 0x99, 0x2e, 0x56, 0xf0, 0xcd, 0x31, 0xbe, 0x38, 0x12, 0xf3, + 0x6f, 0xef, 0x40, 0x39, 0xd1, 0x0a, 0x23, 0x05, 0x58, 0x7e, 0xc8, 0x9e, 0x57, 0x96, 0x48, 0x19, + 0x0a, 0x06, 0xc3, 0x46, 0x42, 0x45, 0xdb, 0xff, 0xaa, 0x00, 0x9b, 0x07, 0x8d, 0xc3, 0xd6, 0x81, + 0xe7, 0xf5, 0xed, 0x0e, 0xe2, 0x19, 0xf9, 0x04, 0xf2, 0x58, 0x4c, 0x67, 0xf8, 0x08, 0x54, 0xcb, + 0xd2, 0x95, 0x22, 0x06, 0xac, 0x60, 0xcd, 0x4d, 0xb2, 0x7c, 0x1b, 0xaa, 0x65, 0x6a, 0x56, 0x89, + 0x4d, 0xa2, 0xc1, 0x65, 0xf8, 0x64, 0x54, 0xcb, 0xd2, 0xc1, 0x22, 0x9f, 0x41, 0x29, 0x2e, 0xa6, + 0xb3, 0x7e, 0x48, 0xaa, 0x65, 0xee, 0x6d, 0x09, 0xf9, 0x71, 0xf9, 0x90, 0xf5, 0x33, 0x4a, 0x2d, + 0x73, 0x53, 0x87, 0x3c, 0x85, 0x42, 0x58, 0xa8, 0x65, 0xfb, 0xd4, 0x53, 0xcb, 0xd8, 0x77, 0x12, + 0xd7, 0x27, 0xeb, 0xeb, 0x2c, 0xdf, 0xb3, 0x6a, 0x99, 0x9a, 0x6b, 0xe4, 0x09, 0xac, 0xaa, 0x0c, + 0x39, 0xd3, 0x47, 0x9c, 0x5a, 0xb6, 0x6e, 0x92, 0x50, 0x72, 0xdc, 0xc1, 0xc8, 0xfa, 0x0d, 0xaf, + 0x96, 0xb9, 0xab, 0x48, 0x28, 0x40, 0xa2, 0xe8, 0xce, 0xfc, 0x71, 0xae, 0x96, 0xbd, 0x5b, 0x48, + 0x7e, 0x0c, 0xc5, 0xa8, 0xb4, 0xca, 0xf8, 0x91, 0xac, 0x96, 0xb5, 0x61, 0xd7, 0x68, 0xfd, 0xe7, + 0x6f, 0x5b, 0xda, 0x6f, 0x4f, 0xb7, 0xb4, 0x2f, 0x4e, 0xb7, 0xb4, 0x2f, 0x4f, 0xb7, 0xb4, 0x3f, + 0x9d, 0x6e, 0x69, 0x7f, 0x3d, 0xdd, 0xd2, 0xfe, 0xf0, 0xf7, 0x2d, 0xed, 0x47, 0xef, 0x74, 0x6d, + 0xde, 0x1b, 0xb6, 0xeb, 0x1d, 0x77, 0xb0, 0x17, 0x0b, 0x4c, 0x3e, 0xc6, 0x5f, 0xbe, 0xdb, 0xab, + 0x18, 0xb0, 0xbe, 0xfd, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0c, 0x66, 0x8a, 0xe9, 0x0e, 0x1f, 0x00, 0x00, } @@ -4512,6 +4521,9 @@ func (this *ResponseCommit) Equal(that interface{}) bool { if !bytes.Equal(this.Data, that1.Data) { return false } + if this.RetainHeight != that1.RetainHeight { + return false + } if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { return false } @@ -7143,6 +7155,11 @@ func (m *ResponseCommit) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.RetainHeight != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.RetainHeight)) + i-- + dAtA[i] = 0x18 + } if len(m.Data) > 0 { i -= len(m.Data) copy(dAtA[i:], m.Data) @@ -8479,8 +8496,12 @@ func NewPopulatedResponseCommit(r randyTypes, easy bool) *ResponseCommit { for i := 0; i < v30; i++ { this.Data[i] = byte(r.Intn(256)) } + this.RetainHeight = int64(r.Int63()) + if r.Intn(2) == 0 { + this.RetainHeight *= -1 + } if !easy && r.Intn(10) != 0 { - this.XXX_unrecognized = randUnrecognizedTypes(r, 3) + this.XXX_unrecognized = randUnrecognizedTypes(r, 4) } return this } @@ -9665,6 +9686,9 @@ func (m *ResponseCommit) Size() (n int) { if l > 0 { n += 1 + l + sovTypes(uint64(l)) } + if m.RetainHeight != 0 { + n += 1 + sovTypes(uint64(m.RetainHeight)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -14046,6 +14070,25 @@ func (m *ResponseCommit) Unmarshal(dAtA []byte) error { m.Data = []byte{} } iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RetainHeight", wireType) + } + m.RetainHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RetainHeight |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/abci/types/types.proto b/abci/types/types.proto index 0d47ad9b3..351329de1 100644 --- a/abci/types/types.proto +++ b/abci/types/types.proto @@ -207,7 +207,8 @@ message ResponseEndBlock { message ResponseCommit { // reserve 1 - bytes data = 2; + bytes data = 2; + int64 retain_height = 3; } //---------------------------------------- diff --git a/blockchain/v0/pool.go b/blockchain/v0/pool.go index 1931d7960..bd8165752 100644 --- a/blockchain/v0/pool.go +++ b/blockchain/v0/pool.go @@ -284,16 +284,17 @@ func (pool *BlockPool) MaxPeerHeight() int64 { return pool.maxPeerHeight } -// SetPeerHeight sets the peer's alleged blockchain height. -func (pool *BlockPool) SetPeerHeight(peerID p2p.ID, height int64) { +// SetPeerRange sets the peer's alleged blockchain base and height. +func (pool *BlockPool) SetPeerRange(peerID p2p.ID, base int64, height int64) { pool.mtx.Lock() defer pool.mtx.Unlock() peer := pool.peers[peerID] if peer != nil { + peer.base = base peer.height = height } else { - peer = newBPPeer(pool, peerID, height) + peer = newBPPeer(pool, peerID, base, height) peer.setLogger(pool.Logger.With("peer", peerID)) pool.peers[peerID] = peer } @@ -346,9 +347,9 @@ func (pool *BlockPool) updateMaxPeerHeight() { pool.maxPeerHeight = max } -// Pick an available peer with at least the given minHeight. +// Pick an available peer with the given height available. // If no peers are available, returns nil. -func (pool *BlockPool) pickIncrAvailablePeer(minHeight int64) *bpPeer { +func (pool *BlockPool) pickIncrAvailablePeer(height int64) *bpPeer { pool.mtx.Lock() defer pool.mtx.Unlock() @@ -360,7 +361,7 @@ func (pool *BlockPool) pickIncrAvailablePeer(minHeight int64) *bpPeer { if peer.numPending >= maxPendingRequestsPerPeer { continue } - if peer.height < minHeight { + if height < peer.base || height > peer.height { continue } peer.incrPending() @@ -432,6 +433,7 @@ type bpPeer struct { didTimeout bool numPending int32 height int64 + base int64 pool *BlockPool id p2p.ID recvMonitor *flow.Monitor @@ -441,10 +443,11 @@ type bpPeer struct { logger log.Logger } -func newBPPeer(pool *BlockPool, peerID p2p.ID, height int64) *bpPeer { +func newBPPeer(pool *BlockPool, peerID p2p.ID, base int64, height int64) *bpPeer { peer := &bpPeer{ pool: pool, id: peerID, + base: base, height: height, numPending: 0, logger: log.NewNopLogger(), diff --git a/blockchain/v0/pool_test.go b/blockchain/v0/pool_test.go index 783ff2526..9a3dd299c 100644 --- a/blockchain/v0/pool_test.go +++ b/blockchain/v0/pool_test.go @@ -20,6 +20,7 @@ func init() { type testPeer struct { id p2p.ID + base int64 height int64 inputChan chan inputData //make sure each peer's data is sequential } @@ -67,7 +68,11 @@ func makePeers(numPeers int, minHeight, maxHeight int64) testPeers { for i := 0; i < numPeers; i++ { peerID := p2p.ID(tmrand.Str(12)) height := minHeight + tmrand.Int63n(maxHeight-minHeight) - peers[peerID] = testPeer{peerID, height, make(chan inputData, 10)} + base := minHeight + int64(i) + if base > height { + base = height + } + peers[peerID] = testPeer{peerID, base, height, make(chan inputData, 10)} } return peers } @@ -93,7 +98,7 @@ func TestBlockPoolBasic(t *testing.T) { // Introduce each peer. go func() { for _, peer := range peers { - pool.SetPeerHeight(peer.id, peer.height) + pool.SetPeerRange(peer.id, peer.base, peer.height) } }() @@ -148,7 +153,7 @@ func TestBlockPoolTimeout(t *testing.T) { // Introduce each peer. go func() { for _, peer := range peers { - pool.SetPeerHeight(peer.id, peer.height) + pool.SetPeerRange(peer.id, peer.base, peer.height) } }() @@ -192,7 +197,7 @@ func TestBlockPoolRemovePeer(t *testing.T) { for i := 0; i < 10; i++ { peerID := p2p.ID(fmt.Sprintf("%d", i+1)) height := int64(i + 1) - peers[peerID] = testPeer{peerID, height, make(chan inputData)} + peers[peerID] = testPeer{peerID, 0, height, make(chan inputData)} } requestsCh := make(chan BlockRequest) errorsCh := make(chan peerError) @@ -205,7 +210,7 @@ func TestBlockPoolRemovePeer(t *testing.T) { // add peers for peerID, peer := range peers { - pool.SetPeerHeight(peerID, peer.height) + pool.SetPeerRange(peerID, peer.base, peer.height) } assert.EqualValues(t, 10, pool.MaxPeerHeight()) diff --git a/blockchain/v0/reactor.go b/blockchain/v0/reactor.go index d47e892c2..247222160 100644 --- a/blockchain/v0/reactor.go +++ b/blockchain/v0/reactor.go @@ -140,12 +140,15 @@ func (bcR *BlockchainReactor) GetChannels() []*p2p.ChannelDescriptor { // AddPeer implements Reactor by sending our state to peer. func (bcR *BlockchainReactor) AddPeer(peer p2p.Peer) { - msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{bcR.store.Height()}) + msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{ + Height: bcR.store.Height(), + Base: bcR.store.Base(), + }) peer.Send(BlockchainChannel, msgBytes) // it's OK if send fails. will try later in poolRoutine // peer is added to the pool once we receive the first - // bcStatusResponseMessage from the peer and call pool.SetPeerHeight + // bcStatusResponseMessage from the peer and call pool.SetPeerRange } // RemovePeer implements Reactor by removing peer from the pool. @@ -155,8 +158,6 @@ func (bcR *BlockchainReactor) RemovePeer(peer p2p.Peer, reason interface{}) { // respondToPeer loads a block and sends it to the requesting peer, // if we have it. Otherwise, we'll respond saying we don't have it. -// According to the Tendermint spec, if all nodes are honest, -// no node should be requesting for a block that's non-existent. func (bcR *BlockchainReactor) respondToPeer(msg *bcBlockRequestMessage, src p2p.Peer) (queued bool) { @@ -196,11 +197,15 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) bcR.pool.AddBlock(src.ID(), msg.Block, len(msgBytes)) case *bcStatusRequestMessage: // Send peer our state. - msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{bcR.store.Height()}) - src.TrySend(BlockchainChannel, msgBytes) + src.TrySend(BlockchainChannel, cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{ + Height: bcR.store.Height(), + Base: bcR.store.Base(), + })) case *bcStatusResponseMessage: // Got a peer status. Unverified. - bcR.pool.SetPeerHeight(src.ID(), msg.Height) + bcR.pool.SetPeerRange(src.ID(), msg.Base, msg.Height) + case *bcNoBlockResponseMessage: + bcR.Logger.Debug("Peer does not have requested block", "peer", src, "height", msg.Height) default: bcR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg))) } @@ -338,7 +343,7 @@ FOR_LOOP: // TODO: same thing for app - but we would need a way to // get the hash without persisting the state var err error - state, err = bcR.blockExec.ApplyBlock(state, firstID, first) + state, _, err = bcR.blockExec.ApplyBlock(state, firstID, first) if err != nil { // TODO This is bad, are we zombie? panic(fmt.Sprintf("Failed to process committed block (%d:%X): %v", first.Height, first.Hash(), err)) @@ -360,9 +365,12 @@ FOR_LOOP: } } -// BroadcastStatusRequest broadcasts `BlockStore` height. +// BroadcastStatusRequest broadcasts `BlockStore` base and height. func (bcR *BlockchainReactor) BroadcastStatusRequest() error { - msgBytes := cdc.MustMarshalBinaryBare(&bcStatusRequestMessage{bcR.store.Height()}) + msgBytes := cdc.MustMarshalBinaryBare(&bcStatusRequestMessage{ + Base: bcR.store.Base(), + Height: bcR.store.Height(), + }) bcR.Switch.Broadcast(BlockchainChannel, msgBytes) return nil } @@ -446,34 +454,48 @@ func (m *bcBlockResponseMessage) String() string { type bcStatusRequestMessage struct { Height int64 + Base int64 } // ValidateBasic performs basic validation. func (m *bcStatusRequestMessage) ValidateBasic() error { + if m.Base < 0 { + return errors.New("negative Base") + } if m.Height < 0 { return errors.New("negative Height") } + if m.Base > m.Height { + return fmt.Errorf("base %v cannot be greater than height %v", m.Base, m.Height) + } return nil } func (m *bcStatusRequestMessage) String() string { - return fmt.Sprintf("[bcStatusRequestMessage %v]", m.Height) + return fmt.Sprintf("[bcStatusRequestMessage %v:%v]", m.Base, m.Height) } //------------------------------------- type bcStatusResponseMessage struct { Height int64 + Base int64 } // ValidateBasic performs basic validation. func (m *bcStatusResponseMessage) ValidateBasic() error { + if m.Base < 0 { + return errors.New("negative Base") + } if m.Height < 0 { return errors.New("negative Height") } + if m.Base > m.Height { + return fmt.Errorf("base %v cannot be greater than height %v", m.Base, m.Height) + } return nil } func (m *bcStatusResponseMessage) String() string { - return fmt.Sprintf("[bcStatusResponseMessage %v]", m.Height) + return fmt.Sprintf("[bcStatusResponseMessage %v:%v]", m.Base, m.Height) } diff --git a/blockchain/v0/reactor_test.go b/blockchain/v0/reactor_test.go index 74811dd0a..a31c9a141 100644 --- a/blockchain/v0/reactor_test.go +++ b/blockchain/v0/reactor_test.go @@ -112,7 +112,7 @@ func newBlockchainReactor( thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes) blockID := types.BlockID{Hash: thisBlock.Hash(), PartsHeader: thisParts.Header()} - state, err = blockExec.ApplyBlock(state, blockID, thisBlock) + state, _, err = blockExec.ApplyBlock(state, blockID, thisBlock) if err != nil { panic(errors.Wrap(err, "error apply block")) } diff --git a/blockchain/v1/peer.go b/blockchain/v1/peer.go index 02b1b4fc1..ad26585b3 100644 --- a/blockchain/v1/peer.go +++ b/blockchain/v1/peer.go @@ -27,6 +27,7 @@ type BpPeer struct { logger log.Logger ID p2p.ID + Base int64 // the peer reported base Height int64 // the peer reported height NumPendingBlockRequests int // number of requests still waiting for block responses blocks map[int64]*types.Block // blocks received or expected to be received from this peer @@ -38,14 +39,15 @@ type BpPeer struct { } // NewBpPeer creates a new peer. -func NewBpPeer( - peerID p2p.ID, height int64, onErr func(err error, peerID p2p.ID), params *BpPeerParams) *BpPeer { +func NewBpPeer(peerID p2p.ID, base int64, height int64, + onErr func(err error, peerID p2p.ID), params *BpPeerParams) *BpPeer { if params == nil { params = BpPeerDefaultParams() } return &BpPeer{ ID: peerID, + Base: base, Height: height, blocks: make(map[int64]*types.Block, maxRequestsPerPeer), logger: log.NewNopLogger(), diff --git a/blockchain/v1/peer_test.go b/blockchain/v1/peer_test.go index aac03db7e..0e7a73473 100644 --- a/blockchain/v1/peer_test.go +++ b/blockchain/v1/peer_test.go @@ -16,7 +16,7 @@ import ( func TestPeerMonitor(t *testing.T) { peer := NewBpPeer( - p2p.ID(tmrand.Str(12)), 10, + p2p.ID(tmrand.Str(12)), 0, 10, func(err error, _ p2p.ID) {}, nil) peer.SetLogger(log.TestingLogger()) @@ -35,7 +35,7 @@ func TestPeerResetBlockResponseTimer(t *testing.T) { params := &BpPeerParams{timeout: 2 * time.Millisecond} peer := NewBpPeer( - p2p.ID(tmrand.Str(12)), 10, + p2p.ID(tmrand.Str(12)), 0, 10, func(err error, _ p2p.ID) { peerTestMtx.Lock() defer peerTestMtx.Unlock() @@ -75,7 +75,7 @@ func TestPeerRequestSent(t *testing.T) { params := &BpPeerParams{timeout: 2 * time.Millisecond} peer := NewBpPeer( - p2p.ID(tmrand.Str(12)), 10, + p2p.ID(tmrand.Str(12)), 0, 10, func(err error, _ p2p.ID) {}, params) @@ -94,7 +94,7 @@ func TestPeerRequestSent(t *testing.T) { func TestPeerGetAndRemoveBlock(t *testing.T) { peer := NewBpPeer( - p2p.ID(tmrand.Str(12)), 100, + p2p.ID(tmrand.Str(12)), 0, 100, func(err error, _ p2p.ID) {}, nil) @@ -142,7 +142,7 @@ func TestPeerGetAndRemoveBlock(t *testing.T) { func TestPeerAddBlock(t *testing.T) { peer := NewBpPeer( - p2p.ID(tmrand.Str(12)), 100, + p2p.ID(tmrand.Str(12)), 0, 100, func(err error, _ p2p.ID) {}, nil) @@ -189,7 +189,7 @@ func TestPeerOnErrFuncCalledDueToExpiration(t *testing.T) { ) peer := NewBpPeer( - p2p.ID(tmrand.Str(12)), 10, + p2p.ID(tmrand.Str(12)), 0, 10, func(err error, _ p2p.ID) { peerTestMtx.Lock() defer peerTestMtx.Unlock() @@ -215,7 +215,7 @@ func TestPeerCheckRate(t *testing.T) { minRecvRate: int64(100), // 100 bytes/sec exponential moving average } peer := NewBpPeer( - p2p.ID(tmrand.Str(12)), 10, + p2p.ID(tmrand.Str(12)), 0, 10, func(err error, _ p2p.ID) {}, params) peer.SetLogger(log.TestingLogger()) @@ -249,7 +249,7 @@ func TestPeerCleanup(t *testing.T) { params := &BpPeerParams{timeout: 2 * time.Millisecond} peer := NewBpPeer( - p2p.ID(tmrand.Str(12)), 10, + p2p.ID(tmrand.Str(12)), 0, 10, func(err error, _ p2p.ID) {}, params) peer.SetLogger(log.TestingLogger()) diff --git a/blockchain/v1/pool.go b/blockchain/v1/pool.go index be2edbc21..27e0f3a04 100644 --- a/blockchain/v1/pool.go +++ b/blockchain/v1/pool.go @@ -66,9 +66,9 @@ func (pool *BlockPool) updateMaxPeerHeight() { pool.MaxPeerHeight = newMax } -// UpdatePeer adds a new peer or updates an existing peer with a new height. +// UpdatePeer adds a new peer or updates an existing peer with a new base and height. // If a peer is short it is not added. -func (pool *BlockPool) UpdatePeer(peerID p2p.ID, height int64) error { +func (pool *BlockPool) UpdatePeer(peerID p2p.ID, base int64, height int64) error { peer := pool.peers[peerID] @@ -79,10 +79,10 @@ func (pool *BlockPool) UpdatePeer(peerID p2p.ID, height int64) error { return errPeerTooShort } // Add new peer. - peer = NewBpPeer(peerID, height, pool.toBcR.sendPeerError, nil) + peer = NewBpPeer(peerID, base, height, pool.toBcR.sendPeerError, nil) peer.SetLogger(pool.logger.With("peer", peerID)) pool.peers[peerID] = peer - pool.logger.Info("added peer", "peerID", peerID, "height", height, "num_peers", len(pool.peers)) + pool.logger.Info("added peer", "peerID", peerID, "base", base, "height", height, "num_peers", len(pool.peers)) } else { // Check if peer is lowering its height. This is not allowed. if height < peer.Height { @@ -90,6 +90,7 @@ func (pool *BlockPool) UpdatePeer(peerID p2p.ID, height int64) error { return errPeerLowersItsHeight } // Update existing peer. + peer.Base = base peer.Height = height } @@ -213,7 +214,7 @@ func (pool *BlockPool) sendRequest(height int64) bool { if peer.NumPendingBlockRequests >= maxRequestsPerPeer { continue } - if peer.Height < height { + if peer.Base > height || peer.Height < height { continue } diff --git a/blockchain/v1/pool_test.go b/blockchain/v1/pool_test.go index e612eb43e..31b9d09f7 100644 --- a/blockchain/v1/pool_test.go +++ b/blockchain/v1/pool_test.go @@ -13,6 +13,7 @@ import ( type testPeer struct { id p2p.ID + base int64 height int64 } @@ -70,7 +71,7 @@ func makeBlockPool(bcr *testBcR, height int64, peers []BpPeer, blocks map[int64] if p.Height > maxH { maxH = p.Height } - bPool.peers[p.ID] = NewBpPeer(p.ID, p.Height, bcr.sendPeerError, nil) + bPool.peers[p.ID] = NewBpPeer(p.ID, p.Base, p.Height, bcr.sendPeerError, nil) bPool.peers[p.ID].SetLogger(bcr.logger) } @@ -93,6 +94,7 @@ func assertPeerSetsEquivalent(t *testing.T, set1 map[p2p.ID]*BpPeer, set2 map[p2 assert.NotNil(t, peer2) assert.Equal(t, peer1.NumPendingBlockRequests, peer2.NumPendingBlockRequests) assert.Equal(t, peer1.Height, peer2.Height) + assert.Equal(t, peer1.Base, peer2.Base) assert.Equal(t, len(peer1.blocks), len(peer2.blocks)) for h, block1 := range peer1.blocks { block2 := peer2.blocks[h] @@ -123,26 +125,32 @@ func TestBlockPoolUpdatePeer(t *testing.T) { { name: "add a first short peer", pool: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}), - args: testPeer{"P1", 50}, + args: testPeer{"P1", 0, 50}, errWanted: errPeerTooShort, poolWanted: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}), }, { name: "add a first good peer", pool: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}), - args: testPeer{"P1", 101}, + args: testPeer{"P1", 0, 101}, poolWanted: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 101}}, map[int64]tPBlocks{}), }, + { + name: "add a first good peer with base", + pool: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}), + args: testPeer{"P1", 10, 101}, + poolWanted: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Base: 10, Height: 101}}, map[int64]tPBlocks{}), + }, { name: "increase the height of P1 from 120 to 123", pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}}, map[int64]tPBlocks{}), - args: testPeer{"P1", 123}, + args: testPeer{"P1", 0, 123}, poolWanted: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 123}}, map[int64]tPBlocks{}), }, { name: "decrease the height of P1 from 120 to 110", pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 120}}, map[int64]tPBlocks{}), - args: testPeer{"P1", 110}, + args: testPeer{"P1", 0, 110}, errWanted: errPeerLowersItsHeight, poolWanted: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}), }, @@ -151,7 +159,7 @@ func TestBlockPoolUpdatePeer(t *testing.T) { pool: makeBlockPool(testBcR, 100, []BpPeer{{ID: "P1", Height: 105}}, map[int64]tPBlocks{ 100: {"P1", true}, 101: {"P1", true}, 102: {"P1", true}}), - args: testPeer{"P1", 102}, + args: testPeer{"P1", 0, 102}, errWanted: errPeerLowersItsHeight, poolWanted: makeBlockPool(testBcR, 100, []BpPeer{}, map[int64]tPBlocks{}), @@ -162,7 +170,7 @@ func TestBlockPoolUpdatePeer(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { pool := tt.pool - err := pool.UpdatePeer(tt.args.id, tt.args.height) + err := pool.UpdatePeer(tt.args.id, tt.args.base, tt.args.height) assert.Equal(t, tt.errWanted, err) assert.Equal(t, tt.poolWanted.blocks, tt.pool.blocks) assertPeerSetsEquivalent(t, tt.poolWanted.peers, tt.pool.peers) @@ -300,20 +308,34 @@ func TestBlockPoolSendRequestBatch(t *testing.T) { testBcR := newTestBcR() tests := []struct { - name string - pool *BlockPool - maxRequestsPerPeer int - expRequests map[int64]bool - expPeerResults []testPeerResult - expnumPendingBlockRequests int + name string + pool *BlockPool + maxRequestsPerPeer int + expRequests map[int64]bool + expRequestsSent int + expPeerResults []testPeerResult }{ { - name: "one peer - send up to maxRequestsPerPeer block requests", - pool: makeBlockPool(testBcR, 10, []BpPeer{{ID: "P1", Height: 100}}, map[int64]tPBlocks{}), - maxRequestsPerPeer: 2, - expRequests: map[int64]bool{10: true, 11: true}, - expPeerResults: []testPeerResult{{id: "P1", numPendingBlockRequests: 2}}, - expnumPendingBlockRequests: 2, + name: "one peer - send up to maxRequestsPerPeer block requests", + pool: makeBlockPool(testBcR, 10, []BpPeer{{ID: "P1", Height: 100}}, map[int64]tPBlocks{}), + maxRequestsPerPeer: 2, + expRequests: map[int64]bool{10: true, 11: true}, + expRequestsSent: 2, + expPeerResults: []testPeerResult{{id: "P1", numPendingBlockRequests: 2}}, + }, + { + name: "multiple peers - stops at gap between height and base", + pool: makeBlockPool(testBcR, 10, []BpPeer{ + {ID: "P1", Base: 1, Height: 12}, + {ID: "P2", Base: 15, Height: 100}, + }, map[int64]tPBlocks{}), + maxRequestsPerPeer: 10, + expRequests: map[int64]bool{10: true, 11: true, 12: true}, + expRequestsSent: 3, + expPeerResults: []testPeerResult{ + {id: "P1", numPendingBlockRequests: 3}, + {id: "P2", numPendingBlockRequests: 0}, + }, }, { name: "n peers - send n*maxRequestsPerPeer block requests", @@ -324,10 +346,10 @@ func TestBlockPoolSendRequestBatch(t *testing.T) { map[int64]tPBlocks{}), maxRequestsPerPeer: 2, expRequests: map[int64]bool{10: true, 11: true}, + expRequestsSent: 4, expPeerResults: []testPeerResult{ {id: "P1", numPendingBlockRequests: 2}, {id: "P2", numPendingBlockRequests: 2}}, - expnumPendingBlockRequests: 4, }, } @@ -339,15 +361,13 @@ func TestBlockPoolSendRequestBatch(t *testing.T) { var pool = tt.pool maxRequestsPerPeer = tt.maxRequestsPerPeer pool.MakeNextRequests(10) - assert.Equal(t, testResults.numRequestsSent, maxRequestsPerPeer*len(pool.peers)) + assert.Equal(t, tt.expRequestsSent, testResults.numRequestsSent) for _, tPeer := range tt.expPeerResults { var peer = pool.peers[tPeer.id] assert.NotNil(t, peer) assert.Equal(t, tPeer.numPendingBlockRequests, peer.NumPendingBlockRequests) } - assert.Equal(t, testResults.numRequestsSent, maxRequestsPerPeer*len(pool.peers)) - }) } } diff --git a/blockchain/v1/reactor.go b/blockchain/v1/reactor.go index d3b9b0216..28a314b8a 100644 --- a/blockchain/v1/reactor.go +++ b/blockchain/v1/reactor.go @@ -169,7 +169,10 @@ func (bcR *BlockchainReactor) GetChannels() []*p2p.ChannelDescriptor { // AddPeer implements Reactor by sending our state to peer. func (bcR *BlockchainReactor) AddPeer(peer p2p.Peer) { - msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{bcR.store.Height()}) + msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{ + Base: bcR.store.Base(), + Height: bcR.store.Height(), + }) peer.Send(BlockchainChannel, msgBytes) // it's OK if send fails. will try later in poolRoutine @@ -196,7 +199,10 @@ func (bcR *BlockchainReactor) sendBlockToPeer(msg *bcBlockRequestMessage, } func (bcR *BlockchainReactor) sendStatusResponseToPeer(msg *bcStatusRequestMessage, src p2p.Peer) (queued bool) { - msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{bcR.store.Height()}) + msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{ + Base: bcR.store.Base(), + Height: bcR.store.Height(), + }) return src.TrySend(BlockchainChannel, msgBytes) } @@ -430,7 +436,7 @@ func (bcR *BlockchainReactor) processBlock() error { bcR.store.SaveBlock(first, firstParts, second.LastCommit) - bcR.state, err = bcR.blockExec.ApplyBlock(bcR.state, firstID, first) + bcR.state, _, err = bcR.blockExec.ApplyBlock(bcR.state, firstID, first) if err != nil { panic(fmt.Sprintf("failed to process committed block (%d:%X): %v", first.Height, first.Hash(), err)) } @@ -441,7 +447,10 @@ func (bcR *BlockchainReactor) processBlock() error { // Implements bcRNotifier // sendStatusRequest broadcasts `BlockStore` height. func (bcR *BlockchainReactor) sendStatusRequest() { - msgBytes := cdc.MustMarshalBinaryBare(&bcStatusRequestMessage{bcR.store.Height()}) + msgBytes := cdc.MustMarshalBinaryBare(&bcStatusRequestMessage{ + Base: bcR.store.Base(), + Height: bcR.store.Height(), + }) bcR.Switch.Broadcast(BlockchainChannel, msgBytes) } @@ -590,6 +599,7 @@ func (m *bcBlockResponseMessage) String() string { type bcStatusRequestMessage struct { Height int64 + Base int64 } // ValidateBasic performs basic validation. @@ -597,17 +607,24 @@ func (m *bcStatusRequestMessage) ValidateBasic() error { if m.Height < 0 { return errors.New("negative Height") } + if m.Base < 0 { + return errors.New("negative Base") + } + if m.Base > m.Height { + return fmt.Errorf("base %v cannot be greater than height %v", m.Base, m.Height) + } return nil } func (m *bcStatusRequestMessage) String() string { - return fmt.Sprintf("[bcStatusRequestMessage %v]", m.Height) + return fmt.Sprintf("[bcStatusRequestMessage %v:%v]", m.Base, m.Height) } //------------------------------------- type bcStatusResponseMessage struct { Height int64 + Base int64 } // ValidateBasic performs basic validation. @@ -615,9 +632,15 @@ func (m *bcStatusResponseMessage) ValidateBasic() error { if m.Height < 0 { return errors.New("negative Height") } + if m.Base < 0 { + return errors.New("negative Base") + } + if m.Base > m.Height { + return fmt.Errorf("base %v cannot be greater than height %v", m.Base, m.Height) + } return nil } func (m *bcStatusResponseMessage) String() string { - return fmt.Sprintf("[bcStatusResponseMessage %v]", m.Height) + return fmt.Sprintf("[bcStatusResponseMessage %v:%v]", m.Base, m.Height) } diff --git a/blockchain/v1/reactor_fsm.go b/blockchain/v1/reactor_fsm.go index 8d3a363ae..0f65f9d66 100644 --- a/blockchain/v1/reactor_fsm.go +++ b/blockchain/v1/reactor_fsm.go @@ -58,6 +58,7 @@ func NewFSM(height int64, toBcR bcReactor) *BcReactorFSM { type bReactorEventData struct { peerID p2p.ID err error // for peer error: timeout, slow; for processed block event if error occurred + base int64 // for status response height int64 // for status response; for processed block event block *types.Block // for block response stateName string // for state timeout events @@ -89,7 +90,7 @@ func (msg *bcReactorMessage) String() string { case startFSMEv: dataStr = "" case statusResponseEv: - dataStr = fmt.Sprintf("peer=%v height=%v", msg.data.peerID, msg.data.height) + dataStr = fmt.Sprintf("peer=%v base=%v height=%v", msg.data.peerID, msg.data.base, msg.data.height) case blockResponseEv: dataStr = fmt.Sprintf("peer=%v block.height=%v length=%v", msg.data.peerID, msg.data.block.Height, msg.data.length) @@ -213,7 +214,7 @@ func init() { return finished, errNoTallerPeer case statusResponseEv: - if err := fsm.pool.UpdatePeer(data.peerID, data.height); err != nil { + if err := fsm.pool.UpdatePeer(data.peerID, data.base, data.height); err != nil { if fsm.pool.NumPeers() == 0 { return waitForPeer, err } @@ -246,7 +247,7 @@ func init() { switch ev { case statusResponseEv: - err := fsm.pool.UpdatePeer(data.peerID, data.height) + err := fsm.pool.UpdatePeer(data.peerID, data.base, data.height) if fsm.pool.NumPeers() == 0 { return waitForPeer, err } diff --git a/blockchain/v1/reactor_test.go b/blockchain/v1/reactor_test.go index ec9707f67..e0b3472bf 100644 --- a/blockchain/v1/reactor_test.go +++ b/blockchain/v1/reactor_test.go @@ -131,7 +131,7 @@ func newBlockchainReactor( thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes) blockID := types.BlockID{Hash: thisBlock.Hash(), PartsHeader: thisParts.Header()} - state, err = blockExec.ApplyBlock(state, blockID, thisBlock) + state, _, err = blockExec.ApplyBlock(state, blockID, thisBlock) if err != nil { panic(errors.Wrap(err, "error apply block")) } diff --git a/blockchain/v2/io.go b/blockchain/v2/io.go index 3db48c8c0..fde446f6e 100644 --- a/blockchain/v2/io.go +++ b/blockchain/v2/io.go @@ -14,7 +14,7 @@ type iIO interface { sendBlockNotFound(height int64, peerID p2p.ID) error sendStatusResponse(height int64, peerID p2p.ID) error - broadcastStatusRequest(height int64) + broadcastStatusRequest(base int64, height int64) trySwitchToConsensus(state state.State, blocksSynced int) } @@ -104,8 +104,11 @@ func (sio *switchIO) trySwitchToConsensus(state state.State, blocksSynced int) { } } -func (sio *switchIO) broadcastStatusRequest(height int64) { - msgBytes := cdc.MustMarshalBinaryBare(&bcStatusRequestMessage{height}) +func (sio *switchIO) broadcastStatusRequest(base int64, height int64) { + msgBytes := cdc.MustMarshalBinaryBare(&bcStatusRequestMessage{ + Base: base, + Height: height, + }) // XXX: maybe we should use an io specific peer list here sio.sw.Broadcast(BlockchainChannel, msgBytes) } diff --git a/blockchain/v2/processor_context.go b/blockchain/v2/processor_context.go index 7e96a3a69..2e8142adc 100644 --- a/blockchain/v2/processor_context.go +++ b/blockchain/v2/processor_context.go @@ -29,7 +29,7 @@ func newProcessorContext(st blockStore, ex blockApplier, s state.State) *pContex } func (pc *pContext) applyBlock(blockID types.BlockID, block *types.Block) error { - newState, err := pc.applier.ApplyBlock(pc.state, blockID, block) + newState, _, err := pc.applier.ApplyBlock(pc.state, blockID, block) pc.state = newState return err } diff --git a/blockchain/v2/reactor.go b/blockchain/v2/reactor.go index e09e0adde..88ec6268d 100644 --- a/blockchain/v2/reactor.go +++ b/blockchain/v2/reactor.go @@ -72,41 +72,56 @@ func (m *bcBlockResponseMessage) String() string { type bcStatusRequestMessage struct { Height int64 + Base int64 } // ValidateBasic performs basic validation. func (m *bcStatusRequestMessage) ValidateBasic() error { + if m.Base < 0 { + return errors.New("negative Base") + } if m.Height < 0 { return errors.New("negative Height") } + if m.Base > m.Height { + return fmt.Errorf("base %v cannot be greater than height %v", m.Base, m.Height) + } return nil } func (m *bcStatusRequestMessage) String() string { - return fmt.Sprintf("[bcStatusRequestMessage %v]", m.Height) + return fmt.Sprintf("[bcStatusRequestMessage %v:%v]", m.Base, m.Height) } //------------------------------------- type bcStatusResponseMessage struct { Height int64 + Base int64 } // ValidateBasic performs basic validation. func (m *bcStatusResponseMessage) ValidateBasic() error { + if m.Base < 0 { + return errors.New("negative Base") + } if m.Height < 0 { return errors.New("negative Height") } + if m.Base > m.Height { + return fmt.Errorf("base %v cannot be greater than height %v", m.Base, m.Height) + } return nil } func (m *bcStatusResponseMessage) String() string { - return fmt.Sprintf("[bcStatusResponseMessage %v]", m.Height) + return fmt.Sprintf("[bcStatusResponseMessage %v:%v]", m.Base, m.Height) } type blockStore interface { LoadBlock(height int64) *types.Block SaveBlock(*types.Block, *types.PartSet, *types.Commit) + Base() int64 Height() int64 } @@ -136,7 +151,7 @@ type blockVerifier interface { //nolint:deadcode type blockApplier interface { - ApplyBlock(state state.State, blockID types.BlockID, block *types.Block) (state.State, error) + ApplyBlock(state state.State, blockID types.BlockID, block *types.Block) (state.State, int64, error) } // XXX: unify naming in this package around tmState @@ -266,6 +281,7 @@ type bcStatusResponse struct { priorityNormal time time.Time peerID p2p.ID + base int64 height int64 } @@ -337,7 +353,7 @@ func (r *BlockchainReactor) demux() { case <-doProcessBlockCh: r.processor.send(rProcessBlock{}) case <-doStatusCh: - r.io.broadcastStatusRequest(r.SyncHeight()) + r.io.broadcastStatusRequest(r.store.Base(), r.SyncHeight()) // Events from peers case event := <-r.events: @@ -483,7 +499,7 @@ func (r *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { } case *bcStatusResponseMessage: - r.events <- bcStatusResponse{peerID: src.ID(), height: msg.Height} + r.events <- bcStatusResponse{peerID: src.ID(), base: msg.Base, height: msg.Height} case *bcBlockResponseMessage: r.events <- bcBlockResponse{ diff --git a/blockchain/v2/reactor_test.go b/blockchain/v2/reactor_test.go index 1d3e51e77..108f6f500 100644 --- a/blockchain/v2/reactor_test.go +++ b/blockchain/v2/reactor_test.go @@ -77,9 +77,9 @@ type mockBlockApplier struct { } // XXX: Add whitelist/blacklist? -func (mba *mockBlockApplier) ApplyBlock(state sm.State, blockID types.BlockID, block *types.Block) (sm.State, error) { +func (mba *mockBlockApplier) ApplyBlock(state sm.State, blockID types.BlockID, block *types.Block) (sm.State, int64, error) { state.LastBlockHeight++ - return state, nil + return state, 0, nil } type mockSwitchIo struct { @@ -127,7 +127,7 @@ func (sio *mockSwitchIo) hasSwitchedToConsensus() bool { return sio.switchedToConsensus } -func (sio *mockSwitchIo) broadcastStatusRequest(height int64) { +func (sio *mockSwitchIo) broadcastStatusRequest(base int64, height int64) { } type testReactorParams struct { @@ -511,7 +511,7 @@ func newReactorStore( thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes) blockID := types.BlockID{Hash: thisBlock.Hash(), PartsHeader: thisParts.Header()} - state, err = blockExec.ApplyBlock(state, blockID, thisBlock) + state, _, err = blockExec.ApplyBlock(state, blockID, thisBlock) if err != nil { panic(errors.Wrap(err, "error apply block")) } diff --git a/blockchain/v2/scheduler.go b/blockchain/v2/scheduler.go index 3cf0b2468..803955b22 100644 --- a/blockchain/v2/scheduler.go +++ b/blockchain/v2/scheduler.go @@ -111,20 +111,22 @@ type scPeer struct { // updated to Removed when peer is removed state peerState + base int64 // updated when statusResponse is received height int64 // updated when statusResponse is received lastTouched time.Time lastRate int64 // last receive rate in bytes } func (p scPeer) String() string { - return fmt.Sprintf("{state %v, height %d, lastTouched %v, lastRate %d, id %v}", - p.state, p.height, p.lastTouched, p.lastRate, p.peerID) + return fmt.Sprintf("{state %v, base %d, height %d, lastTouched %v, lastRate %d, id %v}", + p.state, p.base, p.height, p.lastTouched, p.lastRate, p.peerID) } func newScPeer(peerID p2p.ID) *scPeer { return &scPeer{ peerID: peerID, state: peerStateNew, + base: -1, height: -1, lastTouched: time.Time{}, } @@ -280,7 +282,7 @@ func (sc *scheduler) addNewBlocks() { } } -func (sc *scheduler) setPeerHeight(peerID p2p.ID, height int64) error { +func (sc *scheduler) setPeerRange(peerID p2p.ID, base int64, height int64) error { peer, ok := sc.peers[peerID] if !ok { return fmt.Errorf("cannot find peer %s", peerID) @@ -295,6 +297,11 @@ func (sc *scheduler) setPeerHeight(peerID p2p.ID, height int64) error { return fmt.Errorf("cannot move peer height lower. from %d to %d", peer.height, height) } + if base > height { + return fmt.Errorf("cannot set peer base higher than its height") + } + + peer.base = base peer.height = height peer.state = peerStateReady @@ -312,13 +319,13 @@ func (sc *scheduler) getStateAtHeight(height int64) blockState { } } -func (sc *scheduler) getPeersAtHeightOrAbove(height int64) []p2p.ID { +func (sc *scheduler) getPeersWithHeight(height int64) []p2p.ID { peers := make([]p2p.ID, 0) for _, peer := range sc.peers { if peer.state != peerStateReady { continue } - if peer.height >= height { + if peer.base <= height && peer.height >= height { peers = append(peers, peer.peerID) } } @@ -395,6 +402,11 @@ func (sc *scheduler) markPending(peerID p2p.ID, height int64, time time.Time) er height, peerID, peer.height) } + if height < peer.base { + return fmt.Errorf("cannot request height %d for peer %s with base %d", + height, peerID, peer.base) + } + sc.setStateAtHeight(height, blockStatePending) sc.pendingBlocks[height] = peerID sc.pendingTime[height] = time @@ -463,7 +475,7 @@ func (sc *scheduler) pendingFrom(peerID p2p.ID) []int64 { } func (sc *scheduler) selectPeer(height int64) (p2p.ID, error) { - peers := sc.getPeersAtHeightOrAbove(height) + peers := sc.getPeersWithHeight(height) if len(peers) == 0 { return "", fmt.Errorf("cannot find peer for height %d", height) } @@ -535,8 +547,8 @@ func (sc *scheduler) handleNoBlockResponse(event bcNoBlockResponse) (Event, erro _ = sc.removePeer(event.peerID) return scPeerError{peerID: event.peerID, - reason: fmt.Errorf("peer %v with height %d claims no block for %d", - event.peerID, peer.height, event.height)}, nil + reason: fmt.Errorf("peer %v with base %d height %d claims no block for %d", + event.peerID, peer.base, peer.height, event.height)}, nil } func (sc *scheduler) handleBlockProcessed(event pcBlockProcessed) (Event, error) { @@ -653,7 +665,7 @@ func (sc *scheduler) handleTrySchedule(event rTrySchedule) (Event, error) { } func (sc *scheduler) handleStatusResponse(event bcStatusResponse) (Event, error) { - err := sc.setPeerHeight(event.peerID, event.height) + err := sc.setPeerRange(event.peerID, event.base, event.height) if err != nil { return scPeerError{peerID: event.peerID, reason: err}, nil } diff --git a/blockchain/v2/scheduler_test.go b/blockchain/v2/scheduler_test.go index c91fdd366..4ec81e123 100644 --- a/blockchain/v2/scheduler_test.go +++ b/blockchain/v2/scheduler_test.go @@ -145,8 +145,8 @@ func TestScMaxHeights(t *testing.T) { sc: scheduler{ height: 1, peers: map[p2p.ID]*scPeer{ - "P1": {height: -1, state: peerStateNew}, - "P2": {height: -1, state: peerStateNew}}, + "P1": {base: -1, height: -1, state: peerStateNew}, + "P2": {base: -1, height: -1, state: peerStateNew}}, }, wantMax: 0, }, @@ -194,15 +194,15 @@ func TestScAddPeer(t *testing.T) { name: "add first peer", fields: scTestParams{}, args: args{peerID: "P1"}, - wantFields: scTestParams{peers: map[string]*scPeer{"P1": {height: -1, state: peerStateNew}}}, + wantFields: scTestParams{peers: map[string]*scPeer{"P1": {base: -1, height: -1, state: peerStateNew}}}, }, { name: "add second peer", - fields: scTestParams{peers: map[string]*scPeer{"P1": {height: -1, state: peerStateNew}}}, + fields: scTestParams{peers: map[string]*scPeer{"P1": {base: -1, height: -1, state: peerStateNew}}}, args: args{peerID: "P2"}, wantFields: scTestParams{peers: map[string]*scPeer{ - "P1": {height: -1, state: peerStateNew}, - "P2": {height: -1, state: peerStateNew}}}, + "P1": {base: -1, height: -1, state: peerStateNew}, + "P2": {base: -1, height: -1, state: peerStateNew}}}, }, { name: "attempt to add duplicate peer", @@ -501,10 +501,11 @@ func TestScRemovePeer(t *testing.T) { } } -func TestScSetPeerHeight(t *testing.T) { +func TestScSetPeerRange(t *testing.T) { type args struct { peerID p2p.ID + base int64 height int64 } tests := []struct { @@ -576,13 +577,37 @@ func TestScSetPeerHeight(t *testing.T) { peers: map[string]*scPeer{"P2": {height: 10000000000, state: peerStateReady}}, allB: []int64{1, 2, 3, 4}}, }, + { + name: "add peer with base > height should error", + fields: scTestParams{ + peers: map[string]*scPeer{"P1": {height: 4, state: peerStateReady}}, + allB: []int64{1, 2, 3, 4}}, + args: args{peerID: "P1", base: 6, height: 5}, + wantFields: scTestParams{ + peers: map[string]*scPeer{"P1": {height: 4, state: peerStateReady}}, + allB: []int64{1, 2, 3, 4}}, + wantErr: true, + }, + { + name: "add peer with base == height is fine", + fields: scTestParams{ + peers: map[string]*scPeer{"P1": {height: 4, state: peerStateNew}}, + targetPending: 4, + }, + args: args{peerID: "P1", base: 6, height: 6}, + wantFields: scTestParams{ + targetPending: 4, + peers: map[string]*scPeer{"P1": {base: 6, height: 6, state: peerStateReady}}, + allB: []int64{1, 2, 3, 4}}, + }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { sc := newTestScheduler(tt.fields) - if err := sc.setPeerHeight(tt.args.peerID, tt.args.height); (err != nil) != tt.wantErr { + err := sc.setPeerRange(tt.args.peerID, tt.args.base, tt.args.height) + if (err != nil) != tt.wantErr { t.Errorf("setPeerHeight() wantErr %v, error = %v", tt.wantErr, err) } wantSc := newTestScheduler(tt.wantFields) @@ -591,7 +616,7 @@ func TestScSetPeerHeight(t *testing.T) { } } -func TestScGetPeersAtHeight(t *testing.T) { +func TestScGetPeersWithHeight(t *testing.T) { type args struct { height int64 @@ -648,6 +673,26 @@ func TestScGetPeersAtHeight(t *testing.T) { args: args{height: 4}, wantResult: []p2p.ID{"P1"}, }, + { + name: "one Ready higher peer at base", + fields: scTestParams{ + targetPending: 4, + peers: map[string]*scPeer{"P1": {base: 4, height: 20, state: peerStateReady}}, + allB: []int64{1, 2, 3, 4}, + }, + args: args{height: 4}, + wantResult: []p2p.ID{"P1"}, + }, + { + name: "one Ready higher peer with higher base", + fields: scTestParams{ + targetPending: 4, + peers: map[string]*scPeer{"P1": {base: 10, height: 20, state: peerStateReady}}, + allB: []int64{1, 2, 3, 4}, + }, + args: args{height: 4}, + wantResult: []p2p.ID{}, + }, { name: "multiple mixed peers", fields: scTestParams{ @@ -669,9 +714,9 @@ func TestScGetPeersAtHeight(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { sc := newTestScheduler(tt.fields) - // getPeersAtHeight should not mutate the scheduler + // getPeersWithHeight should not mutate the scheduler wantSc := sc - res := sc.getPeersAtHeightOrAbove(tt.args.height) + res := sc.getPeersWithHeight(tt.args.height) sort.Sort(PeerByID(res)) assert.Equal(t, tt.wantResult, res) assert.Equal(t, wantSc, sc) @@ -695,7 +740,7 @@ func TestScMarkPending(t *testing.T) { wantErr bool }{ { - name: "attempt mark pending an unknown block", + name: "attempt mark pending an unknown block above height", fields: scTestParams{ peers: map[string]*scPeer{"P1": {height: 2, state: peerStateReady}}, allB: []int64{1, 2}}, @@ -705,6 +750,17 @@ func TestScMarkPending(t *testing.T) { allB: []int64{1, 2}}, wantErr: true, }, + { + name: "attempt mark pending an unknown block below base", + fields: scTestParams{ + peers: map[string]*scPeer{"P1": {base: 4, height: 6, state: peerStateReady}}, + allB: []int64{1, 2, 3, 4, 5, 6}}, + args: args{peerID: "P1", height: 3, tm: now}, + wantFields: scTestParams{ + peers: map[string]*scPeer{"P1": {base: 4, height: 6, state: peerStateReady}}, + allB: []int64{1, 2, 3, 4, 5, 6}}, + wantErr: true, + }, { name: "attempt mark pending from non existing peer", fields: scTestParams{ @@ -1202,6 +1258,16 @@ func TestScSelectPeer(t *testing.T) { args: args{height: 4}, wantResult: "P1", }, + { + name: "one Ready higher peer with higher base", + fields: scTestParams{ + peers: map[string]*scPeer{"P1": {base: 4, height: 6, state: peerStateReady}}, + allB: []int64{1, 2, 3, 4, 5, 6}, + }, + args: args{height: 3}, + wantResult: "", + wantError: true, + }, { name: "many Ready higher peers with different number of pending requests", fields: scTestParams{ @@ -1990,7 +2056,7 @@ func TestScHandle(t *testing.T) { args: args{event: bcAddNewPeer{peerID: "P1"}}, wantEvent: noOpEvent{}, wantSc: &scTestParams{startTime: now, peers: map[string]*scPeer{ - "P1": {height: -1, state: peerStateNew}}, height: 1}, + "P1": {base: -1, height: -1, state: peerStateNew}}, height: 1}, }, { // set height of P1 args: args{event: bcStatusResponse{peerID: "P1", time: tick[0], height: 3}}, diff --git a/config/config_test.go b/config/config_test.go index 6da032d07..c83f1c3f5 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -134,27 +134,44 @@ func TestFastSyncConfigValidateBasic(t *testing.T) { assert.Error(t, cfg.ValidateBasic()) } -func TestConsensusConfigValidateBasic(t *testing.T) { - cfg := TestConsensusConfig() - assert.NoError(t, cfg.ValidateBasic()) - - fieldsToTest := []string{ - "TimeoutPropose", - "TimeoutProposeDelta", - "TimeoutPrevote", - "TimeoutPrevoteDelta", - "TimeoutPrecommit", - "TimeoutPrecommitDelta", - "TimeoutCommit", - "CreateEmptyBlocksInterval", - "PeerGossipSleepDuration", - "PeerQueryMaj23SleepDuration", +func TestConsensusConfig_ValidateBasic(t *testing.T) { + // nolint: lll + testcases := map[string]struct { + modify func(*ConsensusConfig) + expectErr bool + }{ + "TimeoutPropose": {func(c *ConsensusConfig) { c.TimeoutPropose = time.Second }, false}, + "TimeoutPropose negative": {func(c *ConsensusConfig) { c.TimeoutPropose = -1 }, true}, + "TimeoutProposeDelta": {func(c *ConsensusConfig) { c.TimeoutProposeDelta = time.Second }, false}, + "TimeoutProposeDelta negative": {func(c *ConsensusConfig) { c.TimeoutProposeDelta = -1 }, true}, + "TimeoutPrevote": {func(c *ConsensusConfig) { c.TimeoutPrevote = time.Second }, false}, + "TimeoutPrevote negative": {func(c *ConsensusConfig) { c.TimeoutPrevote = -1 }, true}, + "TimeoutPrevoteDelta": {func(c *ConsensusConfig) { c.TimeoutPrevoteDelta = time.Second }, false}, + "TimeoutPrevoteDelta negative": {func(c *ConsensusConfig) { c.TimeoutPrevoteDelta = -1 }, true}, + "TimeoutPrecommit": {func(c *ConsensusConfig) { c.TimeoutPrecommit = time.Second }, false}, + "TimeoutPrecommit negative": {func(c *ConsensusConfig) { c.TimeoutPrecommit = -1 }, true}, + "TimeoutPrecommitDelta": {func(c *ConsensusConfig) { c.TimeoutPrecommitDelta = time.Second }, false}, + "TimeoutPrecommitDelta negative": {func(c *ConsensusConfig) { c.TimeoutPrecommitDelta = -1 }, true}, + "TimeoutCommit": {func(c *ConsensusConfig) { c.TimeoutCommit = time.Second }, false}, + "TimeoutCommit negative": {func(c *ConsensusConfig) { c.TimeoutCommit = -1 }, true}, + "PeerGossipSleepDuration": {func(c *ConsensusConfig) { c.PeerGossipSleepDuration = time.Second }, false}, + "PeerGossipSleepDuration negative": {func(c *ConsensusConfig) { c.PeerGossipSleepDuration = -1 }, true}, + "PeerQueryMaj23SleepDuration": {func(c *ConsensusConfig) { c.PeerQueryMaj23SleepDuration = time.Second }, false}, + "PeerQueryMaj23SleepDuration negative": {func(c *ConsensusConfig) { c.PeerQueryMaj23SleepDuration = -1 }, true}, } + for desc, tc := range testcases { + tc := tc // appease linter + t.Run(desc, func(t *testing.T) { + cfg := DefaultConsensusConfig() + tc.modify(cfg) - for _, fieldName := range fieldsToTest { - reflect.ValueOf(cfg).Elem().FieldByName(fieldName).SetInt(-1) - assert.Error(t, cfg.ValidateBasic()) - reflect.ValueOf(cfg).Elem().FieldByName(fieldName).SetInt(0) + err := cfg.ValidateBasic() + if tc.expectErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) } } diff --git a/consensus/reactor.go b/consensus/reactor.go index 3182bba89..c8c344ac8 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -494,8 +494,8 @@ OUTER_LOOP: } } - // If the peer is on a previous height, help catch up. - if (0 < prs.Height) && (prs.Height < rs.Height) { + // If the peer is on a previous height that we have, help catch up. + if (0 < prs.Height) && (prs.Height < rs.Height) && (prs.Height >= conR.conS.blockStore.Base()) { heightLogger := logger.With("height", prs.Height) // if we never received the commit message from the peer, the block parts wont be initialized @@ -503,7 +503,7 @@ OUTER_LOOP: blockMeta := conR.conS.blockStore.LoadBlockMeta(prs.Height) if blockMeta == nil { heightLogger.Error("Failed to load block meta", - "blockstoreHeight", conR.conS.blockStore.Height()) + "blockstoreBase", conR.conS.blockStore.Base(), "blockstoreHeight", conR.conS.blockStore.Height()) time.Sleep(conR.conS.config.PeerGossipSleepDuration) } else { ps.InitProposalBlockParts(blockMeta.BlockID.PartsHeader) @@ -567,8 +567,8 @@ func (conR *Reactor) gossipDataForCatchup(logger log.Logger, rs *cstypes.RoundSt // Ensure that the peer's PartSetHeader is correct blockMeta := conR.conS.blockStore.LoadBlockMeta(prs.Height) if blockMeta == nil { - logger.Error("Failed to load block meta", - "ourHeight", rs.Height, "blockstoreHeight", conR.conS.blockStore.Height()) + logger.Error("Failed to load block meta", "ourHeight", rs.Height, + "blockstoreBase", conR.conS.blockStore.Base(), "blockstoreHeight", conR.conS.blockStore.Height()) time.Sleep(conR.conS.config.PeerGossipSleepDuration) return } else if !blockMeta.BlockID.PartsHeader.Equals(prs.ProposalBlockPartsHeader) { @@ -803,15 +803,17 @@ OUTER_LOOP: // Maybe send Height/CatchupCommitRound/CatchupCommit. { prs := ps.GetRoundState() - if prs.CatchupCommitRound != -1 && 0 < prs.Height && prs.Height <= conR.conS.blockStore.Height() { - commit := conR.conS.LoadCommit(prs.Height) - peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{ - Height: prs.Height, - Round: commit.Round, - Type: types.PrecommitType, - BlockID: commit.BlockID, - })) - time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration) + if prs.CatchupCommitRound != -1 && prs.Height > 0 && prs.Height <= conR.conS.blockStore.Height() && + prs.Height >= conR.conS.blockStore.Base() { + if commit := conR.conS.LoadCommit(prs.Height); commit != nil { + peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{ + Height: prs.Height, + Round: commit.Round, + Type: types.PrecommitType, + BlockID: commit.BlockID, + })) + time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration) + } } } diff --git a/consensus/replay.go b/consensus/replay.go index 1453849cc..3d9d6614b 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -288,6 +288,7 @@ func (h *Handshaker) ReplayBlocks( appBlockHeight int64, proxyApp proxy.AppConns, ) ([]byte, error) { + storeBlockBase := h.store.Base() storeBlockHeight := h.store.Height() stateBlockHeight := state.LastBlockHeight h.logger.Info( @@ -341,12 +342,16 @@ func (h *Handshaker) ReplayBlocks( } } - // First handle edge cases and constraints on the storeBlockHeight. + // First handle edge cases and constraints on the storeBlockHeight and storeBlockBase. switch { case storeBlockHeight == 0: assertAppHashEqualsOneFromState(appHash, state) return appHash, nil + case appBlockHeight < storeBlockBase-1: + // the app is too far behind truncated store (can be 1 behind since we replay the next) + return appHash, sm.ErrAppBlockHeightTooLow{AppHeight: appBlockHeight, StoreBase: storeBlockBase} + case storeBlockHeight < appBlockHeight: // the app should never be ahead of the store (but this is under app's control) return appHash, sm.ErrAppBlockHeightTooHigh{CoreHeight: storeBlockHeight, AppHeight: appBlockHeight} @@ -472,7 +477,7 @@ func (h *Handshaker) replayBlock(state sm.State, height int64, proxyApp proxy.Ap blockExec.SetEventBus(h.eventBus) var err error - state, err = blockExec.ApplyBlock(state, meta.BlockID, block) + state, _, err = blockExec.ApplyBlock(state, meta.BlockID, block) if err != nil { return sm.State{}, err } diff --git a/consensus/replay_test.go b/consensus/replay_test.go index bbb73c9be..f886cdeeb 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -307,7 +307,8 @@ var ( // 0 - all synced up // 1 - saved block but app and state are behind // 2 - save block and committed but state is behind -var modes = []uint{0, 1, 2} +// 3 - save block and committed with truncated block store and state behind +var modes = []uint{0, 1, 2, 3} // This is actually not a test, it's for storing validator change tx data for testHandshakeReplay func TestSimulateValidatorsChange(t *testing.T) { @@ -531,10 +532,10 @@ func TestHandshakeReplayAll(t *testing.T) { // Sync many, not from scratch func TestHandshakeReplaySome(t *testing.T) { for _, m := range modes { - testHandshakeReplay(t, config, 1, m, false) + testHandshakeReplay(t, config, 2, m, false) } for _, m := range modes { - testHandshakeReplay(t, config, 1, m, true) + testHandshakeReplay(t, config, 2, m, true) } } @@ -637,7 +638,7 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin stateDB = dbm.NewMemDB() genisisState = sim.GenesisState config = sim.Config - chain = sim.Chain + chain = append([]*types.Block{}, sim.Chain...) // copy chain commits = sim.Commits store = newMockBlockStore(config, genisisState.ConsensusParams) } else { //test single node @@ -685,6 +686,15 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin buildAppStateFromChain(proxyApp, stateDB1, genisisState, chain, nBlocks, mode) } + // Prune block store if requested + expectError := false + if mode == 3 { + pruned, err := store.PruneBlocks(2) + require.NoError(t, err) + require.EqualValues(t, 1, pruned) + expectError = int64(nBlocks) < 2 + } + // now start the app using the handshake - it should sync genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile()) handshaker := NewHandshaker(stateDB, state, store, genDoc) @@ -693,7 +703,11 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin t.Fatalf("Error starting proxy app connections: %v", err) } defer proxyApp.Stop() - if err := handshaker.Handshake(proxyApp); err != nil { + err := handshaker.Handshake(proxyApp) + if expectError { + require.Error(t, err) + return + } else if err != nil { t.Fatalf("Error on abci handshake: %v", err) } @@ -728,7 +742,7 @@ func applyBlock(stateDB dbm.DB, st sm.State, blk *types.Block, proxyApp proxy.Ap blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool) blkID := types.BlockID{Hash: blk.Hash(), PartsHeader: blk.MakePartSet(testPartSize).Header()} - newState, err := blockExec.ApplyBlock(st, blkID, blk) + newState, _, err := blockExec.ApplyBlock(st, blkID, blk) if err != nil { panic(err) } @@ -758,17 +772,19 @@ func buildAppStateFromChain(proxyApp proxy.AppConns, stateDB dbm.DB, block := chain[i] state = applyBlock(stateDB, state, block, proxyApp) } - case 1, 2: + case 1, 2, 3: for i := 0; i < nBlocks-1; i++ { block := chain[i] state = applyBlock(stateDB, state, block, proxyApp) } - if mode == 2 { + if mode == 2 || mode == 3 { // update the kvstore height and apphash // as if we ran commit but not state = applyBlock(stateDB, state, chain[nBlocks-1], proxyApp) } + default: + panic(fmt.Sprintf("unknown mode %v", mode)) } } @@ -806,7 +822,7 @@ func buildTMStateFromChain( state = applyBlock(stateDB, state, block, proxyApp) } - case 1, 2: + case 1, 2, 3: // sync up to the penultimate as if we stored the block. // whether we commit or not depends on the appHash for _, block := range chain[:len(chain)-1] { @@ -816,6 +832,8 @@ func buildTMStateFromChain( // apply the final block to a state copy so we can // get the right next appHash but keep the state back applyBlock(stateDB, state, chain[len(chain)-1], proxyApp) + default: + panic(fmt.Sprintf("unknown mode %v", mode)) } return state @@ -1075,14 +1093,17 @@ type mockBlockStore struct { params types.ConsensusParams chain []*types.Block commits []*types.Commit + base int64 } // TODO: NewBlockStore(db.NewMemDB) ... func newMockBlockStore(config *cfg.Config, params types.ConsensusParams) *mockBlockStore { - return &mockBlockStore{config, params, nil, nil} + return &mockBlockStore{config, params, nil, nil, 0} } func (bs *mockBlockStore) Height() int64 { return int64(len(bs.chain)) } +func (bs *mockBlockStore) Base() int64 { return bs.base } +func (bs *mockBlockStore) Size() int64 { return bs.Height() - bs.Base() + 1 } func (bs *mockBlockStore) LoadBlock(height int64) *types.Block { return bs.chain[height-1] } func (bs *mockBlockStore) LoadBlockByHash(hash []byte) *types.Block { return bs.chain[int64(len(bs.chain))-1] @@ -1104,6 +1125,17 @@ func (bs *mockBlockStore) LoadSeenCommit(height int64) *types.Commit { return bs.commits[height-1] } +func (bs *mockBlockStore) PruneBlocks(height int64) (uint64, error) { + pruned := uint64(0) + for i := int64(0); i < height-1; i++ { + bs.chain[i] = nil + bs.commits[i] = nil + pruned++ + } + bs.base = height + return pruned, nil +} + //--------------------------------------- // Test handshake/init chain diff --git a/consensus/state.go b/consensus/state.go index cd3dbeccb..b58bb3050 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -490,6 +490,10 @@ func (cs *State) reconstructLastCommit(state sm.State) { return } seenCommit := cs.blockStore.LoadSeenCommit(state.LastBlockHeight) + if seenCommit == nil { + panic(fmt.Sprintf("Failed to reconstruct LastCommit: seen commit for height %v not found", + state.LastBlockHeight)) + } lastPrecommits := types.CommitToVoteSet(state.ChainID, seenCommit, state.LastValidators) if !lastPrecommits.HasTwoThirdsMajority() { panic("Failed to reconstruct LastCommit: Does not have +2/3 maj") @@ -878,6 +882,9 @@ func (cs *State) needProofBlock(height int64) bool { } lastBlockMeta := cs.blockStore.LoadBlockMeta(height - 1) + if lastBlockMeta == nil { + panic(fmt.Sprintf("needProofBlock: last block meta for height %d not found", height-1)) + } return !bytes.Equal(cs.state.AppHash, lastBlockMeta.Header.AppHash) } @@ -1448,7 +1455,8 @@ func (cs *State) finalizeCommit(height int64) { // Execute and commit the block, update and save the state, and update the mempool. // NOTE The block.AppHash wont reflect these txs until the next block. var err error - stateCopy, err = cs.blockExec.ApplyBlock( + var retainHeight int64 + stateCopy, retainHeight, err = cs.blockExec.ApplyBlock( stateCopy, types.BlockID{Hash: block.Hash(), PartsHeader: blockParts.Header()}, block) @@ -1463,6 +1471,16 @@ func (cs *State) finalizeCommit(height int64) { fail.Fail() // XXX + // Prune old heights, if requested by ABCI app. + if retainHeight > 0 { + pruned, err := cs.pruneBlocks(retainHeight) + if err != nil { + cs.Logger.Error("Failed to prune blocks", "retainHeight", retainHeight, "err", err) + } else { + cs.Logger.Info("Pruned blocks", "pruned", pruned, "retainHeight", retainHeight) + } + } + // must be called before we update state cs.recordMetrics(height, block) @@ -1481,6 +1499,22 @@ func (cs *State) finalizeCommit(height int64) { // * cs.StartTime is set to when we will start round0. } +func (cs *State) pruneBlocks(retainHeight int64) (uint64, error) { + base := cs.blockStore.Base() + if retainHeight <= base { + return 0, nil + } + pruned, err := cs.blockStore.PruneBlocks(retainHeight) + if err != nil { + return 0, fmt.Errorf("failed to prune block store: %w", err) + } + err = sm.PruneStates(cs.blockExec.DB(), base, retainHeight) + if err != nil { + return 0, fmt.Errorf("failed to prune state database: %w", err) + } + return pruned, nil +} + func (cs *State) recordMetrics(height int64, block *types.Block) { cs.metrics.Validators.Set(float64(cs.Validators.Size())) cs.metrics.ValidatorsPower.Set(float64(cs.Validators.TotalVotingPower())) @@ -1547,9 +1581,11 @@ func (cs *State) recordMetrics(height int64, block *types.Block) { if height > 1 { lastBlockMeta := cs.blockStore.LoadBlockMeta(height - 1) - cs.metrics.BlockIntervalSeconds.Set( - block.Time.Sub(lastBlockMeta.Header.Time).Seconds(), - ) + if lastBlockMeta != nil { + cs.metrics.BlockIntervalSeconds.Set( + block.Time.Sub(lastBlockMeta.Header.Time).Seconds(), + ) + } } cs.metrics.NumTxs.Set(float64(len(block.Data.Txs))) diff --git a/evidence/pool.go b/evidence/pool.go index a4d72cd2f..8c7d78694 100644 --- a/evidence/pool.go +++ b/evidence/pool.go @@ -95,7 +95,7 @@ func (evpool *Pool) Update(block *types.Block, state sm.State) { } // AddEvidence checks the evidence is valid and adds it to the pool. -func (evpool *Pool) AddEvidence(evidence types.Evidence) (err error) { +func (evpool *Pool) AddEvidence(evidence types.Evidence) error { // TODO: check if we already have evidence for this // validator at this height so we dont get spammed @@ -106,14 +106,17 @@ func (evpool *Pool) AddEvidence(evidence types.Evidence) (err error) { // fetch the validator and return its voting power as its priority // TODO: something better ? - valset, _ := sm.LoadValidators(evpool.stateDB, evidence.Height()) + valset, err := sm.LoadValidators(evpool.stateDB, evidence.Height()) + if err != nil { + return err + } _, val := valset.GetByAddress(evidence.Address()) priority := val.VotingPower added := evpool.store.AddNewEvidence(evidence, priority) if !added { // evidence already known, just ignore - return + return nil } evpool.logger.Info("Verified new evidence of byzantine behaviour", "evidence", evidence) diff --git a/rpc/core/blocks.go b/rpc/core/blocks.go index e340d4dfb..1d608534a 100644 --- a/rpc/core/blocks.go +++ b/rpc/core/blocks.go @@ -17,7 +17,7 @@ func BlockchainInfo(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ctypes. // maximum 20 block metas const limit int64 = 20 var err error - minHeight, maxHeight, err = filterMinMax(blockStore.Height(), minHeight, maxHeight, limit) + minHeight, maxHeight, err = filterMinMax(blockStore.Base(), blockStore.Height(), minHeight, maxHeight, limit) if err != nil { return nil, err } @@ -34,11 +34,10 @@ func BlockchainInfo(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ctypes. BlockMetas: blockMetas}, nil } -// error if either min or max are negative or min < max -// if 0, use 1 for min, latest block height for max +// error if either min or max are negative or min > max +// if 0, use blockstore base for min, latest block height for max // enforce limit. -// error if min > max -func filterMinMax(height, min, max, limit int64) (int64, int64, error) { +func filterMinMax(base, height, min, max, limit int64) (int64, int64, error) { // filter negatives if min < 0 || max < 0 { return min, max, fmt.Errorf("heights must be non-negative") @@ -55,6 +54,9 @@ func filterMinMax(height, min, max, limit int64) (int64, int64, error) { // limit max to the height max = tmmath.MinInt64(height, max) + // limit min to the base + min = tmmath.MaxInt64(base, min) + // limit min to within `limit` of max // so the total number of blocks returned will be `limit` min = tmmath.MaxInt64(min, max-limit+1) @@ -69,8 +71,7 @@ func filterMinMax(height, min, max, limit int64) (int64, int64, error) { // If no height is provided, it will fetch the latest block. // More: https://docs.tendermint.com/master/rpc/#/Info/block func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) { - storeHeight := blockStore.Height() - height, err := getHeight(storeHeight, heightPtr) + height, err := getHeight(blockStore.Base(), blockStore.Height(), heightPtr) if err != nil { return nil, err } @@ -99,8 +100,7 @@ func BlockByHash(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error // If no height is provided, it will fetch the commit for the latest block. // More: https://docs.tendermint.com/master/rpc/#/Info/commit func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, error) { - storeHeight := blockStore.Height() - height, err := getHeight(storeHeight, heightPtr) + height, err := getHeight(blockStore.Base(), blockStore.Height(), heightPtr) if err != nil { return nil, err } @@ -113,7 +113,7 @@ func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, erro // If the next block has not been committed yet, // use a non-canonical commit - if height == storeHeight { + if height == blockStore.Height() { commit := blockStore.LoadSeenCommit(height) return ctypes.NewResultCommit(&header, commit, false), nil } @@ -131,8 +131,7 @@ func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, erro // getBlock(h).Txs[5] // More: https://docs.tendermint.com/master/rpc/#/Info/block_results func BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockResults, error) { - storeHeight := blockStore.Height() - height, err := getHeight(storeHeight, heightPtr) + height, err := getHeight(blockStore.Base(), blockStore.Height(), heightPtr) if err != nil { return nil, err } @@ -152,7 +151,7 @@ func BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockR }, nil } -func getHeight(currentHeight int64, heightPtr *int64) (int64, error) { +func getHeight(currentBase int64, currentHeight int64, heightPtr *int64) (int64, error) { if heightPtr != nil { height := *heightPtr if height <= 0 { @@ -161,6 +160,10 @@ func getHeight(currentHeight int64, heightPtr *int64) (int64, error) { if height > currentHeight { return 0, fmt.Errorf("height must be less than or equal to the current blockchain height") } + if height < currentBase { + return 0, fmt.Errorf("height %v is not available, blocks pruned at height %v", + height, currentBase) + } return height, nil } return currentHeight, nil diff --git a/rpc/core/blocks_test.go b/rpc/core/blocks_test.go index 1c4c3af13..c0561647f 100644 --- a/rpc/core/blocks_test.go +++ b/rpc/core/blocks_test.go @@ -19,42 +19,46 @@ import ( func TestBlockchainInfo(t *testing.T) { cases := []struct { min, max int64 - height int64 + base, height int64 limit int64 resultLength int64 wantErr bool }{ // min > max - {0, 0, 0, 10, 0, true}, // min set to 1 - {0, 1, 0, 10, 0, true}, // max set to height (0) - {0, 0, 1, 10, 1, false}, // max set to height (1) - {2, 0, 1, 10, 0, true}, // max set to height (1) - {2, 1, 5, 10, 0, true}, + {0, 0, 0, 0, 10, 0, true}, // min set to 1 + {0, 1, 0, 0, 10, 0, true}, // max set to height (0) + {0, 0, 0, 1, 10, 1, false}, // max set to height (1) + {2, 0, 0, 1, 10, 0, true}, // max set to height (1) + {2, 1, 0, 5, 10, 0, true}, // negative - {1, 10, 14, 10, 10, false}, // control - {-1, 10, 14, 10, 0, true}, - {1, -10, 14, 10, 0, true}, - {-9223372036854775808, -9223372036854775788, 100, 20, 0, true}, + {1, 10, 0, 14, 10, 10, false}, // control + {-1, 10, 0, 14, 10, 0, true}, + {1, -10, 0, 14, 10, 0, true}, + {-9223372036854775808, -9223372036854775788, 0, 100, 20, 0, true}, + + // check base + {1, 1, 1, 1, 1, 1, false}, + {2, 5, 3, 5, 5, 3, false}, // check limit and height - {1, 1, 1, 10, 1, false}, - {1, 1, 5, 10, 1, false}, - {2, 2, 5, 10, 1, false}, - {1, 2, 5, 10, 2, false}, - {1, 5, 1, 10, 1, false}, - {1, 5, 10, 10, 5, false}, - {1, 15, 10, 10, 10, false}, - {1, 15, 15, 10, 10, false}, - {1, 15, 15, 20, 15, false}, - {1, 20, 15, 20, 15, false}, - {1, 20, 20, 20, 20, false}, + {1, 1, 0, 1, 10, 1, false}, + {1, 1, 0, 5, 10, 1, false}, + {2, 2, 0, 5, 10, 1, false}, + {1, 2, 0, 5, 10, 2, false}, + {1, 5, 0, 1, 10, 1, false}, + {1, 5, 0, 10, 10, 5, false}, + {1, 15, 0, 10, 10, 10, false}, + {1, 15, 0, 15, 10, 10, false}, + {1, 15, 0, 15, 20, 15, false}, + {1, 20, 0, 15, 20, 15, false}, + {1, 20, 0, 20, 20, 20, false}, } for i, c := range cases { caseString := fmt.Sprintf("test %d failed", i) - min, max, err := filterMinMax(c.height, c.min, c.max, c.limit) + min, max, err := filterMinMax(c.base, c.height, c.min, c.max, c.limit) if c.wantErr { require.Error(t, err, caseString) } else { @@ -112,12 +116,15 @@ type mockBlockStore struct { height int64 } +func (mockBlockStore) Base() int64 { return 1 } func (store mockBlockStore) Height() int64 { return store.height } +func (store mockBlockStore) Size() int64 { return store.height } func (mockBlockStore) LoadBlockMeta(height int64) *types.BlockMeta { return nil } func (mockBlockStore) LoadBlock(height int64) *types.Block { return nil } func (mockBlockStore) LoadBlockByHash(hash []byte) *types.Block { return nil } func (mockBlockStore) LoadBlockPart(height int64, index int) *types.Part { return nil } func (mockBlockStore) LoadBlockCommit(height int64) *types.Commit { return nil } func (mockBlockStore) LoadSeenCommit(height int64) *types.Commit { return nil } +func (mockBlockStore) PruneBlocks(height int64) (uint64, error) { return 0, nil } func (mockBlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) { } diff --git a/rpc/core/consensus.go b/rpc/core/consensus.go index a2a619ea5..8ea2dde4f 100644 --- a/rpc/core/consensus.go +++ b/rpc/core/consensus.go @@ -18,7 +18,7 @@ func Validators(ctx *rpctypes.Context, heightPtr *int64, page, perPage int) (*ct // The latest validator that we know is the // NextValidator of the last block. height := consensusState.GetState().LastBlockHeight + 1 - height, err := getHeight(height, heightPtr) + height, err := getHeight(blockStore.Base(), height, heightPtr) if err != nil { return nil, err } @@ -91,7 +91,7 @@ func ConsensusState(ctx *rpctypes.Context) (*ctypes.ResultConsensusState, error) // More: https://docs.tendermint.com/master/rpc/#/Info/consensus_params func ConsensusParams(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultConsensusParams, error) { height := consensusState.GetState().LastBlockHeight + 1 - height, err := getHeight(height, heightPtr) + height, err := getHeight(blockStore.Base(), height, heightPtr) if err != nil { return nil, err } diff --git a/rpc/core/status.go b/rpc/core/status.go index e6438009a..4e950d4a3 100644 --- a/rpc/core/status.go +++ b/rpc/core/status.go @@ -16,6 +16,20 @@ import ( // hash, app hash, block height and time. // More: https://docs.tendermint.com/master/rpc/#/Info/status func Status(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) { + var ( + earliestBlockMeta *types.BlockMeta + earliestBlockHash tmbytes.HexBytes + earliestAppHash tmbytes.HexBytes + earliestBlockTimeNano int64 + ) + earliestBlockHeight := blockStore.Base() + earliestBlockMeta = blockStore.LoadBlockMeta(earliestBlockHeight) + if earliestBlockMeta != nil { + earliestAppHash = earliestBlockMeta.Header.AppHash + earliestBlockHash = earliestBlockMeta.BlockID.Hash + earliestBlockTimeNano = earliestBlockMeta.Header.Time.UnixNano() + } + var latestHeight int64 if consensusReactor.FastSync() { latestHeight = blockStore.Height() @@ -36,8 +50,6 @@ func Status(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) { latestBlockTimeNano = latestBlockMeta.Header.Time.UnixNano() } - latestBlockTime := time.Unix(0, latestBlockTimeNano) - var votingPower int64 if val := validatorAtHeight(latestHeight); val != nil { votingPower = val.VotingPower @@ -46,11 +58,15 @@ func Status(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) { result := &ctypes.ResultStatus{ NodeInfo: p2pTransport.NodeInfo().(p2p.DefaultNodeInfo), SyncInfo: ctypes.SyncInfo{ - LatestBlockHash: latestBlockHash, - LatestAppHash: latestAppHash, - LatestBlockHeight: latestHeight, - LatestBlockTime: latestBlockTime, - CatchingUp: consensusReactor.FastSync(), + LatestBlockHash: latestBlockHash, + LatestAppHash: latestAppHash, + LatestBlockHeight: latestHeight, + LatestBlockTime: time.Unix(0, latestBlockTimeNano), + EarliestBlockHash: earliestBlockHash, + EarliestAppHash: earliestAppHash, + EarliestBlockHeight: earliestBlockHeight, + EarliestBlockTime: time.Unix(0, earliestBlockTimeNano), + CatchingUp: consensusReactor.FastSync(), }, ValidatorInfo: ctypes.ValidatorInfo{ Address: pubKey.Address(), diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index ed435bbe0..18b2109ed 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -65,7 +65,13 @@ type SyncInfo struct { LatestAppHash bytes.HexBytes `json:"latest_app_hash"` LatestBlockHeight int64 `json:"latest_block_height"` LatestBlockTime time.Time `json:"latest_block_time"` - CatchingUp bool `json:"catching_up"` + + EarliestBlockHash bytes.HexBytes `json:"earliest_block_hash"` + EarliestAppHash bytes.HexBytes `json:"earliest_app_hash"` + EarliestBlockHeight int64 `json:"earliest_block_height"` + EarliestBlockTime time.Time `json:"earliest_block_time"` + + CatchingUp bool `json:"catching_up"` } // Info about the node's validator diff --git a/rpc/swagger/swagger.yaml b/rpc/swagger/swagger.yaml index e0f40262b..000ea972c 100644 --- a/rpc/swagger/swagger.yaml +++ b/rpc/swagger/swagger.yaml @@ -1123,6 +1123,18 @@ components: latest_block_time: type: string example: "2019-08-01T11:52:22.818762194Z" + earliest_block_hash: + type: string + example: "790BA84C3545FCCC49A5C629CEE6EA58A6E875C3862175BDC11EE7AF54703501" + earliest_app_hash: + type: string + example: "C9AEBB441B787D9F1D846DE51F3826F4FD386108B59B08239653ABF59455C3F8" + earliest_block_height: + type: string + example: "1262196" + earliest_block_time: + type: string + example: "2019-08-01T11:52:22.818762194Z" catching_up: type: boolean example: false diff --git a/state/errors.go b/state/errors.go index cd4cd7824..6e0cdfa47 100644 --- a/state/errors.go +++ b/state/errors.go @@ -21,6 +21,11 @@ type ( AppHeight int64 } + ErrAppBlockHeightTooLow struct { + AppHeight int64 + StoreBase int64 + } + ErrLastStateMismatch struct { Height int64 Core []byte @@ -46,12 +51,12 @@ type ( ) func (e ErrUnknownBlock) Error() string { - return fmt.Sprintf("Could not find block #%d", e.Height) + return fmt.Sprintf("could not find block #%d", e.Height) } func (e ErrBlockHashMismatch) Error() string { return fmt.Sprintf( - "App block hash (%X) does not match core block hash (%X) for height %d", + "app block hash (%X) does not match core block hash (%X) for height %d", e.AppHash, e.CoreHash, e.Height, @@ -59,11 +64,16 @@ func (e ErrBlockHashMismatch) Error() string { } func (e ErrAppBlockHeightTooHigh) Error() string { - return fmt.Sprintf("App block height (%d) is higher than core (%d)", e.AppHeight, e.CoreHeight) + return fmt.Sprintf("app block height (%d) is higher than core (%d)", e.AppHeight, e.CoreHeight) } + +func (e ErrAppBlockHeightTooLow) Error() string { + return fmt.Sprintf("app block height (%d) is too far below block store base (%d)", e.AppHeight, e.StoreBase) +} + func (e ErrLastStateMismatch) Error() string { return fmt.Sprintf( - "Latest tendermint block (%d) LastAppHash (%X) does not match app's AppHash (%X)", + "latest tendermint block (%d) LastAppHash (%X) does not match app's AppHash (%X)", e.Height, e.Core, e.App, @@ -72,20 +82,20 @@ func (e ErrLastStateMismatch) Error() string { func (e ErrStateMismatch) Error() string { return fmt.Sprintf( - "State after replay does not match saved state. Got ----\n%v\nExpected ----\n%v\n", + "state after replay does not match saved state. Got ----\n%v\nExpected ----\n%v\n", e.Got, e.Expected, ) } func (e ErrNoValSetForHeight) Error() string { - return fmt.Sprintf("Could not find validator set for height #%d", e.Height) + return fmt.Sprintf("could not find validator set for height #%d", e.Height) } func (e ErrNoConsensusParamsForHeight) Error() string { - return fmt.Sprintf("Could not find consensus params for height #%d", e.Height) + return fmt.Sprintf("could not find consensus params for height #%d", e.Height) } func (e ErrNoABCIResponsesForHeight) Error() string { - return fmt.Sprintf("Could not find results for height #%d", e.Height) + return fmt.Sprintf("could not find results for height #%d", e.Height) } diff --git a/state/execution.go b/state/execution.go index 15f436463..c6d0b8475 100644 --- a/state/execution.go +++ b/state/execution.go @@ -119,13 +119,14 @@ func (blockExec *BlockExecutor) ValidateBlock(state State, block *types.Block) e // ApplyBlock validates the block against the state, executes it against the app, // fires the relevant events, commits the app, and saves the new state and responses. +// It returns the new state and the block height to retain (pruning older blocks). // It's the only function that needs to be called // from outside this package to process and commit an entire block. // It takes a blockID to avoid recomputing the parts hash. -func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, block *types.Block) (State, error) { +func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, block *types.Block) (State, int64, error) { if err := blockExec.ValidateBlock(state, block); err != nil { - return state, ErrInvalidBlock(err) + return state, 0, ErrInvalidBlock(err) } startTime := time.Now().UnixNano() @@ -133,7 +134,7 @@ func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, b endTime := time.Now().UnixNano() blockExec.metrics.BlockProcessingTime.Observe(float64(endTime-startTime) / 1000000) if err != nil { - return state, ErrProxyAppConn(err) + return state, 0, ErrProxyAppConn(err) } fail.Fail() // XXX @@ -147,11 +148,11 @@ func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, b abciValUpdates := abciResponses.EndBlock.ValidatorUpdates err = validateValidatorUpdates(abciValUpdates, state.ConsensusParams.Validator) if err != nil { - return state, fmt.Errorf("error in validator updates: %v", err) + return state, 0, fmt.Errorf("error in validator updates: %v", err) } validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciValUpdates) if err != nil { - return state, err + return state, 0, err } if len(validatorUpdates) > 0 { blockExec.logger.Info("Updates to validators", "updates", types.ValidatorListString(validatorUpdates)) @@ -160,13 +161,13 @@ func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, b // Update the state with the block and responses. state, err = updateState(state, blockID, &block.Header, abciResponses, validatorUpdates) if err != nil { - return state, fmt.Errorf("commit failed for application: %v", err) + return state, 0, fmt.Errorf("commit failed for application: %v", err) } // Lock mempool, commit app state, update mempoool. - appHash, err := blockExec.Commit(state, block, abciResponses.DeliverTxs) + appHash, retainHeight, err := blockExec.Commit(state, block, abciResponses.DeliverTxs) if err != nil { - return state, fmt.Errorf("commit failed for application: %v", err) + return state, 0, fmt.Errorf("commit failed for application: %v", err) } // Update evpool with the block and state. @@ -184,12 +185,12 @@ func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, b // NOTE: if we crash between Commit and Save, events wont be fired during replay fireEvents(blockExec.logger, blockExec.eventBus, block, abciResponses, validatorUpdates) - return state, nil + return state, retainHeight, nil } // Commit locks the mempool, runs the ABCI Commit message, and updates the // mempool. -// It returns the result of calling abci.Commit (the AppHash), and an error. +// It returns the result of calling abci.Commit (the AppHash) and the height to retain (if any). // The Mempool must be locked during commit and update because state is // typically reset on Commit and old txs must be replayed against committed // state before new txs are run in the mempool, lest they be invalid. @@ -197,7 +198,7 @@ func (blockExec *BlockExecutor) Commit( state State, block *types.Block, deliverTxResponses []*abci.ResponseDeliverTx, -) ([]byte, error) { +) ([]byte, int64, error) { blockExec.mempool.Lock() defer blockExec.mempool.Unlock() @@ -206,7 +207,7 @@ func (blockExec *BlockExecutor) Commit( err := blockExec.mempool.FlushAppConn() if err != nil { blockExec.logger.Error("Client error during mempool.FlushAppConn", "err", err) - return nil, err + return nil, 0, err } // Commit block, get hash back @@ -216,7 +217,7 @@ func (blockExec *BlockExecutor) Commit( "Client error during proxyAppConn.CommitSync", "err", err, ) - return nil, err + return nil, 0, err } // ResponseCommit has no error code - just data @@ -236,7 +237,7 @@ func (blockExec *BlockExecutor) Commit( TxPostCheck(state), ) - return res.Data, err + return res.Data, res.RetainHeight, err } //--------------------------------------------------------- diff --git a/state/execution_test.go b/state/execution_test.go index 7d1337391..041f232bd 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -27,7 +27,9 @@ var ( ) func TestApplyBlock(t *testing.T) { - cc := proxy.NewLocalClientCreator(kvstore.NewApplication()) + app := kvstore.NewApplication() + app.RetainBlocks = 1 + cc := proxy.NewLocalClientCreator(app) proxyApp := proxy.NewAppConns(cc) err := proxyApp.Start() require.Nil(t, err) @@ -41,9 +43,9 @@ func TestApplyBlock(t *testing.T) { block := makeBlock(state, 1) blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} - //nolint:ineffassign - state, err = blockExec.ApplyBlock(state, blockID, block) + _, retainHeight, err := blockExec.ApplyBlock(state, blockID, block) require.Nil(t, err) + assert.EqualValues(t, retainHeight, 1) // TODO check state and mempool } @@ -356,7 +358,7 @@ func TestEndBlockValidatorUpdates(t *testing.T) { {PubKey: types.TM2PB.PubKey(pubkey), Power: 10}, } - state, err = blockExec.ApplyBlock(state, blockID, block) + state, _, err = blockExec.ApplyBlock(state, blockID, block) require.Nil(t, err) // test new validator was added to NextValidators @@ -410,7 +412,7 @@ func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) { {PubKey: types.TM2PB.PubKey(state.Validators.Validators[0].PubKey), Power: 0}, } - assert.NotPanics(t, func() { state, err = blockExec.ApplyBlock(state, blockID, block) }) + assert.NotPanics(t, func() { state, _, err = blockExec.ApplyBlock(state, blockID, block) }) assert.NotNil(t, err) assert.NotEmpty(t, state.NextValidators.Validators) diff --git a/state/helpers_test.go b/state/helpers_test.go index f8758f987..a85e35748 100644 --- a/state/helpers_test.go +++ b/state/helpers_test.go @@ -66,7 +66,7 @@ func makeAndApplyGoodBlock(state sm.State, height int64, lastCommit *types.Commi } blockID := types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{Total: 3, Hash: tmrand.Bytes(32)}} - state, err := blockExec.ApplyBlock(state, blockID, block) + state, _, err := blockExec.ApplyBlock(state, blockID, block) if err != nil { return state, types.BlockID{}, err } diff --git a/state/services.go b/state/services.go index d83a410c9..a30956bdc 100644 --- a/state/services.go +++ b/state/services.go @@ -14,13 +14,17 @@ import ( // BlockStore defines the interface used by the ConsensusState. type BlockStore interface { + Base() int64 Height() int64 + Size() int64 LoadBlockMeta(height int64) *types.BlockMeta LoadBlock(height int64) *types.Block SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) + PruneBlocks(height int64) (uint64, error) + LoadBlockByHash(hash []byte) *types.Block LoadBlockPart(height int64, index int) *types.Part diff --git a/state/store.go b/state/store.go index efb7bddf0..08b695f8a 100644 --- a/state/store.go +++ b/state/store.go @@ -125,6 +125,102 @@ type ABCIResponses struct { BeginBlock *abci.ResponseBeginBlock `json:"begin_block"` } +// PruneStates deletes states between the given heights (including from, excluding to). It is not +// guaranteed to delete all states, since the last checkpointed state and states being pointed to by +// e.g. `LastHeightChanged` must remain. The state at to must also exist. +// +// The from parameter is necessary since we can't do a key scan in a performant way due to the key +// encoding not preserving ordering: https://github.com/tendermint/tendermint/issues/4567 +// This will cause some old states to be left behind when doing incremental partial prunes, +// specifically older checkpoints and LastHeightChanged targets. +func PruneStates(db dbm.DB, from int64, to int64) error { + if from <= 0 || to <= 0 { + return fmt.Errorf("from height %v and to height %v must be greater than 0", from, to) + } + if from >= to { + return fmt.Errorf("from height %v must be lower than to height %v", from, to) + } + valInfo := loadValidatorsInfo(db, to) + if valInfo == nil { + return fmt.Errorf("validators at height %v not found", to) + } + paramsInfo := loadConsensusParamsInfo(db, to) + if paramsInfo == nil { + return fmt.Errorf("consensus params at height %v not found", to) + } + + keepVals := make(map[int64]bool) + if valInfo.ValidatorSet == nil { + keepVals[valInfo.LastHeightChanged] = true + keepVals[lastStoredHeightFor(to, valInfo.LastHeightChanged)] = true // keep last checkpoint too + } + keepParams := make(map[int64]bool) + if paramsInfo.ConsensusParams.Equals(&types.ConsensusParams{}) { + keepParams[paramsInfo.LastHeightChanged] = true + } + + batch := db.NewBatch() + defer batch.Close() + pruned := uint64(0) + var err error + + // We have to delete in reverse order, to avoid deleting previous heights that have validator + // sets and consensus params that we may need to retrieve. + for h := to - 1; h >= from; h-- { + // For heights we keep, we must make sure they have the full validator set or consensus + // params, otherwise they will panic if they're retrieved directly (instead of + // indirectly via a LastHeightChanged pointer). + if keepVals[h] { + v := loadValidatorsInfo(db, h) + if v.ValidatorSet == nil { + v.ValidatorSet, err = LoadValidators(db, h) + if err != nil { + return err + } + v.LastHeightChanged = h + batch.Set(calcValidatorsKey(h), v.Bytes()) + } + } else { + batch.Delete(calcValidatorsKey(h)) + } + + if keepParams[h] { + p := loadConsensusParamsInfo(db, h) + if p.ConsensusParams.Equals(&types.ConsensusParams{}) { + p.ConsensusParams, err = LoadConsensusParams(db, h) + if err != nil { + return err + } + p.LastHeightChanged = h + batch.Set(calcConsensusParamsKey(h), p.Bytes()) + } + } else { + batch.Delete(calcConsensusParamsKey(h)) + } + + batch.Delete(calcABCIResponsesKey(h)) + pruned++ + + // avoid batches growing too large by flushing to database regularly + if pruned%1000 == 0 && pruned > 0 { + err := batch.Write() + if err != nil { + return err + } + batch.Close() + batch = db.NewBatch() + defer batch.Close() + } + } + + err = batch.WriteSync() + if err != nil { + return err + } + + return nil +} + // NewABCIResponses returns a new ABCIResponses func NewABCIResponses(block *types.Block) *ABCIResponses { resDeliverTxs := make([]*abci.ResponseDeliverTx, len(block.Data.Txs)) diff --git a/state/store_test.go b/state/store_test.go index 7661b631f..d46eaa9eb 100644 --- a/state/store_test.go +++ b/state/store_test.go @@ -65,3 +65,118 @@ func BenchmarkLoadValidators(b *testing.B) { }) } } + +func TestPruneStates(t *testing.T) { + testcases := map[string]struct { + makeHeights int64 + pruneFrom int64 + pruneTo int64 + expectErr bool + expectVals []int64 + expectParams []int64 + expectABCI []int64 + }{ + "error on pruning from 0": {100, 0, 5, true, nil, nil, nil}, + "error when from > to": {100, 3, 2, true, nil, nil, nil}, + "error when from == to": {100, 3, 3, true, nil, nil, nil}, + "error when to does not exist": {100, 1, 101, true, nil, nil, nil}, + "prune all": {100, 1, 100, false, []int64{93, 100}, []int64{95, 100}, []int64{100}}, + "prune some": {10, 2, 8, false, []int64{1, 3, 8, 9, 10}, []int64{1, 5, 8, 9, 10}, []int64{1, 8, 9, 10}}, + "prune across checkpoint": {100001, 1, 100001, false, []int64{99993, 100000, 100001}, []int64{99995, 100001}, []int64{100001}}, + } + for name, tc := range testcases { + tc := tc + t.Run(name, func(t *testing.T) { + db := dbm.NewMemDB() + + // Generate a bunch of state data. Validators change for heights ending with 3, and + // parameters when ending with 5. + validator := &types.Validator{Address: []byte{1, 2, 3}, VotingPower: 100} + validatorSet := &types.ValidatorSet{ + Validators: []*types.Validator{validator}, + Proposer: validator, + } + valsChanged := int64(0) + paramsChanged := int64(0) + + for h := int64(1); h <= tc.makeHeights; h++ { + if valsChanged == 0 || h%10 == 2 { + valsChanged = h + 1 // Have to add 1, since NextValidators is what's stored + } + if paramsChanged == 0 || h%10 == 5 { + paramsChanged = h + } + + sm.SaveState(db, sm.State{ + LastBlockHeight: h - 1, + Validators: validatorSet, + NextValidators: validatorSet, + ConsensusParams: types.ConsensusParams{ + Block: types.BlockParams{MaxBytes: 10e6}, + }, + LastHeightValidatorsChanged: valsChanged, + LastHeightConsensusParamsChanged: paramsChanged, + }) + sm.SaveABCIResponses(db, h, sm.NewABCIResponses(&types.Block{ + Header: types.Header{Height: h}, + Data: types.Data{ + Txs: types.Txs{ + []byte{1}, + []byte{2}, + []byte{3}, + }, + }, + })) + } + + // Test assertions + err := sm.PruneStates(db, tc.pruneFrom, tc.pruneTo) + if tc.expectErr { + require.Error(t, err) + return + } + require.NoError(t, err) + + expectVals := sliceToMap(tc.expectVals) + expectParams := sliceToMap(tc.expectParams) + expectABCI := sliceToMap(tc.expectABCI) + + for h := int64(1); h <= tc.makeHeights; h++ { + vals, err := sm.LoadValidators(db, h) + if expectVals[h] { + require.NoError(t, err, "validators height %v", h) + require.NotNil(t, vals) + } else { + require.Error(t, err, "validators height %v", h) + require.Equal(t, sm.ErrNoValSetForHeight{Height: h}, err) + } + + params, err := sm.LoadConsensusParams(db, h) + if expectParams[h] { + require.NoError(t, err, "params height %v", h) + require.False(t, params.Equals(&types.ConsensusParams{})) + } else { + require.Error(t, err, "params height %v", h) + require.Equal(t, sm.ErrNoConsensusParamsForHeight{Height: h}, err) + } + + abci, err := sm.LoadABCIResponses(db, h) + if expectABCI[h] { + require.NoError(t, err, "abci height %v", h) + require.NotNil(t, abci) + } else { + require.Error(t, err, "abci height %v", h) + require.Equal(t, sm.ErrNoABCIResponsesForHeight{Height: h}, err) + } + } + }) + } +} + +func sliceToMap(s []int64) map[int64]bool { + m := make(map[int64]bool, len(s)) + for _, i := range s { + m[i] = true + } + return m +} diff --git a/store/store.go b/store/store.go index 2f9ba93fd..38a53d590 100644 --- a/store/store.go +++ b/store/store.go @@ -7,6 +7,7 @@ import ( "github.com/pkg/errors" + db "github.com/tendermint/tm-db" dbm "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/types" @@ -24,6 +25,8 @@ Currently the precommit signatures are duplicated in the Block parts as well as the Commit. In the future this may change, perhaps by moving the Commit data outside the Block. (TODO) +The store can be assumed to contain all contiguous blocks between base and height (inclusive). + // NOTE: BlockStore methods will panic if they encounter errors // deserializing loaded data, indicating probable corruption on disk. */ @@ -31,6 +34,7 @@ type BlockStore struct { db dbm.DB mtx sync.RWMutex + base int64 height int64 } @@ -39,18 +43,36 @@ type BlockStore struct { func NewBlockStore(db dbm.DB) *BlockStore { bsjson := LoadBlockStoreStateJSON(db) return &BlockStore{ + base: bsjson.Base, height: bsjson.Height, db: db, } } -// Height returns the last known contiguous block height. +// Base returns the first known contiguous block height, or 0 for empty block stores. +func (bs *BlockStore) Base() int64 { + bs.mtx.RLock() + defer bs.mtx.RUnlock() + return bs.base +} + +// Height returns the last known contiguous block height, or 0 for empty block stores. func (bs *BlockStore) Height() int64 { bs.mtx.RLock() defer bs.mtx.RUnlock() return bs.height } +// Size returns the number of blocks in the block store. +func (bs *BlockStore) Size() int64 { + bs.mtx.RLock() + defer bs.mtx.RUnlock() + if bs.height == 0 { + return 0 + } + return bs.height - bs.base + 1 +} + // LoadBlock returns the block with the given height. // If no block is found for that height, it returns nil. func (bs *BlockStore) LoadBlock(height int64) *types.Block { @@ -171,6 +193,74 @@ func (bs *BlockStore) LoadSeenCommit(height int64) *types.Commit { return commit } +// PruneBlocks removes block up to (but not including) a height. It returns number of blocks pruned. +func (bs *BlockStore) PruneBlocks(height int64) (uint64, error) { + if height <= 0 { + return 0, fmt.Errorf("height must be greater than 0") + } + bs.mtx.RLock() + if height > bs.height { + bs.mtx.RUnlock() + return 0, fmt.Errorf("cannot prune beyond the latest height %v", bs.height) + } + base := bs.base + bs.mtx.RUnlock() + if height < base { + return 0, fmt.Errorf("cannot prune to height %v, it is lower than base height %v", + height, base) + } + + pruned := uint64(0) + batch := bs.db.NewBatch() + defer batch.Close() + flush := func(batch db.Batch, base int64) error { + // We can't trust batches to be atomic, so update base first to make sure noone + // tries to access missing blocks. + bs.mtx.Lock() + bs.base = base + bs.mtx.Unlock() + bs.saveState() + + err := batch.WriteSync() + if err != nil { + return fmt.Errorf("failed to prune up to height %v: %w", base, err) + } + batch.Close() + return nil + } + + for h := base; h < height; h++ { + meta := bs.LoadBlockMeta(h) + if meta == nil { // assume already deleted + continue + } + batch.Delete(calcBlockMetaKey(h)) + batch.Delete(calcBlockHashKey(meta.BlockID.Hash)) + batch.Delete(calcBlockCommitKey(h)) + batch.Delete(calcSeenCommitKey(h)) + for p := 0; p < meta.BlockID.PartsHeader.Total; p++ { + batch.Delete(calcBlockPartKey(h, p)) + } + pruned++ + + // flush every 1000 blocks to avoid batches becoming too large + if pruned%1000 == 0 && pruned > 0 { + err := flush(batch, h) + if err != nil { + return 0, err + } + batch = bs.db.NewBatch() + defer batch.Close() + } + } + + err := flush(batch, height) + if err != nil { + return 0, err + } + return pruned, nil +} + // SaveBlock persists the given block, blockParts, and seenCommit to the underlying db. // blockParts: Must be parts of the block // seenCommit: The +2/3 precommits that were seen which committed at height. @@ -213,14 +303,17 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s seenCommitBytes := cdc.MustMarshalBinaryBare(seenCommit) bs.db.Set(calcSeenCommitKey(height), seenCommitBytes) - // Save new BlockStoreStateJSON descriptor - BlockStoreStateJSON{Height: height}.Save(bs.db) - // Done! bs.mtx.Lock() bs.height = height + if bs.base == 0 && height == 1 { + bs.base = 1 + } bs.mtx.Unlock() + // Save new BlockStoreStateJSON descriptor + bs.saveState() + // Flush bs.db.SetSync(nil, nil) } @@ -233,6 +326,16 @@ func (bs *BlockStore) saveBlockPart(height int64, index int, part *types.Part) { bs.db.Set(calcBlockPartKey(height, index), partBytes) } +func (bs *BlockStore) saveState() { + bs.mtx.RLock() + bsJSON := BlockStoreStateJSON{ + Base: bs.base, + Height: bs.height, + } + bs.mtx.RUnlock() + bsJSON.Save(bs.db) +} + //----------------------------------------------------------------------------- func calcBlockMetaKey(height int64) []byte { @@ -261,6 +364,7 @@ var blockStoreKey = []byte("blockStore") // BlockStoreStateJSON is the block store state JSON structure. type BlockStoreStateJSON struct { + Base int64 `json:"base"` Height int64 `json:"height"` } @@ -282,6 +386,7 @@ func LoadBlockStoreStateJSON(db dbm.DB) BlockStoreStateJSON { } if len(bytes) == 0 { return BlockStoreStateJSON{ + Base: 0, Height: 0, } } @@ -290,5 +395,9 @@ func LoadBlockStoreStateJSON(db dbm.DB) BlockStoreStateJSON { if err != nil { panic(fmt.Sprintf("Could not unmarshal bytes: %X", bytes)) } + // Backwards compatibility with persisted data from before Base existed. + if bsj.Height > 0 && bsj.Base == 0 { + bsj.Base = 1 + } return bsj } diff --git a/store/store_test.go b/store/store_test.go index 7fedf8606..16f52aa88 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -65,20 +65,39 @@ func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore, cleanupFu func TestLoadBlockStoreStateJSON(t *testing.T) { db := db.NewMemDB() + bsj := &BlockStoreStateJSON{Base: 100, Height: 1000} + bsj.Save(db) + + retrBSJ := LoadBlockStoreStateJSON(db) + assert.Equal(t, *bsj, retrBSJ, "expected the retrieved DBs to match") +} + +func TestLoadBlockStoreStateJSON_Empty(t *testing.T) { + db := db.NewMemDB() + + bsj := &BlockStoreStateJSON{} + bsj.Save(db) + + retrBSJ := LoadBlockStoreStateJSON(db) + assert.Equal(t, BlockStoreStateJSON{}, retrBSJ, "expected the retrieved DBs to match") +} + +func TestLoadBlockStoreStateJSON_NoBase(t *testing.T) { + db := db.NewMemDB() bsj := &BlockStoreStateJSON{Height: 1000} bsj.Save(db) retrBSJ := LoadBlockStoreStateJSON(db) - - assert.Equal(t, *bsj, retrBSJ, "expected the retrieved DBs to match") + assert.Equal(t, BlockStoreStateJSON{Base: 1, Height: 1000}, retrBSJ, "expected the retrieved DBs to match") } func TestNewBlockStore(t *testing.T) { db := db.NewMemDB() - err := db.Set(blockStoreKey, []byte(`{"height": "10000"}`)) + err := db.Set(blockStoreKey, []byte(`{"base": "100", "height": "10000"}`)) require.NoError(t, err) bs := NewBlockStore(db) + require.Equal(t, int64(100), bs.Base(), "failed to properly parse blockstore") require.Equal(t, int64(10000), bs.Height(), "failed to properly parse blockstore") panicCausers := []struct { @@ -140,6 +159,7 @@ func TestMain(m *testing.M) { func TestBlockStoreSaveLoadBlock(t *testing.T) { state, bs, cleanup := makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer))) defer cleanup() + require.Equal(t, bs.Base(), int64(0), "initially the base should be zero") require.Equal(t, bs.Height(), int64(0), "initially the height should be zero") // check there are no blocks at various heights @@ -155,7 +175,8 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) { validPartSet := block.MakePartSet(2) seenCommit := makeTestCommit(10, tmtime.Now()) bs.SaveBlock(block, partSet, seenCommit) - require.Equal(t, bs.Height(), block.Header.Height, "expecting the new height to be changed") + require.EqualValues(t, 1, bs.Base(), "expecting the new height to be changed") + require.EqualValues(t, block.Header.Height, bs.Height(), "expecting the new height to be changed") incompletePartSet := types.NewPartSetFromHeader(types.PartSetHeader{Total: 2}) uncontiguousPartSet := types.NewPartSetFromHeader(types.PartSetHeader{Total: 0}) @@ -364,6 +385,92 @@ func TestLoadBlockPart(t *testing.T) { "expecting successful retrieval of previously saved block") } +func TestPruneBlocks(t *testing.T) { + config := cfg.ResetTestRoot("blockchain_reactor_test") + defer os.RemoveAll(config.RootDir) + state, err := sm.LoadStateFromDBOrGenesisFile(dbm.NewMemDB(), config.GenesisFile()) + require.NoError(t, err) + db := dbm.NewMemDB() + bs := NewBlockStore(db) + assert.EqualValues(t, 0, bs.Base()) + assert.EqualValues(t, 0, bs.Height()) + assert.EqualValues(t, 0, bs.Size()) + + // pruning an empty store should error, even when pruning to 0 + _, err = bs.PruneBlocks(1) + require.Error(t, err) + + _, err = bs.PruneBlocks(0) + require.Error(t, err) + + // make more than 1000 blocks, to test batch deletions + for h := int64(1); h <= 1500; h++ { + block := makeBlock(h, state, new(types.Commit)) + partSet := block.MakePartSet(2) + seenCommit := makeTestCommit(h, tmtime.Now()) + bs.SaveBlock(block, partSet, seenCommit) + } + + assert.EqualValues(t, 1, bs.Base()) + assert.EqualValues(t, 1500, bs.Height()) + assert.EqualValues(t, 1500, bs.Size()) + + prunedBlock := bs.LoadBlock(1199) + + // Check that basic pruning works + pruned, err := bs.PruneBlocks(1200) + require.NoError(t, err) + assert.EqualValues(t, 1199, pruned) + assert.EqualValues(t, 1200, bs.Base()) + assert.EqualValues(t, 1500, bs.Height()) + assert.EqualValues(t, 301, bs.Size()) + assert.EqualValues(t, BlockStoreStateJSON{ + Base: 1200, + Height: 1500, + }, LoadBlockStoreStateJSON(db)) + + require.NotNil(t, bs.LoadBlock(1200)) + require.Nil(t, bs.LoadBlock(1199)) + require.Nil(t, bs.LoadBlockByHash(prunedBlock.Hash())) + require.Nil(t, bs.LoadBlockCommit(1199)) + require.Nil(t, bs.LoadBlockMeta(1199)) + require.Nil(t, bs.LoadBlockPart(1199, 1)) + + for i := int64(1); i < 1200; i++ { + require.Nil(t, bs.LoadBlock(i)) + } + for i := int64(1200); i <= 1500; i++ { + require.NotNil(t, bs.LoadBlock(i)) + } + + // Pruning below the current base should error + _, err = bs.PruneBlocks(1199) + require.Error(t, err) + + // Pruning to the current base should work + pruned, err = bs.PruneBlocks(1200) + require.NoError(t, err) + assert.EqualValues(t, 0, pruned) + + // Pruning again should work + pruned, err = bs.PruneBlocks(1300) + require.NoError(t, err) + assert.EqualValues(t, 100, pruned) + assert.EqualValues(t, 1300, bs.Base()) + + // Pruning beyond the current height should error + _, err = bs.PruneBlocks(1501) + require.Error(t, err) + + // Pruning to the current height should work + pruned, err = bs.PruneBlocks(1500) + require.NoError(t, err) + assert.EqualValues(t, 200, pruned) + assert.Nil(t, bs.LoadBlock(1499)) + assert.NotNil(t, bs.LoadBlock(1500)) + assert.Nil(t, bs.LoadBlock(1501)) +} + func TestLoadBlockMeta(t *testing.T) { bs, db := freshBlockStore() height := int64(10)