diff --git a/.circleci/config.yml b/.circleci/config.yml index e0d8129a9..95a4bd354 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 @@ -171,11 +171,15 @@ jobs: GOPATH: /home/circleci/.go_workspace machine: image: circleci/classic:latest + parameters: + ipv: + type: integer + default: 4 steps: - checkout - run: mkdir -p $GOPATH/src/github.com/tendermint - run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint - - run: bash test/p2p/circleci.sh + - run: bash test/p2p/circleci.sh << parameters.ipv >> - store_artifacts: path: /home/circleci/project/test/p2p/logs @@ -339,6 +343,7 @@ jobs: name: Build tendermint no_output_timeout: 20m command: | + sudo apt-get update sudo apt-get install -y ruby bash -x ./scripts/gitian-build.sh all for os in darwin linux windows; do @@ -373,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 @@ -402,7 +404,7 @@ workflows: filters: branches: only: - - docs-theme-latest + - docs-staging - setup_dependencies - test_abci_apps: requires: @@ -425,6 +427,9 @@ workflows: requires: - setup_dependencies - test_p2p + - test_p2p: + name: test_p2p_ipv6 + ipv: 6 - upload_coverage: requires: - test_cover diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md index e88fe61d4..62c3e4f3a 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.md +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -3,11 +3,34 @@ name: Feature Request about: Create a proposal to request a feature --- - -Word of caution: poorly thought out proposals may be rejected without deliberation ---> +## Summary + + + +## Problem Definition + + + +## Proposal + + + +____ + +#### For Admin Use + +- [ ] Not duplicate issue +- [ ] Appropriate labels applied +- [ ] Appropriate contributors tagged +- [ ] Contributor assigned/self-assigned diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index e1863c783..8590275a7 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,13 +1,24 @@ - -Thanks for filing a PR! Before hitting the button, please check the following items. -Please note that every non-trivial PR must reference an issue that explains the -changes in the PR. +Closes: #XXX +## Description + + -* [ ] Referenced an issue explaining the need for the change -* [ ] Updated all relevant documentation in docs -* [ ] Updated all code comments where relevant -* [ ] Wrote tests -* [ ] Updated CHANGELOG_PENDING.md + +______ + +For contributor use: + +- [ ] Wrote tests +- [ ] Updated CHANGELOG_PENDING.md +- [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. +- [ ] Updated relevant documentation (`docs/`) and code comments +- [ ] Re-reviewed `Files changed` in the Github PR explorer 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" diff --git a/.golangci.yml b/.golangci.yml index ce0925357..dc934f43d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -23,7 +23,7 @@ linters: - interfacer - lll - misspell - - maligned + # - maligned - nakedret - prealloc - scopelint diff --git a/.mergify.yml b/.mergify.yml new file mode 100644 index 000000000..136bb1148 --- /dev/null +++ b/.mergify.yml @@ -0,0 +1,9 @@ +pull_request_rules: + - name: automerge to master with label S:automerge and branch protection passing + conditions: + - base=master + - label=S:automerge + actions: + merge: + method: squash + strict: true 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 1c3b89147..c1128895b 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,11 +1,10 @@ -## v0.33.2 +## v0.33.3 \*\* 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,8 +20,7 @@ program](https://hackerone.com/tendermint). ### 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: - -- [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/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 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/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) diff --git a/abci/client/grpc_client.go b/abci/client/grpc_client.go index f853a4254..01583bc1f 100644 --- a/abci/client/grpc_client.go +++ b/abci/client/grpc_client.go @@ -31,7 +31,7 @@ type grpcClient struct { resCb func(*types.Request, *types.Response) // listens to all callbacks } -func NewGRPCClient(addr string, mustConnect bool) *grpcClient { +func NewGRPCClient(addr string, mustConnect bool) Client { cli := &grpcClient{ addr: addr, mustConnect: mustConnect, diff --git a/abci/client/local_client.go b/abci/client/local_client.go index f50d4eae1..3946bfbc5 100644 --- a/abci/client/local_client.go +++ b/abci/client/local_client.go @@ -21,7 +21,7 @@ type localClient struct { Callback } -func NewLocalClient(mtx *sync.Mutex, app types.Application) *localClient { +func NewLocalClient(mtx *sync.Mutex, app types.Application) Client { if mtx == nil { mtx = new(sync.Mutex) } diff --git a/abci/client/socket_client.go b/abci/client/socket_client.go index 2e765119b..7898a8f26 100644 --- a/abci/client/socket_client.go +++ b/abci/client/socket_client.go @@ -43,7 +43,7 @@ type socketClient struct { } -func NewSocketClient(addr string, mustConnect bool) *socketClient { +func NewSocketClient(addr string, mustConnect bool) Client { cli := &socketClient{ reqQueue: make(chan *ReqRes, reqQueueSize), flushTimer: timer.NewThrottleTimer("socketClient", flushThrottleMS), diff --git a/abci/client/socket_client_test.go b/abci/client/socket_client_test.go index c4877ce70..37bc2b57a 100644 --- a/abci/client/socket_client_test.go +++ b/abci/client/socket_client_test.go @@ -16,8 +16,12 @@ import ( "github.com/tendermint/tendermint/libs/service" ) +type errorStopper interface { + StopForError(error) +} + func TestSocketClientStopForErrorDeadlock(t *testing.T) { - c := abcicli.NewSocketClient(":80", false) + c := abcicli.NewSocketClient(":80", false).(errorStopper) err := errors.New("foo-tendermint") // See Issue https://github.com/tendermint/abci/issues/114 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_app/main.go b/abci/tests/test_app/main.go index ae47ac6f9..ca298d7e2 100644 --- a/abci/tests/test_app/main.go +++ b/abci/tests/test_app/main.go @@ -53,7 +53,8 @@ func testCounter() { } fmt.Printf("Running %s test with abci=%s\n", abciApp, abciType) - cmd := exec.Command("bash", "-c", fmt.Sprintf("abci-cli %s", abciApp)) + subCommand := fmt.Sprintf("abci-cli %s", abciApp) + cmd := exec.Command("bash", "-c", subCommand) cmd.Stdout = os.Stdout if err := cmd.Start(); err != nil { log.Fatalf("starting %q err: %v", abciApp, err) 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/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/blockchain/v0/reactor_test.go b/blockchain/v0/reactor_test.go index 08ff10a93..5a97d3aed 100644 --- a/blockchain/v0/reactor_test.go +++ b/blockchain/v0/reactor_test.go @@ -97,7 +97,9 @@ func newBlockchainReactor( lastBlockMeta.BlockID, state.Validators, privVals[0], - lastBlock.Header.ChainID) + lastBlock.Header.ChainID, + time.Now(), + ) if err != nil { panic(err) } 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/blockchain/v2/reactor_test.go b/blockchain/v2/reactor_test.go index 10457cdb0..ad091f582 100644 --- a/blockchain/v2/reactor_test.go +++ b/blockchain/v2/reactor_test.go @@ -166,179 +166,181 @@ func newTestReactor(p testReactorParams) *BlockchainReactor { return r } -func TestReactorTerminationScenarios(t *testing.T) { +// This test is left here and not deleted to retain the termination cases for +// future improvement in [#4482](https://github.com/tendermint/tendermint/issues/4482). +// func TestReactorTerminationScenarios(t *testing.T) { - config := cfg.ResetTestRoot("blockchain_reactor_v2_test") - defer os.RemoveAll(config.RootDir) - genDoc, privVals := randGenesisDoc(config.ChainID(), 1, false, 30) - refStore, _, _ := newReactorStore(genDoc, privVals, 20) +// config := cfg.ResetTestRoot("blockchain_reactor_v2_test") +// defer os.RemoveAll(config.RootDir) +// genDoc, privVals := randGenesisDoc(config.ChainID(), 1, false, 30) +// refStore, _, _ := newReactorStore(genDoc, privVals, 20) - params := testReactorParams{ - logger: log.TestingLogger(), - genDoc: genDoc, - privVals: privVals, - startHeight: 10, - bufferSize: 100, - mockA: true, - } +// params := testReactorParams{ +// logger: log.TestingLogger(), +// genDoc: genDoc, +// privVals: privVals, +// startHeight: 10, +// bufferSize: 100, +// mockA: true, +// } - type testEvent struct { - evType string - peer string - height int64 - } +// type testEvent struct { +// evType string +// peer string +// height int64 +// } - tests := []struct { - name string - params testReactorParams - msgs []testEvent - }{ - { - name: "simple termination on max peer height - one peer", - params: params, - msgs: []testEvent{ - {evType: "AddPeer", peer: "P1"}, - {evType: "ReceiveS", peer: "P1", height: 13}, - {evType: "BlockReq"}, - {evType: "ReceiveB", peer: "P1", height: 11}, - {evType: "BlockReq"}, - {evType: "BlockReq"}, - {evType: "ReceiveB", peer: "P1", height: 12}, - {evType: "Process"}, - {evType: "ReceiveB", peer: "P1", height: 13}, - {evType: "Process"}, - }, - }, - { - name: "simple termination on max peer height - two peers", - params: params, - msgs: []testEvent{ - {evType: "AddPeer", peer: "P1"}, - {evType: "AddPeer", peer: "P2"}, - {evType: "ReceiveS", peer: "P1", height: 13}, - {evType: "ReceiveS", peer: "P2", height: 15}, - {evType: "BlockReq"}, - {evType: "BlockReq"}, - {evType: "ReceiveB", peer: "P1", height: 11}, - {evType: "ReceiveB", peer: "P2", height: 12}, - {evType: "Process"}, - {evType: "BlockReq"}, - {evType: "BlockReq"}, - {evType: "ReceiveB", peer: "P1", height: 13}, - {evType: "Process"}, - {evType: "ReceiveB", peer: "P2", height: 14}, - {evType: "Process"}, - {evType: "BlockReq"}, - {evType: "ReceiveB", peer: "P2", height: 15}, - {evType: "Process"}, - }, - }, - { - name: "termination on max peer height - two peers, noBlock error", - params: params, - msgs: []testEvent{ - {evType: "AddPeer", peer: "P1"}, - {evType: "AddPeer", peer: "P2"}, - {evType: "ReceiveS", peer: "P1", height: 13}, - {evType: "ReceiveS", peer: "P2", height: 15}, - {evType: "BlockReq"}, - {evType: "BlockReq"}, - {evType: "ReceiveNB", peer: "P1", height: 11}, - {evType: "BlockReq"}, - {evType: "ReceiveB", peer: "P2", height: 12}, - {evType: "ReceiveB", peer: "P2", height: 11}, - {evType: "Process"}, - {evType: "BlockReq"}, - {evType: "BlockReq"}, - {evType: "ReceiveB", peer: "P2", height: 13}, - {evType: "Process"}, - {evType: "ReceiveB", peer: "P2", height: 14}, - {evType: "Process"}, - {evType: "BlockReq"}, - {evType: "ReceiveB", peer: "P2", height: 15}, - {evType: "Process"}, - }, - }, - { - name: "termination on max peer height - two peers, remove one peer", - params: params, - msgs: []testEvent{ - {evType: "AddPeer", peer: "P1"}, - {evType: "AddPeer", peer: "P2"}, - {evType: "ReceiveS", peer: "P1", height: 13}, - {evType: "ReceiveS", peer: "P2", height: 15}, - {evType: "BlockReq"}, - {evType: "BlockReq"}, - {evType: "RemovePeer", peer: "P1"}, - {evType: "BlockReq"}, - {evType: "ReceiveB", peer: "P2", height: 12}, - {evType: "ReceiveB", peer: "P2", height: 11}, - {evType: "Process"}, - {evType: "BlockReq"}, - {evType: "BlockReq"}, - {evType: "ReceiveB", peer: "P2", height: 13}, - {evType: "Process"}, - {evType: "ReceiveB", peer: "P2", height: 14}, - {evType: "Process"}, - {evType: "BlockReq"}, - {evType: "ReceiveB", peer: "P2", height: 15}, - {evType: "Process"}, - }, - }, - } +// tests := []struct { +// name string +// params testReactorParams +// msgs []testEvent +// }{ +// { +// name: "simple termination on max peer height - one peer", +// params: params, +// msgs: []testEvent{ +// {evType: "AddPeer", peer: "P1"}, +// {evType: "ReceiveS", peer: "P1", height: 13}, +// {evType: "BlockReq"}, +// {evType: "ReceiveB", peer: "P1", height: 11}, +// {evType: "BlockReq"}, +// {evType: "BlockReq"}, +// {evType: "ReceiveB", peer: "P1", height: 12}, +// {evType: "Process"}, +// {evType: "ReceiveB", peer: "P1", height: 13}, +// {evType: "Process"}, +// }, +// }, +// { +// name: "simple termination on max peer height - two peers", +// params: params, +// msgs: []testEvent{ +// {evType: "AddPeer", peer: "P1"}, +// {evType: "AddPeer", peer: "P2"}, +// {evType: "ReceiveS", peer: "P1", height: 13}, +// {evType: "ReceiveS", peer: "P2", height: 15}, +// {evType: "BlockReq"}, +// {evType: "BlockReq"}, +// {evType: "ReceiveB", peer: "P1", height: 11}, +// {evType: "ReceiveB", peer: "P2", height: 12}, +// {evType: "Process"}, +// {evType: "BlockReq"}, +// {evType: "BlockReq"}, +// {evType: "ReceiveB", peer: "P1", height: 13}, +// {evType: "Process"}, +// {evType: "ReceiveB", peer: "P2", height: 14}, +// {evType: "Process"}, +// {evType: "BlockReq"}, +// {evType: "ReceiveB", peer: "P2", height: 15}, +// {evType: "Process"}, +// }, +// }, +// { +// name: "termination on max peer height - two peers, noBlock error", +// params: params, +// msgs: []testEvent{ +// {evType: "AddPeer", peer: "P1"}, +// {evType: "AddPeer", peer: "P2"}, +// {evType: "ReceiveS", peer: "P1", height: 13}, +// {evType: "ReceiveS", peer: "P2", height: 15}, +// {evType: "BlockReq"}, +// {evType: "BlockReq"}, +// {evType: "ReceiveNB", peer: "P1", height: 11}, +// {evType: "BlockReq"}, +// {evType: "ReceiveB", peer: "P2", height: 12}, +// {evType: "ReceiveB", peer: "P2", height: 11}, +// {evType: "Process"}, +// {evType: "BlockReq"}, +// {evType: "BlockReq"}, +// {evType: "ReceiveB", peer: "P2", height: 13}, +// {evType: "Process"}, +// {evType: "ReceiveB", peer: "P2", height: 14}, +// {evType: "Process"}, +// {evType: "BlockReq"}, +// {evType: "ReceiveB", peer: "P2", height: 15}, +// {evType: "Process"}, +// }, +// }, +// { +// name: "termination on max peer height - two peers, remove one peer", +// params: params, +// msgs: []testEvent{ +// {evType: "AddPeer", peer: "P1"}, +// {evType: "AddPeer", peer: "P2"}, +// {evType: "ReceiveS", peer: "P1", height: 13}, +// {evType: "ReceiveS", peer: "P2", height: 15}, +// {evType: "BlockReq"}, +// {evType: "BlockReq"}, +// {evType: "RemovePeer", peer: "P1"}, +// {evType: "BlockReq"}, +// {evType: "ReceiveB", peer: "P2", height: 12}, +// {evType: "ReceiveB", peer: "P2", height: 11}, +// {evType: "Process"}, +// {evType: "BlockReq"}, +// {evType: "BlockReq"}, +// {evType: "ReceiveB", peer: "P2", height: 13}, +// {evType: "Process"}, +// {evType: "ReceiveB", peer: "P2", height: 14}, +// {evType: "Process"}, +// {evType: "BlockReq"}, +// {evType: "ReceiveB", peer: "P2", height: 15}, +// {evType: "Process"}, +// }, +// }, +// } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - reactor := newTestReactor(params) - reactor.Start() - reactor.reporter = behaviour.NewMockReporter() - mockSwitch := &mockSwitchIo{switchedToConsensus: false} - reactor.io = mockSwitch - // time for go routines to start - time.Sleep(time.Millisecond) +// for _, tt := range tests { +// tt := tt +// t.Run(tt.name, func(t *testing.T) { +// reactor := newTestReactor(params) +// reactor.Start() +// reactor.reporter = behaviour.NewMockReporter() +// mockSwitch := &mockSwitchIo{switchedToConsensus: false} +// reactor.io = mockSwitch +// // time for go routines to start +// time.Sleep(time.Millisecond) - for _, step := range tt.msgs { - switch step.evType { - case "AddPeer": - reactor.scheduler.send(bcAddNewPeer{peerID: p2p.ID(step.peer)}) - case "RemovePeer": - reactor.scheduler.send(bcRemovePeer{peerID: p2p.ID(step.peer)}) - case "ReceiveS": - reactor.scheduler.send(bcStatusResponse{ - peerID: p2p.ID(step.peer), - height: step.height, - time: time.Now(), - }) - case "ReceiveB": - reactor.scheduler.send(bcBlockResponse{ - peerID: p2p.ID(step.peer), - block: refStore.LoadBlock(step.height), - size: 10, - time: time.Now(), - }) - case "ReceiveNB": - reactor.scheduler.send(bcNoBlockResponse{ - peerID: p2p.ID(step.peer), - height: step.height, - time: time.Now(), - }) - case "BlockReq": - reactor.scheduler.send(rTrySchedule{time: time.Now()}) - case "Process": - reactor.processor.send(rProcessBlock{}) - } - // give time for messages to propagate between routines - time.Sleep(time.Millisecond) - } +// for _, step := range tt.msgs { +// switch step.evType { +// case "AddPeer": +// reactor.scheduler.send(bcAddNewPeer{peerID: p2p.ID(step.peer)}) +// case "RemovePeer": +// reactor.scheduler.send(bcRemovePeer{peerID: p2p.ID(step.peer)}) +// case "ReceiveS": +// reactor.scheduler.send(bcStatusResponse{ +// peerID: p2p.ID(step.peer), +// height: step.height, +// time: time.Now(), +// }) +// case "ReceiveB": +// reactor.scheduler.send(bcBlockResponse{ +// peerID: p2p.ID(step.peer), +// block: refStore.LoadBlock(step.height), +// size: 10, +// time: time.Now(), +// }) +// case "ReceiveNB": +// reactor.scheduler.send(bcNoBlockResponse{ +// peerID: p2p.ID(step.peer), +// height: step.height, +// time: time.Now(), +// }) +// case "BlockReq": +// reactor.scheduler.send(rTrySchedule{time: time.Now()}) +// case "Process": +// reactor.processor.send(rProcessBlock{}) +// } +// // give time for messages to propagate between routines +// time.Sleep(time.Millisecond) +// } - // time for processor to finish and reactor to switch to consensus - time.Sleep(20 * time.Millisecond) - assert.True(t, mockSwitch.hasSwitchedToConsensus()) - reactor.Stop() - }) - } -} +// // time for processor to finish and reactor to switch to consensus +// time.Sleep(20 * time.Millisecond) +// assert.True(t, mockSwitch.hasSwitchedToConsensus()) +// reactor.Stop() +// }) +// } +// } func TestReactorHelperMode(t *testing.T) { var ( @@ -493,7 +495,9 @@ func newReactorStore( lastBlockMeta.BlockID, state.Validators, privVals[0], - lastBlock.Header.ChainID) + lastBlock.Header.ChainID, + time.Now(), + ) if err != nil { panic(err) } 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/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 { 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/lite.go b/cmd/tendermint/commands/lite.go index 8477b46ed..dae72266d 100644 --- a/cmd/tendermint/commands/lite.go +++ b/cmd/tendermint/commands/lite.go @@ -2,19 +2,19 @@ package commands import ( "net/http" + "os" "strings" "time" "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" @@ -24,37 +24,52 @@ 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 ( listenAddr string primaryAddr string + witnessAddrsJoined string chainID string home string - witnessesAddrs string maxOpenConnections int 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(&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( @@ -64,65 +79,77 @@ 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 client...", "chainID", chainID) + + witnessesAddrs := strings.Split(witnessAddrsJoined, ",") + + db, err := dbm.NewGoLevelDB("lite-client-db", home) + if err != nil { + return errors.Wrap(err, "new goleveldb") + } + + var c *lite.Client + if trustedHeight > 0 && len(trustedHash) > 0 { // fresh installation + c, err = lite.NewHTTPClient( + chainID, + lite.TrustOptions{ + Period: trustingPeriod, + Height: trustedHeight, + Hash: trustedHash, + }, + primaryAddr, + witnessesAddrs, + dbs.New(db, chainID), + lite.Logger(logger), + ) + } else { // continue from latest state + c, err = lite.NewHTTPClientFromTrustedStore( + chainID, + trustingPeriod, + primaryAddr, + witnessesAddrs, + dbs.New(db, chainID), + lite.Logger(logger), + ) + } + if err != nil { + return err + } + + 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...") - 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...") - 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 - } - 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/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 {} }, 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/byzantine_test.go b/consensus/byzantine_test.go index 65bd88f67..bbf9cbf8f 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -54,7 +54,7 @@ func TestByzantine(t *testing.T) { if i == 0 { // NOTE: Now, test validators are MockPV, which by default doesn't // do any safety checks. - css[i].privValidator.(*types.MockPV).DisableChecks() + css[i].privValidator.(types.MockPV).DisableChecks() css[i].decideProposal = func(j int) func(int64, int) { return func(height int64, round int) { byzantineDecideProposalFunc(t, height, round, css[j], switches[j]) diff --git a/consensus/common_test.go b/consensus/common_test.go index c6cd18193..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" @@ -71,7 +73,7 @@ type validatorStub struct { var testMinPower int64 = 10 -func NewValidatorStub(privValidator types.PrivValidator, valIndex int) *validatorStub { +func newValidatorStub(privValidator types.PrivValidator, valIndex int) *validatorStub { return &validatorStub{ Index: valIndex, PrivValidator: privValidator, @@ -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") @@ -385,7 +407,7 @@ func randState(nValidators int) (*State, []*validatorStub) { cs := newState(state, privVals[0], counter.NewApplication(true)) for i := 0; i < nValidators; i++ { - vss[i] = NewValidatorStub(privVals[i], i) + vss[i] = newValidatorStub(privVals[i], i) } // since cs1 starts at 1 incrementHeight(vss[1:]...) 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 530948a18..24903927d 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -329,7 +329,7 @@ func TestSimulateValidatorsChange(t *testing.T) { vss := make([]*validatorStub, nPeers) for i := 0; i < nPeers; i++ { - vss[i] = NewValidatorStub(css[i].privValidator, i) + vss[i] = newValidatorStub(css[i].privValidator, i) } height, round := css[0].Height, css[0].Round // start the machine @@ -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 @@ -892,7 +912,8 @@ func makeBlock(state sm.State, lastBlock *types.Block, lastBlockMeta *types.Bloc lastBlockMeta.BlockID, state.Validators, privVal, - lastBlock.Header.ChainID) + lastBlock.Header.ChainID, + time.Now()) lastCommit = types.NewCommit(vote.Height, vote.Round, lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()}) } @@ -1094,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 d543c9c84..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)) + } } } } @@ -1644,11 +1673,16 @@ func (cs *State) tryAddVote(vote *types.Vote, peerID p2p.ID) (bool, error) { // If the vote height is off, we'll just ignore it, // But if it's a conflicting sig, add it to the cs.evpool. // If it's otherwise invalid, punish peer. + // nolint: gocritic 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", @@ -1837,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, @@ -1846,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 } @@ -1883,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/consensus/wal.go b/consensus/wal.go index 64f04fee0..989a5dc29 100644 --- a/consensus/wal.go +++ b/consensus/wal.go @@ -78,7 +78,7 @@ type WAL interface { // TODO: currently the wal is overwritten during replay catchup, give it a mode // so it's either reading or appending - must read to end to start appending // again. -type baseWAL struct { +type BaseWAL struct { service.BaseService group *auto.Group @@ -89,11 +89,11 @@ type baseWAL struct { flushInterval time.Duration } -var _ WAL = &baseWAL{} +var _ WAL = &BaseWAL{} // NewWAL returns a new write-ahead logger based on `baseWAL`, which implements // WAL. It's flushed and synced to disk every 2s and once when stopped. -func NewWAL(walFile string, groupOptions ...func(*auto.Group)) (*baseWAL, error) { +func NewWAL(walFile string, groupOptions ...func(*auto.Group)) (*BaseWAL, error) { err := tmos.EnsureDir(filepath.Dir(walFile), 0700) if err != nil { return nil, errors.Wrap(err, "failed to ensure WAL directory is in place") @@ -103,7 +103,7 @@ func NewWAL(walFile string, groupOptions ...func(*auto.Group)) (*baseWAL, error) if err != nil { return nil, err } - wal := &baseWAL{ + wal := &BaseWAL{ group: group, enc: NewWALEncoder(group), flushInterval: walDefaultFlushInterval, @@ -113,20 +113,20 @@ func NewWAL(walFile string, groupOptions ...func(*auto.Group)) (*baseWAL, error) } // SetFlushInterval allows us to override the periodic flush interval for the WAL. -func (wal *baseWAL) SetFlushInterval(i time.Duration) { +func (wal *BaseWAL) SetFlushInterval(i time.Duration) { wal.flushInterval = i } -func (wal *baseWAL) Group() *auto.Group { +func (wal *BaseWAL) Group() *auto.Group { return wal.group } -func (wal *baseWAL) SetLogger(l log.Logger) { +func (wal *BaseWAL) SetLogger(l log.Logger) { wal.BaseService.Logger = l wal.group.SetLogger(l) } -func (wal *baseWAL) OnStart() error { +func (wal *BaseWAL) OnStart() error { size, err := wal.group.Head.Size() if err != nil { return err @@ -142,7 +142,7 @@ func (wal *baseWAL) OnStart() error { return nil } -func (wal *baseWAL) processFlushTicks() { +func (wal *BaseWAL) processFlushTicks() { for { select { case <-wal.flushTicker.C: @@ -157,14 +157,14 @@ func (wal *baseWAL) processFlushTicks() { // FlushAndSync flushes and fsync's the underlying group's data to disk. // See auto#FlushAndSync -func (wal *baseWAL) FlushAndSync() error { +func (wal *BaseWAL) FlushAndSync() error { return wal.group.FlushAndSync() } // Stop the underlying autofile group. // Use Wait() to ensure it's finished shutting down // before cleaning up files. -func (wal *baseWAL) OnStop() { +func (wal *BaseWAL) OnStop() { wal.flushTicker.Stop() wal.FlushAndSync() wal.group.Stop() @@ -173,14 +173,14 @@ func (wal *baseWAL) OnStop() { // Wait for the underlying autofile group to finish shutting down // so it's safe to cleanup files. -func (wal *baseWAL) Wait() { +func (wal *BaseWAL) Wait() { wal.group.Wait() } // Write is called in newStep and for each receive on the // peerMsgQueue and the timeoutTicker. // NOTE: does not call fsync() -func (wal *baseWAL) Write(msg WALMessage) error { +func (wal *BaseWAL) Write(msg WALMessage) error { if wal == nil { return nil } @@ -197,7 +197,7 @@ func (wal *baseWAL) Write(msg WALMessage) error { // WriteSync is called when we receive a msg from ourselves // so that we write to disk before sending signed messages. // NOTE: calls fsync() -func (wal *baseWAL) WriteSync(msg WALMessage) error { +func (wal *BaseWAL) WriteSync(msg WALMessage) error { if wal == nil { return nil } @@ -227,7 +227,7 @@ type WALSearchOptions struct { // Group reader will be nil if found equals false. // // CONTRACT: caller must close group reader. -func (wal *baseWAL) SearchForEndHeight( +func (wal *BaseWAL) SearchForEndHeight( height int64, options *WALSearchOptions) (rd io.ReadCloser, found bool, err error) { var ( diff --git a/crypto/secp256k1/secp256k1.go b/crypto/secp256k1/secp256k1.go index fc64a0b0f..26dcead59 100644 --- a/crypto/secp256k1/secp256k1.go +++ b/crypto/secp256k1/secp256k1.go @@ -8,12 +8,10 @@ import ( "io" "math/big" - "golang.org/x/crypto/ripemd160" - secp256k1 "github.com/btcsuite/btcd/btcec" + "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/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/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 a72dc220f..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 @@ -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 ``` 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 9b3c2f661..9f754fd37 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) @@ -67,5 +67,8 @@ 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) +- [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-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-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-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/architecture/adr-046-light-client-implementation.md b/docs/architecture/adr-046-light-client-implementation.md index 11dc79fb9..37a7c83c5 100644 --- a/docs/architecture/adr-046-light-client-implementation.md +++ b/docs/architecture/adr-046-light-client-implementation.md @@ -2,32 +2,27 @@ ## Changelog * 13-02-2020: Initial draft +* 26-02-2020: Cross-checking the first header +* 28-02-2020: Bisection algorithm details ## 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 + // 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, now time.Time) (*types.SignedHeader, error) - TrustedValidatorSet(height int64, now time.Time) (*types.ValidatorSet, error) + TrustedHeader(height int64) (*types.SignedHeader, error) + TrustedValidatorSet(height int64) (valSet *types.ValidatorSet, heightUsed int64, err error) LastTrustedHeight() (int64, error) FirstTrustedHeight() (int64, error) @@ -36,9 +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 + Cleanup() error } ``` @@ -62,12 +55,17 @@ func NewClient( made to increase security by default. At least one witness is required, although, right now, the light client does not check that primary != witness. When cross-checking a new header with witnesses, minimum number of witnesses -required to respond: 1. +required to respond: 1. Note the very first header (`TrustOptions.Hash`) is +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 `TrustedHeader(X)` or -`TrustedValidatorSet(X)` methods are called, it will download the header from -primary provider and perform a backwards verification. +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`. + +`TrustedHeader`, `TrustedValidatorSet` only communicate with the trusted store. +If some header is not there, an error will be returned indicating that +verification is required. ```go type Provider interface { @@ -91,8 +89,8 @@ The light client stores headers & validators in the trusted store: ```go type Store interface { - SaveSignedHeaderAndNextValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error - DeleteSignedHeaderAndNextValidatorSet(height int64) error + SaveSignedHeaderAndValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error + DeleteSignedHeaderAndValidatorSet(height int64) error SignedHeader(height int64) (*types.SignedHeader, error) ValidatorSet(height int64) (*types.ValidatorSet, error) @@ -125,6 +123,21 @@ 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. +### Bisection algorithm details + +Non-recursive bisection algorithm was implemented despite the spec containing +the recursive version. There are two major reasons: + +1) Constant memory consumption => no risk of getting OOM (Out-Of-Memory) exceptions; +2) Faster finality (see Fig. 1). + +_Fig. 1: Differences between recursive and non-recursive bisections_ + +![Fig. 1](./img/adr-046-fig1.png) + +Specification of the non-recursive bisection can be found +[here](https://github.com/tendermint/spec/blob/zm_non-recursive-verification/spec/consensus/light-client/non-recursive-verification.md). + ## Status Accepted. 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. diff --git a/docs/architecture/adr-053-state-sync-prototype.md b/docs/architecture/adr-053-state-sync-prototype.md index 95615325d..79e39b24d 100644 --- a/docs/architecture/adr-053-state-sync-prototype.md +++ b/docs/architecture/adr-053-state-sync-prototype.md @@ -6,6 +6,14 @@ This ADR outlines the plan for an initial state sync prototype, and is subject t * 2020-01-28: Initial draft (Erik Grinaker) +* 2020-02-18: Updates after initial prototype (Erik Grinaker) + * ABCI: added missing `reason` fields. + * ABCI: used 32-bit 1-based chunk indexes (was 64-bit 0-based). + * ABCI: moved `RequestApplySnapshotChunk.chain_hash` to `RequestOfferSnapshot.app_hash`. + * Gaia: snapshots must include node versions as well, both for inner and leaf nodes. + * Added experimental prototype info. + * Added open questions and implementation plan. + ## 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. @@ -34,14 +42,14 @@ A node can have multiple snapshots taken at various heights. Snapshots can be ta message Snapshot { uint64 height = 1; // The height at which the snapshot was taken uint32 format = 2; // The application-specific snapshot format - uint64 chunks = 3; // The number of chunks in the snapshot + 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 - uint64 chunk = 3; // The chunk index (zero-based) + 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 } @@ -64,11 +72,13 @@ message ResponseListSnapshots { // Offers a snapshot to the application message RequestOfferSnapshot { Snapshot snapshot = 1; + bytes app_hash = 2; } message ResponseOfferSnapshot { bool accepted = 1; - enum reason { // Reason why snapshot was rejected + 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 @@ -79,7 +89,7 @@ message ResponseOfferSnapshot { message RequestGetSnapshotChunk { uint64 height = 1; uint32 format = 2; - uint64 chunk = 3; + uint32 chunk = 3; } message ResponseGetSnapshotChunk { @@ -89,12 +99,12 @@ message ResponseGetSnapshotChunk { // Applies a snapshot chunk message RequestApplySnapshotChunk { SnapshotChunk chunk = 1; - bytes chain_hash = 2; } message ResponseApplySnapshotChunk { bool applied = 1; - enum reason { // Reason why chunk failed + Reason reason = 2; // Reason why chunk failed + enum Reason { unknown = 0; // Unknown or generic reason verify_failed = 1; // Chunk verification failed } @@ -165,24 +175,13 @@ The Gaia data structure consists of a set of named IAVL trees. A root hash is co IAVL trees are versioned, but a snapshot only contains the version relevant for the snapshot height. All historical versions are ignored. -IAVL trees are insertion-order dependent, so key/value pairs must be stored in an appropriate insertion order to produce the same tree branching structure and thus the same Merkle hashes. +IAVL trees are insertion-order dependent, so key/value pairs must be set in an appropriate insertion order to produce the same tree branching structure. This insertion order can be found by doing a breadth-first scan of all nodes (including inner nodes) and collecting unique keys in order. However, the node hash also depends on the node's version, so snapshots must contain the inner nodes' version numbers as well. -A chunk corresponds to a subtree key range within an IAVL tree, in insertion order, along with the Merkle proof from the root of the subtree all the way up to the root of the multistore (including the Merkle proof for the surrounding multistore. +For the initial prototype, each chunk consists of a complete dump of all node data for all nodes in an entire IAVL tree. Thus the number of chunks equals the number of persistent stores in Gaia. No incremental verification of chunks is done, only a final app hash comparison at the end of the snapshot restoration. -```go -struct SnapshotChunk { - Store string // Name (key) of IAVL store in outer MultiStore - Keys [][]byte // Snapshotted keys in insertion order - Values [][]byte // Snapshotted values corresponding to Keys - Proof []merkle.ProofOp // Merkle proof from subtree root to MultiStore root -} -``` +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. -This chunk structure is believed to be sufficient to reconstruct an identical IAVL tree by applying separate chunks, but this may require the chunks to be ordered in a certain way; further research is needed. - -We do not use IAVL RangeProofs, 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 key/value pairs constituting a complete subtree up to some size limit (e.g. 32 MB), then serialized. 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. 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`. ### Snapshot Scheduling @@ -192,17 +191,127 @@ Taking consistent snapshots of IAVL trees is greatly simplified by them being ve Snapshots must also be garbage collected after some configurable time, e.g. by keeping the latest `n` snapshots. +## Experimental Prototype + +An experimental but functional state sync prototype is available in the `erik/statesync-prototype` branches of the Tendermint, IAVL, Cosmos SDK, and Gaia repositories. To fetch the necessary branches: + +```sh +$ mkdir statesync +$ cd statesync +$ git clone git@github.com:tendermint/tendermint -b erik/statesync-prototype +$ git clone git@github.com:tendermint/iavl -b erik/statesync-prototype +$ git clone git@github.com:cosmos/cosmos-sdk -b erik/statesync-prototype +$ git clone git@github.com:cosmos/gaia -b erik/statesync-prototype +``` + +To spin up three nodes of a four-node testnet: + +```sh +$ cd gaia +$ ./tools/start.sh +``` + +Wait for the first snapshot to be taken at height 3, then (in a separate terminal) start the fourth node with state sync enabled: + +```sh +$ ./tools/sync.sh +``` + +To stop the testnet, run: + +```sh +$ ./tools/stop.sh +``` + ## Open Questions -* Is it possible to reconstruct an identical IAVL tree given separate subtrees in an appropriate order, or is more data needed about the branch structure? +* Should we have a simpler scheme for discovering snapshots? E.g. announce supported formats, and have peer supply latest available snapshot. -* Should we punish nodes that provide invalid snapshots? + 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? + > Yes, this is as intended. Maybe backfill blocks later. + +* Do we need incremental chunk verification for first version? + + > No, we'll start simple. Can add chunk verification via a new snapshot format without any breaking changes in Tendermint. For adversarial conditions, maybe consider support for whitelisting peers to download chunks from. + +* Should the snapshot ABCI interface be a separate optional ABCI service, or mandatory? + + > Mandatory, to keep things simple for now. It will therefore be a breaking change and push the release. For apps using the Cosmos SDK, we can provide a default implementation that does not serve snapshots and errors when trying to apply them. + +* How can we make sure `ListSnapshots` data is valid? An adversary can provide fake/invalid snapshots to DoS peers. + + > For now, just pick snapshots that are available on a large number of peers. Maybe support whitelisting. We may consider e.g. placing snapshot manifests on the blockchain later. + +* Should we punish nodes that provide invalid snapshots? How? + + > No, these are full nodes not validators, so we can't punish them. Just disconnect from them and ignore them. + +* Should we call these snapshots? The SDK already uses the term "snapshot" for `PruningOptions.SnapshotEvery`, and state sync will introduce additional SDK options for snapshot scheduling and pruning that are not related to IAVL snapshotting or pruning. + + > Yes. Hopefully these concepts are distinct enough that we can refer to state sync snapshots and IAVL snapshots without too much confusion. + +* Should we store snapshot and chunk metadata in a database? Can we use the database for chunks? + + > As a first approach, store metadata in a database and chunks in the filesystem. + +* Should a snapshot at height H be taken before or after the block at H is processed? E.g. RPC `/commit` returns app_hash after _previous_ height, i.e. _before_ current height. + + > After commit. + +* Do we need to support all versions of blockchain reactor (i.e. fast sync)? + + > We should remove the v1 reactor completely once v2 has stabilized. + +* Should `ListSnapshots` be a streaming API instead of a request/response API? + + > No, just use a max message size. + +## Implementation Plan + +### Core Tasks + +* **Tendermint:** light client P2P transport [#4456](https://github.com/tendermint/tendermint/issues/4456) + +* **IAVL:** export/import API [#210](https://github.com/tendermint/iavl/issues/210) + +* **Cosmos SDK:** snapshotting, scheduling, and pruning [#5689](https://github.com/cosmos/cosmos-sdk/issues/5689) + +* **Tendermint:** support starting with a truncated block history + +* **Tendermint:** state sync reactor and ABCI interface [#828](https://github.com/tendermint/tendermint/issues/828) + +* **Cosmos SDK:** snapshot ABCI implementation [#5690](https://github.com/cosmos/cosmos-sdk/issues/5690) + +### Nice-to-Haves + +* **Tendermint:** staged reactor startup (state sync → fast sync → block replay → wal replay → consensus) + + > Let's do a time-boxed prototype (a few days) and see how much work it will be. + + * Notify P2P peers about channel changes [#4394](https://github.com/tendermint/tendermint/issues/4394) + + * Check peers have certain channels [#1148](https://github.com/tendermint/tendermint/issues/1148) + +* **Tendermint:** prune blockchain history [#3652](https://github.com/tendermint/tendermint/issues/3652) + +* **Tendermint:** allow genesis to start from non-zero height [#2543](https://github.com/tendermint/tendermint/issues/2543) + +### Follow-up Tasks + +* **Tendermint:** light client verification for fast sync [#4457](https://github.com/tendermint/tendermint/issues/4457) + +* **Tendermint:** allow start with only blockstore [#3713](https://github.com/tendermint/tendermint/issues/3713) + +* **Tendermint:** node should go back to fast-syncing when lagging significantly [#129](https://github.com/tendermint/tendermint/issues/129) + ## Status -Proposed +Accepted ## References 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/docs/architecture/img/adr-046-fig1.png b/docs/architecture/img/adr-046-fig1.png new file mode 100644 index 000000000..d68712e8a Binary files /dev/null and b/docs/architecture/img/adr-046-fig1.png differ 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 +} 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/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/docs/tendermint-core/running-in-production.md b/docs/tendermint-core/running-in-production.md index 7a436ec95..d386308de 100644 --- a/docs/tendermint-core/running-in-production.md +++ b/docs/tendermint-core/running-in-production.md @@ -106,59 +106,49 @@ 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 -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/tendermint-core/using-tendermint.md b/docs/tendermint-core/using-tendermint.md index 2c6370e84..b33e770d7 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 @@ -155,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/) ::: 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. diff --git a/go.mod b/go.mod index d2ea9fffb..1217c3beb 100644 --- a/go.mod +++ b/go.mod @@ -3,32 +3,39 @@ module github.com/tendermint/tendermint go 1.13 require ( - github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f - github.com/Workiva/go-datastructures v1.0.50 - github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d - github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a + github.com/ChainSafe/go-schnorrkel v0.0.0-20200115165343-aa45d48b5ed6 + github.com/Workiva/go-datastructures v1.0.52 + 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.3 + github.com/golang/protobuf v1.3.5 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/minio/highwayhash v1.0.0 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.4.1 - github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a + github.com/prometheus/client_golang v1.5.0 + 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.3 + github.com/spf13/cobra v0.0.6 github.com/spf13/viper v1.6.2 - github.com/stretchr/testify v1.5.0 + github.com/stretchr/testify v1.5.1 github.com/tendermint/go-amino v0.14.1 +<<<<<<< HEAD github.com/tendermint/tm-db v0.4.0 golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 // indirect google.golang.org/grpc v1.27.1 +======= + github.com/tendermint/tm-db v0.5.0 + 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 +>>>>>>> master ) diff --git a/go.sum b/go.sum index 039d28b6c..3456e2717 100644 --- a/go.sum +++ b/go.sum @@ -2,16 +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.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.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= @@ -34,16 +35,25 @@ 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= +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= @@ -54,6 +64,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= @@ -66,6 +77,7 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d h1:49RLWk1j44Xu4fjHb6JFYmeUnDORVwHNkDxaQ0ctCVU= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -79,7 +91,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= @@ -129,12 +143,16 @@ 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/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= 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= @@ -151,6 +169,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= @@ -163,6 +182,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= @@ -193,11 +214,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= @@ -209,6 +232,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= @@ -223,6 +247,7 @@ github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoR github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -238,6 +263,8 @@ github.com/minio/highwayhash v1.0.0 h1:iMSDhgUILCr0TNm8LWlSjF8N0ZIj2qbO8WHp6Q/J2 github.com/minio/highwayhash v1.0.0/go.mod h1:xQboMTeM9nY9v/LlAOxFctujiv5+Aq2hR5dxBpaMbdc= 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= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= @@ -299,8 +326,8 @@ 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= 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= @@ -332,6 +359,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= @@ -358,11 +387,14 @@ github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 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/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= github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.6.2 h1:7aKfF+e8/k68gda3LOjo5RxiUqddoFxVq4BKBPrxk5E= github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -376,18 +408,18 @@ 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.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= @@ -420,6 +452,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= @@ -449,6 +484,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= @@ -523,10 +560,11 @@ 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= diff --git a/libs/autofile/group.go b/libs/autofile/group.go index ca9f57003..5d5822ebc 100644 --- a/libs/autofile/group.go +++ b/libs/autofile/group.go @@ -77,7 +77,7 @@ type Group struct { // OpenGroup creates a new Group with head at headPath. It returns an error if // it fails to open head file. -func OpenGroup(headPath string, groupOptions ...func(*Group)) (g *Group, err error) { +func OpenGroup(headPath string, groupOptions ...func(*Group)) (*Group, error) { dir, err := filepath.Abs(filepath.Dir(headPath)) if err != nil { return nil, err @@ -87,7 +87,7 @@ func OpenGroup(headPath string, groupOptions ...func(*Group)) (g *Group, err err return nil, err } - g = &Group{ + g := &Group{ ID: "group:" + head.ID, Head: head, headBuf: bufio.NewWriterSize(head, 4096*10), @@ -109,7 +109,7 @@ func OpenGroup(headPath string, groupOptions ...func(*Group)) (g *Group, err err gInfo := g.readGroupInfo() g.minIndex = gInfo.MinIndex g.maxIndex = gInfo.MaxIndex - return + return g, nil } // GroupCheckDuration allows you to overwrite default groupCheckDuration. 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 b572ac205..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/common.KVPair. 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/common.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 diff --git a/libs/math/fraction.go b/libs/math/fraction.go index dca8a8e79..6cc2ad23e 100644 --- a/libs/math/fraction.go +++ b/libs/math/fraction.go @@ -6,10 +6,10 @@ import "fmt" // format. type Fraction struct { // The portion of the denominator in the faction, e.g. 2 in 2/3. - Numerator int64 + Numerator int64 `json:"numerator"` // The value by which the numerator is divided, e.g. 3 in 2/3. Must be // positive. - Denominator int64 + Denominator int64 `json:"denominator"` } func (fr Fraction) String() string { diff --git a/lite/helpers.go b/lite/helpers.go index 18a64aa25..29dd50b5b 100644 --- a/lite/helpers.go +++ b/lite/helpers.go @@ -42,7 +42,7 @@ func (pkz privKeys) Extend(n int) privKeys { } // GenSecpPrivKeys produces an array of secp256k1 private keys to generate commits. -func GenSecpPrivKeys(n int) privKeys { +func genSecpPrivKeys(n int) privKeys { res := make(privKeys, n) for i := range res { res[i] = secp256k1.GenPrivKey() @@ -52,7 +52,7 @@ func GenSecpPrivKeys(n int) privKeys { // ExtendSecp adds n more secp256k1 keys (to remove, just take a slice). func (pkz privKeys) ExtendSecp(n int) privKeys { - extra := GenSecpPrivKeys(n) + extra := genSecpPrivKeys(n) return append(pkz, extra...) } diff --git a/lite/multiprovider.go b/lite/multiprovider.go index b10c1c3cb..364647a40 100644 --- a/lite/multiprovider.go +++ b/lite/multiprovider.go @@ -16,7 +16,7 @@ type multiProvider struct { } // NewMultiProvider returns a new provider which wraps multiple other providers. -func NewMultiProvider(providers ...PersistentProvider) *multiProvider { +func NewMultiProvider(providers ...PersistentProvider) PersistentProvider { return &multiProvider{ logger: log.NewNopLogger(), providers: providers, diff --git a/lite2/client.go b/lite2/client.go index 2c487f9c9..22c080949 100644 --- a/lite2/client.go +++ b/lite2/client.go @@ -22,9 +22,8 @@ const ( sequential mode = iota + 1 skipping - defaultUpdatePeriod = 5 * time.Second - defaultRemoveNoLongerTrustedHeadersPeriod = 24 * time.Hour - defaultMaxRetryAttempts = 10 + defaultPruningSize = 1000 + defaultMaxRetryAttempts = 10 ) // Option sets a parameter for the light client. @@ -55,20 +54,14 @@ func SkippingVerification(trustLevel tmmath.Fraction) Option { } } -// UpdatePeriod option can be used to change default polling period (5s). -func UpdatePeriod(d time.Duration) Option { +// 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 +// will be removed from the store. Default: 1000. A pruning size of 0 will not +// prune the lite client at all. +func PruningSize(h uint16) Option { return func(c *Client) { - c.updatePeriod = d - } -} - -// RemoveNoLongerTrustedHeadersPeriod option can be used to define how often -// the routine, which cleans up no longer trusted headers (outside of trusting -// period), is run. Default: once a day. When set to zero, the routine won't be -// started. -func RemoveNoLongerTrustedHeadersPeriod(d time.Duration) Option { - return func(c *Client) { - c.removeNoLongerTrustedHeadersPeriod = d + c.pruningSize = h } } @@ -100,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 @@ -121,14 +111,12 @@ type Client struct { // Where trusted headers are stored. trustedStore store.Store // Highest trusted header from the store (height=H). - trustedHeader *types.SignedHeader - // Highest next validator set from the store (height=H+1). - trustedNextVals *types.ValidatorSet + latestTrustedHeader *types.SignedHeader + // Highest validator set from the store (height=H). + latestTrustedVals *types.ValidatorSet - // See UpdatePeriod option - updatePeriod time.Duration // See RemoveNoLongerTrustedHeadersPeriod option - removeNoLongerTrustedHeadersPeriod time.Duration + pruningSize uint16 // See ConfirmationFunction option confirmationFn func(action string) bool @@ -164,13 +152,15 @@ func NewClient( return nil, err } - if c.trustedHeader != nil { + if c.latestTrustedHeader != nil { + c.logger.Info("Checking trusted header using options") if err := c.checkTrustedHeaderUsingOptions(trustOptions); err != nil { return nil, err } } - if c.trustedHeader == nil || c.trustedHeader.Height < trustOptions.Height { + 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 } @@ -191,19 +181,18 @@ func NewClientFromTrustedStore( options ...Option) (*Client, error) { c := &Client{ - chainID: chainID, - trustingPeriod: trustingPeriod, - verificationMode: skipping, - trustLevel: DefaultTrustLevel, - maxRetryAttempts: defaultMaxRetryAttempts, - primary: primary, - witnesses: witnesses, - trustedStore: trustedStore, - updatePeriod: defaultUpdatePeriod, - removeNoLongerTrustedHeadersPeriod: defaultRemoveNoLongerTrustedHeadersPeriod, - confirmationFn: func(action string) bool { return true }, - quit: make(chan struct{}), - logger: log.NewNopLogger(), + chainID: chainID, + trustingPeriod: trustingPeriod, + verificationMode: skipping, + trustLevel: DefaultTrustLevel, + maxRetryAttempts: defaultMaxRetryAttempts, + primary: primary, + witnesses: witnesses, + trustedStore: trustedStore, + pruningSize: defaultPruningSize, + confirmationFn: func(action string) bool { return true }, + quit: make(chan struct{}), + logger: log.NewNopLogger(), } for _, o := range options { @@ -228,15 +217,16 @@ func NewClientFromTrustedStore( return nil, err } - if err := c.restoreTrustedHeaderAndNextVals(); err != nil { + if err := c.restoreTrustedHeaderAndVals(); err != nil { return nil, err } return c, nil } -// Load trustedHeader and trustedNextVals from trustedStore. -func (c *Client) restoreTrustedHeaderAndNextVals() error { +// restoreTrustedHeaderAndVals loads trustedHeader and trustedVals from +// trustedStore. +func (c *Client) restoreTrustedHeaderAndVals() error { lastHeight, err := c.trustedStore.LastSignedHeaderHeight() if err != nil { return errors.Wrap(err, "can't get last trusted header height") @@ -248,15 +238,15 @@ func (c *Client) restoreTrustedHeaderAndNextVals() error { return errors.Wrap(err, "can't get last trusted header") } - trustedNextVals, err := c.trustedStore.ValidatorSet(lastHeight + 1) + trustedVals, err := c.trustedStore.ValidatorSet(lastHeight) if err != nil { - return errors.Wrap(err, "can't get last trusted next validators") + return errors.Wrap(err, "can't get last trusted validators") } - c.trustedHeader = trustedHeader - c.trustedNextVals = trustedNextVals + c.latestTrustedHeader = trustedHeader + c.latestTrustedVals = trustedVals - c.logger.Debug("Restored trusted header and next vals", lastHeight) + c.logger.Info("Restored trusted header and vals", "height", lastHeight) } return nil @@ -283,27 +273,30 @@ func (c *Client) restoreTrustedHeaderAndNextVals() error { func (c *Client) checkTrustedHeaderUsingOptions(options TrustOptions) error { var primaryHash []byte switch { - case options.Height > c.trustedHeader.Height: - h, err := c.signedHeaderFromPrimary(c.trustedHeader.Height) + case options.Height > c.latestTrustedHeader.Height: + h, err := c.signedHeaderFromPrimary(c.latestTrustedHeader.Height) if err != nil { return err } primaryHash = h.Hash() - case options.Height == c.trustedHeader.Height: + case options.Height == c.latestTrustedHeader.Height: primaryHash = options.Hash - case options.Height < c.trustedHeader.Height: + case options.Height < c.latestTrustedHeader.Height: c.logger.Info("Client initialized with old header (trusted is more recent)", "old", options.Height, - "trustedHeight", c.trustedHeader.Height, - "trustedHash", hash2str(c.trustedHeader.Hash())) + "trustedHeight", c.latestTrustedHeader.Height, + "trustedHash", hash2str(c.latestTrustedHeader.Hash())) action := fmt.Sprintf( "Rollback to %d (%X)? Note this will remove newer headers up to %d (%X)", options.Height, options.Hash, - c.trustedHeader.Height, c.trustedHeader.Hash()) + c.latestTrustedHeader.Height, c.latestTrustedHeader.Hash()) if c.confirmationFn(action) { // remove all the headers (options.Height, trustedHeader.Height] - c.cleanup(options.Height + 1) + err := c.cleanupAfter(options.Height) + if err != nil { + return errors.Wrapf(err, "cleanupAfter(%d)", options.Height) + } c.logger.Info("Rolled back to older header (newer headers were removed)", "old", options.Height) @@ -314,13 +307,13 @@ func (c *Client) checkTrustedHeaderUsingOptions(options TrustOptions) error { primaryHash = options.Hash } - if !bytes.Equal(primaryHash, c.trustedHeader.Hash()) { + if !bytes.Equal(primaryHash, c.latestTrustedHeader.Hash()) { c.logger.Info("Prev. trusted header's hash (h1) doesn't match hash from primary provider (h2)", - "h1", c.trustedHeader.Hash(), "h1", primaryHash) + "h1", hash2str(c.latestTrustedHeader.Hash()), "h2", hash2str(primaryHash)) action := fmt.Sprintf( "Prev. trusted header's hash %X doesn't match hash %X from primary provider. Remove all the stored headers?", - c.trustedHeader.Hash(), primaryHash) + c.latestTrustedHeader.Hash(), primaryHash) if c.confirmationFn(action) { err := c.Cleanup() if err != nil { @@ -334,7 +327,9 @@ func (c *Client) checkTrustedHeaderUsingOptions(options TrustOptions) error { return nil } -// Fetch trustedHeader and trustedNextVals from primary provider. +// initializeWithTrustOptions fetches the weakly-trusted header and vals from +// primary provider. The header is cross-checked with witnesses for additional +// security. func (c *Client) initializeWithTrustOptions(options TrustOptions) error { // 1) Fetch and verify the header. h, err := c.signedHeaderFromPrimary(options.Height) @@ -353,6 +348,11 @@ func (c *Client) initializeWithTrustOptions(options TrustOptions) error { return errors.Errorf("expected header's hash %X, but got %X", options.Hash, h.Hash()) } + err = c.compareNewHeaderWithWitnesses(h) + if err != nil { + return err + } + // 2) Fetch and verify the vals. vals, err := c.validatorSetFromPrimary(options.Height) if err != nil { @@ -372,46 +372,11 @@ func (c *Client) initializeWithTrustOptions(options TrustOptions) error { return errors.Wrap(err, "invalid commit") } - // 3) Fetch and verify the next vals (verification happens in - // updateTrustedHeaderAndNextVals). - nextVals, err := c.validatorSetFromPrimary(options.Height + 1) - if err != nil { - return err - } - - // 4) Persist both of them and continue. - return c.updateTrustedHeaderAndNextVals(h, nextVals) -} - -// Start starts two processes: 1) auto updating 2) removing outdated headers. -func (c *Client) Start() error { - c.logger.Info("Starting light client") - - if c.removeNoLongerTrustedHeadersPeriod > 0 { - c.routinesWaitGroup.Add(1) - go c.removeNoLongerTrustedHeadersRoutine() - } - - 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() + // 3) Persist both of them and continue. + return c.updateTrustedHeaderAndVals(h, vals) } // TrustedHeader returns a trusted header at the given height (0 - the latest). -// If a header is missing in trustedStore (e.g. it was skipped during -// bisection), it will be downloaded from primary. // // Headers along with validator sets, which can't be trusted anymore, are // removed once a day (can be changed with RemoveNoLongerTrustedHeadersPeriod @@ -420,84 +385,68 @@ func (c *Client) Stop() { // height must be >= 0. // // It returns an error if: -// - header expired, therefore can't be trusted (ErrOldHeaderExpired); // - there are some issues with the trusted store, although that should not // happen normally; // - negative height is passed; -// - header has not been verified yet +// - header has not been verified yet and is therefore not in the store // // Safe for concurrent use by multiple goroutines. -func (c *Client) TrustedHeader(height int64, now time.Time) (*types.SignedHeader, error) { - if height < 0 { - return nil, errors.New("negative height") - } - - // 1) Get latest height. - latestHeight, err := c.LastTrustedHeight() +func (c *Client) TrustedHeader(height int64) (*types.SignedHeader, error) { + height, err := c.compareWithLatestHeight(height) if err != nil { return nil, err } - if latestHeight == -1 { - return nil, errors.New("no headers exist") - } - if height > latestHeight { - return nil, errors.Errorf("unverified header requested (latest: %d)", latestHeight) - } - if height == 0 { - height = latestHeight - } - - // 2) Get header from store. - h, err := c.trustedStore.SignedHeader(height) - switch { - case errors.Is(err, store.ErrSignedHeaderNotFound): - // 2.1) If not found, try to fetch header from primary. - h, err = c.fetchMissingTrustedHeader(height, now) - if err != nil { - return nil, err - } - case err != nil: - return nil, err - } - - // 3) Ensure header can still be trusted. - if HeaderExpired(h, c.trustingPeriod, now) { - return nil, ErrOldHeaderExpired{h.Time.Add(c.trustingPeriod), now} - } - - return h, nil + return c.trustedStore.SignedHeader(height) } -// TrustedValidatorSet returns a trusted validator set at the given height. If -// a validator set is missing in trustedStore (e.g. the associated header was -// skipped during bisection), it will be downloaded from primary. The second -// return parameter is height validator set corresponds to (useful when you -// pass 0). +// TrustedValidatorSet returns a trusted validator set at the given height (0 - +// latest). The second return parameter is the height used (useful if 0 was +// passed; otherwise can be ignored). // // height must be >= 0. // -// Headers along with validator sets, which can't be trusted anymore, are +// Headers along with validator sets are // removed once a day (can be changed with RemoveNoLongerTrustedHeadersPeriod // option). // -// It returns an error if: -// - header signed by that validator set expired (ErrOldHeaderExpired) +// Function returns an error if: // - there are some issues with the trusted store, although that should not // happen normally; // - negative height is passed; // - header signed by that validator set has not been verified yet // // Safe for concurrent use by multiple goroutines. -func (c *Client) TrustedValidatorSet(height int64, now time.Time) (*types.ValidatorSet, error) { - // Checks height is positive and header (note: height - 1) is not expired. - // Additionally, it fetches validator set from primary if it's missing in - // store. - _, err := c.TrustedHeader(height-1, now) +func (c *Client) TrustedValidatorSet(height int64) (valSet *types.ValidatorSet, heightUsed int64, err error) { + heightUsed, err = c.compareWithLatestHeight(height) if err != nil { - return nil, err + return nil, heightUsed, err + } + valSet, err = c.trustedStore.ValidatorSet(heightUsed) + if err != nil { + return nil, heightUsed, err + } + return valSet, heightUsed, err +} + +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") + } + if latestHeight == -1 { + return 0, errors.New("no headers exist") } - return c.trustedStore.ValidatorSet(height) + switch { + case height > latestHeight: + return 0, errors.Errorf("unverified header/valset requested (latest: %d)", latestHeight) + case height == 0: + return latestHeight, nil + case height < 0: + return 0, errors.New("negative height") + } + + return height, nil } // LastTrustedHeight returns a last trusted height. -1 and nil are returned if @@ -523,15 +472,25 @@ func (c *Client) ChainID() string { return c.chainID } -// VerifyHeaderAtHeight fetches the header and validators at the given height -// and calls VerifyHeader. +// VerifyHeaderAtHeight fetches header and validators at the given height +// and calls VerifyHeader. It returns header immediately if such exists in +// trustedStore (no verification is needed). // -// If the trusted header is more recent than one here, an error is returned. -// If the header is not found by the primary provider, -// provider.ErrSignedHeaderNotFound error is returned. +// height must be > 0. +// +// It returns provider.ErrSignedHeaderNotFound if header is not found by +// primary. func (c *Client) VerifyHeaderAtHeight(height int64, now time.Time) (*types.SignedHeader, error) { - if c.trustedHeader.Height >= height { - return nil, errors.Errorf("header at more recent height #%d exists", c.trustedHeader.Height) + if height <= 0 { + return nil, errors.New("negative or zero height") + } + + // Check if header already verified. + h, err := c.TrustedHeader(height) + if err == nil { + c.logger.Info("Header has already been verified", "height", height, "hash", hash2str(h.Hash())) + // Return already trusted header + return h, nil } // Request the header and the vals. @@ -540,10 +499,12 @@ func (c *Client) VerifyHeaderAtHeight(height int64, now time.Time) (*types.Signe return nil, err } - return newHeader, c.VerifyHeader(newHeader, newVals, now) + return newHeader, c.verifyHeader(newHeader, newVals, now) } -// VerifyHeader verifies new header against the trusted state. +// VerifyHeader verifies new header against the trusted state. It returns +// immediately if newHeader exists in trustedStore (no verification is +// needed). // // SequentialVerification: verifies that 2/3 of the trusted validator set has // signed the new header. If the headers are not adjacent, **all** intermediate @@ -555,31 +516,60 @@ 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 // -// If the trusted header is more recent than one here, an error is returned. +// 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 / // provider.ErrValidatorSetNotFound error is returned. -// -// NOTE: although newVals is entered as input, trustedStore will only store the -// validator set at height newHeader.Height+1 (i.e. -// newHeader.NextValidatorsHash). func (c *Client) VerifyHeader(newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error { + if newHeader.Height <= 0 { + return errors.New("negative or zero height") + } + + // Check if newHeader already verified. + h, err := c.TrustedHeader(newHeader.Height) + 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()) + } + c.logger.Info("Header has already been verified", + "height", newHeader.Height, "hash", hash2str(newHeader.Hash())) + return nil + } + + return c.verifyHeader(newHeader, newVals, now) +} + +func (c *Client) verifyHeader(newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error { c.logger.Info("VerifyHeader", "height", newHeader.Height, "hash", hash2str(newHeader.Hash()), "vals", hash2str(newVals.Hash())) - if c.trustedHeader.Height >= newHeader.Height { - return errors.Errorf("header at more recent height #%d exists", c.trustedHeader.Height) - } - var err error - switch c.verificationMode { - case sequential: - err = c.sequence(c.trustedHeader, c.trustedNextVals, newHeader, newVals, now) - case skipping: - err = c.bisection(c.trustedHeader, c.trustedNextVals, newHeader, newVals, now) - default: - panic(fmt.Sprintf("Unknown verification mode: %b", c.verificationMode)) + + // 1) If going forward, perform either bisection or sequential verification + if newHeader.Height >= c.latestTrustedHeader.Height { + switch c.verificationMode { + case sequential: + err = c.sequence(c.latestTrustedHeader, newHeader, newVals, now) + case skipping: + err = c.bisection(c.latestTrustedHeader, c.latestTrustedVals, newHeader, newVals, now) + default: + 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) + if err != nil { + return errors.Wrapf(err, "can't get signed header after height %d", newHeader.Height) + } + + err = c.backwards(closestHeader, newHeader, now) } if err != nil { c.logger.Error("Can't verify", "err", err) @@ -591,12 +581,7 @@ func (c *Client) VerifyHeader(newHeader *types.SignedHeader, newVals *types.Vali return err } - // Update trusted header and vals. - nextVals, err := c.validatorSetFromPrimary(newHeader.Height + 1) - if err != nil { - return err - } - return c.updateTrustedHeaderAndNextVals(newHeader, nextVals) + return c.updateTrustedHeaderAndVals(newHeader, newVals) } // Primary returns the primary provider. @@ -621,40 +606,36 @@ func (c *Client) Witnesses() []provider.Provider { // client must be stopped at this point. func (c *Client) Cleanup() error { c.logger.Info("Removing all the data") - return c.cleanup(0) + c.latestTrustedHeader = nil + c.latestTrustedVals = nil + return c.trustedStore.Prune(0) } -// cleanup deletes all headers & validator sets between +stopHeight+ and latest -// height included. It also sets trustedHeader (vals) to the latest header -// (vals) if such exists. -func (c *Client) cleanup(stopHeight int64) error { - // 1) Get the oldest height. - oldestHeight, err := c.trustedStore.FirstSignedHeaderHeight() - if err != nil { - return errors.Wrap(err, "can't get first trusted height") - } +// 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 - // 2) Get the latest height. - latestHeight, err := c.trustedStore.LastSignedHeaderHeight() - if err != nil { - return errors.Wrap(err, "can't get last trusted height") - } - - // 3) Remove all headers and validator sets. - if stopHeight < oldestHeight { - stopHeight = oldestHeight - } - for height := stopHeight; height <= latestHeight; height++ { - err = c.trustedStore.DeleteSignedHeaderAndNextValidatorSet(height) - if err != nil { - c.logger.Error("can't remove a trusted header & validator set", "err", err, "height", height) - continue + 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.trustedHeader = nil - c.trustedNextVals = nil - err = c.restoreTrustedHeaderAndNextVals() + c.latestTrustedHeader = nil + c.latestTrustedVals = nil + err := c.restoreTrustedHeaderAndVals() if err != nil { return err } @@ -664,93 +645,100 @@ func (c *Client) cleanup(stopHeight int64) error { // see VerifyHeader func (c *Client) sequence( - trustedHeader *types.SignedHeader, - trustedNextVals *types.ValidatorSet, + initiallyTrustedHeader *types.SignedHeader, newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error { - // 1) Verify any intermediate headers. + var ( - interimHeader *types.SignedHeader - interimNextVals *types.ValidatorSet - err error + trustedHeader = initiallyTrustedHeader + + interimHeader *types.SignedHeader + interimVals *types.ValidatorSet + + err error ) - for height := trustedHeader.Height + 1; height < newHeader.Height; height++ { - interimHeader, err = c.signedHeaderFromPrimary(height) - if err != nil { - return errors.Wrapf(err, "failed to obtain the header #%d", height) - } - c.logger.Debug("Verify newHeader against trustedHeader", - "trustedHeight", c.trustedHeader.Height, - "trustedHash", hash2str(c.trustedHeader.Hash()), - "newHeight", interimHeader.Height, - "newHash", hash2str(interimHeader.Hash())) - err = VerifyAdjacent(c.chainID, trustedHeader, interimHeader, trustedNextVals, - c.trustingPeriod, now) - if err != nil { - return errors.Wrapf(err, "failed to verify the header #%d", height) - } - - // Update trusted header and vals. - if height == newHeader.Height-1 { - interimNextVals = newVals - } else { - interimNextVals, err = c.validatorSetFromPrimary(height + 1) + for height := initiallyTrustedHeader.Height + 1; height <= newHeader.Height; height++ { + // 1) Fetch interim headers and vals if needed. + if height == newHeader.Height { // last header + interimHeader, interimVals = newHeader, newVals + } else { // intermediate headers + interimHeader, interimVals, err = c.fetchHeaderAndValsAtHeight(height) if err != nil { - return errors.Wrapf(err, "failed to obtain the vals #%d", height+1) + return errors.Wrapf(err, "failed to obtain the header #%d", height) } } - err = c.updateTrustedHeaderAndNextVals(interimHeader, interimNextVals) + + // 2) Verify them + c.logger.Debug("Verify newHeader against trustedHeader", + "trustedHeight", trustedHeader.Height, + "trustedHash", hash2str(trustedHeader.Hash()), + "newHeight", interimHeader.Height, + "newHash", hash2str(interimHeader.Hash())) + + err = VerifyAdjacent(c.chainID, trustedHeader, interimHeader, interimVals, + c.trustingPeriod, now) if err != nil { - return errors.Wrapf(err, "failed to update trusted state #%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 + } } - trustedHeader, trustedNextVals = interimHeader, interimNextVals + + // 3) Update trustedHeader + trustedHeader = interimHeader } - // 2) Verify the new header. - return VerifyAdjacent(c.chainID, c.trustedHeader, newHeader, newVals, c.trustingPeriod, now) + return nil } // see VerifyHeader func (c *Client) bisection( - trustedHeader *types.SignedHeader, // height h - trustedNextVals *types.ValidatorSet, // height h + 1 - newHeader *types.SignedHeader, // height g - newVals *types.ValidatorSet, // height g + initiallyTrustedHeader *types.SignedHeader, + initiallyTrustedVals *types.ValidatorSet, + newHeader *types.SignedHeader, + newVals *types.ValidatorSet, now time.Time) error { - interimVals := newVals - interimHeader := newHeader + var ( + trustedHeader = initiallyTrustedHeader + trustedVals = initiallyTrustedVals - for trustedHeader.Height < newHeader.Height { + interimHeader = newHeader + interimVals = newVals + ) + + for { c.logger.Debug("Verify newHeader against trustedHeader", "trustedHeight", trustedHeader.Height, "trustedHash", hash2str(trustedHeader.Hash()), - "newHeight", newHeader.Height, - "newHash", hash2str(newHeader.Hash())) - err := Verify(c.chainID, trustedHeader, trustedNextVals, interimHeader, interimVals, c.trustingPeriod, now, + "newHeight", interimHeader.Height, + "newHash", hash2str(interimHeader.Hash())) + + err := Verify(c.chainID, trustedHeader, trustedVals, interimHeader, interimVals, c.trustingPeriod, now, c.trustLevel) switch err.(type) { case nil: + if interimHeader.Height == newHeader.Height { + return nil + } + // Update the lower bound to the previous upper bound - trustedHeader = interimHeader - trustedNextVals, err = c.validatorSetFromPrimary(interimHeader.Height + 1) - if err != nil { - return err - } - if !bytes.Equal(trustedHeader.NextValidatorsHash, trustedNextVals.Hash()) { - return errors.Errorf("expected next validator's hash %X, but got %X (height #%d)", - trustedHeader.NextValidatorsHash, - trustedNextVals.Hash(), - trustedHeader.Height) - } - - err = c.updateTrustedHeaderAndNextVals(trustedHeader, trustedNextVals) - if err != nil { - return err - } - + trustedHeader, trustedVals = interimHeader, interimVals // Update the upper bound to the untrustedHeader interimHeader, interimVals = newHeader, newVals @@ -761,26 +749,44 @@ 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) } } - - return nil } -// persist header and next validators to trustedStore. -func (c *Client) updateTrustedHeaderAndNextVals(h *types.SignedHeader, nextVals *types.ValidatorSet) error { - if !bytes.Equal(h.NextValidatorsHash, nextVals.Hash()) { - return errors.Errorf("expected next validator's hash %X, but got %X", h.NextValidatorsHash, nextVals.Hash()) +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()) } - if err := c.trustedStore.SaveSignedHeaderAndNextValidatorSet(h, nextVals); err != nil { + if err := c.trustedStore.SaveSignedHeaderAndValidatorSet(h, vals); err != nil { return errors.Wrap(err, "failed to save trusted header") } - c.trustedHeader = h - c.trustedNextVals = nextVals + if c.pruningSize > 0 { + if err := c.trustedStore.Prune(c.pruningSize); err != nil { + return errors.Wrap(err, "prune") + } + } + + if c.latestTrustedHeader == nil || h.Height > c.latestTrustedHeader.Height { + c.latestTrustedHeader = h + c.latestTrustedVals = vals + } return nil } @@ -799,77 +805,49 @@ func (c *Client) fetchHeaderAndValsAtHeight(height int64) (*types.SignedHeader, return h, vals, nil } -// fetchMissingTrustedHeader finds the closest height after the -// requested height and does backwards verification. -func (c *Client) fetchMissingTrustedHeader(height int64, now time.Time) (*types.SignedHeader, error) { - c.logger.Info("Fetching missing header", "height", height) +// 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, + now time.Time) error { - closestHeader, err := c.trustedStore.SignedHeaderAfter(height) - if err != nil { - return nil, errors.Wrapf(err, "can't get signed header after %d", height) + if HeaderExpired(initiallyTrustedHeader, c.trustingPeriod, now) { + return ErrOldHeaderExpired{initiallyTrustedHeader.Time.Add(c.trustingPeriod), now} } - // Perform backwards verification from closestHeader to header at the given - // height. - h, err := c.backwards(height, closestHeader, now) - if err != nil { - return nil, err - } - - // Fetch next validator set from primary and persist it. - nextVals, err := c.validatorSetFromPrimary(height + 1) - if err != nil { - return nil, errors.Wrapf(err, "failed to obtain the vals #%d", height) - } - if !bytes.Equal(h.NextValidatorsHash, nextVals.Hash()) { - return nil, errors.Errorf("expected next validator's hash %X, but got %X", - h.NextValidatorsHash, nextVals.Hash()) - } - if err := c.trustedStore.SaveSignedHeaderAndNextValidatorSet(h, nextVals); err != nil { - return nil, errors.Wrap(err, "failed to save trusted header") - } - - return h, nil -} - -// Backwards verification (see VerifyHeaderBackwards func in the spec) -func (c *Client) backwards(toHeight int64, fromHeader *types.SignedHeader, now time.Time) (*types.SignedHeader, error) { var ( - trustedHeader = fromHeader - untrustedHeader *types.SignedHeader - err error + trustedHeader = initiallyTrustedHeader + interimHeader *types.SignedHeader + err error ) - for i := trustedHeader.Height - 1; i >= toHeight; i-- { - untrustedHeader, err = c.signedHeaderFromPrimary(i) + for trustedHeader.Height > newHeader.Height { + interimHeader, err = c.signedHeaderFromPrimary(trustedHeader.Height - 1) if err != nil { - return nil, errors.Wrapf(err, "failed to obtain the header #%d", i) + return errors.Wrapf(err, "failed to obtain the header at height #%d", trustedHeader.Height-1) } - if err := untrustedHeader.ValidateBasic(c.chainID); err != nil { - return nil, errors.Wrap(err, "untrustedHeader.ValidateBasic failed") + 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) + } } - if !untrustedHeader.Time.Before(trustedHeader.Time) { - return nil, errors.Errorf("expected older header time %v to be before newer header time %v", - untrustedHeader.Time, - trustedHeader.Time) - } - - if HeaderExpired(untrustedHeader, c.trustingPeriod, now) { - return nil, ErrOldHeaderExpired{untrustedHeader.Time.Add(c.trustingPeriod), now} - } - - if !bytes.Equal(untrustedHeader.Hash(), trustedHeader.LastBlockID.Hash) { - return nil, errors.Errorf("older header hash %X does not match trusted header's last block %X", - untrustedHeader.Hash(), - trustedHeader.LastBlockID.Hash) - } - - trustedHeader = untrustedHeader + trustedHeader = interimHeader } - return trustedHeader, nil + // Initially trusted header might have expired at this point. + if HeaderExpired(initiallyTrustedHeader, c.trustingPeriod, now) { + return ErrOldHeaderExpired{initiallyTrustedHeader.Time.Add(c.trustingPeriod), now} + } + + return nil } // compare header with all witnesses provided. @@ -899,7 +877,7 @@ func (c *Client) compareNewHeaderWithWitnesses(h *types.SignedHeader) error { } if !bytes.Equal(h.Hash(), altH.Hash()) { - if err = c.trustedNextVals.VerifyCommitTrusting(c.chainID, altH.Commit.BlockID, + if err = c.latestTrustedVals.VerifyCommitTrusting(c.chainID, altH.Commit.BlockID, altH.Height, altH.Commit, c.trustLevel); err != nil { c.logger.Error("Witness sent us incorrect header", "err", err, "witness", witness) witnessesToRemove = append(witnessesToRemove, i) @@ -945,122 +923,35 @@ func (c *Client) removeWitness(idx int) { } } -func (c *Client) removeNoLongerTrustedHeadersRoutine() { - defer c.routinesWaitGroup.Done() - - ticker := time.NewTicker(c.removeNoLongerTrustedHeadersPeriod) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - c.RemoveNoLongerTrustedHeaders(time.Now()) - case <-c.quit: - return - } - } -} - -// RemoveNoLongerTrustedHeaders removes no longer trusted headers (due to -// expiration). -// -// Exposed for testing. -func (c *Client) RemoveNoLongerTrustedHeaders(now time.Time) { - // 1) Get the oldest height. - oldestHeight, err := c.FirstTrustedHeight() - if err != nil { - c.logger.Error("can't get first trusted height", "err", err) - return - } - if oldestHeight == -1 { // no headers yet => wait - return - } - - // 2) Get the latest height. - latestHeight, err := c.LastTrustedHeight() - if err != nil { - c.logger.Error("can't get last trusted height", "err", err) - return - } - if latestHeight == -1 { // no headers yet => wait - return - } - - // 3) Remove all headers that are outside of the trusting period. - // - // NOTE: even the latest header can be removed. it's okay because - // c.trustedHeader will retain it in memory so other funcs like VerifyHeader - // don't crash. - for height := oldestHeight; height <= latestHeight; height++ { - h, err := c.trustedStore.SignedHeader(height) - if err != nil { - c.logger.Error("can't get a trusted header", "err", err, "height", height) - continue - } - - // Stop if the header is within the trusting period. - if !HeaderExpired(h, c.trustingPeriod, now) { - break - } - - err = c.trustedStore.DeleteSignedHeaderAndNextValidatorSet(height) - if err != nil { - c.logger.Error("can't remove a trusted header & validator set", "err", err, "height", height) - continue - } - } -} - -func (c *Client) autoUpdateRoutine() { - defer c.routinesWaitGroup.Done() - - 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 making exponential steps (note: -// when SequentialVerification is being used, the client will still be -// downloading all intermediate headers). -// -// Exposed for testing. -func (c *Client) Update(now time.Time) error { +// Update attempts to advance the state by downloading the latest header and +// 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 @@ -1097,6 +988,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)) } @@ -1120,6 +1012,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_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/client_test.go b/lite2/client_test.go index d8f44adc6..65ea55122 100644 --- a/lite2/client_test.go +++ b/lite2/client_test.go @@ -39,24 +39,31 @@ var ( Height: 1, Hash: h1.Hash(), } + valSet = map[int64]*types.ValidatorSet{ + 1: vals, + 2: vals, + 3: vals, + 4: vals, + } + headerSet = map[int64]*types.SignedHeader{ + 1: h1, + // interim header (3/3 signed) + 2: h2, + // last header (3/3 signed) + 3: h3, + } fullNode = mockp.New( chainID, - map[int64]*types.SignedHeader{ - 1: h1, - 2: h2, - 3: h3, - }, - map[int64]*types.ValidatorSet{ - 1: vals, - 2: vals, - 3: vals, - 4: vals, - }, + headerSet, + valSet, ) deadNode = mockp.NewDeadMock(chainID) ) func TestClient_SequentialVerification(t *testing.T) { + newKeys := genPrivKeys(4) + newVals := newKeys.ToValidators(10, 1) + testCases := []struct { name string otherHeaders map[int64]*types.SignedHeader // all except ^ @@ -66,20 +73,8 @@ func TestClient_SequentialVerification(t *testing.T) { }{ { "good", - map[int64]*types.SignedHeader{ - // trusted header - 1: h1, - // interim header (3/3 signed) - 2: h2, - // last header (3/3 signed) - 3: h3, - }, - map[int64]*types.ValidatorSet{ - 1: vals, - 2: vals, - 3: vals, - 4: vals, - }, + headerSet, + valSet, false, false, }, @@ -108,12 +103,7 @@ func TestClient_SequentialVerification(t *testing.T) { 3: keys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, vals, vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)), }, - map[int64]*types.ValidatorSet{ - 1: vals, - 2: vals, - 3: vals, - 4: vals, - }, + valSet, false, true, }, @@ -129,11 +119,17 @@ func TestClient_SequentialVerification(t *testing.T) { 3: keys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, vals, vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), len(keys)-1, len(keys)), }, + valSet, + false, + true, + }, + { + "bad: different validator set at height 3", + headerSet, map[int64]*types.ValidatorSet{ 1: vals, 2: vals, - 3: vals, - 4: vals, + 3: newVals, }, false, true, @@ -166,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 { @@ -204,12 +197,7 @@ func TestClient_SkippingVerification(t *testing.T) { // last header (3/3 signed) 3: h3, }, - map[int64]*types.ValidatorSet{ - 1: vals, - 2: vals, - 3: vals, - 4: vals, - }, + valSet, false, false, }, @@ -225,7 +213,6 @@ func TestClient_SkippingVerification(t *testing.T) { 1: vals, 2: vals, 3: transitVals, - 4: transitVals, }, false, false, @@ -246,7 +233,6 @@ func TestClient_SkippingVerification(t *testing.T) { 1: vals, 2: vals, 3: newVals, - 4: newVals, }, false, false, @@ -267,7 +253,6 @@ func TestClient_SkippingVerification(t *testing.T) { 1: vals, 2: vals, 3: newVals, - 4: newVals, }, false, true, @@ -299,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 { @@ -313,47 +295,6 @@ func TestClient_SkippingVerification(t *testing.T) { } } -func TestClientRemovesNoLongerTrustedHeaders(t *testing.T) { - c, err := NewClient( - chainID, - trustOptions, - fullNode, - []provider.Provider{fullNode}, - dbs.New(dbm.NewMemDB(), chainID), - Logger(log.TestingLogger()), - ) - - assert.NotPanics(t, func() { - now := bTime.Add(4 * time.Hour).Add(1 * time.Second) - c.RemoveNoLongerTrustedHeaders(now) - }) - - require.NoError(t, err) - err = c.Start() - require.NoError(t, err) - defer c.Stop() - - // Verify new headers. - _, err = c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour).Add(1*time.Second)) - require.NoError(t, err) - now := bTime.Add(4 * time.Hour).Add(1 * time.Second) - _, err = c.VerifyHeaderAtHeight(3, now) - require.NoError(t, err) - - // Remove expired headers. - c.RemoveNoLongerTrustedHeaders(now) - - // Check expired headers are no longer available. - h, err := c.TrustedHeader(1, now) - assert.Error(t, err) - assert.Nil(t, h) - - // Check not expired headers are available. - h, err = c.TrustedHeader(2, now) - assert.NoError(t, err) - assert.NotNil(t, h) -} - func TestClient_Cleanup(t *testing.T) { c, err := NewClient( chainID, @@ -364,16 +305,20 @@ func TestClient_Cleanup(t *testing.T) { Logger(log.TestingLogger()), ) require.NoError(t, err) - err = c.Start() + _, err = c.TrustedHeader(1) require.NoError(t, err) - c.Stop() - c.Cleanup() + err = c.Cleanup() + require.NoError(t, err) - // Check no headers exist after Cleanup. - h, err := c.TrustedHeader(1, bTime.Add(1*time.Second)) + // Check no headers/valsets exist after Cleanup. + h, err := c.TrustedHeader(1) assert.Error(t, err) assert.Nil(t, h) + + valSet, _, err := c.TrustedValidatorSet(1) + assert.Error(t, err) + assert.Nil(t, valSet) } // trustedHeader.Height == options.Height @@ -381,7 +326,7 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) { // 1. options.Hash == trustedHeader.Hash { trustedStore := dbs.New(dbm.NewMemDB(), chainID) - err := trustedStore.SaveSignedHeaderAndNextValidatorSet(h1, vals) + err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals) require.NoError(t, err) c, err := NewClient( @@ -393,20 +338,24 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) { Logger(log.TestingLogger()), ) require.NoError(t, err) - err = c.Start() - require.NoError(t, err) - defer c.Stop() - h, err := c.TrustedHeader(1, bTime.Add(1*time.Second)) + h, err := c.TrustedHeader(1) assert.NoError(t, err) assert.NotNil(t, h) assert.Equal(t, h.Hash(), h1.Hash()) + + valSet, _, err := c.TrustedValidatorSet(1) + assert.NoError(t, err) + assert.NotNil(t, valSet) + if assert.NotNil(t, valSet) { + assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash()) + } } // 2. options.Hash != trustedHeader.Hash { trustedStore := dbs.New(dbm.NewMemDB(), chainID) - err := trustedStore.SaveSignedHeaderAndNextValidatorSet(h1, vals) + err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals) require.NoError(t, err) // header1 != header @@ -419,10 +368,7 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) { // trusted header 1: header1, }, - map[int64]*types.ValidatorSet{ - 1: vals, - 2: vals, - }, + valSet, ) c, err := NewClient( @@ -438,14 +384,19 @@ func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) { Logger(log.TestingLogger()), ) require.NoError(t, err) - err = c.Start() - require.NoError(t, err) - defer c.Stop() - h, err := c.TrustedHeader(1, bTime.Add(1*time.Second)) + h, err := c.TrustedHeader(1) assert.NoError(t, err) - assert.NotNil(t, h) - assert.Equal(t, h.Hash(), header1.Hash()) + if assert.NotNil(t, h) { + assert.Equal(t, h.Hash(), header1.Hash()) + } + + valSet, _, err := c.TrustedValidatorSet(1) + assert.NoError(t, err) + assert.NotNil(t, valSet) + if assert.NotNil(t, valSet) { + assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash()) + } } } @@ -454,7 +405,7 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) { // 1. options.Hash == trustedHeader.Hash { trustedStore := dbs.New(dbm.NewMemDB(), chainID) - err := trustedStore.SaveSignedHeaderAndNextValidatorSet(h1, vals) + err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals) require.NoError(t, err) c, err := NewClient( @@ -470,22 +421,26 @@ 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, bTime.Add(2*time.Hour).Add(1*time.Second)) + h, err := c.TrustedHeader(1) assert.NoError(t, err) assert.NotNil(t, h) assert.Equal(t, h.Hash(), h1.Hash()) + + valSet, _, err := c.TrustedValidatorSet(1) + assert.NoError(t, err) + assert.NotNil(t, valSet) + if assert.NotNil(t, valSet) { + assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash()) + } } // 2. options.Hash != trustedHeader.Hash // This could happen if previous provider was lying to us. { trustedStore := dbs.New(dbm.NewMemDB(), chainID) - err := trustedStore.SaveSignedHeaderAndNextValidatorSet(h1, vals) + err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals) require.NoError(t, err) // header1 != header @@ -501,11 +456,7 @@ func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) { 1: diffHeader1, 2: diffHeader2, }, - map[int64]*types.ValidatorSet{ - 1: vals, - 2: vals, - 3: vals, - }, + valSet, ) c, err := NewClient( @@ -521,14 +472,15 @@ 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, bTime.Add(2*time.Hour).Add(1*time.Second)) + h, err := c.TrustedHeader(1) assert.Error(t, err) assert.Nil(t, h) + + valSet, _, err := c.TrustedValidatorSet(1) + assert.Error(t, err) + assert.Nil(t, valSet) } } @@ -537,57 +489,52 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) { // 1. options.Hash == trustedHeader.Hash { trustedStore := dbs.New(dbm.NewMemDB(), chainID) - err := trustedStore.SaveSignedHeaderAndNextValidatorSet(h1, vals) + err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals) require.NoError(t, err) //header2 := keys.GenSignedHeader(chainID, 2, bTime.Add(2*time.Hour), nil, vals, vals, // []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)) - err = trustedStore.SaveSignedHeaderAndNextValidatorSet(h2, vals) + err = trustedStore.SaveSignedHeaderAndValidatorSet(h2, vals) require.NoError(t, err) - primary := mockp.New( - chainID, - map[int64]*types.SignedHeader{ - 1: h1, - 2: h2, - }, - map[int64]*types.ValidatorSet{ - 1: vals, - 2: vals, - 3: vals, - }, - ) - c, err := NewClient( chainID, trustOptions, - primary, - []provider.Provider{primary}, + fullNode, + []provider.Provider{fullNode}, trustedStore, 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, bTime.Add(2*time.Hour).Add(1*time.Second)) + h, err := c.TrustedHeader(1) assert.NoError(t, err) assert.NotNil(t, h) assert.Equal(t, h.Hash(), h1.Hash()) + valSet, _, err := c.TrustedValidatorSet(1) + assert.NoError(t, err) + assert.NotNil(t, valSet) + if assert.NotNil(t, valSet) { + assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash()) + } + // Check we no longer have 2nd header (+header2+). - h, err = c.TrustedHeader(2, bTime.Add(2*time.Hour).Add(1*time.Second)) + h, err = c.TrustedHeader(2) assert.Error(t, err) assert.Nil(t, h) + + valSet, _, err = c.TrustedValidatorSet(2) + assert.Error(t, err) + assert.Nil(t, valSet) } // 2. options.Hash != trustedHeader.Hash // This could happen if previous provider was lying to us. { trustedStore := dbs.New(dbm.NewMemDB(), chainID) - err := trustedStore.SaveSignedHeaderAndNextValidatorSet(h1, vals) + err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals) require.NoError(t, err) // header1 != header @@ -596,7 +543,7 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) { header2 := keys.GenSignedHeader(chainID, 2, bTime.Add(2*time.Hour), nil, vals, vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)) - err = trustedStore.SaveSignedHeaderAndNextValidatorSet(header2, vals) + err = trustedStore.SaveSignedHeaderAndValidatorSet(header2, vals) require.NoError(t, err) primary := mockp.New( @@ -604,10 +551,7 @@ func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) { map[int64]*types.SignedHeader{ 1: header1, }, - map[int64]*types.ValidatorSet{ - 1: vals, - 2: vals, - }, + valSet, ) c, err := NewClient( @@ -623,20 +567,28 @@ 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, bTime.Add(2*time.Hour).Add(1*time.Second)) + h, err := c.TrustedHeader(1) assert.NoError(t, err) assert.NotNil(t, h) assert.Equal(t, h.Hash(), header1.Hash()) + valSet, _, err := c.TrustedValidatorSet(1) + assert.NoError(t, err) + assert.NotNil(t, valSet) + if assert.NotNil(t, valSet) { + assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash()) + } + // Check we no longer have invalid 2nd header (+header2+). - h, err = c.TrustedHeader(2, bTime.Add(2*time.Hour).Add(1*time.Second)) + h, err = c.TrustedHeader(2) assert.Error(t, err) assert.Nil(t, h) + + valSet, _, err = c.TrustedValidatorSet(2) + assert.Error(t, err) + assert.Nil(t, valSet) } } @@ -650,18 +602,19 @@ 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)) - require.NoError(t, err) - - h, err := c.TrustedHeader(3, bTime.Add(2*time.Hour)) + 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) + if assert.NotNil(t, valSet) { + assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash()) + } } func TestClient_Concurrency(t *testing.T) { @@ -671,13 +624,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) @@ -699,11 +648,11 @@ func TestClient_Concurrency(t *testing.T) { _, err = c.FirstTrustedHeight() assert.NoError(t, err) - h, err := c.TrustedHeader(1, bTime.Add(2*time.Hour)) + h, err := c.TrustedHeader(1) assert.NoError(t, err) assert.NotNil(t, h) - vals, err := c.TrustedValidatorSet(2, bTime.Add(2*time.Hour)) + vals, _, err := c.TrustedValidatorSet(2) assert.NoError(t, err) assert.NotNil(t, vals) }() @@ -719,55 +668,127 @@ func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) { deadNode, []provider.Provider{fullNode, fullNode}, dbs.New(dbm.NewMemDB(), chainID), - UpdatePeriod(0), Logger(log.TestingLogger()), MaxRetryAttempts(1), ) 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) assert.Equal(t, 1, len(c.Witnesses())) } -func TestClient_TrustedHeaderFetchesMissingHeader(t *testing.T) { - c, err := NewClient( - chainID, - TrustOptions{ - Period: 1 * time.Hour, - Height: 3, - Hash: h3.Hash(), - }, - 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() +func TestClient_BackwardsVerification(t *testing.T) { + { + 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()), + ) + require.NoError(t, err) - // 1) header is missing => expect no error - h, err := c.TrustedHeader(2, bTime.Add(1*time.Hour).Add(1*time.Second)) - require.NoError(t, err) - if assert.NotNil(t, h) { - assert.EqualValues(t, 2, h.Height) + // 1) header is missing => expect no error + h, err := c.VerifyHeaderAtHeight(2, bTime.Add(1*time.Hour).Add(1*time.Second)) + require.NoError(t, err) + if assert.NotNil(t, h) { + assert.EqualValues(t, 2, 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)) + 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)) + 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()), + ) + require.NoError(t, err) - // 2) header is missing, but it's expired => expect error - h, err = c.TrustedHeader(1, bTime.Add(1*time.Hour).Add(1*time.Second)) - assert.Error(t, err) - assert.Nil(t, h) + // 3) trusted header has expired => expect error + _, err = c.VerifyHeaderAtHeight(1, bTime.Add(4*time.Hour).Add(1*time.Second)) + assert.Error(t, err) + } + { + testCases := []struct { + provider provider.Provider + }{ + { + // provides incorrect height + mockp.New( + chainID, + map[int64]*types.SignedHeader{ + 1: h1, + 2: keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Hour), nil, vals, vals, + []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)), + 3: h3, + }, + valSet, + ), + }, + { + // provides incorrect hash + mockp.New( + chainID, + map[int64]*types.SignedHeader{ + 1: h1, + 2: keys.GenSignedHeader(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals, + []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)), + 3: h3, + }, + valSet, + ), + }, + } + + for _, tc := range testCases { + c, err := NewClient( + chainID, + TrustOptions{ + Period: 1 * time.Hour, + Height: 3, + Hash: h3.Hash(), + }, + tc.provider, + []provider.Provider{tc.provider}, + dbs.New(dbm.NewMemDB(), chainID), + Logger(log.TestingLogger()), + ) + require.NoError(t, err) + + _, err = c.VerifyHeaderAtHeight(2, bTime.Add(1*time.Hour).Add(1*time.Second)) + assert.Error(t, err) + } + } } func TestClient_NewClientFromTrustedStore(t *testing.T) { // 1) Initiate DB and fill with a "trusted" header db := dbs.New(dbm.NewMemDB(), chainID) - err := db.SaveSignedHeaderAndNextValidatorSet(h1, vals) + err := db.SaveSignedHeaderAndValidatorSet(h1, vals) require.NoError(t, err) c, err := NewClientFromTrustedStore( @@ -781,49 +802,60 @@ func TestClient_NewClientFromTrustedStore(t *testing.T) { // 2) Check header exists (deadNode is being used to ensure we're not getting // it from primary) - h, err := c.TrustedHeader(1, bTime.Add(1*time.Second)) + h, err := c.TrustedHeader(1) assert.NoError(t, err) assert.EqualValues(t, 1, h.Height) + + valSet, _, err := c.TrustedValidatorSet(1) + assert.NoError(t, err) + assert.NotNil(t, valSet) + if assert.NotNil(t, valSet) { + assert.Equal(t, h.ValidatorsHash.Bytes(), valSet.Hash()) + } } -func TestClientUpdateErrorsIfAllWitnessesUnavailable(t *testing.T) { - c, err := NewClient( +func TestNewClientErrorsIfAllWitnessesUnavailable(t *testing.T) { + _, err := NewClient( chainID, trustOptions, fullNode, - []provider.Provider{deadNode, deadNode, deadNode}, + []provider.Provider{deadNode, deadNode}, dbs.New(dbm.NewMemDB(), chainID), - UpdatePeriod(0), Logger(log.TestingLogger()), MaxRetryAttempts(1), ) - require.NoError(t, err) - - err = c.Update(bTime.Add(2 * time.Hour)) if assert.Error(t, err) { assert.Contains(t, err.Error(), "awaiting response from all witnesses exceeded dropout time") } } func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) { - // straight invalid header + // different headers hash then primary plus less than 1/3 signed (no fork) badProvider1 := mockp.New( chainID, map[int64]*types.SignedHeader{ - 3: {Header: nil, Commit: nil}, + 1: h1, + 2: keys.GenSignedHeaderLastBlockID(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals, + []byte("app_hash2"), []byte("cons_hash"), []byte("results_hash"), + len(keys), len(keys), types.BlockID{Hash: h1.Hash()}), + }, + map[int64]*types.ValidatorSet{ + 1: vals, + 2: vals, }, - map[int64]*types.ValidatorSet{}, ) - - // less than 1/3 signed + // header is empty badProvider2 := mockp.New( chainID, map[int64]*types.SignedHeader{ - 3: keys.GenSignedHeaderLastBlockID(chainID, 3, bTime.Add(1*time.Hour), nil, vals, vals, - []byte("app_hash2"), []byte("cons_hash"), []byte("results_hash"), - len(keys), len(keys), types.BlockID{Hash: h2.Hash()}), + 1: h1, + 2: h2, + 3: {Header: nil, Commit: nil}, + }, + map[int64]*types.ValidatorSet{ + 1: vals, + 2: vals, }, - map[int64]*types.ValidatorSet{}, ) c, err := NewClient( @@ -832,14 +864,43 @@ func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) { fullNode, []provider.Provider{badProvider1, badProvider2}, dbs.New(dbm.NewMemDB(), chainID), - UpdatePeriod(0), + Logger(log.TestingLogger()), + MaxRetryAttempts(1), + ) + // witness should have behaved properly -> no error + require.NoError(t, err) + assert.EqualValues(t, 2, len(c.Witnesses())) + + // witness behaves incorrectly -> removed from list, no error + h, err := c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour)) + assert.NoError(t, err) + assert.EqualValues(t, 1, len(c.Witnesses())) + // header should still be verified + assert.EqualValues(t, 2, h.Height) + + // no witnesses left to verify -> error + _, err = c.VerifyHeaderAtHeight(3, bTime.Add(2*time.Hour)) + assert.Error(t, err) + assert.EqualValues(t, 0, len(c.Witnesses())) +} + +func TestClientTrustedValidatorSet(t *testing.T) { + c, err := NewClient( + chainID, + trustOptions, + fullNode, + []provider.Provider{fullNode}, + dbs.New(dbm.NewMemDB(), chainID), Logger(log.TestingLogger()), ) + require.NoError(t, err) - err = c.Update(bTime.Add(2 * time.Hour)) - if assert.Error(t, err) { - assert.Contains(t, err.Error(), "could not find any witnesses") - } - assert.Zero(t, 0, len(c.Witnesses())) + _, err = c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour).Add(1*time.Second)) + require.NoError(t, err) + + valSet, height, err := c.TrustedValidatorSet(0) + assert.NoError(t, err) + assert.NotNil(t, valSet) + assert.EqualValues(t, 2, height) } 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/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/example_test.go b/lite2/example_test.go index 87e839d9b..e8c3b8bb3 100644 --- a/lite2/example_test.go +++ b/lite2/example_test.go @@ -11,7 +11,6 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/abci/example/kvstore" - "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/lite2/provider" httpp "github.com/tendermint/tendermint/lite2/provider/http" dbs "github.com/tendermint/tendermint/lite2/store/db" @@ -19,7 +18,7 @@ import ( ) // Automatically getting new headers and verifying them. -func TestExample_Client_AutoUpdate(t *testing.T) { +func ExampleClient_Update() { // give Tendermint time to generate some blocks time.Sleep(5 * time.Second) @@ -57,36 +56,38 @@ func TestExample_Client_AutoUpdate(t *testing.T) { 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(1*time.Second), - Logger(log.TestingLogger()), + // Logger(log.TestingLogger()), ) if err != nil { stdlog.Fatal(err) } - err = c.Start() - if err != nil { - stdlog.Fatal(err) - } defer func() { - c.Stop() c.Cleanup() }() time.Sleep(2 * time.Second) - h, err := c.TrustedHeader(0, time.Now()) + // 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 + h, err := c.Update(time.Now().Add(30 * time.Minute)) if err != nil { stdlog.Fatal(err) } - fmt.Println("got header", h.Height) - // Output: got header 3 + if h != nil && h.Height > 2 { + fmt.Println("successful update") + } else { + fmt.Println("update failed") + } + // Output: successful update } // Manually getting headers and verifying them. -func TestExample_Client_ManualUpdate(t *testing.T) { +func ExampleClient_VerifyHeaderAtHeight() { // give Tendermint time to generate some blocks time.Sleep(5 * time.Second) @@ -124,20 +125,14 @@ func TestExample_Client_ManualUpdate(t *testing.T) { 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()), + // Logger(log.TestingLogger()), ) if err != nil { stdlog.Fatal(err) } - err = c.Start() - if err != nil { - stdlog.Fatal(err) - } defer func() { - c.Stop() c.Cleanup() }() @@ -146,7 +141,7 @@ func TestExample_Client_ManualUpdate(t *testing.T) { stdlog.Fatal(err) } - h, err := c.TrustedHeader(3, time.Now()) + h, err := c.TrustedHeader(3) if err != nil { stdlog.Fatal(err) } @@ -158,7 +153,7 @@ func TestExample_Client_ManualUpdate(t *testing.T) { func TestMain(m *testing.M) { // start a tendermint node (and kvstore) in the background to test against app := kvstore.NewApplication() - node := rpctest.StartTendermint(app) + node := rpctest.StartTendermint(app, rpctest.SuppressStdout) code := m.Run() diff --git a/lite2/rpc/client.go b/lite2/rpc/client.go index 9778878ac..abd15adc2 100644 --- a/lite2/rpc/client.go +++ b/lite2/rpc/client.go @@ -191,7 +191,7 @@ func (c *Client) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlock // Verify each of the BlockMetas. for _, meta := range res.BlockMetas { - h, err := c.lc.TrustedHeader(meta.Header.Height, time.Now()) + h, err := c.lc.TrustedHeader(meta.Header.Height) if err != nil { return nil, errors.Wrapf(err, "TrustedHeader(%d)", meta.Header.Height) } @@ -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, time.Now()) - 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) { 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 +} diff --git a/lite2/store/db/db.go b/lite2/store/db/db.go index 89897ed7f..d405b9865 100644 --- a/lite2/store/db/db.go +++ b/lite2/store/db/db.go @@ -1,9 +1,11 @@ package db import ( + "encoding/binary" "fmt" "regexp" "strconv" + "sync" "github.com/pkg/errors" "github.com/tendermint/go-amino" @@ -14,10 +16,17 @@ import ( "github.com/tendermint/tendermint/types" ) +var ( + sizeKey = []byte("size") +) + type dbs struct { db dbm.DB prefix string + mtx sync.RWMutex + size uint16 + cdc *amino.Codec } @@ -28,12 +37,21 @@ type dbs struct { func New(db dbm.DB, prefix string) store.Store { cdc := amino.NewCodec() cryptoAmino.RegisterAmino(cdc) - return &dbs{db: db, prefix: prefix, cdc: cdc} + + size := uint16(0) + bz, err := db.Get(sizeKey) + if err == nil && len(bz) > 0 { + size = unmarshalSize(bz) + } + + return &dbs{db: db, prefix: prefix, cdc: cdc, size: size} } -// SaveSignedHeaderAndNextValidatorSet persists SignedHeader and ValidatorSet -// to the db. -func (s *dbs) SaveSignedHeaderAndNextValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error { +// SaveSignedHeaderAndValidatorSet persists SignedHeader and ValidatorSet to +// the db. +// +// Safe for concurrent use by multiple goroutines. +func (s *dbs) SaveSignedHeaderAndValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error { if sh.Height <= 0 { panic("negative or zero height") } @@ -42,35 +60,60 @@ func (s *dbs) SaveSignedHeaderAndNextValidatorSet(sh *types.SignedHeader, valSet if err != nil { return errors.Wrap(err, "marshalling header") } + valSetBz, err := s.cdc.MarshalBinaryLengthPrefixed(valSet) if err != nil { return errors.Wrap(err, "marshalling validator set") } + s.mtx.Lock() + defer s.mtx.Unlock() + b := s.db.NewBatch() b.Set(s.shKey(sh.Height), shBz) - b.Set(s.vsKey(sh.Height+1), valSetBz) + b.Set(s.vsKey(sh.Height), valSetBz) + b.Set(sizeKey, marshalSize(s.size+1)) + err = b.WriteSync() b.Close() + + if err == nil { + s.size++ + } + return err } -// DeleteSignedHeaderAndNextValidatorSet deletes SignedHeader and ValidatorSet -// from the db. -func (s *dbs) DeleteSignedHeaderAndNextValidatorSet(height int64) error { +// DeleteSignedHeaderAndValidatorSet deletes SignedHeader and ValidatorSet from +// the db. +// +// Safe for concurrent use by multiple goroutines. +func (s *dbs) DeleteSignedHeaderAndValidatorSet(height int64) error { if height <= 0 { panic("negative or zero height") } + s.mtx.Lock() + defer s.mtx.Unlock() + b := s.db.NewBatch() b.Delete(s.shKey(height)) - b.Delete(s.vsKey(height + 1)) + b.Delete(s.vsKey(height)) + b.Set(sizeKey, marshalSize(s.size-1)) + err := b.WriteSync() b.Close() + + if err == nil { + s.size-- + } + return err } // SignedHeader loads SignedHeader at the given height. +// +// Safe for concurrent use by multiple goroutines. func (s *dbs) SignedHeader(height int64) (*types.SignedHeader, error) { if height <= 0 { panic("negative or zero height") @@ -90,6 +133,8 @@ func (s *dbs) SignedHeader(height int64) (*types.SignedHeader, error) { } // ValidatorSet loads ValidatorSet at the given height. +// +// Safe for concurrent use by multiple goroutines. func (s *dbs) ValidatorSet(height int64) (*types.ValidatorSet, error) { if height <= 0 { panic("negative or zero height") @@ -109,6 +154,8 @@ func (s *dbs) ValidatorSet(height int64) (*types.ValidatorSet, error) { } // LastSignedHeaderHeight returns the last SignedHeader height stored. +// +// Safe for concurrent use by multiple goroutines. func (s *dbs) LastSignedHeaderHeight() (int64, error) { itr, err := s.db.ReverseIterator( s.shKey(1), @@ -132,6 +179,8 @@ func (s *dbs) LastSignedHeaderHeight() (int64, error) { } // FirstSignedHeaderHeight returns the first SignedHeader height stored. +// +// Safe for concurrent use by multiple goroutines. func (s *dbs) FirstSignedHeaderHeight() (int64, error) { itr, err := s.db.Iterator( s.shKey(1), @@ -154,12 +203,16 @@ 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. +// +// Safe for concurrent use by multiple goroutines. func (s *dbs) SignedHeaderAfter(height int64) (*types.SignedHeader, error) { if height <= 0 { panic("negative or zero height") } - itr, err := s.db.ReverseIterator( + itr, err := s.db.Iterator( s.shKey(height+1), append(s.shKey(1<<63-1), byte(0x00)), ) @@ -177,7 +230,76 @@ func (s *dbs) SignedHeaderAfter(height int64) (*types.SignedHeader, error) { itr.Next() } - panic(fmt.Sprintf("no header after height %d. make sure height is not greater than latest existing height", height)) + return nil, store.ErrSignedHeaderNotFound +} + +// Prune prunes header & validator set pairs until there are only size pairs +// left. +// +// Safe for concurrent use by multiple goroutines. +func (s *dbs) Prune(size uint16) error { + // 1) Check how many we need to prune. + s.mtx.RLock() + sSize := s.size + s.mtx.RUnlock() + + if sSize <= size { // nothing to prune + return nil + } + numToPrune := sSize - size + + // 2) Iterate over headers and perform a batch operation. + itr, err := s.db.Iterator( + s.shKey(1), + append(s.shKey(1<<63-1), byte(0x00)), + ) + if err != nil { + panic(err) + } + + b := s.db.NewBatch() + + pruned := 0 + for itr.Valid() && numToPrune > 0 { + key := itr.Key() + _, height, ok := parseShKey(key) + if ok { + b.Delete(s.shKey(height)) + b.Delete(s.vsKey(height)) + } + itr.Next() + numToPrune-- + pruned++ + } + + itr.Close() + + err = b.WriteSync() + b.Close() + if err != nil { + return err + } + + // 3) Update size. + s.mtx.Lock() + defer s.mtx.Unlock() + + s.size -= uint16(pruned) + + if wErr := s.db.SetSync(sizeKey, marshalSize(s.size)); wErr != nil { + return errors.Wrap(wErr, "failed to persist size") + } + + return nil +} + +// Size returns the number of header & validator set pairs. +// +// Safe for concurrent use by multiple goroutines. +func (s *dbs) Size() uint16 { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.size } func (s *dbs) shKey(height int64) []byte { @@ -213,3 +335,13 @@ func parseShKey(key []byte) (prefix string, height int64, ok bool) { } return } + +func marshalSize(size uint16) []byte { + bs := make([]byte, 2) + binary.LittleEndian.PutUint16(bs, size) + return bs +} + +func unmarshalSize(bz []byte) uint16 { + return binary.LittleEndian.Uint16(bz) +} diff --git a/lite2/store/db/db_test.go b/lite2/store/db/db_test.go index a1dc3000e..2b82de8f3 100644 --- a/lite2/store/db/db_test.go +++ b/lite2/store/db/db_test.go @@ -1,6 +1,7 @@ package db import ( + "sync" "testing" "github.com/stretchr/testify/assert" @@ -24,7 +25,7 @@ func TestLast_FirstSignedHeaderHeight(t *testing.T) { assert.EqualValues(t, -1, height) // 1 key - err = dbStore.SaveSignedHeaderAndNextValidatorSet( + err = dbStore.SaveSignedHeaderAndValidatorSet( &types.SignedHeader{Header: &types.Header{Height: 1}}, &types.ValidatorSet{}) require.NoError(t, err) @@ -37,20 +38,20 @@ func TestLast_FirstSignedHeaderHeight(t *testing.T) { assert.EqualValues(t, 1, height) } -func Test_SaveSignedHeaderAndNextValidatorSet(t *testing.T) { - dbStore := New(dbm.NewMemDB(), "Test_SaveSignedHeaderAndNextValidatorSet") +func Test_SaveSignedHeaderAndValidatorSet(t *testing.T) { + dbStore := New(dbm.NewMemDB(), "Test_SaveSignedHeaderAndValidatorSet") // Empty store h, err := dbStore.SignedHeader(1) require.Error(t, err) assert.Nil(t, h) - valSet, err := dbStore.ValidatorSet(2) + valSet, err := dbStore.ValidatorSet(1) require.Error(t, err) assert.Nil(t, valSet) // 1 key - err = dbStore.SaveSignedHeaderAndNextValidatorSet( + err = dbStore.SaveSignedHeaderAndValidatorSet( &types.SignedHeader{Header: &types.Header{Height: 1}}, &types.ValidatorSet{}) require.NoError(t, err) @@ -58,19 +59,19 @@ func Test_SaveSignedHeaderAndNextValidatorSet(t *testing.T) { require.NoError(t, err) assert.NotNil(t, h) - valSet, err = dbStore.ValidatorSet(2) + valSet, err = dbStore.ValidatorSet(1) require.NoError(t, err) assert.NotNil(t, valSet) // Empty store - err = dbStore.DeleteSignedHeaderAndNextValidatorSet(1) + err = dbStore.DeleteSignedHeaderAndValidatorSet(1) require.NoError(t, err) h, err = dbStore.SignedHeader(1) require.Error(t, err) assert.Nil(t, h) - valSet, err = dbStore.ValidatorSet(2) + valSet, err = dbStore.ValidatorSet(1) require.Error(t, err) assert.Nil(t, valSet) } @@ -83,7 +84,7 @@ func Test_SignedHeaderAfter(t *testing.T) { dbStore.SignedHeaderAfter(100) }) - err := dbStore.SaveSignedHeaderAndNextValidatorSet( + err := dbStore.SaveSignedHeaderAndValidatorSet( &types.SignedHeader{Header: &types.Header{Height: 2}}, &types.ValidatorSet{}) require.NoError(t, err) @@ -93,3 +94,69 @@ func Test_SignedHeaderAfter(t *testing.T) { assert.EqualValues(t, 2, h.Height) } } + +func Test_Prune(t *testing.T) { + dbStore := New(dbm.NewMemDB(), "Test_Prune") + + // Empty store + assert.EqualValues(t, 0, dbStore.Size()) + err := dbStore.Prune(0) + require.NoError(t, err) + + // One header + err = dbStore.SaveSignedHeaderAndValidatorSet( + &types.SignedHeader{Header: &types.Header{Height: 2}}, &types.ValidatorSet{}) + require.NoError(t, err) + + assert.EqualValues(t, 1, dbStore.Size()) + + err = dbStore.Prune(1) + require.NoError(t, err) + assert.EqualValues(t, 1, dbStore.Size()) + + err = dbStore.Prune(0) + require.NoError(t, err) + assert.EqualValues(t, 0, dbStore.Size()) + + // Multiple headers + for i := 1; i <= 10; i++ { + err = dbStore.SaveSignedHeaderAndValidatorSet( + &types.SignedHeader{Header: &types.Header{Height: int64(i)}}, &types.ValidatorSet{}) + require.NoError(t, err) + } + + err = dbStore.Prune(11) + require.NoError(t, err) + assert.EqualValues(t, 10, dbStore.Size()) + + err = dbStore.Prune(7) + require.NoError(t, err) + assert.EqualValues(t, 7, dbStore.Size()) +} + +func Test_Concurrency(t *testing.T) { + dbStore := New(dbm.NewMemDB(), "Test_Prune") + + var wg sync.WaitGroup + for i := 1; i <= 100; i++ { + wg.Add(1) + go func(i int64) { + defer wg.Done() + + dbStore.SaveSignedHeaderAndValidatorSet( + &types.SignedHeader{Header: &types.Header{Height: i}}, &types.ValidatorSet{}) + + dbStore.SignedHeader(i) + dbStore.ValidatorSet(i) + dbStore.LastSignedHeaderHeight() + dbStore.FirstSignedHeaderHeight() + + dbStore.Prune(2) + _ = dbStore.Size() + + dbStore.DeleteSignedHeaderAndValidatorSet(1) + }(int64(i)) + } + + wg.Wait() +} diff --git a/lite2/store/errors.go b/lite2/store/errors.go index 8a587047b..2f77bc893 100644 --- a/lite2/store/errors.go +++ b/lite2/store/errors.go @@ -6,6 +6,7 @@ var ( // ErrSignedHeaderNotFound is returned when a store does not have the // requested header. ErrSignedHeaderNotFound = errors.New("signed header not found") + // ErrValidatorSetNotFound is returned when a store does not have the // requested validator set. ErrValidatorSetNotFound = errors.New("validator set not found") diff --git a/lite2/store/store.go b/lite2/store/store.go index 33b94821d..7ea6b9c6b 100644 --- a/lite2/store/store.go +++ b/lite2/store/store.go @@ -4,17 +4,17 @@ import "github.com/tendermint/tendermint/types" // Store is anything that can persistenly store headers. type Store interface { - // SaveSignedHeaderAndNextValidatorSet saves a SignedHeader (h: sh.Height) - // and a ValidatorSet (h: sh.Height+1). + // SaveSignedHeaderAndValidatorSet saves a SignedHeader (h: sh.Height) and a + // ValidatorSet (h: sh.Height). // // height must be > 0. - SaveSignedHeaderAndNextValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error + SaveSignedHeaderAndValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error - // DeleteSignedHeaderAndNextValidatorSet deletes SignedHeader (h: height) and - // ValidatorSet (h: height+1). + // DeleteSignedHeaderAndValidatorSet deletes SignedHeader (h: height) and + // ValidatorSet (h: height). // // height must be > 0. - DeleteSignedHeaderAndNextValidatorSet(height int64) error + DeleteSignedHeaderAndValidatorSet(height int64) error // SignedHeader returns the SignedHeader that corresponds to the given // height. @@ -45,4 +45,11 @@ type Store interface { // // height must be > 0 && <= LastSignedHeaderHeight. SignedHeaderAfter(height int64) (*types.SignedHeader, error) + + // Prune removes headers & the associated validator sets when Store reaches a + // defined size (number of header & validator set pairs). + Prune(size uint16) error + + // Size returns a number of currently existing header & validator set pairs. + Size() uint16 } 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 5491c8d91..422e0398e 100644 --- a/lite2/verifier.go +++ b/lite2/verifier.go @@ -10,6 +10,10 @@ 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. @@ -19,17 +23,17 @@ 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; -// c) trustLevel ([1/3, 1]) of trustedHeaderNextVals signed correctly -// (if not, ErrNewValSetCantBeTrusted is returned); -// d) more than 2/3 of 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) 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 +// (otherwise, ErrInvalidHeader is returned) // e) headers are non-adjacent. func VerifyNonAdjacent( chainID string, trustedHeader *types.SignedHeader, // height=X - trustedNextVals *types.ValidatorSet, // height=X+1 + trustedVals *types.ValidatorSet, // height=X or height=X+1 untrustedHeader *types.SignedHeader, // height=Y untrustedVals *types.ValidatorSet, // height=Y trustingPeriod time.Duration, @@ -45,11 +49,11 @@ 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. - err := trustedNextVals.VerifyCommitTrusting(chainID, untrustedHeader.Commit.BlockID, untrustedHeader.Height, + err := trustedVals.VerifyCommitTrusting(chainID, untrustedHeader.Commit.BlockID, untrustedHeader.Height, untrustedHeader.Commit, trustLevel) if err != nil { switch e := err.(type) { @@ -63,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 @@ -76,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 trustedHeaderNextVals.Hash() -// 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, @@ -99,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 @@ -114,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 @@ -124,7 +128,7 @@ func VerifyAdjacent( func Verify( chainID string, trustedHeader *types.SignedHeader, // height=X - trustedNextVals *types.ValidatorSet, // height=X+1 + trustedVals *types.ValidatorSet, // height=X or height=X+1 untrustedHeader *types.SignedHeader, // height=Y untrustedVals *types.ValidatorSet, // height=Y trustingPeriod time.Duration, @@ -132,7 +136,7 @@ func Verify( trustLevel tmmath.Fraction) error { if untrustedHeader.Height != trustedHeader.Height+1 { - return VerifyNonAdjacent(chainID, trustedHeader, trustedNextVals, untrustedHeader, untrustedVals, + return VerifyNonAdjacent(chainID, trustedHeader, trustedVals, untrustedHeader, untrustedVals, trustingPeriod, now, trustLevel) } @@ -162,16 +166,18 @@ func verifyNewHeaderAndVals( trustedHeader.Time) } - if !untrustedHeader.Time.Before(now) { - return errors.Errorf("new header has a time from the future %v (now: %v)", + if !untrustedHeader.Time.Before(now.Add(maxClockDrift)) { + return errors.Errorf("new header has a time from the future %v (now: %v; max clock drift: %v)", untrustedHeader.Time, - now) + now, + maxClockDrift) } 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, ) } @@ -195,3 +201,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 6e118d7ae..adc671516 100644 --- a/lite2/verifier_test.go +++ b/lite2/verifier_test.go @@ -75,8 +75,19 @@ func TestVerifyAdjacentHeaders(t *testing.T) { nil, "new header has a time from the future", }, - // 3/3 signed -> no error + // new header's time is from the future, but it's acceptable (< maxClockDrift) -> no error 4: { + keys.GenSignedHeader(chainID, nextHeight, + bTime.Add(2*time.Hour).Add(maxClockDrift).Add(-1*time.Millisecond), nil, vals, vals, + []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)), + vals, + 3 * time.Hour, + bTime.Add(2 * time.Hour), + nil, + "", + }, + // 3/3 signed -> no error + 5: { keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)), vals, @@ -86,7 +97,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) { "", }, // 2/3 signed -> no error - 5: { + 6: { keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 1, len(keys)), vals, @@ -96,17 +107,17 @@ func TestVerifyAdjacentHeaders(t *testing.T) { "", }, // 1/3 signed -> error - 6: { + 7: { keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), len(keys)-1, len(keys)), 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 - 7: { + 8: { keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, keys.ToValidators(10, 1), vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)), keys.ToValidators(10, 1), @@ -116,7 +127,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) { "to match those from new header", }, // vals are inconsistent with newHeader -> error - 8: { + 9: { keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)), keys.ToValidators(10, 1), @@ -126,7 +137,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) { "to match those that were supplied", }, // old header has expired -> error - 9: { + 10: { keys.GenSignedHeader(chainID, nextHeight, bTime.Add(1*time.Hour), nil, vals, vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)), keys.ToValidators(10, 1), @@ -216,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 diff --git a/node/node.go b/node/node.go index 104e572fb..e9cd02c30 100644 --- a/node/node.go +++ b/node/node.go @@ -6,7 +6,7 @@ import ( "fmt" "net" "net/http" - _ "net/http/pprof" + _ "net/http/pprof" // nolint: gosec // securely exposed on separate, optional port "os" "strings" "time" @@ -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/p2p/conn/secret_connection.go b/p2p/conn/secret_connection.go index d6b20186f..c14f1bb5c 100644 --- a/p2p/conn/secret_connection.go +++ b/p2p/conn/secret_connection.go @@ -5,7 +5,6 @@ import ( "crypto/cipher" crand "crypto/rand" "crypto/sha256" - "crypto/subtle" "encoding/binary" "io" "math" @@ -38,7 +37,6 @@ const ( var ( ErrSmallOrderRemotePubKey = errors.New("detected low order point from remote peer") - ErrSharedSecretIsZero = errors.New("shared secret is all zeroes") labelEphemeralLowerPublicKey = []byte("EPHEMERAL_LOWER_PUBLIC_KEY") labelEphemeralUpperPublicKey = []byte("EPHEMERAL_UPPER_PUBLIC_KEY") @@ -358,19 +356,14 @@ func deriveSecrets( // computeDHSecret computes a Diffie-Hellman shared secret key // from our own local private key and the other's public key. -// -// It returns an error if the computed shared secret is all zeroes. -func computeDHSecret(remPubKey, locPrivKey *[32]byte) (shrKey *[32]byte, err error) { - shrKey = new([32]byte) - curve25519.ScalarMult(shrKey, locPrivKey, remPubKey) - - // reject if the returned shared secret is all zeroes - // related to: https://github.com/tendermint/tendermint/issues/3010 - zero := new([32]byte) - if subtle.ConstantTimeCompare(shrKey[:], zero[:]) == 1 { - return nil, ErrSharedSecretIsZero +func computeDHSecret(remPubKey, locPrivKey *[32]byte) (*[32]byte, error) { + shrKey, err := curve25519.X25519(locPrivKey[:], remPubKey[:]) + if err != nil { + return nil, err } - return + var shrKeyArray [32]byte + copy(shrKeyArray[:], shrKey) + return &shrKeyArray, nil } func sort32(foo, bar *[32]byte) (lo, hi *[32]byte) { diff --git a/p2p/conn_set.go b/p2p/conn_set.go index d64622783..a889ad5e1 100644 --- a/p2p/conn_set.go +++ b/p2p/conn_set.go @@ -26,7 +26,7 @@ type connSet struct { } // NewConnSet returns a ConnSet implementation. -func NewConnSet() *connSet { +func NewConnSet() ConnSet { return &connSet{ conns: map[string]connSetItem{}, } diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index 4605ad260..25ac29a62 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -60,9 +60,12 @@ type AddrBook interface { // Mark address MarkGood(p2p.ID) MarkAttempt(*p2p.NetAddress) - MarkBad(*p2p.NetAddress) + MarkBad(*p2p.NetAddress, time.Duration) // Move peer to bad peers list + // Add bad peers back to addrBook + ReinstateBadPeers() IsGood(*p2p.NetAddress) bool + IsBanned(*p2p.NetAddress) bool // Send a selection of addresses to peers GetSelection() []*p2p.NetAddress @@ -88,6 +91,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 @@ -110,12 +114,13 @@ func newHashKey() []byte { // NewAddrBook creates a new address book. // Use Start to begin processing asynchronous address updates. -func NewAddrBook(filePath string, routabilityStrict bool) *addrBook { +func NewAddrBook(filePath string, routabilityStrict bool) AddrBook { am := &addrBook{ rand: tmrand.NewRand(), 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, hashKey: newHashKey(), @@ -214,12 +219,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 @@ -231,6 +231,15 @@ 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() + _, ok := a.badPeers[addr.ID] + a.mtx.Unlock() + + return ok +} + // HasAddress returns true if the address is in the book. func (a *addrBook) HasAddress(addr *p2p.NetAddress) bool { a.mtx.Lock() @@ -333,10 +342,28 @@ 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 -func (a *addrBook) MarkBad(addr *p2p.NetAddress) { - a.RemoveAddress(addr) +// MarkBad implements AddrBook. Kicks address out from book, places +// the address in the badPeers pool. +func (a *addrBook) MarkBad(addr *p2p.NetAddress, banTime time.Duration) { + a.mtx.Lock() + defer a.mtx.Unlock() + + if a.addBadPeer(addr, banTime) { + a.removeAddress(addr) + } +} + +func (a *addrBook) ReinstateBadPeers() { + a.mtx.Lock() + defer a.mtx.Unlock() + for _, ka := range a.badPeers { + if !ka.isBanned() { + bucket := a.calcNewBucket(ka.Addr, ka.Src) + a.addToNewBucket(ka, bucket) + delete(a.badPeers, ka.ID()) + a.Logger.Info("Reinstated address", "addr", ka.Addr) + } + } } // GetSelection implements AddrBook. @@ -601,6 +628,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} } @@ -744,6 +775,32 @@ func (a *addrBook) moveToOld(ka *knownAddress) error { return nil } +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, banTime time.Duration) bool { + // check it exists in addrbook + ka := a.addrLookup[addr.ID] + // check address is not already there + if ka == nil { + 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 +} + //--------------------------------------------------------------------- // calculate bucket placements diff --git a/p2p/pex/addrbook_test.go b/p2p/pex/addrbook_test.go index 0bcda730e..e5a226daf 100644 --- a/p2p/pex/addrbook_test.go +++ b/p2p/pex/addrbook_test.go @@ -7,16 +7,18 @@ import ( "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" ) +// FIXME These tests should not rely on .(*addrBook) assertions + func TestAddrBookPickAddress(t *testing.T) { fname := createTempFileName("addrbook_test") defer deleteTempFile(fname) @@ -60,13 +62,13 @@ func TestAddrBookSaveLoad(t *testing.T) { // 0 addresses book := NewAddrBook(fname, true) book.SetLogger(log.TestingLogger()) - book.saveToFile(fname) + book.Save() 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) @@ -76,11 +78,11 @@ func TestAddrBookSaveLoad(t *testing.T) { } assert.Equal(t, 100, book.Size()) - book.saveToFile(fname) + book.Save() book = NewAddrBook(fname, true) book.SetLogger(log.TestingLogger()) - book.loadFromFile(fname) + book.Start() assert.Equal(t, 100, book.Size()) } @@ -98,12 +100,8 @@ func TestAddrBookLookup(t *testing.T) { 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) } } @@ -345,7 +343,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)))) @@ -398,6 +396,33 @@ 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)) + assert.True(t, book.IsBanned(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) @@ -575,7 +600,7 @@ func deleteTempFile(fname string) { func createAddrBookWithMOldAndNNewAddrs(t *testing.T, nOld, nNew int) (book *addrBook, fname string) { fname = createTempFileName("addrbook_test") - book = NewAddrBook(fname, true) + book = NewAddrBook(fname, true).(*addrBook) book.SetLogger(log.TestingLogger()) assert.Zero(t, book.Size()) diff --git a/p2p/pex/errors.go b/p2p/pex/errors.go index 911389a9e..1fc54ea50 100644 --- a/p2p/pex/errors.go +++ b/p2p/pex/errors.go @@ -63,3 +63,12 @@ type ErrAddrBookInvalidAddr struct { func (err ErrAddrBookInvalidAddr) Error() string { return fmt.Sprintf("Cannot add invalid address %v: %v", err.Addr, err.AddrErr) } + +// ErrAddressBanned is thrown when the address has been banned and therefore cannot be used +type ErrAddressBanned struct { + Addr *p2p.NetAddress +} + +func (err ErrAddressBanned) Error() string { + return fmt.Sprintf("Address: %v is currently banned", err.Addr) +} diff --git a/p2p/pex/known_address.go b/p2p/pex/known_address.go index af40d6ff0..e98a9e97e 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,16 @@ func (ka *knownAddress) markGood() { ka.LastSuccess = now } +func (ka *knownAddress) ban(banTime time.Duration) { + if ka.LastBanTime.Before(time.Now().Add(banTime)) { + ka.LastBanTime = time.Now().Add(banTime) + } +} + +func (ka *knownAddress) isBanned() bool { + return ka.LastBanTime.After(time.Now()) +} + func (ka *knownAddress) addBucketRef(bucketIdx int) int { for _, bucket := range ka.Buckets { if bucket == bucketIdx { diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index 3a3d2d7de..b0a1c02bb 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" @@ -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 { @@ -494,6 +497,12 @@ func (r *Reactor) ensurePeers() { } if r.book.NeedMoreAddrs() { + // 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) @@ -525,11 +534,7 @@ 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) + r.book.MarkBad(addr, defaultBanTime) return errMaxAttemptsToDial{} } @@ -741,7 +746,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) } diff --git a/p2p/pex/pex_reactor_test.go b/p2p/pex/pex_reactor_test.go index 5e174765a..4cddf6352 100644 --- a/p2p/pex/pex_reactor_test.go +++ b/p2p/pex/pex_reactor_test.go @@ -74,7 +74,7 @@ func TestPEXReactorRunning(t *testing.T) { require.Nil(t, err) defer os.RemoveAll(dir) // nolint: errcheck - books := make([]*addrBook, N) + books := make([]AddrBook, N) logger := log.TestingLogger() // create switches @@ -404,7 +404,7 @@ func TestPEXReactorSeedModeFlushStop(t *testing.T) { require.Nil(t, err) defer os.RemoveAll(dir) // nolint: errcheck - books := make([]*addrBook, N) + books := make([]AddrBook, N) logger := log.TestingLogger() // create switches @@ -631,7 +631,7 @@ func testCreatePeerWithSeed(dir string, id int, seed *p2p.Switch) *p2p.Switch { return testCreatePeerWithConfig(dir, id, conf) } -func createReactor(conf *ReactorConfig) (r *Reactor, book *addrBook) { +func createReactor(conf *ReactorConfig) (r *Reactor, book AddrBook) { // directory to store address book dir, err := ioutil.TempDir("", "pex_reactor") if err != nil { @@ -645,8 +645,9 @@ func createReactor(conf *ReactorConfig) (r *Reactor, book *addrBook) { return } -func teardownReactor(book *addrBook) { - err := os.RemoveAll(filepath.Dir(book.FilePath())) +func teardownReactor(book AddrBook) { + // FIXME Shouldn't rely on .(*addrBook) assertion + err := os.RemoveAll(filepath.Dir(book.(*addrBook).FilePath())) if err != nil { panic(err) } diff --git a/p2p/switch.go b/p2p/switch.go index bc675cdb8..3f9325808 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -584,10 +584,6 @@ func (sw *Switch) AddUnconditionalPeerIDs(ids []string) error { return nil } -func (sw *Switch) isPeerPersistentFn() func(*NetAddress) bool { - return sw.IsPeerPersistent -} - func (sw *Switch) IsPeerPersistent(na *NetAddress) bool { for _, pa := range sw.persistentPeersAddrs { if pa.Equals(na) { diff --git a/p2p/test_util.go b/p2p/test_util.go index f592f5f2f..045dc4c7e 100644 --- a/p2p/test_util.go +++ b/p2p/test_util.go @@ -34,7 +34,7 @@ func AddPeerToSwitchPeerSet(sw *Switch, peer Peer) { sw.peers.Add(peer) } -func CreateRandomPeer(outbound bool) *peer { +func CreateRandomPeer(outbound bool) Peer { addr, netAddr := CreateRoutableAddr() p := &peer{ peerConn: peerConn{ 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/privval/socket_listeners.go b/privval/socket_listeners.go index f4d875e71..0e12e4268 100644 --- a/privval/socket_listeners.go +++ b/privval/socket_listeners.go @@ -23,26 +23,26 @@ type timeoutError interface { // TCP Listener // TCPListenerOption sets an optional parameter on the tcpListener. -type TCPListenerOption func(*tcpListener) +type TCPListenerOption func(*TCPListener) // TCPListenerTimeoutAccept sets the timeout for the listener. // A zero time value disables the timeout. func TCPListenerTimeoutAccept(timeout time.Duration) TCPListenerOption { - return func(tl *tcpListener) { tl.timeoutAccept = timeout } + return func(tl *TCPListener) { tl.timeoutAccept = timeout } } // TCPListenerTimeoutReadWrite sets the read and write timeout for connections // from external signing processes. func TCPListenerTimeoutReadWrite(timeout time.Duration) TCPListenerOption { - return func(tl *tcpListener) { tl.timeoutReadWrite = timeout } + return func(tl *TCPListener) { tl.timeoutReadWrite = timeout } } // tcpListener implements net.Listener. -var _ net.Listener = (*tcpListener)(nil) +var _ net.Listener = (*TCPListener)(nil) -// tcpListener wraps a *net.TCPListener to standardise protocol timeouts +// TCPListener wraps a *net.TCPListener to standardise protocol timeouts // and potentially other tuning parameters. It also returns encrypted connections. -type tcpListener struct { +type TCPListener struct { *net.TCPListener secretConnKey ed25519.PrivKeyEd25519 @@ -53,8 +53,8 @@ type tcpListener struct { // NewTCPListener returns a listener that accepts authenticated encrypted connections // using the given secretConnKey and the default timeout values. -func NewTCPListener(ln net.Listener, secretConnKey ed25519.PrivKeyEd25519) *tcpListener { - return &tcpListener{ +func NewTCPListener(ln net.Listener, secretConnKey ed25519.PrivKeyEd25519) *TCPListener { + return &TCPListener{ TCPListener: ln.(*net.TCPListener), secretConnKey: secretConnKey, timeoutAccept: time.Second * defaultTimeoutAcceptSeconds, @@ -63,7 +63,7 @@ func NewTCPListener(ln net.Listener, secretConnKey ed25519.PrivKeyEd25519) *tcpL } // Accept implements net.Listener. -func (ln *tcpListener) Accept() (net.Conn, error) { +func (ln *TCPListener) Accept() (net.Conn, error) { deadline := time.Now().Add(ln.timeoutAccept) err := ln.SetDeadline(deadline) if err != nil { @@ -89,25 +89,25 @@ func (ln *tcpListener) Accept() (net.Conn, error) { // Unix Listener // unixListener implements net.Listener. -var _ net.Listener = (*unixListener)(nil) +var _ net.Listener = (*UnixListener)(nil) -type UnixListenerOption func(*unixListener) +type UnixListenerOption func(*UnixListener) // UnixListenerTimeoutAccept sets the timeout for the listener. // A zero time value disables the timeout. func UnixListenerTimeoutAccept(timeout time.Duration) UnixListenerOption { - return func(ul *unixListener) { ul.timeoutAccept = timeout } + return func(ul *UnixListener) { ul.timeoutAccept = timeout } } // UnixListenerTimeoutReadWrite sets the read and write timeout for connections // from external signing processes. func UnixListenerTimeoutReadWrite(timeout time.Duration) UnixListenerOption { - return func(ul *unixListener) { ul.timeoutReadWrite = timeout } + return func(ul *UnixListener) { ul.timeoutReadWrite = timeout } } -// unixListener wraps a *net.UnixListener to standardise protocol timeouts +// UnixListener wraps a *net.UnixListener to standardise protocol timeouts // and potentially other tuning parameters. It returns unencrypted connections. -type unixListener struct { +type UnixListener struct { *net.UnixListener timeoutAccept time.Duration @@ -116,8 +116,8 @@ type unixListener struct { // NewUnixListener returns a listener that accepts unencrypted connections // using the default timeout values. -func NewUnixListener(ln net.Listener) *unixListener { - return &unixListener{ +func NewUnixListener(ln net.Listener) *UnixListener { + return &UnixListener{ UnixListener: ln.(*net.UnixListener), timeoutAccept: time.Second * defaultTimeoutAcceptSeconds, timeoutReadWrite: time.Second * defaultTimeoutReadWriteSeconds, @@ -125,7 +125,7 @@ func NewUnixListener(ln net.Listener) *unixListener { } // Accept implements net.Listener. -func (ln *unixListener) Accept() (net.Conn, error) { +func (ln *UnixListener) Accept() (net.Conn, error) { deadline := time.Now().Add(ln.timeoutAccept) err := ln.SetDeadline(deadline) if err != nil { diff --git a/proxy/app_conn.go b/proxy/app_conn.go index 1698ab52f..066d17295 100644 --- a/proxy/app_conn.go +++ b/proxy/app_conn.go @@ -47,7 +47,7 @@ type appConnConsensus struct { appConn abcicli.Client } -func NewAppConnConsensus(appConn abcicli.Client) *appConnConsensus { +func NewAppConnConsensus(appConn abcicli.Client) AppConnConsensus { return &appConnConsensus{ appConn: appConn, } @@ -88,7 +88,7 @@ type appConnMempool struct { appConn abcicli.Client } -func NewAppConnMempool(appConn abcicli.Client) *appConnMempool { +func NewAppConnMempool(appConn abcicli.Client) AppConnMempool { return &appConnMempool{ appConn: appConn, } @@ -121,7 +121,7 @@ type appConnQuery struct { appConn abcicli.Client } -func NewAppConnQuery(appConn abcicli.Client) *appConnQuery { +func NewAppConnQuery(appConn abcicli.Client) AppConnQuery { return &appConnQuery{ appConn: appConn, } diff --git a/proxy/multi_app_conn.go b/proxy/multi_app_conn.go index 44ff242b1..364114115 100644 --- a/proxy/multi_app_conn.go +++ b/proxy/multi_app_conn.go @@ -30,15 +30,15 @@ func NewAppConns(clientCreator ClientCreator) AppConns { type multiAppConn struct { service.BaseService - mempoolConn *appConnMempool - consensusConn *appConnConsensus - queryConn *appConnQuery + mempoolConn AppConnMempool + consensusConn AppConnConsensus + queryConn AppConnQuery clientCreator ClientCreator } // Make all necessary abci connections to the application -func NewMultiAppConn(clientCreator ClientCreator) *multiAppConn { +func NewMultiAppConn(clientCreator ClientCreator) AppConns { multiAppConn := &multiAppConn{ clientCreator: clientCreator, } diff --git a/rpc/client/httpclient.go b/rpc/client/httpclient.go index c844113c3..98875c91e 100644 --- a/rpc/client/httpclient.go +++ b/rpc/client/httpclient.go @@ -96,6 +96,16 @@ func NewHTTP(remote, wsEndpoint string) (*HTTP, error) { return NewHTTPWithClient(remote, wsEndpoint, httpClient) } +// Create timeout enabled http client +func NewHTTPWithTimeout(remote, wsEndpoint string, timeout uint) (*HTTP, error) { + httpClient, err := rpcclient.DefaultHTTPClient(remote) + if err != nil { + return nil, err + } + httpClient.Timeout = time.Duration(timeout) * time.Second + return NewHTTPWithClient(remote, wsEndpoint, httpClient) +} + // NewHTTPWithClient allows for setting a custom http client (See NewHTTP). // An error is returned on invalid remote. The function panics when remote is nil. func NewHTTPWithClient(remote, wsEndpoint string, client *http.Client) (*HTTP, error) { diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 90612d5e0..9196bead0 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -39,6 +39,16 @@ func getHTTPClient() *client.HTTP { return c } +func getHTTPClientWithTimeout(timeout uint) *client.HTTP { + rpcAddr := rpctest.GetConfig().RPC.ListenAddress + c, err := client.NewHTTPWithTimeout(rpcAddr, "/websocket", timeout) + if err != nil { + panic(err) + } + c.SetLogger(log.TestingLogger()) + return c +} + func getLocalClient() *client.Local { return client.NewLocal(node) } @@ -414,6 +424,18 @@ func TestTx(t *testing.T) { } } +func TestTxSearchWithTimeout(t *testing.T) { + // Get a client with a time-out of 10 secs. + timeoutClient := getHTTPClientWithTimeout(10) + + // query using a compositeKey (see kvstore application) + result, err := timeoutClient.TxSearch("app.creator='Cosmoshi Netowoko'", false, 1, 30, "asc") + require.Nil(t, err) + if len(result.Txs) == 0 { + t.Fatal("expected a lot of transactions") + } +} + func TestTxSearch(t *testing.T) { c := getHTTPClient() 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 2a4d042fc..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,11 +38,13 @@ 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 } + // Capture the current ID, since it can change in the future. + subscriptionID := ctx.JSONReq.ID go func() { for { select { @@ -46,7 +53,7 @@ func Subscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, er ctx.WSConn.TryWriteRPCResponse( rpctypes.NewRPCSuccessResponse( ctx.WSConn.Codec(), - ctx.JSONReq.ID, + subscriptionID, resultEvent, )) case <-sub.Cancelled(): @@ -59,7 +66,7 @@ func Subscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, er } ctx.WSConn.TryWriteRPCResponse( rpctypes.RPCServerError( - ctx.JSONReq.ID, + subscriptionID, fmt.Errorf("subscription was cancelled (reason: %s)", reason), )) } diff --git a/rpc/core/routes.go b/rpc/core/routes.go index 42a57456a..aa0403f87 100644 --- a/rpc/core/routes.go +++ b/rpc/core/routes.go @@ -6,6 +6,7 @@ import ( // TODO: better system than "unsafe" prefix // NOTE: Amino is registered in rpc/core/types/codec.go. + var Routes = map[string]*rpc.RPCFunc{ // subscribe/unsubscribe are reserved for websocket events. "subscribe": rpc.NewWSRPCFunc(Subscribe, "query"), diff --git a/rpc/core/tx.go b/rpc/core/tx.go index f894424b7..e7e2582f6 100644 --- a/rpc/core/tx.go +++ b/rpc/core/tx.go @@ -67,7 +67,7 @@ func TxSearch(ctx *rpctypes.Context, query string, prove bool, page, perPage int return nil, err } - results, err := txIndexer.Search(q) + results, err := txIndexer.Search(ctx.Context(), q) if err != nil { return nil, err } diff --git a/rpc/lib/server/ws_handler.go b/rpc/lib/server/ws_handler.go index 07f424b48..e7048db79 100644 --- a/rpc/lib/server/ws_handler.go +++ b/rpc/lib/server/ws_handler.go @@ -92,7 +92,7 @@ func (wm *WebsocketManager) WebsocketHandler(w http.ResponseWriter, r *http.Requ }() // register connection - con := NewWSConnection(wsConn, wm.funcMap, wm.cdc, wm.wsConnOptions...) + con := newWSConnection(wsConn, wm.funcMap, wm.cdc, wm.wsConnOptions...) con.SetLogger(wm.logger.With("remote", wsConn.RemoteAddr())) wm.logger.Info("New websocket connection", "remote", con.remoteAddr) err = con.Start() // BLOCKING @@ -154,7 +154,7 @@ type wsConnection struct { // description of how to configure ping period and pong wait time. NOTE: if the // write buffer is full, pongs may be dropped, which may cause clients to // disconnect. see https://github.com/gorilla/websocket/issues/97 -func NewWSConnection( +func newWSConnection( baseConn *websocket.Conn, funcMap map[string]*RPCFunc, cdc *amino.Codec, diff --git a/rpc/swagger/swagger.yaml b/rpc/swagger/swagger.yaml index aa4d837ba..40b6e0169 100644 --- a/rpc/swagger/swagger.yaml +++ b/rpc/swagger/swagger.yaml @@ -7,7 +7,7 @@ info: name: Apache 2.0 url: https://github.com/tendermint/tendermint/blob/master/LICENSE servers: - - url: https://stargate.cosmos.network:26657 + - url: https://rpc.cosmos.network description: Cosmos mainnet node to interact with the Tendermint RPC - url: http://localhost:26657 description: Interact with the Tendermint RPC locally on your device 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/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/state/helpers_test.go b/state/helpers_test.go index fec965016..f8758f987 100644 --- a/state/helpers_test.go +++ b/state/helpers_test.go @@ -82,7 +82,7 @@ func makeValidCommit( sigs := make([]types.CommitSig, 0) for i := 0; i < vals.Size(); i++ { _, val := vals.GetByIndex(i) - vote, err := types.MakeVote(height, blockID, vals, privVals[val.Address.String()], chainID) + vote, err := types.MakeVote(height, blockID, vals, privVals[val.Address.String()], chainID, time.Now()) if err != nil { return nil, err } 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. 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/txindex/indexer.go b/state/txindex/indexer.go index c66f4322c..5f7ee7544 100644 --- a/state/txindex/indexer.go +++ b/state/txindex/indexer.go @@ -1,6 +1,7 @@ package txindex import ( + "context" "errors" "github.com/tendermint/tendermint/libs/pubsub/query" @@ -21,7 +22,7 @@ type TxIndexer interface { Get(hash []byte) (*types.TxResult, error) // Search allows you to query for transactions. - Search(q *query.Query) ([]*types.TxResult, error) + Search(ctx context.Context, q *query.Query) ([]*types.TxResult, error) } //---------------------------------------------------- diff --git a/state/txindex/kv/kv.go b/state/txindex/kv/kv.go index 5fcd5ab73..c5ba99ca3 100644 --- a/state/txindex/kv/kv.go +++ b/state/txindex/kv/kv.go @@ -2,6 +2,7 @@ package kv import ( "bytes" + "context" "encoding/hex" "fmt" "strconv" @@ -167,7 +168,18 @@ func (txi *TxIndex) indexEvents(result *types.TxResult, hash []byte, store dbm.S // better for the client to provide both lower and upper bounds, so we are not // performing a full scan. Results from querying indexes are then intersected // and returned to the caller, in no particular order. -func (txi *TxIndex) Search(q *query.Query) ([]*types.TxResult, error) { +// +// Search will exit early and return any result fetched so far, +// when a message is received on the context chan. +func (txi *TxIndex) Search(ctx context.Context, q *query.Query) ([]*types.TxResult, error) { + // Potentially exit early. + select { + case <-ctx.Done(): + results := make([]*types.TxResult, 0) + return results, nil + default: + } + var hashesInitialized bool filteredHashes := make(map[string][]byte) @@ -205,7 +217,7 @@ func (txi *TxIndex) Search(q *query.Query) ([]*types.TxResult, error) { for _, r := range ranges { if !hashesInitialized { - filteredHashes = txi.matchRange(r, startKey(r.key), filteredHashes, true) + filteredHashes = txi.matchRange(ctx, r, startKey(r.key), filteredHashes, true) hashesInitialized = true // Ignore any remaining conditions if the first condition resulted @@ -214,7 +226,7 @@ func (txi *TxIndex) Search(q *query.Query) ([]*types.TxResult, error) { break } } else { - filteredHashes = txi.matchRange(r, startKey(r.key), filteredHashes, false) + filteredHashes = txi.matchRange(ctx, r, startKey(r.key), filteredHashes, false) } } } @@ -229,7 +241,7 @@ func (txi *TxIndex) Search(q *query.Query) ([]*types.TxResult, error) { } if !hashesInitialized { - filteredHashes = txi.match(c, startKeyForCondition(c, height), filteredHashes, true) + filteredHashes = txi.match(ctx, c, startKeyForCondition(c, height), filteredHashes, true) hashesInitialized = true // Ignore any remaining conditions if the first condition resulted @@ -238,7 +250,7 @@ func (txi *TxIndex) Search(q *query.Query) ([]*types.TxResult, error) { break } } else { - filteredHashes = txi.match(c, startKeyForCondition(c, height), filteredHashes, false) + filteredHashes = txi.match(ctx, c, startKeyForCondition(c, height), filteredHashes, false) } } @@ -249,6 +261,13 @@ func (txi *TxIndex) Search(q *query.Query) ([]*types.TxResult, error) { return nil, errors.Wrapf(err, "failed to get Tx{%X}", h) } results = append(results, res) + + // Potentially exit early. + select { + case <-ctx.Done(): + break + default: + } } return results, nil @@ -374,6 +393,7 @@ func isRangeOperation(op query.Operator) bool { // // NOTE: filteredHashes may be empty if no previous condition has matched. func (txi *TxIndex) match( + ctx context.Context, c query.Condition, startKeyBz []byte, filteredHashes map[string][]byte, @@ -397,6 +417,13 @@ func (txi *TxIndex) match( for ; it.Valid(); it.Next() { tmpHashes[string(it.Value())] = it.Value() + + // Potentially exit early. + select { + case <-ctx.Done(): + break + default: + } } case c.Op == query.OpContains: @@ -417,6 +444,13 @@ func (txi *TxIndex) match( if strings.Contains(extractValueFromKey(it.Key()), c.Operand.(string)) { tmpHashes[string(it.Value())] = it.Value() } + + // Potentially exit early. + select { + case <-ctx.Done(): + break + default: + } } default: panic("other operators should be handled already") @@ -438,6 +472,13 @@ func (txi *TxIndex) match( for k := range filteredHashes { if tmpHashes[k] == nil { delete(filteredHashes, k) + + // Potentially exit early. + select { + case <-ctx.Done(): + break + default: + } } } @@ -450,6 +491,7 @@ func (txi *TxIndex) match( // // NOTE: filteredHashes may be empty if no previous condition has matched. func (txi *TxIndex) matchRange( + ctx context.Context, r queryRange, startKey []byte, filteredHashes map[string][]byte, @@ -503,6 +545,13 @@ LOOP: // break // } } + + // Potentially exit early. + select { + case <-ctx.Done(): + break + default: + } } if len(tmpHashes) == 0 || firstRun { @@ -521,6 +570,13 @@ LOOP: for k := range filteredHashes { if tmpHashes[k] == nil { delete(filteredHashes, k) + + // Potentially exit early. + select { + case <-ctx.Done(): + break + default: + } } } diff --git a/state/txindex/kv/kv_bench_test.go b/state/txindex/kv/kv_bench_test.go index 3e3bdced3..34d770040 100644 --- a/state/txindex/kv/kv_bench_test.go +++ b/state/txindex/kv/kv_bench_test.go @@ -1,6 +1,7 @@ package kv import ( + "context" "crypto/rand" "fmt" "io/ioutil" @@ -64,8 +65,10 @@ func BenchmarkTxSearch(b *testing.B) { b.ResetTimer() + ctx := context.Background() + for i := 0; i < b.N; i++ { - if _, err := indexer.Search(txQuery); err != nil { + if _, err := indexer.Search(ctx, txQuery); err != nil { b.Errorf("failed to query for txs: %s", err) } } diff --git a/state/txindex/kv/kv_test.go b/state/txindex/kv/kv_test.go index efb767f21..fcd2eba13 100644 --- a/state/txindex/kv/kv_test.go +++ b/state/txindex/kv/kv_test.go @@ -1,6 +1,7 @@ package kv import ( + "context" "fmt" "io/ioutil" "os" @@ -118,10 +119,12 @@ func TestTxSearch(t *testing.T) { {"account.number CONTAINS 'Iv'", 0}, } + ctx := context.Background() + for _, tc := range testCases { tc := tc t.Run(tc.q, func(t *testing.T) { - results, err := indexer.Search(query.MustParse(tc.q)) + results, err := indexer.Search(ctx, query.MustParse(tc.q)) assert.NoError(t, err) assert.Len(t, results, tc.resultsLength) @@ -132,6 +135,25 @@ func TestTxSearch(t *testing.T) { } } +func TestTxSearchWithCancelation(t *testing.T) { + allowedKeys := []string{"account.number", "account.owner", "account.date"} + indexer := NewTxIndex(db.NewMemDB(), IndexEvents(allowedKeys)) + + txResult := txResultWithEvents([]abci.Event{ + {Type: "account", Attributes: []kv.Pair{{Key: []byte("number"), Value: []byte("1")}}}, + {Type: "account", Attributes: []kv.Pair{{Key: []byte("owner"), Value: []byte("Ivan")}}}, + {Type: "", Attributes: []kv.Pair{{Key: []byte("not_allowed"), Value: []byte("Vlad")}}}, + }) + err := indexer.Index(txResult) + require.NoError(t, err) + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + results, err := indexer.Search(ctx, query.MustParse("account.number = 1")) + assert.NoError(t, err) + assert.Empty(t, results) +} + func TestTxSearchDeprecatedIndexing(t *testing.T) { allowedKeys := []string{"account.number", "sender"} indexer := NewTxIndex(db.NewMemDB(), IndexEvents(allowedKeys)) @@ -192,10 +214,12 @@ func TestTxSearchDeprecatedIndexing(t *testing.T) { {"sender = 'addr1'", []*types.TxResult{txResult2}}, } + ctx := context.Background() + for _, tc := range testCases { tc := tc t.Run(tc.q, func(t *testing.T) { - results, err := indexer.Search(query.MustParse(tc.q)) + results, err := indexer.Search(ctx, query.MustParse(tc.q)) require.NoError(t, err) require.Equal(t, results, tc.results) }) @@ -214,7 +238,9 @@ func TestTxSearchOneTxWithMultipleSameTagsButDifferentValues(t *testing.T) { err := indexer.Index(txResult) require.NoError(t, err) - results, err := indexer.Search(query.MustParse("account.number >= 1")) + ctx := context.Background() + + results, err := indexer.Search(ctx, query.MustParse("account.number >= 1")) assert.NoError(t, err) assert.Len(t, results, 1) @@ -268,7 +294,9 @@ func TestTxSearchMultipleTxs(t *testing.T) { err = indexer.Index(txResult4) require.NoError(t, err) - results, err := indexer.Search(query.MustParse("account.number >= 1")) + ctx := context.Background() + + results, err := indexer.Search(ctx, query.MustParse("account.number >= 1")) assert.NoError(t, err) require.Len(t, results, 3) diff --git a/state/txindex/null/null.go b/state/txindex/null/null.go index ae496a851..1ae7f7942 100644 --- a/state/txindex/null/null.go +++ b/state/txindex/null/null.go @@ -1,6 +1,7 @@ package null import ( + "context" "errors" "github.com/tendermint/tendermint/libs/pubsub/query" @@ -28,6 +29,6 @@ func (txi *TxIndex) Index(result *types.TxResult) error { return nil } -func (txi *TxIndex) Search(q *query.Query) ([]*types.TxResult, error) { +func (txi *TxIndex) Search(ctx context.Context, q *query.Query) ([]*types.TxResult, error) { return []*types.TxResult{}, nil } diff --git a/state/validation_test.go b/state/validation_test.go index 9346a3bdd..d7d875f41 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -117,6 +117,7 @@ func TestValidateBlockCommit(t *testing.T) { state.Validators, privVals[proposerAddr.String()], chainID, + time.Now(), ) require.NoError(t, err, "height %d", height) wrongHeightCommit := types.NewCommit( @@ -162,10 +163,20 @@ func TestValidateBlockCommit(t *testing.T) { /* wrongSigsCommit is fine except for the extra bad precommit */ - goodVote, err := types.MakeVote(height, blockID, state.Validators, privVals[proposerAddr.String()], chainID) + goodVote, err := types.MakeVote(height, + blockID, + state.Validators, + privVals[proposerAddr.String()], + chainID, + 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/test/p2p/circleci.sh b/test/p2p/circleci.sh index 92869ff45..fe8a972bd 100644 --- a/test/p2p/circleci.sh +++ b/test/p2p/circleci.sh @@ -1,21 +1,26 @@ #! /bin/bash set -eux +# Take IP version as parameter +IPV="${1:-4}" + # Get the directory of where this script is. SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" # Enable IPv6 support in Docker daemon -echo -echo "* [$(date +"%T")] enabling IPv6 stack in Docker daemon" -cat <<'EOF' | sudo tee /etc/docker/daemon.json +if [[ "$IPV" == "6" ]]; then + echo + echo "* [$(date +"%T")] enabling IPv6 stack in Docker daemon" + cat <<'EOF' | sudo tee /etc/docker/daemon.json { - "ipv6": true, - "fixed-cidr-v6": "2001:db8:1::/64" + "ipv6": true, + "fixed-cidr-v6": "2001:db8:1::/64" } EOF -sudo service docker restart + sudo service docker restart +fi LOGS_DIR="$DIR/logs" echo @@ -42,12 +47,8 @@ if [[ "$SKIP_BUILD" == "" ]]; then fi echo -echo "* [$(date +"%T")] running p2p tests on a local docker network" -bash "$DIR/../p2p/test.sh" tester - -echo -echo "* [$(date +"%T")] running IPv6 p2p tests on a local docker network" -bash "$DIR/../p2p/test.sh" tester 6 +echo "* [$(date +"%T")] running IPv$IPV p2p tests on a local docker network" +bash "$DIR/../p2p/test.sh" tester $IPV echo echo "* [$(date +"%T")] copying log files out of docker container into $LOGS_DIR" 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" 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/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/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.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 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'. 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.go b/types/block.go index 4f61a2bc9..61ae6719b 100644 --- a/types/block.go +++ b/types/block.go @@ -752,7 +752,6 @@ type SignedHeader struct { // ValidateBasic does basic consistency checks and makes sure the header // and commit are consistent. -// // NOTE: This does not actually check the cryptographic signatures. Make // sure to use a Verifier to validate the signatures actually provide a // significantly strong proof for this header's validity. diff --git a/types/block_test.go b/types/block_test.go index 5f2f2e8bc..fe3da920c 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -37,7 +37,7 @@ func TestBlockAddEvidence(t *testing.T) { h := int64(3) voteSet, valSet, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1) - commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals) + commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now()) require.NoError(t, err) ev := NewMockEvidence(h, time.Now(), 0, valSet.Validators[0].Address) @@ -57,7 +57,7 @@ func TestBlockValidateBasic(t *testing.T) { h := int64(3) voteSet, valSet, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1) - commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals) + commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now()) require.NoError(t, err) ev := NewMockEvidence(h, time.Now(), 0, valSet.Validators[0].Address) @@ -120,7 +120,7 @@ func TestBlockMakePartSetWithEvidence(t *testing.T) { h := int64(3) voteSet, valSet, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1) - commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals) + commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now()) require.NoError(t, err) ev := NewMockEvidence(h, time.Now(), 0, valSet.Validators[0].Address) @@ -137,7 +137,7 @@ func TestBlockHashesTo(t *testing.T) { lastID := makeBlockIDRandom() h := int64(3) voteSet, valSet, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1) - commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals) + commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now()) require.NoError(t, err) ev := NewMockEvidence(h, time.Now(), 0, valSet.Validators[0].Address) @@ -210,7 +210,7 @@ func TestCommit(t *testing.T) { lastID := makeBlockIDRandom() h := int64(3) voteSet, _, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1) - commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals) + commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now()) require.NoError(t, err) assert.Equal(t, h-1, commit.Height) @@ -241,7 +241,7 @@ func TestCommitValidateBasic(t *testing.T) { for _, tc := range testCases { tc := tc t.Run(tc.testName, func(t *testing.T) { - com := randCommit() + com := randCommit(time.Now()) tc.malleateCommit(com) assert.Equal(t, tc.expectErr, com.ValidateBasic() != nil, "Validate Basic had an unexpected result") }) @@ -348,11 +348,11 @@ func TestMaxHeaderBytes(t *testing.T) { assert.EqualValues(t, MaxHeaderBytes, int64(len(bz))) } -func randCommit() *Commit { +func randCommit(now time.Time) *Commit { lastID := makeBlockIDRandom() h := int64(3) voteSet, _, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1) - commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals) + commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, now) if err != nil { panic(err) } @@ -431,7 +431,7 @@ func TestCommitToVoteSet(t *testing.T) { h := int64(3) voteSet, valSet, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1) - commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals) + commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now()) assert.NoError(t, err) chainID := voteSet.ChainID() @@ -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, @@ -506,7 +507,7 @@ func TestCommitToVoteSetWithVotesForNilBlock(t *testing.T) { } func TestSignedHeaderValidateBasic(t *testing.T) { - commit := randCommit() + commit := randCommit(time.Now()) chainID := "𠜎" timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC) h := Header{ 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 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 45d0a67b5..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) { @@ -44,35 +52,35 @@ func (pvs PrivValidatorsByAddress) Swap(i, j int) { // MockPV implements PrivValidator without any safety or persistence. // Only use it for testing. type MockPV struct { - privKey crypto.PrivKey + PrivKey crypto.PrivKey breakProposalSigning bool breakVoteSigning bool } -func NewMockPV() *MockPV { - return &MockPV{ed25519.GenPrivKey(), false, false} +func NewMockPV() MockPV { + return MockPV{ed25519.GenPrivKey(), false, false} } // NewMockPVWithParams allows one to create a MockPV instance, but with finer // grained control over the operation of the mock validator. This is useful for // mocking test failures. -func NewMockPVWithParams(privKey crypto.PrivKey, breakProposalSigning, breakVoteSigning bool) *MockPV { - return &MockPV{privKey, breakProposalSigning, breakVoteSigning} +func NewMockPVWithParams(privKey crypto.PrivKey, breakProposalSigning, breakVoteSigning bool) MockPV { + return MockPV{privKey, breakProposalSigning, breakVoteSigning} } // 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. -func (pv *MockPV) SignVote(chainID string, vote *Vote) error { +func (pv MockPV) SignVote(chainID string, vote *Vote) error { useChainID := chainID if pv.breakVoteSigning { useChainID = "incorrect-chain-id" } signBytes := vote.SignBytes(useChainID) - sig, err := pv.privKey.Sign(signBytes) + sig, err := pv.PrivKey.Sign(signBytes) if err != nil { return err } @@ -81,13 +89,13 @@ func (pv *MockPV) SignVote(chainID string, vote *Vote) error { } // Implements PrivValidator. -func (pv *MockPV) SignProposal(chainID string, proposal *Proposal) error { +func (pv MockPV) SignProposal(chainID string, proposal *Proposal) error { useChainID := chainID if pv.breakProposalSigning { useChainID = "incorrect-chain-id" } signBytes := proposal.SignBytes(useChainID) - sig, err := pv.privKey.Sign(signBytes) + sig, err := pv.PrivKey.Sign(signBytes) if err != nil { return err } @@ -96,34 +104,35 @@ 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) +func (pv MockPV) String() string { + mpv, _ := pv.GetPubKey() // mockPV will never return an error, ignored here + return fmt.Sprintf("MockPV{%v}", mpv.Address()) } // XXX: Implement. -func (pv *MockPV) DisableChecks() { +func (pv MockPV) DisableChecks() { // Currently this does nothing, // as MockPV has no safety checks at all. } -type erroringMockPV struct { - *MockPV +type ErroringMockPV struct { + MockPV } var ErroringMockPVErr = errors.New("erroringMockPV always returns an error") // Implements PrivValidator. -func (pv *erroringMockPV) SignVote(chainID string, vote *Vote) error { +func (pv *ErroringMockPV) SignVote(chainID string, vote *Vote) error { return ErroringMockPVErr } // Implements PrivValidator. -func (pv *erroringMockPV) SignProposal(chainID string, proposal *Proposal) error { +func (pv *ErroringMockPV) SignProposal(chainID string, proposal *Proposal) error { return ErroringMockPVErr } // NewErroringMockPV returns a MockPV that fails on each signing request. Again, for testing only. -func NewErroringMockPV() *erroringMockPV { - return &erroringMockPV{&MockPV{ed25519.GenPrivKey(), false, false}} + +func NewErroringMockPV() *ErroringMockPV { + return &ErroringMockPV{MockPV{ed25519.GenPrivKey(), false, false}} } 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.go b/types/protobuf.go index c1063ac94..52815593f 100644 --- a/types/protobuf.go +++ b/types/protobuf.go @@ -27,6 +27,7 @@ const ( ) // TODO: Make non-global by allowing for registration of more pubkey types + var ABCIPubKeyTypesToAminoNames = map[string]string{ ABCIPubKeyTypeEd25519: ed25519.PubKeyAminoName, ABCIPubKeyTypeSr25519: sr25519.PubKeyAminoName, 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 3090f5f8b..377c965a8 100644 --- a/types/test_util.go +++ b/types/test_util.go @@ -1,26 +1,31 @@ package types import ( - tmtime "github.com/tendermint/tendermint/types/time" + "time" + + "github.com/pkg/errors" ) func MakeCommit(blockID BlockID, height int64, round int, - voteSet *VoteSet, validators []PrivValidator) (*Commit, error) { + voteSet *VoteSet, validators []PrivValidator, now time.Time) (*Commit, error) { // 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, Type: PrecommitType, BlockID: blockID, - Timestamp: tmtime.Now(), + Timestamp: now, } - _, err := signAddVote(validators[i], vote, voteSet) + _, err = signAddVote(validators[i], vote, voteSet) if err != nil { return nil, err } @@ -43,15 +48,20 @@ func MakeVote( valSet *ValidatorSet, privVal PrivValidator, 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, ValidatorIndex: idx, Height: height, Round: 0, - Timestamp: tmtime.Now(), + Timestamp: now, Type: PrecommitType, BlockID: blockID, } 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/validator_set.go b/types/validator_set.go index 390df09cf..04de50646 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -636,6 +636,7 @@ func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, } talliedVotingPower := int64(0) + votingPowerNeeded := vals.TotalVotingPower() * 2 / 3 for idx, commitSig := range commit.Signatures { if commitSig.Absent() { continue // OK, some signatures can be absent. @@ -658,13 +659,15 @@ func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, // It's OK that the BlockID doesn't match. We include stray // signatures (~votes for nil) to measure validator availability. // } + + // return as soon as +2/3 of the signatures are verified + if talliedVotingPower > votingPowerNeeded { + return nil + } } - if got, needed := talliedVotingPower, vals.TotalVotingPower()*2/3; got <= needed { - return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed} - } - - return nil + // talliedVotingPower <= needed, thus return error + return ErrNotEnoughVotingPowerSigned{Got: talliedVotingPower, Needed: votingPowerNeeded} } // VerifyFutureCommit will check to see if the set would be valid with a different @@ -762,6 +765,7 @@ func (vals *ValidatorSet) VerifyCommitTrusting(chainID string, blockID BlockID, var ( talliedVotingPower int64 seenVals = make(map[int]int, len(commit.Signatures)) // validator index -> commit index + votingPowerNeeded = (vals.TotalVotingPower() * trustLevel.Numerator) / trustLevel.Denominator ) for idx, commitSig := range commit.Signatures { @@ -795,16 +799,14 @@ func (vals *ValidatorSet) VerifyCommitTrusting(chainID string, blockID BlockID, // It's OK that the BlockID doesn't match. We include stray // signatures (~votes for nil) to measure validator availability. // } + + if talliedVotingPower > votingPowerNeeded { + return nil + } } } - got := talliedVotingPower - needed := (vals.TotalVotingPower() * trustLevel.Numerator) / trustLevel.Denominator - if got <= needed { - return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed} - } - - return nil + return ErrNotEnoughVotingPowerSigned{Got: talliedVotingPower, Needed: votingPowerNeeded} } func verifyCommitBasic(commit *Commit, height int64, blockID BlockID) error { 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) } 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"