diff --git a/.circleci/config.yml b/.circleci/config.yml index ae66aa3d2..6a98bb4de 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -14,6 +14,9 @@ executors: - image: tendermintdev/docker-website-deployment environment: AWS_REGION: us-east-1 + protoc: + docker: + - image: tendermintdev/docker-protoc commands: checkout_with_submodules: @@ -92,6 +95,12 @@ jobs: root: "/tmp/bin" paths: - "." + proto-lint: + executor: protoc + steps: + - checkout + - run: + command: make proto-lint test_abci_apps: executor: golang @@ -179,12 +188,16 @@ jobs: GOPATH: /home/circleci/.go_workspace machine: image: circleci/classic:latest + parameters: + ipv: + type: integer + default: 4 steps: - checkout_with_submodules - make_libsodium - 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 @@ -254,7 +267,7 @@ jobs: # source /tmp/workspace/release-version.source # if test ${CIRCLE_NODE_INDEX:-0} == 0 ;then export GOOS=linux GOARCH=amd64 && export OUTPUT=build/tendermint_${GOOS}_${GOARCH} && make build && python -u scripts/release_management/zip-file.py ;fi # if test ${CIRCLE_NODE_INDEX:-0} == 1 ;then export GOOS=darwin GOARCH=amd64 && export OUTPUT=build/tendermint_${GOOS}_${GOARCH} && make build && python -u scripts/release_management/zip-file.py ;fi -# if test x${CIRCLE_NODE_INDEX:-0} == 2 ;then export GOOS=windows GOARCH=amd64 && export OUTPUT=build/tendermint_${GOOS}_${GOARCH} && make build && python -u scripts/release_management/zip-file.py ;fi +# if test ${CIRCLE_NODE_INDEX:-0} == 2 ;then export GOOS=windows GOARCH=amd64 && export OUTPUT=build/tendermint_${GOOS}_${GOARCH} && make build && python -u scripts/release_management/zip-file.py ;fi # if test ${CIRCLE_NODE_INDEX:-0} == 3 ;then export GOOS=linux GOARCH=arm && export OUTPUT=build/tendermint_${GOOS}_${GOARCH} && make build && python -u scripts/release_management/zip-file.py ;fi # - persist_to_workspace: # root: build @@ -339,6 +352,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 +387,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 @@ -408,6 +419,7 @@ workflows: - test_abci_apps: requires: - setup_dependencies + - proto-lint - test_abci_cli: requires: - setup_dependencies @@ -423,7 +435,10 @@ workflows: - localnet: requires: - setup_dependencies - - test_p2p +# - test_p2p + - test_p2p: + name: test_p2p_ipv6 + ipv: 6 - reproducible_builds: filters: branches: 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 6bda1076b..f6b2dd23f 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 a3dbe07d9..5c860ad67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,142 @@ # Changelog +## v0.33.3 + +*April 6, 2020* + +This security release fixes: + +### Denial of service 1 + +Tendermint 0.33.2 and earlier does not limit P2P connection requests number. +For each p2p connection, Tendermint allocates ~0.5MB. Even though this +memory is garbage collected once the connection is terminated (due to duplicate +IP or reaching a maximum number of inbound peers), temporary memory spikes can +lead to OOM (Out-Of-Memory) exceptions. + +Tendermint 0.33.3 (and 0.32.10) limits the total number of P2P incoming +connection requests to to `p2p.max_num_inbound_peers + +len(p2p.unconditional_peer_ids)`. + +Notes: + +- Tendermint does not rate limit P2P connection requests per IP (an attacker + can saturate all the inbound slots); +- Tendermint does not rate limit HTTP(S) requests. If you expose any RPC + endpoints to the public, please make sure to put in place some protection + (https://www.nginx.com/blog/rate-limiting-nginx/). We may implement this in + the future ([\#1696](https://github.com/tendermint/tendermint/issues/1696)). + +### Denial of service 2 + +Tendermint 0.33.2 and earlier does not reclaim `activeID` of a peer after it's +removed in `Mempool` reactor. This does not happen all the time. It only +happens when a connection fails (for any reason) before the Peer is created and +added to all reactors. `RemovePeer` is therefore called before `AddPeer`, which +leads to always growing memory (`activeIDs` map). The `activeIDs` map has a +maximum size of 65535 and the node will panic if this map reaches the maximum. +An attacker can create a lot of connection attempts (exploiting Denial of +service 1), which ultimately will lead to the node panicking. + +Tendermint 0.33.3 (and 0.32.10) claims `activeID` for a peer in `InitPeer`, +which is executed before `MConnection` is started. + +Notes: + +- `InitPeer` function was added to all reactors to combat a similar issue - + [\#3338](https://github.com/tendermint/tendermint/issues/3338); +- Denial of service 2 is independent of Denial of service 1 and can be executed + without it. + +**All clients are recommended to upgrade** + +Special thanks to [fudongbai](https://hackerone.com/fudongbai) for finding +and reporting this. + +Friendly reminder, we have a [bug bounty +program](https://hackerone.com/tendermint). + +### SECURITY: + +- [mempool] Reserve IDs in InitPeer instead of AddPeer (@tessr) +- [p2p] Limit the number of incoming connections (@melekes) + +## 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* + +Special thanks to external contributors on this release: +@princesinha19 + +Friendly reminder, we have a [bug bounty +program](https://hackerone.com/tendermint). + +### FEATURES: + +- [rpc] [\#3333](https://github.com/tendermint/tendermint/issues/3333) Add `order_by` to `/tx_search` endpoint, allowing to change default ordering from asc to desc (@princesinha19) + +### IMPROVEMENTS: + +- [proto] [\#4369](https://github.com/tendermint/tendermint/issues/4369) Add [buf](https://buf.build/) for usage with linting and checking if there are breaking changes with the master branch. +- [proto] [\#4369](https://github.com/tendermint/tendermint/issues/4369) Add `make proto-gen` cmd to generate proto stubs outside of GOPATH. + +### BUG FIXES: + +- [node] [\#4311](https://github.com/tendermint/tendermint/issues/4311) Use `GRPCMaxOpenConnections` when creating the gRPC server, not `MaxOpenConnections` +- [rpc] [\#4319](https://github.com/tendermint/tendermint/issues/4319) Check `BlockMeta` is not nil in `/block` & `/block_by_hash` + ## v0.33 Special thanks to external contributors on this release: @mrekucci, @PSalant726, @princesinha19, @greg-szabo, @dongsam, @cuonglm, @jgimeno, @yenkhoon diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 1f890cd57..9bbec13dc 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,11 +1,10 @@ -## v0.33.1 +## v0.33.4 \*\* 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: @@ -28,5 +27,3 @@ program](https://hackerone.com/tendermint). ### IMPROVEMENTS: ### BUG FIXES: - - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 52d5532f5..6e6897ffa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,7 @@ at the RFC stage will build collective understanding of the dimensions of the problems and help structure conversations around trade-offs. When the problem is well understood but the solution leads to large -strucural changes to the code base, these changes should be proposed in +structural changes to the code base, these changes should be proposed in the form of an [Architectural Decision Record (ADR)](./docs/architecture/). The ADR will help build consensus on an overall strategy to ensure the code base maintains coherence @@ -98,6 +98,28 @@ need. Instead of running `go get -u=patch`, which will update anything, specify exactly the dependency you want to update, eg. `GO111MODULE=on go get -u github.com/tendermint/go-amino@master`. +## Protobuf + +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 If you are a [Vagrant](https://www.vagrantup.com/) user, you can get started diff --git a/Makefile b/Makefile index edd0638a8..d3d39436f 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,6 @@ PACKAGES=$(shell go list ./...) SRCPATH=$(shell pwd) OUTPUT?=build/tendermint -INCLUDE = -I=${GOPATH}/src/github.com/tendermint/tendermint -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf BUILD_TAGS?='tendermint' CGO_OPTPTION=0 LIBSODIUM_TARGET= @@ -20,63 +19,86 @@ endif LIBSODIM_BUILD_TAGS='libsodium 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 # The below include contains the tools. include tools.mk include tests.mk -######################################## -### Build Tendermint +############################################################################### +### Build Tendermint ### +############################################################################### build: $(LIBSODIUM_TARGET) CGO_ENABLED=$(CGO_OPTION) go build $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o $(OUTPUT) ./cmd/tendermint/ +.PHONY: build build_c: $(LIBSODIUM_TARGET) CGO_ENABLED=$(CGO_OPTION) go build $(BUILD_FLAGS) -tags "$(BUILD_TAGS) cleveldb" -o $(OUTPUT) ./cmd/tendermint/ +.PHONY: build_c build_race: $(LIBSODIUM_TARGET) CGO_ENABLED=$(CGO_OPTION) go build -race $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o $(OUTPUT) ./cmd/tendermint +.PHONY: build_race install: CGO_ENABLED=$(CGO_OPTION) go install $(BUILD_FLAGS) -tags $(BUILD_TAGS) ./cmd/tendermint +.PHONY: install install_c: CGO_ENABLED=$(CGO_OPTION) go install $(BUILD_FLAGS) -tags "$(BUILD_TAGS) cleveldb" ./cmd/tendermint +.PHONY: install_c -######################################## -### Protobuf +############################################################################### +### Protobuf ### +############################################################################### -protoc_all: protoc_libs protoc_merkle protoc_abci protoc_grpc protoc_proto3types +proto-all: proto-gen proto-lint proto-check-breaking +.PHONY: proto-all -%.pb.go: %.proto +proto-gen: ## If you get the following error, ## "error while loading shared libraries: libprotobuf.so.14: cannot open shared object file: No such file or directory" ## See https://stackoverflow.com/a/25518702 ## Note the $< here is substituted for the %.proto ## Note the $@ here is substituted for the %.pb.go - protoc $(INCLUDE) $< --gogo_out=Mgoogle/protobuf/timestamp.proto=github.com/golang/protobuf/ptypes/timestamp,Mgoogle/protobuf/duration.proto=github.com/golang/protobuf/ptypes/duration,plugins=grpc:../../.. + @sh scripts/protocgen.sh +.PHONY: proto-gen -######################################## -### Build ABCI +proto-lint: + @buf check lint --error-format=json +.PHONY: proto-lint -# see protobuf section above -protoc_abci: abci/types/types.pb.go +proto-check-breaking: + @buf check breaking --against-input ".git#branch=master" +.PHONY: proto-check-breaking -protoc_proto3types: types/proto3/block.pb.go +proto-check-breaking-ci: + @buf check breaking --against-input "$(HTTPS_GIT)#branch=master" +.PHONY: proto-check-breaking-ci + +############################################################################### +### Build ABCI ### +############################################################################### build_abci: @go build -mod=readonly -i ./abci/cmd/... +.PHONY: build_abci install_abci: @go install -mod=readonly ./abci/cmd/... +.PHONY: install_abci -######################################## -### libsodium +############################################################################### +### libsodium ### +############################################################################### prepare-libsodium-linux: apt-get update && apt-get -y install libtool libboost-all-dev autoconf build-essential +.PHONY: prepare_libsodium libsodium: cd $(SRCPATH)/crypto/vrf/internal/vrf/libsodium && \ @@ -84,14 +106,17 @@ libsodium: ./configure --disable-shared --prefix="$(SRCPATH)/crypto/vrf/internal/vrf/" && \ $(MAKE) && \ $(MAKE) install +.PHONY: libsodium -######################################## -### Distribution +############################################################################### +### Distribution ### +############################################################################### # dist builds binaries for all platforms and packages them for distribution # TODO add abci to these scripts dist: @BUILD_TAGS=$(BUILD_TAGS) sh -c "'$(CURDIR)/scripts/dist.sh'" +.PHONY: dist go-mod-cache: go.sum @echo "--> Download go modules to local cache" @@ -107,64 +132,55 @@ draw_deps: @# requires brew install graphviz or apt-get install graphviz go get github.com/RobotsAndPencils/goviz @goviz -i github.com/tendermint/tendermint/cmd/tendermint -d 3 | dot -Tpng -o dependency-graph.png +.PHONY: draw_deps get_deps_bin_size: @# Copy of build recipe with additional flags to perform binary size analysis $(eval $(shell go build -work -a $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o $(OUTPUT) ./cmd/tendermint/ 2>&1)) @find $(WORK) -type f -name "*.a" | xargs -I{} du -hxs "{}" | sort -rh | sed -e s:${WORK}/::g > deps_bin_size.log @echo "Results can be found here: $(CURDIR)/deps_bin_size.log" +.PHONY: get_deps_bin_size -######################################## -### Libs - -protoc_libs: libs/kv/types.pb.go +############################################################################### +### Libs ### +############################################################################### # generates certificates for TLS testing in remotedb and RPC server gen_certs: clean_certs certstrap init --common-name "tendermint.com" --passphrase "" - certstrap request-cert --common-name "remotedb" -ip "127.0.0.1" --passphrase "" - certstrap sign "remotedb" --CA "tendermint.com" --passphrase "" - mv out/remotedb.crt libs/db/remotedb/test.crt - mv out/remotedb.key libs/db/remotedb/test.key certstrap request-cert --common-name "server" -ip "127.0.0.1" --passphrase "" certstrap sign "server" --CA "tendermint.com" --passphrase "" mv out/server.crt rpc/lib/server/test.crt mv out/server.key rpc/lib/server/test.key rm -rf out +.PHONY: gen_certs # deletes generated certificates clean_certs: - rm -f libs/db/remotedb/test.crt - rm -f libs/db/remotedb/test.key rm -f rpc/lib/server/test.crt rm -f rpc/lib/server/test.key +.PHONY: clean_certs -test_libs: - go test -tags clevedb boltdb $(PACKAGES) - -grpc_dbserver: - protoc -I libs/db/remotedb/proto/ libs/db/remotedb/proto/defs.proto --go_out=plugins=grpc:libs/db/remotedb/proto - -protoc_grpc: rpc/grpc/types.pb.go - -protoc_merkle: crypto/merkle/merkle.pb.go - - -######################################## -### Formatting, linting, and vetting +############################################################################### +### Formatting, linting, and vetting ### +############################################################################### fmt: @go fmt ./... +.PHONY: fmt lint: + @echo "--> Running linter" sh -c "$(CURDIR)/scripts/current_branch_lint.sh" find . -name '*.go' -type f -not -path "*.git*" | xargs gofmt -d -s go mod verify +.PHONY: lint DESTINATION = ./index.html.md -########################################################### -### Documentation +############################################################################### +### Documentation ### +############################################################################### build-docs: cd docs && \ @@ -174,6 +190,7 @@ build-docs: cp -r .vuepress/dist/* ~/output/$${p}/ ; \ cp ~/output/$${p}/index.html ~/output ; \ done < versions ; +.PHONY: build-docs sync-docs: cd ~/output && \ @@ -183,23 +200,28 @@ sync-docs: aws cloudfront create-invalidation --distribution-id ${CF_DISTRIBUTION_ID} --profile terraform --path "/*" ; .PHONY: sync-docs -########################################################### -### Docker image +############################################################################### +### Docker image ### +############################################################################### build-docker: cp $(OUTPUT) DOCKER/tendermint docker build --label=tendermint --tag="tendermint/tendermint" DOCKER rm -rf DOCKER/tendermint +.PHONY: build-docker -########################################################### -### Local testnet using docker +############################################################################### +### Local testnet using docker ### +############################################################################### # Build linux binary on other platforms build-linux: tools $(PREPARE_LIBSODIUM_TARGET) $(LIBSODIUM_TARGET) GOOS=linux GOARCH=amd64 $(MAKE) build +.PHONY: build-linux build-docker-localnode: @cd networks/local && make +.PHONY: build-docker-localnode # Runs `make build_c` from within an Amazon Linux (v2)-based Docker build # container in order to build an Amazon Linux-compatible binary. Produces a @@ -207,35 +229,18 @@ build-docker-localnode: build_c-amazonlinux: $(MAKE) -C ./DOCKER build_amazonlinux_buildimage docker run --rm -it -v `pwd`:/tendermint tendermint/tendermint:build_c-amazonlinux +.PHONY: build_c-amazonlinux # Run a 4-node testnet locally localnet-start: localnet-stop build-docker-localnode @if ! [ -f build/node0/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/tendermint:Z tendermint/localnode testnet --config /etc/tendermint/config-template.toml --v 4 --o . --populate-persistent-peers --starting-ip-address 192.167.10.2; fi docker-compose up +.PHONY: localnet-start # Stop testnet localnet-stop: docker-compose down - -########################################################### -### Remote full-nodes (sentry) using terraform and ansible - -# Server management -sentry-start: - @if [ -z "$(DO_API_TOKEN)" ]; then echo "DO_API_TOKEN environment variable not set." ; false ; fi - @if ! [ -f $(HOME)/.ssh/id_rsa.pub ]; then ssh-keygen ; fi - cd networks/remote/terraform && terraform init && terraform apply -var DO_API_TOKEN="$(DO_API_TOKEN)" -var SSH_KEY_FILE="$(HOME)/.ssh/id_rsa.pub" - @if ! [ -f $(CURDIR)/build/node0/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/tendermint:Z tendermint/localnode testnet --v 0 --n 4 --o . ; fi - cd networks/remote/ansible && ANSIBLE_HOST_KEY_CHECKING=False ansible-playbook -i inventory/digital_ocean.py -l sentrynet install.yml - @echo "Next step: Add your validator setup in the genesis.json and config.tml files and run \"make sentry-config\". (Public key of validator, chain ID, peer IP and node ID.)" - -# Configuration management -sentry-config: - cd networks/remote/ansible && ansible-playbook -i inventory/digital_ocean.py -l sentrynet config.yml -e BINARY=$(CURDIR)/build/tendermint -e CONFIGDIR=$(CURDIR)/build - -sentry-stop: - @if [ -z "$(DO_API_TOKEN)" ]; then echo "DO_API_TOKEN environment variable not set." ; false ; fi - cd networks/remote/terraform && terraform destroy -var DO_API_TOKEN="$(DO_API_TOKEN)" -var SSH_KEY_FILE="$(HOME)/.ssh/id_rsa.pub" +.PHONY: localnet-stop # Build hooks for dredd, to skip or add information on some steps build-contract-tests-hooks: @@ -244,6 +249,7 @@ ifeq ($(OS),Windows_NT) else go build -mod=readonly $(BUILD_FLAGS) -o build/contract_tests ./cmd/contract_tests endif +.PHONY: build-contract-tests-hooks # Run a nodejs tool to test endpoints against a localnet # The command takes care of starting and stopping the network @@ -252,12 +258,4 @@ endif # The binaries should be built beforehand contract-tests: dredd - -# To avoid unintended conflicts with file names, always add to .PHONY -# unless there is a reason not to. -# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html -.PHONY: check build build_race build_abci dist install install_abci check_tools tools update_tools draw_deps \ - protoc_abci protoc_libs gen_certs clean_certs grpc_dbserver fmt build-linux localnet-start \ - localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop protoc_grpc protoc_all \ - build_c install_c test_with_deadlock cleanup_after_test_with_deadlock lint build-contract-tests-hooks contract-tests \ - build_c-amazonlinux +.PHONY: contract-tests diff --git a/README.md b/README.md index 37d150b6e..c9979aceb 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,9 @@ For examples of the kinds of bugs we're looking for, see [SECURITY.md](SECURITY. ## Minimum requirements -| Requirement | Notes | -| ----------- | ------------------ | -| Go version | Go1.13 or higher | +| Requirement | Notes | +| ----------- | ---------------- | +| Go version | Go1.13 or higher | ## Documentation @@ -125,7 +125,7 @@ For more information on upgrading, see [UPGRADING.md](./UPGRADING.md) ### Tendermint Core For details about the blockchain data structures and the p2p protocols, see the -[Tendermint specification](/docs/spec). +[Tendermint specification](https://docs.tendermint.com/master/spec/). For details on using the software, see the [documentation](/docs/) which is also hosted at: https://docs.tendermint.com/master/ @@ -153,6 +153,5 @@ Additional documentation is found [here](/docs/tools). - [The latest gossip on BFT consensus](https://arxiv.org/abs/1807.04938) - [Master's Thesis on Tendermint](https://atrium.lib.uoguelph.ca/xmlui/handle/10214/9769) -- [Original Whitepaper](https://github.com/tendermint/spec) - - You can find the link at the bottom of the readme +- [Original Whitepaper: "Tendermint: Consensus Without Mining"](https://tendermint.com/static/docs/tendermint.pdf) - [Blog](https://blog.cosmos.network/tendermint/home) diff --git a/UPGRADING.md b/UPGRADING.md index bb531d32f..d568c6e94 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -3,6 +3,18 @@ This guide provides steps to be followed when you upgrade your applications to a newer version of Tendermint Core. +## Unreleased + + + +## v0.33.1 + +This release is compatible with the previous version. The only change that is required is if you are fetching the protobuf files for application use. + +### Protobuf Changes + +When upgrading to version 0.33.1 you will have to fetch the `third_party` directory along with the updated proto files. + ## v0.33.0 This release is not compatible with previous blockchains due to commit becoming signatures only and fields in the header have been removed. @@ -224,14 +236,14 @@ due to changes in how various data structures are hashed. Any implementations of Tendermint blockchain verification, including lite clients, will need to be updated. For specific details: -- [Merkle tree](./docs/spec/blockchain/encoding.md#merkle-trees) -- [ConsensusParams](./docs/spec/blockchain/state.md#consensusparams) +- [Merkle tree](https://github.com/tendermint/spec/blob/master/spec/blockchain/encoding.md#merkle-trees) +- [ConsensusParams](https://github.com/tendermint/spec/blob/master/spec/blockchain/state.md#consensusparams) There was also a small change to field ordering in the vote struct. Any implementations of an out-of-process validator (like a Key-Management Server) will need to be updated. For specific details: -- [Vote](https://github.com/tendermint/tendermint/blob/master/docs/spec/consensus/signing.md#votes) +- [Vote](https://github.com/tendermint/spec/blob/master/spec/consensus/signing.md#votes) Finally, the proposer selection algorithm continues to evolve. See the [work-in-progress diff --git a/abci/README.md b/abci/README.md index ad84eb77c..cd0e9bbd9 100644 --- a/abci/README.md +++ b/abci/README.md @@ -19,7 +19,7 @@ To get up and running quickly, see the [getting started guide](../docs/app-dev/g A detailed description of the ABCI methods and message types is contained in: -- [The main spec](../docs/spec/abci/abci.md) +- [The main spec](https://github.com/tendermint/spec/blob/master/spec/abci/abci.md) - [A protobuf file](./types/types.proto) - [A Go interface](./types/application.go) 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 c7524cb1c..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)) //nolint:gosec + 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 d1a10eb9f..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:"-"` @@ -2960,156 +2960,156 @@ func init() { proto.RegisterFile("abci/types/types.proto", fileDescriptor_9f1eaa func init() { golang_proto.RegisterFile("abci/types/types.proto", fileDescriptor_9f1eaa49c51fa1ac) } var fileDescriptor_9f1eaa49c51fa1ac = []byte{ - // 2371 bytes of a gzipped FileDescriptorProto + // 2370 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x59, 0x4d, 0x90, 0x1b, 0x47, - 0x15, 0xde, 0xd1, 0x6a, 0xf5, 0xf3, 0xb4, 0xbb, 0x52, 0x3a, 0x4e, 0x22, 0x0b, 0x67, 0xd7, 0x35, - 0xfe, 0x5b, 0xe7, 0x47, 0x0e, 0x0b, 0xa1, 0x62, 0xec, 0x0a, 0xb5, 0x5a, 0x3b, 0x48, 0x15, 0xdb, - 0xd9, 0x8c, 0xed, 0xc5, 0x40, 0x55, 0xa6, 0x5a, 0x9a, 0xb6, 0x34, 0xb5, 0xd2, 0xcc, 0x64, 0xa6, - 0x25, 0x6b, 0x29, 0xee, 0x14, 0x55, 0x1c, 0xb8, 0x50, 0xc5, 0x85, 0x3b, 0x47, 0x0e, 0x1c, 0x72, - 0xe4, 0x98, 0x03, 0x07, 0x0e, 0x9c, 0x0d, 0x2c, 0x9c, 0xa8, 0x1c, 0x29, 0x8a, 0x23, 0xd5, 0xaf, - 0x7b, 0xfe, 0xb4, 0xd2, 0x6a, 0x1c, 0x7c, 0xe3, 0x22, 0x4d, 0x77, 0xbf, 0xf7, 0xba, 0xfb, 0xf5, - 0xeb, 0xf7, 0xbd, 0xf7, 0x1a, 0x5e, 0xa7, 0xdd, 0x9e, 0x7d, 0x83, 0x1f, 0x7b, 0x2c, 0x90, 0xbf, - 0x4d, 0xcf, 0x77, 0xb9, 0x4b, 0x5e, 0xe3, 0xcc, 0xb1, 0x98, 0x3f, 0xb2, 0x1d, 0xde, 0x14, 0x24, - 0x4d, 0x1c, 0x6c, 0xbc, 0xdb, 0xb7, 0xf9, 0x60, 0xdc, 0x6d, 0xf6, 0xdc, 0xd1, 0x8d, 0xbe, 0xdb, - 0x77, 0x6f, 0x20, 0x75, 0x77, 0xfc, 0x14, 0x5b, 0xd8, 0xc0, 0x2f, 0x29, 0xa5, 0x71, 0x2b, 0x41, - 0x1e, 0x0b, 0x4c, 0x7e, 0xf6, 0xfc, 0x63, 0x8f, 0xbb, 0x37, 0x46, 0xcc, 0x3f, 0x1a, 0x32, 0xf5, - 0xa7, 0x98, 0xbf, 0xbd, 0x94, 0x79, 0x68, 0x77, 0x83, 0x1b, 0x47, 0x93, 0xe4, 0xc2, 0x1b, 0xdb, - 0x7d, 0xd7, 0xed, 0x0f, 0x59, 0xbc, 0x30, 0x6e, 0x8f, 0x58, 0xc0, 0xe9, 0xc8, 0x53, 0x04, 0x5b, - 0xb3, 0x04, 0xd6, 0xd8, 0xa7, 0xdc, 0x76, 0x1d, 0x39, 0xae, 0xff, 0x7b, 0x0d, 0x8a, 0x06, 0xfb, - 0x7c, 0xcc, 0x02, 0x4e, 0x3e, 0x80, 0x3c, 0xeb, 0x0d, 0xdc, 0x7a, 0xee, 0xa2, 0xb6, 0x53, 0xd9, - 0xd5, 0x9b, 0x73, 0x95, 0xd2, 0x54, 0xd4, 0x77, 0x7b, 0x03, 0xb7, 0xbd, 0x62, 0x20, 0x07, 0xb9, - 0x05, 0x6b, 0x4f, 0x87, 0xe3, 0x60, 0x50, 0x5f, 0x45, 0xd6, 0x4b, 0x67, 0xb3, 0x7e, 0x24, 0x48, - 0xdb, 0x2b, 0x86, 0xe4, 0x11, 0xd3, 0xda, 0xce, 0x53, 0xb7, 0x9e, 0xcf, 0x32, 0x6d, 0xc7, 0x79, - 0x8a, 0xd3, 0x0a, 0x0e, 0xd2, 0x06, 0x08, 0x18, 0x37, 0x5d, 0x4f, 0x6c, 0xa8, 0xbe, 0x86, 0xfc, - 0xd7, 0xce, 0xe6, 0x7f, 0xc8, 0xf8, 0x27, 0x48, 0xde, 0x5e, 0x31, 0xca, 0x41, 0xd8, 0x10, 0x92, - 0x6c, 0xc7, 0xe6, 0x66, 0x6f, 0x40, 0x6d, 0xa7, 0x5e, 0xc8, 0x22, 0xa9, 0xe3, 0xd8, 0x7c, 0x5f, - 0x90, 0x0b, 0x49, 0x76, 0xd8, 0x10, 0xaa, 0xf8, 0x7c, 0xcc, 0xfc, 0xe3, 0x7a, 0x31, 0x8b, 0x2a, - 0x3e, 0x15, 0xa4, 0x42, 0x15, 0xc8, 0x43, 0x3e, 0x86, 0x4a, 0x97, 0xf5, 0x6d, 0xc7, 0xec, 0x0e, - 0xdd, 0xde, 0x51, 0xbd, 0x84, 0x22, 0x76, 0xce, 0x16, 0xd1, 0x12, 0x0c, 0x2d, 0x41, 0xdf, 0x5e, - 0x31, 0xa0, 0x1b, 0xb5, 0x48, 0x0b, 0x4a, 0xbd, 0x01, 0xeb, 0x1d, 0x99, 0x7c, 0x5a, 0x2f, 0xa3, - 0xa4, 0x2b, 0x67, 0x4b, 0xda, 0x17, 0xd4, 0x8f, 0xa6, 0xed, 0x15, 0xa3, 0xd8, 0x93, 0x9f, 0x42, - 0x2f, 0x16, 0x1b, 0xda, 0x13, 0xe6, 0x0b, 0x29, 0xaf, 0x66, 0xd1, 0xcb, 0x1d, 0x49, 0x8f, 0x72, - 0xca, 0x56, 0xd8, 0x20, 0x77, 0xa1, 0xcc, 0x1c, 0x4b, 0x6d, 0xac, 0x82, 0x82, 0xae, 0x2e, 0xb1, - 0x30, 0xc7, 0x0a, 0xb7, 0x55, 0x62, 0xea, 0x9b, 0x7c, 0x08, 0x85, 0x9e, 0x3b, 0x1a, 0xd9, 0xbc, - 0xbe, 0x8e, 0x32, 0x2e, 0x2f, 0xd9, 0x12, 0xd2, 0xb6, 0x57, 0x0c, 0xc5, 0xd5, 0x2a, 0xc2, 0xda, - 0x84, 0x0e, 0xc7, 0x4c, 0xbf, 0x06, 0x95, 0x84, 0x25, 0x93, 0x3a, 0x14, 0x47, 0x2c, 0x08, 0x68, - 0x9f, 0xd5, 0xb5, 0x8b, 0xda, 0x4e, 0xd9, 0x08, 0x9b, 0xfa, 0x26, 0xac, 0x27, 0xed, 0x56, 0x1f, - 0x45, 0x8c, 0xc2, 0x16, 0x05, 0xe3, 0x84, 0xf9, 0x81, 0x30, 0x40, 0xc5, 0xa8, 0x9a, 0xe4, 0x12, - 0x6c, 0xe0, 0x6e, 0xcd, 0x70, 0x5c, 0xdc, 0xab, 0xbc, 0xb1, 0x8e, 0x9d, 0x87, 0x8a, 0x68, 0x1b, - 0x2a, 0xde, 0xae, 0x17, 0x91, 0xac, 0x22, 0x09, 0x78, 0xbb, 0x9e, 0x22, 0xd0, 0xbf, 0x0b, 0xb5, - 0x59, 0xd3, 0x25, 0x35, 0x58, 0x3d, 0x62, 0xc7, 0x6a, 0x3e, 0xf1, 0x49, 0xce, 0xa9, 0x6d, 0xe1, - 0x1c, 0x65, 0x43, 0xed, 0xf1, 0x77, 0xb9, 0x88, 0x39, 0xb2, 0x56, 0x71, 0xdd, 0x84, 0x93, 0x40, - 0xee, 0xca, 0x6e, 0xa3, 0x29, 0x1d, 0x44, 0x33, 0x74, 0x10, 0xcd, 0x47, 0xa1, 0x07, 0x69, 0x95, - 0xbe, 0x7c, 0xbe, 0xbd, 0xf2, 0xcb, 0xbf, 0x6c, 0x6b, 0x06, 0x72, 0x90, 0xf3, 0xc2, 0xa0, 0xa8, - 0xed, 0x98, 0xb6, 0xa5, 0xe6, 0x29, 0x62, 0xbb, 0x63, 0x91, 0x4f, 0xa1, 0xd6, 0x73, 0x9d, 0x80, - 0x39, 0xc1, 0x38, 0x30, 0x3d, 0xea, 0xd3, 0x51, 0xa0, 0x7c, 0xc1, 0xa2, 0x43, 0xde, 0x0f, 0xc9, - 0x0f, 0x90, 0xda, 0xa8, 0xf6, 0xd2, 0x1d, 0xe4, 0x1e, 0xc0, 0x84, 0x0e, 0x6d, 0x8b, 0x72, 0xd7, - 0x0f, 0xea, 0xf9, 0x8b, 0xab, 0x67, 0x08, 0x3b, 0x0c, 0x09, 0x1f, 0x7b, 0x16, 0xe5, 0xac, 0x95, - 0x17, 0x2b, 0x37, 0x12, 0xfc, 0xe4, 0x2a, 0x54, 0xa9, 0xe7, 0x99, 0x01, 0xa7, 0x9c, 0x99, 0xdd, - 0x63, 0xce, 0x02, 0xf4, 0x17, 0xeb, 0xc6, 0x06, 0xf5, 0xbc, 0x87, 0xa2, 0xb7, 0x25, 0x3a, 0x75, - 0x2b, 0x3a, 0x6d, 0xbc, 0x9a, 0x84, 0x40, 0xde, 0xa2, 0x9c, 0xa2, 0xb6, 0xd6, 0x0d, 0xfc, 0x16, - 0x7d, 0x1e, 0xe5, 0x03, 0xa5, 0x03, 0xfc, 0x26, 0xaf, 0x43, 0x61, 0xc0, 0xec, 0xfe, 0x80, 0xe3, - 0xb6, 0x57, 0x0d, 0xd5, 0x12, 0x07, 0xe3, 0xf9, 0xee, 0x84, 0xa1, 0x77, 0x2b, 0x19, 0xb2, 0xa1, - 0xff, 0x2a, 0x07, 0xaf, 0x9c, 0xba, 0xbe, 0x42, 0xee, 0x80, 0x06, 0x83, 0x70, 0x2e, 0xf1, 0x4d, - 0x6e, 0x09, 0xb9, 0xd4, 0x62, 0xbe, 0xf2, 0xca, 0x6f, 0x2e, 0xd0, 0x40, 0x1b, 0x89, 0xd4, 0xc6, - 0x15, 0x0b, 0x79, 0x0c, 0xb5, 0x21, 0x0d, 0xb8, 0x29, 0x6d, 0xdf, 0x44, 0x2f, 0xbb, 0x7a, 0xa6, - 0x27, 0xb8, 0x47, 0xc3, 0x3b, 0x23, 0x8c, 0x5b, 0x89, 0xdb, 0x1c, 0xa6, 0x7a, 0xc9, 0x13, 0x38, - 0xd7, 0x3d, 0xfe, 0x09, 0x75, 0xb8, 0xed, 0x30, 0xf3, 0xd4, 0x19, 0x6d, 0x2f, 0x10, 0x7d, 0x77, - 0x62, 0x5b, 0xcc, 0xe9, 0x85, 0x87, 0xf3, 0x6a, 0x24, 0x22, 0x3a, 0xbc, 0x40, 0x7f, 0x02, 0x9b, - 0x69, 0x5f, 0x44, 0x36, 0x21, 0xc7, 0xa7, 0x4a, 0x23, 0x39, 0x3e, 0x25, 0xdf, 0x81, 0xbc, 0x10, - 0x87, 0xda, 0xd8, 0x5c, 0x08, 0x16, 0x8a, 0xfb, 0xd1, 0xb1, 0xc7, 0x0c, 0xa4, 0xd7, 0xf5, 0xe8, - 0x26, 0x44, 0xfe, 0x69, 0x56, 0xb6, 0x7e, 0x1d, 0xaa, 0x33, 0xae, 0x27, 0x71, 0xac, 0x5a, 0xf2, - 0x58, 0xf5, 0x2a, 0x6c, 0xa4, 0x3c, 0x8c, 0xfe, 0xc7, 0x02, 0x94, 0x0c, 0x16, 0x78, 0xc2, 0x88, - 0x49, 0x1b, 0xca, 0x6c, 0xda, 0x63, 0x12, 0x96, 0xb4, 0x25, 0x4e, 0x5c, 0xf2, 0xdc, 0x0d, 0xe9, - 0x85, 0xd7, 0x8c, 0x98, 0xc9, 0xcd, 0x14, 0x24, 0x5f, 0x5a, 0x26, 0x24, 0x89, 0xc9, 0xb7, 0xd3, - 0x98, 0x7c, 0x79, 0x09, 0xef, 0x0c, 0x28, 0xdf, 0x4c, 0x81, 0xf2, 0xb2, 0x89, 0x53, 0xa8, 0xdc, - 0x99, 0x83, 0xca, 0xcb, 0xb6, 0xbf, 0x00, 0x96, 0x3b, 0x73, 0x60, 0x79, 0x67, 0xe9, 0x5a, 0xe6, - 0xe2, 0xf2, 0xed, 0x34, 0x2e, 0x2f, 0x53, 0xc7, 0x0c, 0x30, 0xdf, 0x9b, 0x07, 0xcc, 0xd7, 0x97, - 0xc8, 0x58, 0x88, 0xcc, 0xfb, 0xa7, 0x90, 0xf9, 0xea, 0x12, 0x51, 0x73, 0xa0, 0xb9, 0x93, 0x82, - 0x66, 0xc8, 0xa4, 0x9b, 0x05, 0xd8, 0xfc, 0xd1, 0x69, 0x6c, 0xbe, 0xb6, 0xcc, 0xd4, 0xe6, 0x81, - 0xf3, 0xf7, 0x66, 0xc0, 0xf9, 0xca, 0xb2, 0x5d, 0x2d, 0x44, 0xe7, 0xeb, 0xc2, 0x3f, 0xce, 0xdc, - 0x0c, 0xe1, 0x4b, 0x99, 0xef, 0xbb, 0xbe, 0x02, 0x3e, 0xd9, 0xd0, 0x77, 0x84, 0xc7, 0x8e, 0xed, - 0xff, 0x0c, 0x24, 0xc7, 0x4b, 0x9b, 0xb0, 0x76, 0xfd, 0x0b, 0x2d, 0xe6, 0x45, 0xcf, 0x96, 0xf4, - 0xf6, 0x65, 0xe5, 0xed, 0x13, 0x00, 0x9f, 0x4b, 0x03, 0xfc, 0x36, 0x54, 0x04, 0xa6, 0xcc, 0x60, - 0x37, 0xf5, 0x42, 0xec, 0x26, 0x6f, 0xc1, 0x2b, 0xe8, 0x7f, 0x65, 0x18, 0xa0, 0x1c, 0x49, 0x1e, - 0x1d, 0x49, 0x55, 0x0c, 0x48, 0x0d, 0x4a, 0xa0, 0x78, 0x17, 0x5e, 0x4d, 0xd0, 0x0a, 0xb9, 0x88, - 0x05, 0x12, 0xa4, 0x6a, 0x11, 0xf5, 0x9e, 0xe7, 0xb5, 0x69, 0x30, 0xd0, 0xef, 0xc7, 0x0a, 0x8a, - 0xe3, 0x02, 0x02, 0xf9, 0x9e, 0x6b, 0xc9, 0x7d, 0x6f, 0x18, 0xf8, 0x2d, 0x62, 0x85, 0xa1, 0xdb, - 0xc7, 0xc5, 0x95, 0x0d, 0xf1, 0x29, 0xa8, 0xa2, 0xab, 0x5d, 0x96, 0x77, 0x56, 0xff, 0xbd, 0x16, - 0xcb, 0x8b, 0x43, 0x85, 0x79, 0xa8, 0xae, 0xbd, 0x4c, 0x54, 0xcf, 0xfd, 0x6f, 0xa8, 0xae, 0xff, - 0x4b, 0x8b, 0x8f, 0x34, 0xc2, 0xeb, 0xaf, 0xa7, 0x02, 0x61, 0x5d, 0xb6, 0x63, 0xb1, 0x29, 0xaa, - 0x7c, 0xd5, 0x90, 0x8d, 0x30, 0xd4, 0x2a, 0xe0, 0x31, 0xa4, 0x43, 0xad, 0x22, 0xf6, 0xc9, 0x06, - 0x79, 0x1f, 0x71, 0xde, 0x7d, 0xaa, 0x5c, 0x43, 0x0a, 0x04, 0x65, 0xd6, 0xd7, 0x54, 0xe9, 0xde, - 0x81, 0x20, 0x33, 0x24, 0x75, 0x02, 0x5f, 0xca, 0xa9, 0xb0, 0xe1, 0x02, 0x94, 0xc5, 0xd2, 0x03, - 0x8f, 0xf6, 0x18, 0xde, 0xed, 0xb2, 0x11, 0x77, 0xe8, 0x16, 0x90, 0xd3, 0x3e, 0x86, 0x3c, 0x80, - 0x02, 0x9b, 0x30, 0x87, 0x8b, 0x33, 0x12, 0x6a, 0xbd, 0xb0, 0x10, 0x88, 0x99, 0xc3, 0x5b, 0x75, - 0xa1, 0xcc, 0x7f, 0x3e, 0xdf, 0xae, 0x49, 0x9e, 0x77, 0xdc, 0x91, 0xcd, 0xd9, 0xc8, 0xe3, 0xc7, - 0x86, 0x92, 0xa2, 0xff, 0x2c, 0x27, 0xf0, 0x30, 0xe5, 0x7f, 0xe6, 0xaa, 0x37, 0xbc, 0x34, 0xb9, - 0x44, 0x88, 0x94, 0x4d, 0xe5, 0x6f, 0x02, 0xf4, 0x69, 0x60, 0x3e, 0xa3, 0x0e, 0x67, 0x96, 0xd2, - 0x7b, 0xb9, 0x4f, 0x83, 0x1f, 0x60, 0x87, 0x88, 0x37, 0xc5, 0xf0, 0x38, 0x60, 0x16, 0x1e, 0xc0, - 0xaa, 0x51, 0xec, 0xd3, 0xe0, 0x71, 0xc0, 0xac, 0xc4, 0x5e, 0x8b, 0x2f, 0x63, 0xaf, 0x69, 0x7d, - 0x97, 0x66, 0xf5, 0xfd, 0xf3, 0x5c, 0x7c, 0x3b, 0xe2, 0xf0, 0xe1, 0xff, 0x53, 0x17, 0xbf, 0xc1, - 0x9c, 0x22, 0x0d, 0x02, 0xe4, 0x87, 0xf0, 0x4a, 0x74, 0x2b, 0xcd, 0x31, 0xde, 0xd6, 0xd0, 0x0a, - 0x5f, 0xec, 0x72, 0xd7, 0x26, 0xe9, 0xee, 0x80, 0x7c, 0x06, 0x6f, 0xcc, 0xf8, 0xa0, 0x68, 0x82, - 0xdc, 0x0b, 0xb9, 0xa2, 0xd7, 0xd2, 0xae, 0x28, 0x94, 0x1f, 0x6b, 0x6f, 0xf5, 0xa5, 0xdc, 0x9a, - 0xcb, 0x22, 0x84, 0x4d, 0xc2, 0xdb, 0x3c, 0x9b, 0xd0, 0xff, 0xac, 0x41, 0x75, 0x66, 0x81, 0xe4, - 0x03, 0x58, 0x93, 0x08, 0xac, 0x9d, 0x59, 0x08, 0x41, 0x8d, 0xab, 0x3d, 0x49, 0x06, 0xb2, 0x07, - 0x25, 0xa6, 0xa2, 0x6b, 0xa5, 0x94, 0x2b, 0x4b, 0x82, 0x70, 0xc5, 0x1f, 0xb1, 0x91, 0x3b, 0x50, - 0x8e, 0x54, 0xbf, 0x24, 0x73, 0x8b, 0x4e, 0x4e, 0x09, 0x89, 0x19, 0xf5, 0x7d, 0xa8, 0x24, 0x96, - 0x47, 0xbe, 0x01, 0xe5, 0x11, 0x9d, 0xaa, 0x74, 0x4b, 0x06, 0xd0, 0xa5, 0x11, 0x9d, 0x62, 0xa6, - 0x45, 0xde, 0x80, 0xa2, 0x18, 0xec, 0x53, 0x79, 0x90, 0xab, 0x46, 0x61, 0x44, 0xa7, 0xdf, 0xa7, - 0x81, 0xfe, 0x0b, 0x0d, 0x36, 0xd3, 0xeb, 0x24, 0x6f, 0x03, 0x11, 0xb4, 0xb4, 0xcf, 0x4c, 0x67, - 0x3c, 0x92, 0x18, 0x19, 0x4a, 0xac, 0x8e, 0xe8, 0x74, 0xaf, 0xcf, 0x1e, 0x8c, 0x47, 0x38, 0x75, - 0x40, 0xee, 0x43, 0x2d, 0x24, 0x0e, 0x8b, 0x5d, 0x4a, 0x2b, 0xe7, 0x4f, 0x25, 0xbb, 0x77, 0x14, - 0x81, 0xcc, 0x75, 0x7f, 0x2d, 0x72, 0xdd, 0x4d, 0x29, 0x2f, 0x1c, 0xd1, 0xdf, 0x87, 0xea, 0xcc, - 0x8e, 0x89, 0x0e, 0x1b, 0xde, 0xb8, 0x6b, 0x1e, 0xb1, 0x63, 0x13, 0x55, 0x82, 0xa6, 0x5e, 0x36, - 0x2a, 0xde, 0xb8, 0xfb, 0x31, 0x3b, 0x16, 0x59, 0x47, 0xa0, 0xf7, 0x60, 0x33, 0x9d, 0x4c, 0x09, - 0xe0, 0xf0, 0xdd, 0xb1, 0x63, 0xe1, 0xba, 0xd7, 0x0c, 0xd9, 0x20, 0xb7, 0x60, 0x6d, 0xe2, 0x4a, - 0x6b, 0x3e, 0x2b, 0x7b, 0x3a, 0x74, 0x39, 0x4b, 0xa4, 0x64, 0x92, 0x47, 0x0f, 0x60, 0x0d, 0xed, - 0x52, 0xd8, 0x18, 0xa6, 0x45, 0x2a, 0x70, 0x11, 0xdf, 0xe4, 0x10, 0x80, 0x72, 0xee, 0xdb, 0xdd, - 0x71, 0x2c, 0xbe, 0x9e, 0x14, 0x3f, 0xb4, 0xbb, 0x41, 0xf3, 0x68, 0xd2, 0x3c, 0xa0, 0xb6, 0xdf, - 0xba, 0xa0, 0x2c, 0xfb, 0x5c, 0xcc, 0x93, 0xb0, 0xee, 0x84, 0x24, 0xfd, 0xab, 0x3c, 0x14, 0x64, - 0xba, 0x49, 0x3e, 0x4c, 0x17, 0x3f, 0x2a, 0xbb, 0x5b, 0x8b, 0x96, 0x2f, 0xa9, 0xd4, 0xea, 0xa3, - 0x08, 0xea, 0xea, 0x6c, 0x45, 0xa1, 0x55, 0x39, 0x79, 0xbe, 0x5d, 0xc4, 0xe8, 0xa3, 0x73, 0x27, - 0x2e, 0x2f, 0x2c, 0xca, 0xae, 0xc3, 0x5a, 0x46, 0xfe, 0x85, 0x6b, 0x19, 0x6d, 0xd8, 0x48, 0x84, - 0x5b, 0xb6, 0xa5, 0xf2, 0x94, 0xad, 0xb3, 0x2e, 0x5d, 0xe7, 0x8e, 0x5a, 0x7f, 0x25, 0x0a, 0xc7, - 0x3a, 0x16, 0xd9, 0x49, 0x27, 0xd9, 0x18, 0xb5, 0xc9, 0x70, 0x21, 0x91, 0x37, 0x8b, 0x98, 0x4d, - 0x5c, 0x07, 0x71, 0xf9, 0x25, 0x89, 0x8c, 0x1e, 0x4a, 0xa2, 0x03, 0x07, 0xaf, 0x41, 0x35, 0x0e, - 0x6c, 0x24, 0x49, 0x49, 0x4a, 0x89, 0xbb, 0x91, 0xf0, 0x3d, 0x38, 0xe7, 0xb0, 0x29, 0x37, 0x67, - 0xa9, 0xcb, 0x48, 0x4d, 0xc4, 0xd8, 0x61, 0x9a, 0xe3, 0x0a, 0x6c, 0xc6, 0x2e, 0x14, 0x69, 0x41, - 0x96, 0x3e, 0xa2, 0x5e, 0x24, 0x3b, 0x0f, 0xa5, 0x28, 0xec, 0xac, 0x20, 0x41, 0x91, 0xca, 0x68, - 0x33, 0x0a, 0x64, 0x7d, 0x16, 0x8c, 0x87, 0x5c, 0x09, 0x59, 0x47, 0x1a, 0x0c, 0x64, 0x0d, 0xd9, - 0x8f, 0xb4, 0x97, 0x60, 0x23, 0xf4, 0x2a, 0x92, 0x6e, 0x03, 0xe9, 0xd6, 0xc3, 0x4e, 0x24, 0xba, - 0x0e, 0x35, 0xcf, 0x77, 0x3d, 0x37, 0x60, 0xbe, 0x49, 0x2d, 0xcb, 0x67, 0x41, 0x50, 0xdf, 0x94, - 0xf2, 0xc2, 0xfe, 0x3d, 0xd9, 0xad, 0x7f, 0x13, 0x8a, 0x61, 0x3c, 0x7d, 0x0e, 0xd6, 0x5a, 0x91, - 0x87, 0xcc, 0x1b, 0xb2, 0x21, 0xf0, 0x75, 0xcf, 0xf3, 0x54, 0x75, 0x4d, 0x7c, 0xea, 0x43, 0x28, - 0xaa, 0x03, 0x9b, 0x5b, 0x53, 0xb9, 0x0f, 0xeb, 0x1e, 0xf5, 0xc5, 0x36, 0x92, 0x95, 0x95, 0x45, - 0x19, 0xe1, 0x01, 0xf5, 0xf9, 0x43, 0xc6, 0x53, 0x05, 0x96, 0x0a, 0xf2, 0xcb, 0x2e, 0xfd, 0x26, - 0x6c, 0xa4, 0x68, 0xc4, 0x32, 0xb9, 0xcb, 0xe9, 0x30, 0xbc, 0xe8, 0xd8, 0x88, 0x56, 0x92, 0x8b, - 0x57, 0xa2, 0xdf, 0x82, 0x72, 0x74, 0x56, 0x22, 0xd1, 0x08, 0x55, 0xa1, 0x29, 0xf5, 0xcb, 0x26, - 0x16, 0x91, 0xdc, 0x67, 0xcc, 0x57, 0xd6, 0x2f, 0x1b, 0x3a, 0x4b, 0x38, 0x26, 0x89, 0x66, 0xe4, - 0x36, 0x14, 0x95, 0x63, 0x52, 0xf7, 0x71, 0x51, 0xb9, 0xe8, 0x00, 0x3d, 0x55, 0x58, 0x2e, 0x92, - 0x7e, 0x2b, 0x9e, 0x26, 0x97, 0x9c, 0xe6, 0xa7, 0x50, 0x0a, 0x9d, 0x4f, 0x1a, 0x25, 0xe4, 0x0c, - 0x17, 0x97, 0xa1, 0x84, 0x9a, 0x24, 0x66, 0x14, 0xd6, 0x14, 0xd8, 0x7d, 0x87, 0x59, 0x66, 0x7c, - 0x05, 0x71, 0xce, 0x92, 0x51, 0x95, 0x03, 0xf7, 0xc2, 0xfb, 0xa5, 0xbf, 0x07, 0x05, 0xb9, 0xd6, - 0xb9, 0x2e, 0x6e, 0x1e, 0xb4, 0xfe, 0x43, 0x83, 0x52, 0x08, 0x1f, 0x73, 0x99, 0x52, 0x9b, 0xc8, - 0x7d, 0xdd, 0x4d, 0xbc, 0x7c, 0x97, 0xf4, 0x0e, 0x10, 0xb4, 0x14, 0x73, 0xe2, 0x72, 0xdb, 0xe9, - 0x9b, 0xf2, 0x2c, 0x64, 0x24, 0x58, 0xc3, 0x91, 0x43, 0x1c, 0x38, 0x10, 0xfd, 0x6f, 0x5d, 0x82, - 0x4a, 0xa2, 0xca, 0x45, 0x8a, 0xb0, 0xfa, 0x80, 0x3d, 0xab, 0xad, 0x90, 0x0a, 0x14, 0x0d, 0x86, - 0x35, 0x82, 0x9a, 0xb6, 0xfb, 0x55, 0x11, 0xaa, 0x7b, 0xad, 0xfd, 0xce, 0x9e, 0xe7, 0x0d, 0xed, - 0x1e, 0xe2, 0x19, 0xf9, 0x04, 0xf2, 0x98, 0x27, 0x67, 0x78, 0xdf, 0x69, 0x64, 0x29, 0x38, 0x11, - 0x03, 0xd6, 0x30, 0x9d, 0x26, 0x59, 0x9e, 0x7d, 0x1a, 0x99, 0xea, 0x50, 0x62, 0x91, 0x68, 0x70, - 0x19, 0x5e, 0x83, 0x1a, 0x59, 0x8a, 0x53, 0xe4, 0x33, 0x28, 0xc7, 0x79, 0x72, 0xd6, 0x37, 0xa2, - 0x46, 0xe6, 0xb2, 0x95, 0x90, 0x1f, 0x67, 0x06, 0x59, 0x5f, 0x48, 0x1a, 0x99, 0xeb, 0x35, 0xe4, - 0x09, 0x14, 0xc3, 0x1c, 0x2c, 0xdb, 0x2b, 0x4e, 0x23, 0x63, 0x49, 0x49, 0x1c, 0x9f, 0x4c, 0x9d, - 0xb3, 0x3c, 0x55, 0x35, 0x32, 0xd5, 0xcd, 0xc8, 0x63, 0x28, 0xa8, 0xe0, 0x37, 0xd3, 0xfb, 0x4c, - 0x23, 0x5b, 0xa1, 0x48, 0x28, 0x39, 0x2e, 0x4e, 0x64, 0x7d, 0x9e, 0x6b, 0x64, 0x2e, 0x18, 0x12, - 0x0a, 0x90, 0xc8, 0xa7, 0x33, 0xbf, 0xbb, 0x35, 0xb2, 0x17, 0x02, 0xc9, 0x8f, 0xa1, 0x14, 0x65, - 0x4d, 0x19, 0xdf, 0xbf, 0x1a, 0x59, 0x6b, 0x71, 0xad, 0xce, 0x7f, 0xfe, 0xb6, 0xa5, 0xfd, 0xf6, - 0x64, 0x4b, 0xfb, 0xe2, 0x64, 0x4b, 0xfb, 0xf2, 0x64, 0x4b, 0xfb, 0xd3, 0xc9, 0x96, 0xf6, 0xd7, - 0x93, 0x2d, 0xed, 0x0f, 0x7f, 0xdf, 0xd2, 0x7e, 0xf4, 0xf6, 0xd2, 0x17, 0xe6, 0xf8, 0x75, 0xbc, - 0x5b, 0x40, 0x87, 0xf5, 0xad, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, 0x5a, 0xa5, 0xa9, 0x6d, 0x32, - 0x1f, 0x00, 0x00, + 0x15, 0xde, 0xd1, 0x6a, 0x57, 0xd2, 0xd3, 0xee, 0x4a, 0x69, 0x3b, 0x89, 0x22, 0x92, 0x5d, 0xd7, + 0xf8, 0x6f, 0x9d, 0x04, 0x6d, 0x58, 0x2a, 0x54, 0x8c, 0x5d, 0xa1, 0x56, 0x6b, 0x07, 0xa9, 0x62, + 0x3b, 0x9b, 0xb1, 0xbd, 0x18, 0xa8, 0xca, 0x54, 0x4b, 0xd3, 0x96, 0xa6, 0x56, 0x9a, 0x99, 0xcc, + 0xb4, 0x64, 0x89, 0xe2, 0x4e, 0x51, 0xc5, 0x81, 0x0b, 0x55, 0x5c, 0xb8, 0x73, 0xe4, 0xc0, 0x21, + 0x47, 0x8e, 0x39, 0x70, 0xe0, 0xc0, 0xd9, 0xc0, 0xc2, 0x89, 0xca, 0x91, 0xa2, 0x38, 0x52, 0xfd, + 0xba, 0xe7, 0x4f, 0x2b, 0xad, 0xc6, 0xc1, 0x37, 0x2e, 0xd2, 0x74, 0xf7, 0x7b, 0xaf, 0xbb, 0x5f, + 0xbf, 0x7e, 0xdf, 0x7b, 0xaf, 0xe1, 0x35, 0xda, 0xe9, 0xda, 0x7b, 0x7c, 0xea, 0xb1, 0x40, 0xfe, + 0x36, 0x3c, 0xdf, 0xe5, 0x2e, 0x79, 0x95, 0x33, 0xc7, 0x62, 0xfe, 0xd0, 0x76, 0x78, 0x43, 0x90, + 0x34, 0x70, 0xb0, 0x7e, 0x8d, 0xf7, 0x6d, 0xdf, 0x32, 0x3d, 0xea, 0xf3, 0xe9, 0x1e, 0x52, 0xee, + 0xf5, 0xdc, 0x9e, 0x1b, 0x7f, 0x49, 0xf6, 0x7a, 0xbd, 0xeb, 0x4f, 0x3d, 0xee, 0xee, 0x0d, 0x99, + 0x7f, 0x32, 0x60, 0xea, 0x4f, 0x8d, 0x5d, 0x18, 0xd8, 0x9d, 0x60, 0xef, 0x64, 0x9c, 0x9c, 0xaf, + 0xbe, 0xd3, 0x73, 0xdd, 0xde, 0x80, 0x49, 0x99, 0x9d, 0xd1, 0xd3, 0x3d, 0x6e, 0x0f, 0x59, 0xc0, + 0xe9, 0xd0, 0x53, 0x04, 0xdb, 0xb3, 0x04, 0xd6, 0xc8, 0xa7, 0xdc, 0x76, 0x1d, 0x39, 0xae, 0xff, + 0x7b, 0x0d, 0x0a, 0x06, 0xfb, 0x7c, 0xc4, 0x02, 0x4e, 0x3e, 0x80, 0x3c, 0xeb, 0xf6, 0xdd, 0x5a, + 0xee, 0x92, 0xb6, 0x5b, 0xde, 0xd7, 0x1b, 0x73, 0xf7, 0xd2, 0x50, 0xd4, 0x77, 0xbb, 0x7d, 0xb7, + 0xb5, 0x62, 0x20, 0x07, 0xb9, 0x05, 0x6b, 0x4f, 0x07, 0xa3, 0xa0, 0x5f, 0x5b, 0x45, 0xd6, 0xcb, + 0xe7, 0xb3, 0x7e, 0x24, 0x48, 0x5b, 0x2b, 0x86, 0xe4, 0x11, 0xd3, 0xda, 0xce, 0x53, 0xb7, 0x96, + 0xcf, 0x32, 0x6d, 0xdb, 0x79, 0x8a, 0xd3, 0x0a, 0x0e, 0xd2, 0x02, 0x08, 0x18, 0x37, 0x5d, 0x4f, + 0x6c, 0xa8, 0xb6, 0x86, 0xfc, 0xd7, 0xcf, 0xe7, 0x7f, 0xc8, 0xf8, 0x27, 0x48, 0xde, 0x5a, 0x31, + 0x4a, 0x41, 0xd8, 0x10, 0x92, 0x6c, 0xc7, 0xe6, 0x66, 0xb7, 0x4f, 0x6d, 0xa7, 0xb6, 0x9e, 0x45, + 0x52, 0xdb, 0xb1, 0xf9, 0xa1, 0x20, 0x17, 0x92, 0xec, 0xb0, 0x21, 0x54, 0xf1, 0xf9, 0x88, 0xf9, + 0xd3, 0x5a, 0x21, 0x8b, 0x2a, 0x3e, 0x15, 0xa4, 0x42, 0x15, 0xc8, 0x43, 0x3e, 0x86, 0x72, 0x87, + 0xf5, 0x6c, 0xc7, 0xec, 0x0c, 0xdc, 0xee, 0x49, 0xad, 0x88, 0x22, 0x76, 0xcf, 0x17, 0xd1, 0x14, + 0x0c, 0x4d, 0x41, 0xdf, 0x5a, 0x31, 0xa0, 0x13, 0xb5, 0x48, 0x13, 0x8a, 0xdd, 0x3e, 0xeb, 0x9e, + 0x98, 0x7c, 0x52, 0x2b, 0xa1, 0xa4, 0xab, 0xe7, 0x4b, 0x3a, 0x14, 0xd4, 0x8f, 0x26, 0xad, 0x15, + 0xa3, 0xd0, 0x95, 0x9f, 0x42, 0x2f, 0x16, 0x1b, 0xd8, 0x63, 0xe6, 0x0b, 0x29, 0x17, 0xb2, 0xe8, + 0xe5, 0x8e, 0xa4, 0x47, 0x39, 0x25, 0x2b, 0x6c, 0x90, 0xbb, 0x50, 0x62, 0x8e, 0xa5, 0x36, 0x56, + 0x46, 0x41, 0xd7, 0x96, 0x58, 0x98, 0x63, 0x85, 0xdb, 0x2a, 0x32, 0xf5, 0x4d, 0x3e, 0x84, 0xf5, + 0xae, 0x3b, 0x1c, 0xda, 0xbc, 0xb6, 0x81, 0x32, 0xae, 0x2c, 0xd9, 0x12, 0xd2, 0xb6, 0x56, 0x0c, + 0xc5, 0xd5, 0x2c, 0xc0, 0xda, 0x98, 0x0e, 0x46, 0x4c, 0xbf, 0x0e, 0xe5, 0x84, 0x25, 0x93, 0x1a, + 0x14, 0x86, 0x2c, 0x08, 0x68, 0x8f, 0xd5, 0xb4, 0x4b, 0xda, 0x6e, 0xc9, 0x08, 0x9b, 0xfa, 0x16, + 0x6c, 0x24, 0xed, 0x56, 0x1f, 0x46, 0x8c, 0xc2, 0x16, 0x05, 0xe3, 0x98, 0xf9, 0x81, 0x30, 0x40, + 0xc5, 0xa8, 0x9a, 0xe4, 0x32, 0x6c, 0xe2, 0x6e, 0xcd, 0x70, 0x5c, 0xdc, 0xab, 0xbc, 0xb1, 0x81, + 0x9d, 0xc7, 0x8a, 0x68, 0x07, 0xca, 0xde, 0xbe, 0x17, 0x91, 0xac, 0x22, 0x09, 0x78, 0xfb, 0x9e, + 0x22, 0xd0, 0xbf, 0x0b, 0xd5, 0x59, 0xd3, 0x25, 0x55, 0x58, 0x3d, 0x61, 0x53, 0x35, 0x9f, 0xf8, + 0x24, 0x17, 0xd5, 0xb6, 0x70, 0x8e, 0x92, 0xa1, 0xf6, 0xf8, 0xbb, 0x5c, 0xc4, 0x1c, 0x59, 0xab, + 0xb8, 0x6e, 0xc2, 0x49, 0x20, 0x77, 0x79, 0xbf, 0xde, 0x90, 0x0e, 0xa2, 0x11, 0x3a, 0x88, 0xc6, + 0xa3, 0xd0, 0x83, 0x34, 0x8b, 0x5f, 0x3e, 0xdf, 0x59, 0xf9, 0xe5, 0x5f, 0x76, 0x34, 0x03, 0x39, + 0xc8, 0x1b, 0xc2, 0xa0, 0xa8, 0xed, 0x98, 0xb6, 0xa5, 0xe6, 0x29, 0x60, 0xbb, 0x6d, 0x91, 0x4f, + 0xa1, 0xda, 0x75, 0x9d, 0x80, 0x39, 0xc1, 0x28, 0x10, 0x6e, 0x8e, 0x0e, 0x03, 0xe5, 0x0b, 0x16, + 0x1d, 0xf2, 0x61, 0x48, 0x7e, 0x84, 0xd4, 0x46, 0xa5, 0x9b, 0xee, 0x20, 0xf7, 0x00, 0xc6, 0x74, + 0x60, 0x5b, 0x94, 0xbb, 0x7e, 0x50, 0xcb, 0x5f, 0x5a, 0x3d, 0x47, 0xd8, 0x71, 0x48, 0xf8, 0xd8, + 0xb3, 0x28, 0x67, 0xcd, 0xbc, 0x58, 0xb9, 0x91, 0xe0, 0x27, 0xd7, 0xa0, 0x42, 0x3d, 0xcf, 0x0c, + 0x38, 0xe5, 0xcc, 0xec, 0x4c, 0x39, 0x0b, 0xd0, 0x5f, 0x6c, 0x18, 0x9b, 0xd4, 0xf3, 0x1e, 0x8a, + 0xde, 0xa6, 0xe8, 0xd4, 0xad, 0xe8, 0xb4, 0xf1, 0x6a, 0x12, 0x02, 0x79, 0x8b, 0x72, 0x8a, 0xda, + 0xda, 0x30, 0xf0, 0x5b, 0xf4, 0x79, 0x94, 0xf7, 0x95, 0x0e, 0xf0, 0x9b, 0xbc, 0x06, 0xeb, 0x7d, + 0x66, 0xf7, 0xfa, 0x1c, 0xb7, 0xbd, 0x6a, 0xa8, 0x96, 0x38, 0x18, 0xcf, 0x77, 0xc7, 0x0c, 0xbd, + 0x5b, 0xd1, 0x90, 0x0d, 0xfd, 0x57, 0x39, 0x78, 0xe5, 0xcc, 0xf5, 0x15, 0x72, 0xfb, 0x34, 0xe8, + 0x87, 0x73, 0x89, 0x6f, 0x72, 0x4b, 0xc8, 0xa5, 0x16, 0xf3, 0x95, 0x57, 0x7e, 0x6b, 0x81, 0x06, + 0x5a, 0x48, 0xa4, 0x36, 0xae, 0x58, 0xc8, 0x63, 0xa8, 0x0e, 0x68, 0xc0, 0x4d, 0x69, 0xfb, 0x26, + 0x7a, 0xd9, 0xd5, 0x73, 0x3d, 0xc1, 0x3d, 0x1a, 0xde, 0x19, 0x61, 0xdc, 0x4a, 0xdc, 0xd6, 0x20, + 0xd5, 0x4b, 0x9e, 0xc0, 0xc5, 0xce, 0xf4, 0x27, 0xd4, 0xe1, 0xb6, 0xc3, 0xcc, 0x33, 0x67, 0xb4, + 0xb3, 0x40, 0xf4, 0xdd, 0xb1, 0x6d, 0x31, 0xa7, 0x1b, 0x1e, 0xce, 0x85, 0x48, 0x44, 0x74, 0x78, + 0x81, 0xfe, 0x04, 0xb6, 0xd2, 0xbe, 0x88, 0x6c, 0x41, 0x8e, 0x4f, 0x94, 0x46, 0x72, 0x7c, 0x42, + 0xbe, 0x03, 0x79, 0x21, 0x0e, 0xb5, 0xb1, 0xb5, 0x10, 0x2c, 0x14, 0xf7, 0xa3, 0xa9, 0xc7, 0x0c, + 0xa4, 0xd7, 0xf5, 0xe8, 0x26, 0x44, 0xfe, 0x69, 0x56, 0xb6, 0x7e, 0x03, 0x2a, 0x33, 0xae, 0x27, + 0x71, 0xac, 0x5a, 0xf2, 0x58, 0xf5, 0x0a, 0x6c, 0xa6, 0x3c, 0x8c, 0xfe, 0xc7, 0x75, 0x28, 0x1a, + 0x2c, 0xf0, 0x84, 0x11, 0x93, 0x16, 0x94, 0xd8, 0xa4, 0xcb, 0x24, 0x2c, 0x69, 0x4b, 0x9c, 0xb8, + 0xe4, 0xb9, 0x1b, 0xd2, 0x0b, 0xaf, 0x19, 0x31, 0x93, 0x9b, 0x29, 0x48, 0xbe, 0xbc, 0x4c, 0x48, + 0x12, 0x93, 0x6f, 0xa7, 0x31, 0xf9, 0xca, 0x12, 0xde, 0x19, 0x50, 0xbe, 0x99, 0x02, 0xe5, 0x65, + 0x13, 0xa7, 0x50, 0xb9, 0x3d, 0x07, 0x95, 0x97, 0x6d, 0x7f, 0x01, 0x2c, 0xb7, 0xe7, 0xc0, 0xf2, + 0xee, 0xd2, 0xb5, 0xcc, 0xc5, 0xe5, 0xdb, 0x69, 0x5c, 0x5e, 0xa6, 0x8e, 0x19, 0x60, 0xbe, 0x37, + 0x0f, 0x98, 0x6f, 0x2c, 0x91, 0xb1, 0x10, 0x99, 0x0f, 0xcf, 0x20, 0xf3, 0xb5, 0x25, 0xa2, 0xe6, + 0x40, 0x73, 0x3b, 0x05, 0xcd, 0x90, 0x49, 0x37, 0x0b, 0xb0, 0xf9, 0xa3, 0xb3, 0xd8, 0x7c, 0x7d, + 0x99, 0xa9, 0xcd, 0x03, 0xe7, 0xef, 0xcd, 0x80, 0xf3, 0xd5, 0x65, 0xbb, 0x5a, 0x88, 0xce, 0x37, + 0x84, 0x7f, 0x9c, 0xb9, 0x19, 0xc2, 0x97, 0x32, 0xdf, 0x77, 0x7d, 0x05, 0x7c, 0xb2, 0xa1, 0xef, + 0x0a, 0x8f, 0x1d, 0xdb, 0xff, 0x39, 0x48, 0x8e, 0x97, 0x36, 0x61, 0xed, 0xfa, 0x17, 0x5a, 0xcc, + 0x8b, 0x9e, 0x2d, 0xe9, 0xed, 0x4b, 0xca, 0xdb, 0x27, 0x00, 0x3e, 0x97, 0x06, 0xf8, 0x1d, 0x28, + 0x0b, 0x4c, 0x99, 0xc1, 0x6e, 0xea, 0x85, 0xd8, 0x4d, 0xde, 0x86, 0x57, 0xd0, 0xff, 0xca, 0x30, + 0x40, 0x39, 0x92, 0x3c, 0x3a, 0x92, 0x8a, 0x18, 0x90, 0x1a, 0x94, 0x40, 0xf1, 0x4d, 0xb8, 0x90, + 0xa0, 0x15, 0x72, 0x11, 0x0b, 0x24, 0x48, 0x55, 0x23, 0xea, 0x03, 0xcf, 0x6b, 0xd1, 0xa0, 0xaf, + 0xdf, 0x8f, 0x15, 0x14, 0xc7, 0x05, 0x04, 0xf2, 0x5d, 0xd7, 0x92, 0xfb, 0xde, 0x34, 0xf0, 0x5b, + 0xc4, 0x0a, 0x03, 0xb7, 0x87, 0x8b, 0x2b, 0x19, 0xe2, 0x53, 0x50, 0x45, 0x57, 0xbb, 0x24, 0xef, + 0xac, 0xfe, 0x7b, 0x2d, 0x96, 0x17, 0x87, 0x0a, 0xf3, 0x50, 0x5d, 0x7b, 0x99, 0xa8, 0x9e, 0xfb, + 0xdf, 0x50, 0x5d, 0xff, 0x97, 0x16, 0x1f, 0x69, 0x84, 0xd7, 0x5f, 0x4f, 0x05, 0xc2, 0xba, 0x6c, + 0xc7, 0x62, 0x13, 0x54, 0xf9, 0xaa, 0x21, 0x1b, 0x61, 0xa8, 0xb5, 0x8e, 0xc7, 0x90, 0x0e, 0xb5, + 0x0a, 0xd8, 0x27, 0x1b, 0xe4, 0x7d, 0xc4, 0x79, 0xf7, 0xa9, 0x72, 0x0d, 0x29, 0x10, 0x94, 0x49, + 0x5d, 0x43, 0x65, 0x73, 0x47, 0x82, 0xcc, 0x90, 0xd4, 0x09, 0x7c, 0x29, 0xa5, 0xc2, 0x86, 0x37, + 0xa1, 0x24, 0x96, 0x1e, 0x78, 0xb4, 0xcb, 0xf0, 0x6e, 0x97, 0x8c, 0xb8, 0x43, 0xb7, 0x80, 0x9c, + 0xf5, 0x31, 0xe4, 0x01, 0xac, 0xb3, 0x31, 0x73, 0xb8, 0x38, 0x23, 0xa1, 0xd6, 0x37, 0x17, 0x02, + 0x31, 0x73, 0x78, 0xb3, 0x26, 0x94, 0xf9, 0xcf, 0xe7, 0x3b, 0x55, 0xc9, 0xf3, 0xae, 0x3b, 0xb4, + 0x39, 0x1b, 0x7a, 0x7c, 0x6a, 0x28, 0x29, 0xfa, 0xcf, 0x72, 0x02, 0x0f, 0x53, 0xfe, 0x67, 0xae, + 0x7a, 0xc3, 0x4b, 0x93, 0x4b, 0x84, 0x48, 0xd9, 0x54, 0xfe, 0x16, 0x40, 0x8f, 0x06, 0xe6, 0x33, + 0xea, 0x70, 0x66, 0x29, 0xbd, 0x97, 0x7a, 0x34, 0xf8, 0x01, 0x76, 0x88, 0x78, 0x53, 0x0c, 0x8f, + 0x02, 0x66, 0xe1, 0x01, 0xac, 0x1a, 0x85, 0x1e, 0x0d, 0x1e, 0x07, 0xcc, 0x4a, 0xec, 0xb5, 0xf0, + 0x32, 0xf6, 0x9a, 0xd6, 0x77, 0x71, 0x56, 0xdf, 0x3f, 0xcf, 0xc5, 0xb7, 0x23, 0x0e, 0x1f, 0xfe, + 0x3f, 0x75, 0xf1, 0x1b, 0xcc, 0x29, 0xd2, 0x20, 0x40, 0x7e, 0x08, 0xaf, 0x44, 0xb7, 0xd2, 0x1c, + 0xe1, 0x6d, 0x0d, 0xad, 0xf0, 0xc5, 0x2e, 0x77, 0x75, 0x9c, 0xee, 0x0e, 0xc8, 0x67, 0xf0, 0xfa, + 0x8c, 0x0f, 0x8a, 0x26, 0xc8, 0xbd, 0x90, 0x2b, 0x7a, 0x35, 0xed, 0x8a, 0x42, 0xf9, 0xb1, 0xf6, + 0x56, 0x5f, 0xca, 0xad, 0xb9, 0x22, 0x42, 0xd8, 0x24, 0xbc, 0xcd, 0xb3, 0x09, 0xfd, 0xcf, 0x1a, + 0x54, 0x66, 0x16, 0x48, 0x3e, 0x80, 0x35, 0x89, 0xc0, 0xda, 0xb9, 0x85, 0x10, 0xd4, 0xb8, 0xda, + 0x93, 0x64, 0x20, 0x07, 0x50, 0x64, 0x2a, 0xba, 0x56, 0x4a, 0xb9, 0xba, 0x24, 0x08, 0x57, 0xfc, + 0x11, 0x1b, 0xb9, 0x03, 0xa5, 0x48, 0xf5, 0x4b, 0x32, 0xb7, 0xe8, 0xe4, 0x94, 0x90, 0x98, 0x51, + 0x3f, 0x84, 0x72, 0x62, 0x79, 0xe4, 0x1b, 0x50, 0x1a, 0xd2, 0x89, 0x4a, 0xb7, 0x64, 0x00, 0x5d, + 0x1c, 0xd2, 0x09, 0x66, 0x5a, 0xe4, 0x75, 0x28, 0x88, 0xc1, 0x1e, 0x95, 0x07, 0xb9, 0x6a, 0xac, + 0x0f, 0xe9, 0xe4, 0xfb, 0x34, 0xd0, 0x7f, 0xa1, 0xc1, 0x56, 0x7a, 0x9d, 0xe4, 0x1d, 0x20, 0x82, + 0x96, 0xf6, 0x98, 0xe9, 0x8c, 0x86, 0x12, 0x23, 0x43, 0x89, 0x95, 0x21, 0x9d, 0x1c, 0xf4, 0xd8, + 0x83, 0xd1, 0x10, 0xa7, 0x0e, 0xc8, 0x7d, 0xa8, 0x86, 0xc4, 0x61, 0xb1, 0x4b, 0x69, 0xe5, 0x8d, + 0x33, 0xc9, 0xee, 0x1d, 0x45, 0x20, 0x73, 0xdd, 0x5f, 0x8b, 0x5c, 0x77, 0x4b, 0xca, 0x0b, 0x47, + 0xf4, 0xf7, 0xa1, 0x32, 0xb3, 0x63, 0xa2, 0xc3, 0xa6, 0x37, 0xea, 0x98, 0x27, 0x6c, 0x6a, 0xa2, + 0x4a, 0xd0, 0xd4, 0x4b, 0x46, 0xd9, 0x1b, 0x75, 0x3e, 0x66, 0x53, 0x91, 0x75, 0x04, 0x7a, 0x17, + 0xb6, 0xd2, 0xc9, 0x94, 0x00, 0x0e, 0xdf, 0x1d, 0x39, 0x16, 0xae, 0x7b, 0xcd, 0x90, 0x0d, 0x72, + 0x0b, 0xd6, 0xc6, 0xae, 0xb4, 0xe6, 0xf3, 0xb2, 0xa7, 0x63, 0x97, 0xb3, 0x44, 0x4a, 0x26, 0x79, + 0xf4, 0x00, 0xd6, 0xd0, 0x2e, 0x85, 0x8d, 0x61, 0x5a, 0xa4, 0x02, 0x17, 0xf1, 0x4d, 0x8e, 0x01, + 0x28, 0xe7, 0xbe, 0xdd, 0x19, 0xc5, 0xe2, 0x6b, 0x49, 0xf1, 0x03, 0xbb, 0x13, 0x34, 0x4e, 0xc6, + 0x8d, 0x23, 0x6a, 0xfb, 0xcd, 0x37, 0x95, 0x65, 0x5f, 0x8c, 0x79, 0x12, 0xd6, 0x9d, 0x90, 0xa4, + 0x7f, 0x95, 0x87, 0x75, 0x99, 0x6e, 0x92, 0x0f, 0xd3, 0xc5, 0x8f, 0xf2, 0xfe, 0xf6, 0xa2, 0xe5, + 0x4b, 0x2a, 0xb5, 0xfa, 0x28, 0x82, 0xba, 0x36, 0x5b, 0x51, 0x68, 0x96, 0x4f, 0x9f, 0xef, 0x14, + 0x30, 0xfa, 0x68, 0xdf, 0x89, 0xcb, 0x0b, 0x8b, 0xb2, 0xeb, 0xb0, 0x96, 0x91, 0x7f, 0xe1, 0x5a, + 0x46, 0x0b, 0x36, 0x13, 0xe1, 0x96, 0x6d, 0xa9, 0x3c, 0x65, 0xfb, 0xbc, 0x4b, 0xd7, 0xbe, 0xa3, + 0xd6, 0x5f, 0x8e, 0xc2, 0xb1, 0xb6, 0x45, 0x76, 0xd3, 0x49, 0x36, 0x46, 0x6d, 0x32, 0x5c, 0x48, + 0xe4, 0xcd, 0x22, 0x66, 0x13, 0xd7, 0x41, 0x5c, 0x7e, 0x49, 0x22, 0xa3, 0x87, 0xa2, 0xe8, 0xc0, + 0xc1, 0xeb, 0x50, 0x89, 0x03, 0x1b, 0x49, 0x52, 0x94, 0x52, 0xe2, 0x6e, 0x24, 0x7c, 0x0f, 0x2e, + 0x3a, 0x6c, 0xc2, 0xcd, 0x59, 0xea, 0x12, 0x52, 0x13, 0x31, 0x76, 0x9c, 0xe6, 0xb8, 0x0a, 0x5b, + 0xb1, 0x0b, 0x45, 0x5a, 0x90, 0xa5, 0x8f, 0xa8, 0x17, 0xc9, 0xde, 0x80, 0x62, 0x14, 0x76, 0x96, + 0x91, 0xa0, 0x40, 0x65, 0xb4, 0x19, 0x05, 0xb2, 0x3e, 0x0b, 0x46, 0x03, 0xae, 0x84, 0x6c, 0x20, + 0x0d, 0x06, 0xb2, 0x86, 0xec, 0x47, 0xda, 0xcb, 0xb0, 0x19, 0x7a, 0x15, 0x49, 0xb7, 0x89, 0x74, + 0x1b, 0x61, 0x27, 0x12, 0xdd, 0x80, 0xaa, 0xe7, 0xbb, 0x9e, 0x1b, 0x30, 0xdf, 0xa4, 0x96, 0xe5, + 0xb3, 0x20, 0xa8, 0x6d, 0x49, 0x79, 0x61, 0xff, 0x81, 0xec, 0xd6, 0xbf, 0x05, 0x85, 0x30, 0x9e, + 0xbe, 0x08, 0x6b, 0xcd, 0xc8, 0x43, 0xe6, 0x0d, 0xd9, 0x10, 0xf8, 0x7a, 0xe0, 0x79, 0xaa, 0xba, + 0x26, 0x3e, 0xf5, 0x01, 0x14, 0xd4, 0x81, 0xcd, 0xad, 0xa9, 0xdc, 0x87, 0x0d, 0x8f, 0xfa, 0x62, + 0x1b, 0xc9, 0xca, 0xca, 0xa2, 0x8c, 0xf0, 0x88, 0xfa, 0xfc, 0x21, 0xe3, 0xa9, 0x02, 0x4b, 0x19, + 0xf9, 0x65, 0x97, 0x7e, 0x13, 0x36, 0x53, 0x34, 0x62, 0x99, 0xdc, 0xe5, 0x74, 0x10, 0x5e, 0x74, + 0x6c, 0x44, 0x2b, 0xc9, 0xc5, 0x2b, 0xd1, 0x6f, 0x41, 0x29, 0x3a, 0x2b, 0x91, 0x68, 0x84, 0xaa, + 0xd0, 0x94, 0xfa, 0x65, 0x13, 0x8b, 0x48, 0xee, 0x33, 0xe6, 0x2b, 0xeb, 0x97, 0x0d, 0x9d, 0x25, + 0x1c, 0x93, 0x44, 0x33, 0x72, 0x1b, 0x0a, 0xca, 0x31, 0xa9, 0xfb, 0xb8, 0xa8, 0x5c, 0x74, 0x84, + 0x9e, 0x2a, 0x2c, 0x17, 0x49, 0xbf, 0x15, 0x4f, 0x93, 0x4b, 0x4e, 0xf3, 0x53, 0x28, 0x86, 0xce, + 0x27, 0x8d, 0x12, 0x72, 0x86, 0x4b, 0xcb, 0x50, 0x42, 0x4d, 0x12, 0x33, 0x0a, 0x6b, 0x0a, 0xec, + 0x9e, 0xc3, 0x2c, 0x33, 0xbe, 0x82, 0x38, 0x67, 0xd1, 0xa8, 0xc8, 0x81, 0x7b, 0xe1, 0xfd, 0xd2, + 0xdf, 0x83, 0x75, 0xb9, 0xd6, 0xb9, 0x2e, 0x6e, 0x1e, 0xb4, 0xfe, 0x43, 0x83, 0x62, 0x08, 0x1f, + 0x73, 0x99, 0x52, 0x9b, 0xc8, 0x7d, 0xdd, 0x4d, 0xbc, 0x7c, 0x97, 0xf4, 0x2e, 0x10, 0xb4, 0x14, + 0x73, 0xec, 0x72, 0xdb, 0xe9, 0x99, 0xf2, 0x2c, 0x64, 0x24, 0x58, 0xc5, 0x91, 0x63, 0x1c, 0x38, + 0x12, 0xfd, 0x6f, 0x5f, 0x86, 0x72, 0xa2, 0xca, 0x45, 0x0a, 0xb0, 0xfa, 0x80, 0x3d, 0xab, 0xae, + 0x90, 0x32, 0x14, 0x0c, 0x86, 0x35, 0x82, 0xaa, 0xb6, 0xff, 0x55, 0x01, 0x2a, 0x07, 0xcd, 0xc3, + 0xf6, 0x81, 0xe7, 0x0d, 0xec, 0x2e, 0xe2, 0x19, 0xf9, 0x04, 0xf2, 0x98, 0x27, 0x67, 0x78, 0xdf, + 0xa9, 0x67, 0x29, 0x38, 0x11, 0x03, 0xd6, 0x30, 0x9d, 0x26, 0x59, 0x9e, 0x7d, 0xea, 0x99, 0xea, + 0x50, 0x62, 0x91, 0x68, 0x70, 0x19, 0x5e, 0x83, 0xea, 0x59, 0x8a, 0x53, 0xe4, 0x33, 0x28, 0xc5, + 0x79, 0x72, 0xd6, 0x37, 0xa2, 0x7a, 0xe6, 0xb2, 0x95, 0x90, 0x1f, 0x67, 0x06, 0x59, 0x5f, 0x48, + 0xea, 0x99, 0xeb, 0x35, 0xe4, 0x09, 0x14, 0xc2, 0x1c, 0x2c, 0xdb, 0x2b, 0x4e, 0x3d, 0x63, 0x49, + 0x49, 0x1c, 0x9f, 0x4c, 0x9d, 0xb3, 0x3c, 0x55, 0xd5, 0x33, 0xd5, 0xcd, 0xc8, 0x63, 0x58, 0x57, + 0xc1, 0x6f, 0xa6, 0xf7, 0x99, 0x7a, 0xb6, 0x42, 0x91, 0x50, 0x72, 0x5c, 0x9c, 0xc8, 0xfa, 0x3c, + 0x57, 0xcf, 0x5c, 0x30, 0x24, 0x14, 0x20, 0x91, 0x4f, 0x67, 0x7e, 0x77, 0xab, 0x67, 0x2f, 0x04, + 0x92, 0x1f, 0x43, 0x31, 0xca, 0x9a, 0x32, 0xbe, 0x7f, 0xd5, 0xb3, 0xd6, 0xe2, 0x9a, 0xed, 0xff, + 0xfc, 0x6d, 0x5b, 0xfb, 0xed, 0xe9, 0xb6, 0xf6, 0xc5, 0xe9, 0xb6, 0xf6, 0xe5, 0xe9, 0xb6, 0xf6, + 0xa7, 0xd3, 0x6d, 0xed, 0xaf, 0xa7, 0xdb, 0xda, 0x1f, 0xfe, 0xbe, 0xad, 0xfd, 0xe8, 0x9d, 0x9e, + 0xcd, 0xfb, 0xa3, 0x4e, 0xa3, 0xeb, 0x0e, 0xf7, 0x62, 0x81, 0xc9, 0xcf, 0xf8, 0x51, 0xbb, 0xb3, + 0x8e, 0x0e, 0xeb, 0xdb, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xce, 0x64, 0xb9, 0xe4, 0xe9, 0x1e, + 0x00, 0x00, } func (this *Request) Equal(that interface{}) bool { diff --git a/abci/types/types.proto b/abci/types/types.proto index 4483e685f..0d47ad9b3 100644 --- a/abci/types/types.proto +++ b/abci/types/types.proto @@ -1,12 +1,12 @@ syntax = "proto3"; package tendermint.abci.types; -option go_package = "github.com/tendermint/tendermint/abci/types"; +option go_package = "github.com/tendermint/tendermint/abci/types"; // For more information on gogo.proto, see: // https://github.com/gogo/protobuf/blob/master/extensions.md -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; -import "github.com/tendermint/tendermint/crypto/merkle/merkle.proto"; -import "github.com/tendermint/tendermint/libs/kv/types.proto"; +import "third_party/proto/gogoproto/gogo.proto"; +import "crypto/merkle/merkle.proto"; +import "libs/kv/types.proto"; import "google/protobuf/timestamp.proto"; import "google/protobuf/duration.proto"; @@ -14,31 +14,31 @@ import "google/protobuf/duration.proto"; // NOTE: When using custom types, mind the warnings. // https://github.com/gogo/protobuf/blob/master/custom_types.md#warnings-and-issues -option (gogoproto.marshaler_all) = true; -option (gogoproto.unmarshaler_all) = true; -option (gogoproto.sizer_all) = true; +option (gogoproto.marshaler_all) = true; +option (gogoproto.unmarshaler_all) = true; +option (gogoproto.sizer_all) = true; option (gogoproto.goproto_registration) = true; // Generate tests option (gogoproto.populate_all) = true; -option (gogoproto.equal_all) = true; -option (gogoproto.testgen_all) = true; +option (gogoproto.equal_all) = true; +option (gogoproto.testgen_all) = true; //---------------------------------------- // Request types message Request { oneof value { - RequestEcho echo = 2; - RequestFlush flush = 3; - RequestInfo info = 4; - RequestSetOption set_option = 5; - RequestInitChain init_chain = 6; - RequestQuery query = 7; + RequestEcho echo = 2; + RequestFlush flush = 3; + RequestInfo info = 4; + RequestSetOption set_option = 5; + RequestInitChain init_chain = 6; + RequestQuery query = 7; RequestBeginBlock begin_block = 8; - RequestCheckTx check_tx = 9; - RequestDeliverTx deliver_tx = 19; - RequestEndBlock end_block = 11; - RequestCommit commit = 12; + RequestCheckTx check_tx = 9; + RequestDeliverTx deliver_tx = 19; + RequestEndBlock end_block = 11; + RequestCommit commit = 12; } } @@ -46,50 +46,49 @@ message RequestEcho { string message = 1; } -message RequestFlush { -} +message RequestFlush {} message RequestInfo { - string version = 1; + string version = 1; uint64 block_version = 2; - uint64 p2p_version = 3; + uint64 p2p_version = 3; } // nondeterministic message RequestSetOption { - string key = 1; + string key = 1; string value = 2; } message RequestInitChain { - google.protobuf.Timestamp time = 1 [(gogoproto.nullable)=false, (gogoproto.stdtime)=true]; - string chain_id = 2; - ConsensusParams consensus_params = 3; - repeated ValidatorUpdate validators = 4 [(gogoproto.nullable)=false]; - bytes app_state_bytes = 5; + google.protobuf.Timestamp time = 1 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + string chain_id = 2; + ConsensusParams consensus_params = 3; + repeated ValidatorUpdate validators = 4 [(gogoproto.nullable) = false]; + bytes app_state_bytes = 5; } message RequestQuery { - bytes data = 1; - string path = 2; - int64 height = 3; - bool prove = 4; + bytes data = 1; + string path = 2; + int64 height = 3; + bool prove = 4; } message RequestBeginBlock { - bytes hash = 1; - Header header = 2 [(gogoproto.nullable)=false]; - LastCommitInfo last_commit_info = 3 [(gogoproto.nullable)=false]; - repeated Evidence byzantine_validators = 4 [(gogoproto.nullable)=false]; + bytes hash = 1; + Header header = 2 [(gogoproto.nullable) = false]; + LastCommitInfo last_commit_info = 3 [(gogoproto.nullable) = false]; + repeated Evidence byzantine_validators = 4 [(gogoproto.nullable) = false]; } enum CheckTxType { - New = 0; + New = 0; Recheck = 1; } message RequestCheckTx { - bytes tx = 1; + bytes tx = 1; CheckTxType type = 2; } @@ -101,26 +100,25 @@ message RequestEndBlock { int64 height = 1; } -message RequestCommit { -} +message RequestCommit {} //---------------------------------------- // Response types message Response { oneof value { - ResponseException exception = 1; - ResponseEcho echo = 2; - ResponseFlush flush = 3; - ResponseInfo info = 4; - ResponseSetOption set_option = 5; - ResponseInitChain init_chain = 6; - ResponseQuery query = 7; + ResponseException exception = 1; + ResponseEcho echo = 2; + ResponseFlush flush = 3; + ResponseInfo info = 4; + ResponseSetOption set_option = 5; + ResponseInitChain init_chain = 6; + ResponseQuery query = 7; ResponseBeginBlock begin_block = 8; - ResponseCheckTx check_tx = 9; - ResponseDeliverTx deliver_tx = 10; - ResponseEndBlock end_block = 11; - ResponseCommit commit = 12; + ResponseCheckTx check_tx = 9; + ResponseDeliverTx deliver_tx = 10; + ResponseEndBlock end_block = 11; + ResponseCommit commit = 12; } } @@ -133,16 +131,15 @@ message ResponseEcho { string message = 1; } -message ResponseFlush { -} +message ResponseFlush {} message ResponseInfo { string data = 1; - string version = 2; + string version = 2; uint64 app_version = 3; - int64 last_block_height = 4; + int64 last_block_height = 4; bytes last_block_app_hash = 5; } @@ -150,58 +147,62 @@ message ResponseInfo { message ResponseSetOption { uint32 code = 1; // bytes data = 2; - string log = 3; + string log = 3; string info = 4; } message ResponseInitChain { - ConsensusParams consensus_params = 1; - repeated ValidatorUpdate validators = 2 [(gogoproto.nullable)=false]; + ConsensusParams consensus_params = 1; + repeated ValidatorUpdate validators = 2 [(gogoproto.nullable) = false]; } message ResponseQuery { uint32 code = 1; // bytes data = 2; // use "value" instead. - string log = 3; // nondeterministic - string info = 4; // nondeterministic - int64 index = 5; - bytes key = 6; - bytes value = 7; - tendermint.crypto.merkle.Proof proof = 8; - int64 height = 9; - string codespace = 10; + string log = 3; // nondeterministic + string info = 4; // nondeterministic + int64 index = 5; + bytes key = 6; + bytes value = 7; + tendermint.crypto.merkle.Proof proof = 8; + int64 height = 9; + string codespace = 10; } message ResponseBeginBlock { - repeated Event events = 1 [(gogoproto.nullable)=false, (gogoproto.jsontag)="events,omitempty"]; + repeated Event events = 1 + [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; } message ResponseCheckTx { - uint32 code = 1; - bytes data = 2; - string log = 3; // nondeterministic - string info = 4; // nondeterministic - int64 gas_wanted = 5; - int64 gas_used = 6; - repeated Event events = 7 [(gogoproto.nullable)=false, (gogoproto.jsontag)="events,omitempty"]; + uint32 code = 1; + bytes data = 2; + string log = 3; // nondeterministic + string info = 4; // nondeterministic + int64 gas_wanted = 5; + int64 gas_used = 6; + repeated Event events = 7 + [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; string codespace = 8; } message ResponseDeliverTx { - uint32 code = 1; - bytes data = 2; - string log = 3; // nondeterministic - string info = 4; // nondeterministic - int64 gas_wanted = 5; - int64 gas_used = 6; - repeated Event events = 7 [(gogoproto.nullable)=false, (gogoproto.jsontag)="events,omitempty"]; + uint32 code = 1; + bytes data = 2; + string log = 3; // nondeterministic + string info = 4; // nondeterministic + int64 gas_wanted = 5; + int64 gas_used = 6; + repeated Event events = 7 + [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; string codespace = 8; } message ResponseEndBlock { - repeated ValidatorUpdate validator_updates = 1 [(gogoproto.nullable)=false]; - ConsensusParams consensus_param_updates = 2; - repeated Event events = 3 [(gogoproto.nullable)=false, (gogoproto.jsontag)="events,omitempty"]; + repeated ValidatorUpdate validator_updates = 1 [(gogoproto.nullable) = false]; + ConsensusParams consensus_param_updates = 2; + repeated Event events = 3 + [(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; } message ResponseCommit { @@ -215,8 +216,8 @@ message ResponseCommit { // ConsensusParams contains all consensus-relevant parameters // that can be adjusted by the abci app message ConsensusParams { - BlockParams block = 1; - EvidenceParams evidence = 2; + BlockParams block = 1; + EvidenceParams evidence = 2; ValidatorParams validator = 3; } @@ -230,8 +231,9 @@ message BlockParams { message EvidenceParams { // Note: must be greater than 0 - int64 max_age_num_blocks = 1; - google.protobuf.Duration max_age_duration = 2 [(gogoproto.nullable)=false, (gogoproto.stdduration)=true]; + int64 max_age_num_blocks = 1; + google.protobuf.Duration max_age_duration = 2 + [(gogoproto.nullable) = false, (gogoproto.stdduration) = true]; } // ValidatorParams contains limits on validators. @@ -240,13 +242,14 @@ message ValidatorParams { } message LastCommitInfo { - int32 round = 1; - repeated VoteInfo votes = 2 [(gogoproto.nullable)=false]; + int32 round = 1; + repeated VoteInfo votes = 2 [(gogoproto.nullable) = false]; } message Event { - string type = 1; - repeated tendermint.libs.kv.Pair attributes = 2 [(gogoproto.nullable)=false, (gogoproto.jsontag)="attributes,omitempty"]; + string type = 1; + repeated tendermint.libs.kv.Pair attributes = 2 + [(gogoproto.nullable) = false, (gogoproto.jsontag) = "attributes,omitempty"]; } //---------------------------------------- @@ -254,63 +257,62 @@ message Event { message Header { // basic block info - Version version = 1 [(gogoproto.nullable)=false]; - string chain_id = 2 [(gogoproto.customname)="ChainID"]; - int64 height = 3; - google.protobuf.Timestamp time = 4 [(gogoproto.nullable)=false, (gogoproto.stdtime)=true]; + Version version = 1 [(gogoproto.nullable) = false]; + string chain_id = 2 [(gogoproto.customname) = "ChainID"]; + int64 height = 3; + google.protobuf.Timestamp time = 4 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; // prev block info - BlockID last_block_id = 5 [(gogoproto.nullable)=false]; + BlockID last_block_id = 5 [(gogoproto.nullable) = false]; // hashes of block data - bytes last_commit_hash = 6; // commit from validators from the last block - bytes data_hash = 7; // transactions + bytes last_commit_hash = 6; // commit from validators from the last block + bytes data_hash = 7; // transactions // hashes from the app output from the prev block - bytes validators_hash = 8; // validators for the current block + bytes validators_hash = 8; // validators for the current block bytes next_validators_hash = 9; // validators for the next block - bytes consensus_hash = 10; // consensus params for current block - bytes app_hash = 11; // state after txs from the previous block - bytes last_results_hash = 12;// root hash of all results from the txs from the previous block + bytes consensus_hash = 10; // consensus params for current block + bytes app_hash = 11; // state after txs from the previous block + bytes last_results_hash = 12; // root hash of all results from the txs from the previous block // consensus info - bytes evidence_hash = 13; // evidence included in the block - bytes proposer_address = 14; // original proposer of the block + bytes evidence_hash = 13; // evidence included in the block + bytes proposer_address = 14; // original proposer of the block } message Version { uint64 Block = 1; - uint64 App = 2; + uint64 App = 2; } - message BlockID { - bytes hash = 1; - PartSetHeader parts_header = 2 [(gogoproto.nullable)=false]; + bytes hash = 1; + PartSetHeader parts_header = 2 [(gogoproto.nullable) = false]; } message PartSetHeader { int32 total = 1; - bytes hash = 2; + bytes hash = 2; } // Validator message Validator { bytes address = 1; - //PubKey pub_key = 2 [(gogoproto.nullable)=false]; + // PubKey pub_key = 2 [(gogoproto.nullable)=false]; int64 power = 3; } // ValidatorUpdate message ValidatorUpdate { - PubKey pub_key = 1 [(gogoproto.nullable)=false]; - int64 power = 2; + PubKey pub_key = 1 [(gogoproto.nullable) = false]; + int64 power = 2; } // VoteInfo message VoteInfo { - Validator validator = 1 [(gogoproto.nullable)=false]; - bool signed_last_block = 2; + Validator validator = 1 [(gogoproto.nullable) = false]; + bool signed_last_block = 2; } message PubKey { @@ -319,18 +321,18 @@ message PubKey { } message Evidence { - string type = 1; - Validator validator = 2 [(gogoproto.nullable)=false]; - int64 height = 3; - google.protobuf.Timestamp time = 4 [(gogoproto.nullable)=false, (gogoproto.stdtime)=true]; - int64 total_voting_power = 5; + string type = 1; + Validator validator = 2 [(gogoproto.nullable) = false]; + int64 height = 3; + google.protobuf.Timestamp time = 4 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true]; + int64 total_voting_power = 5; } //---------------------------------------- // Service Definition service ABCIApplication { - rpc Echo(RequestEcho) returns (ResponseEcho) ; + rpc Echo(RequestEcho) returns (ResponseEcho); rpc Flush(RequestFlush) returns (ResponseFlush); rpc Info(RequestInfo) returns (ResponseInfo); rpc SetOption(RequestSetOption) returns (ResponseSetOption); diff --git a/behaviour/reporter.go b/behaviour/reporter.go index 96ce32994..1f16b9bb3 100644 --- a/behaviour/reporter.go +++ b/behaviour/reporter.go @@ -19,7 +19,7 @@ type SwitchReporter struct { } // NewSwitchReporter return a new SwitchReporter instance which wraps the Switch. -func NewSwitcReporter(sw *p2p.Switch) *SwitchReporter { +func NewSwitchReporter(sw *p2p.Switch) *SwitchReporter { return &SwitchReporter{ sw: sw, } diff --git a/blockchain/v0/reactor.go b/blockchain/v0/reactor.go index c35a3e6a9..d47e892c2 100644 --- a/blockchain/v0/reactor.go +++ b/blockchain/v0/reactor.go @@ -41,7 +41,7 @@ const ( type consensusReactor interface { // for when we switch from blockchain reactor and fast sync to // the consensus machine - SwitchToConsensus(sm.State, int) + SwitchToConsensus(sm.State, uint64) } type peerError struct { @@ -214,7 +214,7 @@ func (bcR *BlockchainReactor) poolRoutine() { statusUpdateTicker := time.NewTicker(statusUpdateIntervalSeconds * time.Second) switchToConsensusTicker := time.NewTicker(switchToConsensusIntervalSeconds * time.Second) - blocksSynced := 0 + blocksSynced := uint64(0) chainID := bcR.initialState.ChainID state := bcR.initialState diff --git a/blockchain/v0/reactor_test.go b/blockchain/v0/reactor_test.go index c82225801..334cdf942 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.go b/blockchain/v1/reactor.go index c1932d406..1aba26b35 100644 --- a/blockchain/v1/reactor.go +++ b/blockchain/v1/reactor.go @@ -43,7 +43,7 @@ var ( type consensusReactor interface { // for when we switch from blockchain reactor and fast sync to // the consensus machine - SwitchToConsensus(sm.State, int) + SwitchToConsensus(sm.State, uint64) } // BlockchainReactor handles long-term catchup syncing. @@ -59,7 +59,7 @@ type BlockchainReactor struct { fastSync bool fsm *BcReactorFSM - blocksSynced int + blocksSynced uint64 // Receive goroutine forwards messages to this channel to be processed in the context of the poolRoutine. messagesForFSMCh chan bcReactorMessage @@ -103,7 +103,7 @@ func NewBlockchainReactor(state sm.State, blockExec *sm.BlockExecutor, store *st fsm := NewFSM(startHeight, bcR) bcR.fsm = fsm bcR.BaseReactor = *p2p.NewBaseReactor("BlockchainReactor", bcR) - //bcR.swReporter = behaviour.NewSwitcReporter(bcR.BaseReactor.Switch) + //bcR.swReporter = behaviour.NewSwitchReporter(bcR.BaseReactor.Switch) return bcR } @@ -141,7 +141,7 @@ func (bcR *BlockchainReactor) SetLogger(l log.Logger) { // OnStart implements service.Service. func (bcR *BlockchainReactor) OnStart() error { - bcR.swReporter = behaviour.NewSwitcReporter(bcR.BaseReactor.Switch) + bcR.swReporter = behaviour.NewSwitchReporter(bcR.BaseReactor.Switch) if bcR.fastSync { go bcR.poolRoutine() } diff --git a/blockchain/v1/reactor_test.go b/blockchain/v1/reactor_test.go index 3a35e6582..2d1f5f130 100644 --- a/blockchain/v1/reactor_test.go +++ b/blockchain/v1/reactor_test.go @@ -157,7 +157,7 @@ type consensusReactorTest struct { mtx sync.Mutex } -func (conR *consensusReactorTest) SwitchToConsensus(state sm.State, blocksSynced int) { +func (conR *consensusReactorTest) SwitchToConsensus(state sm.State, blocksSynced uint64) { conR.mtx.Lock() defer conR.mtx.Unlock() conR.switchedToConsensus = true diff --git a/blockchain/v2/codec.go b/blockchain/v2/codec.go new file mode 100644 index 000000000..f970d115f --- /dev/null +++ b/blockchain/v2/codec.go @@ -0,0 +1,13 @@ +package v2 + +import ( + amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/types" +) + +var cdc = amino.NewCodec() + +func init() { + RegisterBlockchainMessages(cdc) + types.RegisterBlockAmino(cdc) +} diff --git a/blockchain/v2/io.go b/blockchain/v2/io.go new file mode 100644 index 000000000..3db48c8c0 --- /dev/null +++ b/blockchain/v2/io.go @@ -0,0 +1,111 @@ +package v2 + +import ( + "fmt" + + "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/state" + "github.com/tendermint/tendermint/types" +) + +type iIO interface { + sendBlockRequest(peerID p2p.ID, height int64) error + sendBlockToPeer(block *types.Block, peerID p2p.ID) error + sendBlockNotFound(height int64, peerID p2p.ID) error + sendStatusResponse(height int64, peerID p2p.ID) error + + broadcastStatusRequest(height int64) + + trySwitchToConsensus(state state.State, blocksSynced int) +} + +type switchIO struct { + sw *p2p.Switch +} + +func newSwitchIo(sw *p2p.Switch) *switchIO { + return &switchIO{ + sw: sw, + } +} + +const ( + // BlockchainChannel is a channel for blocks and status updates (`BlockStore` height) + BlockchainChannel = byte(0x40) +) + +type consensusReactor interface { + // for when we switch from blockchain reactor and fast sync to + // the consensus machine + SwitchToConsensus(state.State, int) +} + +func (sio *switchIO) sendBlockRequest(peerID p2p.ID, height int64) error { + peer := sio.sw.Peers().Get(peerID) + if peer == nil { + return fmt.Errorf("peer not found") + } + + msgBytes := cdc.MustMarshalBinaryBare(&bcBlockRequestMessage{Height: height}) + queued := peer.TrySend(BlockchainChannel, msgBytes) + if !queued { + return fmt.Errorf("send queue full") + } + return nil +} + +func (sio *switchIO) sendStatusResponse(height int64, peerID p2p.ID) error { + peer := sio.sw.Peers().Get(peerID) + if peer == nil { + return fmt.Errorf("peer not found") + } + msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{Height: height}) + + if queued := peer.TrySend(BlockchainChannel, msgBytes); !queued { + return fmt.Errorf("peer queue full") + } + + return nil +} + +func (sio *switchIO) sendBlockToPeer(block *types.Block, peerID p2p.ID) error { + peer := sio.sw.Peers().Get(peerID) + if peer == nil { + return fmt.Errorf("peer not found") + } + if block == nil { + panic("trying to send nil block") + } + msgBytes := cdc.MustMarshalBinaryBare(&bcBlockResponseMessage{Block: block}) + if queued := peer.TrySend(BlockchainChannel, msgBytes); !queued { + return fmt.Errorf("peer queue full") + } + + return nil +} + +func (sio *switchIO) sendBlockNotFound(height int64, peerID p2p.ID) error { + peer := sio.sw.Peers().Get(peerID) + if peer == nil { + return fmt.Errorf("peer not found") + } + msgBytes := cdc.MustMarshalBinaryBare(&bcNoBlockResponseMessage{Height: height}) + if queued := peer.TrySend(BlockchainChannel, msgBytes); !queued { + return fmt.Errorf("peer queue full") + } + + return nil +} + +func (sio *switchIO) trySwitchToConsensus(state state.State, blocksSynced int) { + conR, ok := sio.sw.Reactor("CONSENSUS").(consensusReactor) + if ok { + conR.SwitchToConsensus(state, blocksSynced) + } +} + +func (sio *switchIO) broadcastStatusRequest(height int64) { + msgBytes := cdc.MustMarshalBinaryBare(&bcStatusRequestMessage{height}) + // XXX: maybe we should use an io specific peer list here + sio.sw.Broadcast(BlockchainChannel, msgBytes) +} diff --git a/blockchain/v2/metrics.go b/blockchain/v2/metrics.go index d865e7360..c68ec6447 100644 --- a/blockchain/v2/metrics.go +++ b/blockchain/v2/metrics.go @@ -37,6 +37,7 @@ type Metrics struct { ErrorsShed metrics.Counter } +// PrometheusMetrics returns metrics for in and out events, errors, etc. handled by routines. // Can we burn in the routine name here? func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics { labels := []string{} diff --git a/blockchain/v2/processor.go b/blockchain/v2/processor.go index e33b36058..d6a2fe1e8 100644 --- a/blockchain/v2/processor.go +++ b/blockchain/v2/processor.go @@ -4,23 +4,12 @@ import ( "fmt" "github.com/tendermint/tendermint/p2p" - tdState "github.com/tendermint/tendermint/state" + tmState "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" ) -type peerError struct { - priorityHigh - peerID p2p.ID -} - -type pcDuplicateBlock struct { - priorityNormal -} - -type pcShortBlock struct { - priorityNormal -} - +// Events generated by the processor: +// block execution failure, event will indicate the peer(s) that caused the error type pcBlockVerificationFailure struct { priorityNormal height int64 @@ -28,24 +17,18 @@ type pcBlockVerificationFailure struct { secondPeerID p2p.ID } +// successful block execution type pcBlockProcessed struct { priorityNormal height int64 peerID p2p.ID } -type pcProcessBlock struct { - priorityNormal -} - -type pcStop struct { - priorityNormal -} - +// processor has finished type pcFinished struct { priorityNormal - height int64 - blocksSynced int64 + blocksSynced int + tmState tmState.State } func (p pcFinished) Error() string { @@ -60,37 +43,38 @@ type queueItem struct { type blockQueue map[int64]queueItem type pcState struct { - height int64 // height of the last synced block - queue blockQueue // blocks waiting to be processed - chainID string - blocksSynced int64 - draining bool - tdState tdState.State - context processorContext + // blocks waiting to be processed + queue blockQueue + + // draining indicates that the next rProcessBlock event with a queue miss constitutes completion + draining bool + + // the number of blocks successfully synced by the processor + blocksSynced int + + // the processorContext which contains the processor dependencies + context processorContext } func (state *pcState) String() string { return fmt.Sprintf("height: %d queue length: %d draining: %v blocks synced: %d", - state.height, len(state.queue), state.draining, state.blocksSynced) + state.height(), len(state.queue), state.draining, state.blocksSynced) } // newPcState returns a pcState initialized with the last verified block enqueued -func newPcState(initHeight int64, tdState tdState.State, chainID string, context processorContext) *pcState { +func newPcState(context processorContext) *pcState { return &pcState{ - height: initHeight, queue: blockQueue{}, - chainID: chainID, draining: false, blocksSynced: 0, context: context, - tdState: tdState, } } // nextTwo returns the next two unverified blocks func (state *pcState) nextTwo() (queueItem, queueItem, error) { - if first, ok := state.queue[state.height+1]; ok { - if second, ok := state.queue[state.height+2]; ok { + if first, ok := state.queue[state.height()+1]; ok { + if second, ok := state.queue[state.height()+2]; ok { return first, second, nil } } @@ -102,18 +86,15 @@ func (state *pcState) synced() bool { return len(state.queue) <= 1 } -func (state *pcState) advance() { - state.height++ - delete(state.queue, state.height) - state.blocksSynced++ -} - -func (state *pcState) enqueue(peerID p2p.ID, block *types.Block, height int64) error { +func (state *pcState) enqueue(peerID p2p.ID, block *types.Block, height int64) { if _, ok := state.queue[height]; ok { - return fmt.Errorf("duplicate queue item") + panic("duplicate block enqueued by processor") } state.queue[height] = queueItem{block: block, peerID: peerID} - return nil +} + +func (state *pcState) height() int64 { + return state.context.tmState().LastBlockHeight } // purgePeer moves all unprocessed blocks from the queue @@ -129,23 +110,34 @@ func (state *pcState) purgePeer(peerID p2p.ID) { // handle processes FSM events func (state *pcState) handle(event Event) (Event, error) { switch event := event.(type) { - case *scBlockReceived: - if event.block == nil { - panic("processor received an event with a nil block") + case scFinishedEv: + if state.synced() { + return pcFinished{tmState: state.context.tmState(), blocksSynced: state.blocksSynced}, nil } - if event.block.Height <= state.height { - return pcShortBlock{}, nil + state.draining = true + return noOp, nil + + case scPeerError: + state.purgePeer(event.peerID) + return noOp, nil + + case scBlockReceived: + if event.block == nil { + return noOp, nil } - err := state.enqueue(event.peerID, event.block, event.block.Height) - if err != nil { - return pcDuplicateBlock{}, nil + + // enqueue block if height is higher than state height, else ignore it + if event.block.Height > state.height() { + state.enqueue(event.peerID, event.block, event.block.Height) } + return noOp, nil - case pcProcessBlock: + case rProcessBlock: + tmState := state.context.tmState() firstItem, secondItem, err := state.nextTwo() if err != nil { if state.draining { - return noOp, pcFinished{height: state.height} + return pcFinished{tmState: tmState, blocksSynced: state.blocksSynced}, nil } return noOp, nil } @@ -155,7 +147,7 @@ func (state *pcState) handle(event Event) (Event, error) { firstPartsHeader := firstParts.Header() firstID := types.BlockID{Hash: first.Hash(), PartsHeader: firstPartsHeader} - err = state.context.verifyCommit(state.chainID, firstID, first.Height, second.LastCommit) + err = state.context.verifyCommit(tmState.ChainID, firstID, first.Height, second.LastCommit) if err != nil { state.purgePeer(firstItem.peerID) state.purgePeer(secondItem.peerID) @@ -166,21 +158,15 @@ func (state *pcState) handle(event Event) (Event, error) { state.context.saveBlock(first, firstParts, second.LastCommit) - state.tdState, err = state.context.applyBlock(state.tdState, firstID, first) - if err != nil { + if err := state.context.applyBlock(firstID, first); err != nil { panic(fmt.Sprintf("failed to process committed block (%d:%X): %v", first.Height, first.Hash(), err)) } - state.advance() - return pcBlockProcessed{height: first.Height, peerID: firstItem.peerID}, nil - case *peerError: - state.purgePeer(event.peerID) + delete(state.queue, first.Height) + state.blocksSynced++ + + return pcBlockProcessed{height: first.Height, peerID: firstItem.peerID}, nil - case pcStop: - if state.synced() { - return noOp, pcFinished{height: state.height, blocksSynced: state.blocksSynced} - } - state.draining = true } return noOp, nil diff --git a/blockchain/v2/processor_context.go b/blockchain/v2/processor_context.go index c4c8770cd..7e96a3a69 100644 --- a/blockchain/v2/processor_context.go +++ b/blockchain/v2/processor_context.go @@ -4,37 +4,41 @@ import ( "fmt" "github.com/tendermint/tendermint/state" - "github.com/tendermint/tendermint/store" "github.com/tendermint/tendermint/types" ) type processorContext interface { - applyBlock(state state.State, blockID types.BlockID, block *types.Block) (state.State, error) + applyBlock(blockID types.BlockID, block *types.Block) error verifyCommit(chainID string, blockID types.BlockID, height int64, commit *types.Commit) error saveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) + tmState() state.State } -// nolint:unused type pContext struct { - store *store.BlockStore - executor *state.BlockExecutor - state *state.State + store blockStore + applier blockApplier + state state.State } -// nolint:unused,deadcode -func newProcessorContext(st *store.BlockStore, ex *state.BlockExecutor, s *state.State) *pContext { +func newProcessorContext(st blockStore, ex blockApplier, s state.State) *pContext { return &pContext{ - store: st, - executor: ex, - state: s, + store: st, + applier: ex, + state: s, } } -func (pc *pContext) applyBlock(state state.State, blockID types.BlockID, block *types.Block) (state.State, error) { - return pc.executor.ApplyBlock(state, blockID, block) +func (pc *pContext) applyBlock(blockID types.BlockID, block *types.Block) error { + newState, err := pc.applier.ApplyBlock(pc.state, blockID, block) + pc.state = newState + return err } -func (pc *pContext) verifyCommit(chainID string, blockID types.BlockID, height int64, commit *types.Commit) error { +func (pc pContext) tmState() state.State { + return pc.state +} + +func (pc pContext) verifyCommit(chainID string, blockID types.BlockID, height int64, commit *types.Commit) error { return pc.state.Validators.VerifyCommit(chainID, blockID, height, commit) } @@ -45,22 +49,28 @@ func (pc *pContext) saveBlock(block *types.Block, blockParts *types.PartSet, see type mockPContext struct { applicationBL []int64 verificationBL []int64 + state state.State } -func newMockProcessorContext(verificationBlackList []int64, applicationBlackList []int64) *mockPContext { +func newMockProcessorContext( + state state.State, + verificationBlackList []int64, + applicationBlackList []int64) *mockPContext { return &mockPContext{ applicationBL: applicationBlackList, verificationBL: verificationBlackList, + state: state, } } -func (mpc *mockPContext) applyBlock(state state.State, blockID types.BlockID, block *types.Block) (state.State, error) { +func (mpc *mockPContext) applyBlock(blockID types.BlockID, block *types.Block) error { for _, h := range mpc.applicationBL { if h == block.Height { - return state, fmt.Errorf("generic application error") + return fmt.Errorf("generic application error") } } - return state, nil + mpc.state.LastBlockHeight = block.Height + return nil } func (mpc *mockPContext) verifyCommit(chainID string, blockID types.BlockID, height int64, commit *types.Commit) error { @@ -73,4 +83,9 @@ func (mpc *mockPContext) verifyCommit(chainID string, blockID types.BlockID, hei } func (mpc *mockPContext) saveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) { + +} + +func (mpc *mockPContext) tmState() state.State { + return mpc.state } diff --git a/blockchain/v2/processor_test.go b/blockchain/v2/processor_test.go index 61be23663..fc35c4c72 100644 --- a/blockchain/v2/processor_test.go +++ b/blockchain/v2/processor_test.go @@ -5,7 +5,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/tendermint/tendermint/p2p" - tdState "github.com/tendermint/tendermint/state" + tmState "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" ) @@ -19,7 +19,7 @@ type pcBlock struct { type params struct { height int64 items []pcBlock - blocksSynced int64 + blocksSynced int verBL []int64 appBL []int64 draining bool @@ -33,13 +33,13 @@ func makePcBlock(height int64) *types.Block { // makeState takes test parameters and creates a specific processor state. func makeState(p *params) *pcState { var ( - tdState = tdState.State{} - context = newMockProcessorContext(p.verBL, p.appBL) + tmState = tmState.State{LastBlockHeight: p.height} + context = newMockProcessorContext(tmState, p.verBL, p.appBL) ) - state := newPcState(p.height, tdState, "test", context) + state := newPcState(context) for _, item := range p.items { - _ = state.enqueue(p2p.ID(item.pid), makePcBlock(item.height), item.height) + state.enqueue(p2p.ID(item.pid), makePcBlock(item.height), item.height) } state.blocksSynced = p.blocksSynced @@ -47,8 +47,8 @@ func makeState(p *params) *pcState { return state } -func mBlockResponse(peerID p2p.ID, height int64) *scBlockReceived { - return &scBlockReceived{ +func mBlockResponse(peerID p2p.ID, height int64) scBlockReceived { + return scBlockReceived{ peerID: peerID, block: makePcBlock(height), } @@ -101,72 +101,57 @@ func executeProcessorTests(t *testing.T, tests []testFields) { } } -func TestPcBlockResponse(t *testing.T) { +func TestRProcessPeerError(t *testing.T) { tests := []testFields{ - { - name: "add one block", + name: "error for existing peer", steps: []pcFsmMakeStateValues{ { - currentState: ¶ms{}, event: mBlockResponse("P1", 1), - wantState: ¶ms{items: []pcBlock{{"P1", 1}}}, wantNextEvent: noOp, + currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}}, + event: scPeerError{peerID: "P2"}, + wantState: ¶ms{items: []pcBlock{{"P1", 1}}}, + wantNextEvent: noOp, }, }, }, { - name: "add two blocks", + name: "error for unknown peer", steps: []pcFsmMakeStateValues{ { - currentState: ¶ms{}, event: mBlockResponse("P1", 3), - wantState: ¶ms{items: []pcBlock{{"P1", 3}}}, wantNextEvent: noOp, - }, - { // use previous wantState as currentState, - event: mBlockResponse("P1", 4), - wantState: ¶ms{items: []pcBlock{{"P1", 3}, {"P1", 4}}}, wantNextEvent: noOp, + currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}}, + event: scPeerError{peerID: "P3"}, + wantState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}}, + wantNextEvent: noOp, }, }, }, + } + + executeProcessorTests(t, tests) +} + +func TestPcBlockResponse(t *testing.T) { + tests := []testFields{ { - name: "add duplicate block from same peer", + name: "add one block", steps: []pcFsmMakeStateValues{ { - currentState: ¶ms{}, event: mBlockResponse("P1", 3), - wantState: ¶ms{items: []pcBlock{{"P1", 3}}}, wantNextEvent: noOp, - }, - { // use previous wantState as currentState, - event: mBlockResponse("P1", 3), - wantState: ¶ms{items: []pcBlock{{"P1", 3}}}, wantNextEvent: pcDuplicateBlock{}, + currentState: ¶ms{}, event: mBlockResponse("P1", 1), + wantState: ¶ms{items: []pcBlock{{"P1", 1}}}, wantNextEvent: noOp, }, }, }, + { - name: "add duplicate block from different peer", + name: "add two blocks", steps: []pcFsmMakeStateValues{ { currentState: ¶ms{}, event: mBlockResponse("P1", 3), wantState: ¶ms{items: []pcBlock{{"P1", 3}}}, wantNextEvent: noOp, }, { // use previous wantState as currentState, - event: mBlockResponse("P2", 3), - wantState: ¶ms{items: []pcBlock{{"P1", 3}}}, wantNextEvent: pcDuplicateBlock{}, - }, - }, - }, - { - name: "attempt to add block with height equal to state.height", - steps: []pcFsmMakeStateValues{ - { - currentState: ¶ms{height: 2, items: []pcBlock{{"P1", 3}}}, event: mBlockResponse("P1", 2), - wantState: ¶ms{height: 2, items: []pcBlock{{"P1", 3}}}, wantNextEvent: pcShortBlock{}, - }, - }, - }, - { - name: "attempt to add block with height smaller than state.height", - steps: []pcFsmMakeStateValues{ - { - currentState: ¶ms{height: 2, items: []pcBlock{{"P1", 3}}}, event: mBlockResponse("P1", 1), - wantState: ¶ms{height: 2, items: []pcBlock{{"P1", 3}}}, wantNextEvent: pcShortBlock{}, + event: mBlockResponse("P1", 4), + wantState: ¶ms{items: []pcBlock{{"P1", 3}, {"P1", 4}}}, wantNextEvent: noOp, }, }, }, @@ -175,13 +160,13 @@ func TestPcBlockResponse(t *testing.T) { executeProcessorTests(t, tests) } -func TestPcProcessBlockSuccess(t *testing.T) { +func TestRProcessBlockSuccess(t *testing.T) { tests := []testFields{ { name: "noop - no blocks over current height", steps: []pcFsmMakeStateValues{ { - currentState: ¶ms{}, event: pcProcessBlock{}, + currentState: ¶ms{}, event: rProcessBlock{}, wantState: ¶ms{}, wantNextEvent: noOp, }, }, @@ -190,7 +175,7 @@ func TestPcProcessBlockSuccess(t *testing.T) { name: "noop - high new blocks", steps: []pcFsmMakeStateValues{ { - currentState: ¶ms{height: 5, items: []pcBlock{{"P1", 30}, {"P2", 31}}}, event: pcProcessBlock{}, + currentState: ¶ms{height: 5, items: []pcBlock{{"P1", 30}, {"P2", 31}}}, event: rProcessBlock{}, wantState: ¶ms{height: 5, items: []pcBlock{{"P1", 30}, {"P2", 31}}}, wantNextEvent: noOp, }, }, @@ -199,7 +184,7 @@ func TestPcProcessBlockSuccess(t *testing.T) { name: "blocks H+1 and H+2 present", steps: []pcFsmMakeStateValues{ { - currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}}, event: pcProcessBlock{}, + currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}}, event: rProcessBlock{}, wantState: ¶ms{height: 1, items: []pcBlock{{"P2", 2}}, blocksSynced: 1}, wantNextEvent: pcBlockProcessed{height: 1, peerID: "P1"}, }, @@ -209,20 +194,20 @@ func TestPcProcessBlockSuccess(t *testing.T) { name: "blocks H+1 and H+2 present after draining", steps: []pcFsmMakeStateValues{ { // some contiguous blocks - on stop check draining is set - currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}, {"P1", 4}}}, event: pcStop{}, + currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}, {"P1", 4}}}, + event: scFinishedEv{}, wantState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}, {"P1", 4}}, draining: true}, wantNextEvent: noOp, }, { - event: pcProcessBlock{}, + event: rProcessBlock{}, wantState: ¶ms{height: 1, items: []pcBlock{{"P2", 2}, {"P1", 4}}, blocksSynced: 1, draining: true}, wantNextEvent: pcBlockProcessed{height: 1, peerID: "P1"}, }, { // finish when H+1 or/and H+2 are missing - event: pcProcessBlock{}, + event: rProcessBlock{}, wantState: ¶ms{height: 1, items: []pcBlock{{"P2", 2}, {"P1", 4}}, blocksSynced: 1, draining: true}, - wantNextEvent: noOp, - wantErr: pcFinished{height: 1}, + wantNextEvent: pcFinished{tmState: tmState.State{LastBlockHeight: 1}, blocksSynced: 1}, }, }, }, @@ -231,13 +216,13 @@ func TestPcProcessBlockSuccess(t *testing.T) { executeProcessorTests(t, tests) } -func TestPcProcessBlockFailures(t *testing.T) { +func TestRProcessBlockFailures(t *testing.T) { tests := []testFields{ { name: "blocks H+1 and H+2 present from different peers - H+1 verification fails ", steps: []pcFsmMakeStateValues{ { - currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}, verBL: []int64{1}}, event: pcProcessBlock{}, + currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}, verBL: []int64{1}}, event: rProcessBlock{}, wantState: ¶ms{items: []pcBlock{}, verBL: []int64{1}}, wantNextEvent: pcBlockVerificationFailure{height: 1, firstPeerID: "P1", secondPeerID: "P2"}, }, @@ -247,7 +232,7 @@ func TestPcProcessBlockFailures(t *testing.T) { name: "blocks H+1 and H+2 present from same peer - H+1 applyBlock fails ", steps: []pcFsmMakeStateValues{ { - currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}, appBL: []int64{1}}, event: pcProcessBlock{}, + currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}, appBL: []int64{1}}, event: rProcessBlock{}, wantState: ¶ms{items: []pcBlock{}, appBL: []int64{1}}, wantPanic: true, }, }, @@ -256,9 +241,9 @@ func TestPcProcessBlockFailures(t *testing.T) { name: "blocks H+1 and H+2 present from same peers - H+1 verification fails ", steps: []pcFsmMakeStateValues{ { - currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P1", 2}, {"P2", 3}}, verBL: []int64{1}}, - event: pcProcessBlock{}, - wantState: ¶ms{items: []pcBlock{{"P2", 3}}, verBL: []int64{1}}, + currentState: ¶ms{height: 0, items: []pcBlock{{"P1", 1}, {"P1", 2}, {"P2", 3}}, + verBL: []int64{1}}, event: rProcessBlock{}, + wantState: ¶ms{height: 0, items: []pcBlock{{"P2", 3}}, verBL: []int64{1}}, wantNextEvent: pcBlockVerificationFailure{height: 1, firstPeerID: "P1", secondPeerID: "P1"}, }, }, @@ -268,7 +253,7 @@ func TestPcProcessBlockFailures(t *testing.T) { steps: []pcFsmMakeStateValues{ { currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}, {"P2", 3}}, appBL: []int64{1}}, - event: pcProcessBlock{}, + event: rProcessBlock{}, wantState: ¶ms{items: []pcBlock{{"P2", 3}}, appBL: []int64{1}}, wantPanic: true, }, }, @@ -278,53 +263,15 @@ func TestPcProcessBlockFailures(t *testing.T) { executeProcessorTests(t, tests) } -func TestPcPeerError(t *testing.T) { - tests := []testFields{ - { - name: "peer not present", - steps: []pcFsmMakeStateValues{ - { - currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}}, event: &peerError{peerID: "P3"}, - wantState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}}, - wantNextEvent: noOp, - }, - }, - }, - { - name: "some blocks are from errored peer", - steps: []pcFsmMakeStateValues{ - { - currentState: ¶ms{items: []pcBlock{{"P1", 100}, {"P1", 99}, {"P2", 101}}}, event: &peerError{peerID: "P1"}, - wantState: ¶ms{items: []pcBlock{{"P2", 101}}}, - wantNextEvent: noOp, - }, - }, - }, - { - name: "all blocks are from errored peer", - steps: []pcFsmMakeStateValues{ - { - currentState: ¶ms{items: []pcBlock{{"P1", 100}, {"P1", 99}}}, event: &peerError{peerID: "P1"}, - wantState: ¶ms{}, - wantNextEvent: noOp, - }, - }, - }, - } - - executeProcessorTests(t, tests) -} - -func TestStop(t *testing.T) { +func TestScFinishedEv(t *testing.T) { tests := []testFields{ { name: "no blocks", steps: []pcFsmMakeStateValues{ { - currentState: ¶ms{height: 100, items: []pcBlock{}, blocksSynced: 100}, event: pcStop{}, + currentState: ¶ms{height: 100, items: []pcBlock{}, blocksSynced: 100}, event: scFinishedEv{}, wantState: ¶ms{height: 100, items: []pcBlock{}, blocksSynced: 100}, - wantNextEvent: noOp, - wantErr: pcFinished{height: 100, blocksSynced: 100}, + wantNextEvent: pcFinished{tmState: tmState.State{LastBlockHeight: 100}, blocksSynced: 100}, }, }, }, @@ -332,10 +279,10 @@ func TestStop(t *testing.T) { name: "maxHeight+1 block present", steps: []pcFsmMakeStateValues{ { - currentState: ¶ms{height: 100, items: []pcBlock{{"P1", 101}}, blocksSynced: 100}, event: pcStop{}, + currentState: ¶ms{height: 100, items: []pcBlock{ + {"P1", 101}}, blocksSynced: 100}, event: scFinishedEv{}, wantState: ¶ms{height: 100, items: []pcBlock{{"P1", 101}}, blocksSynced: 100}, - wantNextEvent: noOp, - wantErr: pcFinished{height: 100, blocksSynced: 100}, + wantNextEvent: pcFinished{tmState: tmState.State{LastBlockHeight: 100}, blocksSynced: 100}, }, }, }, @@ -343,8 +290,10 @@ func TestStop(t *testing.T) { name: "more blocks present", steps: []pcFsmMakeStateValues{ { - currentState: ¶ms{height: 100, items: []pcBlock{{"P1", 101}, {"P1", 102}}, blocksSynced: 100}, event: pcStop{}, - wantState: ¶ms{height: 100, items: []pcBlock{{"P1", 101}, {"P1", 102}}, blocksSynced: 100, draining: true}, + currentState: ¶ms{height: 100, items: []pcBlock{ + {"P1", 101}, {"P1", 102}}, blocksSynced: 100}, event: scFinishedEv{}, + wantState: ¶ms{height: 100, items: []pcBlock{ + {"P1", 101}, {"P1", 102}}, blocksSynced: 100, draining: true}, wantNextEvent: noOp, wantErr: nil, }, diff --git a/blockchain/v2/reactor.go b/blockchain/v2/reactor.go index 8f7143083..767e59819 100644 --- a/blockchain/v2/reactor.go +++ b/blockchain/v2/reactor.go @@ -1,118 +1,529 @@ package v2 import ( + "errors" "fmt" + "sync" "time" + "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/behaviour" "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/state" + "github.com/tendermint/tendermint/types" ) -type timeCheck struct { - priorityHigh - time time.Time +//------------------------------------- + +type bcBlockRequestMessage struct { + Height int64 +} + +// ValidateBasic performs basic validation. +func (m *bcBlockRequestMessage) ValidateBasic() error { + if m.Height < 0 { + return errors.New("negative Height") + } + return nil +} + +func (m *bcBlockRequestMessage) String() string { + return fmt.Sprintf("[bcBlockRequestMessage %v]", m.Height) +} + +type bcNoBlockResponseMessage struct { + Height int64 +} + +// ValidateBasic performs basic validation. +func (m *bcNoBlockResponseMessage) ValidateBasic() error { + if m.Height < 0 { + return errors.New("negative Height") + } + return nil +} + +func (m *bcNoBlockResponseMessage) String() string { + return fmt.Sprintf("[bcNoBlockResponseMessage %d]", m.Height) +} + +//------------------------------------- + +type bcBlockResponseMessage struct { + Block *types.Block +} + +// ValidateBasic performs basic validation. +func (m *bcBlockResponseMessage) ValidateBasic() error { + if m.Block == nil { + return errors.New("block response message has nil block") + } + + return m.Block.ValidateBasic() +} + +func (m *bcBlockResponseMessage) String() string { + return fmt.Sprintf("[bcBlockResponseMessage %v]", m.Block.Height) } -func schedulerHandle(event Event) (Event, error) { - if _, ok := event.(timeCheck); ok { - fmt.Println("scheduler handle timeCheck") +//------------------------------------- + +type bcStatusRequestMessage struct { + Height int64 +} + +// ValidateBasic performs basic validation. +func (m *bcStatusRequestMessage) ValidateBasic() error { + if m.Height < 0 { + return errors.New("negative Height") } - return noOp, nil + return nil +} + +func (m *bcStatusRequestMessage) String() string { + return fmt.Sprintf("[bcStatusRequestMessage %v]", m.Height) +} + +//------------------------------------- + +type bcStatusResponseMessage struct { + Height int64 } -func processorHandle(event Event) (Event, error) { - if _, ok := event.(timeCheck); ok { - fmt.Println("processor handle timeCheck") +// ValidateBasic performs basic validation. +func (m *bcStatusResponseMessage) ValidateBasic() error { + if m.Height < 0 { + return errors.New("negative Height") } - return noOp, nil + return nil +} + +func (m *bcStatusResponseMessage) String() string { + return fmt.Sprintf("[bcStatusResponseMessage %v]", m.Height) +} +type blockStore interface { + LoadBlock(height int64) *types.Block + SaveBlock(*types.Block, *types.PartSet, *types.Commit) + Height() int64 } -type Reactor struct { - events chan Event +// BlockchainReactor handles fast sync protocol. +type BlockchainReactor struct { + p2p.BaseReactor + + events chan Event // XXX: Rename eventsFromPeers stopDemux chan struct{} scheduler *Routine processor *Routine - ticker *time.Ticker logger log.Logger + + mtx sync.RWMutex + maxPeerHeight int64 + syncHeight int64 + + reporter behaviour.Reporter + io iIO + store blockStore +} + +//nolint:unused,deadcode +type blockVerifier interface { + VerifyCommit(chainID string, blockID types.BlockID, height int64, commit *types.Commit) error } -func NewReactor(bufferSize int) *Reactor { - return &Reactor{ +//nolint:deadcode +type blockApplier interface { + ApplyBlock(state state.State, blockID types.BlockID, block *types.Block) (state.State, error) +} + +// XXX: unify naming in this package around tmState +// XXX: V1 stores a copy of state as initialState, which is never mutated. Is that nessesary? +func newReactor(state state.State, store blockStore, reporter behaviour.Reporter, + blockApplier blockApplier, bufferSize int) *BlockchainReactor { + scheduler := newScheduler(state.LastBlockHeight, time.Now()) + pContext := newProcessorContext(store, blockApplier, state) + // TODO: Fix naming to just newProcesssor + // newPcState requires a processorContext + processor := newPcState(pContext) + + return &BlockchainReactor{ events: make(chan Event, bufferSize), stopDemux: make(chan struct{}), - scheduler: newRoutine("scheduler", schedulerHandle, bufferSize), - processor: newRoutine("processor", processorHandle, bufferSize), - ticker: time.NewTicker(1 * time.Second), + scheduler: newRoutine("scheduler", scheduler.handle, bufferSize), + processor: newRoutine("processor", processor.handle, bufferSize), + store: store, + reporter: reporter, logger: log.NewNopLogger(), } } -// nolint:unused -func (r *Reactor) setLogger(logger log.Logger) { +// NewBlockchainReactor creates a new reactor instance. +func NewBlockchainReactor( + state state.State, + blockApplier blockApplier, + store blockStore, + fastSync bool) *BlockchainReactor { + reporter := behaviour.NewMockReporter() + return newReactor(state, store, reporter, blockApplier, 1000) +} + +// SetSwitch implements Reactor interface. +func (r *BlockchainReactor) SetSwitch(sw *p2p.Switch) { + if sw == nil { + panic("set nil switch") + } + + r.Switch = sw + r.io = newSwitchIo(sw) +} + +func (r *BlockchainReactor) setMaxPeerHeight(height int64) { + r.mtx.Lock() + defer r.mtx.Unlock() + if height > r.maxPeerHeight { + r.maxPeerHeight = height + } +} + +func (r *BlockchainReactor) setSyncHeight(height int64) { + r.mtx.Lock() + defer r.mtx.Unlock() + r.syncHeight = height +} + +// SyncHeight returns the height to which the BlockchainReactor has synced. +func (r *BlockchainReactor) SyncHeight() int64 { + r.mtx.RLock() + defer r.mtx.RUnlock() + return r.syncHeight +} + +// SetLogger sets the logger of the reactor. +func (r *BlockchainReactor) SetLogger(logger log.Logger) { r.logger = logger r.scheduler.setLogger(logger) r.processor.setLogger(logger) } -func (r *Reactor) Start() { +// Start implements cmn.Service interface +func (r *BlockchainReactor) Start() error { + r.reporter = behaviour.NewSwitchReporter(r.BaseReactor.Switch) go r.scheduler.start() go r.processor.start() go r.demux() + return nil +} - <-r.scheduler.ready() - <-r.processor.ready() +// reactor generated ticker events: +// ticker for cleaning peers +type rTryPrunePeer struct { + priorityHigh + time time.Time +} - go func() { - for t := range r.ticker.C { - r.events <- timeCheck{time: t} - } - }() +func (e rTryPrunePeer) String() string { + return fmt.Sprintf(": %v", e.time) } -// XXX: How to make this deterministic? -// XXX: Would it be possible here to provide some kind of type safety for the types -// of events that each routine can produce and consume? -func (r *Reactor) demux() { +// ticker event for scheduling block requests +type rTrySchedule struct { + priorityHigh + time time.Time +} + +func (e rTrySchedule) String() string { + return fmt.Sprintf(": %v", e.time) +} + +// ticker for block processing +type rProcessBlock struct { + priorityNormal +} + +// reactor generated events based on blockchain related messages from peers: +// blockResponse message received from a peer +type bcBlockResponse struct { + priorityNormal + time time.Time + peerID p2p.ID + size int64 + block *types.Block +} + +// blockNoResponse message received from a peer +type bcNoBlockResponse struct { + priorityNormal + time time.Time + peerID p2p.ID + height int64 +} + +// statusResponse message received from a peer +type bcStatusResponse struct { + priorityNormal + time time.Time + peerID p2p.ID + height int64 +} + +// new peer is connected +type bcAddNewPeer struct { + priorityNormal + peerID p2p.ID +} + +// existing peer is removed +type bcRemovePeer struct { + priorityHigh + peerID p2p.ID + reason interface{} +} + +func (r *BlockchainReactor) demux() { + var lastRate = 0.0 + var lastHundred = time.Now() + + var ( + processBlockFreq = 20 * time.Millisecond + doProcessBlockCh = make(chan struct{}, 1) + doProcessBlockTk = time.NewTicker(processBlockFreq) + + prunePeerFreq = 1 * time.Second + doPrunePeerCh = make(chan struct{}, 1) + doPrunePeerTk = time.NewTicker(prunePeerFreq) + + scheduleFreq = 20 * time.Millisecond + doScheduleCh = make(chan struct{}, 1) + doScheduleTk = time.NewTicker(scheduleFreq) + + statusFreq = 10 * time.Second + doStatusCh = make(chan struct{}, 1) + doStatusTk = time.NewTicker(statusFreq) + ) + + // XXX: Extract timers to make testing atemporal for { select { + // Pacers: send at most per frequency but don't saturate + case <-doProcessBlockTk.C: + select { + case doProcessBlockCh <- struct{}{}: + default: + } + case <-doPrunePeerTk.C: + select { + case doPrunePeerCh <- struct{}{}: + default: + } + case <-doScheduleTk.C: + select { + case doScheduleCh <- struct{}{}: + default: + } + case <-doStatusTk.C: + select { + case doStatusCh <- struct{}{}: + default: + } + + // Tickers: perform tasks periodically + case <-doScheduleCh: + r.scheduler.send(rTrySchedule{time: time.Now()}) + case <-doPrunePeerCh: + r.scheduler.send(rTryPrunePeer{time: time.Now()}) + case <-doProcessBlockCh: + r.processor.send(rProcessBlock{}) + case <-doStatusCh: + r.io.broadcastStatusRequest(r.SyncHeight()) + + // Events from peers case event := <-r.events: - // XXX: check for backpressure - r.scheduler.send(event) - r.processor.send(event) - case <-r.stopDemux: - r.logger.Info("demuxing stopped") - return + switch event := event.(type) { + case bcStatusResponse: + r.setMaxPeerHeight(event.height) + r.scheduler.send(event) + case bcAddNewPeer, bcRemovePeer, bcBlockResponse, bcNoBlockResponse: + r.scheduler.send(event) + } + + // Incremental events form scheduler case event := <-r.scheduler.next(): - r.processor.send(event) + switch event := event.(type) { + case scBlockReceived: + r.processor.send(event) + case scPeerError: + r.processor.send(event) + r.reporter.Report(behaviour.BadMessage(event.peerID, "scPeerError")) + case scBlockRequest: + r.io.sendBlockRequest(event.peerID, event.height) + case scFinishedEv: + r.processor.send(event) + r.scheduler.stop() + } + + // Incremental events from processor case event := <-r.processor.next(): - r.scheduler.send(event) + switch event := event.(type) { + case pcBlockProcessed: + r.setSyncHeight(event.height) + if r.syncHeight%100 == 0 { + lastRate = 0.9*lastRate + 0.1*(100/time.Since(lastHundred).Seconds()) + r.logger.Info("Fast Syncc Rate", "height", r.syncHeight, + "max_peer_height", r.maxPeerHeight, "blocks/s", lastRate) + lastHundred = time.Now() + } + r.scheduler.send(event) + case pcBlockVerificationFailure: + r.scheduler.send(event) + case pcFinished: + r.io.trySwitchToConsensus(event.tmState, event.blocksSynced) + r.processor.stop() + } + + // Terminal events from scheduler case err := <-r.scheduler.final(): r.logger.Info(fmt.Sprintf("scheduler final %s", err)) - case err := <-r.processor.final(): - r.logger.Info(fmt.Sprintf("processor final %s", err)) - // XXX: switch to consensus + // send the processor stop? + + // Terminal event from processor + case event := <-r.processor.final(): + r.logger.Info(fmt.Sprintf("processor final %s", event)) + + case <-r.stopDemux: + r.logger.Info("demuxing stopped") + return } } } -func (r *Reactor) Stop() { +// Stop implements cmn.Service interface. +func (r *BlockchainReactor) Stop() error { r.logger.Info("reactor stopping") - r.ticker.Stop() r.scheduler.stop() r.processor.stop() close(r.stopDemux) close(r.events) r.logger.Info("reactor stopped") + return nil +} + +const ( + // NOTE: keep up to date with bcBlockResponseMessage + bcBlockResponseMessagePrefixSize = 4 + bcBlockResponseMessageFieldKeySize = 1 + maxMsgSize = types.MaxBlockSizeBytes + + bcBlockResponseMessagePrefixSize + + bcBlockResponseMessageFieldKeySize +) + +// BlockchainMessage is a generic message for this reactor. +type BlockchainMessage interface { + ValidateBasic() error +} + +// RegisterBlockchainMessages registers the fast sync messages for amino encoding. +func RegisterBlockchainMessages(cdc *amino.Codec) { + cdc.RegisterInterface((*BlockchainMessage)(nil), nil) + cdc.RegisterConcrete(&bcBlockRequestMessage{}, "tendermint/blockchain/BlockRequest", nil) + cdc.RegisterConcrete(&bcBlockResponseMessage{}, "tendermint/blockchain/BlockResponse", nil) + cdc.RegisterConcrete(&bcNoBlockResponseMessage{}, "tendermint/blockchain/NoBlockResponse", nil) + cdc.RegisterConcrete(&bcStatusResponseMessage{}, "tendermint/blockchain/StatusResponse", nil) + cdc.RegisterConcrete(&bcStatusRequestMessage{}, "tendermint/blockchain/StatusRequest", nil) } -func (r *Reactor) Receive(event Event) { - // XXX: decode and serialize write events - // TODO: backpressure +func decodeMsg(bz []byte) (msg BlockchainMessage, err error) { + if len(bz) > maxMsgSize { + return msg, fmt.Errorf("msg exceeds max size (%d > %d)", len(bz), maxMsgSize) + } + err = cdc.UnmarshalBinaryBare(bz, &msg) + return +} + +// Receive implements Reactor by handling different message types. +func (r *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { + msg, err := decodeMsg(msgBytes) + if err != nil { + r.logger.Error("error decoding message", + "src", src.ID(), "chId", chID, "msg", msg, "err", err, "bytes", msgBytes) + _ = r.reporter.Report(behaviour.BadMessage(src.ID(), err.Error())) + return + } + + if err = msg.ValidateBasic(); err != nil { + r.logger.Error("peer sent us invalid msg", "peer", src, "msg", msg, "err", err) + _ = r.reporter.Report(behaviour.BadMessage(src.ID(), err.Error())) + return + } + + r.logger.Debug("Receive", "src", src.ID(), "chID", chID, "msg", msg) + + switch msg := msg.(type) { + case *bcStatusRequestMessage: + if err := r.io.sendStatusResponse(r.store.Height(), src.ID()); err != nil { + r.logger.Error("Could not send status message to peer", "src", src) + } + + case *bcBlockRequestMessage: + block := r.store.LoadBlock(msg.Height) + if block != nil { + if err = r.io.sendBlockToPeer(block, src.ID()); err != nil { + r.logger.Error("Could not send block message to peer: ", err) + } + } else { + r.logger.Info("peer asking for a block we don't have", "src", src, "height", msg.Height) + peerID := src.ID() + if err = r.io.sendBlockNotFound(msg.Height, peerID); err != nil { + r.logger.Error("Couldn't send block not found: ", err) + } + } + + case *bcStatusResponseMessage: + r.events <- bcStatusResponse{peerID: src.ID(), height: msg.Height} + + case *bcBlockResponseMessage: + r.events <- bcBlockResponse{ + peerID: src.ID(), + block: msg.Block, + size: int64(len(msgBytes)), + time: time.Now(), + } + + case *bcNoBlockResponseMessage: + r.events <- bcNoBlockResponse{peerID: src.ID(), height: msg.Height, time: time.Now()} + } +} + +// AddPeer implements Reactor interface +func (r *BlockchainReactor) AddPeer(peer p2p.Peer) { + err := r.io.sendStatusResponse(r.store.Height(), peer.ID()) + if err != nil { + r.logger.Error("Could not send status message to peer new", "src", peer.ID, "height", r.SyncHeight()) + } + r.events <- bcAddNewPeer{peerID: peer.ID()} +} + +// RemovePeer implements Reactor interface. +func (r *BlockchainReactor) RemovePeer(peer p2p.Peer, reason interface{}) { + event := bcRemovePeer{ + peerID: peer.ID(), + reason: reason, + } r.events <- event } -func (r *Reactor) AddPeer() { - // TODO: add peer event and send to demuxer +// GetChannels implements Reactor +func (r *BlockchainReactor) GetChannels() []*p2p.ChannelDescriptor { + return []*p2p.ChannelDescriptor{ + { + ID: BlockchainChannel, + Priority: 10, + SendQueueCapacity: 2000, + RecvBufferCapacity: 50 * 4096, + RecvMessageCapacity: maxMsgSize, + }, + } } diff --git a/blockchain/v2/reactor_test.go b/blockchain/v2/reactor_test.go index 46a2e60c6..081fcb4a5 100644 --- a/blockchain/v2/reactor_test.go +++ b/blockchain/v2/reactor_test.go @@ -1,22 +1,525 @@ package v2 import ( + "net" + "os" + "sort" + "sync" "testing" + "time" + + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/behaviour" + cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/libs/service" + "github.com/tendermint/tendermint/mock" + "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/p2p/conn" + "github.com/tendermint/tendermint/proxy" + sm "github.com/tendermint/tendermint/state" + "github.com/tendermint/tendermint/store" + "github.com/tendermint/tendermint/types" + tmtime "github.com/tendermint/tendermint/types/time" + dbm "github.com/tendermint/tm-db" ) -func TestReactor(t *testing.T) { +type mockPeer struct { + service.Service + id p2p.ID +} + +func (mp mockPeer) FlushStop() {} +func (mp mockPeer) ID() p2p.ID { return mp.id } +func (mp mockPeer) RemoteIP() net.IP { return net.IP{} } +func (mp mockPeer) RemoteAddr() net.Addr { return &net.TCPAddr{IP: mp.RemoteIP(), Port: 8800} } + +func (mp mockPeer) IsOutbound() bool { return true } +func (mp mockPeer) IsPersistent() bool { return true } +func (mp mockPeer) CloseConn() error { return nil } + +func (mp mockPeer) NodeInfo() p2p.NodeInfo { + return p2p.DefaultNodeInfo{ + DefaultNodeID: "", + ListenAddr: "", + } +} +func (mp mockPeer) Status() conn.ConnectionStatus { return conn.ConnectionStatus{} } +func (mp mockPeer) SocketAddr() *p2p.NetAddress { return &p2p.NetAddress{} } + +func (mp mockPeer) Send(byte, []byte) bool { return true } +func (mp mockPeer) TrySend(byte, []byte) bool { return true } + +func (mp mockPeer) Set(string, interface{}) {} +func (mp mockPeer) Get(string) interface{} { return struct{}{} } + +//nolint:unused +type mockBlockStore struct { + blocks map[int64]*types.Block +} + +func (ml *mockBlockStore) Height() int64 { + return int64(len(ml.blocks)) +} + +func (ml *mockBlockStore) LoadBlock(height int64) *types.Block { + return ml.blocks[height] +} + +func (ml *mockBlockStore) SaveBlock(block *types.Block, part *types.PartSet, commit *types.Commit) { + ml.blocks[block.Height] = block +} + +type mockBlockApplier struct { +} + +// XXX: Add whitelist/blacklist? +func (mba *mockBlockApplier) ApplyBlock(state sm.State, blockID types.BlockID, block *types.Block) (sm.State, error) { + state.LastBlockHeight++ + return state, nil +} + +type mockSwitchIo struct { + mtx sync.Mutex + switchedToConsensus bool + numStatusResponse int + numBlockResponse int + numNoBlockResponse int +} + +func (sio *mockSwitchIo) sendBlockRequest(peerID p2p.ID, height int64) error { + return nil +} + +func (sio *mockSwitchIo) sendStatusResponse(height int64, peerID p2p.ID) error { + sio.mtx.Lock() + defer sio.mtx.Unlock() + sio.numStatusResponse++ + return nil +} + +func (sio *mockSwitchIo) sendBlockToPeer(block *types.Block, peerID p2p.ID) error { + sio.mtx.Lock() + defer sio.mtx.Unlock() + sio.numBlockResponse++ + return nil +} + +func (sio *mockSwitchIo) sendBlockNotFound(height int64, peerID p2p.ID) error { + sio.mtx.Lock() + defer sio.mtx.Unlock() + sio.numNoBlockResponse++ + return nil +} + +func (sio *mockSwitchIo) trySwitchToConsensus(state sm.State, blocksSynced int) { + sio.mtx.Lock() + defer sio.mtx.Unlock() + sio.switchedToConsensus = true +} + +// nolint:unused +func (sio *mockSwitchIo) hasSwitchedToConsensus() bool { + sio.mtx.Lock() + defer sio.mtx.Unlock() + return sio.switchedToConsensus +} + +func (sio *mockSwitchIo) broadcastStatusRequest(height int64) { +} + +type testReactorParams struct { + logger log.Logger + genDoc *types.GenesisDoc + privVals []types.PrivValidator + startHeight int64 + bufferSize int + mockA bool +} + +func newTestReactor(p testReactorParams) *BlockchainReactor { + store, state, _ := newReactorStore(p.genDoc, p.privVals, p.startHeight) + reporter := behaviour.NewMockReporter() + + var appl blockApplier + + if p.mockA { + appl = &mockBlockApplier{} + } else { + app := &testApp{} + cc := proxy.NewLocalClientCreator(app) + proxyApp := proxy.NewAppConns(cc) + err := proxyApp.Start() + if err != nil { + panic(errors.Wrap(err, "error start app")) + } + db := dbm.NewMemDB() + appl = sm.NewBlockExecutor(db, p.logger, proxyApp.Consensus(), mock.Mempool{}, sm.MockEvidencePool{}) + sm.SaveState(db, state) + } + + r := newReactor(state, store, reporter, appl, p.bufferSize) + logger := log.TestingLogger() + r.SetLogger(logger.With("module", "blockchain")) + + return r +} + +// 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) + +// params := testReactorParams{ +// logger: log.TestingLogger(), +// genDoc: genDoc, +// privVals: privVals, +// startHeight: 10, +// bufferSize: 100, +// mockA: true, +// } + +// 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"}, +// }, +// }, +// } + +// 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) +// } + +// // 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 ( - bufferSize = 10 - reactor = NewReactor(bufferSize) + channelID = byte(0x40) ) - reactor.Start() - script := []Event{ - // TODO + config := cfg.ResetTestRoot("blockchain_reactor_v2_test") + defer os.RemoveAll(config.RootDir) + genDoc, privVals := randGenesisDoc(config.ChainID(), 1, false, 30) + + params := testReactorParams{ + logger: log.TestingLogger(), + genDoc: genDoc, + privVals: privVals, + startHeight: 20, + bufferSize: 100, + mockA: true, + } + + type testEvent struct { + peer string + event interface{} } - for _, event := range script { - reactor.Receive(event) + tests := []struct { + name string + params testReactorParams + msgs []testEvent + }{ + { + name: "status request", + params: params, + msgs: []testEvent{ + {"P1", bcStatusRequestMessage{}}, + {"P1", bcBlockRequestMessage{Height: 13}}, + {"P1", bcBlockRequestMessage{Height: 20}}, + {"P1", bcBlockRequestMessage{Height: 22}}, + }, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + reactor := newTestReactor(params) + reactor.Start() + mockSwitch := &mockSwitchIo{switchedToConsensus: false} + reactor.io = mockSwitch + + for i := 0; i < len(tt.msgs); i++ { + step := tt.msgs[i] + switch ev := step.event.(type) { + case bcStatusRequestMessage: + old := mockSwitch.numStatusResponse + reactor.Receive(channelID, mockPeer{id: p2p.ID(step.peer)}, cdc.MustMarshalBinaryBare(ev)) + assert.Equal(t, old+1, mockSwitch.numStatusResponse) + case bcBlockRequestMessage: + if ev.Height > params.startHeight { + old := mockSwitch.numNoBlockResponse + reactor.Receive(channelID, mockPeer{id: p2p.ID(step.peer)}, cdc.MustMarshalBinaryBare(ev)) + assert.Equal(t, old+1, mockSwitch.numNoBlockResponse) + } else { + old := mockSwitch.numBlockResponse + reactor.Receive(channelID, mockPeer{id: p2p.ID(step.peer)}, cdc.MustMarshalBinaryBare(ev)) + assert.Equal(t, old+1, mockSwitch.numBlockResponse) + } + } + } + reactor.Stop() + }) + } +} + +//---------------------------------------------- +// utility funcs + +func makeTxs(height int64) (txs []types.Tx) { + for i := 0; i < 10; i++ { + txs = append(txs, types.Tx([]byte{byte(height), byte(i)})) + } + return txs +} + +func makeBlock(privVal types.PrivValidator, height int64, state sm.State, lastCommit *types.Commit) *types.Block { + message := state.MakeHashMessage(0) + proof, _ := privVal.GenerateVRFProof(message) + proposerAddr := types.SelectProposer(state.Validators, state.LastProofHash, height, 0).Address + block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, proposerAddr, 0, proof) + return block +} + +type testApp struct { + abci.BaseApplication +} + +func randGenesisDoc(chainID string, numValidators int, randPower bool, minPower int64) ( + *types.GenesisDoc, []types.PrivValidator) { + validators := make([]types.GenesisValidator, numValidators) + privValidators := make([]types.PrivValidator, numValidators) + for i := 0; i < numValidators; i++ { + val, privVal := types.RandValidator(randPower, minPower) + validators[i] = types.GenesisValidator{ + PubKey: val.PubKey, + Power: val.VotingPower, + } + privValidators[i] = privVal + } + sort.Sort(types.PrivValidatorsByAddress(privValidators)) + + return &types.GenesisDoc{ + GenesisTime: tmtime.Now(), + ChainID: chainID, + Validators: validators, + }, privValidators +} + +// Why are we importing the entire blockExecutor dependency graph here +// when we have the facilities to +func newReactorStore( + genDoc *types.GenesisDoc, + privVals []types.PrivValidator, + maxBlockHeight int64) (*store.BlockStore, sm.State, *sm.BlockExecutor) { + if len(privVals) != 1 { + panic("only support one validator") + } + app := &testApp{} + cc := proxy.NewLocalClientCreator(app) + proxyApp := proxy.NewAppConns(cc) + err := proxyApp.Start() + if err != nil { + panic(errors.Wrap(err, "error start app")) + } + + stateDB := dbm.NewMemDB() + blockStore := store.NewBlockStore(dbm.NewMemDB()) + + state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc) + if err != nil { + panic(errors.Wrap(err, "error constructing state from genesis file")) + } + + db := dbm.NewMemDB() + blockExec := sm.NewBlockExecutor(db, log.TestingLogger(), proxyApp.Consensus(), + mock.Mempool{}, sm.MockEvidencePool{}) + sm.SaveState(db, state) + + // add blocks in + for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ { + lastCommit := types.NewCommit(blockHeight-1, 0, types.BlockID{}, nil) + if blockHeight > 1 { + lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1) + lastBlock := blockStore.LoadBlock(blockHeight - 1) + vote, err := types.MakeVote( + lastBlock.Header.Height, + lastBlockMeta.BlockID, + state.Validators, + privVals[0], + lastBlock.Header.ChainID, + time.Now(), + ) + if err != nil { + panic(err) + } + lastCommit = types.NewCommit(vote.Height, vote.Round, + lastBlockMeta.BlockID, []types.CommitSig{vote.CommitSig()}) + } + + thisBlock := makeBlock(privVals[0], blockHeight, state, lastCommit) + + thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes) + blockID := types.BlockID{Hash: thisBlock.Hash(), PartsHeader: thisParts.Header()} + + state, err = blockExec.ApplyBlock(state, blockID, thisBlock) + if err != nil { + panic(errors.Wrap(err, "error apply block")) + } + + blockStore.SaveBlock(thisBlock, thisParts, lastCommit) } - reactor.Stop() + return blockStore, state, blockExec } diff --git a/blockchain/v2/routine.go b/blockchain/v2/routine.go index 897dd738c..1a883c3c4 100644 --- a/blockchain/v2/routine.go +++ b/blockchain/v2/routine.go @@ -10,7 +10,7 @@ import ( type handleFunc = func(event Event) (Event, error) -// Routines are a structure which model a finite state machine as serialized +// Routine is a structure that models a finite state machine as serialized // stream of events processed by a handle function. This Routine structure // handles the concurrency and messaging guarantees. Events are sent via // `send` are handled by the `handle` function to produce an iterator @@ -80,7 +80,7 @@ func (rt *Routine) start() { return } rt.metrics.EventsOut.With("routine", rt.name).Add(1) - rt.logger.Debug(fmt.Sprintf("%s produced %T %+v\n", rt.name, oEvent, oEvent)) + rt.logger.Debug(fmt.Sprintf("%s: produced %T %+v\n", rt.name, oEvent, oEvent)) rt.out <- oEvent } @@ -98,6 +98,7 @@ func (rt *Routine) send(event Event) bool { rt.logger.Info(fmt.Sprintf("%s: send failed, queue was full/stopped \n", rt.name)) return false } + rt.metrics.EventsSent.With("routine", rt.name).Add(1) return true } diff --git a/blockchain/v2/scheduler.go b/blockchain/v2/scheduler.go index ab3892dc5..3cf0b2468 100644 --- a/blockchain/v2/scheduler.go +++ b/blockchain/v2/scheduler.go @@ -11,52 +11,11 @@ import ( "github.com/tendermint/tendermint/types" ) -// Events - -// XXX: The handle API would be much simpler if it return a single event, an -// Event, which embeds a terminationEvent if it wants to terminate the routine. - -// Input events into the scheduler: -// ticker event for cleaning peers -type tryPrunePeer struct { - priorityHigh - time time.Time -} - -// ticker event for scheduling block requests -type trySchedule struct { - priorityHigh - time time.Time -} - -// blockResponse message received from a peer -type bcBlockResponse struct { - priorityNormal - time time.Time - peerID p2p.ID - height int64 - size int64 - block *types.Block -} - -// statusResponse message received from a peer -type bcStatusResponse struct { - priorityNormal - time time.Time - peerID p2p.ID - height int64 -} - -// new peer is connected -type addNewPeer struct { - priorityNormal - peerID p2p.ID -} - -// Output events issued by the scheduler: +// Events generated by the scheduler: // all blocks have been processed type scFinishedEv struct { priorityNormal + reason string } // send a blockRequest message @@ -80,6 +39,10 @@ type scPeerError struct { reason error } +func (e scPeerError) String() string { + return fmt.Sprintf("scPeerError - peerID %s, err %s", e.peerID, e.reason) +} + // scheduler removed a set of peers (timed out or slow peer) type scPeersPruned struct { priorityHigh @@ -160,9 +123,10 @@ func (p scPeer) String() string { func newScPeer(peerID p2p.ID) *scPeer { return &scPeer{ - peerID: peerID, - state: peerStateNew, - height: -1, + peerID: peerID, + state: peerStateNew, + height: -1, + lastTouched: time.Time{}, } } @@ -176,11 +140,17 @@ type scheduler struct { // in Processed state. height int64 + // lastAdvance tracks the last time a block execution happened. + // syncTimeout is the maximum time the scheduler waits to advance in the fast sync process before finishing. + // This covers the cases where there are no peers or all peers have a lower height. + lastAdvance time.Time + syncTimeout time.Duration + // a map of peerID to scheduler specific peer struct `scPeer` used to keep // track of peer specific state peers map[p2p.ID]*scPeer - peerTimeout time.Duration - minRecvRate int64 // minimum receive rate from peer otherwise prune + peerTimeout time.Duration // maximum response time from a peer otherwise prune + minRecvRate int64 // minimum receive rate from peer otherwise prune // the maximum number of blocks that should be New, Received or Pending at any point // in time. This is used to enforce a limit on the blockStates map. @@ -204,15 +174,20 @@ func (sc scheduler) String() string { sc.initHeight, sc.blockStates, sc.peers, sc.pendingBlocks, sc.pendingTime, sc.receivedBlocks) } -func newScheduler(initHeight int64) *scheduler { +func newScheduler(initHeight int64, startTime time.Time) *scheduler { sc := scheduler{ initHeight: initHeight, + lastAdvance: startTime, + syncTimeout: 60 * time.Second, height: initHeight + 1, blockStates: make(map[int64]blockState), peers: make(map[p2p.ID]*scPeer), pendingBlocks: make(map[int64]p2p.ID), pendingTime: make(map[int64]time.Time), receivedBlocks: make(map[int64]p2p.ID), + targetPending: 10, // TODO - pass as param + peerTimeout: 15 * time.Second, // TODO - pass as param + minRecvRate: 0, //int64(7680), TODO - pass as param } return &sc @@ -316,6 +291,7 @@ func (sc *scheduler) setPeerHeight(peerID p2p.ID, height int64) error { } if height < peer.height { + sc.removePeer(peerID) return fmt.Errorf("cannot move peer height lower. from %d to %d", peer.height, height) } @@ -327,7 +303,7 @@ func (sc *scheduler) setPeerHeight(peerID p2p.ID, height int64) error { } func (sc *scheduler) getStateAtHeight(height int64) blockState { - if height <= sc.initHeight { + if height < sc.height { return blockStateProcessed } else if state, ok := sc.blockStates[height]; ok { return state @@ -349,41 +325,8 @@ func (sc *scheduler) getPeersAtHeightOrAbove(height int64) []p2p.ID { return peers } -func (sc *scheduler) peersInactiveSince(duration time.Duration, now time.Time) []p2p.ID { - peers := []p2p.ID{} - for _, peer := range sc.peers { - if peer.state != peerStateReady { - continue - } - if now.Sub(peer.lastTouched) > duration { - peers = append(peers, peer.peerID) - } - } - - // Ensure the order is deterministic for testing - sort.Sort(PeerByID(peers)) - return peers -} - -// will return peers who's lastRate i slower than minSpeed denominated in bytes -func (sc *scheduler) peersSlowerThan(minSpeed int64) []p2p.ID { - peers := []p2p.ID{} - for peerID, peer := range sc.peers { - if peer.state != peerStateReady { - continue - } - if peer.lastRate < minSpeed { - peers = append(peers, peerID) - } - } - - // Ensure the order is deterministic for testing - sort.Sort(PeerByID(peers)) - return peers -} - func (sc *scheduler) prunablePeers(peerTimout time.Duration, minRecvRate int64, now time.Time) []p2p.ID { - prunable := []p2p.ID{} + prunable := make([]p2p.ID, 0) for peerID, peer := range sc.peers { if peer.state != peerStateReady { continue @@ -407,8 +350,8 @@ func (sc *scheduler) markReceived(peerID p2p.ID, height int64, size int64, now t return fmt.Errorf("couldn't find peer %s", peerID) } - if peer.state == peerStateRemoved { - return fmt.Errorf("cannot receive blocks from removed peer %s", peerID) + if peer.state != peerStateReady { + return fmt.Errorf("cannot receive blocks from not ready peer %s", peerID) } if state := sc.getStateAtHeight(height); state != blockStatePending || sc.pendingBlocks[height] != peerID { @@ -454,14 +397,13 @@ func (sc *scheduler) markPending(peerID p2p.ID, height int64, time time.Time) er sc.setStateAtHeight(height, blockStatePending) sc.pendingBlocks[height] = peerID - // XXX: to make this more accurate we can introduce a message from - // the IO routine which indicates the time the request was put on the wire sc.pendingTime[height] = time return nil } func (sc *scheduler) markProcessed(height int64) error { + sc.lastAdvance = time.Now() state := sc.getStateAtHeight(height) if state != blockStateReceived { return fmt.Errorf("cannot mark height %d received from block state %s", height, state) @@ -476,6 +418,9 @@ func (sc *scheduler) markProcessed(height int64) error { } func (sc *scheduler) allBlocksProcessed() bool { + if len(sc.peers) == 0 { + return false + } return sc.height >= sc.maxHeight() } @@ -486,7 +431,7 @@ func (sc *scheduler) maxHeight() int64 { if peer.state != peerStateReady { continue } - if peer.height > max { + if max < peer.height { max = peer.height } } @@ -532,15 +477,15 @@ func (sc *scheduler) selectPeer(height int64) (p2p.ID, error) { } // find the set of peers with minimum number of pending requests. - minPending := math.MaxInt64 + var minPending int64 = math.MaxInt64 for mp := range pendingFrom { - if mp < minPending { - minPending = mp + if int64(mp) < minPending { + minPending = int64(mp) } } - sort.Sort(PeerByID(pendingFrom[minPending])) - return pendingFrom[minPending][0], nil + sort.Sort(PeerByID(pendingFrom[int(minPending)])) + return pendingFrom[int(minPending)][0], nil } // PeerByID is a list of peers sorted by peerID. @@ -570,12 +515,30 @@ func (sc *scheduler) handleBlockResponse(event bcBlockResponse) (Event, error) { err = sc.markReceived(event.peerID, event.block.Height, event.size, event.time) if err != nil { + _ = sc.removePeer(event.peerID) return scPeerError{peerID: event.peerID, reason: err}, nil } return scBlockReceived{peerID: event.peerID, block: event.block}, nil } +func (sc *scheduler) handleNoBlockResponse(event bcNoBlockResponse) (Event, error) { + if len(sc.peers) == 0 { + return noOp, nil + } + + peer, ok := sc.peers[event.peerID] + if !ok || peer.state == peerStateRemoved { + return noOp, nil + } + // The peer may have been just removed due to errors, low speed or timeouts. + _ = sc.removePeer(event.peerID) + + return scPeerError{peerID: event.peerID, + reason: fmt.Errorf("peer %v with height %d claims no block for %d", + event.peerID, peer.height, event.height)}, nil +} + func (sc *scheduler) handleBlockProcessed(event pcBlockProcessed) (Event, error) { if event.height != sc.height { panic(fmt.Sprintf("processed height %d but expected height %d", event.height, sc.height)) @@ -584,12 +547,12 @@ func (sc *scheduler) handleBlockProcessed(event pcBlockProcessed) (Event, error) if err != nil { // It is possible that a peer error or timeout is handled after the processor // has processed the block but before the scheduler received this event, - // so when pcBlockProcessed event is received the block had been requested again + // so when pcBlockProcessed event is received the block had been requested again. return scSchedulerFail{reason: err}, nil } if sc.allBlocksProcessed() { - return scFinishedEv{}, nil + return scFinishedEv{reason: "processed all blocks"}, nil } return noOp, nil @@ -608,13 +571,13 @@ func (sc *scheduler) handleBlockProcessError(event pcBlockVerificationFailure) ( } if sc.allBlocksProcessed() { - return scFinishedEv{}, nil + return scFinishedEv{reason: "error on last block"}, nil } return noOp, nil } -func (sc *scheduler) handleAddNewPeer(event addNewPeer) (Event, error) { +func (sc *scheduler) handleAddNewPeer(event bcAddNewPeer) (Event, error) { err := sc.addPeer(event.peerID) if err != nil { return scSchedulerFail{reason: err}, nil @@ -622,8 +585,7 @@ func (sc *scheduler) handleAddNewPeer(event addNewPeer) (Event, error) { return noOp, nil } -// XXX: unify types peerError -func (sc *scheduler) handlePeerError(event peerError) (Event, error) { +func (sc *scheduler) handleRemovePeer(event bcRemovePeer) (Event, error) { err := sc.removePeer(event.peerID) if err != nil { // XXX - It is possible that the removePeer fails here for legitimate reasons @@ -631,12 +593,23 @@ func (sc *scheduler) handlePeerError(event peerError) (Event, error) { return scSchedulerFail{reason: err}, nil } if sc.allBlocksProcessed() { - return scFinishedEv{}, nil + return scFinishedEv{reason: "removed peer"}, nil } return noOp, nil } -func (sc *scheduler) handleTryPrunePeer(event tryPrunePeer) (Event, error) { +func (sc *scheduler) handleTryPrunePeer(event rTryPrunePeer) (Event, error) { + + // Check behavior of peer responsible to deliver block at sc.height. + timeHeightAsked, ok := sc.pendingTime[sc.height] + if ok && time.Since(timeHeightAsked) > sc.peerTimeout { + // A request was sent to a peer for block at sc.height but a response was not received + // from that peer within sc.peerTimeout. Remove the peer. This is to ensure that a peer + // will be timed out even if it sends blocks at higher heights but prevents progress by + // not sending the block at current height. + sc.removePeer(sc.pendingBlocks[sc.height]) + } + prunablePeers := sc.prunablePeers(sc.peerTimeout, sc.minRecvRate, event.time) if len(prunablePeers) == 0 { return noOp, nil @@ -649,17 +622,19 @@ func (sc *scheduler) handleTryPrunePeer(event tryPrunePeer) (Event, error) { } } - // If all blocks are processed we should finish even some peers were pruned. + // If all blocks are processed we should finish. if sc.allBlocksProcessed() { - return scFinishedEv{}, nil + return scFinishedEv{reason: "after try prune"}, nil } return scPeersPruned{peers: prunablePeers}, nil } -// TODO - Schedule multiple block requests -func (sc *scheduler) handleTrySchedule(event trySchedule) (Event, error) { +func (sc *scheduler) handleTrySchedule(event rTrySchedule) (Event, error) { + if time.Since(sc.lastAdvance) > sc.syncTimeout { + return scFinishedEv{reason: "timeout, no advance"}, nil + } nextHeight := sc.nextHeightToSchedule() if nextHeight == -1 { @@ -693,17 +668,20 @@ func (sc *scheduler) handle(event Event) (Event, error) { case bcBlockResponse: nextEvent, err := sc.handleBlockResponse(event) return nextEvent, err - case trySchedule: + case bcNoBlockResponse: + nextEvent, err := sc.handleNoBlockResponse(event) + return nextEvent, err + case rTrySchedule: nextEvent, err := sc.handleTrySchedule(event) return nextEvent, err - case addNewPeer: + case bcAddNewPeer: nextEvent, err := sc.handleAddNewPeer(event) return nextEvent, err - case tryPrunePeer: - nextEvent, err := sc.handleTryPrunePeer(event) + case bcRemovePeer: + nextEvent, err := sc.handleRemovePeer(event) return nextEvent, err - case peerError: - nextEvent, err := sc.handlePeerError(event) + case rTryPrunePeer: + nextEvent, err := sc.handleTryPrunePeer(event) return nextEvent, err case pcBlockProcessed: nextEvent, err := sc.handleBlockProcessed(event) @@ -714,5 +692,4 @@ func (sc *scheduler) handle(event Event) (Event, error) { default: return scSchedulerFail{reason: fmt.Errorf("unknown event %v", event)}, nil } - //return noOp, nil } diff --git a/blockchain/v2/scheduler_test.go b/blockchain/v2/scheduler_test.go index a3a6db672..445ba51a7 100644 --- a/blockchain/v2/scheduler_test.go +++ b/blockchain/v2/scheduler_test.go @@ -23,6 +23,8 @@ type scTestParams struct { peerTimeout time.Duration minRecvRate int64 targetPending int + startTime time.Time + syncTimeout time.Duration } func verifyScheduler(sc *scheduler) { @@ -37,8 +39,9 @@ func verifyScheduler(sc *scheduler) { func newTestScheduler(params scTestParams) *scheduler { peers := make(map[p2p.ID]*scPeer) + var maxHeight int64 - sc := newScheduler(params.initHeight) + sc := newScheduler(params.initHeight, params.startTime) if params.height != 0 { sc.height = params.height } @@ -46,6 +49,9 @@ func newTestScheduler(params scTestParams) *scheduler { for id, peer := range params.peers { peer.peerID = p2p.ID(id) peers[p2p.ID(id)] = peer + if maxHeight < peer.height { + maxHeight = peer.height + } } for _, h := range params.allB { sc.blockStates[h] = blockStateNew @@ -64,6 +70,12 @@ func newTestScheduler(params scTestParams) *scheduler { sc.peers = peers sc.peerTimeout = params.peerTimeout + if params.syncTimeout == 0 { + sc.syncTimeout = 10 * time.Second + } else { + sc.syncTimeout = params.syncTimeout + } + if params.targetPending == 0 { sc.targetPending = 10 } else { @@ -80,7 +92,7 @@ func newTestScheduler(params scTestParams) *scheduler { func TestScInit(t *testing.T) { var ( initHeight int64 = 5 - sc = newScheduler(initHeight) + sc = newScheduler(initHeight, time.Now()) ) assert.Equal(t, blockStateProcessed, sc.getStateAtHeight(initHeight)) assert.Equal(t, blockStateUnknown, sc.getStateAtHeight(initHeight+1)) @@ -181,21 +193,21 @@ func TestScAddPeer(t *testing.T) { name: "add first peer", fields: scTestParams{}, args: args{peerID: "P1"}, - wantFields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateNew, height: -1}}}, + wantFields: scTestParams{peers: map[string]*scPeer{"P1": {height: -1, state: peerStateNew}}}, }, { name: "add second peer", - fields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateNew, height: -1}}}, + fields: scTestParams{peers: map[string]*scPeer{"P1": {height: -1, state: peerStateNew}}}, args: args{peerID: "P2"}, wantFields: scTestParams{peers: map[string]*scPeer{ - "P1": {state: peerStateNew, height: -1}, - "P2": {state: peerStateNew, height: -1}}}, + "P1": {height: -1, state: peerStateNew}, + "P2": {height: -1, state: peerStateNew}}}, }, { name: "attempt to add duplicate peer", - fields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateNew, height: -1}}}, + fields: scTestParams{peers: map[string]*scPeer{"P1": {height: -1}}}, args: args{peerID: "P1"}, - wantFields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateNew, height: -1}}}, + wantFields: scTestParams{peers: map[string]*scPeer{"P1": {height: -1}}}, wantErr: true, }, { @@ -271,8 +283,8 @@ func TestScTouchPeer(t *testing.T) { name: "touch peer in state Ready", fields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateReady, lastTouched: now}}}, args: args{peerID: "P1", time: now.Add(3 * time.Second)}, - wantFields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateReady, - lastTouched: now.Add(3 * time.Second)}}}, + wantFields: scTestParams{peers: map[string]*scPeer{ + "P1": {state: peerStateReady, lastTouched: now.Add(3 * time.Second)}}}, }, } @@ -289,195 +301,6 @@ func TestScTouchPeer(t *testing.T) { } } -func TestScPeersInactiveSince(t *testing.T) { - now := time.Now() - - type args struct { - threshold time.Duration - time time.Time - } - - tests := []struct { - name string - fields scTestParams - args args - wantResult []p2p.ID - }{ - { - name: "no peers", - fields: scTestParams{peers: map[string]*scPeer{}}, - args: args{threshold: time.Second, time: now}, - wantResult: []p2p.ID{}, - }, - { - name: "one active peer", - fields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateReady, lastTouched: now}}}, - args: args{threshold: time.Second, time: now}, - wantResult: []p2p.ID{}, - }, - { - name: "one inactive peer", - fields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateReady, lastTouched: now}}}, - args: args{threshold: time.Second, time: now.Add(time.Second + time.Millisecond)}, - wantResult: []p2p.ID{"P1"}, - }, - { - name: "one active and one inactive peer", - fields: scTestParams{peers: map[string]*scPeer{ - "P1": {state: peerStateReady, lastTouched: now}, - "P2": {state: peerStateReady, lastTouched: now.Add(time.Second)}}}, - args: args{threshold: time.Second, time: now.Add(time.Second + time.Millisecond)}, - wantResult: []p2p.ID{"P1"}, - }, - { - name: "one New peer", - fields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateNew}}}, - args: args{threshold: time.Second, time: now.Add(time.Millisecond)}, - wantResult: []p2p.ID{}, - }, - { - name: "one Removed peer", - fields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateRemoved, lastTouched: now}}}, - args: args{threshold: time.Second, time: now.Add(time.Millisecond)}, - wantResult: []p2p.ID{}, - }, - { - name: "one Ready active peer and one New", - fields: scTestParams{peers: map[string]*scPeer{ - "P1": {state: peerStateRemoved, lastTouched: now}, - "P2": {state: peerStateReady, lastTouched: now.Add(time.Millisecond)}}}, - args: args{threshold: time.Second, time: now.Add(2 * time.Millisecond)}, - wantResult: []p2p.ID{}, - }, - { - name: "one Ready inactive peer and one New", - fields: scTestParams{peers: map[string]*scPeer{ - "P1": {state: peerStateRemoved, lastTouched: now}, - "P2": {state: peerStateReady, lastTouched: now.Add(time.Millisecond)}}}, - args: args{threshold: time.Second, time: now.Add(time.Second + 2*time.Millisecond)}, - wantResult: []p2p.ID{"P2"}, - }, - { - name: "combination of New, Removed and, active and non active Ready peers", - fields: scTestParams{peers: map[string]*scPeer{ - "P1": {state: peerStateNew}, - "P2": {state: peerStateRemoved, lastTouched: now}, - "P3": {state: peerStateRemoved, lastTouched: now.Add(time.Second)}, - "P4": {state: peerStateReady, lastTouched: now.Add(time.Millisecond)}, - "P5": {state: peerStateReady, lastTouched: now.Add(3 * time.Millisecond)}}}, - args: args{threshold: time.Second, time: now.Add(time.Second + 2*time.Millisecond)}, - wantResult: []p2p.ID{"P4"}, - }, - } - - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - sc := newTestScheduler(tt.fields) - // peersInactiveSince should not mutate the scheduler - wantSc := sc - res := sc.peersInactiveSince(tt.args.threshold, tt.args.time) - sort.Sort(PeerByID(res)) - assert.Equal(t, tt.wantResult, res) - assert.Equal(t, wantSc, sc) - }) - } -} - -func TestScPeersSlowerThan(t *testing.T) { - type args struct { - minSpeed int64 - } - - tests := []struct { - name string - fields scTestParams - args args - wantResult []p2p.ID - }{ - { - name: "no peers", - fields: scTestParams{peers: map[string]*scPeer{}}, - args: args{minSpeed: 100}, - wantResult: []p2p.ID{}, - }, - { - name: "one Ready faster peer", - fields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateReady, lastRate: 101}}}, - args: args{minSpeed: 100}, - wantResult: []p2p.ID{}, - }, - { - name: "one Ready equal peer", - fields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateReady, lastRate: 100}}}, - args: args{minSpeed: 100}, - wantResult: []p2p.ID{}, - }, - { - name: "one Ready slow peer", - fields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateReady, lastRate: 99}}}, - args: args{minSpeed: 100}, - wantResult: []p2p.ID{"P1"}, - }, - { - name: "one Removed faster peer", - fields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateRemoved, lastRate: 101}}}, - args: args{minSpeed: 100}, - wantResult: []p2p.ID{}, - }, { - name: "one Removed equal peer", - fields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateRemoved, lastRate: 100}}}, - args: args{minSpeed: 100}, - wantResult: []p2p.ID{}, - }, - { - name: "one Removed slow peer", - fields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateRemoved, lastRate: 99}}}, - args: args{minSpeed: 100}, - wantResult: []p2p.ID{}, - }, - { - name: "one New peer", - fields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateNew}}}, - args: args{minSpeed: 100}, - wantResult: []p2p.ID{}, - }, - { - name: "one New peer", - fields: scTestParams{peers: map[string]*scPeer{"P1": {state: peerStateNew}}}, - args: args{minSpeed: 100}, - wantResult: []p2p.ID{}, - }, - { - name: "mixed peers", - fields: scTestParams{peers: map[string]*scPeer{ - "P1": {state: peerStateRemoved, lastRate: 101}, - "P2": {state: peerStateReady, lastRate: 101}, - "P3": {state: peerStateRemoved, lastRate: 100}, - "P4": {state: peerStateReady, lastRate: 100}, - "P5": {state: peerStateReady, lastRate: 99}, - "P6": {state: peerStateNew}, - "P7": {state: peerStateRemoved, lastRate: 99}, - "P8": {state: peerStateReady, lastRate: 99}, - }}, - args: args{minSpeed: 100}, - wantResult: []p2p.ID{"P5", "P8"}, - }, - } - - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - sc := newTestScheduler(tt.fields) - // peersSlowerThan should not mutate the scheduler - wantSc := sc - res := sc.peersSlowerThan(tt.args.minSpeed) - assert.Equal(t, tt.wantResult, res) - assert.Equal(t, wantSc, sc) - }) - } -} - func TestScPrunablePeers(t *testing.T) { now := time.Now() @@ -716,8 +539,8 @@ func TestScSetPeerHeight(t *testing.T) { allB: []int64{1, 2, 3, 4}}, args: args{peerID: "P1", height: 2}, wantFields: scTestParams{ - peers: map[string]*scPeer{"P1": {height: 4, state: peerStateReady}}, - allB: []int64{1, 2, 3, 4}}, + peers: map[string]*scPeer{"P1": {height: 4, state: peerStateRemoved}}, + allB: []int64{}}, wantErr: true, }, { @@ -845,7 +668,7 @@ func TestScGetPeersAtHeight(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { sc := newTestScheduler(tt.fields) - // getPeersAtHeightOrAbove should not mutate the scheduler + // getPeersAtHeight should not mutate the scheduler wantSc := sc res := sc.getPeersAtHeightOrAbove(tt.args.height) sort.Sort(PeerByID(res)) @@ -1082,8 +905,11 @@ func TestScMarkReceived(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { sc := newTestScheduler(tt.fields) - if err := sc.markReceived(tt.args.peerID, - tt.args.height, tt.args.size, now.Add(time.Second)); (err != nil) != tt.wantErr { + if err := sc.markReceived( + tt.args.peerID, + tt.args.height, + tt.args.size, + now.Add(time.Second)); (err != nil) != tt.wantErr { t.Errorf("markReceived() wantErr %v, error = %v", tt.wantErr, err) } wantSc := newTestScheduler(tt.wantFields) @@ -1145,11 +971,17 @@ func TestScMarkProcessed(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { sc := newTestScheduler(tt.fields) + oldBlockState := sc.getStateAtHeight(tt.args.height) if err := sc.markProcessed(tt.args.height); (err != nil) != tt.wantErr { t.Errorf("markProcessed() wantErr %v, error = %v", tt.wantErr, err) } + if tt.wantErr { + assert.Equal(t, oldBlockState, sc.getStateAtHeight(tt.args.height)) + } else { + assert.Equal(t, blockStateProcessed, sc.getStateAtHeight(tt.args.height)) + } wantSc := newTestScheduler(tt.wantFields) - assert.Equal(t, wantSc, sc) + checkSameScheduler(t, wantSc, sc) }) } } @@ -1163,9 +995,9 @@ func TestScAllBlocksProcessed(t *testing.T) { wantResult bool }{ { - name: "no blocks", + name: "no blocks, no peers", fields: scTestParams{}, - wantResult: true, + wantResult: false, }, { name: "only New blocks", @@ -1225,7 +1057,7 @@ func TestScAllBlocksProcessed(t *testing.T) { wantSc := sc res := sc.allBlocksProcessed() assert.Equal(t, tt.wantResult, res) - assert.Equal(t, wantSc, sc) + checkSameScheduler(t, wantSc, sc) }) } } @@ -1305,8 +1137,7 @@ func TestScNextHeightToSchedule(t *testing.T) { resMin := sc.nextHeightToSchedule() assert.Equal(t, tt.wantHeight, resMin) - assert.Equal(t, wantSc, sc) - + checkSameScheduler(t, wantSc, sc) }) } } @@ -1414,7 +1245,7 @@ func TestScSelectPeer(t *testing.T) { res, err := sc.selectPeer(tt.args.height) assert.Equal(t, tt.wantResult, res) assert.Equal(t, tt.wantError, err != nil) - assert.Equal(t, wantSc, sc) + checkSameScheduler(t, wantSc, sc) }) } } @@ -1424,6 +1255,20 @@ func makeScBlock(height int64) *types.Block { return &types.Block{Header: types.Header{Height: height}} } +// used in place of assert.Equal(t, want, actual) to avoid failures due to +// scheduler.lastAdvanced timestamp inequalities. +func checkSameScheduler(t *testing.T, want *scheduler, actual *scheduler) { + assert.Equal(t, want.initHeight, actual.initHeight) + assert.Equal(t, want.height, actual.height) + assert.Equal(t, want.peers, actual.peers) + assert.Equal(t, want.blockStates, actual.blockStates) + assert.Equal(t, want.pendingBlocks, actual.pendingBlocks) + assert.Equal(t, want.pendingTime, actual.pendingTime) + assert.Equal(t, want.blockStates, actual.blockStates) + assert.Equal(t, want.receivedBlocks, actual.receivedBlocks) + assert.Equal(t, want.blockStates, actual.blockStates) +} + // checkScResults checks scheduler handler test results func checkScResults(t *testing.T, wantErr bool, err error, wantEvent Event, event Event) { if (err != nil) != wantErr { @@ -1439,8 +1284,6 @@ func checkScResults(t *testing.T, wantErr bool, err error, wantEvent Event, even assert.Equal(t, wantEvent.block, event.(scBlockReceived).block) case scSchedulerFail: assert.Equal(t, wantEvent.reason != nil, event.(scSchedulerFail).reason != nil) - default: - assert.Equal(t, wantEvent, event) } } @@ -1449,7 +1292,6 @@ func TestScHandleBlockResponse(t *testing.T) { block6FromP1 := bcBlockResponse{ time: now.Add(time.Millisecond), peerID: p2p.ID("P1"), - height: 6, size: 100, block: makeScBlock(6), } @@ -1530,6 +1372,82 @@ func TestScHandleBlockResponse(t *testing.T) { } } +func TestScHandleNoBlockResponse(t *testing.T) { + now := time.Now() + noBlock6FromP1 := bcNoBlockResponse{ + time: now.Add(time.Millisecond), + peerID: p2p.ID("P1"), + height: 6, + } + + tests := []struct { + name string + fields scTestParams + wantEvent Event + wantFields scTestParams + wantErr bool + }{ + { + name: "empty scheduler", + fields: scTestParams{}, + wantEvent: noOpEvent{}, + wantFields: scTestParams{}, + }, + { + name: "noBlock from removed peer", + fields: scTestParams{peers: map[string]*scPeer{"P1": {height: 8, state: peerStateRemoved}}}, + wantEvent: noOpEvent{}, + wantFields: scTestParams{peers: map[string]*scPeer{"P1": {height: 8, state: peerStateRemoved}}}, + }, + { + name: "for block we haven't asked for", + fields: scTestParams{ + peers: map[string]*scPeer{"P1": {height: 8, state: peerStateReady}}, + allB: []int64{1, 2, 3, 4, 5, 6, 7, 8}}, + wantEvent: scPeerError{peerID: "P1", reason: fmt.Errorf("some error")}, + wantFields: scTestParams{peers: map[string]*scPeer{"P1": {height: 8, state: peerStateRemoved}}}, + }, + { + name: "noBlock from peer we don't have", + fields: scTestParams{ + peers: map[string]*scPeer{"P2": {height: 8, state: peerStateReady}}, + allB: []int64{1, 2, 3, 4, 5, 6, 7, 8}, + pending: map[int64]p2p.ID{6: "P2"}, + pendingTime: map[int64]time.Time{6: now}, + }, + wantEvent: noOpEvent{}, + wantFields: scTestParams{ + peers: map[string]*scPeer{"P2": {height: 8, state: peerStateReady}}, + allB: []int64{1, 2, 3, 4, 5, 6, 7, 8}, + pending: map[int64]p2p.ID{6: "P2"}, + pendingTime: map[int64]time.Time{6: now}, + }, + }, + { + name: "noBlock from existing peer", + fields: scTestParams{ + peers: map[string]*scPeer{"P1": {height: 8, state: peerStateReady}}, + allB: []int64{1, 2, 3, 4, 5, 6, 7, 8}, + pending: map[int64]p2p.ID{6: "P1"}, + pendingTime: map[int64]time.Time{6: now}, + }, + wantEvent: scPeerError{peerID: "P1", reason: fmt.Errorf("some error")}, + wantFields: scTestParams{peers: map[string]*scPeer{"P1": {height: 8, state: peerStateRemoved}}}, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + sc := newTestScheduler(tt.fields) + event, err := sc.handleNoBlockResponse(noBlock6FromP1) + checkScResults(t, tt.wantErr, err, tt.wantEvent, event) + wantSc := newTestScheduler(tt.wantFields) + assert.Equal(t, wantSc, sc) + }) + } +} + func TestScHandleBlockProcessed(t *testing.T) { now := time.Now() processed6FromP1 := pcBlockProcessed{ @@ -1702,11 +1620,11 @@ func TestScHandleBlockVerificationFailure(t *testing.T) { } func TestScHandleAddNewPeer(t *testing.T) { - addP1 := addNewPeer{ + addP1 := bcAddNewPeer{ peerID: p2p.ID("P1"), } type args struct { - event addNewPeer + event bcAddNewPeer } tests := []struct { @@ -1754,76 +1672,14 @@ func TestScHandleAddNewPeer(t *testing.T) { } } -func TestScHandlePeerError(t *testing.T) { - errP1 := peerError{ - peerID: p2p.ID("P1"), - } - type args struct { - event peerError - } - - tests := []struct { - name string - fields scTestParams - args args - wantEvent Event - wantErr bool - }{ - { - name: "no peers", - fields: scTestParams{}, - args: args{event: errP1}, - wantEvent: scSchedulerFail{reason: fmt.Errorf("some error")}, - }, - { - name: "error finds no peer", - fields: scTestParams{ - height: 6, - peers: map[string]*scPeer{"P2": {height: 8, state: peerStateReady}}, - allB: []int64{6, 7, 8}, - }, - args: args{event: errP1}, - wantEvent: scSchedulerFail{reason: fmt.Errorf("some error")}, - }, - { - name: "error finds peer, only peer is removed", - fields: scTestParams{ - height: 6, - peers: map[string]*scPeer{"P1": {height: 8, state: peerStateReady}}, - allB: []int64{6, 7, 8}, - }, - args: args{event: errP1}, - wantEvent: scFinishedEv{}, - }, - { - name: "error finds peer, one of two peers are removed", - fields: scTestParams{ - peers: map[string]*scPeer{"P1": {height: 8, state: peerStateReady}, "P2": {height: 8, state: peerStateReady}}, - allB: []int64{1, 2, 3, 4, 5, 6, 7, 8}, - }, - args: args{event: errP1}, - wantEvent: noOpEvent{}, - }, - } - - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - sc := newTestScheduler(tt.fields) - event, err := sc.handlePeerError(tt.args.event) - checkScResults(t, tt.wantErr, err, tt.wantEvent, event) - }) - } -} - func TestScHandleTryPrunePeer(t *testing.T) { now := time.Now() - pruneEv := tryPrunePeer{ + pruneEv := rTryPrunePeer{ time: now.Add(time.Second + time.Millisecond), } type args struct { - event tryPrunePeer + event rTryPrunePeer } tests := []struct { @@ -1914,14 +1770,14 @@ func TestScHandleTryPrunePeer(t *testing.T) { } } -func TestHandleTrySchedule(t *testing.T) { +func TestScHandleTrySchedule(t *testing.T) { now := time.Now() - tryEv := trySchedule{ + tryEv := rTrySchedule{ time: now.Add(time.Second + time.Millisecond), } type args struct { - event trySchedule + event rTrySchedule } tests := []struct { name string @@ -1932,41 +1788,44 @@ func TestHandleTrySchedule(t *testing.T) { }{ { name: "no peers", - fields: scTestParams{peers: map[string]*scPeer{}}, + fields: scTestParams{startTime: now, peers: map[string]*scPeer{}}, args: args{event: tryEv}, wantEvent: noOpEvent{}, }, { name: "only new peers", - fields: scTestParams{peers: map[string]*scPeer{"P1": {height: -1, state: peerStateNew}}}, + fields: scTestParams{startTime: now, peers: map[string]*scPeer{"P1": {height: -1, state: peerStateNew}}}, args: args{event: tryEv}, wantEvent: noOpEvent{}, }, { name: "only Removed peers", - fields: scTestParams{peers: map[string]*scPeer{"P1": {height: 4, state: peerStateRemoved}}}, + fields: scTestParams{startTime: now, peers: map[string]*scPeer{"P1": {height: 4, state: peerStateRemoved}}}, args: args{event: tryEv}, wantEvent: noOpEvent{}, }, { name: "one Ready shorter peer", fields: scTestParams{ - height: 6, - peers: map[string]*scPeer{"P1": {height: 4, state: peerStateReady}}}, + startTime: now, + height: 6, + peers: map[string]*scPeer{"P1": {height: 4, state: peerStateReady}}}, args: args{event: tryEv}, wantEvent: noOpEvent{}, }, { name: "one Ready equal peer", fields: scTestParams{ - peers: map[string]*scPeer{"P1": {height: 4, state: peerStateReady}}, - allB: []int64{1, 2, 3, 4}}, + startTime: now, + peers: map[string]*scPeer{"P1": {height: 4, state: peerStateReady}}, + allB: []int64{1, 2, 3, 4}}, args: args{event: tryEv}, wantEvent: scBlockRequest{peerID: "P1", height: 1}, }, { name: "many Ready higher peers with different number of pending requests", fields: scTestParams{ + startTime: now, peers: map[string]*scPeer{ "P1": {height: 4, state: peerStateReady}, "P2": {height: 5, state: peerStateReady}}, @@ -1983,6 +1842,7 @@ func TestHandleTrySchedule(t *testing.T) { { name: "many Ready higher peers with same number of pending requests", fields: scTestParams{ + startTime: now, peers: map[string]*scPeer{ "P2": {height: 8, state: peerStateReady}, "P1": {height: 8, state: peerStateReady}, @@ -2084,6 +1944,8 @@ func TestScHandleStatusResponse(t *testing.T) { } func TestScHandle(t *testing.T) { + now := time.Now() + type unknownEv struct { priorityNormal } @@ -2123,24 +1985,27 @@ func TestScHandle(t *testing.T) { name: "single peer, sync 3 blocks", steps: []scStep{ { // add P1 - currentSc: &scTestParams{peers: map[string]*scPeer{}, height: 1}, - args: args{event: addNewPeer{peerID: "P1"}}, + currentSc: &scTestParams{startTime: now, peers: map[string]*scPeer{}, height: 1}, + args: args{event: bcAddNewPeer{peerID: "P1"}}, wantEvent: noOpEvent{}, - wantSc: &scTestParams{peers: map[string]*scPeer{"P1": {height: -1, state: peerStateNew}}, height: 1}, + wantSc: &scTestParams{startTime: now, peers: map[string]*scPeer{ + "P1": {height: -1, state: peerStateNew}}, height: 1}, }, { // set height of P1 args: args{event: bcStatusResponse{peerID: "P1", time: tick[0], height: 3}}, wantEvent: noOpEvent{}, wantSc: &scTestParams{ - peers: map[string]*scPeer{"P1": {height: 3, state: peerStateReady}}, - allB: []int64{1, 2, 3}, - height: 1, + startTime: now, + peers: map[string]*scPeer{"P1": {height: 3, state: peerStateReady}}, + allB: []int64{1, 2, 3}, + height: 1, }, }, { // schedule block 1 - args: args{event: trySchedule{time: tick[1]}}, + args: args{event: rTrySchedule{time: tick[1]}}, wantEvent: scBlockRequest{peerID: "P1", height: 1}, wantSc: &scTestParams{ + startTime: now, peers: map[string]*scPeer{"P1": {height: 3, state: peerStateReady}}, allB: []int64{1, 2, 3}, pending: map[int64]p2p.ID{1: "P1"}, @@ -2149,9 +2014,10 @@ func TestScHandle(t *testing.T) { }, }, { // schedule block 2 - args: args{event: trySchedule{time: tick[2]}}, + args: args{event: rTrySchedule{time: tick[2]}}, wantEvent: scBlockRequest{peerID: "P1", height: 2}, wantSc: &scTestParams{ + startTime: now, peers: map[string]*scPeer{"P1": {height: 3, state: peerStateReady}}, allB: []int64{1, 2, 3}, pending: map[int64]p2p.ID{1: "P1", 2: "P1"}, @@ -2160,9 +2026,10 @@ func TestScHandle(t *testing.T) { }, }, { // schedule block 3 - args: args{event: trySchedule{time: tick[3]}}, + args: args{event: rTrySchedule{time: tick[3]}}, wantEvent: scBlockRequest{peerID: "P1", height: 3}, wantSc: &scTestParams{ + startTime: now, peers: map[string]*scPeer{"P1": {height: 3, state: peerStateReady}}, allB: []int64{1, 2, 3}, pending: map[int64]p2p.ID{1: "P1", 2: "P1", 3: "P1"}, @@ -2171,9 +2038,10 @@ func TestScHandle(t *testing.T) { }, }, { // block response 1 - args: args{event: bcBlockResponse{peerID: "P1", height: 1, time: tick[4], size: 100, block: makeScBlock(1)}}, + args: args{event: bcBlockResponse{peerID: "P1", time: tick[4], size: 100, block: makeScBlock(1)}}, wantEvent: scBlockReceived{peerID: "P1", block: makeScBlock(1)}, wantSc: &scTestParams{ + startTime: now, peers: map[string]*scPeer{"P1": {height: 3, state: peerStateReady, lastTouched: tick[4]}}, allB: []int64{1, 2, 3}, pending: map[int64]p2p.ID{2: "P1", 3: "P1"}, @@ -2183,9 +2051,10 @@ func TestScHandle(t *testing.T) { }, }, { // block response 2 - args: args{event: bcBlockResponse{peerID: "P1", height: 2, time: tick[5], size: 100, block: makeScBlock(2)}}, + args: args{event: bcBlockResponse{peerID: "P1", time: tick[5], size: 100, block: makeScBlock(2)}}, wantEvent: scBlockReceived{peerID: "P1", block: makeScBlock(2)}, wantSc: &scTestParams{ + startTime: now, peers: map[string]*scPeer{"P1": {height: 3, state: peerStateReady, lastTouched: tick[5]}}, allB: []int64{1, 2, 3}, pending: map[int64]p2p.ID{3: "P1"}, @@ -2195,33 +2064,36 @@ func TestScHandle(t *testing.T) { }, }, { // block response 3 - args: args{event: bcBlockResponse{peerID: "P1", height: 3, time: tick[6], size: 100, block: makeScBlock(3)}}, + args: args{event: bcBlockResponse{peerID: "P1", time: tick[6], size: 100, block: makeScBlock(3)}}, wantEvent: scBlockReceived{peerID: "P1", block: makeScBlock(3)}, wantSc: &scTestParams{ - peers: map[string]*scPeer{"P1": {height: 3, state: peerStateReady, lastTouched: tick[6]}}, - allB: []int64{1, 2, 3}, - received: map[int64]p2p.ID{1: "P1", 2: "P1", 3: "P1"}, - height: 1, + startTime: now, + peers: map[string]*scPeer{"P1": {height: 3, state: peerStateReady, lastTouched: tick[6]}}, + allB: []int64{1, 2, 3}, + received: map[int64]p2p.ID{1: "P1", 2: "P1", 3: "P1"}, + height: 1, }, }, { // processed block 1 args: args{event: pcBlockProcessed{peerID: p2p.ID("P1"), height: 1}}, wantEvent: noOpEvent{}, wantSc: &scTestParams{ - peers: map[string]*scPeer{"P1": {height: 3, state: peerStateReady, lastTouched: tick[6]}}, - allB: []int64{2, 3}, - received: map[int64]p2p.ID{2: "P1", 3: "P1"}, - height: 2, + startTime: now, + peers: map[string]*scPeer{"P1": {height: 3, state: peerStateReady, lastTouched: tick[6]}}, + allB: []int64{2, 3}, + received: map[int64]p2p.ID{2: "P1", 3: "P1"}, + height: 2, }, }, { // processed block 2 args: args{event: pcBlockProcessed{peerID: p2p.ID("P1"), height: 2}}, wantEvent: scFinishedEv{}, wantSc: &scTestParams{ - peers: map[string]*scPeer{"P1": {height: 3, state: peerStateReady, lastTouched: tick[6]}}, - allB: []int64{3}, - received: map[int64]p2p.ID{3: "P1"}, - height: 3, + startTime: now, + peers: map[string]*scPeer{"P1": {height: 3, state: peerStateReady, lastTouched: tick[6]}}, + allB: []int64{3}, + received: map[int64]p2p.ID{3: "P1"}, + height: 3, }, }, }, @@ -2231,6 +2103,7 @@ func TestScHandle(t *testing.T) { steps: []scStep{ { // failure processing block 1 currentSc: &scTestParams{ + startTime: now, peers: map[string]*scPeer{ "P1": {height: 4, state: peerStateReady, lastTouched: tick[6]}, "P2": {height: 3, state: peerStateReady, lastTouched: tick[6]}}, @@ -2241,6 +2114,7 @@ func TestScHandle(t *testing.T) { args: args{event: pcBlockVerificationFailure{height: 1, firstPeerID: "P1", secondPeerID: "P1"}}, wantEvent: noOpEvent{}, wantSc: &scTestParams{ + startTime: now, peers: map[string]*scPeer{ "P1": {height: 4, state: peerStateRemoved, lastTouched: tick[6]}, "P2": {height: 3, state: peerStateReady, lastTouched: tick[6]}}, @@ -2249,19 +2123,6 @@ func TestScHandle(t *testing.T) { height: 1, }, }, - /* - { // processed block 2 - args: args{event: pcBlockProcessed{peerID: p2p.ID("P1"), height: 2}}, - wantEvent: scFinishedEv{}, - wantSc: &scTestParams{ - peers: map[string]*scPeer{"P1": {height: 3, state: peerStateReady, lastTouched: tick[6]}}, - allB: []int64{3}, - received: map[int64]p2p.ID{3: "P1"}, - height: 3, - }, - }, - - */ }, }, } @@ -2280,8 +2141,10 @@ func TestScHandle(t *testing.T) { } nextEvent, err := sc.handle(step.args.event) - assert.Equal(t, newTestScheduler(*step.wantSc), sc) + wantSc := newTestScheduler(*step.wantSc) + t.Logf("step %d(%v): %s", i, step.args.event, sc) + checkSameScheduler(t, wantSc, sc) checkScResults(t, step.wantErr, err, step.wantEvent, nextEvent) diff --git a/blockchain/v2/types.go b/blockchain/v2/types.go index 836e87fd8..7a73728e4 100644 --- a/blockchain/v2/types.go +++ b/blockchain/v2/types.go @@ -4,6 +4,7 @@ import ( "github.com/Workiva/go-datastructures/queue" ) +// Event is the type that can be added to the priority queue. type Event queue.Item type priority interface { diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 000000000..7349a3516 --- /dev/null +++ b/buf.yaml @@ -0,0 +1,15 @@ +build: + roots: + - . +lint: + use: + - MINIMAL + - FILE_LOWER_SNAKE_CASE + - UNARY_RPC + except: + - PACKAGE_DIRECTORY_MATCH + ignore: + - third_party +breaking: + use: + - FILE 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/lite.go b/cmd/tendermint/commands/lite.go index b86714419..dae72266d 100644 --- a/cmd/tendermint/commands/lite.go +++ b/cmd/tendermint/commands/lite.go @@ -2,17 +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" - 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" @@ -22,21 +24,35 @@ import ( // LiteCmd represents the base command when called without any subcommands var LiteCmd = &cobra.Command{ - Use: "lite", - Short: "Run lite-client proxy server, verifying tendermint rpc", - Long: `This node will run a secure proxy to a tendermint rpc server. + 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 that -that it will present the same interface as a full tendermint node, -just with added trust and running locally.`, - RunE: runProxy, - SilenceUsage: true, +will be verified before passing them back to the caller. Other than +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 - nodeAddr string + primaryAddr string + witnessAddrsJoined string chainID string home string maxOpenConnections int @@ -44,67 +60,96 @@ var ( trustingPeriod time.Duration trustedHeight int64 trustedHash []byte + + verbose bool ) func init() { - LiteCmd.Flags().StringVar(&listenAddr, "laddr", "tcp://localhost:8888", "Serve the proxy on the given address") - LiteCmd.Flags().StringVar(&nodeAddr, "node", "tcp://localhost:26657", "Connect to a Tendermint node at this address") - LiteCmd.Flags().StringVar(&chainID, "chain-id", "tendermint", "Specify the Tendermint chain ID") + LiteCmd.Flags().StringVar(&listenAddr, "laddr", "tcp://localhost:8888", + "Serve the proxy on the given address") + LiteCmd.Flags().StringVarP(&primaryAddr, "primary", "p", "", + "Connect to a Tendermint node at this address") + 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( &maxOpenConnections, "max-open-connections", 900, "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().DurationVar(&trustingPeriod, "trusting-period", 168*time.Hour, + "Trusting period. Should be significantly less than the unbonding period") + 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") - - logger.Info("Connecting to Tendermint node...") - // First, connect a client - node, err := rpcclient.NewHTTP(nodeAddr, "/websocket") - if err != nil { - return errors.Wrap(err, "new HTTP client") + // 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) + + chainID = args[0] + logger.Info("Creating client...", "chainID", chainID) + + witnessesAddrs := strings.Split(witnessAddrsJoined, ",") - logger.Info("Creating client...") db, err := dbm.NewGoLevelDB("lite-client-db", home) if err != nil { - return err + 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), + ) } - c, err := lite.NewClient( - chainID, - lite.TrustOptions{ - Period: trustingPeriod, - Height: trustedHeight, - Hash: trustedHash, - }, - httpp.NewWithClient(chainID, node), - dbs.New(db, chainID), - ) if err != nil { return err } - c.SetLogger(liteLogger) + rpcClient, err := rpcclient.NewHTTP(primaryAddr, "/websocket") + if err != nil { + return errors.Wrapf(err, "http client for %s", primaryAddr) + } p := lproxy.Proxy{ Addr: listenAddr, Config: &rpcserver.Config{MaxOpenConnections: maxOpenConnections}, Codec: amino.NewCodec(), - Client: lrpc.NewClient(node, c), - Logger: liteLogger, + Client: lrpc.NewClient(rpcClient, c), + 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/config/config.go b/config/config.go index 38ea4ba0f..dc3eff7d1 100644 --- a/config/config.go +++ b/config/config.go @@ -415,8 +415,8 @@ func DefaultRPCConfig() *RPCConfig { // TestRPCConfig returns a configuration for testing the RPC server func TestRPCConfig() *RPCConfig { cfg := DefaultRPCConfig() - cfg.ListenAddress = "tcp://0.0.0.0:36657" - cfg.GRPCListenAddress = "tcp://0.0.0.0:36658" + cfg.ListenAddress = "tcp://127.0.0.1:36657" + cfg.GRPCListenAddress = "tcp://127.0.0.1:36658" cfg.Unsafe = true return cfg } @@ -584,7 +584,7 @@ func DefaultP2PConfig() *P2PConfig { // TestP2PConfig returns a configuration for testing the peer-to-peer layer func TestP2PConfig() *P2PConfig { cfg := DefaultP2PConfig() - cfg.ListenAddress = "tcp://0.0.0.0:36656" + cfg.ListenAddress = "tcp://127.0.0.1:36656" cfg.FlushThrottleTimeout = 10 * time.Millisecond cfg.AllowDuplicateIP = true return cfg diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index b9395ac8b..a1ddc743a 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -57,7 +57,7 @@ func TestByzantine(t *testing.T) { if i == proposerIdx { // 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 f3cc06c9f..9c5be0c08 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -71,7 +71,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, @@ -386,7 +386,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.go b/consensus/reactor.go index 40868ff9d..3711dd7cf 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -57,7 +57,7 @@ func NewReactor(consensusState *State, fastSync bool, options ...ReactorOption) metrics: NopMetrics(), } conR.updateFastSyncingMetric() - conR.BaseReactor = *p2p.NewBaseReactor("Reactor", conR) + conR.BaseReactor = *p2p.NewBaseReactor("Consensus", conR) for _, option := range options { option(conR) @@ -98,7 +98,7 @@ func (conR *Reactor) OnStop() { // SwitchToConsensus switches from fast_sync mode to consensus mode. // It resets the state, turns off fast_sync, and starts the consensus state-machine -func (conR *Reactor) SwitchToConsensus(state sm.State, blocksSynced int) { +func (conR *Reactor) SwitchToConsensus(state sm.State, blocksSynced uint64) { conR.Logger.Info("SwitchToConsensus") conR.conS.reconstructLastCommit(state) // NOTE: The line below causes broadcastNewRoundStepRoutine() to diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 5f9f14b48..e0b9149ef 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -335,7 +335,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 @@ -913,7 +913,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()}) } diff --git a/consensus/state.go b/consensus/state.go index 0729d6067..b1969efdd 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -1648,6 +1648,7 @@ 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 { @@ -1665,6 +1666,8 @@ func (cs *State) tryAddVote(vote *types.Vote, peerID p2p.ID) (bool, error) { } cs.evpool.AddEvidence(voteErr.DuplicateVoteEvidence) return added, err + } else if err == types.ErrVoteNonDeterministicSignature { + cs.Logger.Debug("Vote has non-deterministic signature", "err", err) } else { // Either // 1) bad peer OR @@ -1868,10 +1871,10 @@ func (cs *State) voteTime() time.Time { now := tmtime.Now() minVoteTime := now // TODO: We should remove next line in case we don't vote for v in case cs.ProposalBlock == nil, - // even if cs.LockedBlock != nil. See https://github.com/tendermint/spec. + // even if cs.LockedBlock != nil. See https://docs.tendermint.com/master/spec/. timeIotaMs := time.Duration(cs.state.ConsensusParams.Block.TimeIotaMs) * time.Millisecond if cs.LockedBlock != nil { - // See the BFT time spec https://tendermint.com/docs/spec/consensus/bft-time.html + // See the BFT time spec https://docs.tendermint.com/master/spec/consensus/bft-time.html minVoteTime = cs.LockedBlock.Time.Add(timeIotaMs) } else if cs.ProposalBlock != nil { minVoteTime = cs.ProposalBlock.Time.Add(timeIotaMs) 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/consensus/wal_generator.go b/consensus/wal_generator.go index 64ac95210..244edd536 100644 --- a/consensus/wal_generator.go +++ b/consensus/wal_generator.go @@ -125,9 +125,9 @@ func randPort() int { func makeAddrs() (string, string, string) { start := randPort() - return fmt.Sprintf("tcp://0.0.0.0:%d", start), - fmt.Sprintf("tcp://0.0.0.0:%d", start+1), - fmt.Sprintf("tcp://0.0.0.0:%d", start+2) + return fmt.Sprintf("tcp://127.0.0.1:%d", start), + fmt.Sprintf("tcp://127.0.0.1:%d", start+1), + fmt.Sprintf("tcp://127.0.0.1:%d", start+2) } // getConfig returns a config for test cases diff --git a/crypto/README.md b/crypto/README.md index 18a400a0f..cfbceb449 100644 --- a/crypto/README.md +++ b/crypto/README.md @@ -14,7 +14,7 @@ If you want to decode bytes into one of the types, but don't care about the spec ## Binary encoding -For Binary encoding, please refer to the [Tendermint encoding specification](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/encoding.md). +For Binary encoding, please refer to the [Tendermint encoding specification](https://docs.tendermint.com/master/spec/blockchain/encoding.html). ## JSON Encoding diff --git a/crypto/merkle/merkle.pb.go b/crypto/merkle/merkle.pb.go index 84d01e5b3..80823dd2b 100644 --- a/crypto/merkle/merkle.pb.go +++ b/crypto/merkle/merkle.pb.go @@ -146,22 +146,22 @@ func init() { func init() { proto.RegisterFile("crypto/merkle/merkle.proto", fileDescriptor_9c1c2162d560d38e) } var fileDescriptor_9c1c2162d560d38e = []byte{ - // 226 bytes of a gzipped FileDescriptorProto + // 230 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4a, 0x2e, 0xaa, 0x2c, 0x28, 0xc9, 0xd7, 0xcf, 0x4d, 0x2d, 0xca, 0xce, 0x49, 0x85, 0x52, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x12, 0x25, 0xa9, 0x79, 0x29, 0xa9, 0x45, 0xb9, 0x99, 0x79, 0x25, 0x7a, 0x10, 0x65, - 0x7a, 0x10, 0x79, 0x29, 0xdd, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0xfd, - 0xf4, 0xfc, 0xf4, 0x7c, 0x7d, 0xb0, 0x86, 0xa4, 0xd2, 0x34, 0x30, 0x0f, 0xcc, 0x01, 0xb3, 0x20, - 0x06, 0x29, 0x39, 0x73, 0xb1, 0x07, 0x14, 0xe5, 0xe7, 0xa7, 0xf9, 0x17, 0x08, 0x09, 0x71, 0xb1, - 0x94, 0x54, 0x16, 0xa4, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0x81, 0xd9, 0x42, 0x02, 0x5c, - 0xcc, 0xd9, 0xa9, 0x95, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0x3c, 0x41, 0x20, 0x26, 0x48, 0x55, 0x4a, - 0x62, 0x49, 0xa2, 0x04, 0x33, 0x58, 0x08, 0xcc, 0x56, 0x72, 0xe2, 0x62, 0x05, 0x1b, 0x22, 0x64, - 0xc9, 0xc5, 0x9c, 0x5f, 0x50, 0x2c, 0xc1, 0xa8, 0xc0, 0xac, 0xc1, 0x6d, 0xa4, 0xa8, 0x87, 0xcb, - 0x91, 0x7a, 0x50, 0x2b, 0x9d, 0x58, 0x4e, 0xdc, 0x93, 0x67, 0x08, 0x02, 0xe9, 0x71, 0x72, 0xf9, - 0xf1, 0x50, 0x8e, 0x71, 0xc5, 0x23, 0x39, 0xc6, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, - 0x7c, 0xf0, 0x48, 0x8e, 0x31, 0x4a, 0x0f, 0xc9, 0x37, 0x08, 0xd3, 0x90, 0x99, 0x28, 0x81, 0x94, - 0xc4, 0x06, 0xf6, 0x95, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xbb, 0x95, 0x22, 0x4e, 0x3c, 0x01, - 0x00, 0x00, + 0x7a, 0x10, 0x79, 0x29, 0xb5, 0x92, 0x8c, 0xcc, 0xa2, 0x94, 0xf8, 0x82, 0xc4, 0xa2, 0x92, 0x4a, + 0x7d, 0xb0, 0x62, 0xfd, 0xf4, 0xfc, 0xf4, 0x7c, 0x04, 0x0b, 0x62, 0x82, 0x92, 0x33, 0x17, 0x7b, + 0x40, 0x51, 0x7e, 0x7e, 0x9a, 0x7f, 0x81, 0x90, 0x10, 0x17, 0x4b, 0x49, 0x65, 0x41, 0xaa, 0x04, + 0xa3, 0x02, 0xa3, 0x06, 0x67, 0x10, 0x98, 0x2d, 0x24, 0xc0, 0xc5, 0x9c, 0x9d, 0x5a, 0x29, 0xc1, + 0xa4, 0xc0, 0xa8, 0xc1, 0x13, 0x04, 0x62, 0x82, 0x54, 0xa5, 0x24, 0x96, 0x24, 0x4a, 0x30, 0x83, + 0x85, 0xc0, 0x6c, 0x25, 0x27, 0x2e, 0x56, 0xb0, 0x21, 0x42, 0x96, 0x5c, 0xcc, 0xf9, 0x05, 0xc5, + 0x12, 0x8c, 0x0a, 0xcc, 0x1a, 0xdc, 0x46, 0x8a, 0x7a, 0xb8, 0x5c, 0xa7, 0x07, 0xb5, 0xd2, 0x89, + 0xe5, 0xc4, 0x3d, 0x79, 0x86, 0x20, 0x90, 0x1e, 0x27, 0x97, 0x1f, 0x0f, 0xe5, 0x18, 0x57, 0x3c, + 0x92, 0x63, 0x3c, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, 0xe4, 0x18, 0xa3, + 0xf4, 0xd2, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0x11, 0xa6, 0x21, 0x33, + 0x51, 0x42, 0x27, 0x89, 0x0d, 0xec, 0x2b, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc8, 0xcc, + 0x2c, 0x91, 0x35, 0x01, 0x00, 0x00, } func (this *ProofOp) Equal(that interface{}) bool { @@ -729,6 +729,7 @@ func (m *Proof) Unmarshal(dAtA []byte) error { func skipMerkle(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -760,10 +761,8 @@ func skipMerkle(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -784,55 +783,30 @@ func skipMerkle(dAtA []byte) (n int, err error) { return 0, ErrInvalidLengthMerkle } iNdEx += length - if iNdEx < 0 { - return 0, ErrInvalidLengthMerkle - } - return iNdEx, nil case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowMerkle - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipMerkle(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - if iNdEx < 0 { - return 0, ErrInvalidLengthMerkle - } - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupMerkle + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthMerkle + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthMerkle = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowMerkle = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthMerkle = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMerkle = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupMerkle = fmt.Errorf("proto: unexpected end of group") ) diff --git a/crypto/merkle/merkle.proto b/crypto/merkle/merkle.proto index 66854923e..159fc58c9 100644 --- a/crypto/merkle/merkle.proto +++ b/crypto/merkle/merkle.proto @@ -1,17 +1,17 @@ syntax = "proto3"; package tendermint.crypto.merkle; -option go_package = "github.com/tendermint/tendermint/crypto/merkle"; +option go_package = "github.com/tendermint/tendermint/crypto/merkle"; // For more information on gogo.proto, see: // https://github.com/gogo/protobuf/blob/master/extensions.md -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "third_party/proto/gogoproto/gogo.proto"; -option (gogoproto.marshaler_all) = true; +option (gogoproto.marshaler_all) = true; option (gogoproto.unmarshaler_all) = true; -option (gogoproto.sizer_all) = true; +option (gogoproto.sizer_all) = true; option (gogoproto.populate_all) = true; -option (gogoproto.equal_all) = true; +option (gogoproto.equal_all) = true; //---------------------------------------- // Message types @@ -21,11 +21,11 @@ option (gogoproto.equal_all) = true; // for example neighbouring node hash message ProofOp { string type = 1; - bytes key = 2; - bytes data = 3; + bytes key = 2; + bytes data = 3; } // Proof is Merkle proof defined by the list of ProofOps message Proof { - repeated ProofOp ops = 1 [(gogoproto.nullable)=false]; + repeated ProofOp ops = 1 [(gogoproto.nullable) = false]; } 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/config.js b/docs/.vuepress/config.js index dc7a200ce..55613252f 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -19,12 +19,6 @@ module.exports = { gutter: { title: "Help & Support", editLink: true, - chat: { - title: "Riot Chat", - text: "Chat with Tendermint developers on Riot Chat.", - url: "https://riot.im/app/#/room/#tendermint:matrix.org", - bg: "#222" - }, forum: { title: "Tendermint Forum", text: "Join the Tendermint forum to learn more", @@ -38,6 +32,7 @@ module.exports = { } }, footer: { + questionsText: "Chat with Cosmos developers in [Discord](https://discordapp.com/channels/669268347736686612) or reach out on the [SDK Developer Forum](https://forum.cosmos.network/c/tendermint) to learn more.", logo: "/logo-bw.svg", textLink: { text: "tendermint.com", @@ -95,10 +90,6 @@ module.exports = { { title: "Forum", url: "https://forum.cosmos.network/c/tendermint" - }, - { - title: "Chat", - url: "https://riot.im/app/#/room/#tendermint:matrix.org" } ] }, @@ -138,11 +129,6 @@ module.exports = { } ] }, - markdown: { - anchor: { - permalinkSymbol: "" - } - }, plugins: [ [ "@vuepress/google-analytics", 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-architecture.md b/docs/app-dev/app-architecture.md index 074f4ad13..d98e279dc 100644 --- a/docs/app-dev/app-architecture.md +++ b/docs/app-dev/app-architecture.md @@ -56,6 +56,6 @@ Tendermint. See the following for more extensive documentation: - [Interchain Standard for the Light-Client REST API](https://github.com/cosmos/cosmos-sdk/pull/1028) -- [Tendermint RPC Docs](https://tendermint.com/rpc/) +- [Tendermint RPC Docs](https://docs.tendermint.com/master/rpc/) - [Tendermint in Production](../tendermint-core/running-in-production.md) - [ABCI spec](https://github.com/tendermint/spec/tree/95cf253b6df623066ff7cd4074a94e7a3f147c7a/spec/abci) 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 b3a4789b0..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: @@ -106,7 +106,7 @@ You can query the transaction results by calling `/tx_search` RPC endpoint: curl "localhost:26657/tx_search?query=\"account.name='igor'\"&prove=true" ``` -Check out [API docs](https://tendermint.com/rpc/#txsearch) for more information +Check out [API docs](https://docs.tendermint.com/master/rpc/#/Info/tx_search) for more information on query syntax and other options. ## Subscribing to Transactions @@ -125,5 +125,5 @@ a query to `/subscribe` RPC endpoint. } ``` -Check out [API docs](https://tendermint.com/rpc/#subscribe) for more information +Check out [API docs](https://docs.tendermint.com/master/rpc/#subscribe) for more information on query syntax and other options. diff --git a/docs/app-dev/subscribing-to-events-via-websocket.md b/docs/app-dev/subscribing-to-events-via-websocket.md index afedc1d59..6e4f0d207 100644 --- a/docs/app-dev/subscribing-to-events-via-websocket.md +++ b/docs/app-dev/subscribing-to-events-via-websocket.md @@ -24,7 +24,7 @@ method via Websocket. } ``` -Check out [API docs](https://tendermint.com/rpc/) for +Check out [API docs](https://docs.tendermint.com/master/rpc/) for more information on query syntax and other options. You can also use tags, given you had included them into DeliverTx @@ -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 cc507ae78..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) @@ -64,3 +64,11 @@ Note the context/background should be written in the present tense. - [ADR-039-Peer-Behaviour](./adr-039-peer-behaviour.md) - [ADR-041-Proposer-Selection-via-ABCI](./adr-041-proposer-selection-via-abci.md) - [ADR-043-Blockchain-RiRi-Org](./adr-043-blockchain-riri-org.md) +- [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-015-crypto-encoding.md b/docs/architecture/adr-015-crypto-encoding.md index 665129f12..bb0a8cd80 100644 --- a/docs/architecture/adr-015-crypto-encoding.md +++ b/docs/architecture/adr-015-crypto-encoding.md @@ -21,12 +21,12 @@ representation of the pubkey. This has two significant drawbacks. Amino encoding is less space-efficient, due to requiring support for upgradability. Amino encoding support requires forking protobuf and adding this new interface support -option in the langauge of choice. +option in the language of choice. The reason for continuing to use amino however is that people can create code more easily in languages that already have an up to date amino library. It is possible that this will change in the future, if it is deemed that -requiring amino for interacting with tendermint cryptography is unneccessary. +requiring amino for interacting with Tendermint cryptography is unnecessary. The arguments for space efficiency here are refuted on the basis that there are far more egregious wastages of space in the SDK. @@ -35,9 +35,9 @@ increasing the space attached to each validator / account. The alternative to using amino here would be for us to create an enum type. Switching to just an enum type is worthy of investigation post-launch. -For referrence, part of amino encoding interfaces is basically a 4 byte enum +For reference, part of amino encoding interfaces is basically a 4 byte enum type definition. -Enum types would just change that 4 bytes to be a varuint, and it would remove +Enum types would just change that 4 bytes to be a variant, and it would remove the protobuf overhead, but it would be hard to integrate into the existing API. ### Signatures 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 8daef1817..303def716 100644 --- a/docs/architecture/adr-043-blockchain-riri-org.md +++ b/docs/architecture/adr-043-blockchain-riri-org.md @@ -1,24 +1,26 @@ # ADR 043: Blockhchain Reactor Riri-Org ## Changelog -* 18-06-2019: Initial draft -* 08-07-2019: Reviewed -* 29-11-2019: Implemented + +- 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). @@ -27,13 +29,20 @@ This ADR is meant to specify the missing components and control necessary to ach Partition the responsibilities of the blockchain reactor into a set of components which communicate exclusively with events. Events will contain timestamps allowing each component to track time as internal state. The internal state will be mutated by a set of `handle*` which will produce event(s). The integration between components will happen in the reactor and reactor tests will then become integration tests between components. This design will be known as `v2`. ![v2 Blockchain Reactor Architecture -Diagram](https://github.com/tendermint/tendermint/blob/f9e556481654a24aeb689bdadaf5eab3ccd66829/docs/architecture/img/blockchain-reactor-v2.png) +Diagram](https://github.com/tendermint/tendermint/blob/584e67ac3fac220c5c3e0652e3582eca8231e814/docs/architecture/img/blockchain-reactor-v2.png) + +### 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. +In addition the per reactor channels used by the sendRoutine to send messages over the Peer MConnection are shown. + +![v2 Blockchain Channels and Queues +Diagram](https://github.com/tendermint/tendermint/blob/5cf570690f989646fb3b615b734da503f038891f/docs/architecture/img/blockchain-v2-channels.png) ### Reactor changes in detail 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) @@ -134,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 @@ -160,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. @@ -223,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 @@ -247,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 @@ -300,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 @@ -333,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}) @@ -346,6 +358,7 @@ func handleTimeCheckEv(time) { ``` ## Peer + The Peer Stores per peer state based on messages received by the scheduler. ```go @@ -367,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 @@ -387,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-044-lite-client-with-weak-subjectivity.md b/docs/architecture/adr-044-lite-client-with-weak-subjectivity.md index 066f68f7f..2109e2952 100644 --- a/docs/architecture/adr-044-lite-client-with-weak-subjectivity.md +++ b/docs/architecture/adr-044-lite-client-with-weak-subjectivity.md @@ -84,7 +84,7 @@ The linear verification algorithm requires downloading all headers between the `TrustHeight` and the `LatestHeight`. The lite client downloads the full header for the provided `TrustHeight` and then proceeds to download `N+1` headers and applies the [Tendermint validation -rules](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/blockchain.md#validation) +rules](https://docs.tendermint.com/master/spec/blockchain/blockchain.html#validation) to each block. ### Bisecting Verification @@ -119,7 +119,7 @@ network usage. --- Check out the formal specification -[here](https://github.com/tendermint/tendermint/blob/master/docs/spec/consensus/light-client.md). +[here](https://docs.tendermint.com/master/spec/consensus/light-client.html). ## Status diff --git a/docs/architecture/adr-045-abci-evidence.md b/docs/architecture/adr-045-abci-evidence.md index 12f55986a..3cb91be75 100644 --- a/docs/architecture/adr-045-abci-evidence.md +++ b/docs/architecture/adr-045-abci-evidence.md @@ -18,7 +18,7 @@ graceful here, but that's for another day. It's possible to fool lite clients without there being a fork on the main chain - so called Fork-Lite. See the -[fork accountability](https://github.com/tendermint/tendermint/blob/master/docs/spec/consensus/fork-accountability.md) +[fork accountability](https://docs.tendermint.com/master/spec/consensus/fork-accountability.html) document for more details. For a sequential lite client, this can happen via equivocation or amnesia attacks. For a skipping lite client this can also happen via lunatic validator attacks. There must be some way for applications to punish diff --git a/docs/architecture/adr-046-light-client-implementation.md b/docs/architecture/adr-046-light-client-implementation.md new file mode 100644 index 000000000..37a7c83c5 --- /dev/null +++ b/docs/architecture/adr-046-light-client-implementation.md @@ -0,0 +1,159 @@ +# ADR 046: Lite Client Implementation + +## 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. + +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 { + // verify new headers + VerifyHeaderAtHeight(height int64, now time.Time) (*types.SignedHeader, error) + VerifyHeader(newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error + Update(now time.Time) (*types.SignedHeader, error) + + // get trusted headers & validators + TrustedHeader(height int64) (*types.SignedHeader, error) + TrustedValidatorSet(height int64) (valSet *types.ValidatorSet, heightUsed int64, err error) + LastTrustedHeight() (int64, error) + FirstTrustedHeight() (int64, error) + + // query configuration options + ChainID() string + Primary() provider.Provider + Witnesses() []provider.Provider + + Cleanup() error +} +``` + +A new light client can either be created from scratch (via `NewClient`) or +using the trusted store (via `NewClientFromTrustedStore`). When there's some +data in the trusted store and `NewClient` is called, the light client will a) +check if stored header is more recent b) optionally ask the user whenever it +should rollback (no confirmation required by default). + +```go +func NewClient( + chainID string, + trustOptions TrustOptions, + primary provider.Provider, + witnesses []provider.Provider, + trustedStore store.Store, + options ...Option) (*Client, error) { +``` + +`witnesses` as argument (as opposite to `Option`) is an intentional choice, +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. 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 `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 { + ChainID() string + + SignedHeader(height int64) (*types.SignedHeader, error) + ValidatorSet(height int64) (*types.ValidatorSet, error) +} +``` + +Provider is a full node usually, but can be another light client. The above +interface is thin and can accommodate many implementations. + +If provider (primary or witness) becomes unavailable for a prolonged period of +time, it will be removed to ensure smooth operation. + +Both `Client` and providers expose chain ID to track if there are on the same +chain. Note, when chain upgrades or intentionally forks, chain ID changes. + +The light client stores headers & validators in the trusted store: + +```go +type Store interface { + SaveSignedHeaderAndValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error + DeleteSignedHeaderAndValidatorSet(height int64) error + + SignedHeader(height int64) (*types.SignedHeader, error) + ValidatorSet(height int64) (*types.ValidatorSet, error) + + LastSignedHeaderHeight() (int64, error) + FirstSignedHeaderHeight() (int64, error) + + SignedHeaderAfter(height int64) (*types.SignedHeader, error) +} +``` + +At the moment, the only implementation is the `db` store (wrapper around the KV +database, used in Tendermint). In the future, remote adapters are possible +(e.g. `Postgresql`). + +```go +func Verify( + chainID string, + h1 *types.SignedHeader, + h1NextVals *types.ValidatorSet, + h2 *types.SignedHeader, + h2Vals *types.ValidatorSet, + trustingPeriod time.Duration, + now time.Time, + trustLevel tmmath.Fraction) error { +``` + +`Verify` pure function is exposed for a header verification. It handles both +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. + +## Consequences + +### Positive + +* single `Client` struct, which is easy to use +* flexible interfaces for header providers and trusted storage + +### Negative + +* `Verify` needs to be aligned with the current spec + +### Neutral + +* `Verify` function might be misused (called with non-adjacent headers in + incorrectly implemented sequential verification) 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-051-double-signing-risk-reduction.md b/docs/architecture/adr-051-double-signing-risk-reduction.md new file mode 100644 index 000000000..ae663e8b5 --- /dev/null +++ b/docs/architecture/adr-051-double-signing-risk-reduction.md @@ -0,0 +1,53 @@ +# ADR 051: Double Signing Risk Reduction + +## Changelog + +* 27-11-2019: Initial draft +* 13-01-2020: Separate into 2 ADR, This ADR will only cover Double signing Protection and ADR-052 handle Tendermint Mode +* 22-01-2020: change the title from "Double signing Protection" to "Double Signing Risk Reduction" + +## Context + +To provide a risk reduction method for double signing incidents mistakenly executed by validators +- Validators often mistakenly run duplicated validators to cause double-signing incident +- This proposed feature is to reduce the risk of mistaken double-signing incident by checking recent N blocks before voting begins +- When we think of such serious impact on double-signing incident, it is very reasonable to have multiple risk reduction algorithm built in node daemon + +## Decision + +We would like to suggest a double signing risk reduction method. + +- Methodology : query recent consensus results to find out whether node's consensus key is used on consensus recently or not +- When to check + - When the state machine starts `ConsensusReactor` after fully synced + - When the node is validator ( with privValidator ) + - When `cs.config.DoubleSignCheckHeight > 0` +- How to check + 1. When a validator is transformed from syncing status to fully synced status, the state machine check recent N blocks (`latest_height - double_sign_check_height`) to find out whether there exists consensus votes using the validator's consensus key + 2. If there exists votes from the validator's consensus key, exit state machine program +- Configuration + - We would like to suggest by introducing `double_sign_check_height` parameter in `config.toml` and cli, how many blocks state machine looks back to check votes + - `double_sign_check_height = {{ .Consensus.DoubleSignCheckHeight }}` in `config.toml` + - `tendermint node --double_sign_check_height` in cli + - State machine ignore checking procedure when `vote-check-height == 0` + +## Status + +Proposed + +## Consequences + +### Positive + +- Validators can avoid double signing incident by mistakes. (eg. If another validator node is voting on consensus, starting new validator node with same consensus key will cause panic stop of the state machine because consensus votes with the consensus key are found in recent blocks) +- We expect this method will prevent majority of double signing incident by mistakes. + +### Negative + +- When the risk reduction method is on, restarting a validator node will panic because the node itself voted on consensus with the same consensus key. So, validators should stop the state machine, wait for some blocks, and then restart the state machine to avoid panic stop. + +### Neutral + +## References + +- Issue [#4059](https://github.com/tendermint/tendermint/issues/4059) : double-signing protection diff --git a/docs/architecture/adr-052-tendermint-mode.md b/docs/architecture/adr-052-tendermint-mode.md new file mode 100644 index 000000000..acd5028b4 --- /dev/null +++ b/docs/architecture/adr-052-tendermint-mode.md @@ -0,0 +1,81 @@ +# ADR 052: Tendermint Mode + +## Changelog + +* 27-11-2019: Initial draft from ADR-051 +* 13-01-2020: Separate ADR Tendermint Mode from ADR-051 + +## Context + +- Fullnode mode: fullnode mode does not have the capability to become a validator. +- Validator mode : this mode is exactly same as existing state machine behavior. sync without voting on consensus, and participate consensus when fully synced +- Seed mode : lightweight seed mode maintaining an address book, p2p like [TenderSeed](https://gitlab.com/polychainlabs/tenderseed) + +## Decision + +We would like to suggest a simple Tendermint mode abstraction. These modes will live under one binary, and when initializing a node the user will be able to specify which node they would like to create. + +- Which reactor, component to include for each node + - fullnode *(default)* + - switch, transport + - reactors + - mempool + - consensus + - evidence + - blockchain + - p2p/pex + - rpc (safe connections only) + - *~~no privValidator(priv_validator_key.json, priv_validator_state.json)~~* + - validator + - switch, transport + - reactors + - mempool + - consensus + - evidence + - blockchain +  - p2p/pex + - rpc (safe connections only) + - with privValidator(priv_validator_key.json, priv_validator_state.json) + - seed + - switch, transport + - reactor + - p2p/pex +- Configuration, cli command + - We would like to suggest by introducing `mode` parameter in `config.toml` and cli + - `mode = "{{ .BaseConfig.Mode }}"` in `config.toml` + - `tendermint node --mode validator` in cli + - fullnode | validator | seed (default: "fullnode") +- RPC modification + - `host:26657/status` + - return empty `validator_info` when fullnode mode + - no rpc server in seed mode +- Where to modify in codebase + - Add switch for `config.Mode` on `node/node.go:DefaultNewNode` + - If `config.Mode==validator`, call default `NewNode` (current logic) + - If `config.Mode==fullnode`, call `NewNode` with `nil` `privValidator` (do not load or generation) + - Need to add exception routine for `nil` `privValidator` to related functions + - If `config.Mode==seed`, call `NewSeedNode` (seed version of `node/node.go:NewNode`) + - Need to add exception routine for `nil` `reactor`, `component` to related functions + +## Status + +Proposed + +## Consequences + +### Positive + +- Node operators can choose mode when they run state machine according to the purpose of the node. +- Mode can prevent mistakes because users have to specify which mode they want to run via flag. (eg. If a user want to run a validator node, she/he should explicitly write down validator as mode) +- Different mode needs different reactors, resulting in efficient resource usage. + +### Negative + +- Users need to study how each mode operate and which capability it has. + +### Neutral + +## References + +- Issue [#2237](https://github.com/tendermint/tendermint/issues/2237) : Tendermint "mode" +- [TenderSeed](https://gitlab.com/polychainlabs/tenderseed) : A lightweight Tendermint Seed Node. diff --git a/docs/architecture/adr-053-state-sync-prototype.md b/docs/architecture/adr-053-state-sync-prototype.md new file mode 100644 index 000000000..79e39b24d --- /dev/null +++ b/docs/architecture/adr-053-state-sync-prototype.md @@ -0,0 +1,318 @@ +# ADR 053: State Sync Prototype + +This ADR outlines the plan for an initial state sync prototype, and is subject to change as we gain feedback and experience. It builds on discussions and findings in [ADR-042](./adr-042-state-sync.md), see that for background information. + +## Changelog + +* 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. + +Background discussions and justifications are detailed in [ADR-042](./adr-042-state-sync.md). Its recommendations can be summarized as: + +* The application periodically takes full state snapshots (i.e. eager snapshots). + +* The application splits snapshots into smaller chunks that can be individually verified against a chain app hash. + +* Tendermint uses the light client to obtain a trusted chain app hash for verification. + +* Tendermint discovers and downloads snapshot chunks in parallel from multiple peers, and passes them to the application via ABCI to be applied and verified against the chain app hash. + +* Historical blocks are not backfilled, so state synced nodes will have a truncated block history. + +## Tendermint Proposal + +This describes the snapshot/restore process seen from Tendermint. The interface is kept as small and general as possible to give applications maximum flexibility. + +### Snapshot Data Structure + +A node can have multiple snapshots taken at various heights. Snapshots can be taken in different application-specified formats (e.g. MessagePack as format `1` and Protobuf as format `2`, or similarly for schema versioning). Each snapshot consists of multiple chunks containing the actual state data, allowing parallel downloads and reduced memory usage. + +```proto +message Snapshot { + uint64 height = 1; // The height at which the snapshot was taken + uint32 format = 2; // The application-specific snapshot format + uint32 chunks = 3; // The number of chunks in the snapshot + bytes metadata = 4; // Arbitrary application metadata +} + +message SnapshotChunk { + uint64 height = 1; // The height of the corresponding snapshot + uint32 format = 2; // The application-specific snapshot format + uint32 chunk = 3; // The chunk index (one-based) + bytes data = 4; // Serialized application state in an arbitrary format + bytes checksum = 5; // SHA-1 checksum of data +} +``` + +Chunk verification data must be encoded along with the state data in the `data` field. + +Chunk `data` cannot be larger than 64 MB, and snapshot `metadata` cannot be larger than 64 KB. + +### ABCI Interface + +```proto +// Lists available snapshots +message RequestListSnapshots {} + +message ResponseListSnapshots { + repeated Snapshot snapshots = 1; +} + +// Offers a snapshot to the application +message RequestOfferSnapshot { + Snapshot snapshot = 1; + bytes app_hash = 2; +} + +message ResponseOfferSnapshot { + bool accepted = 1; + Reason reason = 2; // Reason why snapshot was rejected + enum Reason { + unknown = 0; // Unknown or generic reason + invalid_height = 1; // Height is rejected: avoid this height + invalid_format = 2; // Format is rejected: avoid this format + } +} + +// Fetches a snapshot chunk +message RequestGetSnapshotChunk { + uint64 height = 1; + uint32 format = 2; + uint32 chunk = 3; +} + +message ResponseGetSnapshotChunk { + SnapshotChunk chunk = 1; +} + +// Applies a snapshot chunk +message RequestApplySnapshotChunk { + SnapshotChunk chunk = 1; +} + +message ResponseApplySnapshotChunk { + bool applied = 1; + Reason reason = 2; // Reason why chunk failed + enum Reason { + unknown = 0; // Unknown or generic reason + verify_failed = 1; // Chunk verification failed + } +} +``` + +### Taking Snapshots + +Tendermint is not aware of the snapshotting process at all, it is entirely an application concern. The following guarantees must be provided: + +* **Periodic:** snapshots must be taken periodically, not on-demand, for faster restores, lower load, and less DoS risk. + +* **Deterministic:** snapshots must be deterministic, and identical across all nodes - typically by taking a snapshot at given height intervals. + +* **Consistent:** snapshots must be consistent, i.e. not affected by concurrent writes - typically by using a data store that supports versioning and/or snapshot isolation. + +* **Asynchronous:** snapshots must be asynchronous, i.e. not halt block processing and state transitions. + +* **Chunked:** snapshots must be split into chunks of reasonable size (on the order of megabytes), and each chunk must be verifiable against the chain app hash. + +* **Garbage collected:** snapshots must be garbage collected periodically. + +### Restoring Snapshots + +Nodes should have options for enabling state sync and/or fast sync, and be provided a trusted header hash for the light client. + +When starting an empty node with state sync and fast sync enabled, snapshots are restored as follows: + +1. The node checks that it is empty, i.e. that it has no state nor blocks. + +2. The node contacts the given seeds to discover peers. + +3. The node contacts a set of full nodes, and verifies the trusted block header using the given hash via the light client. + +4. The node requests available snapshots via `RequestListSnapshots`. Snapshots with `metadata` greater than 64 KB are rejected. + +5. The node iterates over all snapshots in reverse order by height and format until it finds one that satisfies all of the following conditions: + + * The snapshot height's block is considered trustworthy by the light client (i.e. snapshot height is greater than trusted header and within unbonding period of the latest trustworthy block). + + * The snapshot's height or format hasn't been explicitly rejected by an earlier `RequestOffsetSnapshot` call (via `invalid_height` or `invalid_format`). + + * The application accepts the `RequestOfferSnapshot` call. + +6. The node downloads chunks in parallel from multiple peers via `RequestGetSnapshotChunk`, and both the sender and receiver verifies their checksums. Chunks with `data` greater than 64 MB are rejected. + +7. The node passes chunks sequentially to the app via `RequestApplySnapshotChunk`, along with the chain's app hash at the snapshot height for verification. If the chunk is rejected the node should retry it. If it was rejected with `verify_failed`, it should be refetched from a different source. If an internal error occurred, `ResponseException` should be returned and state sync should be aborted. + +8. Once all chunks have been applied, the node compares the app hash to the chain app hash, and if they do not match it either errors or discards the state and starts over. + +9. The node switches to fast sync to catch up blocks that were committed while restoring the snapshot. + +10. The node switches to normal consensus mode. + +## Gaia Proposal + +This describes the snapshot process seen from Gaia, using format version `1`. The serialization format is unspecified, but likely to be compressed Amino or Protobuf. + +### Snapshot Metadata + +In the initial version there is no snapshot metadata, so it is set to an empty byte buffer. + +Once all chunks have been successfully built, snapshot metadata should be serialized and stored in the file system as e.g. `snapshots///metadata`, and served via `RequestListSnapshots`. + +### Snapshot Chunk Format + +The Gaia data structure consists of a set of named IAVL trees. A root hash is constructed by taking the root hashes of each of the IAVL trees, then constructing a Merkle tree of the sorted name/hash map. + +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 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. + +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. + +For a production version, it should be sufficient to store key/value/version for all nodes (leaf and inner) in insertion order, chunked in some appropriate way. If per-chunk verification is required, the chunk must also contain enough information to reconstruct the Merkle proofs all the way up to the root of the multistore, e.g. by storing a complete subtree's key/value/version data plus Merkle hashes of all other branches up to the multistore root. The exact approach will depend on tradeoffs between size, time, and verification. IAVL RangeProofs are not recommended, since these include redundant data such as proofs for intermediate and leaf nodes that can be derived from the above data. + +Chunks should be built greedily by collecting node data up to some size limit (e.g. 32 MB) and serializing it. Chunk data is stored in the file system as `snapshots////data`, along with a SHA-1 checksum in `snapshots////checksum`, and served via `RequestGetSnapshotChunk`. + +### Snapshot Scheduling + +Snapshots should be taken at some configurable height interval, e.g. every 1000 blocks. All nodes should preferably have the same snapshot schedule, such that all nodes can serve chunks for a given snapshot. + +Taking consistent snapshots of IAVL trees is greatly simplified by them being versioned: simply snapshot the version that corresponds to the snapshot height, while concurrent writes create new versions. IAVL pruning must not prune a version that is being snapshotted. + +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 + +* Should we have a simpler scheme for discovering snapshots? E.g. announce supported formats, and have peer supply latest available snapshot. + + Downsides: app has to announce supported formats, having a single snapshot per peer may make fewer peers available for chosen snapshot. + +## Resolved Questions + +* Is it OK for state-synced nodes to not have historical blocks nor historical IAVL versions? + + > 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 + +Accepted + +## References + +* [ADR-042](./adr-042-state-sync.md) and its 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/architecture/img/blockchain-reactor-v2.png b/docs/architecture/img/blockchain-reactor-v2.png index 086bf71bd..5f15333a0 100644 Binary files a/docs/architecture/img/blockchain-reactor-v2.png and b/docs/architecture/img/blockchain-reactor-v2.png differ diff --git a/docs/architecture/img/blockchain-v2-channels.png b/docs/architecture/img/blockchain-v2-channels.png new file mode 100644 index 000000000..69886da95 Binary files /dev/null and b/docs/architecture/img/blockchain-v2-channels.png differ diff --git a/docs/guides/go-built-in.md b/docs/guides/go-built-in.md index 56f910331..5ab71b829 100644 --- a/docs/guides/go-built-in.md +++ b/docs/guides/go-built-in.md @@ -1,6 +1,6 @@ ---- + # Creating a built-in application in Go @@ -55,8 +55,8 @@ $ echo $GOPATH We'll start by creating a new Go project. ```sh -$ mkdir -p $GOPATH/src/github.com/me/kvstore -$ cd $GOPATH/src/github.com/me/kvstore +$ mkdir kvstore +$ cd kvstore ``` Inside the example directory create a `main.go` file with the following content: @@ -569,7 +569,6 @@ We are going to use [Go modules](https://github.com/golang/go/wiki/Modules) for dependency management. ```sh -$ export GO111MODULE=on $ go mod init github.com/me/example $ go build ``` @@ -580,8 +579,7 @@ To create a default configuration, nodeKey and private validator files, let's execute `tendermint init`. But before we do that, we will need to install Tendermint Core. Please refer to [the official guide](https://docs.tendermint.com/master/introduction/install.html). If you're -installing from source, don't forget to checkout the latest release (`git -checkout vX.Y.Z`). +installing from source, don't forget to checkout the latest release (`git checkout vX.Y.Z`). ```sh $ rm -rf /tmp/example diff --git a/docs/guides/go.md b/docs/guides/go.md index 6559a0005..f688d0e4e 100644 --- a/docs/guides/go.md +++ b/docs/guides/go.md @@ -1,6 +1,6 @@ ---- + # Creating an application in Go @@ -58,8 +58,8 @@ $ echo $GOPATH We'll start by creating a new Go project. ```sh -$ mkdir -p $GOPATH/src/github.com/me/kvstore -$ cd $GOPATH/src/github.com/me/kvstore +$ mkdir kvstore +$ cd kvstore ``` Inside the example directory create a `main.go` file with the following content: @@ -228,9 +228,9 @@ func NewKVStoreApplication(db *badger.DB) *KVStoreApplication { ### 1.3.2 BeginBlock -> DeliverTx -> EndBlock -> Commit -When Tendermint Core has decided on the block, it's transfered to the +When Tendermint Core has decided on the block, it's transferred to the application in 3 parts: `BeginBlock`, one `DeliverTx` per transaction and -`EndBlock` in the end. DeliverTx are being transfered asynchronously, but the +`EndBlock` in the end. DeliverTx are being transferred asynchronously, but the responses are expected to come in order. ``` @@ -437,8 +437,7 @@ To create a default configuration, nodeKey and private validator files, let's execute `tendermint init`. But before we do that, we will need to install Tendermint Core. Please refer to [the official guide](https://docs.tendermint.com/master/introduction/install.html). If you're -installing from source, don't forget to checkout the latest release (`git -checkout vX.Y.Z`). +installing from source, don't forget to checkout the latest release (`git checkout vX.Y.Z`). ```sh $ rm -rf /tmp/example diff --git a/docs/guides/java.md b/docs/guides/java.md index 4109f042f..12bbc4565 100644 --- a/docs/guides/java.md +++ b/docs/guides/java.md @@ -1,6 +1,6 @@ ---- + # Creating an application in Java @@ -170,15 +170,15 @@ Copy the necessary `.proto` files to your project: mkdir -p \ $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/abci/types \ $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/crypto/merkle \ - $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/libs/common \ + $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/libs/kv \ $KVSTORE_HOME/src/main/proto/github.com/gogo/protobuf/gogoproto cp $GOPATH/src/github.com/tendermint/tendermint/abci/types/types.proto \ $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/abci/types/types.proto cp $GOPATH/src/github.com/tendermint/tendermint/crypto/merkle/merkle.proto \ $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/crypto/merkle/merkle.proto -cp $GOPATH/src/github.com/tendermint/tendermint/libs/common/types.proto \ - $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/libs/common/types.proto +cp $GOPATH/src/github.com/tendermint/tendermint/libs/kv/types.proto \ + $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/libs/kv/types.proto cp $GOPATH/src/github.com/gogo/protobuf/gogoproto/gogo.proto \ $KVSTORE_HOME/src/main/proto/github.com/gogo/protobuf/gogoproto/gogo.proto ``` diff --git a/docs/guides/kotlin.md b/docs/guides/kotlin.md index 1829e1074..0c15098a4 100644 --- a/docs/guides/kotlin.md +++ b/docs/guides/kotlin.md @@ -1,6 +1,7 @@ ---- + + # Creating an application in Kotlin ## Guide Assumptions @@ -169,15 +170,15 @@ Copy the necessary `.proto` files to your project: mkdir -p \ $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/abci/types \ $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/crypto/merkle \ - $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/libs/common \ + $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/libs/kv \ $KVSTORE_HOME/src/main/proto/github.com/gogo/protobuf/gogoproto cp $GOPATH/src/github.com/tendermint/tendermint/abci/types/types.proto \ $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/abci/types/types.proto cp $GOPATH/src/github.com/tendermint/tendermint/crypto/merkle/merkle.proto \ $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/crypto/merkle/merkle.proto -cp $GOPATH/src/github.com/tendermint/tendermint/libs/common/types.proto \ - $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/libs/common/types.proto +cp $GOPATH/src/github.com/tendermint/tendermint/libs/kv/types.proto \ + $KVSTORE_HOME/src/main/proto/github.com/tendermint/tendermint/libs/kv/types.proto cp $GOPATH/src/github.com/gogo/protobuf/gogoproto/gogo.proto \ $KVSTORE_HOME/src/main/proto/github.com/gogo/protobuf/gogoproto/gogo.proto ``` diff --git a/docs/interviews/tendermint-bft.md b/docs/interviews/tendermint-bft.md index 8b3ad5743..6d3a940cc 100644 --- a/docs/interviews/tendermint-bft.md +++ b/docs/interviews/tendermint-bft.md @@ -247,4 +247,4 @@ keep the list of new nodes it discovers, and when you need to establish connection to a peer, you'll look to address book and get some addresses from there. There's categorization/ranking of nodes there. -[1]: https://github.com/tendermint/tendermint/blob/master/docs/spec/reactors/consensus/proposer-selection.md +[1]: https://docs.tendermint.com/master/spec/reactors/consensus/proposer-selection.html diff --git a/docs/networks/terraform-and-ansible.md b/docs/networks/terraform-and-ansible.md index b55563ea9..ec6cee1ba 100644 --- a/docs/networks/terraform-and-ansible.md +++ b/docs/networks/terraform-and-ansible.md @@ -4,6 +4,8 @@ order: 3 # Terraform & Ansible +> Note: These commands/files are not being maintained by the tendermint team currently. Please use them carefully. + Automated deployments are done using [Terraform](https://www.terraform.io/) to create servers on Digital Ocean then [Ansible](http://www.ansible.com/) to create and manage diff --git a/docs/package-lock.json b/docs/package-lock.json index e96344500..a5e8c0880 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -5,27 +5,28 @@ "requires": true, "dependencies": { "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", + "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", "requires": { - "@babel/highlight": "^7.0.0" + "@babel/highlight": "^7.8.3" } }, "@babel/core": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.2.tgz", - "integrity": "sha512-eeD7VEZKfhK1KUXGiyPFettgF3m513f8FoBSWiQ1xTvl1RAopLs42Wp9+Ze911I6H0N9lNqJMDgoZT7gHsipeQ==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.2", - "@babel/helpers": "^7.7.0", - "@babel/parser": "^7.7.2", - "@babel/template": "^7.7.0", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.7.2", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.4.tgz", + "integrity": "sha512-0LiLrB2PwrVI+a2/IEskBopDYSd8BCb3rOvH7D5tzoWd696TBEduBvuLVm4Nx6rltrLZqvI3MCalB2K2aVzQjA==", + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.4", + "@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", "convert-source-map": "^1.7.0", "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", "json5": "^2.1.0", "lodash": "^4.17.13", "resolve": "^1.3.2", @@ -54,6 +55,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -62,11 +68,11 @@ } }, "@babel/generator": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.2.tgz", - "integrity": "sha512-WthSArvAjYLz4TcbKOi88me+KmDJdKSlfwwN8CnUYn9jBkzhq0ZEPuBfkAWIvjJ3AdEV1Cf/+eSQTnp3IDJKlQ==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.4.tgz", + "integrity": "sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==", "requires": { - "@babel/types": "^7.7.2", + "@babel/types": "^7.8.3", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" @@ -80,214 +86,214 @@ } }, "@babel/helper-annotate-as-pure": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.7.0.tgz", - "integrity": "sha512-k50CQxMlYTYo+GGyUGFwpxKVtxVJi9yh61sXZji3zYHccK9RYliZGSTOgci85T+r+0VFN2nWbGM04PIqwfrpMg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz", + "integrity": "sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==", "requires": { - "@babel/types": "^7.7.0" + "@babel/types": "^7.8.3" } }, "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.7.0.tgz", - "integrity": "sha512-Cd8r8zs4RKDwMG/92lpZcnn5WPQ3LAMQbCw42oqUh4s7vsSN5ANUZjMel0OOnxDLq57hoDDbai+ryygYfCTOsw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz", + "integrity": "sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw==", "requires": { - "@babel/helper-explode-assignable-expression": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/helper-explode-assignable-expression": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/helper-call-delegate": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.7.0.tgz", - "integrity": "sha512-Su0Mdq7uSSWGZayGMMQ+z6lnL00mMCnGAbO/R0ZO9odIdB/WNU/VfQKqMQU0fdIsxQYbRjDM4BixIa93SQIpvw==", + "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==", "requires": { - "@babel/helper-hoist-variables": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/helper-hoist-variables": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/helper-create-class-features-plugin": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.7.0.tgz", - "integrity": "sha512-MZiB5qvTWoyiFOgootmRSDV1udjIqJW/8lmxgzKq6oDqxdmHUjeP2ZUOmgHdYjmUVNABqRrHjYAYRvj8Eox/UA==", + "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==", "requires": { - "@babel/helper-function-name": "^7.7.0", - "@babel/helper-member-expression-to-functions": "^7.7.0", - "@babel/helper-optimise-call-expression": "^7.7.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.7.0", - "@babel/helper-split-export-declaration": "^7.7.0" + "@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-split-export-declaration": "^7.8.3" } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.7.2.tgz", - "integrity": "sha512-pAil/ZixjTlrzNpjx+l/C/wJk002Wo7XbbZ8oujH/AoJ3Juv0iN/UTcPUHXKMFLqsfS0Hy6Aow8M31brUYBlQQ==", + "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==", "requires": { - "@babel/helper-regex": "^7.4.4", + "@babel/helper-regex": "^7.8.3", "regexpu-core": "^4.6.0" } }, "@babel/helper-define-map": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.7.0.tgz", - "integrity": "sha512-kPKWPb0dMpZi+ov1hJiwse9dWweZsz3V9rP4KdytnX1E7z3cTNmFGglwklzFPuqIcHLIY3bgKSs4vkwXXdflQA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz", + "integrity": "sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g==", "requires": { - "@babel/helper-function-name": "^7.7.0", - "@babel/types": "^7.7.0", + "@babel/helper-function-name": "^7.8.3", + "@babel/types": "^7.8.3", "lodash": "^4.17.13" } }, "@babel/helper-explode-assignable-expression": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.7.0.tgz", - "integrity": "sha512-CDs26w2shdD1urNUAji2RJXyBFCaR+iBEGnFz3l7maizMkQe3saVw9WtjG1tz8CwbjvlFnaSLVhgnu1SWaherg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz", + "integrity": "sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw==", "requires": { - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/helper-function-name": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.0.tgz", - "integrity": "sha512-tDsJgMUAP00Ugv8O2aGEua5I2apkaQO7lBGUq1ocwN3G23JE5Dcq0uh3GvFTChPa4b40AWiAsLvCZOA2rdnQ7Q==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", + "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", "requires": { - "@babel/helper-get-function-arity": "^7.7.0", - "@babel/template": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/helper-get-function-arity": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.0.tgz", - "integrity": "sha512-tLdojOTz4vWcEnHWHCuPN5P85JLZWbm5Fx5ZsMEMPhF3Uoe3O7awrbM2nQ04bDOUToH/2tH/ezKEOR8zEYzqyw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", "requires": { - "@babel/types": "^7.7.0" + "@babel/types": "^7.8.3" } }, "@babel/helper-hoist-variables": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.7.0.tgz", - "integrity": "sha512-LUe/92NqsDAkJjjCEWkNe+/PcpnisvnqdlRe19FahVapa4jndeuJ+FBiTX1rcAKWKcJGE+C3Q3tuEuxkSmCEiQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz", + "integrity": "sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg==", "requires": { - "@babel/types": "^7.7.0" + "@babel/types": "^7.8.3" } }, "@babel/helper-member-expression-to-functions": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.7.0.tgz", - "integrity": "sha512-QaCZLO2RtBcmvO/ekOLp8p7R5X2JriKRizeDpm5ChATAFWrrYDcDxPuCIBXKyBjY+i1vYSdcUTMIb8psfxHDPA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", + "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", "requires": { - "@babel/types": "^7.7.0" + "@babel/types": "^7.8.3" } }, "@babel/helper-module-imports": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.7.0.tgz", - "integrity": "sha512-Dv3hLKIC1jyfTkClvyEkYP2OlkzNvWs5+Q8WgPbxM5LMeorons7iPP91JM+DU7tRbhqA1ZeooPaMFvQrn23RHw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", + "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", "requires": { - "@babel/types": "^7.7.0" + "@babel/types": "^7.8.3" } }, "@babel/helper-module-transforms": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.7.0.tgz", - "integrity": "sha512-rXEefBuheUYQyX4WjV19tuknrJFwyKw0HgzRwbkyTbB+Dshlq7eqkWbyjzToLrMZk/5wKVKdWFluiAsVkHXvuQ==", - "requires": { - "@babel/helper-module-imports": "^7.7.0", - "@babel/helper-simple-access": "^7.7.0", - "@babel/helper-split-export-declaration": "^7.7.0", - "@babel/template": "^7.7.0", - "@babel/types": "^7.7.0", + "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==", + "requires": { + "@babel/helper-module-imports": "^7.8.3", + "@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", "lodash": "^4.17.13" } }, "@babel/helper-optimise-call-expression": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.7.0.tgz", - "integrity": "sha512-48TeqmbazjNU/65niiiJIJRc5JozB8acui1OS7bSd6PgxfuovWsvjfWSzlgx+gPFdVveNzUdpdIg5l56Pl5jqg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", + "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", "requires": { - "@babel/types": "^7.7.0" + "@babel/types": "^7.8.3" } }, "@babel/helper-plugin-utils": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", - "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==" + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", + "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==" }, "@babel/helper-regex": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.5.5.tgz", - "integrity": "sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.8.3.tgz", + "integrity": "sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ==", "requires": { "lodash": "^4.17.13" } }, "@babel/helper-remap-async-to-generator": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.7.0.tgz", - "integrity": "sha512-pHx7RN8X0UNHPB/fnuDnRXVZ316ZigkO8y8D835JlZ2SSdFKb6yH9MIYRU4fy/KPe5sPHDFOPvf8QLdbAGGiyw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz", + "integrity": "sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA==", "requires": { - "@babel/helper-annotate-as-pure": "^7.7.0", - "@babel/helper-wrap-function": "^7.7.0", - "@babel/template": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-wrap-function": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/helper-replace-supers": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.7.0.tgz", - "integrity": "sha512-5ALYEul5V8xNdxEeWvRsBzLMxQksT7MaStpxjJf9KsnLxpAKBtfw5NeMKZJSYDa0lKdOcy0g+JT/f5mPSulUgg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.3.tgz", + "integrity": "sha512-xOUssL6ho41U81etpLoT2RTdvdus4VfHamCuAm4AHxGr+0it5fnwoVdwUJ7GFEqCsQYzJUhcbsN9wB9apcYKFA==", "requires": { - "@babel/helper-member-expression-to-functions": "^7.7.0", - "@babel/helper-optimise-call-expression": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0" + "@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/helper-simple-access": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.7.0.tgz", - "integrity": "sha512-AJ7IZD7Eem3zZRuj5JtzFAptBw7pMlS3y8Qv09vaBWoFsle0d1kAn5Wq6Q9MyBXITPOKnxwkZKoAm4bopmv26g==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", + "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", "requires": { - "@babel/template": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/template": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/helper-split-export-declaration": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.0.tgz", - "integrity": "sha512-HgYSI8rH08neWlAH3CcdkFg9qX9YsZysZI5GD8LjhQib/mM0jGOZOVkoUiiV2Hu978fRtjtsGsW6w0pKHUWtqA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", "requires": { - "@babel/types": "^7.7.0" + "@babel/types": "^7.8.3" } }, "@babel/helper-wrap-function": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.7.0.tgz", - "integrity": "sha512-sd4QjeMgQqzshSjecZjOp8uKfUtnpmCyQhKQrVJBBgeHAB/0FPi33h3AbVlVp07qQtMD4QgYSzaMI7VwncNK/w==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz", + "integrity": "sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ==", "requires": { - "@babel/helper-function-name": "^7.7.0", - "@babel/template": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/helper-function-name": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/helpers": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.7.0.tgz", - "integrity": "sha512-VnNwL4YOhbejHb7x/b5F39Zdg5vIQpUUNzJwx0ww1EcVRt41bbGRZWhAURrfY32T5zTT3qwNOQFWpn+P0i0a2g==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", + "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", "requires": { - "@babel/template": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/template": "^7.8.3", + "@babel/traverse": "^7.8.4", + "@babel/types": "^7.8.3" } }, "@babel/highlight": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", - "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", + "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", "requires": { "chalk": "^2.0.0", "esutils": "^2.0.2", @@ -295,391 +301,399 @@ } }, "@babel/parser": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.3.tgz", - "integrity": "sha512-bqv+iCo9i+uLVbI0ILzKkvMorqxouI+GbV13ivcARXn9NNEabi2IEz912IgNpT/60BNXac5dgcfjb94NjsF33A==" + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz", + "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==" }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.7.0.tgz", - "integrity": "sha512-ot/EZVvf3mXtZq0Pd0+tSOfGWMizqmOohXmNZg6LNFjHOV+wOPv7BvVYh8oPR8LhpIP3ye8nNooKL50YRWxpYA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz", + "integrity": "sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.7.0", - "@babel/plugin-syntax-async-generators": "^7.2.0" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-remap-async-to-generator": "^7.8.3", + "@babel/plugin-syntax-async-generators": "^7.8.0" } }, "@babel/plugin-proposal-class-properties": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.7.0.tgz", - "integrity": "sha512-tufDcFA1Vj+eWvwHN+jvMN6QsV5o+vUlytNKrbMiCeDL0F2j92RURzUsUMWE5EJkLyWxjdUslCsMQa9FWth16A==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.8.3.tgz", + "integrity": "sha512-EqFhbo7IosdgPgZggHaNObkmO1kNUe3slaKu54d5OWvy+p9QIKOzK1GAEpAIsZtWVtPXUHSMcT4smvDrCfY4AA==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.7.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-create-class-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-proposal-decorators": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.7.0.tgz", - "integrity": "sha512-dMCDKmbYFQQTn1+VJjl5hbqlweuHl5oDeMU9B1Q7oAWi0mHxjQQDHdJIK6iW76NE1KJT3zA6dDU3weR1WT5D4A==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.8.3.tgz", + "integrity": "sha512-e3RvdvS4qPJVTe288DlXjwKflpfy1hr0j5dz5WpIYYeP7vQZg2WfAEIp8k5/Lwis/m5REXEteIz6rrcDtXXG7w==", "requires": { - "@babel/helper-create-class-features-plugin": "^7.7.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-decorators": "^7.2.0" + "@babel/helper-create-class-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-decorators": "^7.8.3" } }, "@babel/plugin-proposal-json-strings": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz", - "integrity": "sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz", + "integrity": "sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-json-strings": "^7.2.0" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.0" } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.6.2.tgz", - "integrity": "sha512-LDBXlmADCsMZV1Y9OQwMc0MyGZ8Ta/zlD9N67BfQT8uYwkRswiu2hU6nJKrjrt/58aH/vqfQlR/9yId/7A2gWw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-8qvuPwU/xxUCt78HocNlv0mXXo0wdh9VT1R04WU8HGOfaOob26pF+9P5/lYjN/q7DHOX1bvX60hnhOvuQUJdbA==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.2.0" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0" } }, "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz", - "integrity": "sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.2.0" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" } }, "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.7.0.tgz", - "integrity": "sha512-mk34H+hp7kRBWJOOAR0ZMGCydgKMD4iN9TpDRp3IIcbunltxEY89XSimc6WbtSLCDrwcdy/EEw7h5CFCzxTchw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.3.tgz", + "integrity": "sha512-1/1/rEZv2XGweRwwSkLpY+s60za9OZ1hJs4YDqFHCw0kYWYwL5IFljVY1MYBL+weT1l9pokDO2uhSTLVxzoHkQ==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-create-regexp-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-syntax-async-generators": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz", - "integrity": "sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.0" } }, "@babel/plugin-syntax-decorators": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.2.0.tgz", - "integrity": "sha512-38QdqVoXdHUQfTpZo3rQwqQdWtCn5tMv4uV6r2RMfTqNBuv4ZBhz79SfaQWKTVmxHjeFv/DnXVC/+agHCklYWA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.8.3.tgz", + "integrity": "sha512-8Hg4dNNT9/LcA1zQlfwuKR8BUc/if7Q7NkTam9sGTcJphLwpf2g4S42uhspQrIrR+dpzE0dtTqBVFoHl8GtnnQ==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-syntax-dynamic-import": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz", - "integrity": "sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.0" } }, "@babel/plugin-syntax-json-strings": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz", - "integrity": "sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.0" } }, "@babel/plugin-syntax-jsx": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.2.0.tgz", - "integrity": "sha512-VyN4QANJkRW6lDBmENzRszvZf3/4AXaj9YR7GwrWeeN9tEBPuXbmDYVU9bYBN0D70zCWVwUy0HWq2553VCb6Hw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.8.3.tgz", + "integrity": "sha512-WxdW9xyLgBdefoo0Ynn3MRSkhe5tFVxxKNVdnZSh318WrG2e2jH+E9wd/++JsqcLJZPfz87njQJ8j2Upjm0M0A==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-syntax-object-rest-spread": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", - "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.0" } }, "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz", - "integrity": "sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.0" } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz", - "integrity": "sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz", + "integrity": "sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.7.0.tgz", - "integrity": "sha512-vLI2EFLVvRBL3d8roAMqtVY0Bm9C1QzLkdS57hiKrjUBSqsQYrBsMCeOg/0KK7B0eK9V71J5mWcha9yyoI2tZw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz", + "integrity": "sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ==", "requires": { - "@babel/helper-module-imports": "^7.7.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.7.0" + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-remap-async-to-generator": "^7.8.3" } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz", - "integrity": "sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz", + "integrity": "sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.6.3.tgz", - "integrity": "sha512-7hvrg75dubcO3ZI2rjYTzUrEuh1E9IyDEhhB6qfcooxhDA33xx2MasuLVgdxzcP6R/lipAC6n9ub9maNW6RKdw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz", + "integrity": "sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-plugin-utils": "^7.8.3", "lodash": "^4.17.13" } }, "@babel/plugin-transform-classes": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.7.0.tgz", - "integrity": "sha512-/b3cKIZwGeUesZheU9jNYcwrEA7f/Bo4IdPmvp7oHgvks2majB5BoT5byAql44fiNQYOPzhk2w8DbgfuafkMoA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.7.0", - "@babel/helper-define-map": "^7.7.0", - "@babel/helper-function-name": "^7.7.0", - "@babel/helper-optimise-call-expression": "^7.7.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.7.0", - "@babel/helper-split-export-declaration": "^7.7.0", + "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==", + "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-split-export-declaration": "^7.8.3", "globals": "^11.1.0" } }, "@babel/plugin-transform-computed-properties": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz", - "integrity": "sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz", + "integrity": "sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-destructuring": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.6.0.tgz", - "integrity": "sha512-2bGIS5P1v4+sWTCnKNDZDxbGvEqi0ijeqM/YqHtVGrvG2y0ySgnEEhXErvE9dA0bnIzY9bIzdFK0jFA46ASIIQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.3.tgz", + "integrity": "sha512-H4X646nCkiEcHZUZaRkhE2XVsoz0J/1x3VVujnn96pSoGCtKPA99ZZA+va+gK+92Zycd6OBKCD8tDb/731bhgQ==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.7.0.tgz", - "integrity": "sha512-3QQlF7hSBnSuM1hQ0pS3pmAbWLax/uGNCbPBND9y+oJ4Y776jsyujG2k0Sn2Aj2a0QwVOiOFL5QVPA7spjvzSA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz", + "integrity": "sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-create-regexp-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.5.0.tgz", - "integrity": "sha512-igcziksHizyQPlX9gfSjHkE2wmoCH3evvD2qR5w29/Dk0SMKE/eOI7f1HhBdNhR/zxJDqrgpoDTq5YSLH/XMsQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz", + "integrity": "sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz", - "integrity": "sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz", + "integrity": "sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ==", "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-for-of": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.4.4.tgz", - "integrity": "sha512-9T/5Dlr14Z9TIEXLXkt8T1DU7F24cbhwhMNUziN3hB1AXoZcdzPcTiKGRn/6iOymDqtTKWnr/BtRKN9JwbKtdQ==", + "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==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-function-name": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.7.0.tgz", - "integrity": "sha512-P5HKu0d9+CzZxP5jcrWdpe7ZlFDe24bmqP6a6X8BHEBl/eizAsY8K6LX8LASZL0Jxdjm5eEfzp+FIrxCm/p8bA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz", + "integrity": "sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ==", "requires": { - "@babel/helper-function-name": "^7.7.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-function-name": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-literals": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz", - "integrity": "sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz", + "integrity": "sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-modules-amd": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.5.0.tgz", - "integrity": "sha512-n20UsQMKnWrltocZZm24cRURxQnWIvsABPJlw/fvoy9c6AgHZzoelAIzajDHAQrDpuKFFPPcFGd7ChsYuIUMpg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.8.3.tgz", + "integrity": "sha512-MadJiU3rLKclzT5kBH4yxdry96odTUwuqrZM+GllFI/VhxfPz+k9MshJM+MwhfkCdxxclSbSBbUGciBngR+kEQ==", "requires": { - "@babel/helper-module-transforms": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-module-transforms": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", "babel-plugin-dynamic-import-node": "^2.3.0" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.7.0.tgz", - "integrity": "sha512-KEMyWNNWnjOom8vR/1+d+Ocz/mILZG/eyHHO06OuBQ2aNhxT62fr4y6fGOplRx+CxCSp3IFwesL8WdINfY/3kg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.8.3.tgz", + "integrity": "sha512-JpdMEfA15HZ/1gNuB9XEDlZM1h/gF/YOH7zaZzQu2xCFRfwc01NXBMHHSTT6hRjlXJJs5x/bfODM3LiCk94Sxg==", "requires": { - "@babel/helper-module-transforms": "^7.7.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-simple-access": "^7.7.0", + "@babel/helper-module-transforms": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-simple-access": "^7.8.3", "babel-plugin-dynamic-import-node": "^2.3.0" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.7.0.tgz", - "integrity": "sha512-ZAuFgYjJzDNv77AjXRqzQGlQl4HdUM6j296ee4fwKVZfhDR9LAGxfvXjBkb06gNETPnN0sLqRm9Gxg4wZH6dXg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.8.3.tgz", + "integrity": "sha512-8cESMCJjmArMYqa9AO5YuMEkE4ds28tMpZcGZB/jl3n0ZzlsxOAi3mC+SKypTfT8gjMupCnd3YiXCkMjj2jfOg==", "requires": { - "@babel/helper-hoist-variables": "^7.7.0", - "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-hoist-variables": "^7.8.3", + "@babel/helper-module-transforms": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", "babel-plugin-dynamic-import-node": "^2.3.0" } }, "@babel/plugin-transform-modules-umd": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.7.0.tgz", - "integrity": "sha512-u7eBA03zmUswQ9LQ7Qw0/ieC1pcAkbp5OQatbWUzY1PaBccvuJXUkYzoN1g7cqp7dbTu6Dp9bXyalBvD04AANA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.8.3.tgz", + "integrity": "sha512-evhTyWhbwbI3/U6dZAnx/ePoV7H6OUG+OjiJFHmhr9FPn0VShjwC2kdxqIuQ/+1P50TMrneGzMeyMTFOjKSnAw==", "requires": { - "@babel/helper-module-transforms": "^7.7.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-module-transforms": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.7.0.tgz", - "integrity": "sha512-+SicSJoKouPctL+j1pqktRVCgy+xAch1hWWTMy13j0IflnyNjaoskj+DwRQFimHbLqO3sq2oN2CXMvXq3Bgapg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz", + "integrity": "sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.0" + "@babel/helper-create-regexp-features-plugin": "^7.8.3" } }, "@babel/plugin-transform-new-target": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.4.4.tgz", - "integrity": "sha512-r1z3T2DNGQwwe2vPGZMBNjioT2scgWzK9BCnDEh+46z8EEwXBq24uRzd65I7pjtugzPSj921aM15RpESgzsSuA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz", + "integrity": "sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-object-super": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.5.5.tgz", - "integrity": "sha512-un1zJQAhSosGFBduPgN/YFNvWVpRuHKU7IHBglLoLZsGmruJPOo6pbInneflUdmq7YvSVqhpPs5zdBvLnteltQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz", + "integrity": "sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.5.5" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-replace-supers": "^7.8.3" } }, "@babel/plugin-transform-parameters": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.4.4.tgz", - "integrity": "sha512-oMh5DUO1V63nZcu/ZVLQFqiihBGo4OpxJxR1otF50GMeCLiRx5nUdtokd+u9SuVJrvvuIh9OosRFPP4pIPnwmw==", + "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==", "requires": { - "@babel/helper-call-delegate": "^7.4.4", - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-call-delegate": "^7.8.3", + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-regenerator": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.7.0.tgz", - "integrity": "sha512-AXmvnC+0wuj/cFkkS/HFHIojxH3ffSXE+ttulrqWjZZRaUOonfJc60e1wSNT4rV8tIunvu/R3wCp71/tLAa9xg==", + "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==", "requires": { "regenerator-transform": "^0.14.0" } }, "@babel/plugin-transform-runtime": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.6.2.tgz", - "integrity": "sha512-cqULw/QB4yl73cS5Y0TZlQSjDvNkzDbu0FurTZyHlJpWE5T3PCMdnyV+xXoH1opr1ldyHODe3QAX3OMAii5NxA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.8.3.tgz", + "integrity": "sha512-/vqUt5Yh+cgPZXXjmaG9NT8aVfThKk7G4OqkVhrXqwsC5soMn/qTCxs36rZ2QFhpfTJcjw4SNDIZ4RUb8OL4jQ==", "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-module-imports": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3", "resolve": "^1.8.1", "semver": "^5.5.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz", - "integrity": "sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz", + "integrity": "sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-spread": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.6.2.tgz", - "integrity": "sha512-DpSvPFryKdK1x+EDJYCy28nmAaIMdxmhot62jAXF/o99iA33Zj2Lmcp3vDmz+MUh0LNYVPvfj5iC3feb3/+PFg==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz", + "integrity": "sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz", - "integrity": "sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz", + "integrity": "sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3", + "@babel/helper-regex": "^7.8.3" } }, "@babel/plugin-transform-template-literals": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.4.4.tgz", - "integrity": "sha512-mQrEC4TWkhLN0z8ygIvEL9ZEToPhG5K7KDW3pzGqOfIGZ28Jb0POUkeWcoz8HnHvhFy6dwAT1j8OzqN8s804+g==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz", + "integrity": "sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ==", "requires": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-annotate-as-pure": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz", - "integrity": "sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz", + "integrity": "sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg==", "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.7.0.tgz", - "integrity": "sha512-RrThb0gdrNwFAqEAAx9OWgtx6ICK69x7i9tCnMdVrxQwSDp/Abu9DXFU5Hh16VP33Rmxh04+NGW28NsIkFvFKA==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz", + "integrity": "sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw==", "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.7.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-create-regexp-features-plugin": "^7.8.3", + "@babel/helper-plugin-utils": "^7.8.3" } }, "@babel/preset-env": { @@ -730,12 +744,19 @@ "invariant": "^2.2.2", "js-levenshtein": "^1.1.3", "semver": "^5.3.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } } }, "@babel/runtime": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.2.tgz", - "integrity": "sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.4.tgz", + "integrity": "sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==", "requires": { "regenerator-runtime": "^0.13.2" }, @@ -748,9 +769,9 @@ } }, "@babel/runtime-corejs2": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.7.2.tgz", - "integrity": "sha512-GfVnHchOBvIMsweQ13l4jd9lT4brkevnavnVOej5g2y7PpTRY+R4pcQlCjWMZoUla5rMLFzaS/Ll2s59cB1TqQ==", + "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" @@ -764,26 +785,26 @@ } }, "@babel/template": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.0.tgz", - "integrity": "sha512-OKcwSYOW1mhWbnTBgQY5lvg1Fxg+VyfQGjcBduZFljfc044J5iDlnDSfhQ867O17XHiSCxYHUxHg2b7ryitbUQ==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", + "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.0", - "@babel/types": "^7.7.0" + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.3", + "@babel/types": "^7.8.3" } }, "@babel/traverse": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.2.tgz", - "integrity": "sha512-TM01cXib2+rgIZrGJOLaHV/iZUAxf4A0dt5auY6KNZ+cm6aschuJGqKJM3ROTt3raPUdIDk9siAufIFEleRwtw==", - "requires": { - "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.7.2", - "@babel/helper-function-name": "^7.7.0", - "@babel/helper-split-export-declaration": "^7.7.0", - "@babel/parser": "^7.7.2", - "@babel/types": "^7.7.2", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.4.tgz", + "integrity": "sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==", + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.8.4", + "@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", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" @@ -805,9 +826,9 @@ } }, "@babel/types": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.2.tgz", - "integrity": "sha512-YTf6PXoh3+eZgRCBzzP25Bugd2ngmpQVrk7kXX0i5N9BO7TFBtIgZYs7WtxtOGs8e6A4ZI7ECkbBCEHeXocvOA==", + "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==", "requires": { "esutils": "^2.0.2", "lodash": "^4.17.13", @@ -822,10 +843,11 @@ } }, "@cosmos-ui/vue": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/@cosmos-ui/vue/-/vue-0.5.16.tgz", - "integrity": "sha512-Z4byEoZLkIbumm3SlnrzwuyvDi6vzWv2GfT36QuaPFWusCShGgc47ehXp2S55tKrNajOIiF9bfgasHRL6mfHFA==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/@cosmos-ui/vue/-/vue-0.5.21.tgz", + "integrity": "sha512-Y60AMxFKgHrgE/EHxnGKaTcYUN1nJa5m3SylhsCe/d0AvzF9RSYGSPwVgDxmW4KiufBKXkv4PmiNG9WDNWwdxw==", "requires": { + "tiny-cookie": "^2.3.1", "vue": "^2.6.10" } }, @@ -843,6 +865,19 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==" }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "requires": { + "defer-to-connect": "^1.0.1" + } + }, "@types/babel-types": { "version": "7.0.7", "resolved": "https://registry.npmjs.org/@types/babel-types/-/babel-types-7.0.7.tgz", @@ -856,6 +891,11 @@ "@types/babel-types": "*" } }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -877,9 +917,9 @@ "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" }, "@types/node": { - "version": "12.12.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.11.tgz", - "integrity": "sha512-O+x6uIpa6oMNTkPuHDa9MhMMehlxLAd5QcOvKRjAFsBVpeFWTOPnXbDvILvFgFFZfQ1xh1EZi1FbXxUix+zpsQ==" + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.0.tgz", + "integrity": "sha512-GnZbirvmqZUzMgkFn70c74OQpTTUcCzlhQliTzYjQMqg+hVKcDnxdL19Ne3UdYzdMA/+W3eb646FWn/ZaT1NfQ==" }, "@types/q": { "version": "1.5.2", @@ -991,26 +1031,21 @@ } }, "@vue/component-compiler-utils": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.0.2.tgz", - "integrity": "sha512-BSnY2PmW4QwU1AOcGSNYAmEPLjdQ9itl1YpLCWtpwMA5Jy/aqWNuzZ9+ZZ8h6yZJ53W95tVkEP6yrXJ/zUHdEA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.1.1.tgz", + "integrity": "sha512-+lN3nsfJJDGMNz7fCpcoYIORrXo0K3OTsdr8jCM7FuqdI4+70TY6gxY6viJ2Xi1clqyPg7LpeOWwjF31vSMmUw==", "requires": { "consolidate": "^0.15.1", "hash-sum": "^1.0.2", "lru-cache": "^4.1.2", "merge-source-map": "^1.1.0", "postcss": "^7.0.14", - "postcss-selector-parser": "^5.0.0", + "postcss-selector-parser": "^6.0.2", "prettier": "^1.18.2", "source-map": "~0.6.1", "vue-template-es2015-compiler": "^1.9.0" }, "dependencies": { - "cssesc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", - "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" - }, "lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", @@ -1020,16 +1055,6 @@ "yallist": "^2.1.2" } }, - "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" - } - }, "yallist": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", @@ -1038,17 +1063,17 @@ } }, "@vuepress/core": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/core/-/core-1.2.0.tgz", - "integrity": "sha512-ZIsUkQIF+h4Yk6q4okoRnRwRhcYePu/kNiL0WWPDGycjai8cFqFjLDP/tJjfTKXmn9A62j2ETjSwaiMxCtDkyw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@vuepress/core/-/core-1.3.0.tgz", + "integrity": "sha512-/KaH10ggZeEnwh/i8A02VtGHfuIfTEf/pIPV9BBVjK5M6ToPhF2pkcXlPk5PbCWam2dKm7ZDQddJzev1dY5TNA==", "requires": { "@babel/core": "^7.0.0", "@vue/babel-preset-app": "^3.1.1", - "@vuepress/markdown": "^1.2.0", - "@vuepress/markdown-loader": "^1.2.0", - "@vuepress/plugin-last-updated": "^1.2.0", - "@vuepress/plugin-register-components": "^1.2.0", - "@vuepress/shared-utils": "^1.2.0", + "@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", "autoprefixer": "^9.5.1", "babel-loader": "^8.0.4", "cache-loader": "^3.0.0", @@ -1075,18 +1100,18 @@ "vuepress-html-webpack-plugin": "^3.2.0", "vuepress-plugin-container": "^2.0.2", "webpack": "^4.8.1", - "webpack-chain": "^4.6.0", + "webpack-chain": "^6.0.0", "webpack-dev-server": "^3.5.1", "webpack-merge": "^4.1.2", "webpackbar": "3.2.0" } }, "@vuepress/markdown": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/markdown/-/markdown-1.2.0.tgz", - "integrity": "sha512-RLRQmTu5wJbCO4Qv+J0K53o5Ew7nAGItLwWyzCbIUB6pRsya3kqSCViWQVlKlS53zFTmRHuAC9tJMRdzly3mCA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@vuepress/markdown/-/markdown-1.3.0.tgz", + "integrity": "sha512-h4FCAxcYLSGuoftbumsesqquRuQksb98sygiP/EV1J7z3qVj8r/1YdRRoUoE0Yd9hw0izN52KJRYZC7tlUmBnw==", "requires": { - "@vuepress/shared-utils": "^1.2.0", + "@vuepress/shared-utils": "^1.3.0", "markdown-it": "^8.4.1", "markdown-it-anchor": "^5.0.2", "markdown-it-chain": "^1.3.0", @@ -1115,62 +1140,56 @@ } }, "@vuepress/markdown-loader": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/markdown-loader/-/markdown-loader-1.2.0.tgz", - "integrity": "sha512-gOZzoHjfp/W6t+qKBRdbHS/9TwRnNuhY7V+yFzxofNONFHQULofIN/arG+ptYc2SuqJ541jqudNQW+ldHNMC2w==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@vuepress/markdown-loader/-/markdown-loader-1.3.0.tgz", + "integrity": "sha512-20J9+wuyCxhwOWfb7aDY0F/+j2oQYaoDE1VbH3zaqI9XesPl42DsEwA1Nw1asEm3yXdh+uC2scBCiNcv94tsHg==", "requires": { - "@vuepress/markdown": "^1.2.0", + "@vuepress/markdown": "^1.3.0", "loader-utils": "^1.1.0", "lru-cache": "^5.1.1" } }, "@vuepress/plugin-active-header-links": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-active-header-links/-/plugin-active-header-links-1.2.0.tgz", - "integrity": "sha512-vdi7l96pElJvEmcx6t9DWJNH25TIurS8acjN3+b7o4NzdaszFn5j6klN6WtI4Z+5BVDrxHP5W1F3Ebw8SZyupA==", + "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==", "requires": { "lodash.debounce": "^4.0.8" } }, - "@vuepress/plugin-google-analytics": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-google-analytics/-/plugin-google-analytics-1.2.0.tgz", - "integrity": "sha512-0zol5D4Efb5GKel7ADO/s65MLtKSLnOEGkeWzuipkWomSQPzP7TJ3+/RcYBnGdyBFHd1BSpTUHGK0b/IGwM3UA==", - "dev": true - }, "@vuepress/plugin-last-updated": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-last-updated/-/plugin-last-updated-1.2.0.tgz", - "integrity": "sha512-j4uZb/MXDyG+v9QCG3T/rkiaOhC/ib7NKCt1cjn3GOwvWTDmB5UZm9EBhUpbDNrBgxW+SaHOe3kMVNO8bGOTGw==", + "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==", "requires": { "cross-spawn": "^6.0.5" } }, "@vuepress/plugin-nprogress": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-nprogress/-/plugin-nprogress-1.2.0.tgz", - "integrity": "sha512-0apt3Dp6XVCOkLViX6seKSEJgARihi+pX3/r8j8ndFp9Y+vmgLFZnQnGE5iKNi1ty+A6PZOK0RQcBjwTAU4pAw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@vuepress/plugin-nprogress/-/plugin-nprogress-1.3.0.tgz", + "integrity": "sha512-PuBDAhaYLvwG63LamIc1fMk+s4kUqPuvNYKfZjQlF3LtXjlCMvd6YEQyogfB9cZnFOg1nryeHJwWoAdFvzw29Q==", "requires": { "nprogress": "^0.2.0" } }, "@vuepress/plugin-register-components": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-register-components/-/plugin-register-components-1.2.0.tgz", - "integrity": "sha512-C32b8sbGtDEX8I3SUUKS/w2rThiRFiKxmzNcJD996me7VY/4rgmZ8CxGtb6G9wByVoK0UdG1SOkrgOPdSCm80A==", + "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==", "requires": { - "@vuepress/shared-utils": "^1.2.0" + "@vuepress/shared-utils": "^1.3.0" } }, "@vuepress/plugin-search": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/plugin-search/-/plugin-search-1.2.0.tgz", - "integrity": "sha512-QU3JfnMfImDAArbJOVH1q1iCDE5QrT99GLpNGo6KQYZWqY1TWAbgyf8C2hQdaI03co1lkU2Wn/iqzHJ5WHlueg==" + "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==" }, "@vuepress/shared-utils": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/shared-utils/-/shared-utils-1.2.0.tgz", - "integrity": "sha512-wo5Ng2/xzsmIYCzvWxgLFlDBp7FkmJp2shAkbSurLNAh1vixhs0+LyDxsk01+m34ktJSp9rTUUsm6khw/Fvo0w==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@vuepress/shared-utils/-/shared-utils-1.3.0.tgz", + "integrity": "sha512-n1AFgt8SiMDdc5aIj5yOqS3E6+dAZ+9tPw6qf1mBiqvdZzwaUtlydvXqVkskrwUo18znLrUr55VYwubMOaxFnQ==", "requires": { "chalk": "^2.3.2", "diacritics": "^1.3.0", @@ -1181,23 +1200,16 @@ "hash-sum": "^1.0.2", "semver": "^6.0.0", "upath": "^1.1.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } } }, "@vuepress/theme-default": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vuepress/theme-default/-/theme-default-1.2.0.tgz", - "integrity": "sha512-mJxAMYQQv4OrGFsArMlONu8RpCzPUVx81dumkyTT4ay5PXAWTj+WDeFQLOT3j0g9QrDJGnHhbiw2aS+R/0WUyQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@vuepress/theme-default/-/theme-default-1.3.0.tgz", + "integrity": "sha512-0KKTIQQAyO3xE9Gn5vdQYWY+B1onzMm2i3Td610FiLsCRqeHsWs/stl6tlP3nV75OUHwBRH/w0ITrIF4kMR7GQ==", "requires": { - "@vuepress/plugin-active-header-links": "^1.2.0", - "@vuepress/plugin-nprogress": "^1.2.0", - "@vuepress/plugin-search": "^1.2.0", + "@vuepress/plugin-active-header-links": "^1.3.0", + "@vuepress/plugin-nprogress": "^1.3.0", + "@vuepress/plugin-search": "^1.3.0", "docsearch.js": "^2.5.2", "lodash": "^4.17.15", "stylus": "^0.54.5", @@ -1414,11 +1426,11 @@ "integrity": "sha1-xdG9SxKQCPEWPyNvhuX66iAm4u8=" }, "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", + "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", "requires": { - "fast-deep-equal": "^2.0.1", + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" @@ -1454,6 +1466,31 @@ "reduce": "^1.0.1", "semver": "^5.1.0", "tunnel-agent": "^0.6.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } } }, "align-text": { @@ -1464,6 +1501,16 @@ "kind-of": "^3.0.2", "longest": "^1.0.1", "repeat-string": "^1.5.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "alphanum-sort": { @@ -1471,6 +1518,39 @@ "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" }, + "ansi-align": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "requires": { + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, "ansi-colors": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", @@ -1666,16 +1746,16 @@ } }, "autoprefixer": { - "version": "9.7.2", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.2.tgz", - "integrity": "sha512-LCAfcdej1182uVvPOZnytbq61AhnOZ/4JelDaJGDeNwewyU1AMaNthcHsyz1NRjTmd2FkurMckLWfkHg3Z//KA==", + "version": "9.7.4", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.4.tgz", + "integrity": "sha512-g0Ya30YrMBAEZk60lp+qfX5YQllG+S5W3GYCFvyHTvhOki0AEQJLPEcIuGRsqVwLi8FvXPVtwTGhfr38hVpm0g==", "requires": { - "browserslist": "^4.7.3", - "caniuse-lite": "^1.0.30001010", + "browserslist": "^4.8.3", + "caniuse-lite": "^1.0.30001020", "chalk": "^2.4.2", "normalize-range": "^0.1.2", "num2fraction": "^1.2.2", - "postcss": "^7.0.23", + "postcss": "^7.0.26", "postcss-value-parser": "^4.0.2" } }, @@ -1685,17 +1765,16 @@ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", + "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" }, "axios": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", - "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", "requires": { - "follow-redirects": "1.5.10", - "is-buffer": "^2.0.2" + "follow-redirects": "1.5.10" } }, "babel-loader": { @@ -1707,21 +1786,6 @@ "loader-utils": "^1.0.2", "mkdirp": "^0.5.1", "pify": "^4.0.1" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - } } }, "babel-plugin-dynamic-import-node": { @@ -1821,11 +1885,6 @@ "is-data-descriptor": "^1.0.0", "kind-of": "^6.0.2" } - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" } } }, @@ -1857,10 +1916,19 @@ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "bluebird": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.1.tgz", - "integrity": "sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==" + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, "bn.js": { "version": "4.11.8", @@ -1889,10 +1957,13 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } } } }, @@ -1914,6 +1985,105 @@ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, + "boxen": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", + "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "cli-boxes": "^2.2.0", + "string-width": "^4.1.0", + "term-size": "^2.1.0", + "type-fest": "^0.8.1", + "widest-line": "^3.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1938,16 +2108,6 @@ "snapdragon-node": "^2.0.1", "split-string": "^3.0.2", "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } } }, "brorand": { @@ -2021,13 +2181,13 @@ } }, "browserslist": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.7.3.tgz", - "integrity": "sha512-jWvmhqYpx+9EZm/FxcZSbUZyDEvDTLDi3nSAKbzEkyWvtI0mNSmUosey+5awDW1RUlrgXbQb5A6qY1xQH9U6MQ==", + "version": "4.8.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.6.tgz", + "integrity": "sha512-ZHao85gf0eZ0ESxLfCp73GG9O/VTytYDIkIiZDlURppLTI9wErSM/5yAKEq6rcUdxBLjMELmrYUJGg5sxGKMHg==", "requires": { - "caniuse-lite": "^1.0.30001010", - "electron-to-chromium": "^1.3.306", - "node-releases": "^1.1.40" + "caniuse-lite": "^1.0.30001023", + "electron-to-chromium": "^1.3.341", + "node-releases": "^1.1.47" } }, "buffer": { @@ -2038,13 +2198,6 @@ "base64-js": "^1.0.2", "ieee754": "^1.1.4", "isarray": "^1.0.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - } } }, "buffer-from": { @@ -2078,9 +2231,9 @@ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" }, "cac": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.5.3.tgz", - "integrity": "sha512-wZfzSWVXuue1H3J7TDNjbzg4KTqPXCmh7F3QIzEYXfnhMCcOUrx99M7rpO2UDVJA9dqv3butGj2nHvCV47CmPg==" + "version": "6.5.6", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.5.6.tgz", + "integrity": "sha512-8jsGLeBiYEVYTDExaj/rDPG4tyra4yjjacIL10TQ+MobPcg9/IST+dkKLu6sOzq0GcIC6fQqX1nkH9HoskQLAw==" }, "cacache": { "version": "12.0.3", @@ -2102,21 +2255,6 @@ "ssri": "^6.0.1", "unique-filename": "^1.1.1", "y18n": "^4.0.0" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - } } }, "cache-base": { @@ -2146,20 +2284,39 @@ "mkdirp": "^0.5.1", "neo-async": "^2.6.1", "schema-utils": "^1.0.0" + } + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" }, "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", "requires": { - "minimist": "0.0.8" + "pump": "^3.0.0" } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==" } } }, @@ -2215,9 +2372,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001011", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001011.tgz", - "integrity": "sha512-h+Eqyn/YA6o6ZTqpS86PyRmNWOs1r54EBDcd2NTwwfsXQ8re1B38SnB+p2RKF8OUsyEIjeDU8XGec1RGO/wYCg==" + "version": "1.0.30001027", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001027.tgz", + "integrity": "sha512-7xvKeErvXZFtUItTHgNtLgS9RJpVnwBlWX8jSo/BO8VsF6deszemZSkJJJA1KOKrXuzZH4WALpAJdq5EyfgMLg==" }, "caseless": { "version": "0.12.0", @@ -2251,6 +2408,26 @@ "is-regex": "^1.0.3" } }, + "cheerio": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz", + "integrity": "sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==", + "requires": { + "css-select": "~1.2.0", + "dom-serializer": "~0.1.1", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash": "^4.15.0", + "parse5": "^3.0.1" + }, + "dependencies": { + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + } + } + }, "chokidar": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", @@ -2319,13 +2496,18 @@ } }, "clean-css": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", - "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", "requires": { "source-map": "~0.6.0" } }, + "cli-boxes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz", + "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==" + }, "clipboard": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz", @@ -2337,6 +2519,11 @@ "tiny-emitter": "^2.0.0" } }, + "clipboard-copy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/clipboard-copy/-/clipboard-copy-3.1.0.tgz", + "integrity": "sha512-Xsu1NddBXB89IUauda5BIq3Zq73UWkjkaQlPQbLNvNsd5WBMnTWPNKYR6HGaySOxGYZ+BKxP2E9X4ElnI3yiPA==" + }, "cliui": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", @@ -2347,6 +2534,14 @@ "wordwrap": "0.0.2" } }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, "coa": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz", @@ -2426,11 +2621,11 @@ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, "compressible": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz", - "integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "requires": { - "mime-db": ">= 1.40.0 < 2" + "mime-db": ">= 1.43.0 < 2" } }, "compression": { @@ -2447,6 +2642,14 @@ "vary": "~1.1.2" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -2468,6 +2671,71 @@ "inherits": "^2.0.3", "readable-stream": "^2.2.2", "typedarray": "^0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "configstore": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.0.tgz", + "integrity": "sha512-eE/hvMs7qw7DlcB5JPRnthmrITuHMmACUJAp89v6PT6iOqzoLS7HRWhBtuHMlhNHo2AhUSA/3Dh1bKNJHcublQ==", + "requires": { + "dot-prop": "^5.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "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==", + "requires": { + "semver": "^6.0.0" + } + } } }, "connect-history-api-fallback": { @@ -2476,9 +2744,9 @@ "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" }, "consola": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/consola/-/consola-2.11.0.tgz", - "integrity": "sha512-2bcAqHastlPSCvZ+ur8bgHInGAWvUnysWz3h3xRX+/XZoCY7avolJJnVXOPGoVoyCcg1b231XixonoArmgxaoA==" + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.11.3.tgz", + "integrity": "sha512-aoW0YIIAmeftGR8GSpw6CGQluNdkWMWh3yEFjH/hmynTYnMtibXszii3lxCXmk8YxJtI3FAK5aTiquA5VH68Gw==" }, "console-browserify": { "version": "1.2.0", @@ -2565,21 +2833,6 @@ "mkdirp": "^0.5.1", "rimraf": "^2.5.4", "run-queue": "^1.0.0" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - } } }, "copy-descriptor": { @@ -2588,9 +2841,9 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" }, "copy-webpack-plugin": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.0.5.tgz", - "integrity": "sha512-7N68eIoQTyudAuxkfPT7HzGoQ+TsmArN/I3HFwG+lVE3FNzqvZKIiaxtYh4o3BIznioxUvx9j26+Rtsc9htQUQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-5.1.1.tgz", + "integrity": "sha512-P15M5ZC8dyCjQHWwd4Ia/dm0SgVvZJMYeykVIVYXbGyqO4dWB5oyPHp9i7wjwo5LhtlhKbiBCdS2NvM07Wlybg==", "requires": { "cacache": "^12.0.3", "find-cache-dir": "^2.1.0", @@ -2602,7 +2855,7 @@ "normalize-path": "^3.0.0", "p-limit": "^2.2.1", "schema-utils": "^1.0.0", - "serialize-javascript": "^2.1.0", + "serialize-javascript": "^2.1.2", "webpack-log": "^2.0.0" }, "dependencies": { @@ -2625,9 +2878,9 @@ "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==" }, "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "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" } @@ -2650,9 +2903,9 @@ } }, "core-js": { - "version": "2.6.10", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.10.tgz", - "integrity": "sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==" + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" }, "core-util-is": { "version": "1.0.2", @@ -2714,6 +2967,13 @@ "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } } }, "crypto-browserify": { @@ -2734,6 +2994,11 @@ "randomfill": "^1.0.3" } }, + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" + }, "css": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", @@ -2798,14 +3063,14 @@ } }, "css-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", - "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "requires": { - "boolbase": "^1.0.0", - "css-what": "^3.2.1", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" } }, "css-select-base-adapter": { @@ -2828,9 +3093,9 @@ "integrity": "sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY=" }, "css-what": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.2.1.tgz", - "integrity": "sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" }, "cssesc": { "version": "3.0.0", @@ -2935,9 +3200,9 @@ "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=" }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "requires": { "ms": "2.0.0" } @@ -2952,6 +3217,14 @@ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, "deep-equal": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", @@ -2965,6 +3238,11 @@ "regexp.prototype.flags": "^1.2.0" } }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, "deepmerge": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz", @@ -2979,6 +3257,11 @@ "ip-regex": "^2.1.0" } }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -3021,11 +3304,6 @@ "is-data-descriptor": "^1.0.0", "kind-of": "^6.0.2" } - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" } } }, @@ -3172,18 +3450,18 @@ } }, "dom-serializer": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", - "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", "requires": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" + "domelementtype": "^1.3.0", + "entities": "^1.1.1" }, "dependencies": { - "domelementtype": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", - "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==" + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" } } }, @@ -3211,9 +3489,9 @@ } }, "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", "requires": { "dom-serializer": "0", "domelementtype": "1" @@ -3227,6 +3505,11 @@ "is-obj": "^1.0.0" } }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -3236,6 +3519,35 @@ "inherits": "^2.0.1", "readable-stream": "^2.0.0", "stream-shift": "^1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "ecc-jsbn": { @@ -3253,14 +3565,14 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.3.310", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.310.tgz", - "integrity": "sha512-ixvxy46JrDv5c8k1+th66Z+xDZD8zShNs6oh7hgyMpNZUgaoRBisXgFZKAyyhQTAj7oU2Y/uZ0AAsj/TY4N0tA==" + "version": "1.3.346", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.346.tgz", + "integrity": "sha512-Yy4jF5hJd57BWmGPt0KjaXc25AmWZeQK75kdr4zIzksWVtiT6DwaNtvTb9dt+LkQKwUpvBfCyyPsXXtbY/5GYw==" }, "elliptic": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz", - "integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", + "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", "requires": { "bn.js": "^4.4.0", "brorand": "^1.0.1", @@ -3312,6 +3624,33 @@ "errno": "^0.1.3", "readable-stream": "^2.0.1" } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } } } }, @@ -3351,20 +3690,21 @@ } }, "es-abstract": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.0.tgz", - "integrity": "sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==", + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", + "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", "requires": { - "es-to-primitive": "^1.2.0", + "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.0", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-inspect": "^1.6.0", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", "object-keys": "^1.1.1", - "string.prototype.trimleft": "^2.1.0", - "string.prototype.trimright": "^2.1.0" + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" } }, "es-to-primitive": { @@ -3435,9 +3775,9 @@ "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==" }, "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", + "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==" }, "eventsource": { "version": "1.0.7", @@ -3484,6 +3824,14 @@ "to-regex": "^3.0.1" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -3491,14 +3839,6 @@ "requires": { "is-descriptor": "^0.1.0" } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } } } }, @@ -3544,10 +3884,13 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } }, "safe-buffer": { "version": "5.1.2", @@ -3562,22 +3905,11 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } + "is-extendable": "^0.1.0" } }, "extglob": { @@ -3603,14 +3935,6 @@ "is-descriptor": "^1.0.0" } }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, "is-accessor-descriptor": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", @@ -3636,11 +3960,6 @@ "is-data-descriptor": "^1.0.0", "kind-of": "^6.0.2" } - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" } } }, @@ -3650,9 +3969,9 @@ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" }, "fast-glob": { "version": "2.2.7", @@ -3668,9 +3987,9 @@ } }, "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "faye-websocket": { "version": "0.10.0", @@ -3702,6 +4021,12 @@ "schema-utils": "^1.0.0" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", @@ -3711,16 +4036,6 @@ "is-number": "^3.0.0", "repeat-string": "^1.6.1", "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } } }, "finalhandler": { @@ -3735,6 +4050,16 @@ "parseurl": "~1.3.3", "statuses": "~1.5.0", "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + } } }, "find-babel-config": { @@ -3778,6 +4103,35 @@ "requires": { "inherits": "^2.0.3", "readable-stream": "^2.3.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "follow-redirects": { @@ -3786,16 +4140,6 @@ "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", "requires": { "debug": "=3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - } } }, "for-in": { @@ -3848,27 +4192,85 @@ "requires": { "inherits": "^2.0.1", "readable-stream": "^2.0.0" - } - }, - "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "requires": { + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "requires": { "graceful-fs": "^4.1.2", "iferr": "^0.1.5", "imurmurhash": "^0.1.4", "readable-stream": "1 || 2" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "fs.realpath": { @@ -3877,13 +4279,14 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", - "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.11.tgz", + "integrity": "sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw==", "optional": true, "requires": { + "bindings": "^1.5.0", "nan": "^2.12.1", - "node-pre-gyp": "^0.12.0" + "node-pre-gyp": "*" }, "dependencies": { "abbrev": { @@ -3925,7 +4328,7 @@ } }, "chownr": { - "version": "1.1.1", + "version": "1.1.3", "bundled": true, "optional": true }, @@ -3950,7 +4353,7 @@ "optional": true }, "debug": { - "version": "4.1.1", + "version": "3.2.6", "bundled": true, "optional": true, "requires": { @@ -3973,11 +4376,11 @@ "optional": true }, "fs-minipass": { - "version": "1.2.5", + "version": "1.2.7", "bundled": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "^2.6.0" } }, "fs.realpath": { @@ -4001,7 +4404,7 @@ } }, "glob": { - "version": "7.1.3", + "version": "7.1.6", "bundled": true, "optional": true, "requires": { @@ -4027,7 +4430,7 @@ } }, "ignore-walk": { - "version": "3.0.1", + "version": "3.0.3", "bundled": true, "optional": true, "requires": { @@ -4044,7 +4447,7 @@ } }, "inherits": { - "version": "2.0.3", + "version": "2.0.4", "bundled": true, "optional": true }, @@ -4080,7 +4483,7 @@ "optional": true }, "minipass": { - "version": "2.3.5", + "version": "2.9.0", "bundled": true, "optional": true, "requires": { @@ -4089,11 +4492,11 @@ } }, "minizlib": { - "version": "1.2.1", + "version": "1.3.3", "bundled": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "^2.9.0" } }, "mkdirp": { @@ -4105,22 +4508,22 @@ } }, "ms": { - "version": "2.1.1", + "version": "2.1.2", "bundled": true, "optional": true }, "needle": { - "version": "2.3.0", + "version": "2.4.0", "bundled": true, "optional": true, "requires": { - "debug": "^4.1.0", + "debug": "^3.2.6", "iconv-lite": "^0.4.4", "sax": "^1.2.4" } }, "node-pre-gyp": { - "version": "0.12.0", + "version": "0.14.0", "bundled": true, "optional": true, "requires": { @@ -4133,7 +4536,7 @@ "rc": "^1.2.7", "rimraf": "^2.6.1", "semver": "^5.3.0", - "tar": "^4" + "tar": "^4.4.2" } }, "nopt": { @@ -4146,12 +4549,20 @@ } }, "npm-bundled": { - "version": "1.0.6", + "version": "1.1.1", + "bundled": true, + "optional": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", "bundled": true, "optional": true }, "npm-packlist": { - "version": "1.4.1", + "version": "1.4.7", "bundled": true, "optional": true, "requires": { @@ -4213,7 +4624,7 @@ "optional": true }, "process-nextick-args": { - "version": "2.0.0", + "version": "2.0.1", "bundled": true, "optional": true }, @@ -4250,7 +4661,7 @@ } }, "rimraf": { - "version": "2.6.3", + "version": "2.7.1", "bundled": true, "optional": true, "requires": { @@ -4273,7 +4684,7 @@ "optional": true }, "semver": { - "version": "5.7.0", + "version": "5.7.1", "bundled": true, "optional": true }, @@ -4319,17 +4730,17 @@ "optional": true }, "tar": { - "version": "4.4.8", + "version": "4.4.13", "bundled": true, "optional": true, "requires": { "chownr": "^1.1.1", "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", "mkdirp": "^0.5.0", "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" + "yallist": "^3.0.3" } }, "util-deprecate": { @@ -4351,7 +4762,7 @@ "optional": true }, "yallist": { - "version": "3.0.3", + "version": "3.1.1", "bundled": true, "optional": true } @@ -4362,6 +4773,16 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "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==" + }, + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==" + }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", @@ -4434,6 +4855,14 @@ "process": "^0.11.10" } }, + "global-dirs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", + "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", + "requires": { + "ini": "^1.3.5" + } + }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -4463,6 +4892,24 @@ "delegate": "^3.1.2" } }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, "graceful-fs": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", @@ -4477,13 +4924,6 @@ "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - } } }, "handle-thing": { @@ -4550,11 +4990,6 @@ "kind-of": "^4.0.0" }, "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", @@ -4565,6 +5000,11 @@ } } }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" + }, "hash-base": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", @@ -4615,8 +5055,20 @@ "requires": { "mkdirp": "0.3.0", "nopt": "1.0.10" + }, + "dependencies": { + "mkdirp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", + "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=" + } } }, + "hotkeys-js": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/hotkeys-js/-/hotkeys-js-3.7.3.tgz", + "integrity": "sha512-CSaeVPAKEEYNexYR35znMJnCqoofk7oqG/AOOqWow1qDT0Yxy+g+Y8Hs/LhGlsZaSJ7973YN6/N41LAr3t30QQ==" + }, "hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", @@ -4626,6 +5078,35 @@ "obuf": "^1.0.0", "readable-stream": "^2.0.1", "wbuf": "^1.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "hsl-regex": { @@ -4702,19 +5183,14 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - }, - "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } } } }, + "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==" + }, "http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", @@ -4846,6 +5322,11 @@ "resolve-from": "^3.0.0" } }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" + }, "import-local": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", @@ -4884,6 +5365,11 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, "internal-ip": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", @@ -4893,11 +5379,6 @@ "ipaddr.js": "^1.9.0" } }, - "intersection-observer": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.7.0.tgz", - "integrity": "sha512-Id0Fij0HsB/vKWGeBe9PxeY45ttRiBmhFyyt/geBdDHBYNctMRTE3dC1U3ujzz3lap+hVXlEcVaB56kZP/eEUg==" - }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -4937,6 +5418,16 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "requires": { "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "is-arguments": { @@ -4958,14 +5449,29 @@ } }, "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", + "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "requires": { + "ci-info": "^2.0.0" + }, + "dependencies": { + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + } + } }, "is-color-stop": { "version": "1.1.0", @@ -4986,12 +5492,22 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "requires": { "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" }, "is-descriptor": { "version": "0.1.6", @@ -5054,12 +5570,43 @@ "is-extglob": "^2.1.1" } }, + "is-installed-globally": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.1.tgz", + "integrity": "sha512-oiEcGoQbGc+3/iijAijrK2qFpkNoNjsHOm/5V5iaeydyrS/hnwaRCEgH5cpW0P3T1lSjV5piB7S5b5lEugNLhg==", + "requires": { + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" + }, + "dependencies": { + "is-path-inside": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==" + } + } + }, + "is-npm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", + "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==" + }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "requires": { "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "is-obj": { @@ -5107,11 +5654,11 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" }, "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", "requires": { - "has": "^1.0.1" + "has": "^1.0.3" } }, "is-resolvable": { @@ -5155,10 +5702,15 @@ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" + }, "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", @@ -5214,6 +5766,11 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -5275,25 +5832,23 @@ "promise": "^7.0.1" } }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "requires": { + "json-buffer": "3.0.0" + } + }, "killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==" }, "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - }, - "dependencies": { - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - } - } + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" }, "last-call-webpack-plugin": { "version": "3.0.0", @@ -5304,6 +5859,14 @@ "webpack-sources": "^1.1.0" } }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "requires": { + "package-json": "^6.3.0" + } + }, "lazy-cache": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", @@ -5364,6 +5927,11 @@ "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" }, + "lodash.chunk": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz", + "integrity": "sha1-ZuXOH3btJ7QwPYxlEujRIW6BBrw=" + }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -5384,6 +5952,16 @@ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=" }, + "lodash.padstart": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", + "integrity": "sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs=" + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" + }, "lodash.template": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", @@ -5429,6 +6007,11 @@ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=" }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -5437,11 +6020,6 @@ "yallist": "^3.0.2" } }, - "lunr": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.8.tgz", - "integrity": "sha512-oxMeX/Y35PNFuZoHp+jUj5OSEmLCaIH4KTFJh7a93cHBoFmpw2IoPs22VIz7vyO2YUnx2Tn9dzIwO2P/4quIRg==" - }, "make-dir": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", @@ -5449,6 +6027,13 @@ "requires": { "pify": "^4.0.1", "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } } }, "mamacro": { @@ -5495,9 +6080,9 @@ "integrity": "sha512-xLIjLQmtym3QpoY9llBgApknl7pxAcN3WDRc2d3rwpl+/YvDZHPmKscGs+L6E05xf2KrCXPBvosWt7MZukwSpQ==" }, "markdown-it-attrs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/markdown-it-attrs/-/markdown-it-attrs-3.0.1.tgz", - "integrity": "sha512-fcpdmxdEsctDVJEunPyrirVtU/6zcTMxPxAu4Ofz51PKAa8vRMpmGQXsmXx1HTdIdUPoDonm/RhS/+jTNywj/Q==" + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/markdown-it-attrs/-/markdown-it-attrs-3.0.2.tgz", + "integrity": "sha512-q45vdXU9TSWaHgFkWEFM97YHEoCmOyG9csLLdv3oVC6ARjT77u4wfng9rRtSOMb5UpxzT7zTX5GBbwm15H40dw==" }, "markdown-it-chain": { "version": "1.3.0", @@ -5505,6 +6090,17 @@ "integrity": "sha512-XClV8I1TKy8L2qsT9iX3qiV+50ZtcInGXI80CA+DP62sMs7hXlyV/RM3hfwy5O3Ad0sJm9xIwQELgANfESo8mQ==", "requires": { "webpack-chain": "^4.9.0" + }, + "dependencies": { + "webpack-chain": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/webpack-chain/-/webpack-chain-4.12.1.tgz", + "integrity": "sha512-BCfKo2YkDe2ByqkEWe1Rw+zko4LsyS75LVr29C6xIrxAg9JHJ4pl8kaIZ396SUSNp6b4815dRZPSTAS8LlURRQ==", + "requires": { + "deepmerge": "^1.5.2", + "javascript-stringify": "^1.6.0" + } + } } }, "markdown-it-container": { @@ -5517,11 +6113,6 @@ "resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-1.4.0.tgz", "integrity": "sha1-m+4OmpkKljupbfaYDE/dsF37Tcw=" }, - "markdown-it-ins": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/markdown-it-ins/-/markdown-it-ins-3.0.0.tgz", - "integrity": "sha512-+vyAdBuMGwmT2yMlAFJSx2VR/0QZ1onQ/Mkkmr4l9tDFOh5sVoAgRbkgbuSsk+sxJ9vaMH/IQ323ydfvQrPO/Q==" - }, "markdown-it-table-of-contents": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/markdown-it-table-of-contents/-/markdown-it-table-of-contents-0.4.4.tgz", @@ -5569,6 +6160,35 @@ "requires": { "errno": "^0.1.3", "readable-stream": "^2.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "merge-descriptors": { @@ -5614,10 +6234,22 @@ "to-regex": "^3.0.2" }, "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } } } }, @@ -5636,16 +6268,16 @@ "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" }, "mime-db": { - "version": "1.42.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", - "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==" + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==" }, "mime-types": { - "version": "2.1.25", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", - "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", + "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", "requires": { - "mime-db": "1.42.0" + "mime-db": "1.43.0" } }, "mimic-fn": { @@ -5653,6 +6285,11 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, "min-document": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", @@ -5732,9 +6369,19 @@ } }, "mkdirp": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz", - "integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=" + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } }, "move-concurrently": { "version": "1.0.1", @@ -5747,21 +6394,6 @@ "mkdirp": "^0.5.1", "rimraf": "^2.5.4", "run-queue": "^1.0.3" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - } } }, "ms": { @@ -5807,10 +6439,22 @@ "to-regex": "^3.0.1" }, "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } } } }, @@ -5872,31 +6516,48 @@ "vm-browserify": "^1.0.1" }, "dependencies": { - "events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", - "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==" - }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" } } }, "node-releases": { - "version": "1.1.41", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.41.tgz", - "integrity": "sha512-+IctMa7wIs8Cfsa8iYzeaLTFwv5Y4r5jZud+4AnfymzeEXKBCavFX0KBgzVaPVqf0ywa6PrO8/b+bPqdwjGBSg==", + "version": "1.1.48", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.48.tgz", + "integrity": "sha512-Hr8BbmUl1ujAST0K0snItzEA5zkJTQup8VNTKNfT6Zw8vTJkIiagUPNfxHmgDOyfFYNfKAul40sD0UEYTvwebw==", "requires": { "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } } }, "nopt": { @@ -5985,6 +6646,14 @@ "requires": { "is-descriptor": "^0.1.0" } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } } } }, @@ -5994,9 +6663,9 @@ "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" }, "object-is": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz", - "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz", + "integrity": "sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==" }, "object-keys": { "version": "1.1.1", @@ -6023,12 +6692,12 @@ } }, "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" } }, "object.pick": { @@ -6040,12 +6709,12 @@ } }, "object.values": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", - "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", "requires": { "define-properties": "^1.1.3", - "es-abstract": "^1.12.0", + "es-abstract": "^1.17.0-next.1", "function-bind": "^1.1.1", "has": "^1.0.3" } @@ -6121,6 +6790,11 @@ "mem": "^4.0.0" } }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, "p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", @@ -6170,10 +6844,21 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + } + }, "pako": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", - "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==" + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" }, "parallel-transform": { "version": "1.2.0", @@ -6183,6 +6868,35 @@ "cyclist": "^1.0.1", "inherits": "^2.0.3", "readable-stream": "^2.1.5" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "param-case": { @@ -6215,6 +6929,14 @@ "json-parse-better-errors": "^1.0.1" } }, + "parse5": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", + "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", + "requires": { + "@types/node": "*" + } + }, "parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -6341,9 +7063,9 @@ } }, "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "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" } @@ -6389,19 +7111,6 @@ "ms": "^2.1.1" } }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -6415,9 +7124,9 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" }, "postcss": { - "version": "7.0.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.23.tgz", - "integrity": "sha512-hOlMf3ouRIFXD+j2VJecwssTwbvsPGJVMzupptg+85WA+i7MwyrydmQAgY3R+m0Bc0exunhbJmijy8u8+vufuQ==", + "version": "7.0.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.26.tgz", + "integrity": "sha512-IY4oRjpXWYshuTDFxMVkJDtWIk2LhsTlu8bZnbEJA4+bYT16Lvpo8Qv6EvDumhYRgzjZl489pmsY3qVgJQ08nA==", "requires": { "chalk": "^2.4.2", "source-map": "^0.6.1", @@ -6700,9 +7409,9 @@ } }, "postcss-modules-scope": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz", - "integrity": "sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.1.tgz", + "integrity": "sha512-OXRUPecnHCg8b9xWvldG/jUpRIGPNRka0r4D4j0ESUU2/5IOnpsjfPPmDprM3Ih8CgZ8FXjWqaniK5v4rWt3oQ==", "requires": { "postcss": "^7.0.6", "postcss-selector-parser": "^6.0.0" @@ -6915,11 +7624,11 @@ } }, "postcss-safe-parser": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.1.tgz", - "integrity": "sha512-xZsFA3uX8MO3yAda03QrG3/Eg1LN3EPfjjf07vke/46HERLZyHrTsQ9E1r1w1W//fWEhtYNndo2hQplN2cVpCQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz", + "integrity": "sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g==", "requires": { - "postcss": "^7.0.0" + "postcss": "^7.0.26" } }, "postcss-selector-parser": { @@ -6990,9 +7699,9 @@ "integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==" }, "prismjs": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.17.1.tgz", - "integrity": "sha512-PrEDJAFdUGbOP6xK/UsfkC5ghJsPJviKgnQOoxaDbBjwc8op68Quupwt1DeAFoG8GImPhiKXAvvsH7wDSLsu1Q==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.19.0.tgz", + "integrity": "sha512-IVFtbW9mCWm9eOIaEkNyo2Vl4NnEifis2GQ7/MLRG5TQe6t+4Sj9J5QWI9i3v+SS43uZBlCAOn+zYTVYQcPXJw==", "requires": { "clipboard": "^2.0.0" } @@ -7045,9 +7754,9 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, "psl": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", - "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==" + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", + "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==" }, "public-encrypt": { "version": "4.0.3", @@ -7225,9 +7934,9 @@ "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" }, "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" }, "query-string": { "version": "5.1.1", @@ -7294,30 +8003,25 @@ } } }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.5.0.tgz", + "integrity": "sha512-gSz026xs2LfxBPudDuI41V1lka8cxg64E66SGe78zJlsUofOg/yqwezdIcdfwik6B4h8LFmWPA9ef9X3FiNFLA==", "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" } }, "readdirp": { @@ -7328,6 +8032,35 @@ "graceful-fs": "^4.1.11", "micromatch": "^3.1.10", "readable-stream": "^2.0.2" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "reduce": { @@ -7371,14 +8104,34 @@ "requires": { "extend-shallow": "^3.0.2", "safe-regex": "^1.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } } }, "regexp.prototype.flags": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz", - "integrity": "sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", "requires": { - "define-properties": "^1.1.2" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" } }, "regexpu-core": { @@ -7394,15 +8147,31 @@ "unicode-match-property-value-ecmascript": "^1.1.0" } }, + "registry-auth-token": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.1.1.tgz", + "integrity": "sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA==", + "requires": { + "rc": "^1.2.8" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "requires": { + "rc": "^1.2.8" + } + }, "regjsgen": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==" }, "regjsparser": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", - "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.2.tgz", + "integrity": "sha512-E9ghzUtoLwDekPT0DYCp+c4h+bvuUpe6rRHCTYn6eGoqj1LgKXxT6I0Il4WbjhQkOghzi/V+y03bPKvbllL93Q==", "requires": { "jsesc": "~0.5.0" }, @@ -7434,33 +8203,6 @@ "htmlparser2": "^3.3.0", "strip-ansi": "^3.0.0", "utila": "^0.4.0" - }, - "dependencies": { - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, - "css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - } } }, "repeat-element": { @@ -7498,6 +8240,13 @@ "tough-cookie": "~2.4.3", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" + }, + "dependencies": { + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + } } }, "require-directory": { @@ -7521,9 +8270,9 @@ "integrity": "sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc=" }, "resolve": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.2.tgz", - "integrity": "sha512-cAVTI2VLHWYsGOirfeYVVQ7ZDejtQ9fp4YhYckWDEkFfqbVjaT11iM8k6xSAfGFMM+gDpZjMnFssPu8we+mqFw==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", "requires": { "path-parse": "^1.0.6" } @@ -7546,6 +8295,14 @@ "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + } + }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -7639,21 +8396,6 @@ "requires": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - } } }, "select": { @@ -7676,9 +8418,17 @@ } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "requires": { + "semver": "^6.3.0" + } }, "send": { "version": "0.17.1", @@ -7700,10 +8450,25 @@ "statuses": "~1.5.0" }, "dependencies": { - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "ms": { "version": "2.1.1", @@ -7713,9 +8478,9 @@ } }, "serialize-javascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.0.tgz", - "integrity": "sha512-a/mxFfU00QT88umAJQsNWOnUKckhNCqOl028N48e7wFmo2/EHpTo9Wso+iJJCMrQnmFvcjto5RJdAHEvVhcyUQ==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==" }, "serve-index": { "version": "1.9.1", @@ -7731,6 +8496,14 @@ "parseurl": "~1.3.2" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, "http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", @@ -7779,16 +8552,6 @@ "is-extendable": "^0.1.1", "is-plain-object": "^2.0.3", "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } } }, "setimmediate": { @@ -7843,6 +8606,17 @@ } } }, + "sitemap": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-3.2.2.tgz", + "integrity": "sha512-TModL/WU4m2q/mQcrDgNANn0P4LwprM9MMvG4hu5zP4c6IIKs2YLTu6nXXnNr8ODW/WFtxKggiJ1EGn2W0GNmg==", + "requires": { + "lodash.chunk": "^4.2.0", + "lodash.padstart": "^4.6.1", + "whatwg-url": "^7.0.0", + "xmlbuilder": "^13.0.0" + } + }, "slash": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", @@ -7868,6 +8642,14 @@ "use": "^3.1.0" }, "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", @@ -7876,14 +8658,6 @@ "is-descriptor": "^0.1.0" } }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -7934,11 +8708,6 @@ "is-data-descriptor": "^1.0.0", "kind-of": "^6.0.2" } - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" } } }, @@ -7948,6 +8717,16 @@ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "requires": { "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } } }, "sockjs": { @@ -8014,11 +8793,11 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", "requires": { - "atob": "^2.1.1", + "atob": "^2.1.2", "decode-uri-component": "^0.2.0", "resolve-url": "^0.2.1", "source-map-url": "^0.4.0", @@ -8091,16 +8870,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } } } }, @@ -8110,6 +8879,25 @@ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "requires": { "extend-shallow": "^3.0.0" + }, + "dependencies": { + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } } }, "sprintf-js": { @@ -8190,6 +8978,35 @@ "requires": { "inherits": "~2.0.1", "readable-stream": "^2.0.2" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "stream-each": { @@ -8211,12 +9028,41 @@ "readable-stream": "^2.3.6", "to-arraybuffer": "^1.0.0", "xtend": "^4.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" }, "strict-uri-encode": { "version": "1.1.0", @@ -8248,36 +9094,29 @@ } }, "string.prototype.trimleft": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", - "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", + "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", "requires": { "define-properties": "^1.1.3", "function-bind": "^1.1.1" } }, "string.prototype.trimright": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", - "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", + "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", "requires": { "define-properties": "^1.1.3", "function-bind": "^1.1.1" } }, "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "requires": { - "safe-buffer": "~5.1.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - } + "safe-buffer": "~5.2.0" } }, "strip-ansi": { @@ -8298,6 +9137,11 @@ "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, "stylehacks": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", @@ -8335,32 +9179,6 @@ "source-map": "^0.7.3" }, "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - }, "source-map": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", @@ -8411,17 +9229,29 @@ "util.promisify": "~1.0.0" }, "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "css-what": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.2.1.tgz", + "integrity": "sha512-WwOrosiQTvyms+Ti5ZC5vGEK0Vod3FTt1ca+payZqvKuGJF+dq7bG63DstxtN0dpm6FxY27a/zS3Wten+gEtGw==" + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", "requires": { - "minimist": "0.0.8" + "dom-serializer": "0", + "domelementtype": "1" } } } @@ -8431,10 +9261,15 @@ "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" }, + "term-size": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz", + "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==" + }, "terser": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.0.tgz", - "integrity": "sha512-oDG16n2WKm27JO8h4y/w3iqBGAOSCtq7k8dRmrn4Wf9NouL0b2WpMHGChFGZq4nFAQy1FsNJrVQHfurXOSTmOA==", + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.3.tgz", + "integrity": "sha512-Lw+ieAXmY69d09IIc/yqeBqXpEQIpDGZqT34ui1QWXIUpR2RjbqEkT8X7Lgex19hslSqcWM5iMN2kM11eMsESQ==", "requires": { "commander": "^2.20.0", "source-map": "~0.6.1", @@ -8449,26 +9284,19 @@ } }, "terser-webpack-plugin": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz", - "integrity": "sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", + "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", "requires": { "cacache": "^12.0.2", "find-cache-dir": "^2.1.0", "is-wsl": "^1.1.0", "schema-utils": "^1.0.0", - "serialize-javascript": "^1.7.0", + "serialize-javascript": "^2.1.2", "source-map": "^0.6.1", "terser": "^4.1.2", "webpack-sources": "^1.4.0", "worker-farm": "^1.7.0" - }, - "dependencies": { - "serialize-javascript": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz", - "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==" - } } }, "text-table": { @@ -8488,6 +9316,35 @@ "requires": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } } }, "thunky": { @@ -8508,39 +9365,17 @@ "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" }, + "tiny-cookie": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tiny-cookie/-/tiny-cookie-2.3.1.tgz", + "integrity": "sha512-C4x1e8dHfKf03ewuN9aIZzzOfN2a6QKhYlnHdzJxmmjMTLqcskI20F+EplszjODQ4SHmIGFJrvUUnBMS/bJbOA==" + }, "tiny-emitter": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", "optional": true }, - "tm-tooltip": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/tm-tooltip/-/tm-tooltip-0.0.10.tgz", - "integrity": "sha512-ud+LicFlyhwLwO/nfGvtASg3TyUCjaEQC5GCZaE/JzXQmwK0ndb+4F0/ek8xr07rOENFUIT9N+1tmUDqCAtv1g==", - "requires": { - "markdown-it": "^9.1.0" - }, - "dependencies": { - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - }, - "markdown-it": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-9.1.0.tgz", - "integrity": "sha512-xHKG4C8iPriyfu/jc2hsCC045fKrMQ0VexX2F1FGYiRxDxqMB2aAhF8WauJ3fltn2kb90moGBkiiEdooGIg55w==", - "requires": { - "argparse": "^1.0.7", - "entities": "~1.1.1", - "linkify-it": "^2.0.0", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - } - } - } - }, "to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", @@ -8562,8 +9397,23 @@ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "requires": { "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } } }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + }, "to-regex": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", @@ -8573,6 +9423,25 @@ "extend-shallow": "^3.0.2", "regex-not": "^1.0.2", "safe-regex": "^1.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + } } }, "to-regex-range": { @@ -8620,6 +9489,14 @@ } } }, + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "requires": { + "punycode": "^2.1.0" + } + }, "tslib": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", @@ -8662,6 +9539,14 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, "uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", @@ -8751,6 +9636,14 @@ "imurmurhash": "^0.1.4" } }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "requires": { + "crypto-random-string": "^2.0.0" + } + }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -8799,11 +9692,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" } } }, @@ -8812,6 +9700,71 @@ "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", "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==", + "requires": { + "boxen": "^4.2.0", + "chalk": "^3.0.0", + "configstore": "^5.0.0", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.3.1", + "is-npm": "^4.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.0.0", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "upper-case": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", @@ -8865,6 +9818,14 @@ "requires-port": "^1.0.0" } }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -8891,12 +9852,14 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" } }, "utila": { @@ -8910,9 +9873,9 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, "v-runtime-template": { "version": "1.10.0", @@ -8925,9 +9888,9 @@ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, "vendors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.3.tgz", - "integrity": "sha512-fOi47nsJP5Wqefa43kyWSg80qF+Q3XA6MUkgi7Hp1HQaKDQW4cQrK2D0P7mmbFtsV1N89am55Yru/nyEwRubcw==" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==" }, "verror": { "version": "1.10.0", @@ -8950,9 +9913,9 @@ "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" }, "vue": { - "version": "2.6.10", - "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.10.tgz", - "integrity": "sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ==" + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz", + "integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==" }, "vue-hot-reload-api": { "version": "2.3.4", @@ -8960,11 +9923,11 @@ "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==" }, "vue-loader": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.7.2.tgz", - "integrity": "sha512-H/P9xt/nkocyu4hZKg5TzPqyCT1oKOaCSk9zs0JCbJuy0Q8KtR0bjJpnT/5R5x/Ckd1GFkkLQnQ1C4x6xXeLZg==", + "version": "15.8.3", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.8.3.tgz", + "integrity": "sha512-yFksTFbhp+lxlm92DrKdpVIWMpranXnTEuGSc0oW+Gk43M9LWaAmBTnfj5+FCdve715mTHvo78IdaXf5TbiTJg==", "requires": { - "@vue/component-compiler-utils": "^3.0.0", + "@vue/component-compiler-utils": "^3.1.0", "hash-sum": "^1.0.2", "loader-utils": "^1.1.0", "vue-hot-reload-api": "^2.3.0", @@ -8972,22 +9935,22 @@ } }, "vue-router": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.1.3.tgz", - "integrity": "sha512-8iSa4mGNXBjyuSZFCCO4fiKfvzqk+mhL0lnKuGcQtO1eoj8nq3CmbEG8FwK5QqoqwDgsjsf1GDuisDX4cdb/aQ==" + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.1.5.tgz", + "integrity": "sha512-BszkPvhl7I9h334GjckCh7sVFyjTPMMJFJ4Bsrem/Ik+B/9gt5tgrk8k4gGLO4ZpdvciVdg7O41gW4DisQWurg==" }, "vue-server-renderer": { - "version": "2.6.10", - "resolved": "https://registry.npmjs.org/vue-server-renderer/-/vue-server-renderer-2.6.10.tgz", - "integrity": "sha512-UYoCEutBpKzL2fKCwx8zlRtRtwxbPZXKTqbl2iIF4yRZUNO/ovrHyDAJDljft0kd+K0tZhN53XRHkgvCZoIhug==", + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/vue-server-renderer/-/vue-server-renderer-2.6.11.tgz", + "integrity": "sha512-V3faFJHr2KYfdSIalL+JjinZSHYUhlrvJ9pzCIjjwSh77+pkrsXpK4PucdPcng57+N77pd1LrKqwbqjQdktU1A==", "requires": { "chalk": "^1.1.3", "hash-sum": "^1.0.2", "he": "^1.1.0", - "lodash.template": "^4.4.0", + "lodash.template": "^4.5.0", "lodash.uniq": "^4.5.0", "resolve": "^1.2.0", - "serialize-javascript": "^1.3.0", + "serialize-javascript": "^2.1.2", "source-map": "0.5.6" }, "dependencies": { @@ -9008,11 +9971,6 @@ "supports-color": "^2.0.0" } }, - "serialize-javascript": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz", - "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==" - }, "source-map": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", @@ -9035,9 +9993,9 @@ } }, "vue-template-compiler": { - "version": "2.6.10", - "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.10.tgz", - "integrity": "sha512-jVZkw4/I/HT5ZMvRnhv78okGusqe0+qH2A0Em0Cp8aq78+NK9TII263CDVz2QXZsIT+yyV/gZc/j/vlwa+Epyg==", + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz", + "integrity": "sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA==", "requires": { "de-indent": "^1.0.2", "he": "^1.1.0" @@ -9049,15 +10007,16 @@ "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==" }, "vuepress": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/vuepress/-/vuepress-1.2.0.tgz", - "integrity": "sha512-EfHo8Cc73qo+1Pm18hM0qOGynmDr8q5fu2664obynsdCJ1zpvoShVnA0Msraw4SI2xDc0iAoIb3dTwxUIM8DAw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/vuepress/-/vuepress-1.3.0.tgz", + "integrity": "sha512-TmPmHiT70aq4xqy4XczUJmUdpGlMSheOGGVwA2nhYSIS9IEd4ngPbfT9oEcAFTsGHXsr5KH8EgEU7G+3wWzY/A==", "requires": { - "@vuepress/core": "^1.2.0", - "@vuepress/theme-default": "^1.2.0", - "cac": "^6.3.9", + "@vuepress/core": "^1.3.0", + "@vuepress/theme-default": "^1.3.0", + "cac": "^6.5.5", "envinfo": "^7.2.0", - "opencollective-postinstall": "^2.0.2" + "opencollective-postinstall": "^2.0.2", + "update-notifier": "^4.0.0" } }, "vuepress-html-webpack-plugin": { @@ -9094,17 +10053,34 @@ "json5": "^0.5.0", "object-assign": "^4.0.1" } + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } } } }, "vuepress-plugin-container": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/vuepress-plugin-container/-/vuepress-plugin-container-2.1.1.tgz", - "integrity": "sha512-1hKZZ9DzVvgNZiSZfiTGiAzbq9bYww64kFz5yv3BRLKdEYX7rSIxKW6lFrbyaTCPQkT1qWgr95sM+GVetpcQFw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/vuepress-plugin-container/-/vuepress-plugin-container-2.1.2.tgz", + "integrity": "sha512-Df5KoIDMYiFg45GTfFw2hIiLGSsjhms4f3ppl2UIBf5nWMxi2lfifcoo8MooMSfxboxRZjoDccqQfu0fypaKrQ==", "requires": { "markdown-it-container": "^2.0.0" } }, + "vuepress-plugin-sitemap": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/vuepress-plugin-sitemap/-/vuepress-plugin-sitemap-2.3.1.tgz", + "integrity": "sha512-n+8lbukhrKrsI9H/EX0EBgkE1pn85LAQFvQ5dIvrZP4Kz6JxPOPPNTQmZMhahQV1tXbLZQCEN7A1WZH4x+arJQ==", + "requires": { + "sitemap": "^3.0.0" + } + }, "vuepress-plugin-smooth-scroll": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/vuepress-plugin-smooth-scroll/-/vuepress-plugin-smooth-scroll-0.0.3.tgz", @@ -9114,29 +10090,28 @@ } }, "vuepress-theme-cosmos": { - "version": "1.0.73", - "resolved": "https://registry.npmjs.org/vuepress-theme-cosmos/-/vuepress-theme-cosmos-1.0.73.tgz", - "integrity": "sha512-WXuld8UBxRG89WpKtmfjOdilARxF6jgcpYdwy7nWe6kv3PA1MNT0S7hDL9nztCwRk/6GAs6rtkhKyAn5WGfvxQ==", - "requires": { - "@cosmos-ui/vue": "^0.5.16", - "@vuepress/plugin-last-updated": "^1.2.0", - "@vuepress/plugin-search": "^1.1.0", - "algoliasearch": "^3.35.1", + "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==", + "requires": { + "@cosmos-ui/vue": "^0.5.20", "axios": "^0.19.0", - "docsearch.js": "^2.6.3", - "intersection-observer": "^0.7.0", - "lunr": "^2.3.8", + "cheerio": "^1.0.0-rc.3", + "clipboard-copy": "^3.1.0", + "entities": "^2.0.0", + "fuse.js": "^3.4.6", + "gray-matter": "^4.0.2", + "hotkeys-js": "^3.7.3", "markdown-it": "^10.0.0", "markdown-it-attrs": "^3.0.1", - "markdown-it-ins": "^3.0.0", "prismjs": "^1.17.1", "pug": "^2.0.4", "pug-plain-loader": "^1.0.0", "stylus": "^0.54.7", "stylus-loader": "^3.0.2", - "tm-tooltip": "0.0.10", "v-runtime-template": "^1.10.0", - "vuepress": "^1.2.0" + "vuepress": "^1.2.0", + "vuepress-plugin-sitemap": "^2.3.1" } }, "watchpack": { @@ -9157,10 +10132,15 @@ "minimalistic-assert": "^1.0.0" } }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + }, "webpack": { - "version": "4.41.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.2.tgz", - "integrity": "sha512-Zhw69edTGfbz9/8JJoyRQ/pq8FYUoY0diOXqW0T6yhgdhCv6wr0hra5DwwWexNRns2Z2+gsnrNcbe9hbGBgk/A==", + "version": "4.41.5", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.5.tgz", + "integrity": "sha512-wp0Co4vpyumnp3KlkmpM5LWuzvZYayDwM2n17EHFr4qxBBbRokC7DJawPJC7TfSFZ9HZ6GsdH40EBj4UV0nmpw==", "requires": { "@webassemblyjs/ast": "1.8.5", "@webassemblyjs/helper-module-context": "1.8.5", @@ -9182,38 +10162,32 @@ "node-libs-browser": "^2.2.1", "schema-utils": "^1.0.0", "tapable": "^1.1.3", - "terser-webpack-plugin": "^1.4.1", + "terser-webpack-plugin": "^1.4.3", "watchpack": "^1.6.0", "webpack-sources": "^1.4.1" }, "dependencies": { "acorn": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", - "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==" - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz", + "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==" } } }, "webpack-chain": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/webpack-chain/-/webpack-chain-4.12.1.tgz", - "integrity": "sha512-BCfKo2YkDe2ByqkEWe1Rw+zko4LsyS75LVr29C6xIrxAg9JHJ4pl8kaIZ396SUSNp6b4815dRZPSTAS8LlURRQ==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/webpack-chain/-/webpack-chain-6.4.0.tgz", + "integrity": "sha512-f97PYqxU+9/u0IUqp/ekAHRhBD1IQwhBv3wlJo2nvyELpr2vNnUqO3XQEk+qneg0uWGP54iciotszpjfnEExFA==", "requires": { "deepmerge": "^1.5.2", - "javascript-stringify": "^1.6.0" + "javascript-stringify": "^2.0.1" + }, + "dependencies": { + "javascript-stringify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.0.1.tgz", + "integrity": "sha512-yV+gqbd5vaOYjqlbk16EG89xB5udgjqQF3C5FAORDg4f/IS1Yc5ERCv5e/57yBcfJYw05V5JyIXabhwb75Xxow==" + } } }, "webpack-dev-middleware": { @@ -9226,27 +10200,12 @@ "mkdirp": "^0.5.1", "range-parser": "^1.2.1", "webpack-log": "^2.0.0" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - } } }, "webpack-dev-server": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.9.0.tgz", - "integrity": "sha512-E6uQ4kRrTX9URN9s/lIbqTAztwEPdvzVrcmHE8EQ9YnuT9J8Es5Wrd8n9BKg1a0oZ5EgEke/EQFgUsp18dSTBw==", + "version": "3.10.3", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.10.3.tgz", + "integrity": "sha512-e4nWev8YzEVNdOMcNzNeCN947sWJNd43E5XvsJzbAL08kGc2frm1tQ32hTJslRS+H65LCb/AaUCYU7fjHCpDeQ==", "requires": { "ansi-html": "0.0.7", "bonjour": "^3.5.0", @@ -9263,7 +10222,7 @@ "ip": "^1.1.5", "is-absolute-url": "^3.0.3", "killable": "^1.0.1", - "loglevel": "^1.6.4", + "loglevel": "^1.6.6", "opn": "^5.5.0", "p-retry": "^3.0.1", "portfinder": "^1.0.25", @@ -9344,9 +10303,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "p-limit": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", - "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "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" } @@ -9364,11 +10323,6 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - }, "supports-color": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", @@ -9489,6 +10443,16 @@ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==" }, + "whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, "when": { "version": "3.6.4", "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz", @@ -9507,6 +10471,49 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "requires": { + "string-width": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, "window-size": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", @@ -9568,6 +10575,17 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "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==", + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, "ws": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", @@ -9576,6 +10594,16 @@ "async-limiter": "~1.0.0" } }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" + }, + "xmlbuilder": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", + "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==" + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -9624,4 +10652,4 @@ "integrity": "sha1-4Se9nmb9hGvl6rSME5SIL3wOT5g=" } } -} \ No newline at end of file +} diff --git a/docs/package.json b/docs/package.json index 0358a91a3..8ce869057 100644 --- a/docs/package.json +++ b/docs/package.json @@ -4,19 +4,16 @@ "description": "Welcome to the Tendermint Core documentation!", "main": "index.js", "dependencies": { - "vuepress-theme-cosmos": "^1.0.73" - }, - "devDependencies": { - "@vuepress/plugin-google-analytics": "^1.2.0" + "vuepress-theme-cosmos": "^1.0.150" }, "scripts": { "preserve": "./pre.sh", - "serve": "trap 'exit 0' SIGINT; vuepress dev", + "serve": "trap 'exit 0' SIGINT; vuepress dev --no-cache", "postserve": "./post.sh", "prebuild": "./pre.sh", - "build": "trap 'exit 0' SIGINT; vuepress build", + "build": "trap 'exit 0' SIGINT; vuepress build --no-cache", "postbuild": "./post.sh" }, "author": "", "license": "ISC" -} +} \ No newline at end of file diff --git a/docs/tendermint-core/block-structure.md b/docs/tendermint-core/block-structure.md index a87589115..95a81e588 100644 --- a/docs/tendermint-core/block-structure.md +++ b/docs/tendermint-core/block-structure.md @@ -11,6 +11,6 @@ nodes. This blockchain is accessible via various rpc endpoints, mainly `/blockchain?minHeight=_&maxHeight=_` to get a list of headers. But what exactly is stored in these blocks? -The [specification](../spec/blockchain/blockchain.md) contains a detailed description of each component - that's the best place to get started. +The [specification](https://github.com/tendermint/spec/blob/953523c3cb99fdb8c8f7a2d21e3a99094279e9de/spec/blockchain/blockchain.md) contains a detailed description of each component - that's the best place to get started. To dig deeper, check out the [types package documentation](https://godoc.org/github.com/tendermint/tendermint/types). 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 d64ca1084..41b7a0e07 100644 --- a/docs/tendermint-core/light-client-protocol.md +++ b/docs/tendermint-core/light-client-protocol.md @@ -4,21 +4,20 @@ order: 9 # Light Client Protocol -Light clients are an important part of the complete blockchain system -for most applications. Tendermint provides unique speed and security -properties for light client applications. +Light clients are an important part of the complete blockchain system for most +applications. Tendermint provides unique speed and security properties for +light client applications. See our [lite -package](https://godoc.org/github.com/tendermint/tendermint/lite). +package](https://pkg.go.dev/github.com/tendermint/tendermint/lite2?tab=doc). ## Overview -The objective of the light client protocol is to get a -commit for a recent block -hash where the commit includes a -majority of signatures from the last known validator set. From there, -all the application state is verifiable with [merkle -proofs](../spec/blockchain/encoding.md#iavl-tree). +The objective of the light client protocol is to get a commit for a recent +block hash where the commit includes a majority of signatures from the last +known validator set. From there, all the application state is verifiable with +[merkle +proofs](https://github.com/tendermint/spec/blob/953523c3cb99fdb8c8f7a2d21e3a99094279e9de/spec/blockchain/encoding.md#iavl-tree). ## Properties @@ -27,8 +26,38 @@ proofs](../spec/blockchain/encoding.md#iavl-tree). - You get the full speed benefits of Tendermint; transactions commit instantly. - You can get the most recent version of the application state - non-interactively (without committing anything to the blockchain). - For example, this means that you can get the most recent value of a - name from the name-registry without worrying about fork censorship - attacks, without posting a commit and waiting for confirmations. - It's fast, secure, and free! + non-interactively (without committing anything to the blockchain). For + example, this means that you can get the most recent value of a name from the + name-registry without worrying about fork censorship attacks, without posting + a commit and waiting for confirmations. It's fast, secure, and free! + +## Where to obtain trusted height & hash? + +https://pkg.go.dev/github.com/tendermint/tendermint/lite2?tab=doc#TrustOptions + +One way to obtain semi-trusted hash & height is to query multiple full nodes +and compare their hashes: + +```sh +$ curl -s https://233.123.0.140:26657:26657/commit | jq "{height: .result.signed_header.header.height, hash: .result.signed_header.commit.block_id.hash}" +{ + "height": "273", + "hash": "188F4F36CBCD2C91B57509BBF231C777E79B52EE3E0D90D06B1A25EB16E6E23D" +} +``` + +## HTTP proxy + +Tendermint comes with a built-in `tendermint lite` command, which can be used +to 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. + +```sh +$ 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/metrics.md b/docs/tendermint-core/metrics.md index c6ac6c3a7..57a6cea50 100644 --- a/docs/tendermint-core/metrics.md +++ b/docs/tendermint-core/metrics.md @@ -34,21 +34,21 @@ The following metrics are available: | consensus_rounds | Gauge | 0.21.0 | | Number of rounds | | consensus_num_txs | Gauge | 0.21.0 | | Number of transactions | | consensus_total_txs | Gauge | 0.21.0 | | Total number of transactions committed | -| consensus_block_parts | counter | on dev | peer_id | number of blockparts transmitted by peer | -| consensus_latest_block_height | gauge | on dev | | /status sync_info number | -| consensus_fast_syncing | gauge | on dev | | either 0 (not fast syncing) or 1 (syncing) | +| consensus_block_parts | counter | 0.25.0 | peer_id | number of blockparts transmitted by peer | +| consensus_latest_block_height | gauge | 0.25.0 | | /status sync_info number | +| consensus_fast_syncing | gauge | 0.25.0 | | either 0 (not fast syncing) or 1 (syncing) | | consensus_block_size_bytes | Gauge | 0.21.0 | | Block size in bytes | | p2p_peers | Gauge | 0.21.0 | | Number of peers node's connected to | -| p2p_peer_receive_bytes_total | counter | on dev | peer_id, chID | number of bytes per channel received from a given peer | -| p2p_peer_send_bytes_total | counter | on dev | peer_id, chID | number of bytes per channel sent to a given peer | -| p2p_peer_pending_send_bytes | gauge | on dev | peer_id | number of pending bytes to be sent to a given peer | -| p2p_num_txs | gauge | on dev | peer_id | number of transactions submitted by each peer_id | -| p2p_pending_send_bytes | gauge | on dev | peer_id | amount of data pending to be sent to peer | +| p2p_peer_receive_bytes_total | counter | 0.25.0 | peer_id, chID | number of bytes per channel received from a given peer | +| p2p_peer_send_bytes_total | counter | 0.25.0 | peer_id, chID | number of bytes per channel sent to a given peer | +| p2p_peer_pending_send_bytes | gauge | 0.25.0 | peer_id | number of pending bytes to be sent to a given peer | +| p2p_num_txs | gauge | 0.25.0 | peer_id | number of transactions submitted by each peer_id | +| p2p_pending_send_bytes | gauge | 0.25.0 | peer_id | amount of data pending to be sent to peer | | mempool_size | Gauge | 0.21.0 | | Number of uncommitted transactions | -| mempool_tx_size_bytes | histogram | on dev | | transaction sizes in bytes | -| mempool_failed_txs | counter | on dev | | number of failed transactions | -| mempool_recheck_times | counter | on dev | | number of transactions rechecked in the mempool | -| state_block_processing_time | histogram | on dev | | time between BeginBlock and EndBlock in ms | +| mempool_tx_size_bytes | histogram | 0.25.0 | | transaction sizes in bytes | +| mempool_failed_txs | counter | 0.25.0 | | number of failed transactions | +| mempool_recheck_times | counter | 0.25.0 | | number of transactions rechecked in the mempool | +| state_block_processing_time | histogram | 0.25.0 | | time between BeginBlock and EndBlock in ms | ## Useful queries diff --git a/docs/tendermint-core/running-in-production.md b/docs/tendermint-core/running-in-production.md index 1df370893..d386308de 100644 --- a/docs/tendermint-core/running-in-production.md +++ b/docs/tendermint-core/running-in-production.md @@ -99,66 +99,56 @@ send & receive rate per connection (`SendRate`, `RecvRate`). ### RPC Endpoints returning multiple entries are limited by default to return 30 -elements (100 max). See the [RPC Documentation](https://tendermint.com/rpc/) +elements (100 max). See the [RPC Documentation](https://docs.tendermint.com/master/rpc/) for more information. 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. - -- [Github Issues](https://github.com/tendermint/tendermint/issues) -- [StackOverflow - questions](https://stackoverflow.com/questions/tagged/tendermint) +There is a reduced version of this endpoint - `/consensus_state`, which returns +just the votes seen at the current height. -### Debug Utility +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. -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/secure-p2p.md b/docs/tendermint-core/secure-p2p.md index 07dba46d0..96df41bc6 100644 --- a/docs/tendermint-core/secure-p2p.md +++ b/docs/tendermint-core/secure-p2p.md @@ -67,7 +67,7 @@ Authenticated encryption is enabled by default. ## Specification -The full p2p specification can be found [here](https://github.com/tendermint/tendermint/tree/master/docs/spec/p2p). +The full p2p specification can be found [here](https://docs.tendermint.com/master/spec/p2p/). ## Additional Reading diff --git a/docs/tendermint-core/using-tendermint.md b/docs/tendermint-core/using-tendermint.md index 46b6cc1b5..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,12 +156,16 @@ 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://tendermint.com/rpc/) +Find the RPC Documentation [here](https://docs.tendermint.com/master/rpc/) ::: ### Formatting diff --git a/docs/tendermint-core/validators.md b/docs/tendermint-core/validators.md index 307d267c6..97a5da8ca 100644 --- a/docs/tendermint-core/validators.md +++ b/docs/tendermint-core/validators.md @@ -31,9 +31,9 @@ There are two ways to become validator. _+2/3 is short for "more than 2/3"_ A block is committed when +2/3 of the validator set sign [precommit -votes](../spec/blockchain/blockchain.md#vote) for that block at the same `round`. +votes](https://github.com/tendermint/spec/blob/953523c3cb99fdb8c8f7a2d21e3a99094279e9de/spec/blockchain/blockchain.md#vote) for that block at the same `round`. The +2/3 set of precommit votes is called a -[_commit_](../spec/blockchain/blockchain.md#commit). While any +2/3 set of +[_commit_](https://github.com/tendermint/spec/blob/953523c3cb99fdb8c8f7a2d21e3a99094279e9de/spec/blockchain/blockchain.md#commit). While any +2/3 set of precommits for the same block at the same height&round can serve as validation, the canonical commit is included in the next block (see -[LastCommit](../spec/blockchain/blockchain.md#lastcommit)). +[LastCommit](https://github.com/tendermint/spec/blob/953523c3cb99fdb8c8f7a2d21e3a99094279e9de/spec/blockchain/blockchain.md#lastcommit)). diff --git a/docs/tools/README.md b/docs/tools/README.md index fbde42db0..bf9dd1f97 100644 --- a/docs/tools/README.md +++ b/docs/tools/README.md @@ -7,8 +7,23 @@ parent: # Overview -Tendermint comes with some tools for: +Tendermint has some tools that are associated with it for: -- [Benchmarking](./benchmarking.md) -- [Monitoring](./monitoring.md) +- [Debugging](./debugging.md) +- [Benchmarking](#benchmarking) +- [Testnets](#testnets) - [Validation of remote signers](./remote-signer-validation.md) + +## Benchmarking + +- https://github.com/interchainio/tm-load-test + +`tm-load-test` is a distributed load testing tool (and framework) for load +testing Tendermint networks. + +## Testnets + +- https://github.com/interchainio/testnets + +This repository contains various different configurations of test networks for, +and relating to, Tendermint. diff --git a/docs/tools/benchmarking.md b/docs/tools/benchmarking.md deleted file mode 100644 index ca6f7ea35..000000000 --- a/docs/tools/benchmarking.md +++ /dev/null @@ -1,4 +0,0 @@ -# tm-load-test - -Tm-bench has been removed in favor of [tm-load-test](https://github.com/interchainio/tm-load-test). -for information on how to use tm-load-test please visit the docs: https://github.com/interchainio/tm-load-test 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/evidence/reactor.go b/evidence/reactor.go index 19ad0f135..e4dbd51ad 100644 --- a/evidence/reactor.go +++ b/evidence/reactor.go @@ -34,7 +34,7 @@ func NewReactor(evpool *Pool) *Reactor { evR := &Reactor{ evpool: evpool, } - evR.BaseReactor = *p2p.NewBaseReactor("Reactor", evR) + evR.BaseReactor = *p2p.NewBaseReactor("Evidence", evR) return evR } diff --git a/go.mod b/go.mod index 21abf4bae..dfeee6219 100644 --- a/go.mod +++ b/go.mod @@ -1,38 +1,35 @@ module github.com/tendermint/tendermint -go 1.12 +go 1.13 require ( github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f - github.com/VividCortex/gohistogram v1.0.0 // indirect - github.com/Workiva/go-datastructures v1.0.50 + github.com/Workiva/go-datastructures v1.0.52 github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a github.com/coniks-sys/coniks-go v0.0.0-20180722014011-11acf4819b71 github.com/fortytw2/leaktest v1.3.0 - github.com/go-kit/kit v0.9.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.2 - github.com/google/keytransparency v0.1.3 + github.com/golang/protobuf v1.3.4 github.com/gorilla/websocket v1.4.1 github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f github.com/libp2p/go-buffer-pool v0.0.2 github.com/magiconair/properties v1.8.1 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.2.1 + github.com/prometheus/client_golang v1.5.0 github.com/r2ishiguro/vrf v0.0.0-20180716233122-192de52975eb - github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165 + github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a github.com/rs/cors v1.7.0 github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa - github.com/spf13/cobra v0.0.5 - github.com/spf13/viper v1.6.1 - github.com/square/certstrap v1.2.0 // indirect - github.com/stretchr/testify v1.4.0 + github.com/spf13/cobra v0.0.6 + github.com/spf13/viper v1.6.2 + github.com/stretchr/testify v1.5.1 github.com/tendermint/go-amino v0.14.1 - github.com/tendermint/tm-db v0.4.0 + github.com/tendermint/tm-db v0.4.1 github.com/yahoo/coname v0.0.0-20170609175141-84592ddf8673 // indirect golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914 - google.golang.org/grpc v1.26.0 + google.golang.org/grpc v1.27.1 ) diff --git a/go.sum b/go.sum index b41a92944..5a64c19de 100644 --- a/go.sum +++ b/go.sum @@ -1,67 +1,38 @@ -bitbucket.org/creachadair/shell v0.0.6/go.mod h1:8Qqi/cYk7vPnsOePHroKXDJYmb5x7ENhtiFtfZq8K+M= -bou.ke/monkey v1.0.1/go.mod h1:FgHuK96Rv2Nlf+0u1OOVDpCMdsWyOFmeeketDHE7LIg= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.41.0/go.mod h1:OauMR7DV8fzvZIl2qg6rkaIhD/vmgk4iwEw/h6ercmg= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.49.0/go.mod h1:hGvAdzcWNbyuxS3nWhD7H2cIJxjRRTRLQVB0bdputVY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/spanner v1.1.0/go.mod h1:TzTaF9l2ZY2CIetNvVpUu6ZQy8YEOtzB6ICa5EwYjL0= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -contrib.go.opencensus.io/exporter/stackdriver v0.12.1/go.mod h1:iwB6wGarfphGGe/e5CWqyUk/cLzKnWsOKPVW3no6OTw= -contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 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/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= -github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= -github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/sprig v2.18.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= -github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +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/OpenPeeDeeP/depguard v1.0.0/go.mod h1:7/4sitnI9YlQgTLLk734QlzXT8DuHVnAyztLplQjk+o= -github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQee8Us= -github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +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/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +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= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/aws/aws-sdk-go v1.19.18/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.19.49/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.25.39/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/benlaurie/objecthash v0.0.0-20180202135721-d1e3d6079fc1/go.mod h1:jvdWlw8vowVGnZqSDC7yhPd7AifQeQbRDkZcQXV2nRg= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -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/bombsimon/wsl v1.2.5/go.mod h1:43lEF/i0kpXbLCeDXL9LMT8c92HyBywXb0AsgMHYngM= -github.com/bombsimon/wsl v1.2.8 h1:b+E/W7koicKBZDU+vEsw/hnQTN8026Gv1eMZDLUU/Wc= -github.com/bombsimon/wsl v1.2.8/go.mod h1:43lEF/i0kpXbLCeDXL9LMT8c92HyBywXb0AsgMHYngM= 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/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= @@ -72,58 +43,43 @@ github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVa github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +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= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= 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/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/coniks-sys/coniks-go v0.0.0-20180722014011-11acf4819b71 h1:MFLTqgfJclmtaQ1SRUrWwmDX/1UBok3XWUethkJ2swQ= github.com/coniks-sys/coniks-go v0.0.0-20180722014011-11acf4819b71/go.mod h1:TrHYHH4Wze7v7Hkwu1MH1W+mCPQKM+gs+PicdEV14o8= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY= -github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.17+incompatible h1:f/Z3EoDSx1yjaIjLQGo1diYUlQYSBrrAQ5vP8NjwXwo= -github.com/coreos/etcd v3.3.17+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 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 v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 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 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= 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= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/emicklei/proto v1.6.12/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= -github.com/emicklei/proto v1.6.13/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= -github.com/emicklei/proto v1.7.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= -github.com/emicklei/proto v1.8.0 h1:/MjKUvy7nh0ryszHc/PIIFiv/BU5XYX4NfvpM19C5+U= -github.com/emicklei/proto v1.8.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A= -github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= -github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +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.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 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= @@ -134,420 +90,240 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojt github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg= github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y= github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= -github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fullstorydev/grpcurl v1.3.2/go.mod h1:kvk8xPCXOrwVd9zYdjy+xSOT4YWm6kyth4Y9NMfBns4= -github.com/fullstorydev/grpcurl v1.4.0 h1:rKQyAaegPtCj4mpItnCHd+PIEHspIZl14VWhHYIHhls= -github.com/fullstorydev/grpcurl v1.4.0/go.mod h1:kvk8xPCXOrwVd9zYdjy+xSOT4YWm6kyth4Y9NMfBns4= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/gliderlabs/ssh v0.1.4/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-critic/go-critic v0.3.5-0.20190526074819-1df300866540/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA= -github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db h1:GYXWx7Vr3+zv833u+8IoXbNnQY0AdXsxAgI0kX7xcwA= -github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-lintpack/lintpack v0.5.2 h1:DI5mA3+eKdWeJ40nU4d6Wc26qmdG8RCi/btYq0TuRN0= -github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM= -github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= +github.com/go-kit/kit v0.10.0 h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g= -github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= -github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8= -github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= -github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= -github.com/go-toolsmith/astequal v1.0.0 h1:4zxD8j3JRFNyLN46lodQuqz3xdKSrur7U/sr0SDS/gQ= -github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= -github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg= -github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k= -github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw= -github.com/go-toolsmith/astinfo v0.0.0-20180906194353-9809ff7efb21/go.mod h1:dDStQCHtmZpYOmjRP/8gHHnCCch3Zz3oEgCdZVdtweU= -github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk= -github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg= -github.com/go-toolsmith/astp v1.0.0/go.mod h1:RSyrtpVlfTFGDYRbrjyWP1pYu//tSFcvdYrA8meBmLI= -github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks= -github.com/go-toolsmith/pkgload v1.0.0 h1:4DFWWMXVfbcN5So1sBNW9+yeiMqLFGl1wFLTL5R0Tgg= -github.com/go-toolsmith/pkgload v1.0.0/go.mod h1:5eFArkbO80v7Z0kdngIxsRXRMTaX4Ilcwuh3clNrQJc= -github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4= -github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= -github.com/go-toolsmith/typep v1.0.0 h1:zKymWyA1TRYvqYrYDrfEMZULyrhcnGY3x7LDKU2XQaA= -github.com/go-toolsmith/typep v1.0.0/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= -github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= -github.com/gobuffalo/flect v0.1.6 h1:D7KWNRFiCknJKA495/e1BO7oxqf8tbieaLv/ehoZ/+g= -github.com/gobuffalo/flect v0.1.6/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80= -github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/gofrs/flock v0.0.0-20190320160742-5135e617513b/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gofrs/flock v0.7.1 h1:DP+LD/t0njgoPBvT5MJLeliUIVQR03hiKR6vezdwHlc= -github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 h1:uHTyIjqVhYRhLbJ8nIiOJHkEZZ+5YoOsAbD3sk82NiE= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.0.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2-0.20190517061210-b285ee9cfc6c/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.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= -github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= -github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= -github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= -github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6 h1:YYWNAGTKWhKpcLLt7aSj/odlKrSrelQwlovBpDuf19w= -github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0= -github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw= -github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= -github.com/golangci/go-tools v0.0.0-20190318055746-e32c54105b7c/go.mod h1:unzUULGw35sjyOYjUt0jMTXqHlZPpPc6e+xfO4cd6mM= -github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3 h1:pe9JHs3cHHDQgOFXJJdYkK6fLz2PWyYtP4hthoCMvs8= -github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o= -github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU= -github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d h1:pXTK/gkVNs7Zyy7WKgLXmpQ5bHTrq5GDsp8R9Qs67g0= -github.com/golangci/gocyclo v0.0.0-20180528144436-0a533e8fa43d/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU= -github.com/golangci/gofmt v0.0.0-20181222123516-0b8337e80d98/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= -github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a h1:iR3fYXUjHCR97qWS8ch1y9zPNsgXThGwjKPrYfqMPks= -github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= -github.com/golangci/golangci-lint v1.17.2-0.20190910081718-bad04bb7378f/go.mod h1:kaqo8l0OZKYPtjNmG4z4HrWLgcYNIJ9B9q3LWri9uLg= -github.com/golangci/golangci-lint v1.21.0 h1:HxAxpR8Z0M8omihvQdsD3PF0qPjlqYqp2vMJzstoKeI= -github.com/golangci/golangci-lint v1.21.0/go.mod h1:phxpHK52q7SE+5KpPnti4oZTdFCEsn/tKN+nFvCKXfk= -github.com/golangci/gosec v0.0.0-20190211064107-66fb7fc33547/go.mod h1:0qUabqiIQgfmlAmulqxyiGkkyF6/tOGSnY2cnPVwrzU= -github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc h1:gLLhTLMk2/SutryVJ6D4VZCU3CUqr8YloG7FPIBWFpI= -github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU= -github.com/golangci/lint-1 v0.0.0-20190420132249-ee948d087217/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= -github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA= -github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= -github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA= -github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= -github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770 h1:EL/O5HGrF7Jaq0yNhBLucz9hTuRzj2LdwGBOaENgxIk= -github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= -github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21 h1:leSNB7iYzLYSSx3J/s5sVf4Drkc68W2wm4Ixh/mr0us= -github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI= -github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4= -github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039 h1:XQKc8IYQOeRwVs36tDrEmTgDgP88d5iEURwpmtiAlOM= -github.com/golangci/revgrep v0.0.0-20180812185044-276a5c0a1039/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4= -github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys= -github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= 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/certificate-transparency-go v1.0.22-0.20190910093103-496c2e82955b/go.mod h1:i+Q7XY+ArBveOUT36jiHGfuSK1fHICIg6sUkRxPAbCs= -github.com/google/certificate-transparency-go v1.1.0 h1:10MlrYzh5wfkToxWI4yJzffsxLfxcEDlOATMx/V9Kzw= -github.com/google/certificate-transparency-go v1.1.0/go.mod h1:i+Q7XY+ArBveOUT36jiHGfuSK1fHICIg6sUkRxPAbCs= -github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/keytransparency v0.1.3 h1:UR3e6DthWjjG7eRffL7XP4B/VS/R/vPvkM9q9yqDCxM= -github.com/google/keytransparency v0.1.3/go.mod h1:I3vUiBA5ST6O8SUDRJm7CmHCuh/V6j9d0fwXpXTBm2g= -github.com/google/licenseclassifier v0.0.0-20190501212618-47b603fe1b8c/go.mod h1:qsqn2hxC+vURpyBRygGUuinTO42MFRLcsmQ/P8v94+M= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/monologue v0.0.0-20190606152607-4b11a32b5934/go.mod h1:6NTfaQoUpg5QmPsCUWLR3ig33FHrKXhTtWzF0DVdmuk= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/tink v1.3.0-rc3/go.mod h1:xCAsc4J1ZYTyVncY9QgOTZsU0l+P3ou0VsiLslNjgAo= -github.com/google/trillian v1.2.2-0.20190612132142-05461f4df60a/go.mod h1:YPmUVn5NGwgnDUgqlVyFGMTgaWlnSvH7W5p+NdOG8UA= -github.com/google/trillian v1.3.3 h1:xwIDDPBdK5I8hvCTZAna9dNCdZc1OUvkPnSSdiwjT8Y= -github.com/google/trillian v1.3.3/go.mod h1:FFIfFeCKosf1D9mKBxMySFmBI96xHCatmA7NZOXo+qI= -github.com/google/trillian-examples v0.0.0-20190603134952-4e75ba15216c/go.mod h1:WgL3XZ3pA8/9cm7yxqWrZE6iZkESB2ItGxy5Fo6k2lk= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 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/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= -github.com/gostaticanalysis/analysisutil v0.0.0-20190329151158-56bca42c7635/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= -github.com/gostaticanalysis/analysisutil v0.0.3 h1:iwp+5/UAyzQSFgQ4uR2sni99sJ8Eo9DEacKWM5pekIg= -github.com/gostaticanalysis/analysisutil v0.0.3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg= -github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.4 h1:5xLhQjsk4zqPf9EHCrja2qFZMx+yBqkO3XgJ14bNnU0= -github.com/grpc-ecosystem/grpc-gateway v1.9.4/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +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/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= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c h1:kQWxfPIHVLbgLzphqk3QUflDy9QdksZR4ygR807bpy0= -github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.2.0 h1:yPeWdRnmynF7p+lLYz0H2tthW9lqhMJrQV/U7yy4wX0= -github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= -github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +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/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jhump/protoreflect v1.5.0 h1:NgpVT+dX71c8hZnxHof2M7QDK7QtohIJ7DYycjnkyfc= -github.com/jhump/protoreflect v1.5.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= 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 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 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= -github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= 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 v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7ILpTPQTk+E2s+z4CUas9lVNjIuKR4c5/zKgM= -github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/letsencrypt/pkcs11key v2.0.1-0.20170608213348-396559074696+incompatible/go.mod h1:iGYXKqDXt0cpBthCHdr9ZdsQwyGlYFh/+8xa4WzIQ34= -github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= -github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/lyft/protoc-gen-validate v0.0.14/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/lyft/protoc-gen-validate v0.1.0 h1:NytKd9K7UW7Szxn+9PYNsaJ/98TL/WsDq4ro4ZVuh5o= -github.com/lyft/protoc-gen-validate v0.1.0/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +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/matoous/godox v0.0.0-20190911065817-5d6d842e92eb h1:RHba4YImhrUVQDHUCe2BNSOz4tVy2yGyXhvYDvxGgeE= -github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.6 h1:V2iyH+aX9C5fsYCpK60U8BYIvmhqxuOL3JZcqc1NB7k= -github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= -github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +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-ps v0.0.0-20170309133038-4fdf99ab2936/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= -github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= -github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +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= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE= -github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= -github.com/mozilla/tls-observatory v0.0.0-20190404164649-a3c1b6cfecfd/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-proto-validators v0.0.0-20190212092829-1f388280e944/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= -github.com/mwitkow/go-proto-validators v0.2.0 h1:F6LFfmgVnfULfaRsQWBbe7F7ocuHCr9+7m+GAeDzNbQ= -github.com/mwitkow/go-proto-validators v0.2.0/go.mod h1:ZfA1hW+UH/2ZHOWvQ3HnQaU0DtnpXu850MZiy+YUgcc= -github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= -github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= -github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d h1:AREM5mwr4u1ORQBMvzfzBgpsctsbQikCVpvC+tX285E= -github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.2 h1:sq53g+DWf0J6/ceFUHpQ0nAEb6WgM++fq16MZ91cS6o= -github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2fSBUmeGDbRWPxyQ= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/otiai10/copy v1.0.1/go.mod h1:8bMCJrAqOtN/d9oyh5HR7HhLQMvcGMpGdwRDYsfOCHc= -github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= -github.com/otiai10/curr v0.0.0-20190513014714-f5a3d24e5776/go.mod h1:3HNVkVOU7vZeFXocWuvtcS0XSFLcf2XUSDHkq9t1jU4= -github.com/otiai10/mint v1.2.3/go.mod h1:YnfyPNhBvnY8bW4SGQHCs/aAFhkgySlMZbrF5U0bOVw= -github.com/otiai10/mint v1.2.4/go.mod h1:d+b7n/0R3tdyUYYylALXpWQ/kTN+QobSq/4SRGBkR3M= -github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= -github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo= -github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4= -github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1 h1:K47Rk0v/fkEfwfQet2KWhscE0cJzjgCCDBG2KHZoVno= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v0.9.4/go.mod h1:oCXIBxdI62A4cR6aTRJCgetEjecSIYzOEaeAn4iYEpM= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.2.1 h1:JnMpQc6ppsNgw9QPAGF6Dod479itz7lvlsMzzNayLOI= -github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.5.0 h1:Ctq0iGpCmr3jeP77kbF2UxgvRwzWWz+4Bh9/vJTyg1A= +github.com/prometheus/client_golang v1.5.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d h1:GoAlyOgbOEIFdaDqxJVlbOQ1DtGmZWs/Qau0hIlk+WQ= +github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.7 h1:RS5GAlMbnkWkhs4+bPocMTmGjYkuCY5djjqEDdXOhcQ= -github.com/prometheus/procfs v0.0.7/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/pseudomuto/protoc-gen-doc v1.3.0 h1:wpwmaSCWY2lGwkzAxAaqYcGyoklZjZmeXrJ/X7IskJM= -github.com/pseudomuto/protoc-gen-doc v1.3.0/go.mod h1:fwtQAY9erXp3mC92O8OTECnDlJT2r0Ff4KSEKbGEmy0= -github.com/pseudomuto/protokit v0.2.0 h1:hlnBDcy3YEDXH7kc9gV+NLaN0cDzhDvD1s7Y6FZ8RpM= -github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= -github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI= github.com/r2ishiguro/vrf v0.0.0-20180716233122-192de52975eb h1:3kW8n+FfBaUoqlHxCa6e90PXWpGCWWkdyTZ6F7c9m2I= github.com/r2ishiguro/vrf v0.0.0-20180716233122-192de52975eb/go.mod h1:2NzHJUkr/ERaPNQ2IUuNbB2jMTWYp2DxhcraWbzZj00= -github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165 h1:nkcn14uNmFEuGCb2mBZbBb24RdNRL08b/wb+xBOYpuk= -github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +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/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.2.1/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk= -github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d/go.mod h1:w5+eXa0mYznDkHaMCXA4XYffjlH+cy1oyKbfzJXa2Do= -github.com/securego/gosec v0.0.0-20191119104125-df484bfa9e9f h1:1egnuKwFhkqg8hXU5huGkxz9iPFu4dLINWRSIPjAM+M= -github.com/securego/gosec v0.0.0-20191119104125-df484bfa9e9f/go.mod h1:H5UrtKXL5BGF4FgRa7p2fyqU/lddaTSCLjexYIfS0Bk= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada/go.mod h1:WWnYX4lzhCH5h/3YBfyVA3VbLYjlMZZAQcW9ojMexNc= -github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go v0.0.0-20190330031554-6713ea532688 h1:p7kt1rmLjMUkAmuyY/GUZeCoOud+nq297UKCCgEqYmA= -github.com/shurcooL/go v0.0.0-20190330031554-6713ea532688/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041 h1:llrF3Fs4018ePo4+G/HV/uQUqEI1HMDjCeOf2V6puPc= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -555,93 +331,49 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa h1:YJfZp12Z3AFhSBeXOlv4BO55RMwPn2NoQeDsrdWnBtY= github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa/go.mod h1:oJyF+mSPHbB5mVY2iO9KV3pTt/QbIkGaO8gQ2WrDbP4= -github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sourcegraph/go-diff v0.5.1 h1:gO6i5zugwzo1RVTvgvfwCOSVegNuvnNi6bAD1QCmkHs= -github.com/sourcegraph/go-diff v0.5.1/go.mod h1:j2dHj3m8aZgQO8lMTcTnBcXkRRRqi34cd2MNlA9u1mE= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= 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.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.4/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +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/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 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/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4= -github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk= -github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= -github.com/square/certstrap v1.2.0 h1:ecgyABrbFLr8jSbOC6oTBmBek0t/HqtgrMUZCPuyfdw= -github.com/square/certstrap v1.2.0/go.mod h1:CUHqV+fxJW0Y5UQFnnbYwQ7bpKXO1AKbic9g73799yw= -github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= +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= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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.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/timakin/bodyclose v0.0.0-20190721030226-87058b9bfcec/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= -github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e h1:RumXZ56IrCj4CL+g1b9OL/oH0QnsF976bC8xQFYUD5Q= -github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= +github.com/tendermint/tm-db v0.4.1 h1:TvX7JWjJOVZ+N3y+I86wddrGttOdMmmBxXcu0/Y7ZJ0= +github.com/tendermint/tm-db v0.4.1/go.mod h1:JsJ6qzYkCGiGwm5GHl/H5GLI9XLb6qZX7PRe425dHAY= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= -github.com/uber/prototool v1.8.2-0.20190910022025-7df3b957ffe3/go.mod h1:fQyN8ZUYd9QoSiV0T/Da6NUSZY4ARW+EfJaL/4V1DD8= -github.com/uber/prototool v1.9.0 h1:s5O7ZQUqmV8jfaB9J69HD0DlU4S0CL4ecZlemkhF3TQ= -github.com/uber/prototool v1.9.0/go.mod h1:srCJMbeTuTT/1xkYIuUc1iCKsjjn8RBw9xxQl0R4aZg= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ultraware/funlen v0.0.1/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= -github.com/ultraware/funlen v0.0.2 h1:Av96YVBwwNSe4MLR7iI/BIa3VyI7/djnto/pK3Uxbdo= -github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= -github.com/ultraware/whitespace v0.0.4 h1:If7Va4cM03mpgrNH9k49/VOicWpGoG70XPBFFODYDsg= -github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ= -github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/uudashr/gocognit v0.0.0-20190926065955-1655d0de0517/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM= -github.com/uudashr/gocognit v1.0.0 h1:NST9SQhYHpFiyKdXn8UAiogdB1xkt5RnA1/rR/oBcGw= -github.com/uudashr/gocognit v1.0.0/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= -github.com/valyala/quicktemplate v1.1.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4= -github.com/valyala/quicktemplate v1.2.0/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4= -github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= -github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8= -github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yahoo/coname v0.0.0-20170609175141-84592ddf8673 h1:PSg2cEFd+9Ae/r5x5iO8cJ3VmTbZNQp6X8tHDmVJAbA= @@ -649,268 +381,150 @@ github.com/yahoo/coname v0.0.0-20170609175141-84592ddf8673/go.mod h1:Wq2sZrP++Us go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd v3.3.13+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI= -go.etcd.io/etcd v3.3.17+incompatible h1:g8iRku1SID8QAW8cDlV0L/PkZlw63LSiYEHYHoE6j/s= -go.etcd.io/etcd v3.3.17+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.5.1 h1:rsqfU5vBkVknbhUGbAUwQKR2H4ItV8tjJ+6kJX4cxHM= -go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.4.0 h1:f3WCSC2KzAcBXGATIxAB1E2XuCpNU255wNKZ505qi3E= -go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= -golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 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= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914 h1:MlY3mEfbnWGmUi4rtHOtNnnnN4UJRGSyLPx+DXA5Sq4= golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914/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/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.0.0-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20170915040203-e531a2a1c15f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190121143147-24cd39ecf745/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190213192042-740235f6c0d8/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190311215038-5c2858a9cfe5/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190322203728-c1a832b0ad89/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190719005602-e377ae9d6386/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190909030654-5b82db07426d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911151314-feee8acb394c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190930201159-7c411dea38b0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191101200257-8dbcdeb83d3f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191105231337-689d0f08e67a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191120221951-8fd459516a27 h1:Hnjbvuhm7GOIJHtxOlsKcq7CEDvPLMHawSA1AzKVlxA= -golang.org/x/tools v0.0.0-20191120221951-8fd459516a27/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.6.0/go.mod h1:btoxGiFvQNVUZQ8W08zLtrVS08CNpINPEfxXxgJL1Q4= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190605220351-eb0b1bdb6ae6/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190626174449-989357319d63/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191009194640-548a555dbc03/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115221424-83cc0476cb11 h1:51D++eCgOHufw5VfDE9Uzqyyc+OyQIjb9hkYy9LN5Fk= -google.golang.org/genproto v0.0.0-20191115221424-83cc0476cb11/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +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.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= -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.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 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= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk= -gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk= -gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk= -gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= -gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= -gopkg.in/src-d/go-git.v4 v4.11.0/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= -mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= -mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= -mvdan.cc/unparam v0.0.0-20190209190245-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY= -mvdan.cc/unparam v0.0.0-20190310220240-1b9ccfa71afe/go.mod h1:BnhuWBAqxH3+J5bDybdxgw5ZfS+DsVd4iylsKQePN8o= -mvdan.cc/unparam v0.0.0-20190720180237-d51796306d8f/go.mod h1:4G1h5nDURzA3bwVMZIVpwbkw+04kSxk3rAtzlimaUJw= -mvdan.cc/unparam v0.0.0-20191111180625-960b1ec0f2c2 h1:K7wru2CfJGumS5hkiguQ0Rb9ebKM2Jo8s5d4Jm9lFaM= -mvdan.cc/unparam v0.0.0-20191111180625-960b1ec0f2c2/go.mod h1:rCqoQrfAmpTX/h2APczwM7UymU/uvaOluiVPIYCSY/k= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= -sourcegraph.com/sqs/pbtypes v1.0.0 h1:f7lAwqviDEGvON4kRv0o5V7FT/IQK+tbkF664XMbP3o= -sourcegraph.com/sqs/pbtypes v1.0.0/go.mod h1:3AciMUv4qUuRHRHhOG4TZOB+72GdPVz5k+c648qsFS4= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/libs/autofile/autofile.go b/libs/autofile/autofile.go index 3a6244894..061726f7d 100644 --- a/libs/autofile/autofile.go +++ b/libs/autofile/autofile.go @@ -3,6 +3,7 @@ package autofile import ( "os" "os/signal" + "path/filepath" "sync" "syscall" "time" @@ -57,6 +58,11 @@ type AutoFile struct { // an error, it will be of type *PathError or *ErrPermissionsChanged (if file's // permissions got changed (should be 0600)). func OpenAutoFile(path string) (*AutoFile, error) { + var err error + path, err = filepath.Abs(path) + if err != nil { + return nil, err + } af := &AutoFile{ ID: tmrand.Str(12) + ":" + path, Path: path, diff --git a/libs/autofile/autofile_test.go b/libs/autofile/autofile_test.go index 664264cdc..24ca6343d 100644 --- a/libs/autofile/autofile_test.go +++ b/libs/autofile/autofile_test.go @@ -3,26 +3,34 @@ package autofile import ( "io/ioutil" "os" + "path/filepath" "syscall" "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" tmos "github.com/tendermint/tendermint/libs/os" ) func TestSIGHUP(t *testing.T) { - // First, create an AutoFile writing to a tempfile dir - file, err := ioutil.TempFile("", "sighup_test") + origDir, err := os.Getwd() + require.NoError(t, err) + defer os.Chdir(origDir) + + // First, create a temporary directory and move into it + dir, err := ioutil.TempDir("", "sighup_test") require.NoError(t, err) - err = file.Close() + defer os.RemoveAll(dir) + err = os.Chdir(dir) require.NoError(t, err) - name := file.Name() - // Here is the actual AutoFile + // Create an AutoFile in the temporary directory + name := "sighup_test" af, err := OpenAutoFile(name) require.NoError(t, err) + require.True(t, filepath.IsAbs(af.Path)) // Write to the file. _, err = af.Write([]byte("Line 1\n")) @@ -34,6 +42,13 @@ func TestSIGHUP(t *testing.T) { err = os.Rename(name, name+"_old") require.NoError(t, err) + // Move into a different temporary directory + otherDir, err := ioutil.TempDir("", "sighup_test_other") + require.NoError(t, err) + defer os.RemoveAll(otherDir) + err = os.Chdir(otherDir) + require.NoError(t, err) + // Send SIGHUP to self. syscall.Kill(syscall.Getpid(), syscall.SIGHUP) @@ -49,12 +64,17 @@ func TestSIGHUP(t *testing.T) { require.NoError(t, err) // Both files should exist - if body := tmos.MustReadFile(name + "_old"); string(body) != "Line 1\nLine 2\n" { + if body := tmos.MustReadFile(filepath.Join(dir, name+"_old")); string(body) != "Line 1\nLine 2\n" { t.Errorf("unexpected body %s", body) } - if body := tmos.MustReadFile(name); string(body) != "Line 3\nLine 4\n" { + if body := tmos.MustReadFile(filepath.Join(dir, name)); string(body) != "Line 3\nLine 4\n" { t.Errorf("unexpected body %s", body) } + + // The current directory should be empty + files, err := ioutil.ReadDir(".") + require.NoError(t, err) + assert.Empty(t, files) } // // Manually modify file permissions, close, and reopen using autofile: diff --git a/libs/autofile/group.go b/libs/autofile/group.go index e859149b9..5d5822ebc 100644 --- a/libs/autofile/group.go +++ b/libs/autofile/group.go @@ -6,7 +6,6 @@ import ( "fmt" "io" "os" - "path" "path/filepath" "regexp" "strconv" @@ -78,14 +77,17 @@ 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) { - dir := path.Dir(headPath) +func OpenGroup(headPath string, groupOptions ...func(*Group)) (*Group, error) { + dir, err := filepath.Abs(filepath.Dir(headPath)) + if err != nil { + return nil, err + } head, err := OpenAutoFile(headPath) if err != nil { return nil, err } - g = &Group{ + g := &Group{ ID: "group:" + head.ID, Head: head, headBuf: bufio.NewWriterSize(head, 4096*10), @@ -107,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/autofile/group_test.go b/libs/autofile/group_test.go index a91ef0633..de29a0fba 100644 --- a/libs/autofile/group_test.go +++ b/libs/autofile/group_test.go @@ -4,6 +4,7 @@ import ( "io" "io/ioutil" "os" + "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -105,6 +106,23 @@ func TestCheckHeadSizeLimit(t *testing.T) { func TestRotateFile(t *testing.T) { g := createTestGroupWithHeadSizeLimit(t, 0) + + // Create a different temporary directory and move into it, to make sure + // relative paths are resolved at Group creation + origDir, err := os.Getwd() + require.NoError(t, err) + defer os.Chdir(origDir) + + dir, err := ioutil.TempDir("", "rotate_test") + require.NoError(t, err) + defer os.RemoveAll(dir) + err = os.Chdir(dir) + require.NoError(t, err) + + require.True(t, filepath.IsAbs(g.Head.Path)) + require.True(t, filepath.IsAbs(g.Dir)) + + // Create and rotate files g.WriteLine("Line 1") g.WriteLine("Line 2") g.WriteLine("Line 3") @@ -129,6 +147,11 @@ func TestRotateFile(t *testing.T) { t.Errorf("got unexpected contents: [%v]", string(body2)) } + // Make sure there are no files in the current, temporary directory + files, err := ioutil.ReadDir(".") + require.NoError(t, err) + assert.Empty(t, files) + // Cleanup destroyTestGroup(t, g) } 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 6354c77a5..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,87 +81,28 @@ 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{ - // 193 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, - 0x97, 0x49, 0xe9, 0xa6, 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0xa7, 0xe7, - 0xa7, 0xe7, 0xeb, 0x83, 0x95, 0x26, 0x95, 0xa6, 0x81, 0x79, 0x60, 0x0e, 0x98, 0x05, 0x31, 0x42, - 0x49, 0x8f, 0x8b, 0x25, 0x20, 0x31, 0xb3, 0x48, 0x48, 0x80, 0x8b, 0x39, 0x3b, 0xb5, 0x52, 0x82, - 0x51, 0x81, 0x51, 0x83, 0x27, 0x08, 0xc4, 0x14, 0x12, 0xe1, 0x62, 0x2d, 0x4b, 0xcc, 0x29, 0x4d, - 0x95, 0x60, 0x02, 0x8b, 0x41, 0x38, 0x4a, 0x46, 0x5c, 0x1c, 0xde, 0x9e, 0x66, 0x26, 0xc4, 0xe8, - 0x61, 0x86, 0xea, 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, 0x90, 0x1c, 0x8c, 0xf0, 0x0f, 0x32, 0x13, 0xea, 0xf5, 0x24, 0x36, - 0xb0, 0x93, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x01, 0x3f, 0x5a, 0xca, 0x0c, 0x01, 0x00, - 0x00, + 0x97, 0x49, 0xa9, 0x95, 0x64, 0x64, 0x16, 0xa5, 0xc4, 0x17, 0x24, 0x16, 0x95, 0x54, 0xea, 0x83, + 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, + 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 59940c8d0..1b6a7a58d 100644 --- a/libs/kv/types.proto +++ b/libs/kv/types.proto @@ -1,29 +1,22 @@ syntax = "proto3"; package tendermint.libs.kv; -option go_package = "github.com/tendermint/tendermint/libs/kv"; +option go_package = "github.com/tendermint/tendermint/libs/kv"; -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; +import "third_party/proto/gogoproto/gogo.proto"; -option (gogoproto.marshaler_all) = true; -option (gogoproto.unmarshaler_all) = true; -option (gogoproto.sizer_all) = true; +option (gogoproto.marshaler_all) = true; +option (gogoproto.unmarshaler_all) = true; +option (gogoproto.sizer_all) = true; option (gogoproto.goproto_registration) = true; // Generate tests option (gogoproto.populate_all) = true; -option (gogoproto.equal_all) = true; -option (gogoproto.testgen_all) = true; +option (gogoproto.equal_all) = true; +option (gogoproto.testgen_all) = true; //---------------------------------------- // Abstract types -// Define these here for compatibility but use tmlibs/common.KVPair. message Pair { - bytes key = 1; + bytes key = 1; bytes value = 2; } - -// Define these here for compatibility but use tmlibs/common.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/auto_client.go b/lite2/auto_client.go deleted file mode 100644 index a5b2489c0..000000000 --- a/lite2/auto_client.go +++ /dev/null @@ -1,76 +0,0 @@ -package lite - -import ( - "time" - - "github.com/tendermint/tendermint/types" -) - -// AutoClient can auto update itself by fetching headers every N seconds. -type AutoClient struct { - base *Client - updatePeriod time.Duration - quit chan struct{} - - trustedHeaders chan *types.SignedHeader - errs chan error -} - -// NewAutoClient creates a new client and starts a polling goroutine. -func NewAutoClient(base *Client, updatePeriod time.Duration) *AutoClient { - c := &AutoClient{ - base: base, - updatePeriod: updatePeriod, - quit: make(chan struct{}), - trustedHeaders: make(chan *types.SignedHeader), - errs: make(chan error), - } - go c.autoUpdate() - return c -} - -// TrustedHeaders returns a channel onto which new trusted headers are posted. -func (c *AutoClient) TrustedHeaders() <-chan *types.SignedHeader { - return c.trustedHeaders -} - -// Err returns a channel onto which errors are posted. -func (c *AutoClient) Errs() <-chan error { - return c.errs -} - -// Stop stops the client. -func (c *AutoClient) Stop() { - close(c.quit) -} - -func (c *AutoClient) autoUpdate() { - ticker := time.NewTicker(c.updatePeriod) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - lastTrustedHeight, err := c.base.LastTrustedHeight() - if err != nil { - c.errs <- err - continue - } - - if lastTrustedHeight == -1 { - // no headers yet => wait - continue - } - - h, err := c.base.VerifyHeaderAtHeight(lastTrustedHeight+1, time.Now()) - if err != nil { - // no header yet or verification error => try again after updatePeriod - c.errs <- err - continue - } - c.trustedHeaders <- h - case <-c.quit: - return - } - } -} diff --git a/lite2/auto_client_test.go b/lite2/auto_client_test.go deleted file mode 100644 index d80c60a9a..000000000 --- a/lite2/auto_client_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package lite - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - dbm "github.com/tendermint/tm-db" - - "github.com/tendermint/tendermint/libs/log" - mockp "github.com/tendermint/tendermint/lite2/provider/mock" - dbs "github.com/tendermint/tendermint/lite2/store/db" - "github.com/tendermint/tendermint/types" -) - -func TestAutoClient(t *testing.T) { - const ( - chainID = "TestAutoClient" - ) - - var ( - keys = genPrivKeys(4) - // 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do! - vals = keys.ToValidators(20, 10) - bTime = time.Now().Add(-1 * time.Hour) - header = keys.GenSignedHeader(chainID, 1, bTime, nil, vals, vals, - []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)) - ) - - base, err := NewClient( - chainID, - TrustOptions{ - Period: 4 * time.Hour, - Height: 1, - Hash: header.Hash(), - }, - mockp.New( - chainID, - map[int64]*types.SignedHeader{ - // trusted header - 1: header, - // interim header (3/3 signed) - 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)), - // last header (3/3 signed) - 3: keys.GenSignedHeader(chainID, 3, bTime.Add(1*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, - }, - ), - dbs.New(dbm.NewMemDB(), chainID), - ) - require.NoError(t, err) - base.SetLogger(log.TestingLogger()) - - c := NewAutoClient(base, 1*time.Second) - defer c.Stop() - - for i := 2; i <= 3; i++ { - select { - case h := <-c.TrustedHeaders(): - assert.EqualValues(t, i, h.Height) - case err := <-c.Errs(): - require.NoError(t, err) - case <-time.After(2 * time.Second): - t.Fatal("no headers/errors received in 2 sec") - } - } -} diff --git a/lite2/client.go b/lite2/client.go index b705a932b..b5ffca78c 100644 --- a/lite2/client.go +++ b/lite2/client.go @@ -3,52 +3,27 @@ package lite import ( "bytes" "fmt" + "math/rand" + "sync" "time" "github.com/pkg/errors" "github.com/tendermint/tendermint/libs/log" tmmath "github.com/tendermint/tendermint/libs/math" - tmrand "github.com/tendermint/tendermint/libs/rand" "github.com/tendermint/tendermint/lite2/provider" "github.com/tendermint/tendermint/lite2/store" "github.com/tendermint/tendermint/types" ) -// TrustOptions are the trust parameters needed when a new light client -// connects to the network or when an existing light client that has been -// offline for longer than the trusting period connects to the network. -// -// The expectation is the user will get this information from a trusted source -// like a validator, a friend, or a secure website. A more user friendly -// solution with trust tradeoffs is that we establish an https based protocol -// with a default end point that populates this information. Also an on-chain -// registry of roots-of-trust (e.g. on the Cosmos Hub) seems likely in the -// future. -type TrustOptions struct { - // tp: trusting period. - // - // Should be significantly less than the unbonding period (e.g. unbonding - // period = 3 weeks, trusting period = 2 weeks). - // - // More specifically, trusting period + time needed to check headers + time - // needed to report and punish misbehavior should be less than the unbonding - // period. - Period time.Duration - - // Header's Height and Hash must both be provided to force the trusting of a - // particular header. - Height int64 - Hash []byte -} - type mode byte const ( sequential mode = iota + 1 skipping - defaultRemoveNoLongerTrustedHeadersPeriod = 24 * time.Hour + defaultPruningSize = 1000 + defaultMaxRetryAttempts = 10 ) // Option sets a parameter for the light client. @@ -73,30 +48,20 @@ func SequentialVerification() Option { // applies to non-adjacent headers. For adjacent headers, sequential // verification is used. func SkippingVerification(trustLevel tmmath.Fraction) Option { - if err := ValidateTrustLevel(trustLevel); err != nil { - panic(err) - } return func(c *Client) { c.verificationMode = skipping c.trustLevel = trustLevel } } -// AlternativeSources option can be used to supply alternative providers, which -// will be used for cross-checking the primary provider. -func AlternativeSources(providers []provider.Provider) 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.alternatives = providers - } -} - -// 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 } } @@ -109,6 +74,21 @@ func ConfirmationFunction(fn func(action string) bool) Option { } } +// Logger option can be used to set a logger for the client. +func Logger(l log.Logger) Option { + return func(c *Client) { + c.logger = l + } +} + +// MaxRetryAttempts option can be used to set max attempts before replacing +// primary with a witness. +func MaxRetryAttempts(max uint16) Option { + return func(c *Client) { + c.maxRetryAttempts = max + } +} + // Client represents a light client, connected to a single chain, which gets // headers from a primary provider, verifies them either sequentially or by // skipping some and stores them in a trusted store (usually, a local FS). @@ -119,26 +99,30 @@ type Client struct { trustingPeriod time.Duration // see TrustOptions.Period verificationMode mode trustLevel tmmath.Fraction + maxRetryAttempts uint16 // see MaxRetryAttempts option + // Mutex for locking during changes of the lite clients providers + providerMutex sync.Mutex // Primary provider of new headers. primary provider.Provider - - // Alternative providers for checking the primary for misbehavior by - // comparing data. - alternatives []provider.Provider + // See Witnesses option + witnesses []provider.Provider // 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 - - removeNoLongerTrustedHeadersPeriod time.Duration + latestTrustedHeader *types.SignedHeader + // Highest validator set from the store (height=H). + latestTrustedVals *types.ValidatorSet + // See RemoveNoLongerTrustedHeadersPeriod option + pruningSize uint16 + // See ConfirmationFunction option confirmationFn func(action string) bool - quit chan struct{} + //nolint:unused,structcheck + routinesWaitGroup sync.WaitGroup + quit chan struct{} logger log.Logger } @@ -147,55 +131,103 @@ type Client struct { // obtain the header & vals from the primary or they are invalid (e.g. trust // hash does not match with the one from the header). // +// Witnesses are providers, which will be used for cross-checking the primary +// provider. At least one witness must be given. A witness can become a primary +// iff the current primary is unavailable. +// // See all Option(s) for the additional configuration. func NewClient( chainID string, trustOptions TrustOptions, primary provider.Provider, + witnesses []provider.Provider, trustedStore store.Store, options ...Option) (*Client, error) { - c := &Client{ - chainID: chainID, - trustingPeriod: trustOptions.Period, - verificationMode: skipping, - trustLevel: DefaultTrustLevel, - primary: primary, - trustedStore: trustedStore, - removeNoLongerTrustedHeadersPeriod: defaultRemoveNoLongerTrustedHeadersPeriod, - confirmationFn: func(action string) bool { return true }, - quit: make(chan struct{}), - logger: log.NewNopLogger(), - } - - for _, o := range options { - o(c) + if err := trustOptions.ValidateBasic(); err != nil { + return nil, errors.Wrap(err, "invalid TrustOptions") } - if err := c.restoreTrustedHeaderAndNextVals(); err != nil { + c, err := NewClientFromTrustedStore(chainID, trustOptions.Period, primary, witnesses, trustedStore, options...) + if err != nil { 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 } } - if c.removeNoLongerTrustedHeadersPeriod > 0 { - go c.removeNoLongerTrustedHeadersRoutine() + return c, err +} + +// NewClientFromTrustedStore initializes existing client from the trusted store. +// +// See NewClient +func NewClientFromTrustedStore( + chainID string, + trustingPeriod time.Duration, + primary provider.Provider, + witnesses []provider.Provider, + trustedStore store.Store, + options ...Option) (*Client, error) { + + c := &Client{ + 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 { + o(c) + } + + // Validate the number of witnesses. + if len(c.witnesses) < 1 { + return nil, errors.New("expected at least one witness") + } + + // Verify witnesses are all on the same chain. + for i, w := range witnesses { + if w.ChainID() != chainID { + return nil, errors.Errorf("witness #%d: %v is on another chain %s, expected %s", + i, w, w.ChainID(), chainID) + } + } + + // Validate trust level. + if err := ValidateTrustLevel(c.trustLevel); err != nil { + return nil, err + } + + 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") @@ -207,15 +239,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 @@ -242,45 +274,47 @@ 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.primary.SignedHeader(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, - "trusted", c.trustedHeader.Height) + "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) - // set c.trustedHeader to one at options.Height - c.restoreTrustedHeaderAndNextVals() + // remove all the headers (options.Height, trustedHeader.Height] + 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) } else { - return errors.New("rollback aborted") + return nil } 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 { @@ -294,15 +328,19 @@ 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.primary.SignedHeader(options.Height) + h, err := c.signedHeaderFromPrimary(options.Height) if err != nil { return err } - // NOTE: Verify func will check if it's expired or not. + // NOTE: - Verify func will check if it's expired or not. + // - h.Time is not being checked against time.Now() because we don't + // want to add yet another argument to NewClient* functions. if err := h.ValidateBasic(c.chainID); err != nil { return err } @@ -311,106 +349,149 @@ 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.primary.ValidatorSet(options.Height) + vals, err := c.validatorSetFromPrimary(options.Height) if err != nil { return err } + if !bytes.Equal(h.ValidatorsHash, vals.Hash()) { return errors.Errorf("expected header's validators (%X) to match those that were supplied (%X)", h.ValidatorsHash, vals.Hash(), ) } + // Ensure that +2/3 of validators signed correctly. err = vals.VerifyCommit(c.chainID, h.Commit.BlockID, h.Height, h.Commit) if err != nil { return errors.Wrap(err, "invalid commit") } - // 3) Fetch and verify the next vals (verification happens in - // updateTrustedHeaderAndVals). - nextVals, err := c.primary.ValidatorSet(options.Height + 1) - if err != nil { - return err - } - - // 4) Persist both of them and continue. - return c.updateTrustedHeaderAndVals(h, nextVals) -} - -// Stop stops the light client. -func (c *Client) Stop() { - close(c.quit) -} - -// SetLogger sets a logger. -func (c *Client) SetLogger(l log.Logger) { - c.logger = l + // 3) Persist both of them and continue. + return c.updateTrustedHeaderAndVals(h, vals) } -// TrustedHeader returns a trusted header at the given height (0 - the latest) -// or nil if no such header exist. +// TrustedHeader returns a trusted header at the given height (0 - the latest). // -// Headers, which can't be trusted anymore, are removed once a day (can be -// changed with RemoveNoLongerTrustedHeadersPeriod option). +// Headers along with validator sets, which can't be trusted anymore, are +// removed once a day (can be changed with RemoveNoLongerTrustedHeadersPeriod +// option). // . // 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. -func (c *Client) TrustedHeader(height int64, now time.Time) (*types.SignedHeader, error) { - if height < 0 { - return nil, errors.New("negative height") +// - negative height is passed; +// - 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) (*types.SignedHeader, error) { + height, err := c.compareWithLatestHeight(height) + if err != nil { + return nil, err } + return c.trustedStore.SignedHeader(height) +} - if height == 0 { - var err error - height, err = c.LastTrustedHeight() - if err != nil { - return nil, err - } +// 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 are +// removed once a day (can be changed with RemoveNoLongerTrustedHeadersPeriod +// option). +// +// 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) (valSet *types.ValidatorSet, heightUsed int64, err error) { + heightUsed, err = c.compareWithLatestHeight(height) + if err != nil { + return nil, heightUsed, err } + valSet, err = c.trustedStore.ValidatorSet(heightUsed) + if err != nil { + return nil, heightUsed, err + } + return valSet, heightUsed, err +} - h, err := c.trustedStore.SignedHeader(height) +func (c *Client) compareWithLatestHeight(height int64) (int64, error) { + latestHeight, err := c.LastTrustedHeight() if err != nil { - return nil, err + return 0, errors.Wrap(err, "can't get last trusted height") } - if h == nil { - return nil, nil + if latestHeight == -1 { + return 0, errors.New("no headers exist") } - // Ensure header can still be trusted. - if HeaderExpired(h, c.trustingPeriod, now) { - return nil, ErrOldHeaderExpired{h.Time.Add(c.trustingPeriod), now} + 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 h, nil + return height, nil } // LastTrustedHeight returns a last trusted height. -1 and nil are returned if // there are no trusted headers. +// +// Safe for concurrent use by multiple goroutines. func (c *Client) LastTrustedHeight() (int64, error) { return c.trustedStore.LastSignedHeaderHeight() } +// FirstTrustedHeight returns a first trusted height. -1 and nil are returned if +// there are no trusted headers. +// +// Safe for concurrent use by multiple goroutines. +func (c *Client) FirstTrustedHeight() (int64, error) { + return c.trustedStore.FirstSignedHeaderHeight() +} + // ChainID returns the chain ID the light client was configured with. +// +// Safe for concurrent use by multiple goroutines. func (c *Client) ChainID() string { return c.chainID } -// 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. +// 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) { - c.logger.Info("VerifyHeaderAtHeight", "height", height) + if height <= 0 { + return nil, errors.New("negative or zero height") + } - if c.trustedHeader.Height >= height { - return nil, errors.Errorf("header at more recent height #%d exists", c.trustedHeader.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. @@ -419,10 +500,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 @@ -434,294 +517,521 @@ 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. func (c *Client) VerifyHeader(newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error { - c.logger.Info("VerifyHeader", "height", newHeader.Hash(), "newVals", newVals.Hash()) - - if c.trustedHeader.Height >= newHeader.Height { - return errors.Errorf("header at more recent height #%d exists", c.trustedHeader.Height) + if newHeader.Height <= 0 { + return errors.New("negative or zero height") } - if len(c.alternatives) > 0 { - if err := c.compareNewHeaderWithRandomAlternative(newHeader); err != nil { - return err + // 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())) + var err error - switch c.verificationMode { - case sequential: - err = c.sequence(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) return err } - // Update trusted header and vals. - nextVals, err := c.primary.ValidatorSet(newHeader.Height + 1) - if err != nil { + if err := c.compareNewHeaderWithWitnesses(newHeader); err != nil { + c.logger.Error("Error when comparing new header with witnesses", "err", err) return err } - return c.updateTrustedHeaderAndVals(newHeader, nextVals) + + return c.updateTrustedHeaderAndVals(newHeader, newVals) +} + +// Primary returns the primary provider. +// +// NOTE: provider may be not safe for concurrent access. +func (c *Client) Primary() provider.Provider { + c.providerMutex.Lock() + defer c.providerMutex.Unlock() + return c.primary +} + +// Witnesses returns the witness providers. +// +// NOTE: providers may be not safe for concurrent access. +func (c *Client) Witnesses() []provider.Provider { + c.providerMutex.Lock() + defer c.providerMutex.Unlock() + return c.witnesses } -// Cleanup removes all the data (headers and validator sets) stored. +// Cleanup removes all the data (headers and validator sets) stored. Note: the +// client must be stopped at this point. func (c *Client) Cleanup() error { - c.logger.Info("Cleanup everything") - return c.cleanup(0) + c.logger.Info("Removing all the data") + c.latestTrustedHeader = nil + c.latestTrustedVals = nil + return c.trustedStore.Prune(0) } -// stopHeight=0 -> remove all data -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") - } + 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) + } - // 3) Remove all headers and validator sets. - if stopHeight == 0 { - stopHeight = oldestHeight - } - for height := stopHeight; height <= latestHeight; height++ { - err = c.trustedStore.DeleteSignedHeaderAndNextValidatorSet(height) + err = c.trustedStore.DeleteSignedHeaderAndValidatorSet(h.Height) if err != nil { - c.logger.Error("can't remove a trusted header & validator set", "err", err, "height", height) - continue + 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 + c.latestTrustedHeader = nil + c.latestTrustedVals = nil + err := c.restoreTrustedHeaderAndVals() + if err != nil { + return err + } return nil } // see VerifyHeader -func (c *Client) sequence(newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error { - // 1) Verify any intermediate headers. +func (c *Client) sequence( + initiallyTrustedHeader *types.SignedHeader, + newHeader *types.SignedHeader, + newVals *types.ValidatorSet, + now time.Time) error { + var ( + trustedHeader = initiallyTrustedHeader + interimHeader *types.SignedHeader - nextVals *types.ValidatorSet - err error - ) - for height := c.trustedHeader.Height + 1; height < newHeader.Height; height++ { - interimHeader, err = c.primary.SignedHeader(height) - if err != nil { - return errors.Wrapf(err, "failed to obtain the header #%d", height) - } + interimVals *types.ValidatorSet - c.logger.Debug("Verify newHeader against lastHeader", - "lastHeight", c.trustedHeader.Height, - "lastHash", c.trustedHeader.Hash(), - "newHeight", interimHeader.Height, - "newHash", interimHeader.Hash()) - err = Verify(c.chainID, c.trustedHeader, c.trustedNextVals, interimHeader, c.trustedNextVals, - c.trustingPeriod, now, c.trustLevel) - if err != nil { - return errors.Wrapf(err, "failed to verify the header #%d", height) - } + err error + ) - // Update trusted header and vals. - if height == newHeader.Height-1 { - nextVals = newVals - } else { - nextVals, err = c.primary.ValidatorSet(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.updateTrustedHeaderAndVals(interimHeader, nextVals) + + // 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 + } } + + // 3) Update trustedHeader + trustedHeader = interimHeader } - // 2) Verify the new header. - return Verify(c.chainID, c.trustedHeader, c.trustedNextVals, newHeader, newVals, c.trustingPeriod, now, c.trustLevel) + return nil } // see VerifyHeader func (c *Client) bisection( - lastHeader *types.SignedHeader, - lastVals *types.ValidatorSet, + initiallyTrustedHeader *types.SignedHeader, + initiallyTrustedVals *types.ValidatorSet, newHeader *types.SignedHeader, newVals *types.ValidatorSet, now time.Time) error { - c.logger.Debug("Verify newHeader against lastHeader", - "lastHeight", lastHeader.Height, - "lastHash", lastHeader.Hash(), - "newHeight", newHeader.Height, - "newHash", newHeader.Hash()) - err := Verify(c.chainID, lastHeader, lastVals, newHeader, newVals, c.trustingPeriod, now, c.trustLevel) - switch err.(type) { - case nil: - return nil - case ErrNewValSetCantBeTrusted: - // continue bisection - default: - return errors.Wrapf(err, "failed to verify the header #%d", newHeader.Height) - } + var ( + trustedHeader = initiallyTrustedHeader + trustedVals = initiallyTrustedVals - pivot := (c.trustedHeader.Height + newHeader.Header.Height) / 2 - pivotHeader, pivotVals, err := c.fetchHeaderAndValsAtHeight(pivot) - if err != nil { - return err - } + interimHeader = newHeader + interimVals = newVals + ) - // left branch - { - err := c.bisection(lastHeader, lastVals, pivotHeader, pivotVals, now) - if err != nil { - return errors.Wrapf(err, "bisection of #%d and #%d", lastHeader.Height, pivot) - } - } + for { + c.logger.Debug("Verify newHeader against trustedHeader", + "trustedHeight", trustedHeader.Height, + "trustedHash", hash2str(trustedHeader.Hash()), + "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 + } - // right branch - { - nextVals, err := c.primary.ValidatorSet(pivot + 1) - if err != nil { - return errors.Wrapf(err, "failed to obtain the vals #%d", pivot+1) - } - if !bytes.Equal(pivotHeader.NextValidatorsHash, nextVals.Hash()) { - return errors.Errorf("expected next validator's hash %X, but got %X (height #%d)", - pivotHeader.NextValidatorsHash, - nextVals.Hash(), - pivot) - } + // Update the lower bound to the previous upper bound + trustedHeader, trustedVals = interimHeader, interimVals + // Update the upper bound to the untrustedHeader + interimHeader, interimVals = newHeader, newVals - err = c.updateTrustedHeaderAndVals(pivotHeader, nextVals) - if err != nil { - return errors.Wrapf(err, "failed to update trusted state #%d", pivot) - } + case ErrNewValSetCantBeTrusted: + pivotHeight := (interimHeader.Height + trustedHeader.Height) / 2 + interimHeader, interimVals, err = c.fetchHeaderAndValsAtHeight(pivotHeight) + if err != nil { + return err + } - err = c.bisection(pivotHeader, nextVals, newHeader, newVals, now) - if err != nil { - return errors.Wrapf(err, "bisection of #%d and #%d", pivot, newHeader.Height) + 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, "verify from #%d to #%d failed", + trustedHeader.Height, interimHeader.Height) } } - - return nil } -// persist header and next validators to trustedStore. -func (c *Client) updateTrustedHeaderAndVals(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 } -// fetch header and validators for the given height from primary provider. +// fetch header and validators for the given height (0 - latest) from primary +// provider. func (c *Client) fetchHeaderAndValsAtHeight(height int64) (*types.SignedHeader, *types.ValidatorSet, error) { - h, err := c.primary.SignedHeader(height) + h, err := c.signedHeaderFromPrimary(height) if err != nil { return nil, nil, errors.Wrapf(err, "failed to obtain the header #%d", height) } - vals, err := c.primary.ValidatorSet(height) + vals, err := c.validatorSetFromPrimary(height) if err != nil { return nil, nil, errors.Wrapf(err, "failed to obtain the vals #%d", height) } return h, vals, nil } -// compare header with one from a random alternative provider. -func (c *Client) compareNewHeaderWithRandomAlternative(h *types.SignedHeader) error { - // 1. Pick an alternative provider. - p := c.alternatives[tmrand.Intn(len(c.alternatives))] +// 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 { - // 2. Fetch the header. - altHeader, err := p.SignedHeader(h.Height) - if err != nil { - return errors.Wrapf(err, - "failed to obtain header #%d from alternative provider %v", h.Height, p) + if HeaderExpired(initiallyTrustedHeader, c.trustingPeriod, now) { + return ErrOldHeaderExpired{initiallyTrustedHeader.Time.Add(c.trustingPeriod), now} + } + + var ( + trustedHeader = initiallyTrustedHeader + interimHeader *types.SignedHeader + err error + ) + + for trustedHeader.Height > newHeader.Height { + interimHeader, err = c.signedHeaderFromPrimary(trustedHeader.Height - 1) + if err != nil { + return errors.Wrapf(err, "failed to obtain the header at height #%d", trustedHeader.Height-1) + } + + if err := VerifyBackwards(c.chainID, interimHeader, trustedHeader); err != nil { + c.logger.Error("primary sent invalid header -> replacing", "err", err) + if replaceErr := c.replacePrimaryProvider(); replaceErr != nil { + c.logger.Error("Can't replace primary", "err", replaceErr) + // return original error + return errors.Wrapf(err, "verify backwards from %d to %d failed", + trustedHeader.Height, interimHeader.Height) + } + } + + trustedHeader = interimHeader } - // 3. Compare hashes. - if !bytes.Equal(h.Hash(), altHeader.Hash()) { - // TODO: One of the providers is lying. Send the evidence to fork - // accountability server. - return errors.Errorf( - "new header hash %X does not match one from alternative provider %X", - h.Hash(), altHeader.Hash()) + // 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 } -func (c *Client) removeNoLongerTrustedHeadersRoutine() { - ticker := time.NewTicker(c.removeNoLongerTrustedHeadersPeriod) - defer ticker.Stop() +// compare header with all witnesses provided. +func (c *Client) compareNewHeaderWithWitnesses(h *types.SignedHeader) error { + c.providerMutex.Lock() + defer c.providerMutex.Unlock() + + // 1. Make sure AT LEAST ONE witness returns the same header. + headerMatched := false + witnessesToRemove := make([]int, 0) + for attempt := uint16(1); attempt <= c.maxRetryAttempts; attempt++ { + if len(c.witnesses) == 0 { + return errors.New("could not find any witnesses. please reset the light client") + } - for { - select { - case <-ticker.C: - c.RemoveNoLongerTrustedHeaders(time.Now()) - case <-c.quit: - return + for i, witness := range c.witnesses { + altH, err := witness.SignedHeader(h.Height) + if err != nil { + c.logger.Error("Failed to get a header from witness", "height", h.Height, "witness", witness) + continue + } + + if err = altH.ValidateBasic(c.chainID); err != nil { + c.logger.Error("Witness sent us incorrect header", "err", err, "witness", witness) + witnessesToRemove = append(witnessesToRemove, i) + continue + } + + if !bytes.Equal(h.Hash(), altH.Hash()) { + 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) + continue + } + + // TODO: send the diverged headers to primary && all witnesses + + return errors.Errorf( + "header hash %X does not match one %X from the witness %v", + h.Hash(), altH.Hash(), witness) + } + + headerMatched = true + } + + for _, idx := range witnessesToRemove { + c.removeWitness(idx) } + witnessesToRemove = make([]int, 0) + + if headerMatched { + return nil + } + + // 2. Otherwise, sleep + time.Sleep(backoffTimeout(attempt)) } + + return errors.New("awaiting response from all witnesses exceeded dropout time") } -// 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.trustedStore.FirstSignedHeaderHeight() +// NOTE: requires a providerMutex locked. +func (c *Client) removeWitness(idx int) { + switch len(c.witnesses) { + case 0: + panic(fmt.Sprintf("wanted to remove %d element from empty witnesses slice", idx)) + case 1: + c.witnesses = make([]provider.Provider, 0) + default: + c.witnesses[idx] = c.witnesses[len(c.witnesses)-1] + c.witnesses = c.witnesses[:len(c.witnesses)-1] + } +} + +// 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 { - c.logger.Error("can't get first trusted height", "err", err) - return + return nil, errors.Wrap(err, "can't get last trusted height") } - // 2) Get the latest height. - latestHeight, err := c.LastTrustedHeight() + if lastTrustedHeight == -1 { + // no headers yet => wait + return nil, nil + } + + latestHeader, latestVals, err := c.fetchHeaderAndValsAtHeight(0) if err != nil { - c.logger.Error("can't get last trusted height", "err", err) - return + return nil, errors.Wrapf(err, "can't get latest header and vals") } - // 3) Remove all headers that are outside of the trusting period. - for height := oldestHeight; height <= latestHeight; height++ { - h, err := c.trustedStore.SignedHeader(height) + if latestHeader.Height > lastTrustedHeight { + err = c.VerifyHeader(latestHeader, latestVals, now) if err != nil { - c.logger.Error("can't get a trusted header", "err", err, "height", height) - continue - } - if h == nil { - c.logger.Debug("attempted to remove non-existing header", "height", height) - continue + return nil, err } + c.logger.Info("Advanced to new state", "height", latestHeader.Height, "hash", hash2str(latestHeader.Hash())) + return latestHeader, nil + } - // Stop if the header is within the trusting period. - if !HeaderExpired(h, c.trustingPeriod, now) { - break + return nil, nil +} + +// replaceProvider takes the first alternative provider and promotes it as the +// primary provider. +func (c *Client) replacePrimaryProvider() error { + c.providerMutex.Lock() + defer c.providerMutex.Unlock() + + if len(c.witnesses) <= 1 { + return errors.Errorf("only one witness left. please reset the light client") + } + c.primary = c.witnesses[0] + c.witnesses = c.witnesses[1:] + c.logger.Info("New primary", "p", c.primary) + + return nil +} + +// signedHeaderFromPrimary retrieves the SignedHeader from the primary provider +// at the specified height. Handles dropout by the primary provider by swapping +// with an alternative provider. +func (c *Client) signedHeaderFromPrimary(height int64) (*types.SignedHeader, error) { + for attempt := uint16(1); attempt <= c.maxRetryAttempts; attempt++ { + c.providerMutex.Lock() + h, err := c.primary.SignedHeader(height) + c.providerMutex.Unlock() + if err == nil { + // sanity check + if height > 0 && h.Height != height { + return nil, errors.Errorf("expected %d height, got %d", height, h.Height) + } + return h, nil } + 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)) + } - err = c.trustedStore.DeleteSignedHeaderAndNextValidatorSet(height) - if err != nil { - c.logger.Error("can't remove a trusted header & validator set", "err", err, "height", height) - continue + c.logger.Info("Primary is unavailable. Replacing with the first witness") + err := c.replacePrimaryProvider() + if err != nil { + return nil, err + } + + return c.signedHeaderFromPrimary(height) +} + +// validatorSetFromPrimary retrieves the ValidatorSet from the primary provider +// at the specified height. Handles dropout by the primary provider after 5 +// attempts by replacing it with an alternative provider. +func (c *Client) validatorSetFromPrimary(height int64) (*types.ValidatorSet, error) { + for attempt := uint16(1); attempt <= c.maxRetryAttempts; attempt++ { + c.providerMutex.Lock() + vals, err := c.primary.ValidatorSet(height) + c.providerMutex.Unlock() + 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)) + } + + c.logger.Info("Primary is unavailable. Replacing with the first witness") + err := c.replacePrimaryProvider() + if err != nil { + return nil, err } + + return c.validatorSetFromPrimary(height) +} + +// exponential backoff (with jitter) +// 0.5s -> 2s -> 4.5s -> 8s -> 12.5 with 1s variation +func backoffTimeout(attempt uint16) time.Duration { + return time.Duration(500*attempt*attempt)*time.Millisecond + time.Duration(rand.Intn(1000))*time.Millisecond +} + +func hash2str(hash []byte) string { + return fmt.Sprintf("%X", hash) } diff --git a/lite2/client_test.go b/lite2/client_test.go index c7bafd6bb..65ea55122 100644 --- a/lite2/client_test.go +++ b/lite2/client_test.go @@ -1,6 +1,7 @@ package lite import ( + "sync" "testing" "time" @@ -10,54 +11,75 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/lite2/provider" mockp "github.com/tendermint/tendermint/lite2/provider/mock" dbs "github.com/tendermint/tendermint/lite2/store/db" "github.com/tendermint/tendermint/types" ) -func TestClient_SequentialVerification(t *testing.T) { - const ( - chainID = "sequential-verification" - ) +const ( + chainID = "test" +) - var ( - keys = genPrivKeys(4) - // 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do! - vals = keys.ToValidators(20, 10) - bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") - header = keys.GenSignedHeader(chainID, 1, bTime, nil, vals, vals, - []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)) +var ( + keys = genPrivKeys(4) + vals = keys.ToValidators(20, 10) + bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") + h1 = keys.GenSignedHeader(chainID, 1, bTime, nil, vals, vals, + []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)) + // 3/3 signed + h2 = keys.GenSignedHeaderLastBlockID(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals, + []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys), types.BlockID{Hash: h1.Hash()}) + // 3/3 signed + h3 = keys.GenSignedHeaderLastBlockID(chainID, 3, bTime.Add(1*time.Hour), nil, vals, vals, + []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys), types.BlockID{Hash: h2.Hash()}) + trustPeriod = 4 * time.Hour + trustOptions = TrustOptions{ + Period: 4 * time.Hour, + 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, + 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 ^ vals map[int64]*types.ValidatorSet initErr bool verifyErr bool }{ - // good { - map[int64]*types.SignedHeader{ - // trusted header - 1: header, - // interim header (3/3 signed) - 2: keys.GenSignedHeader(chainID, 2, bTime.Add(1*time.Hour), nil, vals, vals, - []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)), - // last header (3/3 signed) - 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, - }, + "good", + headerSet, + valSet, false, false, }, - // bad: different first header { + "bad: different first header", map[int64]*types.SignedHeader{ // different header 1: keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Hour), nil, vals, vals, @@ -69,11 +91,11 @@ func TestClient_SequentialVerification(t *testing.T) { true, false, }, - // bad: 1/3 signed interim header { + "bad: 1/3 signed interim header", map[int64]*types.SignedHeader{ // trusted header - 1: header, + 1: h1, // interim header (1/3 signed) 2: keys.GenSignedHeader(chainID, 2, bTime.Add(1*time.Hour), nil, vals, vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), len(keys)-1, len(keys)), @@ -81,20 +103,15 @@ 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, }, - // bad: 1/3 signed last header { + "bad: 1/3 signed last header", map[int64]*types.SignedHeader{ // trusted header - 1: header, + 1: h1, // interim header (3/3 signed) 2: keys.GenSignedHeader(chainID, 2, bTime.Add(1*time.Hour), nil, vals, vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)), @@ -102,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, @@ -114,86 +137,91 @@ func TestClient_SequentialVerification(t *testing.T) { } for _, tc := range testCases { - c, err := NewClient( - chainID, - TrustOptions{ - Period: 4 * time.Hour, - Height: 1, - Hash: header.Hash(), - }, - mockp.New( + tc := tc + t.Run(tc.name, func(t *testing.T) { + c, err := NewClient( chainID, - tc.otherHeaders, - tc.vals, - ), - dbs.New(dbm.NewMemDB(), chainID), - SequentialVerification(), - ) + trustOptions, + mockp.New( + chainID, + tc.otherHeaders, + tc.vals, + ), + []provider.Provider{mockp.New( + chainID, + tc.otherHeaders, + tc.vals, + )}, + dbs.New(dbm.NewMemDB(), chainID), + SequentialVerification(), + ) + + if tc.initErr { + require.Error(t, err) + return + } - if tc.initErr { - require.Error(t, err) - continue - } else { require.NoError(t, err) - } - defer c.Stop() - _, err = c.VerifyHeaderAtHeight(3, bTime.Add(3*time.Hour)) - if tc.verifyErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } + _, err = c.VerifyHeaderAtHeight(3, bTime.Add(3*time.Hour)) + if tc.verifyErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) } } func TestClient_SkippingVerification(t *testing.T) { - const ( - chainID = "skipping-verification" - ) - - var ( - keys = genPrivKeys(4) - // 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do! - vals = keys.ToValidators(20, 10) - bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") - header = keys.GenSignedHeader(chainID, 1, bTime, nil, vals, vals, - []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)) - ) - // required for 2nd test case newKeys := genPrivKeys(4) newVals := newKeys.ToValidators(10, 1) + // 1/3+ of vals, 2/3- of newVals + transitKeys := keys.Extend(3) + transitVals := transitKeys.ToValidators(10, 1) + testCases := []struct { + name string otherHeaders map[int64]*types.SignedHeader // all except ^ vals map[int64]*types.ValidatorSet initErr bool verifyErr bool }{ - // good { + "good", map[int64]*types.SignedHeader{ // trusted header - 1: header, + 1: h1, // last header (3/3 signed) - 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)), + 3: h3, + }, + valSet, + false, + false, + }, + { + "good, but val set changes by 2/3 (1/3 of vals is still present)", + map[int64]*types.SignedHeader{ + // trusted header + 1: h1, + 3: transitKeys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, transitVals, transitVals, + []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(transitKeys)), }, map[int64]*types.ValidatorSet{ 1: vals, 2: vals, - 3: vals, - 4: vals, + 3: transitVals, }, false, false, }, - // good, val set changes 100% at height 2 { + "good, but val set changes 100% at height 2", map[int64]*types.SignedHeader{ // trusted header - 1: header, + 1: h1, // interim header (3/3 signed) 2: keys.GenSignedHeader(chainID, 2, bTime.Add(1*time.Hour), nil, vals, newVals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)), @@ -205,218 +233,144 @@ func TestClient_SkippingVerification(t *testing.T) { 1: vals, 2: vals, 3: newVals, - 4: newVals, }, false, false, }, - } - - for _, tc := range testCases { - c, err := NewClient( - chainID, - TrustOptions{ - Period: 4 * time.Hour, - Height: 1, - Hash: header.Hash(), - }, - mockp.New( - chainID, - tc.otherHeaders, - tc.vals, - ), - dbs.New(dbm.NewMemDB(), chainID), - SkippingVerification(DefaultTrustLevel), - ) - if tc.initErr { - require.Error(t, err) - continue - } else { - require.NoError(t, err) - } - defer c.Stop() - - _, err = c.VerifyHeaderAtHeight(3, bTime.Add(3*time.Hour)) - if tc.verifyErr { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - } -} - -func TestClientRemovesNoLongerTrustedHeaders(t *testing.T) { - const ( - chainID = "TestClientRemovesNoLongerTrustedHeaders" - ) - - var ( - keys = genPrivKeys(4) - // 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do! - vals = keys.ToValidators(20, 10) - bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") - header = keys.GenSignedHeader(chainID, 1, bTime, nil, vals, vals, - []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)) - ) - - c, err := NewClient( - chainID, - TrustOptions{ - Period: 4 * time.Hour, - Height: 1, - Hash: header.Hash(), - }, - mockp.New( - chainID, + { + "bad: last header signed by newVals, interim header has no signers", map[int64]*types.SignedHeader{ // trusted header - 1: header, - // interim header (3/3 signed) - 2: keys.GenSignedHeader(chainID, 2, bTime.Add(2*time.Hour), nil, vals, vals, - []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)), - // last header (3/3 signed) - 3: keys.GenSignedHeader(chainID, 3, bTime.Add(4*time.Hour), nil, vals, vals, - []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)), + 1: h1, + // last header (0/4 of the original val set signed) + 2: keys.GenSignedHeader(chainID, 2, bTime.Add(1*time.Hour), nil, vals, newVals, + []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, 0), + // last header (0/4 of the original val set signed) + 3: newKeys.GenSignedHeader(chainID, 3, bTime.Add(2*time.Hour), nil, newVals, newVals, + []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(newKeys)), }, map[int64]*types.ValidatorSet{ 1: vals, 2: vals, - 3: vals, - 4: vals, + 3: newVals, }, - ), - dbs.New(dbm.NewMemDB(), chainID), - ) - require.NoError(t, err) - defer c.Stop() - c.SetLogger(log.TestingLogger()) - - // 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) + false, + true, + }, + } - // Remove expired headers. - c.RemoveNoLongerTrustedHeaders(now) + for _, tc := range testCases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + c, err := NewClient( + chainID, + trustOptions, + mockp.New( + chainID, + tc.otherHeaders, + tc.vals, + ), + []provider.Provider{mockp.New( + chainID, + tc.otherHeaders, + tc.vals, + )}, + dbs.New(dbm.NewMemDB(), chainID), + SkippingVerification(DefaultTrustLevel), + ) + if tc.initErr { + require.Error(t, err) + return + } - // Check expired headers are no longer available. - h, err := c.TrustedHeader(1, now) - assert.NoError(t, err) - assert.Nil(t, h) + require.NoError(t, err) - // Check not expired headers are available. - h, err = c.TrustedHeader(2, now) - assert.NoError(t, err) - assert.NotNil(t, h) + _, err = c.VerifyHeaderAtHeight(3, bTime.Add(3*time.Hour)) + if tc.verifyErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } } func TestClient_Cleanup(t *testing.T) { - const ( - chainID = "TestClient_Cleanup" - ) - - var ( - keys = genPrivKeys(4) - // 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do! - vals = keys.ToValidators(20, 10) - bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") - header = keys.GenSignedHeader(chainID, 1, bTime, nil, vals, vals, - []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)) - ) - c, err := NewClient( chainID, - TrustOptions{ - Period: 4 * time.Hour, - Height: 1, - Hash: header.Hash(), - }, - mockp.New( - chainID, - map[int64]*types.SignedHeader{ - // trusted header - 1: header, - }, - map[int64]*types.ValidatorSet{ - 1: vals, - 2: vals, - }, - ), + trustOptions, + fullNode, + []provider.Provider{fullNode}, dbs.New(dbm.NewMemDB(), chainID), + Logger(log.TestingLogger()), ) require.NoError(t, err) - c.SetLogger(log.TestingLogger()) + _, err = c.TrustedHeader(1) + require.NoError(t, err) - 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)) - assert.NoError(t, err) + // 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 -func TestClientRestoreTrustedHeaderAfterStartup1(t *testing.T) { - const ( - chainID = "TestClientRestoreTrustedHeaderAfterStartup1" - ) - - var ( - keys = genPrivKeys(4) - // 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do! - vals = keys.ToValidators(20, 10) - bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") - header = keys.GenSignedHeader(chainID, 1, bTime, nil, vals, vals, - []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)) - ) - +func TestClientRestoresTrustedHeaderAfterStartup1(t *testing.T) { // 1. options.Hash == trustedHeader.Hash { trustedStore := dbs.New(dbm.NewMemDB(), chainID) - err := trustedStore.SaveSignedHeaderAndNextValidatorSet(header, vals) + err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals) require.NoError(t, err) c, err := NewClient( chainID, - TrustOptions{ - Period: 4 * time.Hour, - Height: 1, - Hash: header.Hash(), - }, - mockp.New( - chainID, - map[int64]*types.SignedHeader{ - // trusted header - 1: header, - }, - map[int64]*types.ValidatorSet{ - 1: vals, - 2: vals, - }, - ), + trustOptions, + fullNode, + []provider.Provider{fullNode}, trustedStore, + Logger(log.TestingLogger()), ) require.NoError(t, err) - c.SetLogger(log.TestingLogger()) - 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(), header.Hash()) + 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(header, vals) + err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals) require.NoError(t, err) // header1 != header header1 := keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Hour), nil, vals, vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)) + primary := mockp.New( + chainID, + map[int64]*types.SignedHeader{ + // trusted header + 1: header1, + }, + valSet, + ) + c, err := NewClient( chainID, TrustOptions{ @@ -424,196 +378,163 @@ func TestClientRestoreTrustedHeaderAfterStartup1(t *testing.T) { Height: 1, Hash: header1.Hash(), }, - mockp.New( - chainID, - map[int64]*types.SignedHeader{ - // trusted header - 1: header1, - }, - map[int64]*types.ValidatorSet{ - 1: vals, - 2: vals, - }, - ), + primary, + []provider.Provider{primary}, trustedStore, + Logger(log.TestingLogger()), ) require.NoError(t, err) - c.SetLogger(log.TestingLogger()) - 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()) + } } } // trustedHeader.Height < options.Height -func TestClientRestoreTrustedHeaderAfterStartup2(t *testing.T) { - const ( - chainID = "TestClientRestoreTrustedHeaderAfterStartup2" - ) - - var ( - keys = genPrivKeys(4) - // 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do! - vals = keys.ToValidators(20, 10) - bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") - header = keys.GenSignedHeader(chainID, 1, bTime, nil, vals, vals, - []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)) - ) - +func TestClientRestoresTrustedHeaderAfterStartup2(t *testing.T) { // 1. options.Hash == trustedHeader.Hash { trustedStore := dbs.New(dbm.NewMemDB(), chainID) - err := trustedStore.SaveSignedHeaderAndNextValidatorSet(header, 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)) - c, err := NewClient( chainID, TrustOptions{ Period: 4 * time.Hour, Height: 2, - Hash: header2.Hash(), + Hash: h2.Hash(), }, - mockp.New( - chainID, - map[int64]*types.SignedHeader{ - 1: header, - 2: header2, - }, - map[int64]*types.ValidatorSet{ - 1: vals, - 2: vals, - 3: vals, - }, - ), + fullNode, + []provider.Provider{fullNode}, trustedStore, + Logger(log.TestingLogger()), ) require.NoError(t, err) - c.SetLogger(log.TestingLogger()) // 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(), header.Hash()) + 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(header, vals) + err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals) require.NoError(t, err) // header1 != header - header1 := keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Hour), nil, vals, vals, + diffHeader1 := keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Hour), nil, vals, vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)) - header2 := keys.GenSignedHeader(chainID, 2, bTime.Add(2*time.Hour), nil, vals, vals, + diffHeader2 := keys.GenSignedHeader(chainID, 2, bTime.Add(2*time.Hour), nil, vals, vals, []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)) + primary := mockp.New( + chainID, + map[int64]*types.SignedHeader{ + 1: diffHeader1, + 2: diffHeader2, + }, + valSet, + ) + c, err := NewClient( chainID, TrustOptions{ Period: 4 * time.Hour, Height: 2, - Hash: header2.Hash(), + Hash: diffHeader2.Hash(), }, - mockp.New( - chainID, - map[int64]*types.SignedHeader{ - 1: header1, - 2: header2, - }, - map[int64]*types.ValidatorSet{ - 1: vals, - 2: vals, - 3: vals, - }, - ), + primary, + []provider.Provider{primary}, trustedStore, + Logger(log.TestingLogger()), ) require.NoError(t, err) - c.SetLogger(log.TestingLogger()) // Check we no longer have the invalid 1st header (+header+). - h, err := c.TrustedHeader(1, bTime.Add(2*time.Hour).Add(1*time.Second)) - assert.NoError(t, err) + 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 -func TestClientRestoreTrustedHeaderAfterStartup3(t *testing.T) { - const ( - chainID = "TestClientRestoreTrustedHeaderAfterStartup3" - ) - - var ( - keys = genPrivKeys(4) - // 20, 30, 40, 50 - the first 3 don't have 2/3, the last 3 do! - vals = keys.ToValidators(20, 10) - bTime, _ = time.Parse(time.RFC3339, "2006-01-02T15:04:05Z") - header = keys.GenSignedHeader(chainID, 1, bTime, nil, vals, vals, - []byte("app_hash"), []byte("cons_hash"), []byte("results_hash"), 0, len(keys)) - ) - +func TestClientRestoresTrustedHeaderAfterStartup3(t *testing.T) { // 1. options.Hash == trustedHeader.Hash { trustedStore := dbs.New(dbm.NewMemDB(), chainID) - err := trustedStore.SaveSignedHeaderAndNextValidatorSet(header, 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(header2, vals) + //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.SaveSignedHeaderAndValidatorSet(h2, vals) require.NoError(t, err) c, err := NewClient( chainID, - TrustOptions{ - Period: 4 * time.Hour, - Height: 1, - Hash: header.Hash(), - }, - mockp.New( - chainID, - map[int64]*types.SignedHeader{ - 1: header, - 2: header2, - }, - map[int64]*types.ValidatorSet{ - 1: vals, - 2: vals, - 3: vals, - }, - ), + trustOptions, + fullNode, + []provider.Provider{fullNode}, trustedStore, + Logger(log.TestingLogger()), ) require.NoError(t, err) - c.SetLogger(log.TestingLogger()) // 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(), header.Hash()) + assert.Equal(t, h.Hash(), h1.Hash()) - // Check we no longer have 2nd header (+header2+). - h, err = c.TrustedHeader(2, bTime.Add(2*time.Hour).Add(1*time.Second)) + 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) + 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(header, vals) + err := trustedStore.SaveSignedHeaderAndValidatorSet(h1, vals) require.NoError(t, err) // header1 != header @@ -622,9 +543,17 @@ func TestClientRestoreTrustedHeaderAfterStartup3(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( + chainID, + map[int64]*types.SignedHeader{ + 1: header1, + }, + valSet, + ) + c, err := NewClient( chainID, TrustOptions{ @@ -632,30 +561,346 @@ func TestClientRestoreTrustedHeaderAfterStartup3(t *testing.T) { Height: 1, Hash: header1.Hash(), }, - mockp.New( - chainID, - map[int64]*types.SignedHeader{ - 1: header1, - }, - map[int64]*types.ValidatorSet{ - 1: vals, - 2: vals, - }, - ), + primary, + []provider.Provider{primary}, trustedStore, + Logger(log.TestingLogger()), ) require.NoError(t, err) - c.SetLogger(log.TestingLogger()) // 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()) - // Check we no longer have invalid 2nd header (+header2+). - h, err = c.TrustedHeader(2, bTime.Add(2*time.Hour).Add(1*time.Second)) + 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) + assert.Error(t, err) assert.Nil(t, h) + + valSet, _, err = c.TrustedValidatorSet(2) + assert.Error(t, err) + assert.Nil(t, valSet) } } + +func TestClient_Update(t *testing.T) { + c, err := NewClient( + chainID, + trustOptions, + fullNode, + []provider.Provider{fullNode}, + dbs.New(dbm.NewMemDB(), chainID), + Logger(log.TestingLogger()), + ) + require.NoError(t, err) + + // should result in downloading & verifying header #3 + h, err := c.Update(bTime.Add(2 * time.Hour)) + assert.NoError(t, err) + 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) { + c, err := NewClient( + chainID, + trustOptions, + fullNode, + []provider.Provider{fullNode}, + dbs.New(dbm.NewMemDB(), chainID), + Logger(log.TestingLogger()), + ) + require.NoError(t, err) + + _, err = c.VerifyHeaderAtHeight(2, bTime.Add(2*time.Hour)) + require.NoError(t, err) + + var wg sync.WaitGroup + for i := 0; i < 100; i++ { + wg.Add(1) + go func() { + defer wg.Done() + + // NOTE: Cleanup, Stop, VerifyHeaderAtHeight and Verify are not supposed + // to be concurrenly safe. + + assert.Equal(t, chainID, c.ChainID()) + + _, err := c.LastTrustedHeight() + assert.NoError(t, err) + + _, err = c.FirstTrustedHeight() + assert.NoError(t, err) + + h, err := c.TrustedHeader(1) + assert.NoError(t, err) + assert.NotNil(t, h) + + vals, _, err := c.TrustedValidatorSet(2) + assert.NoError(t, err) + assert.NotNil(t, vals) + }() + } + + wg.Wait() +} + +func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) { + c, err := NewClient( + chainID, + trustOptions, + deadNode, + []provider.Provider{fullNode, fullNode}, + dbs.New(dbm.NewMemDB(), chainID), + Logger(log.TestingLogger()), + MaxRetryAttempts(1), + ) + + require.NoError(t, err) + _, 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_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.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) + + // 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.SaveSignedHeaderAndValidatorSet(h1, vals) + require.NoError(t, err) + + c, err := NewClientFromTrustedStore( + chainID, + trustPeriod, + deadNode, + []provider.Provider{deadNode}, + db, + ) + require.NoError(t, err) + + // 2) Check header exists (deadNode is being used to ensure we're not getting + // it from primary) + 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 TestNewClientErrorsIfAllWitnessesUnavailable(t *testing.T) { + _, err := NewClient( + chainID, + trustOptions, + fullNode, + []provider.Provider{deadNode, deadNode}, + dbs.New(dbm.NewMemDB(), chainID), + Logger(log.TestingLogger()), + MaxRetryAttempts(1), + ) + if assert.Error(t, err) { + assert.Contains(t, err.Error(), "awaiting response from all witnesses exceeded dropout time") + } +} + +func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) { + // different headers hash then primary plus less than 1/3 signed (no fork) + badProvider1 := mockp.New( + chainID, + map[int64]*types.SignedHeader{ + 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, + }, + ) + // header is empty + badProvider2 := mockp.New( + chainID, + map[int64]*types.SignedHeader{ + 1: h1, + 2: h2, + 3: {Header: nil, Commit: nil}, + }, + map[int64]*types.ValidatorSet{ + 1: vals, + 2: vals, + }, + ) + + c, err := NewClient( + chainID, + trustOptions, + fullNode, + []provider.Provider{badProvider1, badProvider2}, + dbs.New(dbm.NewMemDB(), chainID), + 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.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 920032f36..b61f5453f 100644 --- a/lite2/doc.go +++ b/lite2/doc.go @@ -65,35 +65,36 @@ 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"), - dbs.New(db, chainID), + "http://localhost:26657", + []string{"http://witness1:26657"}, + dbs.New(db, ""), ) - - err = c.VerifyHeaderAtHeight(101, time.Now()) if err != nil { - fmt.Println("retry?") + // handle error } - h, err := c.TrustedHeader(101) + h, err := c.TrustedHeader(100) if err != nil { - fmt.Println("retry?") + // handle error } - fmt.Println("got header", h) + fmt.Println("header", h) + +Check out other examples in example_test.go ## 2. Pure functions to verify a new header (see verifier.go) Verify function verifies a new header against some trusted header. See -https://github.com/tendermint/spec/blob/master/spec/consensus/light-client.md +https://github.com/tendermint/spec/blob/master/spec/consensus/light-client/verification.md for details. ## 3. Secure RPC proxy @@ -105,7 +106,7 @@ as a wrapper, which verifies all the headers, using a light client connected to some other node. See -https://github.com/tendermint/tendermint/blob/master/cmd/tendermint/commands/lite.go +https://docs.tendermint.com/master/tendermint-core/light-client-protocol.html for usage example. */ package lite 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 5afa07900..e8c3b8bb3 100644 --- a/lite2/example_test.go +++ b/lite2/example_test.go @@ -8,18 +8,17 @@ import ( "testing" "time" - "github.com/pkg/errors" - 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" rpctest "github.com/tendermint/tendermint/rpc/test" ) -func TestExample_Client(t *testing.T) { +// Automatically getting new headers and verifying them. +func ExampleClient_Update() { // give Tendermint time to generate some blocks time.Sleep(5 * time.Second) @@ -34,12 +33,12 @@ func TestExample_Client(t *testing.T) { chainID = config.ChainID() ) - provider, err := httpp.New(chainID, config.RPC.ListenAddress) + primary, err := httpp.New(chainID, config.RPC.ListenAddress) if err != nil { stdlog.Fatal(err) } - header, err := provider.SignedHeader(2) + header, err := primary.SignedHeader(2) if err != nil { stdlog.Fatal(err) } @@ -56,29 +55,39 @@ func TestExample_Client(t *testing.T) { Height: 2, Hash: header.Hash(), }, - provider, + primary, + []provider.Provider{primary}, // NOTE: primary should not be used here dbs.New(db, chainID), + // Logger(log.TestingLogger()), ) if err != nil { stdlog.Fatal(err) } - c.SetLogger(log.TestingLogger()) + defer func() { + c.Cleanup() + }() - _, err = c.VerifyHeaderAtHeight(3, time.Now()) - if err != nil { - stdlog.Fatal(err) - } + time.Sleep(2 * time.Second) - h, err := c.TrustedHeader(3, 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 } -func TestExample_AutoClient(t *testing.T) { +// Manually getting headers and verifying them. +func ExampleClient_VerifyHeaderAtHeight() { // give Tendermint time to generate some blocks time.Sleep(5 * time.Second) @@ -93,12 +102,12 @@ func TestExample_AutoClient(t *testing.T) { chainID = config.ChainID() ) - provider, err := httpp.New(chainID, config.RPC.ListenAddress) + primary, err := httpp.New(chainID, config.RPC.ListenAddress) if err != nil { stdlog.Fatal(err) } - header, err := provider.SignedHeader(2) + header, err := primary.SignedHeader(2) if err != nil { stdlog.Fatal(err) } @@ -108,44 +117,43 @@ func TestExample_AutoClient(t *testing.T) { stdlog.Fatal(err) } - base, err := NewClient( + c, err := NewClient( chainID, TrustOptions{ Period: 504 * time.Hour, // 21 days Height: 2, Hash: header.Hash(), }, - provider, + primary, + []provider.Provider{primary}, // NOTE: primary should not be used here dbs.New(db, chainID), + // Logger(log.TestingLogger()), ) if err != nil { stdlog.Fatal(err) } - base.SetLogger(log.TestingLogger()) - - c := NewAutoClient(base, 1*time.Second) - defer c.Stop() - - select { - case h := <-c.TrustedHeaders(): - fmt.Println("got header", h.Height) - // Output: got header 3 - case err := <-c.Errs(): - switch errors.Cause(err).(type) { - case ErrOldHeaderExpired: - // reobtain trust height and hash - stdlog.Fatal(err) - default: - // try with another full node - stdlog.Fatal(err) - } + defer func() { + c.Cleanup() + }() + + _, err = c.VerifyHeaderAtHeight(3, time.Now()) + if err != nil { + stdlog.Fatal(err) + } + + h, err := c.TrustedHeader(3) + if err != nil { + stdlog.Fatal(err) } + + fmt.Println("got header", h.Height) + // Output: got header 3 } 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/provider/errors.go b/lite2/provider/errors.go new file mode 100644 index 000000000..05a242acd --- /dev/null +++ b/lite2/provider/errors.go @@ -0,0 +1,12 @@ +package provider + +import "errors" + +var ( + // ErrSignedHeaderNotFound is returned when a provider can't find the + // requested header. + ErrSignedHeaderNotFound = errors.New("signed header not found") + // ErrValidatorSetNotFound is returned when a provider can't find the + // requested validator set. + ErrValidatorSetNotFound = errors.New("validator set not found") +) diff --git a/lite2/provider/http/http.go b/lite2/provider/http/http.go index 24c9d5527..130bf0a24 100644 --- a/lite2/provider/http/http.go +++ b/lite2/provider/http/http.go @@ -1,7 +1,9 @@ package http import ( + "errors" "fmt" + "strings" "github.com/tendermint/tendermint/lite2/provider" rpcclient "github.com/tendermint/tendermint/rpc/client" @@ -12,6 +14,8 @@ import ( type SignStatusClient interface { rpcclient.SignClient rpcclient.StatusClient + // Remote returns the remote network address in a string form. + Remote() string } // http provider uses an RPC client (or SignStatusClient more generally) to @@ -44,6 +48,10 @@ func (p *http) ChainID() string { return p.chainID } +func (p *http) String() string { + return fmt.Sprintf("http{%s}", p.client.Remote()) +} + // SignedHeader fetches a SignedHeader at the given height and checks the // chainID matches. func (p *http) SignedHeader(height int64) (*types.SignedHeader, error) { @@ -54,9 +62,17 @@ func (p *http) SignedHeader(height int64) (*types.SignedHeader, error) { commit, err := p.client.Commit(h) if err != nil { + // TODO: standartise errors on the RPC side + if strings.Contains(err.Error(), "height must be less than or equal") { + return nil, provider.ErrSignedHeaderNotFound + } return nil, err } + if commit.Header == nil { + return nil, errors.New("header is nil") + } + // Verify we're still on the same chain. if p.chainID != commit.Header.ChainID { return nil, fmt.Errorf("expected chainID %s, got %s", p.chainID, commit.Header.ChainID) @@ -76,6 +92,10 @@ func (p *http) ValidatorSet(height int64) (*types.ValidatorSet, error) { const maxPerPage = 100 res, err := p.client.Validators(h, 0, maxPerPage) if err != nil { + // TODO: standartise errors on the RPC side + if strings.Contains(err.Error(), "height must be less than or equal") { + return nil, provider.ErrValidatorSetNotFound + } return nil, err } diff --git a/lite2/provider/mock/deadmock.go b/lite2/provider/mock/deadmock.go new file mode 100644 index 000000000..77c474411 --- /dev/null +++ b/lite2/provider/mock/deadmock.go @@ -0,0 +1,33 @@ +package mock + +import ( + "errors" + + "github.com/tendermint/tendermint/lite2/provider" + "github.com/tendermint/tendermint/types" +) + +type deadMock struct { + chainID string +} + +// NewDeadMock creates a mock provider that always errors. +func NewDeadMock(chainID string) provider.Provider { + return &deadMock{chainID: chainID} +} + +func (p *deadMock) ChainID() string { + return p.chainID +} + +func (p *deadMock) String() string { + return "deadMock" +} + +func (p *deadMock) SignedHeader(height int64) (*types.SignedHeader, error) { + return nil, errors.New("no response from provider") +} + +func (p *deadMock) ValidatorSet(height int64) (*types.ValidatorSet, error) { + return nil, errors.New("no response from provider") +} diff --git a/lite2/provider/mock/mock.go b/lite2/provider/mock/mock.go index 83548e2c3..7ff7bc9a1 100644 --- a/lite2/provider/mock/mock.go +++ b/lite2/provider/mock/mock.go @@ -1,21 +1,21 @@ package mock import ( - "github.com/pkg/errors" + "fmt" + "strings" "github.com/tendermint/tendermint/lite2/provider" "github.com/tendermint/tendermint/types" ) -// mock provider allows to directly set headers & vals, which can be handy when -// testing. type mock struct { chainID string headers map[int64]*types.SignedHeader vals map[int64]*types.ValidatorSet } -// New creates a mock provider. +// New creates a mock provider with the given set of headers and validator +// sets. func New(chainID string, headers map[int64]*types.SignedHeader, vals map[int64]*types.ValidatorSet) provider.Provider { return &mock{ chainID: chainID, @@ -24,20 +24,41 @@ func New(chainID string, headers map[int64]*types.SignedHeader, vals map[int64]* } } +// ChainID returns the blockchain ID. func (p *mock) ChainID() string { return p.chainID } +func (p *mock) String() string { + var headers strings.Builder + for _, h := range p.headers { + fmt.Fprintf(&headers, " %d:%X", h.Height, h.Hash()) + } + + var vals strings.Builder + for _, v := range p.vals { + fmt.Fprintf(&vals, " %X", v.Hash()) + } + + return fmt.Sprintf("mock{headers: %s, vals: %v}", headers.String(), vals.String()) +} + func (p *mock) SignedHeader(height int64) (*types.SignedHeader, error) { + if height == 0 && len(p.headers) > 0 { + return p.headers[int64(len(p.headers))], nil + } if _, ok := p.headers[height]; ok { return p.headers[height], nil } - return nil, errors.Errorf("no header at height %d", height) + return nil, provider.ErrSignedHeaderNotFound } func (p *mock) ValidatorSet(height int64) (*types.ValidatorSet, error) { + if height == 0 && len(p.vals) > 0 { + return p.vals[int64(len(p.vals))], nil + } if _, ok := p.vals[height]; ok { return p.vals[height], nil } - return nil, errors.Errorf("no vals for height %d", height) + return nil, provider.ErrValidatorSetNotFound } diff --git a/lite2/provider/provider.go b/lite2/provider/provider.go index 14a1df993..773e17e32 100644 --- a/lite2/provider/provider.go +++ b/lite2/provider/provider.go @@ -1,6 +1,8 @@ package provider -import "github.com/tendermint/tendermint/types" +import ( + "github.com/tendermint/tendermint/types" +) // Provider provides information for the lite client to sync (verification // happens in the client). @@ -17,7 +19,7 @@ type Provider interface { // If the provider fails to fetch the SignedHeader due to the IO or other // issues, an error will be returned. // If there's no SignedHeader for the given height, ErrSignedHeaderNotFound - // will be returned. + // error is returned. SignedHeader(height int64) (*types.SignedHeader, error) // ValidatorSet returns the ValidatorSet that corresponds to height. @@ -28,6 +30,6 @@ type Provider interface { // If the provider fails to fetch the ValidatorSet due to the IO or other // issues, an error will be returned. // If there's no ValidatorSet for the given height, ErrValidatorSetNotFound - // will be returned. + // error is returned. ValidatorSet(height int64) (*types.ValidatorSet, error) } diff --git a/lite2/proxy/routes.go b/lite2/proxy/routes.go index 110cfe88f..f7d5cd25b 100644 --- a/lite2/proxy/routes.go +++ b/lite2/proxy/routes.go @@ -26,7 +26,7 @@ func RPCRoutes(c *lrpc.Client) map[string]*rpcserver.RPCFunc { "block_results": rpcserver.NewRPCFunc(makeBlockResultsFunc(c), "height"), "commit": rpcserver.NewRPCFunc(makeCommitFunc(c), "height"), "tx": rpcserver.NewRPCFunc(makeTxFunc(c), "hash,prove"), - "tx_search": rpcserver.NewRPCFunc(makeTxSearchFunc(c), "query,prove,page,per_page"), + "tx_search": rpcserver.NewRPCFunc(makeTxSearchFunc(c), "query,prove,page,per_page,order_by"), "validators": rpcserver.NewRPCFunc(makeValidatorsFunc(c), "height,page,per_page"), "dump_consensus_state": rpcserver.NewRPCFunc(makeDumpConsensusStateFunc(c), ""), "consensus_state": rpcserver.NewRPCFunc(makeConsensusStateFunc(c), ""), @@ -122,11 +122,12 @@ func makeTxFunc(c *lrpc.Client) rpcTxFunc { } type rpcTxSearchFunc func(ctx *rpctypes.Context, query string, prove bool, - page, perPage int) (*ctypes.ResultTxSearch, error) + page, perPage int, orderBy string) (*ctypes.ResultTxSearch, error) func makeTxSearchFunc(c *lrpc.Client) rpcTxSearchFunc { - return func(ctx *rpctypes.Context, query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) { - return c.TxSearch(query, prove, page, perPage) + return func(ctx *rpctypes.Context, query string, prove bool, page, perPage int, orderBy string) ( + *ctypes.ResultTxSearch, error) { + return c.TxSearch(query, prove, page, perPage, orderBy) } } diff --git a/lite2/rpc/client.go b/lite2/rpc/client.go index 458e871ad..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) } @@ -295,8 +295,9 @@ func (c *Client) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { return res, res.Proof.Validate(h.DataHash) } -func (c *Client) TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) { - return c.next.TxSearch(query, prove, page, perPage) +func (c *Client) TxSearch(query string, prove bool, page, perPage int, orderBy string) ( + *ctypes.ResultTxSearch, error) { + return c.next.TxSearch(query, prove, page, perPage, orderBy) } func (c *Client) Validators(height *int64, page, perPage int) (*ctypes.ResultValidators, error) { @@ -321,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 ee0d26141..d405b9865 100644 --- a/lite2/store/db/db.go +++ b/lite2/store/db/db.go @@ -1,10 +1,13 @@ package db import ( + "encoding/binary" "fmt" "regexp" "strconv" + "sync" + "github.com/pkg/errors" "github.com/tendermint/go-amino" dbm "github.com/tendermint/tm-db" @@ -13,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 } @@ -27,47 +37,83 @@ 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") } - // TODO: batch - bz, err := s.cdc.MarshalBinaryLengthPrefixed(sh) + shBz, err := s.cdc.MarshalBinaryLengthPrefixed(sh) if err != nil { - return err + return errors.Wrap(err, "marshalling header") } - s.db.Set(s.shKey(sh.Height), bz) - bz, err = s.cdc.MarshalBinaryLengthPrefixed(valSet) + valSetBz, err := s.cdc.MarshalBinaryLengthPrefixed(valSet) if err != nil { - return err + return errors.Wrap(err, "marshalling validator set") } - s.db.Set(s.vsKey(sh.Height+1), bz) - return nil + s.mtx.Lock() + defer s.mtx.Unlock() + + b := s.db.NewBatch() + b.Set(s.shKey(sh.Height), shBz) + 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") } - // TODO: batch - s.db.Delete(s.shKey(height)) - s.db.Delete(s.vsKey(height + 1)) + s.mtx.Lock() + defer s.mtx.Unlock() - return nil + b := s.db.NewBatch() + b.Delete(s.shKey(height)) + 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") @@ -78,7 +124,7 @@ func (s *dbs) SignedHeader(height int64) (*types.SignedHeader, error) { panic(err) } if len(bz) == 0 { - return nil, nil + return nil, store.ErrSignedHeaderNotFound } var signedHeader *types.SignedHeader @@ -87,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") @@ -97,7 +145,7 @@ func (s *dbs) ValidatorSet(height int64) (*types.ValidatorSet, error) { panic(err) } if len(bz) == 0 { - return nil, nil + return nil, store.ErrValidatorSetNotFound } var valSet *types.ValidatorSet @@ -106,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), @@ -129,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), @@ -151,6 +203,105 @@ 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.Iterator( + s.shKey(height+1), + append(s.shKey(1<<63-1), byte(0x00)), + ) + if err != nil { + panic(err) + } + defer itr.Close() + + for itr.Valid() { + key := itr.Key() + _, existingHeight, ok := parseShKey(key) + if ok { + return s.SignedHeader(existingHeight) + } + itr.Next() + } + + 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 { return []byte(fmt.Sprintf("sh/%s/%020d", s.prefix, height)) } @@ -184,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 9edc1b2d9..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.NoError(t, err) + require.Error(t, err) assert.Nil(t, h) - valSet, err := dbStore.ValidatorSet(2) - require.NoError(t, err) + 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,104 @@ 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.NoError(t, err) + require.Error(t, err) assert.Nil(t, h) - valSet, err = dbStore.ValidatorSet(2) - require.NoError(t, err) + valSet, err = dbStore.ValidatorSet(1) + require.Error(t, err) assert.Nil(t, valSet) } + +func Test_SignedHeaderAfter(t *testing.T) { + dbStore := New(dbm.NewMemDB(), "Test_SignedHeaderAfter") + + assert.Panics(t, func() { + dbStore.SignedHeaderAfter(0) + dbStore.SignedHeaderAfter(100) + }) + + err := dbStore.SaveSignedHeaderAndValidatorSet( + &types.SignedHeader{Header: &types.Header{Height: 2}}, &types.ValidatorSet{}) + require.NoError(t, err) + + h, err := dbStore.SignedHeaderAfter(1) + require.NoError(t, err) + if assert.NotNil(t, h) { + 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 new file mode 100644 index 000000000..2f77bc893 --- /dev/null +++ b/lite2/store/errors.go @@ -0,0 +1,13 @@ +package store + +import "errors" + +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 cdcc25fa5..7ea6b9c6b 100644 --- a/lite2/store/store.go +++ b/lite2/store/store.go @@ -4,33 +4,31 @@ 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. // // height must be > 0. // - // If the store is empty and the latest SignedHeader is requested, an error - // is returned. + // If SignedHeader is not found, ErrSignedHeaderNotFound is returned. SignedHeader(height int64) (*types.SignedHeader, error) // ValidatorSet returns the ValidatorSet that corresponds to height. // // height must be > 0. // - // If the store is empty and the latest ValidatorSet is requested, an error - // is returned. + // If ValidatorSet is not found, ErrValidatorSetNotFound is returned. ValidatorSet(height int64) (*types.ValidatorSet, error) // LastSignedHeaderHeight returns the last (newest) SignedHeader height. @@ -42,4 +40,16 @@ type Store interface { // // If the store is empty, -1 and nil error are returned. FirstSignedHeaderHeight() (int64, error) + + // SignedHeaderAfter returns the SignedHeader after the certain height. + // + // 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 872704294..cc1bf4eb9 100644 --- a/lite2/test_helpers.go +++ b/lite2/test_helpers.go @@ -36,11 +36,11 @@ func genPrivKeys(n int) privKeys { // return res // } -// // Extend adds n more keys (to remove, just take a slice). -// func (pkz privKeys) Extend(n int) privKeys { -// extra := genPrivKeys(n) -// return append(pkz, extra...) -// } +// Extend adds n more keys (to remove, just take a slice). +func (pkz privKeys) Extend(n int) privKeys { + extra := genPrivKeys(n) + return append(pkz, extra...) +} // // GenSecpPrivKeys produces an array of secp256k1 private keys to generate commits. // func GenSecpPrivKeys(n int) privKeys { @@ -147,3 +147,16 @@ func (pkz privKeys) GenSignedHeader(chainID string, height int64, bTime time.Tim Commit: pkz.signHeader(header, first, last), } } + +// GenSignedHeaderLastBlockID calls genHeader and signHeader and combines them into a SignedHeader. +func (pkz privKeys) GenSignedHeaderLastBlockID(chainID string, height int64, bTime time.Time, txs types.Txs, + valset, nextValset *types.ValidatorSet, appHash, consHash, resHash []byte, first, last int, + lastBlockID types.BlockID) *types.SignedHeader { + + header := genHeader(chainID, height, bTime, txs, valset, nextValset, appHash, consHash, resHash) + header.LastBlockID = lastBlockID + return &types.SignedHeader{ + Header: header, + Commit: pkz.signHeader(header, first, last), + } +} diff --git a/lite2/trust_options.go b/lite2/trust_options.go new file mode 100644 index 000000000..7bd36fe5c --- /dev/null +++ b/lite2/trust_options.go @@ -0,0 +1,53 @@ +package lite + +import ( + "time" + + "github.com/pkg/errors" + + "github.com/tendermint/tendermint/crypto/tmhash" +) + +// TrustOptions are the trust parameters needed when a new light client +// connects to the network or when an existing light client that has been +// offline for longer than the trusting period connects to the network. +// +// The expectation is the user will get this information from a trusted source +// like a validator, a friend, or a secure website. A more user friendly +// solution with trust tradeoffs is that we establish an https based protocol +// with a default end point that populates this information. Also an on-chain +// registry of roots-of-trust (e.g. on the Cosmos Hub) seems likely in the +// future. +type TrustOptions struct { + // tp: trusting period. + // + // Should be significantly less than the unbonding period (e.g. unbonding + // period = 3 weeks, trusting period = 2 weeks). + // + // More specifically, trusting period + time needed to check headers + time + // needed to report and punish misbehavior should be less than the unbonding + // period. + Period time.Duration + + // Header's Height and Hash must both be provided to force the trusting of a + // particular header. + Height int64 + Hash []byte +} + +// ValidateBasic performs basic validation. +func (opts TrustOptions) ValidateBasic() error { + if opts.Period <= 0 { + return errors.New("negative or zero period") + } + if opts.Height <= 0 { + return errors.New("negative or zero height") + } + if len(opts.Hash) != tmhash.Size { + return errors.Errorf("expected hash size to be %d bytes, got %d bytes", + tmhash.Size, + len(opts.Hash), + ) + } + return nil +} diff --git a/lite2/verifier.go b/lite2/verifier.go index d2f656e41..6d8459ab6 100644 --- a/lite2/verifier.go +++ b/lite2/verifier.go @@ -10,107 +10,173 @@ import ( "github.com/tendermint/tendermint/types" ) +const ( + maxClockDrift = 10 * time.Second +) + var ( - // DefaultTrustLevel - new header can be trusted if at least one correct old + // DefaultTrustLevel - new header can be trusted if at least one correct // validator signed it. DefaultTrustLevel = tmmath.Fraction{Numerator: 1, Denominator: 3} ) -// Verify verifies the new header (h2) against the old header (h1). It ensures that: +// VerifyNonAdjacent verifies non-adjacent untrustedHeader against +// trustedHeader. It ensures that: // -// a) h1 can still be trusted (if not, ErrOldHeaderExpired is returned); -// b) h2 is valid; -// c) either h2.ValidatorsHash equals h1NextVals.Hash() -// OR trustLevel ([1/3, 1]) of last trusted validators (h1NextVals) signed -// correctly (if not, ErrNewValSetCantBeTrusted is returned); -// c) more than 2/3 of new validators (h2Vals) have signed h2 (if not, -// ErrNotEnoughVotingPowerSigned is returned). -func Verify( +// 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, - h1 *types.SignedHeader, - h1NextVals *types.ValidatorSet, - h2 *types.SignedHeader, - h2Vals *types.ValidatorSet, + trustedHeader *types.SignedHeader, // height=X + trustedVals *types.ValidatorSet, // height=X or height=X+1 + untrustedHeader *types.SignedHeader, // height=Y + untrustedVals *types.ValidatorSet, // height=Y trustingPeriod time.Duration, now time.Time, trustLevel tmmath.Fraction) error { - if err := ValidateTrustLevel(trustLevel); err != nil { - return err + if untrustedHeader.Height == trustedHeader.Height+1 { + return errors.New("headers must be non adjacent in height") } - // Ensure last header can still be trusted. - if HeaderExpired(h1, trustingPeriod, now) { - return ErrOldHeaderExpired{h1.Time.Add(trustingPeriod), now} + if HeaderExpired(trustedHeader, trustingPeriod, now) { + return ErrOldHeaderExpired{trustedHeader.Time.Add(trustingPeriod), now} } - if err := verifyNewHeaderAndVals(chainID, h2, h2Vals, h1, now); err != nil { - return err + if err := verifyNewHeaderAndVals(chainID, untrustedHeader, untrustedVals, trustedHeader, now); err != nil { + return ErrInvalidHeader{err} } - if h2.Height == h1.Height+1 { - if !bytes.Equal(h2.ValidatorsHash, h1NextVals.Hash()) { - err := errors.Errorf("expected old header next validators (%X) to match those from new header (%X)", - h1NextVals.Hash(), - h2.ValidatorsHash, - ) - return err - } - } else { - // Ensure that +`trustLevel` (default 1/3) or more of last trusted validators signed correctly. - err := h1NextVals.VerifyCommitTrusting(chainID, h2.Commit.BlockID, h2.Height, h2.Commit, trustLevel) - if err != nil { - switch e := err.(type) { - case types.ErrNotEnoughVotingPowerSigned: - return ErrNewValSetCantBeTrusted{e} - default: - return e - } + // Ensure that +`trustLevel` (default 1/3) or more of last trusted validators signed correctly. + err := trustedVals.VerifyCommitTrusting(chainID, untrustedHeader.Commit.BlockID, untrustedHeader.Height, + untrustedHeader.Commit, trustLevel) + if err != nil { + switch e := err.(type) { + case types.ErrNotEnoughVotingPowerSigned: + return ErrNewValSetCantBeTrusted{e} + default: + return e } } // Ensure that +2/3 of new validators signed correctly. - err := h2Vals.VerifyCommit(chainID, h2.Commit.BlockID, h2.Height, h2.Commit) - if err != nil { + // + // NOTE: this should always be the last check because untrustedVals can be + // 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 ErrInvalidHeader{err} + } + + return nil +} + +// 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 (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, + trustedHeader *types.SignedHeader, // height=X + untrustedHeader *types.SignedHeader, // height=X+1 + untrustedVals *types.ValidatorSet, // height=X+1 + trustingPeriod time.Duration, + now time.Time) error { + + if untrustedHeader.Height != trustedHeader.Height+1 { + return errors.New("headers must be adjacent in height") + } + + if HeaderExpired(trustedHeader, trustingPeriod, now) { + return ErrOldHeaderExpired{trustedHeader.Time.Add(trustingPeriod), now} + } + + if err := verifyNewHeaderAndVals(chainID, untrustedHeader, untrustedVals, trustedHeader, now); err != nil { + return ErrInvalidHeader{err} + } + + // Check the validator hashes are the same + if !bytes.Equal(untrustedHeader.ValidatorsHash, trustedHeader.NextValidatorsHash) { + err := errors.Errorf("expected old header next validators (%X) to match those from new header (%X)", + trustedHeader.NextValidatorsHash, + untrustedHeader.ValidatorsHash, + ) return err } + // Ensure that +2/3 of new validators signed correctly. + if err := untrustedVals.VerifyCommit(chainID, untrustedHeader.Commit.BlockID, untrustedHeader.Height, + untrustedHeader.Commit); err != nil { + return ErrInvalidHeader{err} + } + return nil } +// Verify combines both VerifyAdjacent and VerifyNonAdjacent functions. +func Verify( + chainID string, + trustedHeader *types.SignedHeader, // height=X + trustedVals *types.ValidatorSet, // height=X or height=X+1 + untrustedHeader *types.SignedHeader, // height=Y + untrustedVals *types.ValidatorSet, // height=Y + trustingPeriod time.Duration, + now time.Time, + trustLevel tmmath.Fraction) error { + + if untrustedHeader.Height != trustedHeader.Height+1 { + return VerifyNonAdjacent(chainID, trustedHeader, trustedVals, untrustedHeader, untrustedVals, + trustingPeriod, now, trustLevel) + } + + return VerifyAdjacent(chainID, trustedHeader, untrustedHeader, untrustedVals, trustingPeriod, now) +} + func verifyNewHeaderAndVals( chainID string, - h2 *types.SignedHeader, - h2Vals *types.ValidatorSet, - h1 *types.SignedHeader, + untrustedHeader *types.SignedHeader, + untrustedVals *types.ValidatorSet, + trustedHeader *types.SignedHeader, now time.Time) error { - if err := h2.ValidateBasic(chainID); err != nil { - return errors.Wrap(err, "h2.ValidateBasic failed") + if err := untrustedHeader.ValidateBasic(chainID); err != nil { + return errors.Wrap(err, "untrustedHeader.ValidateBasic failed") } - if h2.Height <= h1.Height { + if untrustedHeader.Height <= trustedHeader.Height { return errors.Errorf("expected new header height %d to be greater than one of old header %d", - h2.Height, - h1.Height) + untrustedHeader.Height, + trustedHeader.Height) } - if !h2.Time.After(h1.Time) { + if !untrustedHeader.Time.After(trustedHeader.Time) { return errors.Errorf("expected new header time %v to be after old header time %v", - h2.Time, - h1.Time) + untrustedHeader.Time, + trustedHeader.Time) } - if !h2.Time.Before(now) { - return errors.Errorf("new header has a time from the future %v (now: %v)", - h2.Time, - now) + 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, + maxClockDrift) } - if !bytes.Equal(h2.ValidatorsHash, h2Vals.Hash()) { + if !bytes.Equal(untrustedHeader.ValidatorsHash, untrustedVals.Hash()) { return errors.Errorf("expected new header validators (%X) to match those that were supplied (%X)", - h2.ValidatorsHash, - h2Vals.Hash(), + untrustedHeader.ValidatorsHash, + untrustedVals.Hash(), ) } @@ -134,3 +200,34 @@ func HeaderExpired(h *types.SignedHeader, trustingPeriod time.Duration, now time expirationTime := h.Time.Add(trustingPeriod) return !expirationTime.After(now) } + +// VerifyBackwards verifies an untrusted header with a height one less than +// that of an adjacent trusted header. It ensures that: +// +// a) untrusted header is valid +// b) untrusted header has a time before the trusted header +// c) that the LastBlockID hash of the trusted header is the same as the hash +// of the trusted header +// +// For any of these cases ErrInvalidHeader is returned. +func VerifyBackwards(chainID string, untrustedHeader, trustedHeader *types.SignedHeader) error { + if err := untrustedHeader.ValidateBasic(chainID); err != nil { + return ErrInvalidHeader{err} + } + + if !untrustedHeader.Time.Before(trustedHeader.Time) { + return ErrInvalidHeader{ + errors.Errorf("expected older header time %v to be before new header time %v", + untrustedHeader.Time, + trustedHeader.Time)} + } + + if !bytes.Equal(untrustedHeader.Hash(), trustedHeader.LastBlockID.Hash) { + return ErrInvalidHeader{ + errors.Errorf("older header hash %X does not match trusted header's last block %X", + untrustedHeader.Hash(), + trustedHeader.LastBlockID.Hash)} + } + + return nil +} diff --git a/lite2/verifier_test.go b/lite2/verifier_test.go index 1625213cc..adc671516 100644 --- a/lite2/verifier_test.go +++ b/lite2/verifier_test.go @@ -42,7 +42,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) { 3 * time.Hour, bTime.Add(2 * time.Hour), nil, - "expected new header height 1 to be greater than one of old header 1", + "headers must be adjacent in height", }, // different chainID -> error 1: { @@ -52,7 +52,8 @@ func TestVerifyAdjacentHeaders(t *testing.T) { 3 * time.Hour, bTime.Add(2 * time.Hour), nil, - "h2.ValidateBasic failed: signedHeader belongs to another chain 'different-chainID' not 'TestVerifyAdjacentHeaders'", + "untrustedHeader.ValidateBasic failed: signedHeader belongs to another chain 'different-chainID' not" + + " 'TestVerifyAdjacentHeaders'", }, // new header's time is before old header's time -> error 2: { @@ -74,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, @@ -85,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, @@ -95,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), @@ -115,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), @@ -125,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), @@ -139,8 +151,7 @@ func TestVerifyAdjacentHeaders(t *testing.T) { for i, tc := range testCases { tc := tc t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { - err := Verify(chainID, header, vals, tc.newHeader, tc.newVals, tc.trustingPeriod, tc.now, DefaultTrustLevel) - + err := VerifyAdjacent(chainID, header, tc.newHeader, tc.newVals, tc.trustingPeriod, tc.now) switch { case tc.expErr != nil && assert.Error(t, err): assert.Equal(t, tc.expErr, err) @@ -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 @@ -254,7 +265,8 @@ func TestVerifyNonAdjacentHeaders(t *testing.T) { for i, tc := range testCases { tc := tc t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) { - err := Verify(chainID, header, vals, tc.newHeader, tc.newVals, tc.trustingPeriod, tc.now, DefaultTrustLevel) + err := VerifyNonAdjacent(chainID, header, vals, tc.newHeader, tc.newVals, tc.trustingPeriod, tc.now, + DefaultTrustLevel) switch { case tc.expErr != nil && assert.Error(t, err): diff --git a/mempool/reactor.go b/mempool/reactor.go index 161fc7212..fda12c021 100644 --- a/mempool/reactor.go +++ b/mempool/reactor.go @@ -47,7 +47,7 @@ type mempoolIDs struct { activeIDs map[uint16]struct{} // used to check if a given peerID key is used, the value doesn't matter } -// Reserve searches for the next unused ID and assignes it to the +// Reserve searches for the next unused ID and assigns it to the // peer. func (ids *mempoolIDs) ReserveForPeer(peer p2p.Peer) { ids.mtx.Lock() @@ -110,10 +110,16 @@ func NewReactor(config *cfg.MempoolConfig, mempool *CListMempool) *Reactor { mempool: mempool, ids: newMempoolIDs(), } - memR.BaseReactor = *p2p.NewBaseReactor("Reactor", memR) + memR.BaseReactor = *p2p.NewBaseReactor("Mempool", memR) return memR } +// InitPeer implements Reactor by creating a state for the peer. +func (memR *Reactor) InitPeer(peer p2p.Peer) p2p.Peer { + memR.ids.ReserveForPeer(peer) + return peer +} + // SetLogger sets the Logger on the reactor and the underlying mempool. func (memR *Reactor) SetLogger(l log.Logger) { memR.Logger = l @@ -142,7 +148,6 @@ func (memR *Reactor) GetChannels() []*p2p.ChannelDescriptor { // AddPeer implements Reactor. // It starts a broadcast routine ensuring all txs are forwarded to the given peer. func (memR *Reactor) AddPeer(peer p2p.Peer) { - memR.ids.ReserveForPeer(peer) go memR.broadcastTxRoutine(peer) } diff --git a/mempool/reactor_test.go b/mempool/reactor_test.go index 27c82f2c4..87da0557d 100644 --- a/mempool/reactor_test.go +++ b/mempool/reactor_test.go @@ -223,3 +223,21 @@ func TestMempoolIDsPanicsIfNodeRequestsOvermaxActiveIDs(t *testing.T) { ids.ReserveForPeer(peer) }) } + +func TestDontExhaustMaxActiveIDs(t *testing.T) { + config := cfg.TestConfig() + const N = 1 + reactors := makeAndConnectReactors(config, N) + defer func() { + for _, r := range reactors { + r.Stop() + } + }() + reactor := reactors[0] + + for i := 0; i < maxActiveIDs+1; i++ { + peer := mock.NewPeer(nil) + reactor.Receive(MempoolChannel, peer, []byte{0x1, 0x2, 0x3}) + reactor.AddPeer(peer) + } +} diff --git a/networks/local/README.md b/networks/local/README.md index 8d4299693..dcb31ae71 100644 --- a/networks/local/README.md +++ b/networks/local/README.md @@ -1,83 +1,3 @@ # Local Cluster with Docker Compose -DEPRECATED! - -See the [docs](https://tendermint.com/docs/networks/docker-compose.html). - -## Requirements - -- [Install tendermint](/docs/install.md) -- [Install docker](https://docs.docker.com/engine/installation/) -- [Install docker-compose](https://docs.docker.com/compose/install/) - -## Build - -Build the `tendermint` binary and the `tendermint/localnode` docker image. - -Note the binary will be mounted into the container so it can be updated without -rebuilding the image. - -``` -cd $GOPATH/src/github.com/tendermint/tendermint - -# Build the linux binary in ./build -make build-linux - -# Build tendermint/localnode image -make build-docker-localnode -``` - - -## Run a testnet - -To start a 4 node testnet run: - -``` -make localnet-start -``` - -The nodes bind their RPC servers to ports 26657, 26660, 26662, and 26664 on the host. -This file creates a 4-node network using the localnode image. -The nodes of the network expose their P2P and RPC endpoints to the host machine on ports 26656-26657, 26659-26660, 26661-26662, and 26663-26664 respectively. - -To update the binary, just rebuild it and restart the nodes: - -``` -make build-linux -make localnet-stop -make localnet-start -``` - -## Configuration - -The `make localnet-start` creates files for a 4-node testnet in `./build` by calling the `tendermint testnet` command. - -The `./build` directory is mounted to the `/tendermint` mount point to attach the binary and config files to the container. - -For instance, to create a single node testnet: - -``` -cd $GOPATH/src/github.com/tendermint/tendermint - -# Clear the build folder -rm -rf ./build - -# Build binary -make build-linux - -# Create configuration -docker run -e LOG="stdout" -v `pwd`/build:/tendermint tendermint/localnode testnet --o . --v 1 - -#Run the node -docker run -v `pwd`/build:/tendermint tendermint/localnode - -``` - -## Logging - -Log is saved under the attached volume, in the `tendermint.log` file. If the `LOG` environment variable is set to `stdout` at start, the log is not saved, but printed on the screen. - -## Special binaries - -If you have multiple binaries with different names, you can specify which one to run with the BINARY environment variable. The path of the binary is relative to the attached volume. - +See the [docs](https://docs.tendermint.com/master/networks/docker-compose.html). diff --git a/node/node.go b/node/node.go index ffca39ee0..7242995ca 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" @@ -467,6 +467,11 @@ func createTransport( } p2p.MultiplexTransportConnFilters(connFilters...)(transport) + + // Limit the number of incoming connections. + max := config.P2P.MaxNumInboundPeers + len(splitAndTrimEmpty(config.P2P.UnconditionalPeerIDs, ",", " ")) + p2p.MultiplexTransportMaxIncomingConnections(max)(transport) + return transport, peerFilters } @@ -948,7 +953,16 @@ func (n *Node) startRPC() ([]net.Listener, error) { grpcListenAddr := n.config.RPC.GRPCListenAddress if grpcListenAddr != "" { config := rpcserver.DefaultConfig() - config.MaxOpenConnections = n.config.RPC.MaxOpenConnections + config.MaxBodyBytes = n.config.RPC.MaxBodyBytes + config.MaxHeaderBytes = n.config.RPC.MaxHeaderBytes + // NOTE: GRPCMaxOpenConnections is used, not MaxOpenConnections + config.MaxOpenConnections = n.config.RPC.GRPCMaxOpenConnections + // If necessary adjust global WriteTimeout to ensure it's greater than + // TimeoutBroadcastTxCommit. + // See https://github.com/tendermint/tendermint/issues/3435 + if config.WriteTimeout <= n.config.RPC.TimeoutBroadcastTxCommit { + config.WriteTimeout = n.config.RPC.TimeoutBroadcastTxCommit + 1*time.Second + } listener, err := rpcserver.Listen(grpcListenAddr, config) if err != nil { return nil, err diff --git a/p2p/README.md b/p2p/README.md index df4c2ae01..9ba7303fa 100644 --- a/p2p/README.md +++ b/p2p/README.md @@ -4,8 +4,8 @@ The p2p package provides an abstraction around peer-to-peer communication. Docs: -- [Connection](https://github.com/tendermint/tendermint/blob/master/docs/spec/p2p/connection.md) for details on how connections and multiplexing work -- [Peer](https://github.com/tendermint/tendermint/blob/master/docs/spec/p2p/peer.md) for details on peer ID, handshakes, and peer exchange -- [Node](https://github.com/tendermint/tendermint/blob/master/docs/spec/p2p/node.md) for details about different types of nodes and how they should work -- [Pex](https://github.com/tendermint/tendermint/blob/master/docs/spec/reactors/pex/pex.md) for details on peer discovery and exchange -- [Config](https://github.com/tendermint/tendermint/blob/master/docs/spec/p2p/config.md) for details on some config option +- [Connection](https://docs.tendermint.com/master/spec/p2p/connection.html) for details on how connections and multiplexing work +- [Peer](https://docs.tendermint.com/master/spec/p2p/node.html) for details on peer ID, handshakes, and peer exchange +- [Node](https://docs.tendermint.com/master/spec/p2p/node.html) for details about different types of nodes and how they should work +- [Pex](https://docs.tendermint.com/master/spec/reactors/pex/pex.html) for details on peer discovery and exchange +- [Config](https://docs.tendermint.com/master/spec/p2p/config.html) for details on some config option 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/netaddress.go b/p2p/netaddress.go index ed5b703e7..c71f3ce7f 100644 --- a/p2p/netaddress.go +++ b/p2p/netaddress.go @@ -48,7 +48,7 @@ func NewNetAddress(id ID, addr net.Addr) *NetAddress { if flag.Lookup("test.v") == nil { // normal run panic(fmt.Sprintf("Only TCPAddrs are supported. Got: %v", addr)) } else { // in testing - netAddr := NewNetAddressIPPort(net.IP("0.0.0.0"), 0) + netAddr := NewNetAddressIPPort(net.IP("127.0.0.1"), 0) netAddr.ID = id return netAddr } diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index 4efb4d1e0..dbba71345 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -102,7 +102,7 @@ type addrBook struct { // 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{}), diff --git a/p2p/pex/addrbook_test.go b/p2p/pex/addrbook_test.go index 0bcda730e..363958c44 100644 --- a/p2p/pex/addrbook_test.go +++ b/p2p/pex/addrbook_test.go @@ -17,6 +17,8 @@ import ( "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) } } @@ -575,7 +573,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/pex_reactor.go b/p2p/pex/pex_reactor.go index 3a3d2d7de..6dc38a921 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -137,7 +137,7 @@ func NewReactor(b AddrBook, config *ReactorConfig) *Reactor { lastReceivedRequests: cmap.NewCMap(), crawlPeerInfos: make(map[p2p.ID]crawlPeerInfo), } - r.BaseReactor = *p2p.NewBaseReactor("Reactor", r) + r.BaseReactor = *p2p.NewBaseReactor("PEX", r) return r } 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/p2p/transport.go b/p2p/transport.go index 89d63da7b..6b749c61f 100644 --- a/p2p/transport.go +++ b/p2p/transport.go @@ -7,6 +7,7 @@ import ( "time" "github.com/pkg/errors" + "golang.org/x/net/netutil" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/p2p/conn" @@ -122,11 +123,18 @@ func MultiplexTransportResolver(resolver IPResolver) MultiplexTransportOption { return func(mt *MultiplexTransport) { mt.resolver = resolver } } +// MultiplexTransportMaxIncomingConnections sets the maximum number of +// simultaneous connections (incoming). Default: 0 (unlimited) +func MultiplexTransportMaxIncomingConnections(n int) MultiplexTransportOption { + return func(mt *MultiplexTransport) { mt.maxIncomingConnections = n } +} + // MultiplexTransport accepts and dials tcp connections and upgrades them to // multiplexed peers. type MultiplexTransport struct { - netAddr NetAddress - listener net.Listener + netAddr NetAddress + listener net.Listener + maxIncomingConnections int // see MaxIncomingConnections acceptc chan accept closec chan struct{} @@ -240,6 +248,10 @@ func (mt *MultiplexTransport) Listen(addr NetAddress) error { return err } + if mt.maxIncomingConnections > 0 { + ln = netutil.LimitListener(ln, mt.maxIncomingConnections) + } + mt.netAddr = addr mt.listener = ln diff --git a/p2p/transport_test.go b/p2p/transport_test.go index dd0457fb5..2fc69ce05 100644 --- a/p2p/transport_test.go +++ b/p2p/transport_test.go @@ -5,6 +5,7 @@ import ( "math/rand" "net" "reflect" + "strings" "testing" "time" @@ -134,6 +135,50 @@ func TestTransportMultiplexConnFilterTimeout(t *testing.T) { } } +func TestTransportMultiplexMaxIncomingConnections(t *testing.T) { + mt := newMultiplexTransport( + emptyNodeInfo(), + NodeKey{ + PrivKey: ed25519.GenPrivKey(), + }, + ) + id := mt.nodeKey.ID() + + MultiplexTransportMaxIncomingConnections(0)(mt) + + addr, err := NewNetAddressString(IDAddressString(id, "127.0.0.1:0")) + if err != nil { + t.Fatal(err) + } + + if err := mt.Listen(*addr); err != nil { + t.Fatal(err) + } + + errc := make(chan error) + + go func() { + addr := NewNetAddress(id, mt.listener.Addr()) + + _, err := addr.Dial() + if err != nil { + errc <- err + return + } + + close(errc) + }() + + if err := <-errc; err != nil { + t.Errorf("connection failed: %v", err) + } + + _, err = mt.Accept(peerConfig{}) + if err == nil || !strings.Contains(err.Error(), "connection reset by peer") { + t.Errorf("expected connection reset by peer error, got %v", err) + } +} + func TestTransportMultiplexAcceptMultiple(t *testing.T) { mt := testSetupMultiplexTransport(t) laddr := NewNetAddress(mt.nodeKey.ID(), mt.listener.Addr()) 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 29175fe19..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) { @@ -123,10 +133,16 @@ func NewHTTPWithClient(remote, wsEndpoint string, client *http.Client) (*HTTP, e var _ Client = (*HTTP)(nil) +// SetLogger sets a logger. func (c *HTTP) SetLogger(l log.Logger) { c.WSEvents.SetLogger(l) } +// Remote returns the remote network address in a string form. +func (c *HTTP) Remote() string { + return c.remote +} + // NewBatch creates a new batch client for this HTTP client. func (c *HTTP) NewBatch() *BatchHTTP { rpcBatch := c.rpc.NewRequestBatch() @@ -348,13 +364,15 @@ func (c *baseRPCClient) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { return result, nil } -func (c *baseRPCClient) TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) { +func (c *baseRPCClient) TxSearch(query string, prove bool, page, perPage int, orderBy string) ( + *ctypes.ResultTxSearch, error) { result := new(ctypes.ResultTxSearch) params := map[string]interface{}{ "query": query, "prove": prove, "page": page, "per_page": perPage, + "order_by": orderBy, } _, err := c.caller.Call("tx_search", params, result) if err != nil { diff --git a/rpc/client/interface.go b/rpc/client/interface.go index 72e899551..408d803c8 100644 --- a/rpc/client/interface.go +++ b/rpc/client/interface.go @@ -69,7 +69,7 @@ type SignClient interface { Commit(height *int64) (*ctypes.ResultCommit, error) Validators(height *int64, page, perPage int) (*ctypes.ResultValidators, error) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) - TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) + TxSearch(query string, prove bool, page, perPage int, orderBy string) (*ctypes.ResultTxSearch, error) } // HistoryClient provides access to data from genesis to now in large chunks. diff --git a/rpc/client/localclient.go b/rpc/client/localclient.go index c1d0809a6..e6b0eb937 100644 --- a/rpc/client/localclient.go +++ b/rpc/client/localclient.go @@ -160,8 +160,9 @@ func (c *Local) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { return core.Tx(c.ctx, hash, prove) } -func (c *Local) TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) { - return core.TxSearch(c.ctx, query, prove, page, perPage) +func (c *Local) TxSearch(query string, prove bool, page, perPage int, orderBy string) ( + *ctypes.ResultTxSearch, error) { + return core.TxSearch(c.ctx, query, prove, page, perPage, orderBy) } func (c *Local) BroadcastEvidence(ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) { diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 52f0961b9..5e83675e3 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -3,6 +3,7 @@ package client_test import ( "bytes" "fmt" + "math" "math/rand" "net/http" "strings" @@ -38,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) } @@ -413,68 +424,129 @@ 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) { - // first we broadcast a tx c := getHTTPClient() - _, _, tx := MakeTxKV() - bres, err := c.BroadcastTxCommit(tx) - require.Nil(t, err, "%+v", err) - txHeight := bres.Height - txHash := bres.Hash + // first we broadcast a few txs + for i := 0; i < 10; i++ { + _, _, tx := MakeTxKV() + _, err := c.BroadcastTxCommit(tx) + require.NoError(t, err) + } + + // since we're not using an isolated test server, we'll have lingering transactions + // from other tests as well + result, err := c.TxSearch("tx.height >= 0", true, 1, 100, "asc") + require.NoError(t, err) + txCount := len(result.Txs) + // pick out the last tx to have something to search for in tests + find := result.Txs[len(result.Txs)-1] anotherTxHash := types.Tx("a different tx").Hash() for i, c := range GetClients() { t.Logf("client %d", i) // now we query for the tx. - // since there's only one tx, we know index=0. - result, err := c.TxSearch(fmt.Sprintf("tx.hash='%v'", txHash), true, 1, 30) - require.Nil(t, err, "%+v", err) + result, err := c.TxSearch(fmt.Sprintf("tx.hash='%v'", find.Hash), true, 1, 30, "asc") + require.Nil(t, err) require.Len(t, result.Txs, 1) + require.Equal(t, find.Hash, result.Txs[0].Hash) ptx := result.Txs[0] - assert.EqualValues(t, txHeight, ptx.Height) - assert.EqualValues(t, tx, ptx.Tx) + assert.EqualValues(t, find.Height, ptx.Height) + assert.EqualValues(t, find.Tx, ptx.Tx) assert.Zero(t, ptx.Index) assert.True(t, ptx.TxResult.IsOK()) - assert.EqualValues(t, txHash, ptx.Hash) + assert.EqualValues(t, find.Hash, ptx.Hash) // time to verify the proof - proof := ptx.Proof - if assert.EqualValues(t, tx, proof.Data) { - assert.NoError(t, proof.Proof.Verify(proof.RootHash, txHash)) + if assert.EqualValues(t, find.Tx, ptx.Proof.Data) { + assert.NoError(t, ptx.Proof.Proof.Verify(ptx.Proof.RootHash, find.Hash)) } // query by height - result, err = c.TxSearch(fmt.Sprintf("tx.height=%d", txHeight), true, 1, 30) - require.Nil(t, err, "%+v", err) + result, err = c.TxSearch(fmt.Sprintf("tx.height=%d", find.Height), true, 1, 30, "asc") + require.Nil(t, err) require.Len(t, result.Txs, 1) // query for non existing tx - result, err = c.TxSearch(fmt.Sprintf("tx.hash='%X'", anotherTxHash), false, 1, 30) - require.Nil(t, err, "%+v", err) + result, err = c.TxSearch(fmt.Sprintf("tx.hash='%X'", anotherTxHash), false, 1, 30, "asc") + require.Nil(t, err) require.Len(t, result.Txs, 0) // query using a compositeKey (see kvstore application) - result, err = c.TxSearch("app.creator='Cosmoshi Netowoko'", false, 1, 30) - require.Nil(t, err, "%+v", err) + result, err = c.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") } // query using a compositeKey (see kvstore application) and height - result, err = c.TxSearch("app.creator='Cosmoshi Netowoko' AND tx.height<10000", true, 1, 30) - require.Nil(t, err, "%+v", err) + result, err = c.TxSearch("app.creator='Cosmoshi Netowoko' AND tx.height<10000", true, 1, 30, "asc") + require.Nil(t, err) if len(result.Txs) == 0 { t.Fatal("expected a lot of transactions") } // query a non existing tx with page 1 and txsPerPage 1 - result, err = c.TxSearch("app.creator='Cosmoshi Neetowoko'", true, 1, 1) - require.Nil(t, err, "%+v", err) + result, err = c.TxSearch("app.creator='Cosmoshi Neetowoko'", true, 1, 1, "asc") + require.Nil(t, err) require.Len(t, result.Txs, 0) + + // check sorting + result, err = c.TxSearch("tx.height >= 1", false, 1, 30, "asc") + require.Nil(t, err) + for k := 0; k < len(result.Txs)-1; k++ { + require.LessOrEqual(t, result.Txs[k].Height, result.Txs[k+1].Height) + require.LessOrEqual(t, result.Txs[k].Index, result.Txs[k+1].Index) + } + + result, err = c.TxSearch("tx.height >= 1", false, 1, 30, "desc") + require.Nil(t, err) + for k := 0; k < len(result.Txs)-1; k++ { + require.GreaterOrEqual(t, result.Txs[k].Height, result.Txs[k+1].Height) + require.GreaterOrEqual(t, result.Txs[k].Index, result.Txs[k+1].Index) + } + + // check pagination + var ( + seen = map[int64]bool{} + maxHeight int64 + perPage = 3 + pages = int(math.Ceil(float64(txCount) / float64(perPage))) + ) + for page := 1; page <= pages; page++ { + result, err = c.TxSearch("tx.height >= 1", false, page, perPage, "asc") + require.NoError(t, err) + if page < pages { + require.Len(t, result.Txs, perPage) + } else { + require.LessOrEqual(t, len(result.Txs), perPage) + } + require.Equal(t, txCount, result.TotalCount) + for _, tx := range result.Txs { + require.False(t, seen[tx.Height], + "Found duplicate height %v in page %v", tx.Height, page) + require.Greater(t, tx.Height, maxHeight, + "Found decreasing height %v (max seen %v) in page %v", tx.Height, maxHeight, page) + seen[tx.Height] = true + maxHeight = tx.Height + } + } + require.Len(t, seen, txCount) } } 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/abci.go b/rpc/core/abci.go index a9955d1a5..8f135ba26 100644 --- a/rpc/core/abci.go +++ b/rpc/core/abci.go @@ -9,7 +9,7 @@ import ( ) // ABCIQuery queries the application for some information. -// More: https://tendermint.com/rpc/#/ABCI/abci_query +// More: https://docs.tendermint.com/master/rpc/#/ABCI/abci_query func ABCIQuery( ctx *rpctypes.Context, path string, @@ -31,7 +31,7 @@ func ABCIQuery( } // ABCIInfo gets some info about the application. -// More: https://tendermint.com/rpc/#/ABCI/abci_info +// More: https://docs.tendermint.com/master/rpc/#/ABCI/abci_info func ABCIInfo(ctx *rpctypes.Context) (*ctypes.ResultABCIInfo, error) { resInfo, err := proxyAppQuery.InfoSync(proxy.RequestInfo) if err != nil { diff --git a/rpc/core/blocks.go b/rpc/core/blocks.go index ed3c4257b..e340d4dfb 100644 --- a/rpc/core/blocks.go +++ b/rpc/core/blocks.go @@ -12,9 +12,8 @@ import ( // BlockchainInfo gets block headers for minHeight <= height <= maxHeight. // Block headers are returned in descending order (highest first). -// More: https://tendermint.com/rpc/#/Info/blockchain +// More: https://docs.tendermint.com/master/rpc/#/Info/blockchain func BlockchainInfo(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { - // maximum 20 block metas const limit int64 = 20 var err error @@ -68,7 +67,7 @@ func filterMinMax(height, min, max, limit int64) (int64, int64, error) { // Block gets block at a given height. // If no height is provided, it will fetch the latest block. -// More: https://tendermint.com/rpc/#/Info/block +// More: https://docs.tendermint.com/master/rpc/#/Info/block func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) { storeHeight := blockStore.Height() height, err := getHeight(storeHeight, heightPtr) @@ -76,24 +75,29 @@ func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) return nil, err } - blockMeta := blockStore.LoadBlockMeta(height) block := blockStore.LoadBlock(height) + blockMeta := blockStore.LoadBlockMeta(height) + if blockMeta == nil { + return &ctypes.ResultBlock{BlockID: types.BlockID{}, Block: block}, nil + } return &ctypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil } // BlockByHash gets block by hash. -// More: https://tendermint.com/rpc/#/Info/block_by_hash +// More: https://docs.tendermint.com/master/rpc/#/Info/block_by_hash func BlockByHash(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error) { block := blockStore.LoadBlockByHash(hash) - height := block.Height - - blockMeta := blockStore.LoadBlockMeta(height) + if block == nil { + return &ctypes.ResultBlock{BlockID: types.BlockID{}, Block: nil}, nil + } + // If block is not nil, then blockMeta can't be nil. + blockMeta := blockStore.LoadBlockMeta(block.Height) return &ctypes.ResultBlock{BlockID: blockMeta.BlockID, Block: block}, nil } // Commit gets block commit at a given height. // If no height is provided, it will fetch the commit for the latest block. -// More: https://tendermint.com/rpc/#/Info/commit +// More: https://docs.tendermint.com/master/rpc/#/Info/commit func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, error) { storeHeight := blockStore.Height() height, err := getHeight(storeHeight, heightPtr) @@ -101,7 +105,11 @@ func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, erro return nil, err } - header := blockStore.LoadBlockMeta(height).Header + blockMeta := blockStore.LoadBlockMeta(height) + if blockMeta == nil { + return nil, nil + } + header := blockMeta.Header // If the next block has not been committed yet, // use a non-canonical commit @@ -121,7 +129,7 @@ func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, erro // Results are for the height of the block containing the txs. // Thus response.results.deliver_tx[5] is the results of executing // getBlock(h).Txs[5] -// More: https://tendermint.com/rpc/#/Info/block_results +// More: https://docs.tendermint.com/master/rpc/#/Info/block_results func BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockResults, error) { storeHeight := blockStore.Height() height, err := getHeight(storeHeight, heightPtr) diff --git a/rpc/core/consensus.go b/rpc/core/consensus.go index 6a5a49e03..a2a619ea5 100644 --- a/rpc/core/consensus.go +++ b/rpc/core/consensus.go @@ -13,7 +13,7 @@ import ( // If no height is provided, it will fetch the current validator set. // Note the validators are sorted by their address - this is the canonical // order for the validators in the set as used in computing their Merkle root. -// More: https://tendermint.com/rpc/#/Info/validators +// More: https://docs.tendermint.com/master/rpc/#/Info/validators func Validators(ctx *rpctypes.Context, heightPtr *int64, page, perPage int) (*ctypes.ResultValidators, error) { // The latest validator that we know is the // NextValidator of the last block. @@ -46,7 +46,7 @@ func Validators(ctx *rpctypes.Context, heightPtr *int64, page, perPage int) (*ct // DumpConsensusState dumps consensus state. // UNSTABLE -// More: https://tendermint.com/rpc/#/Info/dump_consensus_state +// More: https://docs.tendermint.com/master/rpc/#/Info/dump_consensus_state func DumpConsensusState(ctx *rpctypes.Context) (*ctypes.ResultDumpConsensusState, error) { // Get Peer consensus states. peers := p2pPeers.Peers().List() @@ -79,7 +79,7 @@ func DumpConsensusState(ctx *rpctypes.Context) (*ctypes.ResultDumpConsensusState // ConsensusState returns a concise summary of the consensus state. // UNSTABLE -// More: https://tendermint.com/rpc/#/Info/consensus_state +// More: https://docs.tendermint.com/master/rpc/#/Info/consensus_state func ConsensusState(ctx *rpctypes.Context) (*ctypes.ResultConsensusState, error) { // Get self round state. bz, err := consensusState.GetRoundStateSimpleJSON() @@ -88,7 +88,7 @@ func ConsensusState(ctx *rpctypes.Context) (*ctypes.ResultConsensusState, error) // ConsensusParams gets the consensus parameters at the given block height. // If no height is provided, it will fetch the current consensus params. -// More: https://tendermint.com/rpc/#/Info/consensus_params +// More: https://docs.tendermint.com/master/rpc/#/Info/consensus_params func ConsensusParams(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultConsensusParams, error) { height := consensusState.GetState().LastBlockHeight + 1 height, err := getHeight(height, heightPtr) diff --git a/rpc/core/events.go b/rpc/core/events.go index 49bf7be5d..7802f160e 100644 --- a/rpc/core/events.go +++ b/rpc/core/events.go @@ -12,8 +12,13 @@ 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://tendermint.com/rpc/#/Websocket/subscribe +// More: https://docs.tendermint.com/master/rpc/#/Websocket/subscribe func Subscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, error) { addr := ctx.RemoteAddr() @@ -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), )) } @@ -72,7 +79,7 @@ func Subscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, er } // Unsubscribe from events via WebSocket. -// More: https://tendermint.com/rpc/#/Websocket/unsubscribe +// More: https://docs.tendermint.com/master/rpc/#/Websocket/unsubscribe func Unsubscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultUnsubscribe, error) { addr := ctx.RemoteAddr() logger.Info("Unsubscribe from query", "remote", addr, "query", query) @@ -88,7 +95,7 @@ func Unsubscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultUnsubscribe } // UnsubscribeAll from all events via WebSocket. -// More: https://tendermint.com/rpc/#/Websocket/unsubscribe_all +// More: https://docs.tendermint.com/master/rpc/#/Websocket/unsubscribe_all func UnsubscribeAll(ctx *rpctypes.Context) (*ctypes.ResultUnsubscribe, error) { addr := ctx.RemoteAddr() logger.Info("Unsubscribe from all", "remote", addr) diff --git a/rpc/core/evidence.go b/rpc/core/evidence.go index 7edda8b57..4ae138e7e 100644 --- a/rpc/core/evidence.go +++ b/rpc/core/evidence.go @@ -7,7 +7,7 @@ import ( ) // BroadcastEvidence broadcasts evidence of the misbehavior. -// More: https://tendermint.com/rpc/#/Info/broadcast_evidence +// More: https://docs.tendermint.com/master/rpc/#/Info/broadcast_evidence func BroadcastEvidence(ctx *rpctypes.Context, ev types.Evidence) (*ctypes.ResultBroadcastEvidence, error) { err := evidencePool.AddEvidence(ev) if err != nil { diff --git a/rpc/core/health.go b/rpc/core/health.go index 3d4f70dd1..eb715bea0 100644 --- a/rpc/core/health.go +++ b/rpc/core/health.go @@ -7,7 +7,7 @@ import ( // Health gets node health. Returns empty result (200 OK) on success, no // response - in case of an error. -// More: https://tendermint.com/rpc/#/Info/health +// More: https://docs.tendermint.com/master/rpc/#/Info/health func Health(ctx *rpctypes.Context) (*ctypes.ResultHealth, error) { return &ctypes.ResultHealth{}, nil } diff --git a/rpc/core/mempool.go b/rpc/core/mempool.go index 03123d057..28b73ab33 100644 --- a/rpc/core/mempool.go +++ b/rpc/core/mempool.go @@ -19,7 +19,7 @@ import ( // BroadcastTxAsync returns right away, with no response. Does not wait for // CheckTx nor DeliverTx results. -// More: https://tendermint.com/rpc/#/Tx/broadcast_tx_async +// More: https://docs.tendermint.com/master/rpc/#/Tx/broadcast_tx_async func BroadcastTxAsync(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { err := mempool.CheckTx(tx, nil, mempl.TxInfo{}) @@ -31,7 +31,7 @@ func BroadcastTxAsync(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadca // BroadcastTxSync returns with the response from CheckTx. Does not wait for // DeliverTx result. -// More: https://tendermint.com/rpc/#/Tx/broadcast_tx_sync +// More: https://docs.tendermint.com/master/rpc/#/Tx/broadcast_tx_sync func BroadcastTxSync(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { resCh := make(chan *abci.Response, 1) err := mempool.CheckTx(tx, func(res *abci.Response) { @@ -51,7 +51,7 @@ func BroadcastTxSync(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcas } // BroadcastTxCommit returns with the responses from CheckTx and DeliverTx. -// More: https://tendermint.com/rpc/#/Tx/broadcast_tx_commit +// More: https://docs.tendermint.com/master/rpc/#/Tx/broadcast_tx_commit func BroadcastTxCommit(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { subscriber := ctx.RemoteAddr() @@ -129,7 +129,7 @@ func BroadcastTxCommit(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadc // UnconfirmedTxs gets unconfirmed transactions (maximum ?limit entries) // including their number. -// More: https://tendermint.com/rpc/#/Info/unconfirmed_txs +// More: https://docs.tendermint.com/master/rpc/#/Info/unconfirmed_txs func UnconfirmedTxs(ctx *rpctypes.Context, limit int) (*ctypes.ResultUnconfirmedTxs, error) { // reuse per_page validator limit = validatePerPage(limit) @@ -143,7 +143,7 @@ func UnconfirmedTxs(ctx *rpctypes.Context, limit int) (*ctypes.ResultUnconfirmed } // NumUnconfirmedTxs gets number of unconfirmed transactions. -// More: https://tendermint.com/rpc/#/Info/num_unconfirmed_txs +// More: https://docs.tendermint.com/master/rpc/#/Info/num_unconfirmed_txs func NumUnconfirmedTxs(ctx *rpctypes.Context) (*ctypes.ResultUnconfirmedTxs, error) { return &ctypes.ResultUnconfirmedTxs{ Count: mempool.Size(), diff --git a/rpc/core/net.go b/rpc/core/net.go index 6f3e4bb7f..4a3d67d4f 100644 --- a/rpc/core/net.go +++ b/rpc/core/net.go @@ -11,7 +11,7 @@ import ( ) // NetInfo returns network info. -// More: https://tendermint.com/rpc/#/Info/net_info +// More: https://docs.tendermint.com/master/rpc/#/Info/net_info func NetInfo(ctx *rpctypes.Context) (*ctypes.ResultNetInfo, error) { peersList := p2pPeers.Peers().List() peers := make([]ctypes.Peer, 0, len(peersList)) @@ -69,7 +69,7 @@ func UnsafeDialPeers(ctx *rpctypes.Context, peers []string, persistent bool) (*c } // Genesis returns genesis file. -// More: https://tendermint.com/rpc/#/Info/genesis +// More: https://docs.tendermint.com/master/rpc/#/Info/genesis func Genesis(ctx *rpctypes.Context) (*ctypes.ResultGenesis, error) { return &ctypes.ResultGenesis{Genesis: genDoc}, nil } diff --git a/rpc/core/routes.go b/rpc/core/routes.go index 0e959d6d4..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"), @@ -23,7 +24,7 @@ var Routes = map[string]*rpc.RPCFunc{ "block_results": rpc.NewRPCFunc(BlockResults, "height"), "commit": rpc.NewRPCFunc(Commit, "height"), "tx": rpc.NewRPCFunc(Tx, "hash,prove"), - "tx_search": rpc.NewRPCFunc(TxSearch, "query,prove,page,per_page"), + "tx_search": rpc.NewRPCFunc(TxSearch, "query,prove,page,per_page,order_by"), "validators": rpc.NewRPCFunc(Validators, "height,page,per_page"), "dump_consensus_state": rpc.NewRPCFunc(DumpConsensusState, ""), "consensus_state": rpc.NewRPCFunc(ConsensusState, ""), diff --git a/rpc/core/status.go b/rpc/core/status.go index 36c521f30..e6438009a 100644 --- a/rpc/core/status.go +++ b/rpc/core/status.go @@ -14,7 +14,7 @@ import ( // Status returns Tendermint status including node info, pubkey, latest block // hash, app hash, block height and time. -// More: https://tendermint.com/rpc/#/Info/status +// More: https://docs.tendermint.com/master/rpc/#/Info/status func Status(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) { var latestHeight int64 if consensusReactor.FastSync() { @@ -22,6 +22,7 @@ func Status(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) { } else { latestHeight = consensusState.GetLastHeight() } + var ( latestBlockMeta *types.BlockMeta latestBlockHash tmbytes.HexBytes diff --git a/rpc/core/tx.go b/rpc/core/tx.go index 26d5364f4..e7e2582f6 100644 --- a/rpc/core/tx.go +++ b/rpc/core/tx.go @@ -2,9 +2,11 @@ package core import ( "fmt" + "sort" - tmmath "github.com/tendermint/tendermint/libs/math" + "github.com/pkg/errors" + tmmath "github.com/tendermint/tendermint/libs/math" tmquery "github.com/tendermint/tendermint/libs/pubsub/query" ctypes "github.com/tendermint/tendermint/rpc/core/types" rpctypes "github.com/tendermint/tendermint/rpc/lib/types" @@ -15,9 +17,8 @@ import ( // Tx allows you to query the transaction results. `nil` could mean the // transaction is in the mempool, invalidated, or was not sent in the first // place. -// More: https://tendermint.com/rpc/#/Info/tx +// More: https://docs.tendermint.com/master/rpc/#/Info/tx func Tx(ctx *rpctypes.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) { - // if index is disabled, return error if _, ok := txIndexer.(*null.TxIndex); ok { return nil, fmt.Errorf("transaction indexing is disabled") @@ -53,11 +54,12 @@ func Tx(ctx *rpctypes.Context, hash []byte, prove bool) (*ctypes.ResultTx, error // TxSearch allows you to query for multiple transactions results. It returns a // list of transactions (maximum ?per_page entries) and the total count. -// More: https://tendermint.com/rpc/#/Info/tx_search -func TxSearch(ctx *rpctypes.Context, query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) { +// More: https://docs.tendermint.com/master/rpc/#/Info/tx_search +func TxSearch(ctx *rpctypes.Context, query string, prove bool, page, perPage int, orderBy string) ( + *ctypes.ResultTxSearch, error) { // if index is disabled, return error if _, ok := txIndexer.(*null.TxIndex); ok { - return nil, fmt.Errorf("transaction indexing is disabled") + return nil, errors.New("transaction indexing is disabled") } q, err := tmquery.New(query) @@ -65,11 +67,32 @@ 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 } + // sort results (must be done before pagination) + switch orderBy { + case "desc": + sort.Slice(results, func(i, j int) bool { + if results[i].Height == results[j].Height { + return results[i].Index > results[j].Index + } + return results[i].Height > results[j].Height + }) + case "asc", "": + sort.Slice(results, func(i, j int) bool { + if results[i].Height == results[j].Height { + return results[i].Index < results[j].Index + } + return results[i].Height < results[j].Height + }) + default: + return nil, errors.New("expected order_by to be either `asc` or `desc` or empty") + } + + // paginate results totalCount := len(results) perPage = validatePerPage(perPage) page, err = validatePage(page, perPage, totalCount) @@ -77,28 +100,26 @@ func TxSearch(ctx *rpctypes.Context, query string, prove bool, page, perPage int return nil, err } skipCount := validateSkipCount(page, perPage) + pageSize := tmmath.MinInt(perPage, totalCount-skipCount) - apiResults := make([]*ctypes.ResultTx, tmmath.MinInt(perPage, totalCount-skipCount)) - var proof types.TxProof - // if there's no tx in the results array, we don't need to loop through the apiResults array - for i := 0; i < len(apiResults); i++ { - r := results[skipCount+i] - height := r.Height - index := r.Index + apiResults := make([]*ctypes.ResultTx, 0, pageSize) + for i := skipCount; i < skipCount+pageSize; i++ { + r := results[i] + var proof types.TxProof if prove { - block := blockStore.LoadBlock(height) - proof = block.Data.Txs.Proof(int(index)) // XXX: overflow on 32-bit machines + block := blockStore.LoadBlock(r.Height) + proof = block.Data.Txs.Proof(int(r.Index)) // XXX: overflow on 32-bit machines } - apiResults[i] = &ctypes.ResultTx{ + apiResults = append(apiResults, &ctypes.ResultTx{ Hash: r.Tx.Hash(), - Height: height, - Index: index, + Height: r.Height, + Index: r.Index, TxResult: r.Result, Tx: r.Tx, Proof: proof, - } + }) } return &ctypes.ResultTxSearch{Txs: apiResults, TotalCount: totalCount}, nil diff --git a/rpc/grpc/types.pb.go b/rpc/grpc/types.pb.go index 41cbc8488..f7fdf6b53 100644 --- a/rpc/grpc/types.pb.go +++ b/rpc/grpc/types.pb.go @@ -226,29 +226,29 @@ func init() { proto.RegisterFile("rpc/grpc/types.proto", fileDescriptor_15f63baa func init() { golang_proto.RegisterFile("rpc/grpc/types.proto", fileDescriptor_15f63baabf91876a) } var fileDescriptor_15f63baabf91876a = []byte{ - // 346 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x29, 0x2a, 0x48, 0xd6, - 0x4f, 0x07, 0x11, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0xc2, - 0x25, 0xa9, 0x79, 0x29, 0xa9, 0x45, 0xb9, 0x99, 0x79, 0x25, 0x7a, 0x45, 0x05, 0xc9, 0x7a, 0x20, - 0x05, 0x52, 0xba, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0xe9, 0xf9, - 0xe9, 0xf9, 0xfa, 0x60, 0xb5, 0x49, 0xa5, 0x69, 0x60, 0x1e, 0x98, 0x03, 0x66, 0x41, 0xcc, 0x90, - 0x32, 0x47, 0x52, 0x8e, 0x30, 0x0e, 0x99, 0x99, 0x98, 0x94, 0x9c, 0x09, 0xb1, 0x16, 0xd9, 0x72, - 0x25, 0x5e, 0x2e, 0xee, 0xa0, 0xd4, 0xc2, 0xd2, 0xd4, 0xe2, 0x92, 0x80, 0xcc, 0xbc, 0x74, 0x25, - 0x15, 0x2e, 0x21, 0x28, 0xd7, 0xa9, 0x28, 0x3f, 0x31, 0x25, 0x39, 0xb1, 0xb8, 0x24, 0xa4, 0x42, - 0x88, 0x8f, 0x8b, 0xa9, 0xa4, 0x42, 0x82, 0x51, 0x81, 0x51, 0x83, 0x27, 0x88, 0xa9, 0xa4, 0x42, - 0x89, 0x8f, 0x8b, 0x27, 0x28, 0xb5, 0xb8, 0x20, 0x3f, 0xaf, 0x38, 0x15, 0xac, 0x6b, 0x21, 0x23, - 0x97, 0x30, 0x4c, 0x00, 0x59, 0x9f, 0x23, 0x17, 0x47, 0x72, 0x46, 0x6a, 0x72, 0x76, 0x3c, 0x54, - 0x37, 0xb7, 0x91, 0x9a, 0x1e, 0x92, 0x67, 0x41, 0x4e, 0xd2, 0x83, 0x38, 0x06, 0xa6, 0xdb, 0x19, - 0xa4, 0x3c, 0xa4, 0x22, 0x88, 0x3d, 0x19, 0xc2, 0x10, 0x72, 0xe7, 0xe2, 0x4a, 0x49, 0xcd, 0xc9, - 0x2c, 0x4b, 0x2d, 0x02, 0x19, 0xc2, 0x04, 0x36, 0x44, 0x83, 0x80, 0x21, 0x2e, 0x10, 0x0d, 0x21, - 0x15, 0x41, 0x9c, 0x29, 0x30, 0xa6, 0xd1, 0x5e, 0x46, 0x2e, 0x1e, 0xb8, 0xdb, 0x1c, 0x03, 0x3c, - 0x85, 0xbc, 0xb9, 0x58, 0x40, 0x8e, 0x17, 0x52, 0xd0, 0xc3, 0x12, 0xfe, 0x7a, 0x48, 0x81, 0x22, - 0xa5, 0x88, 0x43, 0x05, 0x22, 0x04, 0x84, 0x12, 0xb8, 0xb8, 0x91, 0x3d, 0xae, 0x8e, 0xcf, 0x4c, - 0x24, 0x85, 0x52, 0x1a, 0x78, 0x8d, 0x46, 0x52, 0xe9, 0x14, 0xf8, 0xe3, 0xa1, 0x1c, 0xe3, 0x8a, - 0x47, 0x72, 0x8c, 0x3b, 0x1e, 0xc9, 0x31, 0x9e, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, - 0x83, 0x47, 0x72, 0x8c, 0x07, 0x1e, 0xcb, 0x31, 0x46, 0x19, 0x13, 0x8c, 0x7f, 0x58, 0xd2, 0xb3, - 0x4e, 0xce, 0x2f, 0x4a, 0x8d, 0x07, 0xb1, 0x92, 0xd8, 0xc0, 0x49, 0xc0, 0x18, 0x10, 0x00, 0x00, - 0xff, 0xff, 0xe9, 0x1d, 0x64, 0xc2, 0x97, 0x02, 0x00, 0x00, + // 344 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0xb1, 0x4e, 0xf3, 0x30, + 0x14, 0x85, 0xe5, 0xea, 0xd7, 0x0f, 0xdc, 0x96, 0x0e, 0x2e, 0x42, 0x28, 0x83, 0x55, 0x2a, 0x54, + 0x3a, 0x39, 0x52, 0x19, 0x99, 0x5a, 0x90, 0x10, 0x62, 0xa9, 0xa2, 0x4e, 0x2c, 0x25, 0x75, 0xac, + 0x34, 0x82, 0xc6, 0xc6, 0x71, 0x51, 0xfa, 0x38, 0x6c, 0x3c, 0x02, 0x0b, 0x12, 0x23, 0x23, 0x8f, + 0x00, 0xe1, 0x25, 0x18, 0x91, 0x93, 0x86, 0x78, 0x80, 0xb2, 0x44, 0x27, 0xd6, 0x39, 0x9f, 0xce, + 0xbd, 0xba, 0xb0, 0xa3, 0x24, 0x73, 0x43, 0xf3, 0xd1, 0x4b, 0xc9, 0x13, 0x2a, 0x95, 0xd0, 0x02, + 0xb7, 0x34, 0x8f, 0x03, 0xae, 0xe6, 0x51, 0xac, 0xa9, 0x92, 0x8c, 0x1a, 0x83, 0xd3, 0xd5, 0xb3, + 0x48, 0x05, 0x13, 0xe9, 0x2b, 0xbd, 0x74, 0x73, 0x9f, 0x1b, 0x8a, 0x50, 0x54, 0xaa, 0x08, 0x3b, + 0xbb, 0xfe, 0x94, 0x45, 0x05, 0xce, 0x86, 0x76, 0xb6, 0xa1, 0xee, 0xf1, 0xdb, 0x05, 0x4f, 0xf4, + 0x28, 0x8a, 0xc3, 0xce, 0x01, 0xe0, 0xd5, 0xef, 0x50, 0x09, 0x3f, 0x60, 0x7e, 0xa2, 0xc7, 0x29, + 0x6e, 0x42, 0x4d, 0xa7, 0x7b, 0xa8, 0x8d, 0x7a, 0x0d, 0xaf, 0xa6, 0xd3, 0x4e, 0x13, 0x1a, 0x1e, + 0x4f, 0xa4, 0x88, 0x13, 0x9e, 0xa7, 0xee, 0x11, 0xb4, 0xca, 0x07, 0x3b, 0x37, 0x80, 0x4d, 0x36, + 0xe3, 0xec, 0x7a, 0xb2, 0x4a, 0xd7, 0xfb, 0x5d, 0x6a, 0x0d, 0x61, 0x2a, 0xd1, 0xa2, 0x4c, 0x99, + 0x3e, 0x31, 0xf6, 0x71, 0xea, 0x6d, 0xb0, 0x42, 0xe0, 0x33, 0x80, 0x80, 0xdf, 0x44, 0x77, 0x5c, + 0x19, 0x48, 0x2d, 0x87, 0xf4, 0xfe, 0x80, 0x9c, 0x16, 0x81, 0x71, 0xea, 0x6d, 0x05, 0xa5, 0xec, + 0x3f, 0x21, 0x68, 0x7c, 0x77, 0x1b, 0x8c, 0xce, 0xf1, 0x05, 0xfc, 0x33, 0xe5, 0x71, 0x9b, 0xfe, + 0xb0, 0x57, 0x6a, 0x2d, 0xc5, 0xd9, 0xff, 0xc5, 0x51, 0x6d, 0x00, 0x5f, 0x41, 0xdd, 0x1e, 0xfc, + 0x70, 0x1d, 0xd3, 0x32, 0x3a, 0xbd, 0xb5, 0x68, 0xcb, 0x39, 0x1c, 0x7d, 0xbe, 0x13, 0xf4, 0x90, + 0x11, 0xf4, 0x98, 0x11, 0xf4, 0x92, 0x11, 0xf4, 0x9a, 0x11, 0xf4, 0x96, 0x11, 0xf4, 0xfc, 0x41, + 0xd0, 0x65, 0x3f, 0x8c, 0xf4, 0x6c, 0x31, 0xa5, 0x4c, 0xcc, 0xdd, 0x8a, 0x68, 0xcb, 0xf2, 0xa4, + 0x8e, 0x99, 0x50, 0xdc, 0x88, 0xe9, 0xff, 0xfc, 0x02, 0x8e, 0xbe, 0x02, 0x00, 0x00, 0xff, 0xff, + 0x30, 0xfd, 0xaa, 0xac, 0x6e, 0x02, 0x00, 0x00, } func (this *RequestPing) Equal(that interface{}) bool { @@ -1129,6 +1129,7 @@ func (m *ResponseBroadcastTx) Unmarshal(dAtA []byte) error { func skipTypes(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 + depth := 0 for iNdEx < l { var wire uint64 for shift := uint(0); ; shift += 7 { @@ -1160,10 +1161,8 @@ func skipTypes(dAtA []byte) (n int, err error) { break } } - return iNdEx, nil case 1: iNdEx += 8 - return iNdEx, nil case 2: var length int for shift := uint(0); ; shift += 7 { @@ -1184,55 +1183,30 @@ func skipTypes(dAtA []byte) (n int, err error) { return 0, ErrInvalidLengthTypes } iNdEx += length - if iNdEx < 0 { - return 0, ErrInvalidLengthTypes - } - return iNdEx, nil case 3: - for { - var innerWire uint64 - var start int = iNdEx - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return 0, ErrIntOverflowTypes - } - if iNdEx >= l { - return 0, io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - innerWire |= (uint64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } - innerWireType := int(innerWire & 0x7) - if innerWireType == 4 { - break - } - next, err := skipTypes(dAtA[start:]) - if err != nil { - return 0, err - } - iNdEx = start + next - if iNdEx < 0 { - return 0, ErrInvalidLengthTypes - } - } - return iNdEx, nil + depth++ case 4: - return iNdEx, nil + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTypes + } + depth-- case 5: iNdEx += 4 - return iNdEx, nil default: return 0, fmt.Errorf("proto: illegal wireType %d", wireType) } + if iNdEx < 0 { + return 0, ErrInvalidLengthTypes + } + if depth == 0 { + return iNdEx, nil + } } - panic("unreachable") + return 0, io.ErrUnexpectedEOF } var ( - ErrInvalidLengthTypes = fmt.Errorf("proto: negative length found during unmarshaling") - ErrIntOverflowTypes = fmt.Errorf("proto: integer overflow") + ErrInvalidLengthTypes = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTypes = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTypes = fmt.Errorf("proto: unexpected end of group") ) diff --git a/rpc/grpc/types.proto b/rpc/grpc/types.proto index 75054b0d2..fc778cacd 100644 --- a/rpc/grpc/types.proto +++ b/rpc/grpc/types.proto @@ -1,26 +1,25 @@ syntax = "proto3"; package tendermint.rpc.grpc; -option go_package = "github.com/tendermint/tendermint/rpc/grpc;core_grpc"; +option go_package = "github.com/tendermint/tendermint/rpc/grpc;coregrpc"; -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; -import "github.com/tendermint/tendermint/abci/types/types.proto"; +import "third_party/proto/gogoproto/gogo.proto"; +import "abci/types/types.proto"; -option (gogoproto.marshaler_all) = true; -option (gogoproto.unmarshaler_all) = true; -option (gogoproto.sizer_all) = true; +option (gogoproto.marshaler_all) = true; +option (gogoproto.unmarshaler_all) = true; +option (gogoproto.sizer_all) = true; option (gogoproto.goproto_registration) = true; // Generate tests option (gogoproto.populate_all) = true; -option (gogoproto.equal_all) = true; -option (gogoproto.testgen_all) = true; +option (gogoproto.equal_all) = true; +option (gogoproto.testgen_all) = true; //---------------------------------------- // Message types //---------------------------------------- // Request types -message RequestPing { -} +message RequestPing {} message RequestBroadcastTx { bytes tx = 1; @@ -29,11 +28,10 @@ message RequestBroadcastTx { //---------------------------------------- // Response types -message ResponsePing{ -} +message ResponsePing {} -message ResponseBroadcastTx{ - tendermint.abci.types.ResponseCheckTx check_tx = 1; +message ResponseBroadcastTx { + tendermint.abci.types.ResponseCheckTx check_tx = 1; tendermint.abci.types.ResponseDeliverTx deliver_tx = 2; } @@ -41,6 +39,6 @@ message ResponseBroadcastTx{ // Service Definition service BroadcastAPI { - rpc Ping(RequestPing) returns (ResponsePing) ; - rpc BroadcastTx(RequestBroadcastTx) returns (ResponseBroadcastTx) ; + rpc Ping(RequestPing) returns (ResponsePing); + rpc BroadcastTx(RequestBroadcastTx) returns (ResponseBroadcastTx); } diff --git a/rpc/lib/rpc_test.go b/rpc/lib/rpc_test.go index fbf041f57..5b95666a7 100644 --- a/rpc/lib/rpc_test.go +++ b/rpc/lib/rpc_test.go @@ -28,7 +28,7 @@ import ( // Client and Server should work over tcp or unix sockets const ( - tcpAddr = "tcp://0.0.0.0:47768" + tcpAddr = "tcp://127.0.0.1:47768" unixSocket = "/tmp/rpc_test.sock" unixAddr = "unix://" + unixSocket diff --git a/rpc/lib/server/ws_handler.go b/rpc/lib/server/ws_handler.go index 548a244cb..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, @@ -299,8 +299,6 @@ func (wsc *wsConnection) Context() context.Context { // Read from the socket and subscribe to or unsubscribe from events func (wsc *wsConnection) readRoutine() { - var request types.RPCRequest - defer func() { if r := recover(); r != nil { err, ok := r.(error) @@ -308,7 +306,7 @@ func (wsc *wsConnection) readRoutine() { err = fmt.Errorf("WSJSONRPC: %v", r) } wsc.Logger.Error("Panic in WSJSONRPC handler", "err", err, "stack", string(debug.Stack())) - wsc.WriteRPCResponse(types.RPCInternalError(request.ID, err)) + wsc.WriteRPCResponse(types.RPCInternalError(types.JSONRPCIntID(-1), err)) go wsc.readRoutine() } }() @@ -339,6 +337,7 @@ func (wsc *wsConnection) readRoutine() { return } + var request types.RPCRequest err = json.Unmarshal(in, &request) if err != nil { wsc.WriteRPCResponse(types.RPCParseError(errors.Wrap(err, "error unmarshaling request"))) diff --git a/rpc/lib/test/main.go b/rpc/lib/test/main.go index 891f9388d..a7141048c 100644 --- a/rpc/lib/test/main.go +++ b/rpc/lib/test/main.go @@ -37,7 +37,7 @@ func main() { rpcserver.RegisterRPCFuncs(mux, routes, cdc, logger) config := rpcserver.DefaultConfig() - listener, err := rpcserver.Listen("0.0.0.0:8008", config) + listener, err := rpcserver.Listen("tcp://127.0.0.1:8008", config) if err != nil { tmos.Exit(err.Error()) } diff --git a/rpc/swagger/swagger.yaml b/rpc/swagger/swagger.yaml index f56380450..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 @@ -859,6 +859,14 @@ paths: type: number default: 30 example: 30 + - in: query + name: order_by + description: Order in which transactions are sorted ("asc" or "desc"), by height & index. If empty, default sorting will be still applied. + required: false + schema: + type: string + default: "asc" + example: "asc" tags: - Info description: | diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 0b25b2e5f..46aea59e1 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -85,9 +85,9 @@ func randPort() int { } func makeAddrs() (string, string, string) { - return fmt.Sprintf("tcp://0.0.0.0:%d", randPort()), - fmt.Sprintf("tcp://0.0.0.0:%d", randPort()), - fmt.Sprintf("tcp://0.0.0.0:%d", randPort()) + return fmt.Sprintf("tcp://127.0.0.1:%d", randPort()), + fmt.Sprintf("tcp://127.0.0.1:%d", randPort()), + fmt.Sprintf("tcp://127.0.0.1:%d", randPort()) } func createConfig() *cfg.Config { diff --git a/scripts/protocgen.sh b/scripts/protocgen.sh new file mode 100644 index 000000000..e6226dc1a --- /dev/null +++ b/scripts/protocgen.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -eo pipefail + +proto_dirs=$(find . -path ./third_party -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) +for dir in $proto_dirs; do + protoc \ + -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 diff --git a/state/helpers_test.go b/state/helpers_test.go index f7cff0f0f..8bfe803c5 100644 --- a/state/helpers_test.go +++ b/state/helpers_test.go @@ -86,7 +86,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/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 ce894f25b..c5ba99ca3 100644 --- a/state/txindex/kv/kv.go +++ b/state/txindex/kv/kv.go @@ -2,9 +2,9 @@ package kv import ( "bytes" + "context" "encoding/hex" "fmt" - "sort" "strconv" "strings" "time" @@ -160,13 +160,26 @@ func (txi *TxIndex) indexEvents(result *types.TxResult, hash []byte, store dbm.S } } -// Search performs a search using the given query. It breaks the query into -// conditions (like "tx.height > 5"). For each condition, it queries the DB -// index. One special use cases here: (1) if "tx.hash" is found, it returns tx -// result for it (2) for range queries it is 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. -func (txi *TxIndex) Search(q *query.Query) ([]*types.TxResult, error) { +// Search performs a search using the given query. +// +// It breaks the query into conditions (like "tx.height > 5"). For each +// condition, it queries the DB index. One special use cases here: (1) if +// "tx.hash" is found, it returns tx result for it (2) for range queries it is +// 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. +// +// 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) @@ -204,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 @@ -213,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) } } } @@ -228,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 @@ -237,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) } } @@ -248,15 +261,14 @@ 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) - } - // sort by height & index by default - sort.Slice(results, func(i, j int) bool { - if results[i].Height == results[j].Height { - return results[i].Index < results[j].Index + // Potentially exit early. + select { + case <-ctx.Done(): + break + default: } - return results[i].Height < results[j].Height - }) + } return results, nil } @@ -381,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, @@ -404,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: @@ -424,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") @@ -445,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: + } } } @@ -457,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, @@ -510,6 +545,13 @@ LOOP: // break // } } + + // Potentially exit early. + select { + case <-ctx.Done(): + break + default: + } } if len(tmpHashes) == 0 || firstRun { @@ -528,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 e6c077917..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,33 +294,12 @@ func TestTxSearchMultipleTxs(t *testing.T) { err = indexer.Index(txResult4) require.NoError(t, err) - results, err := indexer.Search(query.MustParse("account.number >= 1")) - assert.NoError(t, err) - - require.Len(t, results, 3) - assert.Equal(t, []*types.TxResult{txResult3, txResult2, txResult}, results) -} - -func TestIndexAllTags(t *testing.T) { - indexer := NewTxIndex(db.NewMemDB(), IndexAllEvents()) - - txResult := txResultWithEvents([]abci.Event{ - {Type: "account", Attributes: []kv.Pair{{Key: []byte("owner"), Value: []byte("Ivan")}}}, - {Type: "account", Attributes: []kv.Pair{{Key: []byte("number"), Value: []byte("1")}}}, - }) - - err := indexer.Index(txResult) - require.NoError(t, err) + ctx := context.Background() - results, err := indexer.Search(query.MustParse("account.number >= 1")) + results, err := indexer.Search(ctx, query.MustParse("account.number >= 1")) assert.NoError(t, err) - assert.Len(t, results, 1) - assert.Equal(t, []*types.TxResult{txResult}, results) - results, err = indexer.Search(query.MustParse("account.owner = 'Ivan'")) - assert.NoError(t, err) - assert.Len(t, results, 1) - assert.Equal(t, []*types.TxResult{txResult}, results) + require.Len(t, results, 3) } func txResultWithEvents(events []abci.Event) *types.TxResult { 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 83331c314..eae63943d 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -119,6 +119,7 @@ func TestValidateBlockCommit(t *testing.T) { state.Validators, privVals[proposerAddr.String()], chainID, + time.Now(), ) require.NoError(t, err, "height %d", height) wrongHeightCommit := types.NewCommit( @@ -166,7 +167,13 @@ 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) badVote := &types.Vote{ ValidatorAddress: badPrivVal.GetPubKey().Address(), diff --git a/test/app/counter_test.sh b/test/app/counter_test.sh index a4f7c83b9..3af2b885f 100755 --- a/test/app/counter_test.sh +++ b/test/app/counter_test.sh @@ -36,11 +36,11 @@ function getCode() { # build grpc client if needed if [[ "$GRPC_BROADCAST_TX" != "" ]]; then - if [ -f grpc_client ]; then - rm grpc_client + if [ -f test/app/grpc_client ]; then + rm test/app/grpc_client fi echo "... building grpc_client" - go build -mod=readonly -o grpc_client grpc_client.go + go build -mod=readonly -o test/app/grpc_client test/app/grpc_client.go fi function sendTx() { @@ -59,7 +59,7 @@ function sendTx() { RESPONSE=$(echo "$RESPONSE" | jq '.result') else - RESPONSE=$(./grpc_client "$TX") + RESPONSE=$(./test/app/grpc_client "$TX") IS_ERR=false ERROR="" fi diff --git a/test/app/test.sh b/test/app/test.sh index 0f77da04e..dc60bfc1f 100755 --- a/test/app/test.sh +++ b/test/app/test.sh @@ -22,7 +22,7 @@ function kvstore_over_socket(){ sleep 5 echo "running test" - bash kvstore_test.sh "KVStore over Socket" + bash test/app/kvstore_test.sh "KVStore over Socket" kill -9 $pid_kvstore $pid_tendermint } @@ -40,7 +40,7 @@ function kvstore_over_socket_reorder(){ sleep 5 echo "running test" - bash kvstore_test.sh "KVStore over Socket" + bash test/app/kvstore_test.sh "KVStore over Socket" kill -9 $pid_kvstore $pid_tendermint } @@ -57,7 +57,7 @@ function counter_over_socket() { sleep 5 echo "running test" - bash counter_test.sh "Counter over Socket" + bash test/app/counter_test.sh "Counter over Socket" kill -9 $pid_counter $pid_tendermint } @@ -73,7 +73,7 @@ function counter_over_grpc() { sleep 5 echo "running test" - bash counter_test.sh "Counter over GRPC" + bash test/app/counter_test.sh "Counter over GRPC" kill -9 $pid_counter $pid_tendermint } @@ -91,13 +91,11 @@ function counter_over_grpc_grpc() { sleep 5 echo "running test" - GRPC_BROADCAST_TX=true bash counter_test.sh "Counter over GRPC via GRPC BroadcastTx" + GRPC_BROADCAST_TX=true bash test/app/counter_test.sh "Counter over GRPC via GRPC BroadcastTx" kill -9 $pid_counter $pid_tendermint } -cd $GOPATH/src/github.com/tendermint/tendermint/test/app - case "$1" in "kvstore_over_socket") kvstore_over_socket diff --git a/test/p2p/README.md b/test/p2p/README.md index 956ce906c..1ebf4c17f 100644 --- a/test/p2p/README.md +++ b/test/p2p/README.md @@ -4,7 +4,7 @@ These scripts facilitate setting up and testing a local testnet using docker con Setup your own local testnet as follows. -For consistency, we assume all commands are run from the Tendermint repository root (ie. $GOPATH/src/github.com/tendermint/tendermint). +For consistency, we assume all commands are run from the Tendermint repository root. First, build the docker image: @@ -49,3 +49,18 @@ We can confirm they are making blocks by checking the `/status` message using `c ``` curl 172.57.0.101:26657/status | jq . ``` + +## IPv6 tests + +IPv6 tests require a Docker daemon with IPv6 enabled, by setting the following in `daemon.json`: + +```json +{ + "ipv6": true, + "fixed-cidr-v6": "2001:db8:1::/64" +} +``` + +In Docker for Mac, this is done via Preferences → Docker Engine. + +Once set, run IPv6 tests via `make test_p2p_ipv6`. \ No newline at end of file diff --git a/test/p2p/address.sh b/test/p2p/address.sh new file mode 100755 index 000000000..0b0248db2 --- /dev/null +++ b/test/p2p/address.sh @@ -0,0 +1,28 @@ +#! /bin/bash +set -eu + +IPV=$1 +ID=$2 +PORT=${3:-} +DOCKER_IMAGE=${4:-} + +if [[ "$IPV" == 6 ]]; then + ADDRESS="fd80:b10c::" +else + ADDRESS="172.57.0." +fi +ADDRESS="$ADDRESS$((100+$ID))" + +if [[ -n "$PORT" ]]; then + if [[ "$IPV" == 6 ]]; then + ADDRESS="[$ADDRESS]" + fi + ADDRESS="$ADDRESS:$PORT" +fi + +if [[ -n "$DOCKER_IMAGE" ]]; then + NODEID="$(docker run --rm -e TMHOME=/go/src/github.com/tendermint/tendermint/test/p2p/data/mach$((ID-1)) $DOCKER_IMAGE tendermint show_node_id)" + ADDRESS="$NODEID@$ADDRESS" +fi + +echo $ADDRESS \ No newline at end of file diff --git a/test/p2p/atomic_broadcast/test.sh b/test/p2p/atomic_broadcast/test.sh index f066707d3..a93067c3d 100644 --- a/test/p2p/atomic_broadcast/test.sh +++ b/test/p2p/atomic_broadcast/test.sh @@ -1,7 +1,8 @@ #! /bin/bash set -u -N=$1 +IPV=$1 +N=$2 ################################################################### # assumes peers are already synced up @@ -14,7 +15,7 @@ N=$1 echo "" # run the test on each of them for i in $(seq 1 "$N"); do - addr=$(test/p2p/ip.sh "$i"):26657 + addr=$(test/p2p/address.sh $IPV $i 26657) # current state HASH1=$(curl -s "$addr/status" | jq .result.sync_info.latest_app_hash) @@ -37,7 +38,7 @@ for i in $(seq 1 "$N"); do minHeight=$h2 for j in $(seq 1 "$N"); do if [[ "$i" != "$j" ]]; then - addrJ=$(test/p2p/ip.sh "$j"):26657 + addrJ=$(test/p2p/address.sh $IPV $j 26657) h=$(curl -s "$addrJ/status" | jq .result.sync_info.latest_block_height | jq fromjson) while [ "$h" -lt "$minHeight" ]; do @@ -57,7 +58,7 @@ for i in $(seq 1 "$N"); do # check we get the same new hash on all other nodes for j in $(seq 1 "$N"); do if [[ "$i" != "$j" ]]; then - addrJ=$(test/p2p/ip.sh "$j"):26657 + addrJ=$(test/p2p/address.sh $IPV $j 26657) HASH3=$(curl -s "$addrJ/status" | jq .result.sync_info.latest_app_hash) if [[ "$HASH2" != "$HASH3" ]]; then diff --git a/test/p2p/basic/test.sh b/test/p2p/basic/test.sh index 423b5b017..676b5cbe6 100755 --- a/test/p2p/basic/test.sh +++ b/test/p2p/basic/test.sh @@ -1,7 +1,8 @@ #! /bin/bash set -u -N=$1 +IPV=$1 +N=$2 ################################################################### # wait for all peers to come online @@ -16,7 +17,7 @@ MAX_SLEEP=60 # wait for everyone to come online echo "Waiting for nodes to come online" for i in `seq 1 $N`; do - addr=$(test/p2p/ip.sh $i):26657 + addr=$(test/p2p/address.sh $IPV $i 26657) curl -s $addr/status > /dev/null ERR=$? COUNT=0 @@ -36,7 +37,7 @@ done echo "" # wait for each of them to sync up for i in `seq 1 $N`; do - addr=$(test/p2p/ip.sh $i):26657 + addr=$(test/p2p/address.sh $IPV $i 26657) N_1=$(($N - 1)) # - assert everyone has N-1 other peers diff --git a/test/p2p/circleci.sh b/test/p2p/circleci.sh index c548d5752..fe8a972bd 100644 --- a/test/p2p/circleci.sh +++ b/test/p2p/circleci.sh @@ -1,11 +1,27 @@ #! /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 +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" +} +EOF + sudo service docker restart +fi + LOGS_DIR="$DIR/logs" echo echo "* [$(date +"%T")] cleaning up $LOGS_DIR" @@ -31,8 +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 "* [$(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/test/p2p/client.sh b/test/p2p/client.sh index fa11ce870..b3c907fba 100644 --- a/test/p2p/client.sh +++ b/test/p2p/client.sh @@ -3,17 +3,24 @@ set -eu DOCKER_IMAGE=$1 NETWORK_NAME=$2 -ID=$3 -CMD=$4 +IPV=$3 +ID=$4 +CMD=$5 NAME=test_container_$ID +if [[ "$IPV" == 6 ]]; then + IP_SWITCH="--ip6" +else + IP_SWITCH="--ip" +fi + echo "starting test client container with CMD=$CMD" # run the test container on the local network docker run -t --rm \ - -v "$GOPATH/src/github.com/tendermint/tendermint/test/p2p/:/go/src/github.com/tendermint/tendermint/test/p2p" \ + -v "$PWD/test/p2p/:/go/src/github.com/tendermint/tendermint/test/p2p" \ --net="$NETWORK_NAME" \ - --ip=$(test/p2p/ip.sh "-1") \ + $IP_SWITCH=$(test/p2p/address.sh $IPV -1) \ --name "$NAME" \ --entrypoint bash \ "$DOCKER_IMAGE" $CMD diff --git a/test/p2p/fast_sync/check_peer.sh b/test/p2p/fast_sync/check_peer.sh index d5d3fc2b5..798b508fa 100644 --- a/test/p2p/fast_sync/check_peer.sh +++ b/test/p2p/fast_sync/check_peer.sh @@ -2,7 +2,8 @@ set -eu set -o pipefail -ID=$1 +IPV=$1 +ID=$2 ########################################### # @@ -10,9 +11,9 @@ ID=$1 # ########################################### -addr=$(test/p2p/ip.sh $ID):26657 +addr=$(test/p2p/address.sh $IPV $ID 26657) peerID=$(( $(($ID % 4)) + 1 )) # 1->2 ... 3->4 ... 4->1 -peer_addr=$(test/p2p/ip.sh $peerID):26657 +peer_addr=$(test/p2p/address.sh $IPV $peerID 26657) # get another peer's height h1=`curl -s $peer_addr/status | jq .result.sync_info.latest_block_height | jq fromjson` diff --git a/test/p2p/fast_sync/test.sh b/test/p2p/fast_sync/test.sh index 8820d199c..79655232f 100644 --- a/test/p2p/fast_sync/test.sh +++ b/test/p2p/fast_sync/test.sh @@ -3,14 +3,13 @@ set -eu DOCKER_IMAGE=$1 NETWORK_NAME=$2 -N=$3 -PROXY_APP=$4 - -cd $GOPATH/src/github.com/tendermint/tendermint +IPV=$3 +N=$4 +PROXY_APP=$5 # run it on each of them for i in `seq 1 $N`; do - bash test/p2p/fast_sync/test_peer.sh $DOCKER_IMAGE $NETWORK_NAME $i $N $PROXY_APP + bash test/p2p/fast_sync/test_peer.sh $DOCKER_IMAGE $NETWORK_NAME $IPV $i $N $PROXY_APP done diff --git a/test/p2p/fast_sync/test_peer.sh b/test/p2p/fast_sync/test_peer.sh index 08ea9deb1..b4c34336f 100644 --- a/test/p2p/fast_sync/test_peer.sh +++ b/test/p2p/fast_sync/test_peer.sh @@ -3,9 +3,10 @@ set -eu DOCKER_IMAGE=$1 NETWORK_NAME=$2 -ID=$3 -N=$4 -PROXY_APP=$5 +IPV=$3 +ID=$4 +N=$5 +PROXY_APP=$6 ############################################################### # this runs on each peer: @@ -23,14 +24,14 @@ set +e # circle sigh :( set -e # restart peer - should have an empty blockchain - PERSISTENT_PEERS="$(test/p2p/ip_plus_id.sh 1 $DOCKER_IMAGE):26656" + PERSISTENT_PEERS="$(test/p2p/address.sh $IPV 1 26656 $DOCKER_IMAGE)" for j in `seq 2 $N`; do - PERSISTENT_PEERS="$PERSISTENT_PEERS,$(test/p2p/ip_plus_id.sh $j $DOCKER_IMAGE):26656" + PERSISTENT_PEERS="$PERSISTENT_PEERS,$(test/p2p/address.sh $IPV $j 26656 $DOCKER_IMAGE)" done - bash test/p2p/peer.sh $DOCKER_IMAGE $NETWORK_NAME $ID $PROXY_APP "--p2p.persistent_peers $PERSISTENT_PEERS --p2p.pex --rpc.unsafe" + bash test/p2p/peer.sh $DOCKER_IMAGE $NETWORK_NAME $IPV $ID $PROXY_APP "--p2p.persistent_peers $PERSISTENT_PEERS --p2p.pex --rpc.unsafe" # wait for peer to sync and check the app hash - bash test/p2p/client.sh $DOCKER_IMAGE $NETWORK_NAME fs_$ID "test/p2p/fast_sync/check_peer.sh $ID" + bash test/p2p/client.sh $DOCKER_IMAGE $NETWORK_NAME $IPV fs_$ID "test/p2p/fast_sync/check_peer.sh $IPV $ID" echo "" echo "PASS" diff --git a/test/p2p/ip.sh b/test/p2p/ip.sh deleted file mode 100755 index 77753f541..000000000 --- a/test/p2p/ip.sh +++ /dev/null @@ -1,5 +0,0 @@ -#! /bin/bash -set -eu - -ID=$1 -echo "172.57.0.$((100+$ID))" diff --git a/test/p2p/ip_plus_id.sh b/test/p2p/ip_plus_id.sh deleted file mode 100755 index 95871d3f1..000000000 --- a/test/p2p/ip_plus_id.sh +++ /dev/null @@ -1,7 +0,0 @@ -#! /bin/bash -set -eu - -ID=$1 -DOCKER_IMAGE=$2 -NODEID="$(docker run --rm -e TMHOME=/go/src/github.com/tendermint/tendermint/test/p2p/data/mach$((ID-1)) $DOCKER_IMAGE tendermint show_node_id)" -echo "$NODEID@172.57.0.$((100+$ID))" diff --git a/test/p2p/kill_all/check_peers.sh b/test/p2p/kill_all/check_peers.sh index 95da7484e..504cdeddd 100644 --- a/test/p2p/kill_all/check_peers.sh +++ b/test/p2p/kill_all/check_peers.sh @@ -1,7 +1,8 @@ #! /bin/bash set -eu -NUM_OF_PEERS=$1 +IPV=$1 +NUM_OF_PEERS=$2 # how many attempts for each peer to catch up by height MAX_ATTEMPTS_TO_CATCH_UP=120 @@ -9,7 +10,7 @@ MAX_ATTEMPTS_TO_CATCH_UP=120 echo "Waiting for nodes to come online" set +e for i in $(seq 1 "$NUM_OF_PEERS"); do - addr=$(test/p2p/ip.sh "$i"):26657 + addr=$(test/p2p/address.sh $IPV $i 26657) curl -s "$addr/status" > /dev/null ERR=$? while [ "$ERR" != 0 ]; do @@ -22,7 +23,7 @@ done set -e # get the first peer's height -addr=$(test/p2p/ip.sh 1):26657 +addr=$(test/p2p/address.sh $IPV 1 26657) h1=$(curl -s "$addr/status" | jq .result.sync_info.latest_block_height | sed -e "s/^\"\(.*\)\"$/\1/g") echo "1st peer is on height $h1" @@ -32,7 +33,7 @@ for i in $(seq 2 "$NUM_OF_PEERS"); do hi=0 while [[ $hi -le $h1 ]] ; do - addr=$(test/p2p/ip.sh "$i"):26657 + addr=$(test/p2p/address.sh $IPV $i 26657) hi=$(curl -s "$addr/status" | jq .result.sync_info.latest_block_height | sed -e "s/^\"\(.*\)\"$/\1/g") echo "... peer $i is on height $hi" diff --git a/test/p2p/kill_all/test.sh b/test/p2p/kill_all/test.sh index 318a1fe47..755612130 100644 --- a/test/p2p/kill_all/test.sh +++ b/test/p2p/kill_all/test.sh @@ -3,10 +3,9 @@ set -eu DOCKER_IMAGE=$1 NETWORK_NAME=$2 -NUM_OF_PEERS=$3 -NUM_OF_CRASHES=$4 - -cd "$GOPATH/src/github.com/tendermint/tendermint" +IPV=$3 +NUM_OF_PEERS=$4 +NUM_OF_CRASHES=$5 ############################################################### # NUM_OF_CRASHES times: @@ -24,7 +23,7 @@ for i in $(seq 1 "$NUM_OF_CRASHES"); do docker start "local_testnet_$j" done - bash test/p2p/client.sh "$DOCKER_IMAGE" "$NETWORK_NAME" kill_all_$i "test/p2p/kill_all/check_peers.sh $NUM_OF_PEERS" + bash test/p2p/client.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$IPV" kill_all_$i "test/p2p/kill_all/check_peers.sh $IPV $NUM_OF_PEERS" done echo "" diff --git a/test/p2p/local_testnet_start.sh b/test/p2p/local_testnet_start.sh index 25b3c6d3e..8da6be4bb 100644 --- a/test/p2p/local_testnet_start.sh +++ b/test/p2p/local_testnet_start.sh @@ -3,22 +3,25 @@ set -eu DOCKER_IMAGE=$1 NETWORK_NAME=$2 -N=$3 -APP_PROXY=$4 +IPV=$3 +N=$4 +APP_PROXY=$5 set +u -PERSISTENT_PEERS=$5 +PERSISTENT_PEERS=$6 if [[ "$PERSISTENT_PEERS" != "" ]]; then echo "PersistentPeers: $PERSISTENT_PEERS" PERSISTENT_PEERS="--p2p.persistent_peers $PERSISTENT_PEERS" fi set -u -cd "$GOPATH/src/github.com/tendermint/tendermint" - # create docker network -docker network create --driver bridge --subnet 172.57.0.0/16 "$NETWORK_NAME" +if [[ $IPV == 6 ]]; then + docker network create --driver bridge --ipv6 --subnet fd80:b10c::/48 "$NETWORK_NAME" +else + docker network create --driver bridge --subnet 172.57.0.0/16 "$NETWORK_NAME" +fi for i in $(seq 1 "$N"); do - bash test/p2p/peer.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$i" "$APP_PROXY" "$PERSISTENT_PEERS --p2p.pex --rpc.unsafe" + bash test/p2p/peer.sh "$DOCKER_IMAGE" "$NETWORK_NAME" $IPV "$i" "$APP_PROXY" "$PERSISTENT_PEERS --p2p.pex --rpc.unsafe" done diff --git a/test/p2p/peer.sh b/test/p2p/peer.sh index 63d46f8d5..bf146ca1b 100644 --- a/test/p2p/peer.sh +++ b/test/p2p/peer.sh @@ -3,13 +3,20 @@ set -eu DOCKER_IMAGE=$1 NETWORK_NAME=$2 -ID=$3 -APP_PROXY=$4 +IPV=$3 +ID=$4 +APP_PROXY=$5 set +u -NODE_FLAGS=$5 +NODE_FLAGS=$6 set -u +if [[ "$IPV" == 6 ]]; then + IP_SWITCH="--ip6" +else + IP_SWITCH="--ip" +fi + echo "starting tendermint peer ID=$ID" # start tendermint container on the network # NOTE: $NODE_FLAGS should be unescaped (no quotes). otherwise it will be @@ -20,7 +27,7 @@ echo "starting tendermint peer ID=$ID" if [[ "$ID" == "x" ]]; then # Set "x" to "1" to print to console. docker run \ --net="$NETWORK_NAME" \ - --ip=$(test/p2p/ip.sh "$ID") \ + $IP_SWITCH=$(test/p2p/address.sh $IPV $ID) \ --name "local_testnet_$ID" \ --entrypoint tendermint \ -e TMHOME="/go/src/github.com/tendermint/tendermint/test/p2p/data/mach$((ID-1))" \ @@ -33,7 +40,7 @@ if [[ "$ID" == "x" ]]; then # Set "x" to "1" to print to console. else docker run -d \ --net="$NETWORK_NAME" \ - --ip=$(test/p2p/ip.sh "$ID") \ + $IP_SWITCH=$(test/p2p/address.sh $IPV $ID) \ --name "local_testnet_$ID" \ --entrypoint tendermint \ -e TMHOME="/go/src/github.com/tendermint/tendermint/test/p2p/data/mach$((ID-1))" \ diff --git a/test/p2p/persistent_peers.sh b/test/p2p/persistent_peers.sh index 6d3e1ed66..a1e76991a 100644 --- a/test/p2p/persistent_peers.sh +++ b/test/p2p/persistent_peers.sh @@ -1,13 +1,12 @@ #! /bin/bash set -eu -N=$1 -DOCKER_IMAGE=$2 +IPV=$1 +N=$2 +DOCKER_IMAGE=$3 -cd "$GOPATH/src/github.com/tendermint/tendermint" - -persistent_peers="$(test/p2p/ip_plus_id.sh 1 $DOCKER_IMAGE):26656" +persistent_peers="$(test/p2p/address.sh $IPV 1 26656 $DOCKER_IMAGE)" for i in $(seq 2 $N); do - persistent_peers="$persistent_peers,$(test/p2p/ip_plus_id.sh $i $DOCKER_IMAGE):26656" + persistent_peers="$persistent_peers,$(test/p2p/address.sh $IPV $i 26656 $DOCKER_IMAGE)" done echo "$persistent_peers" diff --git a/test/p2p/pex/check_peer.sh b/test/p2p/pex/check_peer.sh index 7ae42e9b6..93499d9f9 100644 --- a/test/p2p/pex/check_peer.sh +++ b/test/p2p/pex/check_peer.sh @@ -1,10 +1,11 @@ #! /bin/bash set -u -ID=$1 -N=$2 +IPV=$1 +ID=$2 +N=$3 -addr=$(test/p2p/ip.sh "$ID"):26657 +addr=$(test/p2p/address.sh $IPV "$ID" 26657) echo "2. wait until peer $ID connects to other nodes using pex reactor" peers_count="0" diff --git a/test/p2p/pex/dial_peers.sh b/test/p2p/pex/dial_peers.sh index 43bde48b5..8c4d40f44 100644 --- a/test/p2p/pex/dial_peers.sh +++ b/test/p2p/pex/dial_peers.sh @@ -1,14 +1,13 @@ #! /bin/bash set -u -N=$1 -PEERS=$2 - -cd "$GOPATH/src/github.com/tendermint/tendermint" +IPV=$1 +N=$2 +PEERS=$3 echo "Waiting for nodes to come online" for i in $(seq 1 "$N"); do - addr=$(test/p2p/ip.sh "$i"):26657 + addr=$(test/p2p/address.sh $IPV $i 26657) curl -s "$addr/status" > /dev/null ERR=$? while [ "$ERR" != 0 ]; do @@ -19,5 +18,5 @@ for i in $(seq 1 "$N"); do echo "... node $i is up" done -IP=$(test/p2p/ip.sh 1) -curl "$IP:26657/dial_peers?persistent=true&peers=\\[$PEERS\\]" +ADDR=$(test/p2p/address.sh $IPV 1 26657) +curl "$ADDR/dial_peers?persistent=true&peers=\\[$PEERS\\]" diff --git a/test/p2p/pex/test.sh b/test/p2p/pex/test.sh index ffecd6510..1e87a9fa5 100644 --- a/test/p2p/pex/test.sh +++ b/test/p2p/pex/test.sh @@ -3,13 +3,12 @@ set -eu DOCKER_IMAGE=$1 NETWORK_NAME=$2 -N=$3 -PROXY_APP=$4 - -cd "$GOPATH/src/github.com/tendermint/tendermint" +IPV=$3 +N=$4 +PROXY_APP=$5 echo "Test reconnecting from the address book" -bash test/p2p/pex/test_addrbook.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$N" "$PROXY_APP" +bash test/p2p/pex/test_addrbook.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$IPV" "$N" "$PROXY_APP" echo "Test connecting via /dial_peers" -bash test/p2p/pex/test_dial_peers.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$N" "$PROXY_APP" +bash test/p2p/pex/test_dial_peers.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$IPV" "$N" "$PROXY_APP" diff --git a/test/p2p/pex/test_addrbook.sh b/test/p2p/pex/test_addrbook.sh index 06f9212fd..06fc4215f 100644 --- a/test/p2p/pex/test_addrbook.sh +++ b/test/p2p/pex/test_addrbook.sh @@ -3,8 +3,9 @@ set -eu DOCKER_IMAGE=$1 NETWORK_NAME=$2 -N=$3 -PROXY_APP=$4 +IPV=$3 +N=$4 +PROXY_APP=$5 ID=1 @@ -24,11 +25,11 @@ docker rm -vf "local_testnet_$ID" set -e # NOTE that we do not provide persistent_peers -bash test/p2p/peer.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$ID" "$PROXY_APP" "--p2p.pex --rpc.unsafe" +bash test/p2p/peer.sh "$DOCKER_IMAGE" "$NETWORK_NAME" $IPV "$ID" "$PROXY_APP" "--p2p.pex --rpc.unsafe" echo "started local_testnet_$ID" # if the client runs forever, it means addrbook wasn't saved or was empty -bash test/p2p/client.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$CLIENT_NAME" "test/p2p/pex/check_peer.sh $ID $N" +bash test/p2p/client.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$IPV" "$CLIENT_NAME" "test/p2p/pex/check_peer.sh $IPV $ID $N" # Now we know that the node is up. @@ -53,11 +54,11 @@ docker rm -vf "local_testnet_$ID" set -e # NOTE that we do not provide persistent_peers -bash test/p2p/peer.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$ID" "$PROXY_APP" "--p2p.pex --rpc.unsafe" +bash test/p2p/peer.sh "$DOCKER_IMAGE" "$NETWORK_NAME" $IPV "$ID" "$PROXY_APP" "--p2p.pex --rpc.unsafe" echo "started local_testnet_$ID" # if the client runs forever, it means other peers have removed us from their books (which should not happen) -bash test/p2p/client.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$CLIENT_NAME" "test/p2p/pex/check_peer.sh $ID $N" +bash test/p2p/client.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$IPV" "$CLIENT_NAME" "test/p2p/pex/check_peer.sh $IPV $ID $N" # Now we know that the node is up. diff --git a/test/p2p/pex/test_dial_peers.sh b/test/p2p/pex/test_dial_peers.sh index cb6e7e182..af76a5699 100644 --- a/test/p2p/pex/test_dial_peers.sh +++ b/test/p2p/pex/test_dial_peers.sh @@ -3,13 +3,12 @@ set -eu DOCKER_IMAGE=$1 NETWORK_NAME=$2 -N=$3 -PROXY_APP=$4 +IPV=$3 +N=$4 +PROXY_APP=$5 ID=1 -cd $GOPATH/src/github.com/tendermint/tendermint - echo "----------------------------------------------------------------------" echo "Testing full network connection using one /dial_peers call" echo "(assuming peers are started with pex enabled)" @@ -21,19 +20,19 @@ set -e # start the testnet on a local network # NOTE we re-use the same network for all tests -bash test/p2p/local_testnet_start.sh $DOCKER_IMAGE $NETWORK_NAME $N $PROXY_APP "" +bash test/p2p/local_testnet_start.sh $DOCKER_IMAGE $NETWORK_NAME $IPV $N $PROXY_APP "" -PERSISTENT_PEERS="\"$(test/p2p/ip_plus_id.sh 1 $DOCKER_IMAGE):26656\"" +PERSISTENT_PEERS="\"$(test/p2p/address.sh $IPV 1 26656 $DOCKER_IMAGE)\"" for i in $(seq 2 $N); do - PERSISTENT_PEERS="$PERSISTENT_PEERS,\"$(test/p2p/ip_plus_id.sh $i $DOCKER_IMAGE):26656\"" + PERSISTENT_PEERS="$PERSISTENT_PEERS,\"$(test/p2p/address.sh $IPV $i 26656 $DOCKER_IMAGE)\"" done echo "$PERSISTENT_PEERS" # dial peers from one node CLIENT_NAME="dial_peers" -bash test/p2p/client.sh $DOCKER_IMAGE $NETWORK_NAME $CLIENT_NAME "test/p2p/pex/dial_peers.sh $N $PERSISTENT_PEERS" +bash test/p2p/client.sh $DOCKER_IMAGE $NETWORK_NAME $IPV $CLIENT_NAME "test/p2p/pex/dial_peers.sh $IPV $N $PERSISTENT_PEERS" # test basic connectivity and consensus # start client container and check the num peers and height for all nodes CLIENT_NAME="dial_peers_basic" -bash test/p2p/client.sh $DOCKER_IMAGE $NETWORK_NAME $CLIENT_NAME "test/p2p/basic/test.sh $N" +bash test/p2p/client.sh $DOCKER_IMAGE $NETWORK_NAME $IPV $CLIENT_NAME "test/p2p/basic/test.sh $IPV $N" diff --git a/test/p2p/test.sh b/test/p2p/test.sh index abcf2ca07..fe28f02a9 100644 --- a/test/p2p/test.sh +++ b/test/p2p/test.sh @@ -5,34 +5,38 @@ DOCKER_IMAGE=$1 NETWORK_NAME=local_testnet N=4 PROXY_APP=persistent_kvstore +IPV=${2:-4} # Default to IPv4 -cd "$GOPATH/src/github.com/tendermint/tendermint" +if [[ "$IPV" != "4" && "$IPV" != "6" ]]; then + echo "IP version must be 4 or 6" >&2 + exit 1 +fi # stop the existing testnet and remove local network set +e bash test/p2p/local_testnet_stop.sh "$NETWORK_NAME" "$N" set -e -PERSISTENT_PEERS=$(bash test/p2p/persistent_peers.sh $N $DOCKER_IMAGE) +PERSISTENT_PEERS=$(bash test/p2p/persistent_peers.sh $IPV $N $DOCKER_IMAGE) # start the testnet on a local network # NOTE we re-use the same network for all tests -bash test/p2p/local_testnet_start.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$N" "$PROXY_APP" "$PERSISTENT_PEERS" +bash test/p2p/local_testnet_start.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$IPV" "$N" "$PROXY_APP" "$PERSISTENT_PEERS" # test basic connectivity and consensus # start client container and check the num peers and height for all nodes -bash test/p2p/client.sh "$DOCKER_IMAGE" "$NETWORK_NAME" basic "test/p2p/basic/test.sh $N" +bash test/p2p/client.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$IPV" basic "test/p2p/basic/test.sh $IPV $N" # test atomic broadcast: # start client container and test sending a tx to each node -bash test/p2p/client.sh "$DOCKER_IMAGE" "$NETWORK_NAME" ab "test/p2p/atomic_broadcast/test.sh $N" +bash test/p2p/client.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$IPV" ab "test/p2p/atomic_broadcast/test.sh $IPV $N" # test fast sync (from current state of network): # for each node, kill it and readd via fast sync -bash test/p2p/fast_sync/test.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$N" "$PROXY_APP" +bash test/p2p/fast_sync/test.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$IPV" "$N" "$PROXY_APP" # test killing all peers 3 times -bash test/p2p/kill_all/test.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$N" 3 +bash test/p2p/kill_all/test.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$IPV" "$N" 3 # test pex -bash test/p2p/pex/test.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$N" "$PROXY_APP" +bash test/p2p/pex/test.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$IPV" "$N" "$PROXY_APP" diff --git a/tests.mk b/tests.mk index 18caef496..d7e305312 100644 --- a/tests.mk +++ b/tests.mk @@ -8,25 +8,30 @@ BINDIR ?= $(GOPATH)/bin ## required to be run first by most tests build_docker_test_image: docker build -t tester -f ./test/docker/Dockerfile . +.PHONY: build_docker_test_image ### coverage, app, persistence, and libs tests test_cover: # run the go unit tests with coverage bash test/test_cover.sh +.PHONY: test_cover test_apps: # run the app tests using bash # requires `abci-cli` and `tendermint` binaries installed bash test/app/test.sh +.PHONY: test_apps test_abci_apps: bash abci/tests/test_app/test.sh +.PHONY: test_abci_apps test_abci_cli: # test the cli against the examples in the tutorial at: # ./docs/abci-cli.md # if test fails, update the docs ^ @ bash abci/tests/test_cli/test.sh +.PHONY: test_abci_cli test_persistence: # run the persistence tests using bash @@ -35,19 +40,37 @@ test_persistence: # TODO undockerize # bash test/persist/test_failure_indices.sh +.PHONY: test_persistence test_p2p: docker rm -f rsyslog || true - rm -rf test/logs || true - mkdir test/logs - cd test/ - docker run -d -v "logs:/var/log/" -p 127.0.0.1:5514:514/udp --name rsyslog voxxit/rsyslog - cd .. + rm -rf test/logs && mkdir -p test/logs + docker run -d -v "$(CURDIR)/test/logs:/var/log/" -p 127.0.0.1:5514:514/udp --name rsyslog voxxit/rsyslog # requires 'tester' the image from above bash test/p2p/test.sh tester # the `docker cp` takes a really long time; uncomment for debugging # # mkdir -p test/p2p/logs && docker cp rsyslog:/var/log test/p2p/logs +.PHONY: test_p2p + +test_p2p_ipv6: + # IPv6 tests require Docker daemon with IPv6 enabled, e.g. in daemon.json: + # + # { + # "ipv6": true, + # "fixed-cidr-v6": "2001:db8:1::/64" + # } + # + # Docker for Mac can set this via Preferences -> Docker Engine. + docker rm -f rsyslog || true + rm -rf test/logs && mkdir -p test/logs + docker run -d -v "$(CURDIR)/test/logs:/var/log/" -p 127.0.0.1:5514:514/udp --name rsyslog voxxit/rsyslog + # requires 'tester' the image from above + bash test/p2p/test.sh tester 6 + # the `docker cp` takes a really long time; uncomment for debugging + # + # mkdir -p test/p2p/logs && docker cp rsyslog:/var/log test/p2p/logs +.PHONY: test_p2p_ipv6 test_integrations: make build_docker_test_image @@ -60,31 +83,40 @@ test_integrations: make test_libs make test_persistence make test_p2p + # Disabled by default since it requires Docker daemon with IPv6 enabled + #make test_p2p_ipv6 +.PHONY: test_integrations test_release: @go test -tags release $(PACKAGES) +.PHONY: test_release test100: @for i in {1..100}; do make test; done +.PHONY: test100 vagrant_test: vagrant up vagrant ssh -c 'make test_integrations' +.PHONY: vagrant_test ### go tests test: @echo "--> Running go test" @go test -p 1 $(PACKAGES) +.PHONY: test test_race: @echo "--> Running go test --race" @go test -p 1 -v -race $(PACKAGES) +.PHONY: test_race # uses https://github.com/sasha-s/go-deadlock/ to detect potential deadlocks test_with_deadlock: make set_with_deadlock make test make cleanup_after_test_with_deadlock +.PHONY: test_with_deadlock set_with_deadlock: @echo "Get Goid" @@ -94,6 +126,7 @@ set_with_deadlock: find . -name "*.go" | grep -v "vendor/" | xargs -n 1 sed -i.bak 's/sync.RWMutex/deadlock.RWMutex/' find . -name "*.go" | grep -v "vendor/" | xargs -n 1 sed -i.bak 's/sync.Mutex/deadlock.Mutex/' find . -name "*.go" | grep -v "vendor/" | xargs -n 1 goimports -w +.PHONY: set_with_deadlock # cleanes up after you ran test_with_deadlock cleanup_after_test_with_deadlock: @@ -102,5 +135,4 @@ cleanup_after_test_with_deadlock: find . -name "*.go" | grep -v "vendor/" | xargs -n 1 goimports -w # cleans up the deps to not include the need libs go mod tidy - -.PHONY: test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test +.PHONY: cleanup_after_test_with_deadlock diff --git a/third_party/proto/gogoproto/gogo.proto b/third_party/proto/gogoproto/gogo.proto new file mode 100644 index 000000000..27960ecfb --- /dev/null +++ b/third_party/proto/gogoproto/gogo.proto @@ -0,0 +1,147 @@ +// Protocol Buffers for Go with Gadgets +// +// Copied from https://github.com/gogo/protobuf/blob/master/gogoproto/gogo.proto +// +// Copyright (c) 2013, The GoGo Authors. All rights reserved. +// http://github.com/gogo/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto2"; +package gogoproto; + +import "google/protobuf/descriptor.proto"; + +option java_package = "com.google.protobuf"; +option java_outer_classname = "GoGoProtos"; +option go_package = "github.com/gogo/protobuf/gogoproto"; + +extend google.protobuf.EnumOptions { + optional bool goproto_enum_prefix = 62001; + optional bool goproto_enum_stringer = 62021; + optional bool enum_stringer = 62022; + optional string enum_customname = 62023; + optional bool enumdecl = 62024; +} + +extend google.protobuf.EnumValueOptions { + optional string enumvalue_customname = 66001; +} + +extend google.protobuf.FileOptions { + optional bool goproto_getters_all = 63001; + optional bool goproto_enum_prefix_all = 63002; + optional bool goproto_stringer_all = 63003; + optional bool verbose_equal_all = 63004; + optional bool face_all = 63005; + optional bool gostring_all = 63006; + optional bool populate_all = 63007; + optional bool stringer_all = 63008; + optional bool onlyone_all = 63009; + + optional bool equal_all = 63013; + optional bool description_all = 63014; + optional bool testgen_all = 63015; + optional bool benchgen_all = 63016; + optional bool marshaler_all = 63017; + optional bool unmarshaler_all = 63018; + optional bool stable_marshaler_all = 63019; + + optional bool sizer_all = 63020; + + optional bool goproto_enum_stringer_all = 63021; + optional bool enum_stringer_all = 63022; + + optional bool unsafe_marshaler_all = 63023; + optional bool unsafe_unmarshaler_all = 63024; + + optional bool goproto_extensions_map_all = 63025; + optional bool goproto_unrecognized_all = 63026; + optional bool gogoproto_import = 63027; + optional bool protosizer_all = 63028; + optional bool compare_all = 63029; + optional bool typedecl_all = 63030; + optional bool enumdecl_all = 63031; + + optional bool goproto_registration = 63032; + optional bool messagename_all = 63033; + + optional bool goproto_sizecache_all = 63034; + optional bool goproto_unkeyed_all = 63035; +} + +extend google.protobuf.MessageOptions { + optional bool goproto_getters = 64001; + optional bool goproto_stringer = 64003; + optional bool verbose_equal = 64004; + optional bool face = 64005; + optional bool gostring = 64006; + optional bool populate = 64007; + optional bool stringer = 67008; + optional bool onlyone = 64009; + + optional bool equal = 64013; + optional bool description = 64014; + optional bool testgen = 64015; + optional bool benchgen = 64016; + optional bool marshaler = 64017; + optional bool unmarshaler = 64018; + optional bool stable_marshaler = 64019; + + optional bool sizer = 64020; + + optional bool unsafe_marshaler = 64023; + optional bool unsafe_unmarshaler = 64024; + + optional bool goproto_extensions_map = 64025; + optional bool goproto_unrecognized = 64026; + + optional bool protosizer = 64028; + optional bool compare = 64029; + + optional bool typedecl = 64030; + + optional bool messagename = 64033; + + optional bool goproto_sizecache = 64034; + optional bool goproto_unkeyed = 64035; +} + +extend google.protobuf.FieldOptions { + optional bool nullable = 65001; + optional bool embed = 65002; + optional string customtype = 65003; + optional string customname = 65004; + optional string jsontag = 65005; + optional string moretags = 65006; + optional string casttype = 65007; + optional string castkey = 65008; + optional string castvalue = 65009; + + optional bool stdtime = 65010; + optional bool stdduration = 65011; + optional bool wktpointer = 65012; + + optional string castrepeated = 65013; +} \ No newline at end of file diff --git a/tools.mk b/tools.mk index 140bcfd3e..516fc494e 100644 --- a/tools.mk +++ b/tools.mk @@ -38,43 +38,65 @@ mkfile_dir := $(shell cd $(shell dirname $(mkfile_path)); pwd) # Go tools ### +BIN ?= /usr/local/bin +UNAME_S ?= $(shell uname -s) +UNAME_M ?= $(shell uname -m) + TOOLS_DESTDIR ?= $(GOPATH)/bin +BUF_VERSION ?= 0.7.0 + CERTSTRAP = $(TOOLS_DESTDIR)/certstrap PROTOBUF = $(TOOLS_DESTDIR)/protoc GOODMAN = $(TOOLS_DESTDIR)/goodman all: tools +.PHONY: all tools: certstrap protobuf goodman +.PHONY: tools check: check_tools +.PHONY: check check_tools: @# https://stackoverflow.com/a/25668869 @echo "Found tools: $(foreach tool,$(notdir $(GOTOOLS)),\ $(if $(shell which $(tool)),$(tool),$(error "No $(tool) in PATH")))" +.PHONY: check_tools certstrap: $(CERTSTRAP) $(CERTSTRAP): @echo "Get Certstrap" @go get github.com/square/certstrap@v1.2.0 +.PHONY: certstrap protobuf: $(PROTOBUF) $(PROTOBUF): @echo "Get GoGo Protobuf" @go get github.com/gogo/protobuf/protoc-gen-gogo@v1.3.1 +.PHONY: protobuf + +buf: + @echo "Installing buf..." + @curl -sSL \ + "https://github.com/bufbuild/buf/releases/download/v$(BUF_VERSION)/buf-$(UNAME_S)-$(UNAME_M)" \ + -o "$(BIN)/buf" && \ + chmod +x "$(BIN)/buf" +.PHONY: buf goodman: $(GOODMAN) $(GOODMAN): @echo "Get Goodman" @go get github.com/snikch/goodman/cmd/goodman@10e37e294daa3c9a90abded60ff9924bafab3888 +.PHONY: goodman tools-clean: rm -f $(CERTSTRAP) $(PROTOBUF) $(GOX) $(GOODMAN) rm -f tools-stamp rm -rf /usr/local/include/google/protobuf rm -f /usr/local/bin/protoc +.PHONY: tooks-clean ### # Non Go tools @@ -100,5 +122,4 @@ protoc: @unzip -o $(PROTOC_ZIP) -d /usr/local bin/protoc @unzip -o $(PROTOC_ZIP) -d /usr/local 'include/*' @rm -f $(PROTOC_ZIP) - -.PHONY: all tools tools-clean protoc +.PHONY: protoc diff --git a/types/block.go b/types/block.go index 857955a28..b06730fb0 100644 --- a/types/block.go +++ b/types/block.go @@ -320,7 +320,7 @@ func MaxDataBytesUnknownEvidence(maxBytes int64, valsCount int) int64 { // NOTE: changes to the Header should be duplicated in: // - header.Hash() // - abci.Header -// - /docs/spec/blockchain/blockchain.md +// - https://github.com/tendermint/spec/blob/master/spec/blockchain/blockchain.md type Header struct { // basic block info Version version.Consensus `json:"version"` @@ -768,7 +768,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 d15ab698e..11725673a 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -36,7 +36,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) @@ -56,7 +56,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) @@ -121,7 +121,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) @@ -138,7 +138,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) @@ -211,7 +211,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) @@ -242,7 +242,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") }) @@ -351,11 +351,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) } @@ -434,7 +434,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() @@ -509,7 +509,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/priv_validator.go b/types/priv_validator.go index e35d78e7b..d1593a744 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -47,35 +47,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 { + return pv.PrivKey.PubKey() } // 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 } @@ -84,13 +84,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 } @@ -99,8 +99,8 @@ func (pv *MockPV) SignProposal(chainID string, proposal *Proposal) error { } // Implements PrivValidator. -func (pv *MockPV) GenerateVRFProof(message []byte) (vrf.Proof, error) { - privKey, ok := pv.privKey.(ed25519.PrivKeyEd25519) +func (pv MockPV) GenerateVRFProof(message []byte) (vrf.Proof, error) { + privKey, ok := pv.PrivKey.(ed25519.PrivKeyEd25519) if !ok { return nil, NewErrUnsupportedKey("ed25519") } @@ -108,34 +108,35 @@ func (pv *MockPV) GenerateVRFProof(message []byte) (vrf.Proof, error) { } // String returns a string representation of the MockPV. -func (pv *MockPV) String() string { +func (pv MockPV) String() string { addr := pv.GetPubKey().Address() return fmt.Sprintf("MockPV{%v}", addr) } // 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/proto3/block.proto b/types/proto3/block.proto index fb81c4763..adaa0a00d 100644 --- a/types/proto3/block.proto +++ b/types/proto3/block.proto @@ -1,47 +1,47 @@ syntax = "proto3"; package tendermint.types.proto3; -option go_package = "github.com/tendermint/tendermint/types/proto3"; +option go_package = "github.com/tendermint/tendermint/types/proto3"; message PartSetHeader { int32 Total = 1; - bytes Hash = 2; + bytes Hash = 2; } message BlockID { - bytes Hash = 1; + bytes Hash = 1; PartSetHeader PartsHeader = 2; } message Header { // basic block info - Version Version = 1; - string ChainID = 2; - int64 Height = 3; - Timestamp Time = 4; + Version Version = 1; + string ChainID = 2; + int64 Height = 3; + Timestamp Time = 4; // prev block info BlockID LastBlockID = 5; // hashes of block data - bytes LastCommitHash = 6; // commit from validators from the last block - bytes DataHash = 7; // transactions + bytes LastCommitHash = 6; // commit from validators from the last block + bytes DataHash = 7; // transactions // hashes from the app output from the prev block - bytes ValidatorsHash = 8; // validators for the current block - bytes NextValidatorsHash = 9; // validators for the next block - bytes ConsensusHash = 10; // consensus params for current block - bytes AppHash = 11; // state after txs from the previous block - bytes LastResultsHash = 12; // root hash of all results from the txs from the previous block + bytes ValidatorsHash = 8; // validators for the current block + bytes NextValidatorsHash = 9; // validators for the next block + bytes ConsensusHash = 10; // consensus params for current block + bytes AppHash = 11; // state after txs from the previous block + bytes LastResultsHash = 12; // root hash of all results from the txs from the previous block // consensus info - bytes EvidenceHash = 13; // evidence included in the block - bytes ProposerAddress = 14; // original proposer of the block + bytes EvidenceHash = 13; // evidence included in the block + bytes ProposerAddress = 14; // original proposer of the block } message Version { uint64 Block = 1; - uint64 App = 2; + uint64 App = 2; } // Timestamp wraps how amino encodes time. @@ -51,5 +51,5 @@ message Version { // NOTE/XXX: nanos do not get skipped if they are zero in amino. message Timestamp { int64 seconds = 1; - int32 nanos = 2; + int32 nanos = 2; } 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/test_util.go b/types/test_util.go index 3090f5f8b..48913f308 100644 --- a/types/test_util.go +++ b/types/test_util.go @@ -1,11 +1,11 @@ package types import ( - tmtime "github.com/tendermint/tendermint/types/time" + "time" ) 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++ { @@ -17,7 +17,7 @@ func MakeCommit(blockID BlockID, height int64, round int, Round: round, Type: PrecommitType, BlockID: blockID, - Timestamp: tmtime.Now(), + Timestamp: now, } _, err := signAddVote(validators[i], vote, voteSet) @@ -43,6 +43,7 @@ func MakeVote( valSet *ValidatorSet, privVal PrivValidator, chainID string, + now time.Time, ) (*Vote, error) { addr := privVal.GetPubKey().Address() idx, _ := valSet.GetByAddress(addr) @@ -51,7 +52,7 @@ func MakeVote( ValidatorIndex: idx, Height: height, Round: 0, - Timestamp: tmtime.Now(), + Timestamp: now, Type: PrecommitType, BlockID: blockID, } diff --git a/types/validator_set.go b/types/validator_set.go index 5fb1f8e4b..ad804bd25 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -627,6 +627,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. @@ -649,13 +650,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. // } - } - if got, needed := talliedVotingPower, vals.TotalVotingPower()*2/3; got <= needed { - return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed} + // return as soon as +2/3 of the signatures are verified + if talliedVotingPower > votingPowerNeeded { + return nil + } } - 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 @@ -753,6 +756,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 { @@ -786,16 +790,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. // } - } - } - got := talliedVotingPower - needed := (vals.TotalVotingPower() * trustLevel.Numerator) / trustLevel.Denominator - if got <= needed { - return ErrNotEnoughVotingPowerSigned{Got: got, Needed: needed} + if talliedVotingPower > votingPowerNeeded { + return nil + } + } } - return nil + return ErrNotEnoughVotingPowerSigned{Got: talliedVotingPower, Needed: votingPowerNeeded} } func verifyCommitBasic(commit *Commit, height int64, blockID BlockID) error { diff --git a/types/validator_set_test.go b/types/validator_set_test.go index dc61defba..468d33f59 100644 --- a/types/validator_set_test.go +++ b/types/validator_set_test.go @@ -126,6 +126,7 @@ func BenchmarkValidatorSetCopy(b *testing.B) { //------------------------------------------------------------------- +//nolint:unused,deadcode func intToBytes(i int) []byte { hash := make([]byte, 64) binary.LittleEndian.PutUint64(hash, uint64(i)) diff --git a/version/version.go b/version/version.go index 989e8ddb1..6e73878c6 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.0" + TMCoreSemVer = "0.33.3" // ABCISemVer is the semantic version of the ABCI library ABCISemVer = "0.16.1"