mirror of
https://github.com/tendermint/tendermint.git
synced 2026-07-24 17:12:45 +00:00
Merge pull request #4550 from tendermint/release/v0.33.2
Release/v0.33.2
This commit is contained in:
+10
-5
@@ -87,7 +87,7 @@ jobs:
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
command: make proto-check-breaking
|
||||
command: make proto-check-breaking-ci
|
||||
|
||||
test_abci_apps:
|
||||
executor: golang
|
||||
@@ -171,11 +171,15 @@ jobs:
|
||||
GOPATH: /home/circleci/.go_workspace
|
||||
machine:
|
||||
image: circleci/classic:latest
|
||||
parameters:
|
||||
ipv:
|
||||
type: integer
|
||||
default: 4
|
||||
steps:
|
||||
- checkout
|
||||
- run: mkdir -p $GOPATH/src/github.com/tendermint
|
||||
- run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint
|
||||
- run: bash test/p2p/circleci.sh
|
||||
- run: bash test/p2p/circleci.sh << parameters.ipv >>
|
||||
- store_artifacts:
|
||||
path: /home/circleci/project/test/p2p/logs
|
||||
|
||||
@@ -339,6 +343,7 @@ jobs:
|
||||
name: Build tendermint
|
||||
no_output_timeout: 20m
|
||||
command: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ruby
|
||||
bash -x ./scripts/gitian-build.sh all
|
||||
for os in darwin linux windows; do
|
||||
@@ -373,13 +378,10 @@ jobs:
|
||||
# command: |
|
||||
# set -x
|
||||
# export PATH=~/.local/bin:$PATH
|
||||
|
||||
# # install node and dredd
|
||||
# ./scripts/get_nodejs.sh
|
||||
|
||||
# # build the binaries with a proper version of Go
|
||||
# docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint golang make build-linux build-contract-tests-hooks
|
||||
|
||||
# # This docker image works with go 1.7, we can install here the hook handler that contract-tests is going to use
|
||||
# go get github.com/snikch/goodman/cmd/goodman
|
||||
# make contract-tests
|
||||
@@ -425,6 +427,9 @@ workflows:
|
||||
requires:
|
||||
- setup_dependencies
|
||||
- test_p2p
|
||||
- test_p2p:
|
||||
name: test_p2p_ipv6
|
||||
ipv: 6
|
||||
- upload_coverage:
|
||||
requires:
|
||||
- test_cover
|
||||
|
||||
@@ -3,11 +3,34 @@ name: Feature Request
|
||||
about: Create a proposal to request a feature
|
||||
|
||||
---
|
||||
<!--
|
||||
Please describe *in detail* the feature/behavior/change you'd like to see.
|
||||
|
||||
Be ready for followup questions, and please respond in a timely
|
||||
manner.
|
||||
<!-- < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < ☺
|
||||
v ✰ Thanks for opening an issue! ✰
|
||||
v Before smashing the submit button please review the template.
|
||||
v Word of caution: poorly thought-out proposals may be rejected
|
||||
v without deliberation
|
||||
☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > -->
|
||||
|
||||
Word of caution: poorly thought out proposals may be rejected without deliberation
|
||||
-->
|
||||
## Summary
|
||||
|
||||
<!-- Short, concise description of the proposed feature -->
|
||||
|
||||
## Problem Definition
|
||||
|
||||
<!-- Why do we need this feature?
|
||||
What problems may be addressed by introducing this feature?
|
||||
What benefits does Tendermint stand to gain by including this feature?
|
||||
Are there any disadvantages of including this feature? -->
|
||||
|
||||
## Proposal
|
||||
|
||||
<!-- Detailed description of requirements of implementation -->
|
||||
|
||||
____
|
||||
|
||||
#### For Admin Use
|
||||
|
||||
- [ ] Not duplicate issue
|
||||
- [ ] Appropriate labels applied
|
||||
- [ ] Appropriate contributors tagged
|
||||
- [ ] Contributor assigned/self-assigned
|
||||
|
||||
@@ -1,13 +1,24 @@
|
||||
<!--
|
||||
<!-- < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < ☺
|
||||
v ✰ Thanks for creating a PR! ✰
|
||||
v Before smashing the submit button please review the checkboxes.
|
||||
v If a checkbox is n/a - please still include it but + a little note why
|
||||
☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > -->
|
||||
|
||||
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
|
||||
|
||||
<!-- Add a description of the changes that this PR introduces and the files that
|
||||
are the most critical to review.
|
||||
-->
|
||||
|
||||
* [ ] 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
|
||||
|
||||
@@ -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"
|
||||
+1
-1
@@ -23,7 +23,7 @@ linters:
|
||||
- interfacer
|
||||
- lll
|
||||
- misspell
|
||||
- maligned
|
||||
# - maligned
|
||||
- nakedret
|
||||
- prealloc
|
||||
- scopelint
|
||||
|
||||
@@ -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
|
||||
@@ -1,5 +1,57 @@
|
||||
# Changelog
|
||||
|
||||
## v0.33.2
|
||||
|
||||
*March 11, 2020*
|
||||
|
||||
Special thanks to external contributors on this release:
|
||||
@antho1404, @michaelfig, @gterzian, @tau3, @Shivani912
|
||||
|
||||
Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint).
|
||||
|
||||
### BREAKING CHANGES:
|
||||
|
||||
- CLI/RPC/Config
|
||||
- [cli] [\#4505](https://github.com/tendermint/tendermint/pull/4505) `tendermint lite` sub-command new syntax (@melekes):
|
||||
`lite cosmoshub-3 -p 52.57.29.196:26657 -w public-seed-node.cosmoshub.certus.one:26657
|
||||
--height 962118 --hash 28B97BE9F6DE51AC69F70E0B7BFD7E5C9CD1A595B7DC31AFF27C50D4948`
|
||||
|
||||
- Go API
|
||||
- [lite2] [\#4535](https://github.com/tendermint/tendermint/pull/4535) Remove `Start/Stop` (@melekes)
|
||||
- [lite2] [\#4469](https://github.com/tendermint/tendermint/issues/4469) Remove `RemoveNoLongerTrustedHeaders` and `RemoveNoLongerTrustedHeadersPeriod` option (@cmwaters)
|
||||
- [lite2] [\#4473](https://github.com/tendermint/tendermint/issues/4473) Return height as a 2nd param in `TrustedValidatorSet` (@melekes)
|
||||
- [lite2] [\#4536](https://github.com/tendermint/tendermint/pull/4536) `Update` returns a signed header (1st param) (@melekes)
|
||||
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
- [blockchain/v2] [\#4361](https://github.com/tendermint/tendermint/pull/4361) Add reactor (@brapse)
|
||||
- [cmd] [\#4515](https://github.com/tendermint/tendermint/issues/4515) Change `tendermint debug dump` sub-command archives filename's format (@melekes)
|
||||
- [consensus] [\#3583](https://github.com/tendermint/tendermint/issues/3583) Reduce `non-deterministic signature` log noise (@tau3)
|
||||
- [examples/kvstore] [\#4507](https://github.com/tendermint/tendermint/issues/4507) ABCI query now returns the proper height (@erikgrinaker)
|
||||
- [lite2] [\#4462](https://github.com/tendermint/tendermint/issues/4462) Add `NewHTTPClient` and `NewHTTPClientFromTrustedStore` (@cmwaters)
|
||||
- [lite2] [\#4329](https://github.com/tendermint/tendermint/issues/4329) modified bisection to loop (@cmwaters)
|
||||
- [lite2] [\#4385](https://github.com/tendermint/tendermint/issues/4385) Disconnect from bad nodes (@melekes)
|
||||
- [lite2] [\#4398](https://github.com/tendermint/tendermint/issues/4398) Add `VerifyAdjacent` and `VerifyNonAdjacent` funcs (@cmwaters)
|
||||
- [lite2] [\#4426](https://github.com/tendermint/tendermint/issues/4426) Don't save intermediate headers (@cmwaters)
|
||||
- [lite2] [\#4464](https://github.com/tendermint/tendermint/issues/4464) Cross-check first header (@cmwaters)
|
||||
- [lite2] [\#4470](https://github.com/tendermint/tendermint/issues/4470) Fix inconsistent header-validatorset pairing (@melekes)
|
||||
- [lite2] [\#4488](https://github.com/tendermint/tendermint/issues/4488) Allow local clock drift -10 sec. (@melekes)
|
||||
- [p2p] [\#4449](https://github.com/tendermint/tendermint/pull/4449) Use `curve25519.X25519()` instead of `ScalarMult` (@erikgrinaker)
|
||||
- [types] [\#4417](https://github.com/tendermint/tendermint/issues/4417) **VerifyCommitX() functions should return as soon as +2/3 threshold is reached** (@alessio).
|
||||
- [libs/kv] [\#4542](https://github.com/tendermint/tendermint/pull/4542) remove unused type KI64Pair (@tessr)
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [cmd] [\#4303](https://github.com/tendermint/tendermint/issues/4303) Show useful error when Tendermint is not initialized (@melekes)
|
||||
- [cmd] [\#4515](https://github.com/tendermint/tendermint/issues/4515) **Fix `tendermint debug kill` sub-command** (@melekes)
|
||||
- [rpc] [\#3935](https://github.com/tendermint/tendermint/issues/3935) **Create buffered subscriptions on `/subscribe`** (@melekes)
|
||||
- [rpc] [\#4375](https://github.com/tendermint/tendermint/issues/4375) Stop searching for txs in `/tx_search` upon client timeout (@gterzian)
|
||||
- [rpc] [\#4406](https://github.com/tendermint/tendermint/pull/4406) Fix issue with multiple subscriptions on the websocket (@antho1404)
|
||||
- [rpc] [\#4432](https://github.com/tendermint/tendermint/issues/4432) Fix `/tx_search` pagination with ordered results (@erikgrinaker)
|
||||
- [rpc] [\#4492](https://github.com/tendermint/tendermint/issues/4492) Keep the original subscription "id" field when new RPCs come in (@michaelfig)
|
||||
|
||||
|
||||
## v0.33.1
|
||||
|
||||
*Feburary 13, 2020*
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
## v0.33.2
|
||||
## v0.33.3
|
||||
|
||||
\*\*
|
||||
|
||||
Special thanks to external contributors on this release:
|
||||
|
||||
Friendly reminder, we have a [bug bounty
|
||||
program](https://hackerone.com/tendermint).
|
||||
Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint).
|
||||
|
||||
### BREAKING CHANGES:
|
||||
|
||||
|
||||
+19
-1
@@ -100,7 +100,25 @@ specify exactly the dependency you want to update, eg.
|
||||
|
||||
## Protobuf
|
||||
|
||||
When working with [protobuf](https://developers.google.com/protocol-buffers) there are a few things you should know. We use [buf](https://buf.build/) for our linting and breaking changes checking. If you would like to run linting and check if the changes you have made are breaking then you will have to install the needed dependencies with `make buf`. Then the linting cmd will be `make proto-lint` and the breaking changes check will be `make proto-check-breaking`. To generate new stubs based off of your changes you can run `make proto-gen` (you can do this outside of GOPATH).
|
||||
We use [Protocol Buffers](https://developers.google.com/protocol-buffers) along with [gogoproto](https://github.com/gogo/protobuf) to generate code for use across Tendermint Core.
|
||||
|
||||
For linting and checking breaking changes, we use [buf](https://buf.build/). If you would like to run linting and check if the changes you have made are breaking then you will have to install the needed dependencies with `make buf`. Then the linting cmd will be `make proto-lint` and the breaking changes check will be `make proto-check-breaking`.
|
||||
|
||||
To generate new stubs based off of your changes you can run `make proto-gen` after installing `protoc` and gogoproto.
|
||||
|
||||
### Installation Instructions
|
||||
|
||||
To install `protoc`, download an appropriate release (https://github.com/protocolbuffers/protobuf) and then move the provided binaries into your PATH (follow instructions in README included with the download).
|
||||
|
||||
To install `gogoproto`, do the following:
|
||||
|
||||
```sh
|
||||
$ go get github.com/gogo/protobuf/gogoproto
|
||||
$ cd $GOPATH/pkg/mod/github.com/gogo/protobuf@v1.3.1 # or wherever go get installs things
|
||||
$ make install
|
||||
```
|
||||
|
||||
You should now be able to run `make proto-gen` from inside the root Tendermint directory to generate new files from proto files.
|
||||
|
||||
## Vagrant
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@ OUTPUT?=build/tendermint
|
||||
BUILD_TAGS?='tendermint'
|
||||
LD_FLAGS = -X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD` -s -w
|
||||
BUILD_FLAGS = -mod=readonly -ldflags "$(LD_FLAGS)"
|
||||
HTTPS_GIT := https://github.com/tendermint/tendermint.git
|
||||
|
||||
all: check build test install
|
||||
.PHONY: all
|
||||
|
||||
# The below include contains the tools.
|
||||
include tools.mk
|
||||
@@ -17,24 +19,30 @@ include tests.mk
|
||||
|
||||
build:
|
||||
CGO_ENABLED=0 go build $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o $(OUTPUT) ./cmd/tendermint/
|
||||
.PHONY: build
|
||||
|
||||
build_c:
|
||||
CGO_ENABLED=1 go build $(BUILD_FLAGS) -tags "$(BUILD_TAGS) cleveldb" -o $(OUTPUT) ./cmd/tendermint/
|
||||
.PHONY: build_c
|
||||
|
||||
build_race:
|
||||
CGO_ENABLED=1 go build -race $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o $(OUTPUT) ./cmd/tendermint
|
||||
.PHONY: build_race
|
||||
|
||||
install:
|
||||
CGO_ENABLED=0 go install $(BUILD_FLAGS) -tags $(BUILD_TAGS) ./cmd/tendermint
|
||||
.PHONY: install
|
||||
|
||||
install_c:
|
||||
CGO_ENABLED=1 go install $(BUILD_FLAGS) -tags "$(BUILD_TAGS) cleveldb" ./cmd/tendermint
|
||||
.PHONY: install_c
|
||||
|
||||
###############################################################################
|
||||
### Protobuf ###
|
||||
###############################################################################
|
||||
|
||||
proto-all: proto-gen proto-lint proto-check-breaking
|
||||
.PHONY: proto-all
|
||||
|
||||
proto-gen:
|
||||
## If you get the following error,
|
||||
@@ -43,14 +51,19 @@ proto-gen:
|
||||
## Note the $< here is substituted for the %.proto
|
||||
## Note the $@ here is substituted for the %.pb.go
|
||||
@sh scripts/protocgen.sh
|
||||
.PHONY: proto-gen
|
||||
|
||||
proto-lint:
|
||||
@buf check lint --error-format=json
|
||||
.PHONY: proto-lint
|
||||
|
||||
proto-check-breaking:
|
||||
@buf check breaking --against-input '.git#branch=master'
|
||||
@buf check breaking --against-input ".git#branch=master"
|
||||
.PHONY: proto-check-breaking
|
||||
|
||||
.PHONY: proto-all proto-gen proto-lint proto-check-breaking
|
||||
proto-check-breaking-ci:
|
||||
@buf check breaking --against-input "$(HTTPS_GIT)#branch=master"
|
||||
.PHONY: proto-check-breaking-ci
|
||||
|
||||
###############################################################################
|
||||
### Build ABCI ###
|
||||
@@ -58,9 +71,11 @@ proto-check-breaking:
|
||||
|
||||
build_abci:
|
||||
@go build -mod=readonly -i ./abci/cmd/...
|
||||
.PHONY: build_abci
|
||||
|
||||
install_abci:
|
||||
@go install -mod=readonly ./abci/cmd/...
|
||||
.PHONY: install_abci
|
||||
|
||||
###############################################################################
|
||||
### Distribution ###
|
||||
@@ -70,6 +85,7 @@ install_abci:
|
||||
# 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"
|
||||
@@ -85,12 +101,14 @@ 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 ###
|
||||
@@ -104,11 +122,13 @@ gen_certs: clean_certs
|
||||
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 rpc/lib/server/test.crt
|
||||
rm -f rpc/lib/server/test.key
|
||||
.PHONY: clean_certs
|
||||
|
||||
###############################################################################
|
||||
### Formatting, linting, and vetting ###
|
||||
@@ -116,10 +136,12 @@ clean_certs:
|
||||
|
||||
fmt:
|
||||
@go fmt ./...
|
||||
.PHONY: fmt
|
||||
|
||||
lint:
|
||||
@echo "--> Running linter"
|
||||
@golangci-lint run
|
||||
.PHONY: lint
|
||||
|
||||
DESTINATION = ./index.html.md
|
||||
|
||||
@@ -135,6 +157,7 @@ build-docs:
|
||||
cp -r .vuepress/dist/* ~/output/$${p}/ ; \
|
||||
cp ~/output/$${p}/index.html ~/output ; \
|
||||
done < versions ;
|
||||
.PHONY: build-docs
|
||||
|
||||
sync-docs:
|
||||
cd ~/output && \
|
||||
@@ -152,6 +175,7 @@ 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 ###
|
||||
@@ -160,9 +184,11 @@ build-docker:
|
||||
# Build linux binary on other platforms
|
||||
build-linux: tools
|
||||
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
|
||||
@@ -170,15 +196,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
|
||||
.PHONY: localnet-stop
|
||||
|
||||
# Build hooks for dredd, to skip or add information on some steps
|
||||
build-contract-tests-hooks:
|
||||
@@ -187,6 +216,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
|
||||
@@ -195,12 +225,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 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
|
||||
|
||||
@@ -9,7 +9,7 @@ Or [Blockchain](<https://en.wikipedia.org/wiki/Blockchain_(database)>), for shor
|
||||
[](https://github.com/tendermint/tendermint/releases/latest)
|
||||
[](https://godoc.org/github.com/tendermint/tendermint)
|
||||
[](https://github.com/moovweb/gvm)
|
||||
[](https://riot.im/app/#/room/#tendermint:matrix.org)
|
||||
[](https://discord.gg/AzefAFd)
|
||||
[](https://github.com/tendermint/tendermint/blob/master/LICENSE)
|
||||
[](https://github.com/tendermint/tendermint)
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -53,7 +53,8 @@ func testCounter() {
|
||||
}
|
||||
|
||||
fmt.Printf("Running %s test with abci=%s\n", abciApp, abciType)
|
||||
cmd := exec.Command("bash", "-c", fmt.Sprintf("abci-cli %s", abciApp))
|
||||
subCommand := fmt.Sprintf("abci-cli %s", abciApp)
|
||||
cmd := exec.Command("bash", "-c", subCommand)
|
||||
cmd.Stdout = os.Stdout
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Fatalf("starting %q err: %v", abciApp, err)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:"-"`
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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{}
|
||||
|
||||
+56
-70
@@ -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:
|
||||
case scFinishedEv:
|
||||
if state.synced() {
|
||||
return pcFinished{tmState: state.context.tmState(), blocksSynced: state.blocksSynced}, nil
|
||||
}
|
||||
state.draining = true
|
||||
return noOp, nil
|
||||
|
||||
case scPeerError:
|
||||
state.purgePeer(event.peerID)
|
||||
return noOp, nil
|
||||
|
||||
case scBlockReceived:
|
||||
if event.block == nil {
|
||||
panic("processor received an event with a nil block")
|
||||
}
|
||||
if event.block.Height <= state.height {
|
||||
return pcShortBlock{}, nil
|
||||
}
|
||||
err := state.enqueue(event.peerID, event.block, event.block.Height)
|
||||
if err != nil {
|
||||
return pcDuplicateBlock{}, nil
|
||||
return noOp, nil
|
||||
}
|
||||
|
||||
case pcProcessBlock:
|
||||
// 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 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()
|
||||
|
||||
delete(state.queue, first.Height)
|
||||
state.blocksSynced++
|
||||
|
||||
return pcBlockProcessed{height: first.Height, peerID: firstItem.peerID}, nil
|
||||
|
||||
case *peerError:
|
||||
state.purgePeer(event.peerID)
|
||||
|
||||
case pcStop:
|
||||
if state.synced() {
|
||||
return noOp, pcFinished{height: state.height, blocksSynced: state.blocksSynced}
|
||||
}
|
||||
state.draining = true
|
||||
}
|
||||
|
||||
return noOp, nil
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+64
-115
@@ -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,9 +101,37 @@ func executeProcessorTests(t *testing.T, tests []testFields) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRProcessPeerError(t *testing.T) {
|
||||
tests := []testFields{
|
||||
{
|
||||
name: "error for existing peer",
|
||||
steps: []pcFsmMakeStateValues{
|
||||
{
|
||||
currentState: ¶ms{items: []pcBlock{{"P1", 1}, {"P2", 2}}},
|
||||
event: scPeerError{peerID: "P2"},
|
||||
wantState: ¶ms{items: []pcBlock{{"P1", 1}}},
|
||||
wantNextEvent: noOp,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error for unknown peer",
|
||||
steps: []pcFsmMakeStateValues{
|
||||
{
|
||||
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 one block",
|
||||
steps: []pcFsmMakeStateValues{
|
||||
@@ -113,6 +141,7 @@ func TestPcBlockResponse(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "add two blocks",
|
||||
steps: []pcFsmMakeStateValues{
|
||||
@@ -126,62 +155,18 @@ func TestPcBlockResponse(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add duplicate block from same 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", 3),
|
||||
wantState: ¶ms{items: []pcBlock{{"P1", 3}}}, wantNextEvent: pcDuplicateBlock{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "add duplicate block from different peer",
|
||||
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{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
|
||||
+466
-55
@@ -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
|
||||
}
|
||||
|
||||
func schedulerHandle(event Event) (Event, error) {
|
||||
if _, ok := event.(timeCheck); ok {
|
||||
fmt.Println("scheduler handle timeCheck")
|
||||
// ValidateBasic performs basic validation.
|
||||
func (m *bcBlockRequestMessage) ValidateBasic() error {
|
||||
if m.Height < 0 {
|
||||
return errors.New("negative Height")
|
||||
}
|
||||
return noOp, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func processorHandle(event Event) (Event, error) {
|
||||
if _, ok := event.(timeCheck); ok {
|
||||
fmt.Println("processor handle timeCheck")
|
||||
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 noOp, nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Reactor struct {
|
||||
events chan Event
|
||||
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)
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
type bcStatusRequestMessage struct {
|
||||
Height int64
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (m *bcStatusRequestMessage) ValidateBasic() error {
|
||||
if m.Height < 0 {
|
||||
return errors.New("negative Height")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *bcStatusRequestMessage) String() string {
|
||||
return fmt.Sprintf("[bcStatusRequestMessage %v]", m.Height)
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
type bcStatusResponseMessage struct {
|
||||
Height int64
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (m *bcStatusResponseMessage) ValidateBasic() error {
|
||||
if m.Height < 0 {
|
||||
return errors.New("negative Height")
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
func NewReactor(bufferSize int) *Reactor {
|
||||
return &Reactor{
|
||||
//nolint:unused,deadcode
|
||||
type blockVerifier interface {
|
||||
VerifyCommit(chainID string, blockID types.BlockID, height int64, commit *types.Commit) error
|
||||
}
|
||||
|
||||
//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()
|
||||
|
||||
<-r.scheduler.ready()
|
||||
<-r.processor.ready()
|
||||
|
||||
go func() {
|
||||
for t := range r.ticker.C {
|
||||
r.events <- timeCheck{time: t}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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() {
|
||||
// reactor generated ticker events:
|
||||
// ticker for cleaning peers
|
||||
type rTryPrunePeer struct {
|
||||
priorityHigh
|
||||
time time.Time
|
||||
}
|
||||
|
||||
func (e rTryPrunePeer) String() string {
|
||||
return fmt.Sprintf(": %v", e.time)
|
||||
}
|
||||
|
||||
// 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)
|
||||
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():
|
||||
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():
|
||||
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))
|
||||
// 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
|
||||
case event := <-r.scheduler.next():
|
||||
r.processor.send(event)
|
||||
case event := <-r.processor.next():
|
||||
r.scheduler.send(event)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (r *Reactor) Receive(event Event) {
|
||||
// XXX: decode and serialize write events
|
||||
// TODO: backpressure
|
||||
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 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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,521 @@
|
||||
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
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
for _, event := range script {
|
||||
reactor.Receive(event)
|
||||
type testEvent struct {
|
||||
peer string
|
||||
event interface{}
|
||||
}
|
||||
|
||||
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()
|
||||
})
|
||||
}
|
||||
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(height int64, state sm.State, lastCommit *types.Commit) *types.Block {
|
||||
block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address)
|
||||
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(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)
|
||||
}
|
||||
return blockStore, state, blockExec
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+90
-113
@@ -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
|
||||
}
|
||||
|
||||
+191
-328
@@ -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)
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -2,19 +2,19 @@ package commands
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
amino "github.com/tendermint/go-amino"
|
||||
"github.com/tendermint/go-amino"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmos "github.com/tendermint/tendermint/libs/os"
|
||||
lite "github.com/tendermint/tendermint/lite2"
|
||||
"github.com/tendermint/tendermint/lite2/provider"
|
||||
httpp "github.com/tendermint/tendermint/lite2/provider/http"
|
||||
lproxy "github.com/tendermint/tendermint/lite2/proxy"
|
||||
lrpc "github.com/tendermint/tendermint/lite2/rpc"
|
||||
dbs "github.com/tendermint/tendermint/lite2/store/db"
|
||||
@@ -24,113 +24,132 @@ 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
|
||||
primaryAddr string
|
||||
witnessAddrsJoined string
|
||||
chainID string
|
||||
home string
|
||||
witnessesAddrs string
|
||||
maxOpenConnections int
|
||||
|
||||
trustingPeriod time.Duration
|
||||
trustedHeight int64
|
||||
trustedHash []byte
|
||||
|
||||
verbose bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
LiteCmd.Flags().StringVar(&listenAddr, "laddr", "tcp://localhost:8888",
|
||||
"Serve the proxy on the given address")
|
||||
|
||||
LiteCmd.Flags().StringVar(&primaryAddr, "primary", "tcp://localhost:26657",
|
||||
LiteCmd.Flags().StringVarP(&primaryAddr, "primary", "p", "",
|
||||
"Connect to a Tendermint node at this address")
|
||||
|
||||
LiteCmd.Flags().StringVar(&witnessesAddrs, "witnesses", "",
|
||||
LiteCmd.Flags().StringVarP(&witnessAddrsJoined, "witnesses", "w", "",
|
||||
"Tendermint nodes to cross-check the primary node, comma-separated")
|
||||
|
||||
LiteCmd.Flags().StringVar(&chainID, "chain-id", "tendermint", "Specify the Tendermint chain ID")
|
||||
|
||||
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().Int64Var(&trustedHeight, "height", 1, "Trusted header's height")
|
||||
LiteCmd.Flags().BytesHexVar(&trustedHash, "hash", []byte{}, "Trusted header's hash")
|
||||
LiteCmd.Flags().BoolVar(&verbose, "verbose", false, "Verbose output")
|
||||
}
|
||||
|
||||
func runProxy(cmd *cobra.Command, args []string) error {
|
||||
liteLogger := logger.With("module", "lite")
|
||||
// Initialise logger.
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||
var option log.Option
|
||||
if verbose {
|
||||
option, _ = log.AllowLevel("debug")
|
||||
} else {
|
||||
option, _ = log.AllowLevel("info")
|
||||
}
|
||||
logger = log.NewFilter(logger, option)
|
||||
|
||||
logger.Info("Connecting to the primary node...")
|
||||
rpcClient, err := rpcclient.NewHTTP(chainID, primaryAddr)
|
||||
chainID = args[0]
|
||||
logger.Info("Creating client...", "chainID", chainID)
|
||||
|
||||
witnessesAddrs := strings.Split(witnessAddrsJoined, ",")
|
||||
|
||||
db, err := dbm.NewGoLevelDB("lite-client-db", home)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "new goleveldb")
|
||||
}
|
||||
|
||||
var c *lite.Client
|
||||
if trustedHeight > 0 && len(trustedHash) > 0 { // fresh installation
|
||||
c, err = lite.NewHTTPClient(
|
||||
chainID,
|
||||
lite.TrustOptions{
|
||||
Period: trustingPeriod,
|
||||
Height: trustedHeight,
|
||||
Hash: trustedHash,
|
||||
},
|
||||
primaryAddr,
|
||||
witnessesAddrs,
|
||||
dbs.New(db, chainID),
|
||||
lite.Logger(logger),
|
||||
)
|
||||
} else { // continue from latest state
|
||||
c, err = lite.NewHTTPClientFromTrustedStore(
|
||||
chainID,
|
||||
trustingPeriod,
|
||||
primaryAddr,
|
||||
witnessesAddrs,
|
||||
dbs.New(db, chainID),
|
||||
lite.Logger(logger),
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rpcClient, err := rpcclient.NewHTTP(primaryAddr, "/websocket")
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "http client for %s", primaryAddr)
|
||||
}
|
||||
primary := httpp.NewWithClient(chainID, rpcClient)
|
||||
|
||||
logger.Info("Connecting to the witness nodes...")
|
||||
addrs := strings.Split(witnessesAddrs, ",")
|
||||
witnesses := make([]provider.Provider, len(addrs))
|
||||
for i, addr := range addrs {
|
||||
p, err := httpp.New(chainID, addr)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "http provider for %s", addr)
|
||||
}
|
||||
witnesses[i] = p
|
||||
}
|
||||
|
||||
logger.Info("Creating client...")
|
||||
db, err := dbm.NewGoLevelDB("lite-client-db", home)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c, err := lite.NewClient(
|
||||
chainID,
|
||||
lite.TrustOptions{
|
||||
Period: trustingPeriod,
|
||||
Height: trustedHeight,
|
||||
Hash: trustedHash,
|
||||
},
|
||||
primary,
|
||||
witnesses,
|
||||
dbs.New(db, chainID),
|
||||
lite.Logger(liteLogger),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p := lproxy.Proxy{
|
||||
Addr: listenAddr,
|
||||
Config: &rpcserver.Config{MaxOpenConnections: maxOpenConnections},
|
||||
Codec: amino.NewCodec(),
|
||||
Client: lrpc.NewClient(rpcClient, c),
|
||||
Logger: liteLogger,
|
||||
Logger: logger,
|
||||
}
|
||||
// Stop upon receiving SIGTERM or CTRL-C.
|
||||
tmos.TrapSignal(liteLogger, func() {
|
||||
tmos.TrapSignal(logger, func() {
|
||||
p.Listener.Close()
|
||||
})
|
||||
|
||||
logger.Info("Starting proxy...")
|
||||
logger.Info("Starting proxy...", "laddr", listenAddr)
|
||||
if err := p.ListenAndServe(); err != http.ErrServerClosed {
|
||||
// Error starting or closing listener:
|
||||
logger.Error("proxy ListenAndServe", "err", err)
|
||||
|
||||
@@ -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 {}
|
||||
},
|
||||
|
||||
@@ -54,7 +54,7 @@ func TestByzantine(t *testing.T) {
|
||||
if i == 0 {
|
||||
// NOTE: Now, test validators are MockPV, which by default doesn't
|
||||
// do any safety checks.
|
||||
css[i].privValidator.(*types.MockPV).DisableChecks()
|
||||
css[i].privValidator.(types.MockPV).DisableChecks()
|
||||
css[i].decideProposal = func(j int) func(int64, int) {
|
||||
return func(height int64, round int) {
|
||||
byzantineDecideProposalFunc(t, height, round, css[j], switches[j])
|
||||
|
||||
@@ -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,
|
||||
@@ -385,7 +385,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:]...)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -329,7 +329,7 @@ func TestSimulateValidatorsChange(t *testing.T) {
|
||||
|
||||
vss := make([]*validatorStub, nPeers)
|
||||
for i := 0; i < nPeers; i++ {
|
||||
vss[i] = NewValidatorStub(css[i].privValidator, i)
|
||||
vss[i] = newValidatorStub(css[i].privValidator, i)
|
||||
}
|
||||
height, round := css[0].Height, css[0].Round
|
||||
// start the machine
|
||||
@@ -892,7 +892,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()})
|
||||
}
|
||||
|
||||
@@ -1644,6 +1644,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 {
|
||||
@@ -1661,6 +1662,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
|
||||
|
||||
+15
-15
@@ -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 (
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
+6
-3
@@ -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
|
||||
```
|
||||
|
||||
<!-- markdown-link-check-disable -->
|
||||
|
||||
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.
|
||||
<!-- markdown-link-check-enable -->
|
||||
|
||||
To build documentation as a static website run `npm run build`. You will find the website in `.vuepress/dist` directory.
|
||||
|
||||
## Search
|
||||
|
||||
|
||||
+1
-1
@@ -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/).
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,5 +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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`.
|
||||
|
||||

|
||||
|
||||
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`.
|
||||
|
||||

|
||||
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.
|
||||
|
||||

|
||||
|
||||
### 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)
|
||||
|
||||
@@ -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_
|
||||
|
||||

|
||||
|
||||
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)
|
||||
@@ -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.
|
||||
@@ -27,7 +27,7 @@ We would like to suggest a double signing risk reduction method.
|
||||
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`
|
||||
- <span v-pre>`double_sign_check_height = {{ .Consensus.DoubleSignCheckHeight }}`</span> in `config.toml`
|
||||
- `tendermint node --double_sign_check_height` in cli
|
||||
- State machine ignore checking procedure when `vote-check-height == 0`
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ We would like to suggest a simple Tendermint mode abstraction. These modes will
|
||||
- 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`
|
||||
- <span v-pre>`mode = "{{ .BaseConfig.Mode }}"`</span> in `config.toml`
|
||||
- `tendermint node --mode validator` in cli
|
||||
- fullnode | validator | seed (default: "fullnode")
|
||||
- RPC modification
|
||||
|
||||
@@ -6,6 +6,14 @@ This ADR outlines the plan for an initial state sync prototype, and is subject t
|
||||
|
||||
* 2020-01-28: Initial draft (Erik Grinaker)
|
||||
|
||||
* 2020-02-18: Updates after initial prototype (Erik Grinaker)
|
||||
* ABCI: added missing `reason` fields.
|
||||
* ABCI: used 32-bit 1-based chunk indexes (was 64-bit 0-based).
|
||||
* ABCI: moved `RequestApplySnapshotChunk.chain_hash` to `RequestOfferSnapshot.app_hash`.
|
||||
* Gaia: snapshots must include node versions as well, both for inner and leaf nodes.
|
||||
* Added experimental prototype info.
|
||||
* Added open questions and implementation plan.
|
||||
|
||||
## Context
|
||||
|
||||
State sync will allow a new node to receive a snapshot of the application state without downloading blocks or going through consensus. This bootstraps the node significantly faster than the current fast sync system, which replays all historical blocks.
|
||||
@@ -34,14 +42,14 @@ A node can have multiple snapshots taken at various heights. Snapshots can be ta
|
||||
message Snapshot {
|
||||
uint64 height = 1; // The height at which the snapshot was taken
|
||||
uint32 format = 2; // The application-specific snapshot format
|
||||
uint64 chunks = 3; // The number of chunks in the snapshot
|
||||
uint32 chunks = 3; // The number of chunks in the snapshot
|
||||
bytes metadata = 4; // Arbitrary application metadata
|
||||
}
|
||||
|
||||
message SnapshotChunk {
|
||||
uint64 height = 1; // The height of the corresponding snapshot
|
||||
uint32 format = 2; // The application-specific snapshot format
|
||||
uint64 chunk = 3; // The chunk index (zero-based)
|
||||
uint32 chunk = 3; // The chunk index (one-based)
|
||||
bytes data = 4; // Serialized application state in an arbitrary format
|
||||
bytes checksum = 5; // SHA-1 checksum of data
|
||||
}
|
||||
@@ -64,11 +72,13 @@ message ResponseListSnapshots {
|
||||
// Offers a snapshot to the application
|
||||
message RequestOfferSnapshot {
|
||||
Snapshot snapshot = 1;
|
||||
bytes app_hash = 2;
|
||||
}
|
||||
|
||||
message ResponseOfferSnapshot {
|
||||
bool accepted = 1;
|
||||
enum reason { // Reason why snapshot was rejected
|
||||
Reason reason = 2; // Reason why snapshot was rejected
|
||||
enum Reason {
|
||||
unknown = 0; // Unknown or generic reason
|
||||
invalid_height = 1; // Height is rejected: avoid this height
|
||||
invalid_format = 2; // Format is rejected: avoid this format
|
||||
@@ -79,7 +89,7 @@ message ResponseOfferSnapshot {
|
||||
message RequestGetSnapshotChunk {
|
||||
uint64 height = 1;
|
||||
uint32 format = 2;
|
||||
uint64 chunk = 3;
|
||||
uint32 chunk = 3;
|
||||
}
|
||||
|
||||
message ResponseGetSnapshotChunk {
|
||||
@@ -89,12 +99,12 @@ message ResponseGetSnapshotChunk {
|
||||
// Applies a snapshot chunk
|
||||
message RequestApplySnapshotChunk {
|
||||
SnapshotChunk chunk = 1;
|
||||
bytes chain_hash = 2;
|
||||
}
|
||||
|
||||
message ResponseApplySnapshotChunk {
|
||||
bool applied = 1;
|
||||
enum reason { // Reason why chunk failed
|
||||
Reason reason = 2; // Reason why chunk failed
|
||||
enum Reason {
|
||||
unknown = 0; // Unknown or generic reason
|
||||
verify_failed = 1; // Chunk verification failed
|
||||
}
|
||||
@@ -165,24 +175,13 @@ The Gaia data structure consists of a set of named IAVL trees. A root hash is co
|
||||
|
||||
IAVL trees are versioned, but a snapshot only contains the version relevant for the snapshot height. All historical versions are ignored.
|
||||
|
||||
IAVL trees are insertion-order dependent, so key/value pairs must be stored in an appropriate insertion order to produce the same tree branching structure and thus the same Merkle hashes.
|
||||
IAVL trees are insertion-order dependent, so key/value pairs must be set in an appropriate insertion order to produce the same tree branching structure. This insertion order can be found by doing a breadth-first scan of all nodes (including inner nodes) and collecting unique keys in order. However, the node hash also depends on the node's version, so snapshots must contain the inner nodes' version numbers as well.
|
||||
|
||||
A chunk corresponds to a subtree key range within an IAVL tree, in insertion order, along with the Merkle proof from the root of the subtree all the way up to the root of the multistore (including the Merkle proof for the surrounding multistore.
|
||||
For the initial prototype, each chunk consists of a complete dump of all node data for all nodes in an entire IAVL tree. Thus the number of chunks equals the number of persistent stores in Gaia. No incremental verification of chunks is done, only a final app hash comparison at the end of the snapshot restoration.
|
||||
|
||||
```go
|
||||
struct SnapshotChunk {
|
||||
Store string // Name (key) of IAVL store in outer MultiStore
|
||||
Keys [][]byte // Snapshotted keys in insertion order
|
||||
Values [][]byte // Snapshotted values corresponding to Keys
|
||||
Proof []merkle.ProofOp // Merkle proof from subtree root to MultiStore root
|
||||
}
|
||||
```
|
||||
For a production version, it should be sufficient to store key/value/version for all nodes (leaf and inner) in insertion order, chunked in some appropriate way. If per-chunk verification is required, the chunk must also contain enough information to reconstruct the Merkle proofs all the way up to the root of the multistore, e.g. by storing a complete subtree's key/value/version data plus Merkle hashes of all other branches up to the multistore root. The exact approach will depend on tradeoffs between size, time, and verification. IAVL RangeProofs are not recommended, since these include redundant data such as proofs for intermediate and leaf nodes that can be derived from the above data.
|
||||
|
||||
This chunk structure is believed to be sufficient to reconstruct an identical IAVL tree by applying separate chunks, but this may require the chunks to be ordered in a certain way; further research is needed.
|
||||
|
||||
We do not use IAVL RangeProofs, since these include redundant data such as proofs for intermediate and leaf nodes that can be derived from the above data.
|
||||
|
||||
Chunks should be built greedily by collecting key/value pairs constituting a complete subtree up to some size limit (e.g. 32 MB), then serialized. Chunk data is stored in the file system as `snapshots/<height>/<format>/<chunk>/data`, along with a SHA-1 checksum in `snapshots/<height>/<format>/<chunk>/checksum`, and served via `RequestGetSnapshotChunk`.
|
||||
Chunks should be built greedily by collecting node data up to some size limit (e.g. 32 MB) and serializing it. Chunk data is stored in the file system as `snapshots/<height>/<format>/<chunk>/data`, along with a SHA-1 checksum in `snapshots/<height>/<format>/<chunk>/checksum`, and served via `RequestGetSnapshotChunk`.
|
||||
|
||||
### Snapshot Scheduling
|
||||
|
||||
@@ -192,17 +191,127 @@ Taking consistent snapshots of IAVL trees is greatly simplified by them being ve
|
||||
|
||||
Snapshots must also be garbage collected after some configurable time, e.g. by keeping the latest `n` snapshots.
|
||||
|
||||
## Experimental Prototype
|
||||
|
||||
An experimental but functional state sync prototype is available in the `erik/statesync-prototype` branches of the Tendermint, IAVL, Cosmos SDK, and Gaia repositories. To fetch the necessary branches:
|
||||
|
||||
```sh
|
||||
$ mkdir statesync
|
||||
$ cd statesync
|
||||
$ git clone git@github.com:tendermint/tendermint -b erik/statesync-prototype
|
||||
$ git clone git@github.com:tendermint/iavl -b erik/statesync-prototype
|
||||
$ git clone git@github.com:cosmos/cosmos-sdk -b erik/statesync-prototype
|
||||
$ git clone git@github.com:cosmos/gaia -b erik/statesync-prototype
|
||||
```
|
||||
|
||||
To spin up three nodes of a four-node testnet:
|
||||
|
||||
```sh
|
||||
$ cd gaia
|
||||
$ ./tools/start.sh
|
||||
```
|
||||
|
||||
Wait for the first snapshot to be taken at height 3, then (in a separate terminal) start the fourth node with state sync enabled:
|
||||
|
||||
```sh
|
||||
$ ./tools/sync.sh
|
||||
```
|
||||
|
||||
To stop the testnet, run:
|
||||
|
||||
```sh
|
||||
$ ./tools/stop.sh
|
||||
```
|
||||
|
||||
## Open Questions
|
||||
|
||||
* Is it possible to reconstruct an identical IAVL tree given separate subtrees in an appropriate order, or is more data needed about the branch structure?
|
||||
* Should we have a simpler scheme for discovering snapshots? E.g. announce supported formats, and have peer supply latest available snapshot.
|
||||
|
||||
* Should we punish nodes that provide invalid snapshots?
|
||||
Downsides: app has to announce supported formats, having a single snapshot per peer may make fewer peers available for chosen snapshot.
|
||||
|
||||
## Resolved Questions
|
||||
|
||||
* Is it OK for state-synced nodes to not have historical blocks nor historical IAVL versions?
|
||||
|
||||
> Yes, this is as intended. Maybe backfill blocks later.
|
||||
|
||||
* Do we need incremental chunk verification for first version?
|
||||
|
||||
> No, we'll start simple. Can add chunk verification via a new snapshot format without any breaking changes in Tendermint. For adversarial conditions, maybe consider support for whitelisting peers to download chunks from.
|
||||
|
||||
* Should the snapshot ABCI interface be a separate optional ABCI service, or mandatory?
|
||||
|
||||
> Mandatory, to keep things simple for now. It will therefore be a breaking change and push the release. For apps using the Cosmos SDK, we can provide a default implementation that does not serve snapshots and errors when trying to apply them.
|
||||
|
||||
* How can we make sure `ListSnapshots` data is valid? An adversary can provide fake/invalid snapshots to DoS peers.
|
||||
|
||||
> For now, just pick snapshots that are available on a large number of peers. Maybe support whitelisting. We may consider e.g. placing snapshot manifests on the blockchain later.
|
||||
|
||||
* Should we punish nodes that provide invalid snapshots? How?
|
||||
|
||||
> No, these are full nodes not validators, so we can't punish them. Just disconnect from them and ignore them.
|
||||
|
||||
* Should we call these snapshots? The SDK already uses the term "snapshot" for `PruningOptions.SnapshotEvery`, and state sync will introduce additional SDK options for snapshot scheduling and pruning that are not related to IAVL snapshotting or pruning.
|
||||
|
||||
> Yes. Hopefully these concepts are distinct enough that we can refer to state sync snapshots and IAVL snapshots without too much confusion.
|
||||
|
||||
* Should we store snapshot and chunk metadata in a database? Can we use the database for chunks?
|
||||
|
||||
> As a first approach, store metadata in a database and chunks in the filesystem.
|
||||
|
||||
* Should a snapshot at height H be taken before or after the block at H is processed? E.g. RPC `/commit` returns app_hash after _previous_ height, i.e. _before_ current height.
|
||||
|
||||
> After commit.
|
||||
|
||||
* Do we need to support all versions of blockchain reactor (i.e. fast sync)?
|
||||
|
||||
> We should remove the v1 reactor completely once v2 has stabilized.
|
||||
|
||||
* Should `ListSnapshots` be a streaming API instead of a request/response API?
|
||||
|
||||
> No, just use a max message size.
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Core Tasks
|
||||
|
||||
* **Tendermint:** light client P2P transport [#4456](https://github.com/tendermint/tendermint/issues/4456)
|
||||
|
||||
* **IAVL:** export/import API [#210](https://github.com/tendermint/iavl/issues/210)
|
||||
|
||||
* **Cosmos SDK:** snapshotting, scheduling, and pruning [#5689](https://github.com/cosmos/cosmos-sdk/issues/5689)
|
||||
|
||||
* **Tendermint:** support starting with a truncated block history
|
||||
|
||||
* **Tendermint:** state sync reactor and ABCI interface [#828](https://github.com/tendermint/tendermint/issues/828)
|
||||
|
||||
* **Cosmos SDK:** snapshot ABCI implementation [#5690](https://github.com/cosmos/cosmos-sdk/issues/5690)
|
||||
|
||||
### Nice-to-Haves
|
||||
|
||||
* **Tendermint:** staged reactor startup (state sync → fast sync → block replay → wal replay → consensus)
|
||||
|
||||
> Let's do a time-boxed prototype (a few days) and see how much work it will be.
|
||||
|
||||
* Notify P2P peers about channel changes [#4394](https://github.com/tendermint/tendermint/issues/4394)
|
||||
|
||||
* Check peers have certain channels [#1148](https://github.com/tendermint/tendermint/issues/1148)
|
||||
|
||||
* **Tendermint:** prune blockchain history [#3652](https://github.com/tendermint/tendermint/issues/3652)
|
||||
|
||||
* **Tendermint:** allow genesis to start from non-zero height [#2543](https://github.com/tendermint/tendermint/issues/2543)
|
||||
|
||||
### Follow-up Tasks
|
||||
|
||||
* **Tendermint:** light client verification for fast sync [#4457](https://github.com/tendermint/tendermint/issues/4457)
|
||||
|
||||
* **Tendermint:** allow start with only blockstore [#3713](https://github.com/tendermint/tendermint/issues/3713)
|
||||
|
||||
* **Tendermint:** node should go back to fast-syncing when lagging significantly [#129](https://github.com/tendermint/tendermint/issues/129)
|
||||
|
||||
## Status
|
||||
|
||||
Proposed
|
||||
Accepted
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -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 `<PrefixBytes> <Length> <ByteArray>`.
|
||||
|
||||
## 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}
|
||||
@@ -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}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 167 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 107 KiB |
Generated
+28
-34
@@ -1157,12 +1157,6 @@
|
||||
"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.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@vuepress/plugin-last-updated/-/plugin-last-updated-1.3.0.tgz",
|
||||
@@ -2378,9 +2372,9 @@
|
||||
}
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001023",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001023.tgz",
|
||||
"integrity": "sha512-C5TDMiYG11EOhVOA62W1p3UsJ2z4DsHtMBQtjzp3ZsUglcQn62WOUgW0y795c7A5uZ+GCEIvzkMatLIlAsbNTA=="
|
||||
"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",
|
||||
@@ -3571,9 +3565,9 @@
|
||||
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.344",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.344.tgz",
|
||||
"integrity": "sha512-tvbx2Wl8WBR+ym3u492D0L6/jH+8NoQXqe46+QhbWH3voVPauGuZYeb1QAXYoOAWuiP2dbSvlBx0kQ1F3hu/Mw=="
|
||||
"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.2",
|
||||
@@ -6086,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",
|
||||
@@ -6559,9 +6553,9 @@
|
||||
}
|
||||
},
|
||||
"node-releases": {
|
||||
"version": "1.1.47",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.47.tgz",
|
||||
"integrity": "sha512-k4xjVPx5FpwBUj0Gw7uvFOTF4Ep8Hok1I6qjwL3pLfwe7Y0REQSAqOwwv9TWBCUtMHxcXfY4PgRLRozcChvTcA==",
|
||||
"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"
|
||||
}
|
||||
@@ -7630,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": {
|
||||
@@ -8276,9 +8270,9 @@
|
||||
"integrity": "sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc="
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.0.tgz",
|
||||
"integrity": "sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw==",
|
||||
"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"
|
||||
}
|
||||
@@ -10096,9 +10090,9 @@
|
||||
}
|
||||
},
|
||||
"vuepress-theme-cosmos": {
|
||||
"version": "1.0.148",
|
||||
"resolved": "https://registry.npmjs.org/vuepress-theme-cosmos/-/vuepress-theme-cosmos-1.0.148.tgz",
|
||||
"integrity": "sha512-IVzX339e9k25YNC62UZPfkLg50IGCctvlI/TQF6s63eCdh+ayHOPLTFq5zAiYTpP09Dbz4tSC74cQyO3fUyPOQ==",
|
||||
"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",
|
||||
@@ -10181,9 +10175,9 @@
|
||||
}
|
||||
},
|
||||
"webpack-chain": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack-chain/-/webpack-chain-6.3.1.tgz",
|
||||
"integrity": "sha512-PLWPY92p5Vj0hOylUGjRoRY7Kgrns5vmPFAQ9BpSHnBbVbh2akRQVUlbRb2mmGYRSY1FklTULtyVChNmcQjIVg==",
|
||||
"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": "^2.0.1"
|
||||
@@ -10209,9 +10203,9 @@
|
||||
}
|
||||
},
|
||||
"webpack-dev-server": {
|
||||
"version": "3.10.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.10.2.tgz",
|
||||
"integrity": "sha512-pxZKPYb+n77UN8u9YxXT4IaIrGcNtijh/mi8TXbErHmczw0DtPnMTTjHj+eNjkqLOaAZM/qD7V59j/qJsEiaZA==",
|
||||
"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",
|
||||
|
||||
+4
-7
@@ -4,19 +4,16 @@
|
||||
"description": "Welcome to the Tendermint Core documentation!",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"vuepress-theme-cosmos": "^1.0.148"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -4,20 +4,19 @@ 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
|
||||
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](https://github.com/tendermint/spec/blob/953523c3cb99fdb8c8f7a2d21e3a9909
|
||||
- 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`.
|
||||
|
||||
@@ -106,59 +106,49 @@ Rate-limiting and authentication are another key aspects to help protect
|
||||
against DOS attacks. While in the future we may implement these
|
||||
features, for now, validators are supposed to use external tools like
|
||||
[NGINX](https://www.nginx.com/blog/rate-limiting-nginx/) or
|
||||
[traefik](https://docs.traefik.io/configuration/commons/#rate-limiting)
|
||||
[traefik](https://docs.traefik.io/middlewares/ratelimit/)
|
||||
to achieve the same things.
|
||||
|
||||
## Debugging Tendermint
|
||||
|
||||
If you ever have to debug Tendermint, the first thing you should
|
||||
probably do is to check out the logs. See [How to read
|
||||
logs](./how-to-read-logs.md), where we explain what certain log
|
||||
statements mean.
|
||||
If you ever have to debug Tendermint, the first thing you should probably do is
|
||||
check out the logs. See [How to read logs](./how-to-read-logs.md), where we
|
||||
explain what certain log statements mean.
|
||||
|
||||
If, after skimming through the logs, things are not clear still, the
|
||||
next thing to try is query the /status RPC endpoint. It provides the
|
||||
necessary info: whenever the node is syncing or not, what height it is
|
||||
on, etc.
|
||||
If, after skimming through the logs, things are not clear still, the next thing
|
||||
to try is querying the `/status` RPC endpoint. It provides the necessary info:
|
||||
whenever the node is syncing or not, what height it is on, etc.
|
||||
|
||||
```
|
||||
```sh
|
||||
curl http(s)://{ip}:{rpcPort}/status
|
||||
```
|
||||
|
||||
`dump_consensus_state` will give you a detailed overview of the
|
||||
consensus state (proposer, lastest validators, peers states). From it,
|
||||
you should be able to figure out why, for example, the network had
|
||||
halted.
|
||||
`/dump_consensus_state` will give you a detailed overview of the consensus
|
||||
state (proposer, latest validators, peers states). From it, you should be able
|
||||
to figure out why, for example, the network had halted.
|
||||
|
||||
```
|
||||
```sh
|
||||
curl http(s)://{ip}:{rpcPort}/dump_consensus_state
|
||||
```
|
||||
|
||||
There is a reduced version of this endpoint - `consensus_state`, which
|
||||
returns just the votes seen at the current height.
|
||||
There is a reduced version of this endpoint - `/consensus_state`, which returns
|
||||
just the votes seen at the current height.
|
||||
|
||||
- [Github Issues](https://github.com/tendermint/tendermint/issues)
|
||||
- [StackOverflow
|
||||
questions](https://stackoverflow.com/questions/tagged/tendermint)
|
||||
If, after consulting with the logs and above endpoints, you still have no idea
|
||||
what's happening, consider using `tendermint debug kill` sub-command. This
|
||||
command will scrap all the available info and kill the process. See
|
||||
[Debugging](../tools/debugging.md) for the exact format.
|
||||
|
||||
### Debug Utility
|
||||
|
||||
Tendermint also ships with a `debug` sub-command that allows you to kill a live
|
||||
Tendermint process while collecting useful information in a compressed archive
|
||||
such as the configuration used, consensus state, network state, the node' status,
|
||||
the WAL, and even the stacktrace of the process before exit. These files can be
|
||||
useful to examine when debugging a faulty Tendermint process.
|
||||
|
||||
In addition, the `debug` sub-command also allows you to dump debugging data into
|
||||
compressed archives at a regular interval. These archives contain the goroutine
|
||||
and heap profiles in addition to the consensus state, network info, node status,
|
||||
and even the WAL.
|
||||
You can inspect the resulting archive yourself or create an issue on
|
||||
[Github](https://github.com/tendermint/tendermint). Before opening an issue
|
||||
however, be sure to check if there's [no existing
|
||||
issue](https://github.com/tendermint/tendermint/issues) already.
|
||||
|
||||
## Monitoring Tendermint
|
||||
|
||||
Each Tendermint instance has a standard `/health` RPC endpoint, which
|
||||
responds with 200 (OK) if everything is fine and 500 (or no response) -
|
||||
if something is wrong.
|
||||
Each Tendermint instance has a standard `/health` RPC endpoint, which responds
|
||||
with 200 (OK) if everything is fine and 500 (or no response) - if something is
|
||||
wrong.
|
||||
|
||||
Other useful endpoints include mentioned earlier `/status`, `/net_info` and
|
||||
`/validators`.
|
||||
@@ -166,6 +156,10 @@ Other useful endpoints include mentioned earlier `/status`, `/net_info` and
|
||||
Tendermint also can report and serve Prometheus metrics. See
|
||||
[Metrics](./metrics.md).
|
||||
|
||||
`tendermint debug dump` sub-command can be used to periodically dump useful
|
||||
information into an archive. See [Debugging](../tools/debugging.md) for more
|
||||
information.
|
||||
|
||||
## What happens when my app dies?
|
||||
|
||||
You are supposed to run Tendermint under a [process
|
||||
|
||||
@@ -104,36 +104,37 @@ definition](https://github.com/tendermint/tendermint/blob/master/types/genesis.g
|
||||
|
||||
## Run
|
||||
|
||||
To run a Tendermint node, use
|
||||
To run a Tendermint node, use:
|
||||
|
||||
```
|
||||
```sh
|
||||
tendermint node
|
||||
```
|
||||
|
||||
By default, Tendermint will try to connect to an ABCI application on
|
||||
[127.0.0.1:26658](127.0.0.1:26658). If you have the `kvstore` ABCI app
|
||||
installed, run it in another window. If you don't, kill Tendermint and
|
||||
run an in-process version of the `kvstore` app:
|
||||
`127.0.0.1:26658`. If you have the `kvstore` ABCI app installed, run it in
|
||||
another window. If you don't, kill Tendermint and run an in-process version of
|
||||
the `kvstore` app:
|
||||
|
||||
```
|
||||
```sh
|
||||
tendermint node --proxy_app=kvstore
|
||||
```
|
||||
|
||||
After a few seconds you should see blocks start streaming in. Note that
|
||||
blocks are produced regularly, even if there are no transactions. See
|
||||
_No Empty Blocks_, below, to modify this setting.
|
||||
After a few seconds, you should see blocks start streaming in. Note that blocks
|
||||
are produced regularly, even if there are no transactions. See _No Empty
|
||||
Blocks_, below, to modify this setting.
|
||||
|
||||
Tendermint supports in-process versions of the `counter`, `kvstore` and
|
||||
`noop` apps that ship as examples with `abci-cli`. It's easy to compile
|
||||
your own app in-process with Tendermint if it's written in Go. If your
|
||||
app is not written in Go, simply run it in another process, and use the
|
||||
`--proxy_app` flag to specify the address of the socket it is listening
|
||||
on, for instance:
|
||||
Tendermint supports in-process versions of the `counter`, `kvstore`, and `noop`
|
||||
apps that ship as examples with `abci-cli`. It's easy to compile your app
|
||||
in-process with Tendermint if it's written in Go. If your app is not written in
|
||||
Go, run it in another process, and use the `--proxy_app` flag to specify the
|
||||
address of the socket it is listening on, for instance:
|
||||
|
||||
```
|
||||
```sh
|
||||
tendermint node --proxy_app=/var/run/abci.sock
|
||||
```
|
||||
|
||||
You can find out what flags are supported by running `tendermint node --help`.
|
||||
|
||||
## Transactions
|
||||
|
||||
To send a transaction, use `curl` to make requests to the Tendermint RPC
|
||||
@@ -155,10 +156,14 @@ and the `latest_app_hash` in particular:
|
||||
curl http://localhost:26657/status | json_pp | grep latest_app_hash
|
||||
```
|
||||
|
||||
<!-- markdown-link-check-disable -->
|
||||
|
||||
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.
|
||||
|
||||
<!-- markdown-link-check-enable -->
|
||||
|
||||
::: tip
|
||||
Find the RPC Documentation [here](https://docs.tendermint.com/master/rpc/)
|
||||
:::
|
||||
|
||||
@@ -9,16 +9,21 @@ parent:
|
||||
|
||||
Tendermint has some tools that are associated with it for:
|
||||
|
||||
- [Debugging](./debugging.md)
|
||||
- [Benchmarking](#benchmarking)
|
||||
- [Validation of remote signers](./remote-signer-validation.md)
|
||||
- [Testnets](#testnets)
|
||||
|
||||
- [Validation of remote signers](./remote-signer-validation.md)
|
||||
|
||||
## Benchmarking
|
||||
|
||||
Benchmarking is done with tm-load-test, for information on how to use the tool please visit the docs: https://github.com/interchainio/tm-load-test
|
||||
- https://github.com/interchainio/tm-load-test
|
||||
|
||||
`tm-load-test` is a distributed load testing tool (and framework) for load
|
||||
testing Tendermint networks.
|
||||
|
||||
## Testnets
|
||||
|
||||
The testnets tool is aimed at testing Tendermint with different configurations. For more information please visit: https://github.com/interchainio/testnets.
|
||||
- https://github.com/interchainio/testnets
|
||||
|
||||
This repository contains various different configurations of test networks for,
|
||||
and relating to, Tendermint.
|
||||
|
||||
@@ -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 <pid> </path/to/out.zip> --home=</path/to/app.d>
|
||||
```
|
||||
|
||||
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 </path/to/out> --home=</path/to/app.d>
|
||||
```
|
||||
|
||||
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.
|
||||
@@ -1,36 +1,32 @@
|
||||
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/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.3
|
||||
github.com/google/gofuzz v1.0.0 // indirect
|
||||
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/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
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 v0.9.3
|
||||
github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165
|
||||
github.com/prometheus/client_golang v1.5.0
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a
|
||||
github.com/rs/cors v1.7.0
|
||||
github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa
|
||||
github.com/spf13/cobra v0.0.1
|
||||
github.com/spf13/cobra v0.0.6
|
||||
github.com/spf13/viper v1.6.2
|
||||
github.com/stretchr/testify v1.4.0
|
||||
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
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a // indirect
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
|
||||
google.golang.org/grpc v1.27.1
|
||||
)
|
||||
|
||||
@@ -1,21 +1,43 @@
|
||||
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=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f h1:4O1om+UVU+Hfcihr1timk8YNXHxzZWgCo7ofnrZRApw=
|
||||
github.com/ChainSafe/go-schnorrkel v0.0.0-20200102211924-4bcbc698314f/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/Workiva/go-datastructures v1.0.50 h1:slDmfW6KCHcC7U+LP3DDBbm4fqTwZGn1beOFPfGaLvo=
|
||||
github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA=
|
||||
github.com/Workiva/go-datastructures v1.0.51 h1:LJHjjfcv+1gH+1D1SgrjcrF8iSZkgsAdCjclvHvVecQ=
|
||||
github.com/Workiva/go-datastructures v1.0.51/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA=
|
||||
github.com/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9abU0yMQt0NI=
|
||||
github.com/Workiva/go-datastructures v1.0.52/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
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/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/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 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
||||
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/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=
|
||||
@@ -26,22 +48,41 @@ 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/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.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/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.2.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/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/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/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 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=
|
||||
@@ -52,8 +93,11 @@ 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.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
@@ -61,59 +105,109 @@ 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-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 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic=
|
||||
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-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/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.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
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/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
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 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
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 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/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/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
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/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/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/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U=
|
||||
github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jrick/logrotate v1.0.0/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/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
@@ -131,56 +225,132 @@ 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/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/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
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/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/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
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/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
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.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/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
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/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/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.0 h1:J8lpUdobwIeCI7OiSxHqEwJUKvJwicL5+3v1oe2Yb4k=
|
||||
github.com/pkg/errors v0.9.0/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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_golang v0.9.1 h1:K47Rk0v/fkEfwfQet2KWhscE0cJzjgCCDBG2KHZoVno=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
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 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.3.0 h1:miYCvYqFXtl/J9FIy8eNpBfYthAEFg+Ys0XyUVEcDsc=
|
||||
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
||||
github.com/prometheus/client_golang v1.4.1 h1:FFSuS004yOQEtDdTq+TAOLP5xUq63KqAFYyOi8zA+Y8=
|
||||
github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
github.com/prometheus/client_golang v1.5.0 h1:Ctq0iGpCmr3jeP77kbF2UxgvRwzWWz+4Bh9/vJTyg1A=
|
||||
github.com/prometheus/client_golang v1.5.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
||||
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 h1:ElTg5tNp4DqfV7UQjDqv2+RJlNzsDtvNAWccbItceIE=
|
||||
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.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
|
||||
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.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY=
|
||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||
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 h1:GoAlyOgbOEIFdaDqxJVlbOQ1DtGmZWs/Qau0hIlk+WQ=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
|
||||
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.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/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.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
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/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
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.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
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=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
@@ -188,108 +358,181 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9
|
||||
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/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
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.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
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.1 h1:zZh3X5aZbdnoj+4XkaBxKfhO4ot82icYdhhREIAXIj8=
|
||||
github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs=
|
||||
github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk=
|
||||
github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/spf13/viper v1.6.2 h1:7aKfF+e8/k68gda3LOjo5RxiUqddoFxVq4BKBPrxk5E=
|
||||
github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
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/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/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/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
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=
|
||||
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 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/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
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/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-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/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-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
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/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-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
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-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-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-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||
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-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-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-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/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-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 h1:68K/z8GLUxV76xGSqwTWw2gyk/jwn79LUL43rES2g8o=
|
||||
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 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/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-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/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/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-20190114222345-bf090417da8b/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-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
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-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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/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-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
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/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.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
@@ -297,13 +540,19 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
|
||||
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.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/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/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=
|
||||
@@ -311,5 +560,11 @@ 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.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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
||||
|
||||
@@ -77,7 +77,7 @@ type Group struct {
|
||||
|
||||
// OpenGroup creates a new Group with head at headPath. It returns an error if
|
||||
// it fails to open head file.
|
||||
func OpenGroup(headPath string, groupOptions ...func(*Group)) (g *Group, err error) {
|
||||
func OpenGroup(headPath string, groupOptions ...func(*Group)) (*Group, error) {
|
||||
dir, err := filepath.Abs(filepath.Dir(headPath))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -87,7 +87,7 @@ func OpenGroup(headPath string, groupOptions ...func(*Group)) (g *Group, err err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g = &Group{
|
||||
g := &Group{
|
||||
ID: "group:" + head.ID,
|
||||
Head: head,
|
||||
headBuf: bufio.NewWriterSize(head, 4096*10),
|
||||
@@ -109,7 +109,7 @@ func OpenGroup(headPath string, groupOptions ...func(*Group)) (g *Group, err err
|
||||
gInfo := g.readGroupInfo()
|
||||
g.minIndex = gInfo.MinIndex
|
||||
g.maxIndex = gInfo.MaxIndex
|
||||
return
|
||||
return g, nil
|
||||
}
|
||||
|
||||
// GroupCheckDuration allows you to overwrite default groupCheckDuration.
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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)
|
||||
|
||||
+12
-284
@@ -26,7 +26,6 @@ var _ = math.Inf
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
// Define these here for compatibility but use tmlibs/common.KVPair.
|
||||
type Pair struct {
|
||||
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
|
||||
@@ -82,74 +81,16 @@ func (m *Pair) GetValue() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Define these here for compatibility but use tmlibs/common.KI64Pair.
|
||||
type KI64Pair struct {
|
||||
Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Value int64 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *KI64Pair) Reset() { *m = KI64Pair{} }
|
||||
func (m *KI64Pair) String() string { return proto.CompactTextString(m) }
|
||||
func (*KI64Pair) ProtoMessage() {}
|
||||
func (*KI64Pair) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_31432671d164f444, []int{1}
|
||||
}
|
||||
func (m *KI64Pair) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *KI64Pair) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_KI64Pair.Marshal(b, m, deterministic)
|
||||
} else {
|
||||
b = b[:cap(b)]
|
||||
n, err := m.MarshalToSizedBuffer(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
}
|
||||
func (m *KI64Pair) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_KI64Pair.Merge(m, src)
|
||||
}
|
||||
func (m *KI64Pair) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *KI64Pair) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_KI64Pair.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_KI64Pair proto.InternalMessageInfo
|
||||
|
||||
func (m *KI64Pair) GetKey() []byte {
|
||||
if m != nil {
|
||||
return m.Key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *KI64Pair) GetValue() int64 {
|
||||
if m != nil {
|
||||
return m.Value
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Pair)(nil), "tendermint.libs.kv.Pair")
|
||||
golang_proto.RegisterType((*Pair)(nil), "tendermint.libs.kv.Pair")
|
||||
proto.RegisterType((*KI64Pair)(nil), "tendermint.libs.kv.KI64Pair")
|
||||
golang_proto.RegisterType((*KI64Pair)(nil), "tendermint.libs.kv.KI64Pair")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("libs/kv/types.proto", fileDescriptor_31432671d164f444) }
|
||||
func init() { golang_proto.RegisterFile("libs/kv/types.proto", fileDescriptor_31432671d164f444) }
|
||||
|
||||
var fileDescriptor_31432671d164f444 = []byte{
|
||||
// 196 bytes of a gzipped FileDescriptorProto
|
||||
// 182 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xce, 0xc9, 0x4c, 0x2a,
|
||||
0xd6, 0xcf, 0x2e, 0xd3, 0x2f, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17,
|
||||
0x12, 0x2a, 0x49, 0xcd, 0x4b, 0x49, 0x2d, 0xca, 0xcd, 0xcc, 0x2b, 0xd1, 0x03, 0xc9, 0xeb, 0x65,
|
||||
@@ -157,12 +98,11 @@ var fileDescriptor_31432671d164f444 = []byte{
|
||||
0x95, 0xe9, 0xa7, 0xe7, 0xa7, 0xe7, 0x23, 0x58, 0x10, 0xbd, 0x4a, 0x7a, 0x5c, 0x2c, 0x01, 0x89,
|
||||
0x99, 0x45, 0x42, 0x02, 0x5c, 0xcc, 0xd9, 0xa9, 0x95, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x3c, 0x41,
|
||||
0x20, 0xa6, 0x90, 0x08, 0x17, 0x6b, 0x59, 0x62, 0x4e, 0x69, 0xaa, 0x04, 0x13, 0x58, 0x0c, 0xc2,
|
||||
0x51, 0x32, 0xe2, 0xe2, 0xf0, 0xf6, 0x34, 0x33, 0x21, 0x46, 0x0f, 0x33, 0x54, 0x8f, 0x93, 0xdb,
|
||||
0x8f, 0x87, 0x72, 0x8c, 0x2b, 0x1e, 0xc9, 0x31, 0xee, 0x78, 0x24, 0xc7, 0x78, 0xe2, 0x91, 0x1c,
|
||||
0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, 0xc9, 0x31, 0x1e, 0x78, 0x2c, 0xc7, 0x18, 0xa5, 0x91,
|
||||
0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x8f, 0xf0, 0x08, 0x32, 0x13, 0xea,
|
||||
0xe7, 0x24, 0x36, 0xb0, 0x93, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xc5, 0x5f, 0x67, 0xcb,
|
||||
0x05, 0x01, 0x00, 0x00,
|
||||
0x71, 0x72, 0xfb, 0xf1, 0x50, 0x8e, 0x71, 0xc5, 0x23, 0x39, 0xc6, 0x1d, 0x8f, 0xe4, 0x18, 0x4f,
|
||||
0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x03, 0x8f, 0xe5, 0x18,
|
||||
0xa3, 0x34, 0xd2, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0x11, 0x8e, 0x42,
|
||||
0x66, 0x42, 0xdd, 0x9f, 0xc4, 0x06, 0xb6, 0xde, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x5d, 0x3a,
|
||||
0xdc, 0xba, 0xd1, 0x00, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (this *Pair) Equal(that interface{}) bool {
|
||||
@@ -195,36 +135,6 @@ func (this *Pair) Equal(that interface{}) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
func (this *KI64Pair) Equal(that interface{}) bool {
|
||||
if that == nil {
|
||||
return this == nil
|
||||
}
|
||||
|
||||
that1, ok := that.(*KI64Pair)
|
||||
if !ok {
|
||||
that2, ok := that.(KI64Pair)
|
||||
if ok {
|
||||
that1 = &that2
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if that1 == nil {
|
||||
return this == nil
|
||||
} else if this == nil {
|
||||
return false
|
||||
}
|
||||
if !bytes.Equal(this.Key, that1.Key) {
|
||||
return false
|
||||
}
|
||||
if this.Value != that1.Value {
|
||||
return false
|
||||
}
|
||||
if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
func (m *Pair) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
@@ -266,45 +176,6 @@ func (m *Pair) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *KI64Pair) Marshal() (dAtA []byte, err error) {
|
||||
size := m.Size()
|
||||
dAtA = make([]byte, size)
|
||||
n, err := m.MarshalToSizedBuffer(dAtA[:size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dAtA[:n], nil
|
||||
}
|
||||
|
||||
func (m *KI64Pair) MarshalTo(dAtA []byte) (int, error) {
|
||||
size := m.Size()
|
||||
return m.MarshalToSizedBuffer(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *KI64Pair) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
i := len(dAtA)
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.XXX_unrecognized != nil {
|
||||
i -= len(m.XXX_unrecognized)
|
||||
copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
if m.Value != 0 {
|
||||
i = encodeVarintTypes(dAtA, i, uint64(m.Value))
|
||||
i--
|
||||
dAtA[i] = 0x10
|
||||
}
|
||||
if len(m.Key) > 0 {
|
||||
i -= len(m.Key)
|
||||
copy(dAtA[i:], m.Key)
|
||||
i = encodeVarintTypes(dAtA, i, uint64(len(m.Key)))
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func encodeVarintTypes(dAtA []byte, offset int, v uint64) int {
|
||||
offset -= sovTypes(v)
|
||||
base := offset
|
||||
@@ -334,23 +205,6 @@ func NewPopulatedPair(r randyTypes, easy bool) *Pair {
|
||||
return this
|
||||
}
|
||||
|
||||
func NewPopulatedKI64Pair(r randyTypes, easy bool) *KI64Pair {
|
||||
this := &KI64Pair{}
|
||||
v3 := r.Intn(100)
|
||||
this.Key = make([]byte, v3)
|
||||
for i := 0; i < v3; i++ {
|
||||
this.Key[i] = byte(r.Intn(256))
|
||||
}
|
||||
this.Value = int64(r.Int63())
|
||||
if r.Intn(2) == 0 {
|
||||
this.Value *= -1
|
||||
}
|
||||
if !easy && r.Intn(10) != 0 {
|
||||
this.XXX_unrecognized = randUnrecognizedTypes(r, 3)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
type randyTypes interface {
|
||||
Float32() float32
|
||||
Float64() float64
|
||||
@@ -370,9 +224,9 @@ func randUTF8RuneTypes(r randyTypes) rune {
|
||||
return rune(ru + 61)
|
||||
}
|
||||
func randStringTypes(r randyTypes) string {
|
||||
v4 := r.Intn(100)
|
||||
tmps := make([]rune, v4)
|
||||
for i := 0; i < v4; i++ {
|
||||
v3 := r.Intn(100)
|
||||
tmps := make([]rune, v3)
|
||||
for i := 0; i < v3; i++ {
|
||||
tmps[i] = randUTF8RuneTypes(r)
|
||||
}
|
||||
return string(tmps)
|
||||
@@ -394,11 +248,11 @@ func randFieldTypes(dAtA []byte, r randyTypes, fieldNumber int, wire int) []byte
|
||||
switch wire {
|
||||
case 0:
|
||||
dAtA = encodeVarintPopulateTypes(dAtA, uint64(key))
|
||||
v5 := r.Int63()
|
||||
v4 := r.Int63()
|
||||
if r.Intn(2) == 0 {
|
||||
v5 *= -1
|
||||
v4 *= -1
|
||||
}
|
||||
dAtA = encodeVarintPopulateTypes(dAtA, uint64(v5))
|
||||
dAtA = encodeVarintPopulateTypes(dAtA, uint64(v4))
|
||||
case 1:
|
||||
dAtA = encodeVarintPopulateTypes(dAtA, uint64(key))
|
||||
dAtA = append(dAtA, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)))
|
||||
@@ -443,25 +297,6 @@ func (m *Pair) Size() (n int) {
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *KI64Pair) Size() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.Key)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovTypes(uint64(l))
|
||||
}
|
||||
if m.Value != 0 {
|
||||
n += 1 + sovTypes(uint64(m.Value))
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func sovTypes(x uint64) (n int) {
|
||||
return (math_bits.Len64(x|1) + 6) / 7
|
||||
}
|
||||
@@ -590,113 +425,6 @@ func (m *Pair) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m *KI64Pair) Unmarshal(dAtA []byte) error {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
preIndex := iNdEx
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= uint64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
fieldNum := int32(wire >> 3)
|
||||
wireType := int(wire & 0x7)
|
||||
if wireType == 4 {
|
||||
return fmt.Errorf("proto: KI64Pair: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: KI64Pair: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
|
||||
}
|
||||
var byteLen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
byteLen |= int(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if byteLen < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
postIndex := iNdEx + byteLen
|
||||
if postIndex < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
|
||||
if m.Key == nil {
|
||||
m.Key = []byte{}
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 2:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType)
|
||||
}
|
||||
m.Value = 0
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowTypes
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
m.Value |= int64(b&0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipTypes(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) < 0 {
|
||||
return ErrInvalidLengthTypes
|
||||
}
|
||||
if (iNdEx + skippy) > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
|
||||
iNdEx += skippy
|
||||
}
|
||||
}
|
||||
|
||||
if iNdEx > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func skipTypes(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
|
||||
@@ -16,14 +16,7 @@ option (gogoproto.testgen_all) = true;
|
||||
//----------------------------------------
|
||||
// Abstract types
|
||||
|
||||
// Define these here for compatibility but use tmlibs/kv.Pair.
|
||||
message Pair {
|
||||
bytes key = 1;
|
||||
bytes value = 2;
|
||||
}
|
||||
|
||||
// Define these here for compatibility but use tmlibs/kv.KI64Pair.
|
||||
message KI64Pair {
|
||||
bytes key = 1;
|
||||
int64 value = 2;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+2
-2
@@ -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...)
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
+429
-531
File diff suppressed because it is too large
Load Diff
+429
-272
File diff suppressed because it is too large
Load Diff
+14
-13
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
+19
-24
@@ -11,7 +11,6 @@ import (
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/lite2/provider"
|
||||
httpp "github.com/tendermint/tendermint/lite2/provider/http"
|
||||
dbs "github.com/tendermint/tendermint/lite2/store/db"
|
||||
@@ -19,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
// Automatically getting new headers and verifying them.
|
||||
func TestExample_Client_AutoUpdate(t *testing.T) {
|
||||
func ExampleClient_Update() {
|
||||
// give Tendermint time to generate some blocks
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
@@ -57,36 +56,38 @@ func TestExample_Client_AutoUpdate(t *testing.T) {
|
||||
Hash: header.Hash(),
|
||||
},
|
||||
primary,
|
||||
[]provider.Provider{primary}, // TODO: primary should not be used here
|
||||
[]provider.Provider{primary}, // NOTE: primary should not be used here
|
||||
dbs.New(db, chainID),
|
||||
UpdatePeriod(1*time.Second),
|
||||
Logger(log.TestingLogger()),
|
||||
// Logger(log.TestingLogger()),
|
||||
)
|
||||
if err != nil {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
err = c.Start()
|
||||
if err != nil {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
c.Stop()
|
||||
c.Cleanup()
|
||||
}()
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
h, err := c.TrustedHeader(0, time.Now())
|
||||
// XXX: 30 * time.Minute clock drift is needed because a) Tendermint strips
|
||||
// monotonic component (see types/time/time.go) b) single instance is being
|
||||
// run.
|
||||
// https://github.com/tendermint/tendermint/issues/4489
|
||||
h, err := c.Update(time.Now().Add(30 * time.Minute))
|
||||
if err != nil {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println("got header", h.Height)
|
||||
// Output: got header 3
|
||||
if h != nil && h.Height > 2 {
|
||||
fmt.Println("successful update")
|
||||
} else {
|
||||
fmt.Println("update failed")
|
||||
}
|
||||
// Output: successful update
|
||||
}
|
||||
|
||||
// Manually getting headers and verifying them.
|
||||
func TestExample_Client_ManualUpdate(t *testing.T) {
|
||||
func ExampleClient_VerifyHeaderAtHeight() {
|
||||
// give Tendermint time to generate some blocks
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
@@ -124,20 +125,14 @@ func TestExample_Client_ManualUpdate(t *testing.T) {
|
||||
Hash: header.Hash(),
|
||||
},
|
||||
primary,
|
||||
[]provider.Provider{primary}, // TODO: primary should not be used here
|
||||
[]provider.Provider{primary}, // NOTE: primary should not be used here
|
||||
dbs.New(db, chainID),
|
||||
UpdatePeriod(0),
|
||||
Logger(log.TestingLogger()),
|
||||
// Logger(log.TestingLogger()),
|
||||
)
|
||||
if err != nil {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
err = c.Start()
|
||||
if err != nil {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
c.Stop()
|
||||
c.Cleanup()
|
||||
}()
|
||||
|
||||
@@ -146,7 +141,7 @@ func TestExample_Client_ManualUpdate(t *testing.T) {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
|
||||
h, err := c.TrustedHeader(3, time.Now())
|
||||
h, err := c.TrustedHeader(3)
|
||||
if err != nil {
|
||||
stdlog.Fatal(err)
|
||||
}
|
||||
@@ -158,7 +153,7 @@ func TestExample_Client_ManualUpdate(t *testing.T) {
|
||||
func TestMain(m *testing.M) {
|
||||
// start a tendermint node (and kvstore) in the background to test against
|
||||
app := kvstore.NewApplication()
|
||||
node := rpctest.StartTendermint(app)
|
||||
node := rpctest.StartTendermint(app, rpctest.SuppressStdout)
|
||||
|
||||
code := m.Run()
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -68,6 +69,10 @@ func (p *http) SignedHeader(height int64) (*types.SignedHeader, error) {
|
||||
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)
|
||||
|
||||
+3
-15
@@ -191,7 +191,7 @@ func (c *Client) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlock
|
||||
|
||||
// Verify each of the BlockMetas.
|
||||
for _, meta := range res.BlockMetas {
|
||||
h, err := c.lc.TrustedHeader(meta.Header.Height, time.Now())
|
||||
h, err := c.lc.TrustedHeader(meta.Header.Height)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "TrustedHeader(%d)", meta.Header.Height)
|
||||
}
|
||||
@@ -322,20 +322,8 @@ func (c *Client) UnsubscribeAll(ctx context.Context, subscriber string) error {
|
||||
}
|
||||
|
||||
func (c *Client) updateLiteClientIfNeededTo(height int64) (*types.SignedHeader, error) {
|
||||
lastTrustedHeight, err := c.lc.LastTrustedHeight()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "LastTrustedHeight")
|
||||
}
|
||||
|
||||
if lastTrustedHeight < height {
|
||||
return c.lc.VerifyHeaderAtHeight(height, time.Now())
|
||||
}
|
||||
|
||||
h, err := c.lc.TrustedHeader(height, time.Now())
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "TrustedHeader(#%d)", height)
|
||||
}
|
||||
return h, nil
|
||||
h, err := c.lc.VerifyHeaderAtHeight(height, time.Now())
|
||||
return h, errors.Wrapf(err, "failed to update light client to %d", height)
|
||||
}
|
||||
|
||||
func (c *Client) RegisterOpDecoder(typ string, dec merkle.OpDecoder) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
+143
-11
@@ -1,9 +1,11 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/tendermint/go-amino"
|
||||
@@ -14,10 +16,17 @@ import (
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
var (
|
||||
sizeKey = []byte("size")
|
||||
)
|
||||
|
||||
type dbs struct {
|
||||
db dbm.DB
|
||||
prefix string
|
||||
|
||||
mtx sync.RWMutex
|
||||
size uint16
|
||||
|
||||
cdc *amino.Codec
|
||||
}
|
||||
|
||||
@@ -28,12 +37,21 @@ type dbs struct {
|
||||
func New(db dbm.DB, prefix string) store.Store {
|
||||
cdc := amino.NewCodec()
|
||||
cryptoAmino.RegisterAmino(cdc)
|
||||
return &dbs{db: db, prefix: prefix, cdc: cdc}
|
||||
|
||||
size := uint16(0)
|
||||
bz, err := db.Get(sizeKey)
|
||||
if err == nil && len(bz) > 0 {
|
||||
size = unmarshalSize(bz)
|
||||
}
|
||||
|
||||
return &dbs{db: db, prefix: prefix, cdc: cdc, size: size}
|
||||
}
|
||||
|
||||
// SaveSignedHeaderAndNextValidatorSet persists SignedHeader and ValidatorSet
|
||||
// to the db.
|
||||
func (s *dbs) SaveSignedHeaderAndNextValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error {
|
||||
// SaveSignedHeaderAndValidatorSet persists SignedHeader and ValidatorSet to
|
||||
// the db.
|
||||
//
|
||||
// Safe for concurrent use by multiple goroutines.
|
||||
func (s *dbs) SaveSignedHeaderAndValidatorSet(sh *types.SignedHeader, valSet *types.ValidatorSet) error {
|
||||
if sh.Height <= 0 {
|
||||
panic("negative or zero height")
|
||||
}
|
||||
@@ -42,35 +60,60 @@ func (s *dbs) SaveSignedHeaderAndNextValidatorSet(sh *types.SignedHeader, valSet
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshalling header")
|
||||
}
|
||||
|
||||
valSetBz, err := s.cdc.MarshalBinaryLengthPrefixed(valSet)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "marshalling validator set")
|
||||
}
|
||||
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
|
||||
b := s.db.NewBatch()
|
||||
b.Set(s.shKey(sh.Height), shBz)
|
||||
b.Set(s.vsKey(sh.Height+1), valSetBz)
|
||||
b.Set(s.vsKey(sh.Height), valSetBz)
|
||||
b.Set(sizeKey, marshalSize(s.size+1))
|
||||
|
||||
err = b.WriteSync()
|
||||
b.Close()
|
||||
|
||||
if err == nil {
|
||||
s.size++
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteSignedHeaderAndNextValidatorSet deletes SignedHeader and ValidatorSet
|
||||
// from the db.
|
||||
func (s *dbs) DeleteSignedHeaderAndNextValidatorSet(height int64) error {
|
||||
// DeleteSignedHeaderAndValidatorSet deletes SignedHeader and ValidatorSet from
|
||||
// the db.
|
||||
//
|
||||
// Safe for concurrent use by multiple goroutines.
|
||||
func (s *dbs) DeleteSignedHeaderAndValidatorSet(height int64) error {
|
||||
if height <= 0 {
|
||||
panic("negative or zero height")
|
||||
}
|
||||
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
|
||||
b := s.db.NewBatch()
|
||||
b.Delete(s.shKey(height))
|
||||
b.Delete(s.vsKey(height + 1))
|
||||
b.Delete(s.vsKey(height))
|
||||
b.Set(sizeKey, marshalSize(s.size-1))
|
||||
|
||||
err := b.WriteSync()
|
||||
b.Close()
|
||||
|
||||
if err == nil {
|
||||
s.size--
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// SignedHeader loads SignedHeader at the given height.
|
||||
//
|
||||
// Safe for concurrent use by multiple goroutines.
|
||||
func (s *dbs) SignedHeader(height int64) (*types.SignedHeader, error) {
|
||||
if height <= 0 {
|
||||
panic("negative or zero height")
|
||||
@@ -90,6 +133,8 @@ func (s *dbs) SignedHeader(height int64) (*types.SignedHeader, error) {
|
||||
}
|
||||
|
||||
// ValidatorSet loads ValidatorSet at the given height.
|
||||
//
|
||||
// Safe for concurrent use by multiple goroutines.
|
||||
func (s *dbs) ValidatorSet(height int64) (*types.ValidatorSet, error) {
|
||||
if height <= 0 {
|
||||
panic("negative or zero height")
|
||||
@@ -109,6 +154,8 @@ func (s *dbs) ValidatorSet(height int64) (*types.ValidatorSet, error) {
|
||||
}
|
||||
|
||||
// LastSignedHeaderHeight returns the last SignedHeader height stored.
|
||||
//
|
||||
// Safe for concurrent use by multiple goroutines.
|
||||
func (s *dbs) LastSignedHeaderHeight() (int64, error) {
|
||||
itr, err := s.db.ReverseIterator(
|
||||
s.shKey(1),
|
||||
@@ -132,6 +179,8 @@ func (s *dbs) LastSignedHeaderHeight() (int64, error) {
|
||||
}
|
||||
|
||||
// FirstSignedHeaderHeight returns the first SignedHeader height stored.
|
||||
//
|
||||
// Safe for concurrent use by multiple goroutines.
|
||||
func (s *dbs) FirstSignedHeaderHeight() (int64, error) {
|
||||
itr, err := s.db.Iterator(
|
||||
s.shKey(1),
|
||||
@@ -154,12 +203,16 @@ func (s *dbs) FirstSignedHeaderHeight() (int64, error) {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
// SignedHeaderAfter iterates over headers until it finds a header after one at
|
||||
// height. It returns ErrSignedHeaderNotFound if no such header exists.
|
||||
//
|
||||
// Safe for concurrent use by multiple goroutines.
|
||||
func (s *dbs) SignedHeaderAfter(height int64) (*types.SignedHeader, error) {
|
||||
if height <= 0 {
|
||||
panic("negative or zero height")
|
||||
}
|
||||
|
||||
itr, err := s.db.ReverseIterator(
|
||||
itr, err := s.db.Iterator(
|
||||
s.shKey(height+1),
|
||||
append(s.shKey(1<<63-1), byte(0x00)),
|
||||
)
|
||||
@@ -177,7 +230,76 @@ func (s *dbs) SignedHeaderAfter(height int64) (*types.SignedHeader, error) {
|
||||
itr.Next()
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("no header after height %d. make sure height is not greater than latest existing height", height))
|
||||
return nil, store.ErrSignedHeaderNotFound
|
||||
}
|
||||
|
||||
// Prune prunes header & validator set pairs until there are only size pairs
|
||||
// left.
|
||||
//
|
||||
// Safe for concurrent use by multiple goroutines.
|
||||
func (s *dbs) Prune(size uint16) error {
|
||||
// 1) Check how many we need to prune.
|
||||
s.mtx.RLock()
|
||||
sSize := s.size
|
||||
s.mtx.RUnlock()
|
||||
|
||||
if sSize <= size { // nothing to prune
|
||||
return nil
|
||||
}
|
||||
numToPrune := sSize - size
|
||||
|
||||
// 2) Iterate over headers and perform a batch operation.
|
||||
itr, err := s.db.Iterator(
|
||||
s.shKey(1),
|
||||
append(s.shKey(1<<63-1), byte(0x00)),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
b := s.db.NewBatch()
|
||||
|
||||
pruned := 0
|
||||
for itr.Valid() && numToPrune > 0 {
|
||||
key := itr.Key()
|
||||
_, height, ok := parseShKey(key)
|
||||
if ok {
|
||||
b.Delete(s.shKey(height))
|
||||
b.Delete(s.vsKey(height))
|
||||
}
|
||||
itr.Next()
|
||||
numToPrune--
|
||||
pruned++
|
||||
}
|
||||
|
||||
itr.Close()
|
||||
|
||||
err = b.WriteSync()
|
||||
b.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 3) Update size.
|
||||
s.mtx.Lock()
|
||||
defer s.mtx.Unlock()
|
||||
|
||||
s.size -= uint16(pruned)
|
||||
|
||||
if wErr := s.db.SetSync(sizeKey, marshalSize(s.size)); wErr != nil {
|
||||
return errors.Wrap(wErr, "failed to persist size")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Size returns the number of header & validator set pairs.
|
||||
//
|
||||
// Safe for concurrent use by multiple goroutines.
|
||||
func (s *dbs) Size() uint16 {
|
||||
s.mtx.RLock()
|
||||
defer s.mtx.RUnlock()
|
||||
return s.size
|
||||
}
|
||||
|
||||
func (s *dbs) shKey(height int64) []byte {
|
||||
@@ -213,3 +335,13 @@ func parseShKey(key []byte) (prefix string, height int64, ok bool) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func marshalSize(size uint16) []byte {
|
||||
bs := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(bs, size)
|
||||
return bs
|
||||
}
|
||||
|
||||
func unmarshalSize(bz []byte) uint16 {
|
||||
return binary.LittleEndian.Uint16(bz)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user