mirror of
https://github.com/fluencelabs/tendermint
synced 2025-07-25 09:11:56 +00:00
Compare commits
311 Commits
v0.16.0
...
zarko/add-
Author | SHA1 | Date | |
---|---|---|---|
|
4accdb5f59 | ||
|
ece3f678da | ||
|
45a05b4726 | ||
|
1706ce6f7f | ||
|
d0beaba7e8 | ||
|
c28784de5e | ||
|
d06390638d | ||
|
3a0edc561d | ||
|
f8ed578325 | ||
|
5babaf9a88 | ||
|
c0610b2c32 | ||
|
1db2224241 | ||
|
0323b03daf | ||
|
379f9f875b | ||
|
ab00bf7c8b | ||
|
64879c1e6a | ||
|
7c22e47629 | ||
|
384b3ea065 | ||
|
6a48bd0c88 | ||
|
d93e177a69 | ||
|
cef053386b | ||
|
cca1dd8e3e | ||
|
26c38e770e | ||
|
609452958c | ||
|
c954fca376 | ||
|
9be16d56ba | ||
|
2b732bc11a | ||
|
dcd00b0e68 | ||
|
ff3f35c5f4 | ||
|
93c4312cdd | ||
|
1a1e4e767b | ||
|
bb1b249e8a | ||
|
c778d7f5d1 | ||
|
bb9b12d67a | ||
|
767521ac52 | ||
|
df9bf60b05 | ||
|
466c3ab1c7 | ||
|
c68d406195 | ||
|
02c0835e9b | ||
|
c170800fbd | ||
|
7afe74a963 | ||
|
02531ca5a3 | ||
|
d24e4cb821 | ||
|
fb64314d1c | ||
|
4930b61a38 | ||
|
9cc2cf362f | ||
|
ed93fb34ab | ||
|
3d32474da8 | ||
|
3233c318ea | ||
|
32e1d195a0 | ||
|
3ca5292dc9 | ||
|
c541d58d2f | ||
|
3037b5b7ca | ||
|
c9a263c589 | ||
|
e4492afbad | ||
|
799beebd36 | ||
|
45ec5fd170 | ||
|
6e39ec6e26 | ||
|
d38a6cc7ea | ||
|
7f6ee7a46b | ||
|
34b77fcad4 | ||
|
3b3f45d49b | ||
|
3284a13fee | ||
|
fc9ffee2e3 | ||
|
3a672cb2a9 | ||
|
4b8e342309 | ||
|
5a2fa71b03 | ||
|
9a57ef9cbf | ||
|
59ca9bf480 | ||
|
7cce07bc99 | ||
|
0ae66f75ce | ||
|
5d1c758730 | ||
|
1b9323f105 | ||
|
cee7b5cb54 | ||
|
1585152341 | ||
|
8e699c2bfd | ||
|
904a3115a6 | ||
|
a506cf47ad | ||
|
7689c15413 | ||
|
f907113c19 | ||
|
140f962201 | ||
|
c23d907f12 | ||
|
ed782e7508 | ||
|
0732526465 | ||
|
39a4963782 | ||
|
37ce6b195a | ||
|
7aa6d36258 | ||
|
991017fc41 | ||
|
5f548c7679 | ||
|
d14aacf03e | ||
|
39ff4d22e9 | ||
|
196f8410ba | ||
|
8462493cbf | ||
|
47b8bd1728 | ||
|
89cdde7f1e | ||
|
657fd671ea | ||
|
315c475b79 | ||
|
b800b4ec1d | ||
|
208ac32fa2 | ||
|
641476d40f | ||
|
491c8ab4c1 | ||
|
5ef8a6e887 | ||
|
d694d47d22 | ||
|
ecdc1b9bb0 | ||
|
9c757108ca | ||
|
5243e54641 | ||
|
70e7454c21 | ||
|
2644a529f0 | ||
|
eaee98ee1f | ||
|
35a1d747b0 | ||
|
34974e3932 | ||
|
575a46d9d4 | ||
|
bcadbd1b10 | ||
|
ead9daf1ba | ||
|
22949e6dfd | ||
|
49986b05bc | ||
|
2fa7af4614 | ||
|
2d857c4b1b | ||
|
2b63f57b4c | ||
|
4085c72496 | ||
|
6f9956990c | ||
|
9bf5862def | ||
|
e1d98bb7f6 | ||
|
e5cd006bce | ||
|
58242e1b63 | ||
|
4e86835163 | ||
|
ab4ac04c88 | ||
|
2c1887a635 | ||
|
1c82281b77 | ||
|
43ac92b615 | ||
|
901b456151 | ||
|
8813684040 | ||
|
58f36bb321 | ||
|
4c2f56626a | ||
|
e3337d764a | ||
|
214817ed17 | ||
|
116a4ec705 | ||
|
bbaad22982 | ||
|
a7250af303 | ||
|
6545a21369 | ||
|
416f03c05b | ||
|
ced74251e9 | ||
|
6c345f9fa2 | ||
|
8c0c8e8e01 | ||
|
79315efd1f | ||
|
a61130aebb | ||
|
5a51a0ba06 | ||
|
0d0b56739d | ||
|
eb1816c9ff | ||
|
50ae892d5e | ||
|
5a79b3d74a | ||
|
460599ef75 | ||
|
830bb72d6f | ||
|
b11c26cc1c | ||
|
152290db7e | ||
|
20b198681b | ||
|
2bf106a1b3 | ||
|
2c445059f2 | ||
|
d8b08cd943 | ||
|
ab59f64f57 | ||
|
42e3457884 | ||
|
31f3dd42e7 | ||
|
5fab8e404d | ||
|
701df09971 | ||
|
d350da3135 | ||
|
ab7dea4f20 | ||
|
b297efb532 | ||
|
eaabdb5cac | ||
|
066aee3045 | ||
|
ff1ec0260e | ||
|
7cb3188fbc | ||
|
9b9022f8df | ||
|
68e049d3af | ||
|
86ddf17db0 | ||
|
a655500047 | ||
|
714f885dac | ||
|
b0d8f552c5 | ||
|
63cb69cb96 | ||
|
266974cb59 | ||
|
bcf54b0aa3 | ||
|
d86855ad7a | ||
|
d0c67bbe16 | ||
|
4242352852 | ||
|
f299689573 | ||
|
baf457e6d4 | ||
|
0c7e871ef0 | ||
|
87ce804b4a | ||
|
2a258a2c3f | ||
|
a40518c7da | ||
|
31deaa4a79 | ||
|
736ea055a8 | ||
|
a39aec0bae | ||
|
8bef3eb1f4 | ||
|
244d88dfda | ||
|
76e1dd41e4 | ||
|
e39187a063 | ||
|
cd2ba4aa7f | ||
|
0de19420f6 | ||
|
f3000d0c84 | ||
|
fc5b0471d9 | ||
|
264bce4ddd | ||
|
0f41570c80 | ||
|
8723c91db9 | ||
|
f85c8896d9 | ||
|
f0d4f56327 | ||
|
3d5c05e4e6 | ||
|
018da09f14 | ||
|
60a64af28d | ||
|
13a2013229 | ||
|
1941b5c769 | ||
|
21e2c41c6b | ||
|
589781721a | ||
|
2ce57a65ff | ||
|
2aa77025c3 | ||
|
8e1856a90a | ||
|
ca619c80b6 | ||
|
25ff699425 | ||
|
879b4c0a2c | ||
|
45d07a3d0b | ||
|
788354d81e | ||
|
b7ce89e568 | ||
|
8d81a259c7 | ||
|
3019761204 | ||
|
6120a4c5e4 | ||
|
533ed2a876 | ||
|
d4e4055d57 | ||
|
ee51ad8e29 | ||
|
bdd50c5f37 | ||
|
3d88612690 | ||
|
630d54c95a | ||
|
3cedd8cf07 | ||
|
929f326dd2 | ||
|
ff8c648c23 | ||
|
8bceb5ce36 | ||
|
8f2703e8b2 | ||
|
c394eef7b8 | ||
|
f9921ae362 | ||
|
fff0c6cd8e | ||
|
59872bf335 | ||
|
656854186c | ||
|
6596bff8ec | ||
|
eaafd9d61c | ||
|
5378bfc5c7 | ||
|
abeeeeb611 | ||
|
ca3655a409 | ||
|
6cf5100645 | ||
|
51628aea08 | ||
|
3395f5fb0e | ||
|
d2cd079541 | ||
|
fc35e3b8c5 | ||
|
085b4f5f2e | ||
|
fd58645dd2 | ||
|
200787ede2 | ||
|
9cdba04fe9 | ||
|
e92c87630d | ||
|
d4e93a6de3 | ||
|
4670857c15 | ||
|
e8d8aedd1f | ||
|
87372da730 | ||
|
3b40b62d04 | ||
|
c41cbf2a07 | ||
|
1a3faa8db1 | ||
|
4ce79baac7 | ||
|
056b70b4ce | ||
|
4806b3b9bf | ||
|
2a8f0000b2 | ||
|
dd2d846c02 | ||
|
2ae87eee4e | ||
|
4be23027ed | ||
|
c19bbb2403 | ||
|
edb871f514 | ||
|
9c5937df96 | ||
|
be6082df8e | ||
|
66354de219 | ||
|
458a40f74e | ||
|
0821384ac6 | ||
|
e01650f21d | ||
|
8dd06cf197 | ||
|
93732b4c1e | ||
|
2cc63069c6 | ||
|
6270ecef8c | ||
|
9293ae76bf | ||
|
74d3f7e1fd | ||
|
2fd023a239 | ||
|
ffd2483e67 | ||
|
a14aab67de | ||
|
106d804357 | ||
|
a1020307a0 | ||
|
6c70b4ce05 | ||
|
2a292efb56 | ||
|
82b1a34a36 | ||
|
8da2a6a147 | ||
|
7d71e702d8 | ||
|
38d18ca11a | ||
|
32d9563a15 | ||
|
18f7e52562 | ||
|
fec541373d | ||
|
ff600e9aa0 | ||
|
a49357b19e | ||
|
4b997c29ee | ||
|
d321839669 | ||
|
c27fda09dd | ||
|
23eb84db35 | ||
|
bef91ea7fe | ||
|
459633fb4c | ||
|
9ed296ae71 | ||
|
e8d0960cef | ||
|
2023115ff8 | ||
|
7790ae9e6f | ||
|
206da7a1b8 | ||
|
14eaba9ec3 |
201
.circleci/config.yml
Normal file
201
.circleci/config.yml
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
version: 2
|
||||||
|
|
||||||
|
defaults: &defaults
|
||||||
|
working_directory: /go/src/github.com/tendermint/tendermint
|
||||||
|
docker:
|
||||||
|
- image: circleci/golang:1.10.0
|
||||||
|
environment:
|
||||||
|
GOBIN: /tmp/workspace/bin
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
setup_dependencies:
|
||||||
|
<<: *defaults
|
||||||
|
steps:
|
||||||
|
- run: mkdir -p /tmp/workspace/bin
|
||||||
|
- run: mkdir -p /tmp/workspace/profiles
|
||||||
|
- checkout
|
||||||
|
- restore_cache:
|
||||||
|
keys:
|
||||||
|
- v1-pkg-cache
|
||||||
|
- run:
|
||||||
|
name: tools
|
||||||
|
command: |
|
||||||
|
export PATH="$GOBIN:$PATH"
|
||||||
|
make get_tools
|
||||||
|
- run:
|
||||||
|
name: dependencies
|
||||||
|
command: |
|
||||||
|
export PATH="$GOBIN:$PATH"
|
||||||
|
make get_vendor_deps
|
||||||
|
- run:
|
||||||
|
name: binaries
|
||||||
|
command: |
|
||||||
|
export PATH="$GOBIN:$PATH"
|
||||||
|
make install
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: /tmp/workspace
|
||||||
|
paths:
|
||||||
|
- bin
|
||||||
|
- profiles
|
||||||
|
- save_cache:
|
||||||
|
key: v1-pkg-cache
|
||||||
|
paths:
|
||||||
|
- /go/pkg
|
||||||
|
- save_cache:
|
||||||
|
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
|
paths:
|
||||||
|
- /go/src/github.com/tendermint/tendermint
|
||||||
|
|
||||||
|
setup_abci:
|
||||||
|
<<: *defaults
|
||||||
|
steps:
|
||||||
|
- attach_workspace:
|
||||||
|
at: /tmp/workspace
|
||||||
|
- restore_cache:
|
||||||
|
key: v1-pkg-cache
|
||||||
|
- restore_cache:
|
||||||
|
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
|
- run:
|
||||||
|
name: Checkout abci
|
||||||
|
command: |
|
||||||
|
commit=$(bash scripts/dep_utils/parse.sh abci)
|
||||||
|
go get -v -u -d github.com/tendermint/abci/...
|
||||||
|
cd /go/src/github.com/tendermint/abci
|
||||||
|
git checkout "$commit"
|
||||||
|
- run:
|
||||||
|
working_directory: /go/src/github.com/tendermint/abci
|
||||||
|
name: Install abci
|
||||||
|
command: |
|
||||||
|
set -ex
|
||||||
|
export PATH="$GOBIN:$PATH"
|
||||||
|
make get_tools
|
||||||
|
make get_vendor_deps
|
||||||
|
make install
|
||||||
|
- run: ls -lah /tmp/workspace/bin
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: /tmp/workspace
|
||||||
|
paths:
|
||||||
|
- "bin/abci*"
|
||||||
|
|
||||||
|
lint:
|
||||||
|
<<: *defaults
|
||||||
|
steps:
|
||||||
|
- attach_workspace:
|
||||||
|
at: /tmp/workspace
|
||||||
|
- restore_cache:
|
||||||
|
key: v1-pkg-cache
|
||||||
|
- restore_cache:
|
||||||
|
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
|
- run:
|
||||||
|
name: metalinter
|
||||||
|
command: |
|
||||||
|
set -ex
|
||||||
|
export PATH="$GOBIN:$PATH"
|
||||||
|
make metalinter
|
||||||
|
|
||||||
|
test_apps:
|
||||||
|
<<: *defaults
|
||||||
|
steps:
|
||||||
|
- attach_workspace:
|
||||||
|
at: /tmp/workspace
|
||||||
|
- restore_cache:
|
||||||
|
key: v1-pkg-cache
|
||||||
|
- restore_cache:
|
||||||
|
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
|
- run: sudo apt-get update && sudo apt-get install -y --no-install-recommends bsdmainutils
|
||||||
|
- run:
|
||||||
|
name: Run tests
|
||||||
|
command: bash test/app/test.sh
|
||||||
|
|
||||||
|
test_cover:
|
||||||
|
<<: *defaults
|
||||||
|
parallelism: 4
|
||||||
|
steps:
|
||||||
|
- attach_workspace:
|
||||||
|
at: /tmp/workspace
|
||||||
|
- restore_cache:
|
||||||
|
key: v1-pkg-cache
|
||||||
|
- restore_cache:
|
||||||
|
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
|
- run:
|
||||||
|
name: Run tests
|
||||||
|
command: |
|
||||||
|
for pkg in $(go list github.com/tendermint/tendermint/... | grep -v /vendor/ | circleci tests split --split-by=timings); do
|
||||||
|
id=$(basename "$pkg")
|
||||||
|
|
||||||
|
go test -timeout 5m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg"
|
||||||
|
done
|
||||||
|
- persist_to_workspace:
|
||||||
|
root: /tmp/workspace
|
||||||
|
paths:
|
||||||
|
- "profiles/*"
|
||||||
|
|
||||||
|
test_persistence:
|
||||||
|
<<: *defaults
|
||||||
|
steps:
|
||||||
|
- attach_workspace:
|
||||||
|
at: /tmp/workspace
|
||||||
|
- restore_cache:
|
||||||
|
key: v1-pkg-cache
|
||||||
|
- restore_cache:
|
||||||
|
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
|
- run:
|
||||||
|
name: Run tests
|
||||||
|
command: bash test/persist/test_failure_indices.sh
|
||||||
|
|
||||||
|
test_p2p:
|
||||||
|
environment:
|
||||||
|
GOBIN: /home/circleci/.go_workspace/bin
|
||||||
|
GOPATH: /home/circleci/.go_workspace
|
||||||
|
machine:
|
||||||
|
image: circleci/classic:latest
|
||||||
|
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/circleci/p2p.sh
|
||||||
|
|
||||||
|
upload_coverage:
|
||||||
|
<<: *defaults
|
||||||
|
steps:
|
||||||
|
- attach_workspace:
|
||||||
|
at: /tmp/workspace
|
||||||
|
- restore_cache:
|
||||||
|
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
|
- run:
|
||||||
|
name: gather
|
||||||
|
command: |
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
echo "mode: atomic" > coverage.txt
|
||||||
|
for prof in $(ls /tmp/workspace/profiles/); do
|
||||||
|
tail -n +2 /tmp/workspace/profiles/"$prof" >> coverage.txt
|
||||||
|
done
|
||||||
|
- run:
|
||||||
|
name: upload
|
||||||
|
command: bash <(curl -s https://codecov.io/bash) -f coverage.txt
|
||||||
|
|
||||||
|
workflows:
|
||||||
|
version: 2
|
||||||
|
test-suite:
|
||||||
|
jobs:
|
||||||
|
- setup_dependencies
|
||||||
|
- setup_abci:
|
||||||
|
requires:
|
||||||
|
- setup_dependencies
|
||||||
|
- lint:
|
||||||
|
requires:
|
||||||
|
- setup_dependencies
|
||||||
|
- test_apps:
|
||||||
|
requires:
|
||||||
|
- setup_abci
|
||||||
|
- test_cover:
|
||||||
|
requires:
|
||||||
|
- setup_dependencies
|
||||||
|
- test_persistence:
|
||||||
|
requires:
|
||||||
|
- setup_abci
|
||||||
|
- test_p2p
|
||||||
|
- upload_coverage:
|
||||||
|
requires:
|
||||||
|
- test_cover
|
26
.codecov.yml
26
.codecov.yml
@@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
#
|
|
||||||
# This codecov.yml is the default configuration for
|
|
||||||
# all repositories on Codecov. You may adjust the settings
|
|
||||||
# below in your own codecov.yml in your repository.
|
|
||||||
#
|
|
||||||
coverage:
|
|
||||||
precision: 2
|
|
||||||
round: down
|
|
||||||
range: 70...100
|
|
||||||
|
|
||||||
status:
|
|
||||||
# Learn more at https://codecov.io/docs#yaml_default_commit_status
|
|
||||||
project:
|
|
||||||
default:
|
|
||||||
threshold: 1% # allow this much decrease on project
|
|
||||||
changes: false
|
|
||||||
|
|
||||||
comment:
|
|
||||||
layout: "header, diff"
|
|
||||||
behavior: default # update if exists else create new
|
|
||||||
|
|
||||||
ignore:
|
|
||||||
- "docs"
|
|
||||||
- "*.md"
|
|
||||||
- "*.rst"
|
|
3
.github/ISSUE_TEMPLATE
vendored
3
.github/ISSUE_TEMPLATE
vendored
@@ -37,5 +37,8 @@ in a case of bug.
|
|||||||
|
|
||||||
**How to reproduce it** (as minimally and precisely as possible):
|
**How to reproduce it** (as minimally and precisely as possible):
|
||||||
|
|
||||||
|
**Logs (you can paste a part showing an error or attach the whole file)**:
|
||||||
|
|
||||||
|
**`/dump_consensus_state` output for consensus bugs**
|
||||||
|
|
||||||
**Anything else do we need to know**:
|
**Anything else do we need to know**:
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -17,7 +17,11 @@ test/logs
|
|||||||
coverage.txt
|
coverage.txt
|
||||||
docs/_build
|
docs/_build
|
||||||
docs/tools
|
docs/tools
|
||||||
|
docs/abci-spec.rst
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
scripts/wal2json/wal2json
|
scripts/wal2json/wal2json
|
||||||
scripts/cutWALUntil/cutWALUntil
|
scripts/cutWALUntil/cutWALUntil
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
80
CHANGELOG.md
80
CHANGELOG.md
@@ -7,7 +7,6 @@ BREAKING CHANGES:
|
|||||||
- Upgrade consensus for more real-time use of evidence
|
- Upgrade consensus for more real-time use of evidence
|
||||||
|
|
||||||
FEATURES:
|
FEATURES:
|
||||||
- Peer reputation management
|
|
||||||
- Use the chain as its own CA for nodes and validators
|
- Use the chain as its own CA for nodes and validators
|
||||||
- Tooling to run multiple blockchains/apps, possibly in a single process
|
- Tooling to run multiple blockchains/apps, possibly in a single process
|
||||||
- State syncing (without transaction replay)
|
- State syncing (without transaction replay)
|
||||||
@@ -25,7 +24,84 @@ BUG FIXES:
|
|||||||
- Graceful handling/recovery for apps that have non-determinism or fail to halt
|
- Graceful handling/recovery for apps that have non-determinism or fail to halt
|
||||||
- Graceful handling/recovery for violations of safety, or liveness
|
- Graceful handling/recovery for violations of safety, or liveness
|
||||||
|
|
||||||
## 0.16.0 (February 20th, 2017)
|
## 0.19.0 (April 13th, 2018)
|
||||||
|
|
||||||
|
BREAKING:
|
||||||
|
- [cmd] improved `testnet` command; now it can fill in `persistent_peers` for you in the config file and much more (see `tendermint testnet --help` for details)
|
||||||
|
- [cmd] `show_node_id` now returns an error if there is no node key
|
||||||
|
- [rpc]: changed the output format for the `/status` endpoint (see https://godoc.org/github.com/tendermint/tendermint/rpc/core#Status)
|
||||||
|
|
||||||
|
Upgrade from go-wire to go-amino. This is a sweeping change that breaks everything that is
|
||||||
|
serialized to disk or over the network.
|
||||||
|
|
||||||
|
See github.com/tendermint/go-amino for details on the new format.
|
||||||
|
|
||||||
|
See `scripts/wire2amino.go` for a tool to upgrade
|
||||||
|
genesis/priv_validator/node_key JSON files.
|
||||||
|
|
||||||
|
FEATURES:
|
||||||
|
- [cmd] added `gen_node_key` command
|
||||||
|
|
||||||
|
## 0.18.0 (April 6th, 2018)
|
||||||
|
|
||||||
|
BREAKING:
|
||||||
|
|
||||||
|
- [types] Merkle tree uses different encoding for varints (see tmlibs v0.8.0)
|
||||||
|
- [types] ValidtorSet.GetByAddress returns -1 if no validator found
|
||||||
|
- [p2p] require all addresses come with an ID no matter what
|
||||||
|
- [rpc] Listening address must contain tcp:// or unix:// prefix
|
||||||
|
|
||||||
|
FEATURES:
|
||||||
|
|
||||||
|
- [rpc] StartHTTPAndTLSServer (not used yet)
|
||||||
|
- [rpc] Include validator's voting power in `/status`
|
||||||
|
- [rpc] `/tx` and `/tx_search` responses now include the transaction hash
|
||||||
|
- [rpc] Include peer NodeIDs in `/net_info`
|
||||||
|
|
||||||
|
IMPROVEMENTS:
|
||||||
|
- [config] trim whitespace from elements of lists (like `persistent_peers`)
|
||||||
|
- [rpc] `/tx_search` results are sorted by height
|
||||||
|
- [p2p] do not try to connect to ourselves (ok, maybe only once)
|
||||||
|
- [p2p] seeds respond with a bias towards good peers
|
||||||
|
|
||||||
|
BUG FIXES:
|
||||||
|
- [rpc] fix subscribing using an abci.ResponseDeliverTx tag
|
||||||
|
- [rpc] fix tx_indexers matchRange
|
||||||
|
- [rpc] fix unsubscribing (see tmlibs v0.8.0)
|
||||||
|
|
||||||
|
## 0.17.1 (March 27th, 2018)
|
||||||
|
|
||||||
|
BUG FIXES:
|
||||||
|
- [types] Actually support `app_state` in genesis as `AppStateJSON`
|
||||||
|
|
||||||
|
## 0.17.0 (March 27th, 2018)
|
||||||
|
|
||||||
|
BREAKING:
|
||||||
|
- [types] WriteSignBytes -> SignBytes
|
||||||
|
|
||||||
|
IMPROVEMENTS:
|
||||||
|
- [all] renamed `dummy` (`persistent_dummy`) to `kvstore` (`persistent_kvstore`) (name "dummy" is deprecated and will not work in the next breaking release)
|
||||||
|
- [docs] note on determinism (docs/determinism.rst)
|
||||||
|
- [genesis] `app_options` field is deprecated. please rename it to `app_state` in your genesis file(s). `app_options` will not work in the next breaking release
|
||||||
|
- [p2p] dial seeds directly without potential peers
|
||||||
|
- [p2p] exponential backoff for addrs in the address book
|
||||||
|
- [p2p] mark peer as good if it contributed enough votes or block parts
|
||||||
|
- [p2p] stop peer if it sends incorrect data, msg to unknown channel, msg we did not expect
|
||||||
|
- [p2p] when `auth_enc` is true, all dialed peers must have a node ID in their address
|
||||||
|
- [spec] various improvements
|
||||||
|
- switched from glide to dep internally for package management
|
||||||
|
- [wire] prep work for upgrading to new go-wire (which is now called go-amino)
|
||||||
|
|
||||||
|
FEATURES:
|
||||||
|
- [config] exposed `auth_enc` flag to enable/disable encryption
|
||||||
|
- [config] added the `--p2p.private_peer_ids` flag and `PrivatePeerIDs` config variable (see config for description)
|
||||||
|
- [rpc] added `/health` endpoint, which returns empty result for now
|
||||||
|
- [types/priv_validator] new format and socket client, allowing for remote signing
|
||||||
|
|
||||||
|
BUG FIXES:
|
||||||
|
- [consensus] fix liveness bug by introducing ValidBlock mechanism
|
||||||
|
|
||||||
|
## 0.16.0 (February 20th, 2018)
|
||||||
|
|
||||||
BREAKING CHANGES:
|
BREAKING CHANGES:
|
||||||
- [config] use $TMHOME/config for all config and json files
|
- [config] use $TMHOME/config for all config and json files
|
||||||
|
@@ -34,15 +34,26 @@ Please don't make Pull Requests to `master`.
|
|||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
We use [glide](https://github.com/masterminds/glide) to manage dependencies.
|
We use [dep](https://github.com/golang/dep) to manage dependencies.
|
||||||
That said, the master branch of every Tendermint repository should just build with `go get`, which means they should be kept up-to-date with their dependencies so we can get away with telling people they can just `go get` our software.
|
|
||||||
Since some dependencies are not under our control, a third party may break our build, in which case we can fall back on `glide install`. Even for dependencies under our control, glide helps us keeps multiple repos in sync as they evolve. Anything with an executable, such as apps, tools, and the core, should use glide.
|
|
||||||
|
|
||||||
Run `bash scripts/glide/status.sh` to get a list of vendored dependencies that may not be up-to-date.
|
That said, the master branch of every Tendermint repository should just build
|
||||||
|
with `go get`, which means they should be kept up-to-date with their
|
||||||
|
dependencies so we can get away with telling people they can just `go get` our
|
||||||
|
software.
|
||||||
|
|
||||||
|
Since some dependencies are not under our control, a third party may break our
|
||||||
|
build, in which case we can fall back on `dep ensure` (or `make
|
||||||
|
get_vendor_deps`). Even for dependencies under our control, dep helps us to
|
||||||
|
keep multiple repos in sync as they evolve. Anything with an executable, such
|
||||||
|
as apps, tools, and the core, should use dep.
|
||||||
|
|
||||||
|
Run `dep status` to get a list of vendored dependencies that may not be
|
||||||
|
up-to-date.
|
||||||
|
|
||||||
## Vagrant
|
## Vagrant
|
||||||
|
|
||||||
If you are a [Vagrant](https://www.vagrantup.com/) user, you can get started hacking Tendermint with the commands below.
|
If you are a [Vagrant](https://www.vagrantup.com/) user, you can get started
|
||||||
|
hacking Tendermint with the commands below.
|
||||||
|
|
||||||
NOTE: In case you installed Vagrant in 2017, you might need to run
|
NOTE: In case you installed Vagrant in 2017, you might need to run
|
||||||
`vagrant box update` to upgrade to the latest `ubuntu/xenial64`.
|
`vagrant box update` to upgrade to the latest `ubuntu/xenial64`.
|
||||||
@@ -53,11 +64,14 @@ vagrant ssh
|
|||||||
make test
|
make test
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
All repos should be hooked up to circle.
|
All repos should be hooked up to [CircleCI](https://circleci.com/).
|
||||||
If they have `.go` files in the root directory, they will be automatically tested by circle using `go test -v -race ./...`. If not, they will need a `circle.yml`. Ideally, every repo has a `Makefile` that defines `make test` and includes its continuous integration status using a badge in the `README.md`.
|
|
||||||
|
If they have `.go` files in the root directory, they will be automatically
|
||||||
|
tested by circle using `go test -v -race ./...`. If not, they will need a
|
||||||
|
`circle.yml`. Ideally, every repo has a `Makefile` that defines `make test` and
|
||||||
|
includes its continuous integration status using a badge in the `README.md`.
|
||||||
|
|
||||||
## Branching Model and Release
|
## Branching Model and Release
|
||||||
|
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
FROM alpine:3.6
|
FROM alpine:3.7
|
||||||
|
|
||||||
# This is the release of tendermint to pull in.
|
# This is the release of tendermint to pull in.
|
||||||
ENV TM_VERSION 0.15.0
|
ENV TM_VERSION 0.17.1
|
||||||
ENV TM_SHA256SUM 71cc271c67eca506ca492c8b90b090132f104bf5dbfe0af2702a50886e88de17
|
ENV TM_SHA256SUM d57008c63d2d9176861137e38ed203da486febf20ae7d388fb810a75afff8f24
|
||||||
|
|
||||||
# Tendermint will be looking for genesis file in /tendermint (unless you change
|
# Tendermint will be looking for genesis file in /tendermint (unless you change
|
||||||
# `genesis_file` in config.toml). You can put your config.toml and private
|
# `genesis_file` in config.toml). You can put your config.toml and private
|
||||||
@@ -26,7 +26,7 @@ RUN mkdir -p $DATA_ROOT && \
|
|||||||
RUN apk add --no-cache bash curl jq
|
RUN apk add --no-cache bash curl jq
|
||||||
|
|
||||||
RUN apk add --no-cache openssl && \
|
RUN apk add --no-cache openssl && \
|
||||||
wget https://s3-us-west-2.amazonaws.com/tendermint/binaries/tendermint/v${TM_VERSION}/tendermint_${TM_VERSION}_linux_amd64.zip && \
|
wget https://github.com/tendermint/tendermint/releases/download/v${TM_VERSION}/tendermint_${TM_VERSION}_linux_amd64.zip && \
|
||||||
echo "${TM_SHA256SUM} tendermint_${TM_VERSION}_linux_amd64.zip" | sha256sum -c && \
|
echo "${TM_SHA256SUM} tendermint_${TM_VERSION}_linux_amd64.zip" | sha256sum -c && \
|
||||||
unzip -d /bin tendermint_${TM_VERSION}_linux_amd64.zip && \
|
unzip -d /bin tendermint_${TM_VERSION}_linux_amd64.zip && \
|
||||||
apk del openssl && \
|
apk del openssl && \
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
FROM alpine:3.6
|
FROM alpine:3.7
|
||||||
|
|
||||||
ENV DATA_ROOT /tendermint
|
ENV DATA_ROOT /tendermint
|
||||||
ENV TMHOME $DATA_ROOT
|
ENV TMHOME $DATA_ROOT
|
||||||
@@ -18,9 +18,9 @@ RUN mkdir -p /go/src/github.com/tendermint/tendermint && \
|
|||||||
cd /go/src/github.com/tendermint/tendermint && \
|
cd /go/src/github.com/tendermint/tendermint && \
|
||||||
git clone https://github.com/tendermint/tendermint . && \
|
git clone https://github.com/tendermint/tendermint . && \
|
||||||
git checkout develop && \
|
git checkout develop && \
|
||||||
|
make get_tools && \
|
||||||
make get_vendor_deps && \
|
make get_vendor_deps && \
|
||||||
make install && \
|
make install && \
|
||||||
glide cc && \
|
|
||||||
cd - && \
|
cd - && \
|
||||||
rm -rf /go/src/github.com/tendermint/tendermint && \
|
rm -rf /go/src/github.com/tendermint/tendermint && \
|
||||||
apk del go build-base git
|
apk del go build-base git
|
||||||
@@ -32,4 +32,4 @@ EXPOSE 46657
|
|||||||
|
|
||||||
ENTRYPOINT ["tendermint"]
|
ENTRYPOINT ["tendermint"]
|
||||||
|
|
||||||
CMD ["node", "--moniker=`hostname`", "--proxy_app=dummy"]
|
CMD ["node", "--moniker=`hostname`", "--proxy_app=kvstore"]
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
# Supported tags and respective `Dockerfile` links
|
# Supported tags and respective `Dockerfile` links
|
||||||
|
|
||||||
- `0.15.0`, `latest` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/170777300ea92dc21a8aec1abc16cb51812513a4/DOCKER/Dockerfile)
|
- `0.17.1`, `latest` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/208ac32fa266657bd6c304e84ec828aa252bb0b8/DOCKER/Dockerfile)
|
||||||
|
- `0.15.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/170777300ea92dc21a8aec1abc16cb51812513a4/DOCKER/Dockerfile)
|
||||||
- `0.13.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/a28b3fff49dce2fb31f90abb2fc693834e0029c2/DOCKER/Dockerfile)
|
- `0.13.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/a28b3fff49dce2fb31f90abb2fc693834e0029c2/DOCKER/Dockerfile)
|
||||||
- `0.12.1` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/457c688346b565e90735431619ca3ca597ef9007/DOCKER/Dockerfile)
|
- `0.12.1` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/457c688346b565e90735431619ca3ca597ef9007/DOCKER/Dockerfile)
|
||||||
- `0.12.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/70d8afa6e952e24c573ece345560a5971bf2cc0e/DOCKER/Dockerfile)
|
- `0.12.0` [(Dockerfile)](https://github.com/tendermint/tendermint/blob/70d8afa6e952e24c573ece345560a5971bf2cc0e/DOCKER/Dockerfile)
|
||||||
@@ -34,13 +35,13 @@ To get started developing applications, see the [application developers guide](h
|
|||||||
|
|
||||||
# How to use this image
|
# How to use this image
|
||||||
|
|
||||||
## Start one instance of the Tendermint core with the `dummy` app
|
## Start one instance of the Tendermint core with the `kvstore` app
|
||||||
|
|
||||||
A very simple example of a built-in app and Tendermint core in one container.
|
A very simple example of a built-in app and Tendermint core in one container.
|
||||||
|
|
||||||
```
|
```
|
||||||
docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint init
|
docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint init
|
||||||
docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint node --proxy_app=dummy
|
docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint node --proxy_app=kvstore
|
||||||
```
|
```
|
||||||
|
|
||||||
## mintnet-kubernetes
|
## mintnet-kubernetes
|
||||||
|
388
Gopkg.lock
generated
Normal file
388
Gopkg.lock
generated
Normal file
@@ -0,0 +1,388 @@
|
|||||||
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/btcsuite/btcd"
|
||||||
|
packages = ["btcec"]
|
||||||
|
revision = "2be2f12b358dc57d70b8f501b00be450192efbc3"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/davecgh/go-spew"
|
||||||
|
packages = ["spew"]
|
||||||
|
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||||
|
version = "v1.1.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/ebuchman/fail-test"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "95f809107225be108efcf10a3509e4ea6ceef3c4"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/fortytw2/leaktest"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "a5ef70473c97b71626b9abeda80ee92ba2a7de9e"
|
||||||
|
version = "v1.2.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/fsnotify/fsnotify"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
|
||||||
|
version = "v1.4.7"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/go-kit/kit"
|
||||||
|
packages = [
|
||||||
|
"log",
|
||||||
|
"log/level",
|
||||||
|
"log/term"
|
||||||
|
]
|
||||||
|
revision = "4dc7be5d2d12881735283bcab7352178e190fc71"
|
||||||
|
version = "v0.6.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/go-logfmt/logfmt"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5"
|
||||||
|
version = "v0.3.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/go-stack/stack"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc"
|
||||||
|
version = "v1.7.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/gogo/protobuf"
|
||||||
|
packages = [
|
||||||
|
"gogoproto",
|
||||||
|
"jsonpb",
|
||||||
|
"proto",
|
||||||
|
"protoc-gen-gogo/descriptor",
|
||||||
|
"sortkeys",
|
||||||
|
"types"
|
||||||
|
]
|
||||||
|
revision = "1adfc126b41513cc696b209667c8656ea7aac67c"
|
||||||
|
version = "v1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/golang/protobuf"
|
||||||
|
packages = [
|
||||||
|
"proto",
|
||||||
|
"ptypes",
|
||||||
|
"ptypes/any",
|
||||||
|
"ptypes/duration",
|
||||||
|
"ptypes/timestamp"
|
||||||
|
]
|
||||||
|
revision = "925541529c1fa6821df4e44ce2723319eb2be768"
|
||||||
|
version = "v1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/golang/snappy"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "553a641470496b2327abcac10b36396bd98e45c9"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/gorilla/websocket"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
|
||||||
|
version = "v1.2.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/hashicorp/hcl"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"hcl/ast",
|
||||||
|
"hcl/parser",
|
||||||
|
"hcl/printer",
|
||||||
|
"hcl/scanner",
|
||||||
|
"hcl/strconv",
|
||||||
|
"hcl/token",
|
||||||
|
"json/parser",
|
||||||
|
"json/scanner",
|
||||||
|
"json/token"
|
||||||
|
]
|
||||||
|
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/inconshreveable/mousetrap"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
||||||
|
version = "v1.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/jmhodges/levigo"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/kr/logfmt"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/magiconair/properties"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "c3beff4c2358b44d0493c7dda585e7db7ff28ae6"
|
||||||
|
version = "v1.7.6"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/mitchellh/mapstructure"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "00c29f56e2386353d58c599509e8dc3801b0d716"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/pelletier/go-toml"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "acdc4509485b587f5e675510c4f2c63e90ff68a8"
|
||||||
|
version = "v1.1.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/pkg/errors"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||||
|
version = "v0.8.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/pmezard/go-difflib"
|
||||||
|
packages = ["difflib"]
|
||||||
|
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||||
|
version = "v1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/rcrowley/go-metrics"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "d932a24a8ccb8fcadc993e5c6c58f93dac168294"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/spf13/afero"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"mem"
|
||||||
|
]
|
||||||
|
revision = "63644898a8da0bc22138abf860edaf5277b6102e"
|
||||||
|
version = "v1.1.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/spf13/cast"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "8965335b8c7107321228e3e3702cab9832751bac"
|
||||||
|
version = "v1.2.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/spf13/cobra"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4"
|
||||||
|
version = "v0.0.2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/spf13/jwalterweatherman"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/spf13/pflag"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
|
||||||
|
version = "v1.0.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/spf13/viper"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "b5e8006cbee93ec955a89ab31e0e3ce3204f3736"
|
||||||
|
version = "v1.0.2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/stretchr/testify"
|
||||||
|
packages = [
|
||||||
|
"assert",
|
||||||
|
"require"
|
||||||
|
]
|
||||||
|
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
|
||||||
|
version = "v1.2.1"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/syndtr/goleveldb"
|
||||||
|
packages = [
|
||||||
|
"leveldb",
|
||||||
|
"leveldb/cache",
|
||||||
|
"leveldb/comparer",
|
||||||
|
"leveldb/errors",
|
||||||
|
"leveldb/filter",
|
||||||
|
"leveldb/iterator",
|
||||||
|
"leveldb/journal",
|
||||||
|
"leveldb/memdb",
|
||||||
|
"leveldb/opt",
|
||||||
|
"leveldb/storage",
|
||||||
|
"leveldb/table",
|
||||||
|
"leveldb/util"
|
||||||
|
]
|
||||||
|
revision = "714f901b98fdb3aa954b4193d8cbd64a28d80cad"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/tendermint/abci"
|
||||||
|
packages = [
|
||||||
|
"client",
|
||||||
|
"example/code",
|
||||||
|
"example/counter",
|
||||||
|
"example/kvstore",
|
||||||
|
"server",
|
||||||
|
"types"
|
||||||
|
]
|
||||||
|
revision = "78a8905690ef54f9d57e3b2b0ee7ad3a04ef3f1f"
|
||||||
|
version = "v0.10.3"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/tendermint/ed25519"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"edwards25519",
|
||||||
|
"extra25519"
|
||||||
|
]
|
||||||
|
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/tendermint/go-amino"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "42246108ff925a457fb709475070a03dfd3e2b5c"
|
||||||
|
version = "0.9.6"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/tendermint/go-crypto"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "915416979bf70efa4bcbf1c6cd5d64c5fff9fc19"
|
||||||
|
version = "v0.6.2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/tendermint/go-wire"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "fa721242b042ecd4c6ed1a934ee740db4f74e45c"
|
||||||
|
version = "v0.7.3"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/tendermint/tmlibs"
|
||||||
|
packages = [
|
||||||
|
"autofile",
|
||||||
|
"cli",
|
||||||
|
"cli/flags",
|
||||||
|
"clist",
|
||||||
|
"common",
|
||||||
|
"db",
|
||||||
|
"flowrate",
|
||||||
|
"log",
|
||||||
|
"merkle",
|
||||||
|
"pubsub",
|
||||||
|
"pubsub/query",
|
||||||
|
"test"
|
||||||
|
]
|
||||||
|
revision = "97e1f1ad3f510048929a51475811a18686c894df"
|
||||||
|
version = "0.8.2-rc0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/crypto"
|
||||||
|
packages = [
|
||||||
|
"curve25519",
|
||||||
|
"nacl/box",
|
||||||
|
"nacl/secretbox",
|
||||||
|
"openpgp/armor",
|
||||||
|
"openpgp/errors",
|
||||||
|
"poly1305",
|
||||||
|
"ripemd160",
|
||||||
|
"salsa20/salsa"
|
||||||
|
]
|
||||||
|
revision = "d6449816ce06963d9d136eee5a56fca5b0616e7e"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/net"
|
||||||
|
packages = [
|
||||||
|
"context",
|
||||||
|
"http2",
|
||||||
|
"http2/hpack",
|
||||||
|
"idna",
|
||||||
|
"internal/timeseries",
|
||||||
|
"lex/httplex",
|
||||||
|
"trace"
|
||||||
|
]
|
||||||
|
revision = "61147c48b25b599e5b561d2e9c4f3e1ef489ca41"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/sys"
|
||||||
|
packages = ["unix"]
|
||||||
|
revision = "3b87a42e500a6dc65dae1a55d0b641295971163e"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "golang.org/x/text"
|
||||||
|
packages = [
|
||||||
|
"collate",
|
||||||
|
"collate/build",
|
||||||
|
"internal/colltab",
|
||||||
|
"internal/gen",
|
||||||
|
"internal/tag",
|
||||||
|
"internal/triegen",
|
||||||
|
"internal/ucd",
|
||||||
|
"language",
|
||||||
|
"secure/bidirule",
|
||||||
|
"transform",
|
||||||
|
"unicode/bidi",
|
||||||
|
"unicode/cldr",
|
||||||
|
"unicode/norm",
|
||||||
|
"unicode/rangetable"
|
||||||
|
]
|
||||||
|
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||||
|
version = "v0.3.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "google.golang.org/genproto"
|
||||||
|
packages = ["googleapis/rpc/status"]
|
||||||
|
revision = "51d0944304c3cbce4afe9e5247e21100037bff78"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "google.golang.org/grpc"
|
||||||
|
packages = [
|
||||||
|
".",
|
||||||
|
"balancer",
|
||||||
|
"codes",
|
||||||
|
"connectivity",
|
||||||
|
"credentials",
|
||||||
|
"grpclb/grpc_lb_v1/messages",
|
||||||
|
"grpclog",
|
||||||
|
"internal",
|
||||||
|
"keepalive",
|
||||||
|
"metadata",
|
||||||
|
"naming",
|
||||||
|
"peer",
|
||||||
|
"resolver",
|
||||||
|
"stats",
|
||||||
|
"status",
|
||||||
|
"tap",
|
||||||
|
"transport"
|
||||||
|
]
|
||||||
|
revision = "5b3c4e850e90a4cf6a20ebd46c8b32a0a3afcb9e"
|
||||||
|
version = "v1.7.5"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "gopkg.in/yaml.v2"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
|
||||||
|
version = "v2.2.1"
|
||||||
|
|
||||||
|
[solve-meta]
|
||||||
|
analyzer-name = "dep"
|
||||||
|
analyzer-version = 1
|
||||||
|
inputs-digest = "e70f8692c825e80ae8510546e297840b9560d00e11b2272749a55cc2ffd147f0"
|
||||||
|
solver-name = "gps-cdcl"
|
||||||
|
solver-version = 1
|
94
Gopkg.toml
Normal file
94
Gopkg.toml
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
# Gopkg.toml example
|
||||||
|
#
|
||||||
|
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||||
|
# for detailed Gopkg.toml documentation.
|
||||||
|
#
|
||||||
|
# required = ["github.com/user/thing/cmd/thing"]
|
||||||
|
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project"
|
||||||
|
# version = "1.0.0"
|
||||||
|
#
|
||||||
|
# [[constraint]]
|
||||||
|
# name = "github.com/user/project2"
|
||||||
|
# branch = "dev"
|
||||||
|
# source = "github.com/myfork/project2"
|
||||||
|
#
|
||||||
|
# [[override]]
|
||||||
|
# name = "github.com/x/y"
|
||||||
|
# version = "2.4.0"
|
||||||
|
#
|
||||||
|
# [prune]
|
||||||
|
# non-go = false
|
||||||
|
# go-tests = true
|
||||||
|
# unused-packages = true
|
||||||
|
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/ebuchman/fail-test"
|
||||||
|
branch = "master"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/fortytw2/leaktest"
|
||||||
|
branch = "master"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/go-kit/kit"
|
||||||
|
version = "~0.6.0"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/gogo/protobuf"
|
||||||
|
version = "~1.0.0"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/golang/protobuf"
|
||||||
|
version = "~1.0.0"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/gorilla/websocket"
|
||||||
|
version = "~1.2.0"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/pkg/errors"
|
||||||
|
version = "~0.8.0"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/rcrowley/go-metrics"
|
||||||
|
branch = "master"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/spf13/cobra"
|
||||||
|
version = "~0.0.1"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/spf13/viper"
|
||||||
|
version = "~1.0.0"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/stretchr/testify"
|
||||||
|
version = "~1.2.1"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/tendermint/abci"
|
||||||
|
version = "~0.10.3"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/tendermint/go-crypto"
|
||||||
|
version = "~0.6.2"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/tendermint/go-amino"
|
||||||
|
version = "~0.9.6"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/tendermint/tmlibs"
|
||||||
|
version = "~0.8.2-rc0"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "google.golang.org/grpc"
|
||||||
|
version = "~1.7.3"
|
||||||
|
|
||||||
|
[prune]
|
||||||
|
go-tests = true
|
||||||
|
unused-packages = true
|
112
Makefile
112
Makefile
@@ -1,26 +1,26 @@
|
|||||||
GOTOOLS = \
|
GOTOOLS = \
|
||||||
github.com/tendermint/glide \
|
github.com/golang/dep/cmd/dep \
|
||||||
# gopkg.in/alecthomas/gometalinter.v2
|
gopkg.in/alecthomas/gometalinter.v2
|
||||||
PACKAGES=$(shell go list ./... | grep -v '/vendor/')
|
PACKAGES=$(shell go list ./... | grep -v '/vendor/')
|
||||||
BUILD_TAGS?=tendermint
|
BUILD_TAGS?=tendermint
|
||||||
BUILD_FLAGS = -ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD`"
|
BUILD_FLAGS = -ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD`"
|
||||||
|
|
||||||
all: check build test install
|
all: check build test install
|
||||||
|
|
||||||
check: check_tools get_vendor_deps
|
check: check_tools ensure_deps
|
||||||
|
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
### Build
|
### Build
|
||||||
|
|
||||||
build:
|
build:
|
||||||
go build $(BUILD_FLAGS) -tags '$(BUILD_TAGS)' -o build/tendermint ./cmd/tendermint/
|
CGO_ENABLED=0 go build $(BUILD_FLAGS) -tags '$(BUILD_TAGS)' -o build/tendermint ./cmd/tendermint/
|
||||||
|
|
||||||
build_race:
|
build_race:
|
||||||
go build -race $(BUILD_FLAGS) -tags '$(BUILD_TAGS)' -o build/tendermint ./cmd/tendermint
|
CGO_ENABLED=0 go build -race $(BUILD_FLAGS) -tags '$(BUILD_TAGS)' -o build/tendermint ./cmd/tendermint
|
||||||
|
|
||||||
install:
|
install:
|
||||||
go install $(BUILD_FLAGS) -tags '$(BUILD_TAGS)' ./cmd/tendermint
|
CGO_ENABLED=0 go install $(BUILD_FLAGS) -tags '$(BUILD_TAGS)' ./cmd/tendermint
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
### Distribution
|
### Distribution
|
||||||
@@ -40,36 +40,84 @@ check_tools:
|
|||||||
get_tools:
|
get_tools:
|
||||||
@echo "--> Installing tools"
|
@echo "--> Installing tools"
|
||||||
go get -u -v $(GOTOOLS)
|
go get -u -v $(GOTOOLS)
|
||||||
# @gometalinter.v2 --install
|
@gometalinter.v2 --install
|
||||||
|
|
||||||
update_tools:
|
update_tools:
|
||||||
@echo "--> Updating tools"
|
@echo "--> Updating tools"
|
||||||
@go get -u $(GOTOOLS)
|
@go get -u $(GOTOOLS)
|
||||||
|
|
||||||
|
#Run this from CI
|
||||||
get_vendor_deps:
|
get_vendor_deps:
|
||||||
@rm -rf vendor/
|
@rm -rf vendor/
|
||||||
@echo "--> Running glide install"
|
@echo "--> Running dep"
|
||||||
@glide install
|
@dep ensure -vendor-only
|
||||||
|
|
||||||
|
|
||||||
|
#Run this locally.
|
||||||
|
ensure_deps:
|
||||||
|
@rm -rf vendor/
|
||||||
|
@echo "--> Running dep"
|
||||||
|
@dep ensure
|
||||||
|
|
||||||
draw_deps:
|
draw_deps:
|
||||||
@# requires brew install graphviz or apt-get install graphviz
|
@# requires brew install graphviz or apt-get install graphviz
|
||||||
go get github.com/RobotsAndPencils/goviz
|
go get github.com/RobotsAndPencils/goviz
|
||||||
@goviz -i github.com/tendermint/tendermint/cmd/tendermint -d 3 | dot -Tpng -o dependency-graph.png
|
@goviz -i github.com/tendermint/tendermint/cmd/tendermint -d 3 | dot -Tpng -o dependency-graph.png
|
||||||
|
|
||||||
|
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 build/tendermint ./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"
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
test:
|
## required to be run first by most tests
|
||||||
@echo "--> Running go test"
|
build_docker_test_image:
|
||||||
@go test $(PACKAGES)
|
docker build -t tester -f ./test/docker/Dockerfile .
|
||||||
|
|
||||||
test_race:
|
### coverage, app, persistence, and libs tests
|
||||||
@echo "--> Running go test --race"
|
test_cover:
|
||||||
@go test -v -race $(PACKAGES)
|
# run the go unit tests with coverage
|
||||||
|
bash test/test_cover.sh
|
||||||
|
|
||||||
|
test_apps:
|
||||||
|
# run the app tests using bash
|
||||||
|
# requires `abci-cli` and `tendermint` binaries installed
|
||||||
|
bash test/app/test.sh
|
||||||
|
|
||||||
|
test_persistence:
|
||||||
|
# run the persistence tests using bash
|
||||||
|
# requires `abci-cli` installed
|
||||||
|
docker run --name run_persistence -t tester bash test/persist/test_failure_indices.sh
|
||||||
|
|
||||||
|
# TODO undockerize
|
||||||
|
# bash test/persist/test_failure_indices.sh
|
||||||
|
|
||||||
|
test_p2p:
|
||||||
|
docker rm -f rsyslog || true
|
||||||
|
rm -rf test/logs || true
|
||||||
|
mkdir test/logs
|
||||||
|
cd test/
|
||||||
|
docker run -d -v "logs:/var/log/" -p 127.0.0.1:5514:514/udp --name rsyslog voxxit/rsyslog
|
||||||
|
cd ..
|
||||||
|
# requires 'tester' the image from above
|
||||||
|
bash test/p2p/test.sh tester
|
||||||
|
|
||||||
|
need_abci:
|
||||||
|
bash scripts/install_abci_apps.sh
|
||||||
|
|
||||||
test_integrations:
|
test_integrations:
|
||||||
@bash ./test/test.sh
|
make build_docker_test_image
|
||||||
|
make get_tools
|
||||||
|
make get_vendor_deps
|
||||||
|
make install
|
||||||
|
make need_abci
|
||||||
|
make test_cover
|
||||||
|
make test_apps
|
||||||
|
make test_persistence
|
||||||
|
make test_p2p
|
||||||
|
|
||||||
test_release:
|
test_release:
|
||||||
@go test -tags release $(PACKAGES)
|
@go test -tags release $(PACKAGES)
|
||||||
@@ -79,10 +127,17 @@ test100:
|
|||||||
|
|
||||||
vagrant_test:
|
vagrant_test:
|
||||||
vagrant up
|
vagrant up
|
||||||
vagrant ssh -c 'make install'
|
|
||||||
vagrant ssh -c 'make test_race'
|
|
||||||
vagrant ssh -c 'make test_integrations'
|
vagrant ssh -c 'make test_integrations'
|
||||||
|
|
||||||
|
### go tests
|
||||||
|
test:
|
||||||
|
@echo "--> Running go test"
|
||||||
|
@go test $(PACKAGES)
|
||||||
|
|
||||||
|
test_race:
|
||||||
|
@echo "--> Running go test --race"
|
||||||
|
@go test -v -race $(PACKAGES)
|
||||||
|
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
### Formatting, linting, and vetting
|
### Formatting, linting, and vetting
|
||||||
@@ -92,7 +147,7 @@ fmt:
|
|||||||
|
|
||||||
metalinter:
|
metalinter:
|
||||||
@echo "--> Running linter"
|
@echo "--> Running linter"
|
||||||
gometalinter.v2 --vendor --deadline=600s --disable-all \
|
@gometalinter.v2 --vendor --deadline=600s --disable-all \
|
||||||
--enable=deadcode \
|
--enable=deadcode \
|
||||||
--enable=gosimple \
|
--enable=gosimple \
|
||||||
--enable=misspell \
|
--enable=misspell \
|
||||||
@@ -123,8 +178,25 @@ metalinter_all:
|
|||||||
@echo "--> Running linter (all)"
|
@echo "--> Running linter (all)"
|
||||||
gometalinter.v2 --vendor --deadline=600s --enable-all --disable=lll ./...
|
gometalinter.v2 --vendor --deadline=600s --enable-all --disable=lll ./...
|
||||||
|
|
||||||
|
###########################################################
|
||||||
|
### Local testnet using docker
|
||||||
|
|
||||||
|
# Build linux binary on other platforms
|
||||||
|
build-linux:
|
||||||
|
GOOS=linux GOARCH=amd64 $(MAKE) build
|
||||||
|
|
||||||
|
# Run a 4-node testnet locally
|
||||||
|
docker-start:
|
||||||
|
@echo "Wait until 'Attaching to node0, node1, node2, node3' message appears"
|
||||||
|
@if ! [ -f build/node0/config/genesis.json ]; then docker run --rm -v `pwd`/build:/tendermint:Z tendermint/localnode testnet --v 4 --o . --populate-persistent-peers --starting-ip-address 192.167.10.2 ; fi
|
||||||
|
docker-compose up
|
||||||
|
|
||||||
|
# Stop testnet
|
||||||
|
docker-stop:
|
||||||
|
docker-compose down
|
||||||
|
|
||||||
# To avoid unintended conflicts with file names, always add to .PHONY
|
# To avoid unintended conflicts with file names, always add to .PHONY
|
||||||
# unless there is a reason not to.
|
# unless there is a reason not to.
|
||||||
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
|
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
|
||||||
.PHONY: check build build_race dist install check_tools get_tools update_tools get_vendor_deps draw_deps test test_race test_integrations test_release test100 vagrant_test fmt metalinter metalinter_all
|
.PHONY: check build build_race dist install check_tools get_tools update_tools get_vendor_deps draw_deps test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt build-linux docker-start docker-stop
|
||||||
|
|
||||||
|
@@ -14,7 +14,7 @@ if [ ! -d $DATA ]; then
|
|||||||
echo "starting node"
|
echo "starting node"
|
||||||
tendermint node \
|
tendermint node \
|
||||||
--home $DATA \
|
--home $DATA \
|
||||||
--proxy_app dummy \
|
--proxy_app kvstore \
|
||||||
--p2p.laddr tcp://127.0.0.1:56656 \
|
--p2p.laddr tcp://127.0.0.1:56656 \
|
||||||
--rpc.laddr tcp://127.0.0.1:56657 \
|
--rpc.laddr tcp://127.0.0.1:56657 \
|
||||||
--log_level error &
|
--log_level error &
|
||||||
@@ -35,7 +35,7 @@ cp -R $DATA $HOME1
|
|||||||
echo "starting validator node"
|
echo "starting validator node"
|
||||||
tendermint node \
|
tendermint node \
|
||||||
--home $HOME1 \
|
--home $HOME1 \
|
||||||
--proxy_app dummy \
|
--proxy_app kvstore \
|
||||||
--p2p.laddr tcp://127.0.0.1:56656 \
|
--p2p.laddr tcp://127.0.0.1:56656 \
|
||||||
--rpc.laddr tcp://127.0.0.1:56657 \
|
--rpc.laddr tcp://127.0.0.1:56657 \
|
||||||
--log_level error &
|
--log_level error &
|
||||||
@@ -48,7 +48,7 @@ cp $HOME1/genesis.json $HOME2
|
|||||||
printf "starting downloader node"
|
printf "starting downloader node"
|
||||||
tendermint node \
|
tendermint node \
|
||||||
--home $HOME2 \
|
--home $HOME2 \
|
||||||
--proxy_app dummy \
|
--proxy_app kvstore \
|
||||||
--p2p.laddr tcp://127.0.0.1:56666 \
|
--p2p.laddr tcp://127.0.0.1:56666 \
|
||||||
--rpc.laddr tcp://127.0.0.1:56667 \
|
--rpc.laddr tcp://127.0.0.1:56667 \
|
||||||
--p2p.persistent_peers 127.0.0.1:56656 \
|
--p2p.persistent_peers 127.0.0.1:56656 \
|
||||||
|
@@ -4,8 +4,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
amino "github.com/tendermint/go-amino"
|
||||||
"github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
"github.com/tendermint/go-wire"
|
|
||||||
|
|
||||||
proto "github.com/tendermint/tendermint/benchmarks/proto"
|
proto "github.com/tendermint/tendermint/benchmarks/proto"
|
||||||
"github.com/tendermint/tendermint/p2p"
|
"github.com/tendermint/tendermint/p2p"
|
||||||
@@ -14,26 +14,35 @@ import (
|
|||||||
|
|
||||||
func BenchmarkEncodeStatusWire(b *testing.B) {
|
func BenchmarkEncodeStatusWire(b *testing.B) {
|
||||||
b.StopTimer()
|
b.StopTimer()
|
||||||
pubKey := crypto.GenPrivKeyEd25519().PubKey()
|
cdc := amino.NewCodec()
|
||||||
|
ctypes.RegisterAmino(cdc)
|
||||||
|
nodeKey := p2p.NodeKey{PrivKey: crypto.GenPrivKeyEd25519()}
|
||||||
status := &ctypes.ResultStatus{
|
status := &ctypes.ResultStatus{
|
||||||
NodeInfo: p2p.NodeInfo{
|
NodeInfo: p2p.NodeInfo{
|
||||||
PubKey: pubKey,
|
ID: nodeKey.ID(),
|
||||||
Moniker: "SOMENAME",
|
Moniker: "SOMENAME",
|
||||||
Network: "SOMENAME",
|
Network: "SOMENAME",
|
||||||
ListenAddr: "SOMEADDR",
|
ListenAddr: "SOMEADDR",
|
||||||
Version: "SOMEVER",
|
Version: "SOMEVER",
|
||||||
Other: []string{"SOMESTRING", "OTHERSTRING"},
|
Other: []string{"SOMESTRING", "OTHERSTRING"},
|
||||||
},
|
},
|
||||||
PubKey: pubKey,
|
SyncInfo: ctypes.SyncInfo{
|
||||||
LatestBlockHash: []byte("SOMEBYTES"),
|
LatestBlockHash: []byte("SOMEBYTES"),
|
||||||
LatestBlockHeight: 123,
|
LatestBlockHeight: 123,
|
||||||
LatestBlockTime: time.Unix(0, 1234),
|
LatestBlockTime: time.Unix(0, 1234),
|
||||||
|
},
|
||||||
|
ValidatorInfo: ctypes.ValidatorInfo{
|
||||||
|
PubKey: nodeKey.PubKey(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
b.StartTimer()
|
b.StartTimer()
|
||||||
|
|
||||||
counter := 0
|
counter := 0
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
jsonBytes := wire.JSONBytes(status)
|
jsonBytes, err := cdc.MarshalJSON(status)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
counter += len(jsonBytes)
|
counter += len(jsonBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,9 +50,11 @@ func BenchmarkEncodeStatusWire(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkEncodeNodeInfoWire(b *testing.B) {
|
func BenchmarkEncodeNodeInfoWire(b *testing.B) {
|
||||||
b.StopTimer()
|
b.StopTimer()
|
||||||
pubKey := crypto.GenPrivKeyEd25519().PubKey()
|
cdc := amino.NewCodec()
|
||||||
|
ctypes.RegisterAmino(cdc)
|
||||||
|
nodeKey := p2p.NodeKey{PrivKey: crypto.GenPrivKeyEd25519()}
|
||||||
nodeInfo := p2p.NodeInfo{
|
nodeInfo := p2p.NodeInfo{
|
||||||
PubKey: pubKey,
|
ID: nodeKey.ID(),
|
||||||
Moniker: "SOMENAME",
|
Moniker: "SOMENAME",
|
||||||
Network: "SOMENAME",
|
Network: "SOMENAME",
|
||||||
ListenAddr: "SOMEADDR",
|
ListenAddr: "SOMEADDR",
|
||||||
@@ -54,16 +65,21 @@ func BenchmarkEncodeNodeInfoWire(b *testing.B) {
|
|||||||
|
|
||||||
counter := 0
|
counter := 0
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
jsonBytes := wire.JSONBytes(nodeInfo)
|
jsonBytes, err := cdc.MarshalJSON(nodeInfo)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
counter += len(jsonBytes)
|
counter += len(jsonBytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkEncodeNodeInfoBinary(b *testing.B) {
|
func BenchmarkEncodeNodeInfoBinary(b *testing.B) {
|
||||||
b.StopTimer()
|
b.StopTimer()
|
||||||
pubKey := crypto.GenPrivKeyEd25519().PubKey()
|
cdc := amino.NewCodec()
|
||||||
|
ctypes.RegisterAmino(cdc)
|
||||||
|
nodeKey := p2p.NodeKey{PrivKey: crypto.GenPrivKeyEd25519()}
|
||||||
nodeInfo := p2p.NodeInfo{
|
nodeInfo := p2p.NodeInfo{
|
||||||
PubKey: pubKey,
|
ID: nodeKey.ID(),
|
||||||
Moniker: "SOMENAME",
|
Moniker: "SOMENAME",
|
||||||
Network: "SOMENAME",
|
Network: "SOMENAME",
|
||||||
ListenAddr: "SOMEADDR",
|
ListenAddr: "SOMEADDR",
|
||||||
@@ -74,7 +90,7 @@ func BenchmarkEncodeNodeInfoBinary(b *testing.B) {
|
|||||||
|
|
||||||
counter := 0
|
counter := 0
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
jsonBytes := wire.BinaryBytes(nodeInfo)
|
jsonBytes := cdc.MustMarshalBinaryBare(nodeInfo)
|
||||||
counter += len(jsonBytes)
|
counter += len(jsonBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,15 +98,20 @@ func BenchmarkEncodeNodeInfoBinary(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkEncodeNodeInfoProto(b *testing.B) {
|
func BenchmarkEncodeNodeInfoProto(b *testing.B) {
|
||||||
b.StopTimer()
|
b.StopTimer()
|
||||||
pubKey := crypto.GenPrivKeyEd25519().PubKey().Unwrap().(crypto.PubKeyEd25519)
|
nodeKey := p2p.NodeKey{PrivKey: crypto.GenPrivKeyEd25519()}
|
||||||
pubKey2 := &proto.PubKey{Ed25519: &proto.PubKeyEd25519{Bytes: pubKey[:]}}
|
nodeID := string(nodeKey.ID())
|
||||||
|
someName := "SOMENAME"
|
||||||
|
someAddr := "SOMEADDR"
|
||||||
|
someVer := "SOMEVER"
|
||||||
|
someString := "SOMESTRING"
|
||||||
|
otherString := "OTHERSTRING"
|
||||||
nodeInfo := proto.NodeInfo{
|
nodeInfo := proto.NodeInfo{
|
||||||
PubKey: pubKey2,
|
Id: &proto.ID{Id: &nodeID},
|
||||||
Moniker: "SOMENAME",
|
Moniker: &someName,
|
||||||
Network: "SOMENAME",
|
Network: &someName,
|
||||||
ListenAddr: "SOMEADDR",
|
ListenAddr: &someAddr,
|
||||||
Version: "SOMEVER",
|
Version: &someVer,
|
||||||
Other: []string{"SOMESTRING", "OTHERSTRING"},
|
Other: []string{someString, otherString},
|
||||||
}
|
}
|
||||||
b.StartTimer()
|
b.StartTimer()
|
||||||
|
|
||||||
|
26
benchmarks/experiments/Makefile
Normal file
26
benchmarks/experiments/Makefile
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
DIST_DIRS := find * -type d -exec
|
||||||
|
VERSION := $(shell perl -ne '/^var version.*"([^"]+)".*$$/ && print "v$$1\n"' main.go)
|
||||||
|
GOTOOLS = \
|
||||||
|
github.com/mitchellh/gox
|
||||||
|
|
||||||
|
tools:
|
||||||
|
go get $(GOTOOLS)
|
||||||
|
|
||||||
|
get_vendor_deps:
|
||||||
|
@hash glide 2>/dev/null || go get github.com/Masterminds/glide
|
||||||
|
glide install
|
||||||
|
|
||||||
|
build:
|
||||||
|
go build
|
||||||
|
|
||||||
|
install:
|
||||||
|
go install
|
||||||
|
|
||||||
|
test:
|
||||||
|
go test -race
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f ./experiments
|
||||||
|
rm -rf ./dist
|
||||||
|
|
||||||
|
.PHONY: tools get_vendor_deps build install test clean
|
12
benchmarks/experiments/glide.yaml
Normal file
12
benchmarks/experiments/glide.yaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package: github.com/tendermint/tendermint/benchmarks/experiments
|
||||||
|
import:
|
||||||
|
- package: github.com/tendermint/tendermint
|
||||||
|
version: v0.16.0
|
||||||
|
subpackages:
|
||||||
|
- rpc/client
|
||||||
|
- rpc/lib/types
|
||||||
|
- types
|
||||||
|
- package: github.com/tendermint/tmlibs
|
||||||
|
version: v0.7.0
|
||||||
|
subpackages:
|
||||||
|
- log
|
126
benchmarks/experiments/main.go
Normal file
126
benchmarks/experiments/main.go
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/tendermint/tendermint/rpc/client"
|
||||||
|
"github.com/tendermint/tendermint/types"
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var logger = log.NewNopLogger()
|
||||||
|
var finishedTasks = 0
|
||||||
|
var mutex = &sync.Mutex{}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
var endpoint = "tcp://0.0.0.0:46657"
|
||||||
|
|
||||||
|
var httpClient = getHTTPClient(endpoint)
|
||||||
|
|
||||||
|
var res, err = httpClient.Status()
|
||||||
|
if err != nil {
|
||||||
|
logger.Info("something wrong happens", err)
|
||||||
|
}
|
||||||
|
logger.Info("received status", res)
|
||||||
|
|
||||||
|
go monitorTask(endpoint)
|
||||||
|
|
||||||
|
txCount := 10
|
||||||
|
var clientNumber = 10
|
||||||
|
for i := 0; i < clientNumber; i++ {
|
||||||
|
go clientTask(i, txCount, endpoint)
|
||||||
|
}
|
||||||
|
for finishedTasks < clientNumber+1 {
|
||||||
|
}
|
||||||
|
fmt.Printf("Done: %d\n", finishedTasks)
|
||||||
|
}
|
||||||
|
|
||||||
|
func clientTask(id, txCount int, endpoint string) {
|
||||||
|
var httpClient = getHTTPClient(endpoint)
|
||||||
|
for i := 0; i < txCount; i++ {
|
||||||
|
var _, err = httpClient.BroadcastTxSync(generateTx(id, rand.Int()))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Something wrong happened: %s\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf("Finished client task: %d\n", id)
|
||||||
|
|
||||||
|
mutex.Lock()
|
||||||
|
finishedTasks++
|
||||||
|
mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHTTPClient(rpcAddr string) *client.HTTP {
|
||||||
|
return client.NewHTTP(rpcAddr, "/websocket")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTx(i, valI int) []byte {
|
||||||
|
// a tx encodes the validator index, the tx number, and some random junk
|
||||||
|
tx := make([]byte, 250)
|
||||||
|
binary.PutUvarint(tx[:32], uint64(valI))
|
||||||
|
binary.PutUvarint(tx[32:64], uint64(i))
|
||||||
|
if _, err := rand.Read(tx[65:]); err != nil {
|
||||||
|
fmt.Println("err reading from crypto/rand", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return tx
|
||||||
|
}
|
||||||
|
|
||||||
|
func monitorTask(endpoint string) {
|
||||||
|
fmt.Println("Monitor task started...")
|
||||||
|
var duration = 5 * time.Second
|
||||||
|
|
||||||
|
const subscriber = "monitor"
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), duration)
|
||||||
|
defer cancel()
|
||||||
|
evts := make(chan interface{})
|
||||||
|
|
||||||
|
var httpClient = getHTTPClient(endpoint)
|
||||||
|
httpClient.Start()
|
||||||
|
|
||||||
|
evtTyp := types.EventNewBlockHeader
|
||||||
|
|
||||||
|
// register for the next event of this type
|
||||||
|
query := types.QueryForEvent(evtTyp)
|
||||||
|
err := httpClient.Subscribe(ctx, subscriber, query, evts)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("error when subscribing", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure to unregister after the test is over
|
||||||
|
defer httpClient.UnsubscribeAll(ctx, subscriber)
|
||||||
|
|
||||||
|
totalNumOfCommittedTxs := int64(0)
|
||||||
|
|
||||||
|
for {
|
||||||
|
fmt.Println("Starting main loop", err)
|
||||||
|
select {
|
||||||
|
case evt := <-evts:
|
||||||
|
event := evt.(types.TMEventData)
|
||||||
|
header, ok := event.Unwrap().(types.EventDataNewBlockHeader)
|
||||||
|
if ok {
|
||||||
|
fmt.Println("received header\n", header.Header.StringIndented(""))
|
||||||
|
} else {
|
||||||
|
fmt.Println("not able to unwrap header")
|
||||||
|
}
|
||||||
|
// Do some metric computation with header
|
||||||
|
totalNumOfCommittedTxs += header.Header.NumTxs
|
||||||
|
|
||||||
|
case <-ctx.Done():
|
||||||
|
fmt.Printf("Finished monitor task. Received %d transactions \n", totalNumOfCommittedTxs)
|
||||||
|
|
||||||
|
mutex.Lock()
|
||||||
|
finishedTasks++
|
||||||
|
mutex.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ message ResultStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message NodeInfo {
|
message NodeInfo {
|
||||||
required PubKey pubKey = 1;
|
required ID id = 1;
|
||||||
required string moniker = 2;
|
required string moniker = 2;
|
||||||
required string network = 3;
|
required string network = 3;
|
||||||
required string remoteAddr = 4;
|
required string remoteAddr = 4;
|
||||||
@@ -16,6 +16,10 @@ message NodeInfo {
|
|||||||
repeated string other = 7;
|
repeated string other = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message ID {
|
||||||
|
required string id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message PubKey {
|
message PubKey {
|
||||||
optional PubKeyEd25519 ed25519 = 1;
|
optional PubKeyEd25519 ed25519 = 1;
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package blockchain
|
package blockchain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -39,9 +40,12 @@ const (
|
|||||||
// Assuming a DSL connection (not a good choice) 128 Kbps (upload) ~ 15 KB/s,
|
// Assuming a DSL connection (not a good choice) 128 Kbps (upload) ~ 15 KB/s,
|
||||||
// sending data across atlantic ~ 7.5 KB/s.
|
// sending data across atlantic ~ 7.5 KB/s.
|
||||||
minRecvRate = 7680
|
minRecvRate = 7680
|
||||||
|
|
||||||
|
// Maximum difference between current and new block's height.
|
||||||
|
maxDiffBetweenCurrentAndReceivedBlockHeight = 100
|
||||||
)
|
)
|
||||||
|
|
||||||
var peerTimeoutSeconds = time.Duration(15) // not const so we can override with tests
|
var peerTimeout = 15 * time.Second // not const so we can override with tests
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Peers self report their heights when we join the block pool.
|
Peers self report their heights when we join the block pool.
|
||||||
@@ -68,10 +72,10 @@ type BlockPool struct {
|
|||||||
maxPeerHeight int64
|
maxPeerHeight int64
|
||||||
|
|
||||||
requestsCh chan<- BlockRequest
|
requestsCh chan<- BlockRequest
|
||||||
timeoutsCh chan<- p2p.ID
|
errorsCh chan<- peerError
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBlockPool(start int64, requestsCh chan<- BlockRequest, timeoutsCh chan<- p2p.ID) *BlockPool {
|
func NewBlockPool(start int64, requestsCh chan<- BlockRequest, errorsCh chan<- peerError) *BlockPool {
|
||||||
bp := &BlockPool{
|
bp := &BlockPool{
|
||||||
peers: make(map[p2p.ID]*bpPeer),
|
peers: make(map[p2p.ID]*bpPeer),
|
||||||
|
|
||||||
@@ -80,7 +84,7 @@ func NewBlockPool(start int64, requestsCh chan<- BlockRequest, timeoutsCh chan<-
|
|||||||
numPending: 0,
|
numPending: 0,
|
||||||
|
|
||||||
requestsCh: requestsCh,
|
requestsCh: requestsCh,
|
||||||
timeoutsCh: timeoutsCh,
|
errorsCh: errorsCh,
|
||||||
}
|
}
|
||||||
bp.BaseService = *cmn.NewBaseService(nil, "BlockPool", bp)
|
bp.BaseService = *cmn.NewBaseService(nil, "BlockPool", bp)
|
||||||
return bp
|
return bp
|
||||||
@@ -128,9 +132,10 @@ func (pool *BlockPool) removeTimedoutPeers() {
|
|||||||
curRate := peer.recvMonitor.Status().CurRate
|
curRate := peer.recvMonitor.Status().CurRate
|
||||||
// curRate can be 0 on start
|
// curRate can be 0 on start
|
||||||
if curRate != 0 && curRate < minRecvRate {
|
if curRate != 0 && curRate < minRecvRate {
|
||||||
pool.sendTimeout(peer.id)
|
err := errors.New("peer is not sending us data fast enough")
|
||||||
|
pool.sendError(err, peer.id)
|
||||||
pool.Logger.Error("SendTimeout", "peer", peer.id,
|
pool.Logger.Error("SendTimeout", "peer", peer.id,
|
||||||
"reason", "peer is not sending us data fast enough",
|
"reason", err,
|
||||||
"curRate", fmt.Sprintf("%d KB/s", curRate/1024),
|
"curRate", fmt.Sprintf("%d KB/s", curRate/1024),
|
||||||
"minRate", fmt.Sprintf("%d KB/s", minRecvRate/1024))
|
"minRate", fmt.Sprintf("%d KB/s", minRecvRate/1024))
|
||||||
peer.didTimeout = true
|
peer.didTimeout = true
|
||||||
@@ -199,7 +204,7 @@ func (pool *BlockPool) PopRequest() {
|
|||||||
delete(pool.requesters, pool.height)
|
delete(pool.requesters, pool.height)
|
||||||
pool.height++
|
pool.height++
|
||||||
} else {
|
} else {
|
||||||
cmn.PanicSanity(cmn.Fmt("Expected requester to pop, got nothing at height %v", pool.height))
|
panic(fmt.Sprintf("Expected requester to pop, got nothing at height %v", pool.height))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,8 +218,9 @@ func (pool *BlockPool) RedoRequest(height int64) p2p.ID {
|
|||||||
request := pool.requesters[height]
|
request := pool.requesters[height]
|
||||||
|
|
||||||
if request.block == nil {
|
if request.block == nil {
|
||||||
cmn.PanicSanity("Expected block to be non-nil")
|
panic("Expected block to be non-nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemovePeer will redo all requesters associated with this peer.
|
// RemovePeer will redo all requesters associated with this peer.
|
||||||
pool.removePeer(request.peerID)
|
pool.removePeer(request.peerID)
|
||||||
return request.peerID
|
return request.peerID
|
||||||
@@ -227,8 +233,14 @@ func (pool *BlockPool) AddBlock(peerID p2p.ID, block *types.Block, blockSize int
|
|||||||
|
|
||||||
requester := pool.requesters[block.Height]
|
requester := pool.requesters[block.Height]
|
||||||
if requester == nil {
|
if requester == nil {
|
||||||
// a block we didn't expect.
|
pool.Logger.Info("peer sent us a block we didn't expect", "peer", peerID, "curHeight", pool.height, "blockHeight", block.Height)
|
||||||
// TODO:if height is too far ahead, punish peer
|
diff := pool.height - block.Height
|
||||||
|
if diff < 0 {
|
||||||
|
diff *= -1
|
||||||
|
}
|
||||||
|
if diff > maxDiffBetweenCurrentAndReceivedBlockHeight {
|
||||||
|
pool.sendError(errors.New("peer sent us a block we didn't expect with a height too far ahead/behind"), peerID)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,11 +351,11 @@ func (pool *BlockPool) sendRequest(height int64, peerID p2p.ID) {
|
|||||||
pool.requestsCh <- BlockRequest{height, peerID}
|
pool.requestsCh <- BlockRequest{height, peerID}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pool *BlockPool) sendTimeout(peerID p2p.ID) {
|
func (pool *BlockPool) sendError(err error, peerID p2p.ID) {
|
||||||
if !pool.IsRunning() {
|
if !pool.IsRunning() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pool.timeoutsCh <- peerID
|
pool.errorsCh <- peerError{err, peerID}
|
||||||
}
|
}
|
||||||
|
|
||||||
// unused by tendermint; left for debugging purposes
|
// unused by tendermint; left for debugging purposes
|
||||||
@@ -402,9 +414,9 @@ func (peer *bpPeer) resetMonitor() {
|
|||||||
|
|
||||||
func (peer *bpPeer) resetTimeout() {
|
func (peer *bpPeer) resetTimeout() {
|
||||||
if peer.timeout == nil {
|
if peer.timeout == nil {
|
||||||
peer.timeout = time.AfterFunc(time.Second*peerTimeoutSeconds, peer.onTimeout)
|
peer.timeout = time.AfterFunc(peerTimeout, peer.onTimeout)
|
||||||
} else {
|
} else {
|
||||||
peer.timeout.Reset(time.Second * peerTimeoutSeconds)
|
peer.timeout.Reset(peerTimeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,8 +442,9 @@ func (peer *bpPeer) onTimeout() {
|
|||||||
peer.pool.mtx.Lock()
|
peer.pool.mtx.Lock()
|
||||||
defer peer.pool.mtx.Unlock()
|
defer peer.pool.mtx.Unlock()
|
||||||
|
|
||||||
peer.pool.sendTimeout(peer.id)
|
err := errors.New("peer did not send us anything")
|
||||||
peer.logger.Error("SendTimeout", "reason", "onTimeout")
|
peer.pool.sendError(err, peer.id)
|
||||||
|
peer.logger.Error("SendTimeout", "reason", err, "timeout", peerTimeout)
|
||||||
peer.didTimeout = true
|
peer.didTimeout = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
peerTimeoutSeconds = time.Duration(2)
|
peerTimeout = 2 * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
type testPeer struct {
|
type testPeer struct {
|
||||||
@@ -34,9 +34,9 @@ func makePeers(numPeers int, minHeight, maxHeight int64) map[p2p.ID]testPeer {
|
|||||||
func TestBasic(t *testing.T) {
|
func TestBasic(t *testing.T) {
|
||||||
start := int64(42)
|
start := int64(42)
|
||||||
peers := makePeers(10, start+1, 1000)
|
peers := makePeers(10, start+1, 1000)
|
||||||
timeoutsCh := make(chan p2p.ID, 100)
|
errorsCh := make(chan peerError, 1000)
|
||||||
requestsCh := make(chan BlockRequest, 100)
|
requestsCh := make(chan BlockRequest, 1000)
|
||||||
pool := NewBlockPool(start, requestsCh, timeoutsCh)
|
pool := NewBlockPool(start, requestsCh, errorsCh)
|
||||||
pool.SetLogger(log.TestingLogger())
|
pool.SetLogger(log.TestingLogger())
|
||||||
|
|
||||||
err := pool.Start()
|
err := pool.Start()
|
||||||
@@ -71,8 +71,8 @@ func TestBasic(t *testing.T) {
|
|||||||
// Pull from channels
|
// Pull from channels
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case peerID := <-timeoutsCh:
|
case err := <-errorsCh:
|
||||||
t.Errorf("timeout: %v", peerID)
|
t.Error(err)
|
||||||
case request := <-requestsCh:
|
case request := <-requestsCh:
|
||||||
t.Logf("Pulled new BlockRequest %v", request)
|
t.Logf("Pulled new BlockRequest %v", request)
|
||||||
if request.Height == 300 {
|
if request.Height == 300 {
|
||||||
@@ -91,9 +91,9 @@ func TestBasic(t *testing.T) {
|
|||||||
func TestTimeout(t *testing.T) {
|
func TestTimeout(t *testing.T) {
|
||||||
start := int64(42)
|
start := int64(42)
|
||||||
peers := makePeers(10, start+1, 1000)
|
peers := makePeers(10, start+1, 1000)
|
||||||
timeoutsCh := make(chan p2p.ID, 100)
|
errorsCh := make(chan peerError, 1000)
|
||||||
requestsCh := make(chan BlockRequest, 100)
|
requestsCh := make(chan BlockRequest, 1000)
|
||||||
pool := NewBlockPool(start, requestsCh, timeoutsCh)
|
pool := NewBlockPool(start, requestsCh, errorsCh)
|
||||||
pool.SetLogger(log.TestingLogger())
|
pool.SetLogger(log.TestingLogger())
|
||||||
err := pool.Start()
|
err := pool.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -132,9 +132,10 @@ func TestTimeout(t *testing.T) {
|
|||||||
timedOut := map[p2p.ID]struct{}{}
|
timedOut := map[p2p.ID]struct{}{}
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case peerID := <-timeoutsCh:
|
case err := <-errorsCh:
|
||||||
t.Logf("Peer %v timeouted", peerID)
|
t.Log(err)
|
||||||
if _, ok := timedOut[peerID]; !ok {
|
// consider error to be always timeout here
|
||||||
|
if _, ok := timedOut[err.peerID]; !ok {
|
||||||
counter++
|
counter++
|
||||||
if counter == len(peers) {
|
if counter == len(peers) {
|
||||||
return // Done!
|
return // Done!
|
||||||
|
@@ -1,28 +1,22 @@
|
|||||||
package blockchain
|
package blockchain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
wire "github.com/tendermint/go-wire"
|
amino "github.com/tendermint/go-amino"
|
||||||
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
|
||||||
"github.com/tendermint/tmlibs/log"
|
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/p2p"
|
"github.com/tendermint/tendermint/p2p"
|
||||||
sm "github.com/tendermint/tendermint/state"
|
sm "github.com/tendermint/tendermint/state"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// BlockchainChannel is a channel for blocks and status updates (`BlockStore` height)
|
// BlockchainChannel is a channel for blocks and status updates (`BlockStore` height)
|
||||||
BlockchainChannel = byte(0x40)
|
BlockchainChannel = byte(0x40)
|
||||||
|
|
||||||
defaultChannelCapacity = 1000
|
|
||||||
trySyncIntervalMS = 50
|
trySyncIntervalMS = 50
|
||||||
// stop syncing when last block's time is
|
// stop syncing when last block's time is
|
||||||
// within this much of the system time.
|
// within this much of the system time.
|
||||||
@@ -32,6 +26,13 @@ const (
|
|||||||
statusUpdateIntervalSeconds = 10
|
statusUpdateIntervalSeconds = 10
|
||||||
// check if we should switch to consensus reactor
|
// check if we should switch to consensus reactor
|
||||||
switchToConsensusIntervalSeconds = 1
|
switchToConsensusIntervalSeconds = 1
|
||||||
|
|
||||||
|
// NOTE: keep up to date with bcBlockResponseMessage
|
||||||
|
bcBlockResponseMessagePrefixSize = 4
|
||||||
|
bcBlockResponseMessageFieldKeySize = 1
|
||||||
|
maxMsgSize = types.MaxBlockSizeBytes +
|
||||||
|
bcBlockResponseMessagePrefixSize +
|
||||||
|
bcBlockResponseMessageFieldKeySize
|
||||||
)
|
)
|
||||||
|
|
||||||
type consensusReactor interface {
|
type consensusReactor interface {
|
||||||
@@ -40,13 +41,19 @@ type consensusReactor interface {
|
|||||||
SwitchToConsensus(sm.State, int)
|
SwitchToConsensus(sm.State, int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type peerError struct {
|
||||||
|
err error
|
||||||
|
peerID p2p.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e peerError) Error() string {
|
||||||
|
return fmt.Sprintf("error with peer %v: %s", e.peerID, e.err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
// BlockchainReactor handles long-term catchup syncing.
|
// BlockchainReactor handles long-term catchup syncing.
|
||||||
type BlockchainReactor struct {
|
type BlockchainReactor struct {
|
||||||
p2p.BaseReactor
|
p2p.BaseReactor
|
||||||
|
|
||||||
mtx sync.Mutex
|
|
||||||
params types.ConsensusParams
|
|
||||||
|
|
||||||
// immutable
|
// immutable
|
||||||
initialState sm.State
|
initialState sm.State
|
||||||
|
|
||||||
@@ -56,7 +63,7 @@ type BlockchainReactor struct {
|
|||||||
fastSync bool
|
fastSync bool
|
||||||
|
|
||||||
requestsCh <-chan BlockRequest
|
requestsCh <-chan BlockRequest
|
||||||
timeoutsCh <-chan p2p.ID
|
errorsCh <-chan peerError
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBlockchainReactor returns new reactor instance.
|
// NewBlockchainReactor returns new reactor instance.
|
||||||
@@ -64,26 +71,28 @@ func NewBlockchainReactor(state sm.State, blockExec *sm.BlockExecutor, store *Bl
|
|||||||
fastSync bool) *BlockchainReactor {
|
fastSync bool) *BlockchainReactor {
|
||||||
|
|
||||||
if state.LastBlockHeight != store.Height() {
|
if state.LastBlockHeight != store.Height() {
|
||||||
cmn.PanicSanity(cmn.Fmt("state (%v) and store (%v) height mismatch", state.LastBlockHeight,
|
panic(fmt.Sprintf("state (%v) and store (%v) height mismatch", state.LastBlockHeight,
|
||||||
store.Height()))
|
store.Height()))
|
||||||
}
|
}
|
||||||
|
|
||||||
requestsCh := make(chan BlockRequest, defaultChannelCapacity)
|
const capacity = 1000 // must be bigger than peers count
|
||||||
timeoutsCh := make(chan p2p.ID, defaultChannelCapacity)
|
requestsCh := make(chan BlockRequest, capacity)
|
||||||
|
errorsCh := make(chan peerError, capacity) // so we don't block in #Receive#pool.AddBlock
|
||||||
|
|
||||||
pool := NewBlockPool(
|
pool := NewBlockPool(
|
||||||
store.Height()+1,
|
store.Height()+1,
|
||||||
requestsCh,
|
requestsCh,
|
||||||
timeoutsCh,
|
errorsCh,
|
||||||
)
|
)
|
||||||
|
|
||||||
bcR := &BlockchainReactor{
|
bcR := &BlockchainReactor{
|
||||||
params: state.ConsensusParams,
|
|
||||||
initialState: state,
|
initialState: state,
|
||||||
blockExec: blockExec,
|
blockExec: blockExec,
|
||||||
store: store,
|
store: store,
|
||||||
pool: pool,
|
pool: pool,
|
||||||
fastSync: fastSync,
|
fastSync: fastSync,
|
||||||
requestsCh: requestsCh,
|
requestsCh: requestsCh,
|
||||||
timeoutsCh: timeoutsCh,
|
errorsCh: errorsCh,
|
||||||
}
|
}
|
||||||
bcR.BaseReactor = *p2p.NewBaseReactor("BlockchainReactor", bcR)
|
bcR.BaseReactor = *p2p.NewBaseReactor("BlockchainReactor", bcR)
|
||||||
return bcR
|
return bcR
|
||||||
@@ -123,14 +132,16 @@ func (bcR *BlockchainReactor) GetChannels() []*p2p.ChannelDescriptor {
|
|||||||
ID: BlockchainChannel,
|
ID: BlockchainChannel,
|
||||||
Priority: 10,
|
Priority: 10,
|
||||||
SendQueueCapacity: 1000,
|
SendQueueCapacity: 1000,
|
||||||
|
RecvBufferCapacity: 50 * 4096,
|
||||||
|
RecvMessageCapacity: maxMsgSize,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddPeer implements Reactor by sending our state to peer.
|
// AddPeer implements Reactor by sending our state to peer.
|
||||||
func (bcR *BlockchainReactor) AddPeer(peer p2p.Peer) {
|
func (bcR *BlockchainReactor) AddPeer(peer p2p.Peer) {
|
||||||
if !peer.Send(BlockchainChannel,
|
msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{bcR.store.Height()})
|
||||||
struct{ BlockchainMessage }{&bcStatusResponseMessage{bcR.store.Height()}}) {
|
if !peer.Send(BlockchainChannel, msgBytes) {
|
||||||
// doing nothing, will try later in `poolRoutine`
|
// doing nothing, will try later in `poolRoutine`
|
||||||
}
|
}
|
||||||
// peer is added to the pool once we receive the first
|
// peer is added to the pool once we receive the first
|
||||||
@@ -151,22 +162,22 @@ func (bcR *BlockchainReactor) respondToPeer(msg *bcBlockRequestMessage,
|
|||||||
|
|
||||||
block := bcR.store.LoadBlock(msg.Height)
|
block := bcR.store.LoadBlock(msg.Height)
|
||||||
if block != nil {
|
if block != nil {
|
||||||
msg := &bcBlockResponseMessage{Block: block}
|
msgBytes := cdc.MustMarshalBinaryBare(&bcBlockResponseMessage{Block: block})
|
||||||
return src.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg})
|
return src.TrySend(BlockchainChannel, msgBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
bcR.Logger.Info("Peer asking for a block we don't have", "src", src, "height", msg.Height)
|
bcR.Logger.Info("Peer asking for a block we don't have", "src", src, "height", msg.Height)
|
||||||
|
|
||||||
return src.TrySend(BlockchainChannel, struct{ BlockchainMessage }{
|
msgBytes := cdc.MustMarshalBinaryBare(&bcNoBlockResponseMessage{Height: msg.Height})
|
||||||
&bcNoBlockResponseMessage{Height: msg.Height},
|
return src.TrySend(BlockchainChannel, msgBytes)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Receive implements Reactor by handling 4 types of messages (look below).
|
// Receive implements Reactor by handling 4 types of messages (look below).
|
||||||
func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
|
func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
|
||||||
_, msg, err := DecodeMessage(msgBytes, bcR.maxMsgSize())
|
msg, err := DecodeMessage(msgBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bcR.Logger.Error("Error decoding message", "err", err)
|
bcR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes)
|
||||||
|
bcR.Switch.StopPeerForError(src, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,8 +193,8 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
|||||||
bcR.pool.AddBlock(src.ID(), msg.Block, len(msgBytes))
|
bcR.pool.AddBlock(src.ID(), msg.Block, len(msgBytes))
|
||||||
case *bcStatusRequestMessage:
|
case *bcStatusRequestMessage:
|
||||||
// Send peer our state.
|
// Send peer our state.
|
||||||
queued := src.TrySend(BlockchainChannel,
|
msgBytes := cdc.MustMarshalBinaryBare(&bcStatusResponseMessage{bcR.store.Height()})
|
||||||
struct{ BlockchainMessage }{&bcStatusResponseMessage{bcR.store.Height()}})
|
queued := src.TrySend(BlockchainChannel, msgBytes)
|
||||||
if !queued {
|
if !queued {
|
||||||
// sorry
|
// sorry
|
||||||
}
|
}
|
||||||
@@ -195,21 +206,6 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// maxMsgSize returns the maximum allowable size of a
|
|
||||||
// message on the blockchain reactor.
|
|
||||||
func (bcR *BlockchainReactor) maxMsgSize() int {
|
|
||||||
bcR.mtx.Lock()
|
|
||||||
defer bcR.mtx.Unlock()
|
|
||||||
return bcR.params.BlockSize.MaxBytes + 2
|
|
||||||
}
|
|
||||||
|
|
||||||
// updateConsensusParams updates the internal consensus params
|
|
||||||
func (bcR *BlockchainReactor) updateConsensusParams(params types.ConsensusParams) {
|
|
||||||
bcR.mtx.Lock()
|
|
||||||
defer bcR.mtx.Unlock()
|
|
||||||
bcR.params = params
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle messages from the poolReactor telling the reactor what to do.
|
// Handle messages from the poolReactor telling the reactor what to do.
|
||||||
// NOTE: Don't sleep in the FOR_LOOP or otherwise slow it down!
|
// NOTE: Don't sleep in the FOR_LOOP or otherwise slow it down!
|
||||||
// (Except for the SYNC_LOOP, which is the primary purpose and must be synchronous.)
|
// (Except for the SYNC_LOOP, which is the primary purpose and must be synchronous.)
|
||||||
@@ -230,23 +226,22 @@ func (bcR *BlockchainReactor) poolRoutine() {
|
|||||||
FOR_LOOP:
|
FOR_LOOP:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case request := <-bcR.requestsCh: // chan BlockRequest
|
case request := <-bcR.requestsCh:
|
||||||
peer := bcR.Switch.Peers().Get(request.PeerID)
|
peer := bcR.Switch.Peers().Get(request.PeerID)
|
||||||
if peer == nil {
|
if peer == nil {
|
||||||
continue FOR_LOOP // Peer has since been disconnected.
|
continue FOR_LOOP // Peer has since been disconnected.
|
||||||
}
|
}
|
||||||
msg := &bcBlockRequestMessage{request.Height}
|
msgBytes := cdc.MustMarshalBinaryBare(&bcBlockRequestMessage{request.Height})
|
||||||
queued := peer.TrySend(BlockchainChannel, struct{ BlockchainMessage }{msg})
|
queued := peer.TrySend(BlockchainChannel, msgBytes)
|
||||||
if !queued {
|
if !queued {
|
||||||
// We couldn't make the request, send-queue full.
|
// We couldn't make the request, send-queue full.
|
||||||
// The pool handles timeouts, just let it go.
|
// The pool handles timeouts, just let it go.
|
||||||
continue FOR_LOOP
|
continue FOR_LOOP
|
||||||
}
|
}
|
||||||
case peerID := <-bcR.timeoutsCh: // chan string
|
case err := <-bcR.errorsCh:
|
||||||
// Peer timed out.
|
peer := bcR.Switch.Peers().Get(err.peerID)
|
||||||
peer := bcR.Switch.Peers().Get(peerID)
|
|
||||||
if peer != nil {
|
if peer != nil {
|
||||||
bcR.Switch.StopPeerForError(peer, errors.New("BlockchainReactor Timeout"))
|
bcR.Switch.StopPeerForError(peer, err)
|
||||||
}
|
}
|
||||||
case <-statusUpdateTicker.C:
|
case <-statusUpdateTicker.C:
|
||||||
// ask for status updates
|
// ask for status updates
|
||||||
@@ -310,9 +305,6 @@ FOR_LOOP:
|
|||||||
}
|
}
|
||||||
blocksSynced++
|
blocksSynced++
|
||||||
|
|
||||||
// update the consensus params
|
|
||||||
bcR.updateConsensusParams(state.ConsensusParams)
|
|
||||||
|
|
||||||
if blocksSynced%100 == 0 {
|
if blocksSynced%100 == 0 {
|
||||||
lastRate = 0.9*lastRate + 0.1*(100/time.Since(lastHundred).Seconds())
|
lastRate = 0.9*lastRate + 0.1*(100/time.Since(lastHundred).Seconds())
|
||||||
bcR.Logger.Info("Fast Sync Rate", "height", bcR.pool.height,
|
bcR.Logger.Info("Fast Sync Rate", "height", bcR.pool.height,
|
||||||
@@ -330,43 +322,36 @@ FOR_LOOP:
|
|||||||
|
|
||||||
// BroadcastStatusRequest broadcasts `BlockStore` height.
|
// BroadcastStatusRequest broadcasts `BlockStore` height.
|
||||||
func (bcR *BlockchainReactor) BroadcastStatusRequest() error {
|
func (bcR *BlockchainReactor) BroadcastStatusRequest() error {
|
||||||
bcR.Switch.Broadcast(BlockchainChannel,
|
msgBytes := cdc.MustMarshalBinaryBare(&bcStatusRequestMessage{bcR.store.Height()})
|
||||||
struct{ BlockchainMessage }{&bcStatusRequestMessage{bcR.store.Height()}})
|
bcR.Switch.Broadcast(BlockchainChannel, msgBytes)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Messages
|
// Messages
|
||||||
|
|
||||||
const (
|
|
||||||
msgTypeBlockRequest = byte(0x10)
|
|
||||||
msgTypeBlockResponse = byte(0x11)
|
|
||||||
msgTypeNoBlockResponse = byte(0x12)
|
|
||||||
msgTypeStatusResponse = byte(0x20)
|
|
||||||
msgTypeStatusRequest = byte(0x21)
|
|
||||||
)
|
|
||||||
|
|
||||||
// BlockchainMessage is a generic message for this reactor.
|
// BlockchainMessage is a generic message for this reactor.
|
||||||
type BlockchainMessage interface{}
|
type BlockchainMessage interface{}
|
||||||
|
|
||||||
var _ = wire.RegisterInterface(
|
func RegisterBlockchainMessages(cdc *amino.Codec) {
|
||||||
struct{ BlockchainMessage }{},
|
cdc.RegisterInterface((*BlockchainMessage)(nil), nil)
|
||||||
wire.ConcreteType{&bcBlockRequestMessage{}, msgTypeBlockRequest},
|
cdc.RegisterConcrete(&bcBlockRequestMessage{}, "tendermint/mempool/BlockRequest", nil)
|
||||||
wire.ConcreteType{&bcBlockResponseMessage{}, msgTypeBlockResponse},
|
cdc.RegisterConcrete(&bcBlockResponseMessage{}, "tendermint/mempool/BlockResponse", nil)
|
||||||
wire.ConcreteType{&bcNoBlockResponseMessage{}, msgTypeNoBlockResponse},
|
cdc.RegisterConcrete(&bcNoBlockResponseMessage{}, "tendermint/mempool/NoBlockResponse", nil)
|
||||||
wire.ConcreteType{&bcStatusResponseMessage{}, msgTypeStatusResponse},
|
cdc.RegisterConcrete(&bcStatusResponseMessage{}, "tendermint/mempool/StatusResponse", nil)
|
||||||
wire.ConcreteType{&bcStatusRequestMessage{}, msgTypeStatusRequest},
|
cdc.RegisterConcrete(&bcStatusRequestMessage{}, "tendermint/mempool/StatusRequest", nil)
|
||||||
)
|
}
|
||||||
|
|
||||||
// DecodeMessage decodes BlockchainMessage.
|
// DecodeMessage decodes BlockchainMessage.
|
||||||
// TODO: ensure that bz is completely read.
|
// TODO: ensure that bz is completely read.
|
||||||
func DecodeMessage(bz []byte, maxSize int) (msgType byte, msg BlockchainMessage, err error) {
|
func DecodeMessage(bz []byte) (msg BlockchainMessage, err error) {
|
||||||
msgType = bz[0]
|
if len(bz) > maxMsgSize {
|
||||||
n := int(0)
|
return msg, fmt.Errorf("Msg exceeds max size (%d > %d)",
|
||||||
r := bytes.NewReader(bz)
|
len(bz), maxMsgSize)
|
||||||
msg = wire.ReadBinary(struct{ BlockchainMessage }{}, r, maxSize, &n, &err).(struct{ BlockchainMessage }).BlockchainMessage
|
}
|
||||||
if err != nil && n != len(bz) {
|
err = cdc.UnmarshalBinaryBare(bz, &msg)
|
||||||
err = errors.New("DecodeMessage() had bytes left over")
|
if err != nil {
|
||||||
|
err = cmn.ErrorWrap(err, "DecodeMessage() had bytes left over")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -391,7 +376,6 @@ func (brm *bcNoBlockResponseMessage) String() string {
|
|||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
|
||||||
// NOTE: keep up-to-date with maxBlockchainResponseSize
|
|
||||||
type bcBlockResponseMessage struct {
|
type bcBlockResponseMessage struct {
|
||||||
Block *types.Block
|
Block *types.Block
|
||||||
}
|
}
|
||||||
|
@@ -3,8 +3,6 @@ package blockchain
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
wire "github.com/tendermint/go-wire"
|
|
||||||
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
dbm "github.com/tendermint/tmlibs/db"
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
@@ -18,8 +16,15 @@ import (
|
|||||||
|
|
||||||
func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore) {
|
func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore) {
|
||||||
config := cfg.ResetTestRoot("blockchain_reactor_test")
|
config := cfg.ResetTestRoot("blockchain_reactor_test")
|
||||||
blockStore := NewBlockStore(dbm.NewMemDB())
|
// blockDB := dbm.NewDebugDB("blockDB", dbm.NewMemDB())
|
||||||
state, _ := sm.LoadStateFromDBOrGenesisFile(dbm.NewMemDB(), config.GenesisFile())
|
// stateDB := dbm.NewDebugDB("stateDB", dbm.NewMemDB())
|
||||||
|
blockDB := dbm.NewMemDB()
|
||||||
|
stateDB := dbm.NewMemDB()
|
||||||
|
blockStore := NewBlockStore(blockDB)
|
||||||
|
state, err := sm.LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile())
|
||||||
|
if err != nil {
|
||||||
|
panic(cmn.ErrorWrap(err, "error constructing state from genesis file"))
|
||||||
|
}
|
||||||
return state, blockStore
|
return state, blockStore
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,10 +81,9 @@ func TestNoBlockResponse(t *testing.T) {
|
|||||||
// wait for our response to be received on the peer
|
// wait for our response to be received on the peer
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
reqBlockMsg := &bcBlockRequestMessage{tt.height}
|
reqBlockMsg := &bcBlockRequestMessage{tt.height}
|
||||||
reqBlockBytes := wire.BinaryBytes(struct{ BlockchainMessage }{reqBlockMsg})
|
reqBlockBytes := cdc.MustMarshalBinaryBare(reqBlockMsg)
|
||||||
bcr.Receive(chID, peer, reqBlockBytes)
|
bcr.Receive(chID, peer, reqBlockBytes)
|
||||||
value := peer.lastValue()
|
msg := peer.lastBlockchainMessage()
|
||||||
msg := value.(struct{ BlockchainMessage }).BlockchainMessage
|
|
||||||
|
|
||||||
if tt.existent {
|
if tt.existent {
|
||||||
if blockMsg, ok := msg.(*bcBlockResponseMessage); !ok {
|
if blockMsg, ok := msg.(*bcBlockResponseMessage); !ok {
|
||||||
@@ -173,22 +177,26 @@ func newbcrTestPeer(id p2p.ID) *bcrTestPeer {
|
|||||||
return bcr
|
return bcr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tp *bcrTestPeer) lastValue() interface{} { return <-tp.ch }
|
func (tp *bcrTestPeer) lastBlockchainMessage() interface{} { return <-tp.ch }
|
||||||
|
|
||||||
func (tp *bcrTestPeer) TrySend(chID byte, value interface{}) bool {
|
func (tp *bcrTestPeer) TrySend(chID byte, msgBytes []byte) bool {
|
||||||
if _, ok := value.(struct{ BlockchainMessage }).
|
var msg BlockchainMessage
|
||||||
BlockchainMessage.(*bcStatusResponseMessage); ok {
|
err := cdc.UnmarshalBinaryBare(msgBytes, &msg)
|
||||||
|
if err != nil {
|
||||||
|
panic(cmn.ErrorWrap(err, "Error while trying to parse a BlockchainMessage"))
|
||||||
|
}
|
||||||
|
if _, ok := msg.(*bcStatusResponseMessage); ok {
|
||||||
// Discard status response messages since they skew our results
|
// Discard status response messages since they skew our results
|
||||||
// We only want to deal with:
|
// We only want to deal with:
|
||||||
// + bcBlockResponseMessage
|
// + bcBlockResponseMessage
|
||||||
// + bcNoBlockResponseMessage
|
// + bcNoBlockResponseMessage
|
||||||
} else {
|
} else {
|
||||||
tp.ch <- value
|
tp.ch <- msg
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tp *bcrTestPeer) Send(chID byte, data interface{}) bool { return tp.TrySend(chID, data) }
|
func (tp *bcrTestPeer) Send(chID byte, msgBytes []byte) bool { return tp.TrySend(chID, msgBytes) }
|
||||||
func (tp *bcrTestPeer) NodeInfo() p2p.NodeInfo { return p2p.NodeInfo{} }
|
func (tp *bcrTestPeer) NodeInfo() p2p.NodeInfo { return p2p.NodeInfo{} }
|
||||||
func (tp *bcrTestPeer) Status() p2p.ConnectionStatus { return p2p.ConnectionStatus{} }
|
func (tp *bcrTestPeer) Status() p2p.ConnectionStatus { return p2p.ConnectionStatus{} }
|
||||||
func (tp *bcrTestPeer) ID() p2p.ID { return tp.id }
|
func (tp *bcrTestPeer) ID() p2p.ID { return tp.id }
|
||||||
|
@@ -1,14 +1,9 @@
|
|||||||
package blockchain
|
package blockchain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
wire "github.com/tendermint/go-wire"
|
|
||||||
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
dbm "github.com/tendermint/tmlibs/db"
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
|
|
||||||
@@ -54,38 +49,25 @@ func (bs *BlockStore) Height() int64 {
|
|||||||
return bs.height
|
return bs.height
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetReader returns the value associated with the given key wrapped in an io.Reader.
|
|
||||||
// If no value is found, it returns nil.
|
|
||||||
// It's mainly for use with wire.ReadBinary.
|
|
||||||
func (bs *BlockStore) GetReader(key []byte) io.Reader {
|
|
||||||
bytez := bs.db.Get(key)
|
|
||||||
if bytez == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return bytes.NewReader(bytez)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadBlock returns the block with the given height.
|
// LoadBlock returns the block with the given height.
|
||||||
// If no block is found for that height, it returns nil.
|
// If no block is found for that height, it returns nil.
|
||||||
func (bs *BlockStore) LoadBlock(height int64) *types.Block {
|
func (bs *BlockStore) LoadBlock(height int64) *types.Block {
|
||||||
var n int
|
var blockMeta = bs.LoadBlockMeta(height)
|
||||||
var err error
|
if blockMeta == nil {
|
||||||
r := bs.GetReader(calcBlockMetaKey(height))
|
|
||||||
if r == nil {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
blockMeta := wire.ReadBinary(&types.BlockMeta{}, r, 0, &n, &err).(*types.BlockMeta)
|
|
||||||
if err != nil {
|
var block = new(types.Block)
|
||||||
cmn.PanicCrisis(cmn.Fmt("Error reading block meta: %v", err))
|
buf := []byte{}
|
||||||
}
|
|
||||||
bytez := []byte{}
|
|
||||||
for i := 0; i < blockMeta.BlockID.PartsHeader.Total; i++ {
|
for i := 0; i < blockMeta.BlockID.PartsHeader.Total; i++ {
|
||||||
part := bs.LoadBlockPart(height, i)
|
part := bs.LoadBlockPart(height, i)
|
||||||
bytez = append(bytez, part.Bytes...)
|
buf = append(buf, part.Bytes...)
|
||||||
}
|
}
|
||||||
block := wire.ReadBinary(&types.Block{}, bytes.NewReader(bytez), 0, &n, &err).(*types.Block)
|
err := cdc.UnmarshalBinary(buf, block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmn.PanicCrisis(cmn.Fmt("Error reading block: %v", err))
|
// NOTE: The existence of meta should imply the existence of the
|
||||||
|
// block. So, make sure meta is only saved after blocks are saved.
|
||||||
|
panic(cmn.ErrorWrap(err, "Error reading block"))
|
||||||
}
|
}
|
||||||
return block
|
return block
|
||||||
}
|
}
|
||||||
@@ -94,15 +76,14 @@ func (bs *BlockStore) LoadBlock(height int64) *types.Block {
|
|||||||
// from the block at the given height.
|
// from the block at the given height.
|
||||||
// If no part is found for the given height and index, it returns nil.
|
// If no part is found for the given height and index, it returns nil.
|
||||||
func (bs *BlockStore) LoadBlockPart(height int64, index int) *types.Part {
|
func (bs *BlockStore) LoadBlockPart(height int64, index int) *types.Part {
|
||||||
var n int
|
var part = new(types.Part)
|
||||||
var err error
|
bz := bs.db.Get(calcBlockPartKey(height, index))
|
||||||
r := bs.GetReader(calcBlockPartKey(height, index))
|
if len(bz) == 0 {
|
||||||
if r == nil {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
part := wire.ReadBinary(&types.Part{}, r, 0, &n, &err).(*types.Part)
|
err := cdc.UnmarshalBinaryBare(bz, part)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmn.PanicCrisis(cmn.Fmt("Error reading block part: %v", err))
|
panic(cmn.ErrorWrap(err, "Error reading block part"))
|
||||||
}
|
}
|
||||||
return part
|
return part
|
||||||
}
|
}
|
||||||
@@ -110,15 +91,14 @@ func (bs *BlockStore) LoadBlockPart(height int64, index int) *types.Part {
|
|||||||
// LoadBlockMeta returns the BlockMeta for the given height.
|
// LoadBlockMeta returns the BlockMeta for the given height.
|
||||||
// If no block is found for the given height, it returns nil.
|
// If no block is found for the given height, it returns nil.
|
||||||
func (bs *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
|
func (bs *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
|
||||||
var n int
|
var blockMeta = new(types.BlockMeta)
|
||||||
var err error
|
bz := bs.db.Get(calcBlockMetaKey(height))
|
||||||
r := bs.GetReader(calcBlockMetaKey(height))
|
if len(bz) == 0 {
|
||||||
if r == nil {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
blockMeta := wire.ReadBinary(&types.BlockMeta{}, r, 0, &n, &err).(*types.BlockMeta)
|
err := cdc.UnmarshalBinaryBare(bz, blockMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmn.PanicCrisis(cmn.Fmt("Error reading block meta: %v", err))
|
panic(cmn.ErrorWrap(err, "Error reading block meta"))
|
||||||
}
|
}
|
||||||
return blockMeta
|
return blockMeta
|
||||||
}
|
}
|
||||||
@@ -128,15 +108,14 @@ func (bs *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
|
|||||||
// and it comes from the block.LastCommit for `height+1`.
|
// and it comes from the block.LastCommit for `height+1`.
|
||||||
// If no commit is found for the given height, it returns nil.
|
// If no commit is found for the given height, it returns nil.
|
||||||
func (bs *BlockStore) LoadBlockCommit(height int64) *types.Commit {
|
func (bs *BlockStore) LoadBlockCommit(height int64) *types.Commit {
|
||||||
var n int
|
var commit = new(types.Commit)
|
||||||
var err error
|
bz := bs.db.Get(calcBlockCommitKey(height))
|
||||||
r := bs.GetReader(calcBlockCommitKey(height))
|
if len(bz) == 0 {
|
||||||
if r == nil {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
commit := wire.ReadBinary(&types.Commit{}, r, 0, &n, &err).(*types.Commit)
|
err := cdc.UnmarshalBinaryBare(bz, commit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmn.PanicCrisis(cmn.Fmt("Error reading commit: %v", err))
|
panic(cmn.ErrorWrap(err, "Error reading block commit"))
|
||||||
}
|
}
|
||||||
return commit
|
return commit
|
||||||
}
|
}
|
||||||
@@ -145,15 +124,14 @@ func (bs *BlockStore) LoadBlockCommit(height int64) *types.Commit {
|
|||||||
// This is useful when we've seen a commit, but there has not yet been
|
// This is useful when we've seen a commit, but there has not yet been
|
||||||
// a new block at `height + 1` that includes this commit in its block.LastCommit.
|
// a new block at `height + 1` that includes this commit in its block.LastCommit.
|
||||||
func (bs *BlockStore) LoadSeenCommit(height int64) *types.Commit {
|
func (bs *BlockStore) LoadSeenCommit(height int64) *types.Commit {
|
||||||
var n int
|
var commit = new(types.Commit)
|
||||||
var err error
|
bz := bs.db.Get(calcSeenCommitKey(height))
|
||||||
r := bs.GetReader(calcSeenCommitKey(height))
|
if len(bz) == 0 {
|
||||||
if r == nil {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
commit := wire.ReadBinary(&types.Commit{}, r, 0, &n, &err).(*types.Commit)
|
err := cdc.UnmarshalBinaryBare(bz, commit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmn.PanicCrisis(cmn.Fmt("Error reading commit: %v", err))
|
panic(cmn.ErrorWrap(err, "Error reading block seen commit"))
|
||||||
}
|
}
|
||||||
return commit
|
return commit
|
||||||
}
|
}
|
||||||
@@ -178,21 +156,22 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s
|
|||||||
|
|
||||||
// Save block meta
|
// Save block meta
|
||||||
blockMeta := types.NewBlockMeta(block, blockParts)
|
blockMeta := types.NewBlockMeta(block, blockParts)
|
||||||
metaBytes := wire.BinaryBytes(blockMeta)
|
metaBytes := cdc.MustMarshalBinaryBare(blockMeta)
|
||||||
bs.db.Set(calcBlockMetaKey(height), metaBytes)
|
bs.db.Set(calcBlockMetaKey(height), metaBytes)
|
||||||
|
|
||||||
// Save block parts
|
// Save block parts
|
||||||
for i := 0; i < blockParts.Total(); i++ {
|
for i := 0; i < blockParts.Total(); i++ {
|
||||||
bs.saveBlockPart(height, i, blockParts.GetPart(i))
|
part := blockParts.GetPart(i)
|
||||||
|
bs.saveBlockPart(height, i, part)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save block commit (duplicate and separate from the Block)
|
// Save block commit (duplicate and separate from the Block)
|
||||||
blockCommitBytes := wire.BinaryBytes(block.LastCommit)
|
blockCommitBytes := cdc.MustMarshalBinaryBare(block.LastCommit)
|
||||||
bs.db.Set(calcBlockCommitKey(height-1), blockCommitBytes)
|
bs.db.Set(calcBlockCommitKey(height-1), blockCommitBytes)
|
||||||
|
|
||||||
// Save seen commit (seen +2/3 precommits for block)
|
// Save seen commit (seen +2/3 precommits for block)
|
||||||
// NOTE: we can delete this at a later height
|
// NOTE: we can delete this at a later height
|
||||||
seenCommitBytes := wire.BinaryBytes(seenCommit)
|
seenCommitBytes := cdc.MustMarshalBinaryBare(seenCommit)
|
||||||
bs.db.Set(calcSeenCommitKey(height), seenCommitBytes)
|
bs.db.Set(calcSeenCommitKey(height), seenCommitBytes)
|
||||||
|
|
||||||
// Save new BlockStoreStateJSON descriptor
|
// Save new BlockStoreStateJSON descriptor
|
||||||
@@ -211,7 +190,7 @@ func (bs *BlockStore) saveBlockPart(height int64, index int, part *types.Part) {
|
|||||||
if height != bs.Height()+1 {
|
if height != bs.Height()+1 {
|
||||||
cmn.PanicSanity(cmn.Fmt("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.Height()+1, height))
|
cmn.PanicSanity(cmn.Fmt("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.Height()+1, height))
|
||||||
}
|
}
|
||||||
partBytes := wire.BinaryBytes(part)
|
partBytes := cdc.MustMarshalBinaryBare(part)
|
||||||
bs.db.Set(calcBlockPartKey(height, index), partBytes)
|
bs.db.Set(calcBlockPartKey(height, index), partBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,12 +217,12 @@ func calcSeenCommitKey(height int64) []byte {
|
|||||||
var blockStoreKey = []byte("blockStore")
|
var blockStoreKey = []byte("blockStore")
|
||||||
|
|
||||||
type BlockStoreStateJSON struct {
|
type BlockStoreStateJSON struct {
|
||||||
Height int64
|
Height int64 `json:"height"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save persists the blockStore state to the database as JSON.
|
// Save persists the blockStore state to the database as JSON.
|
||||||
func (bsj BlockStoreStateJSON) Save(db dbm.DB) {
|
func (bsj BlockStoreStateJSON) Save(db dbm.DB) {
|
||||||
bytes, err := json.Marshal(bsj)
|
bytes, err := cdc.MarshalJSON(bsj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmn.PanicSanity(cmn.Fmt("Could not marshal state bytes: %v", err))
|
cmn.PanicSanity(cmn.Fmt("Could not marshal state bytes: %v", err))
|
||||||
}
|
}
|
||||||
@@ -260,9 +239,9 @@ func LoadBlockStoreStateJSON(db dbm.DB) BlockStoreStateJSON {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
bsj := BlockStoreStateJSON{}
|
bsj := BlockStoreStateJSON{}
|
||||||
err := json.Unmarshal(bytes, &bsj)
|
err := cdc.UnmarshalJSON(bytes, &bsj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmn.PanicCrisis(cmn.Fmt("Could not unmarshal bytes: %X", bytes))
|
panic(fmt.Sprintf("Could not unmarshal bytes: %X", bytes))
|
||||||
}
|
}
|
||||||
return bsj
|
return bsj
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,6 @@ package blockchain
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -11,9 +10,6 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
wire "github.com/tendermint/go-wire"
|
|
||||||
|
|
||||||
"github.com/tendermint/tmlibs/db"
|
"github.com/tendermint/tmlibs/db"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
@@ -35,7 +31,7 @@ func TestNewBlockStore(t *testing.T) {
|
|||||||
db := db.NewMemDB()
|
db := db.NewMemDB()
|
||||||
db.Set(blockStoreKey, []byte(`{"height": 10000}`))
|
db.Set(blockStoreKey, []byte(`{"height": 10000}`))
|
||||||
bs := NewBlockStore(db)
|
bs := NewBlockStore(db)
|
||||||
assert.Equal(t, bs.Height(), int64(10000), "failed to properly parse blockstore")
|
require.Equal(t, int64(10000), bs.Height(), "failed to properly parse blockstore")
|
||||||
|
|
||||||
panicCausers := []struct {
|
panicCausers := []struct {
|
||||||
data []byte
|
data []byte
|
||||||
@@ -61,38 +57,6 @@ func TestNewBlockStore(t *testing.T) {
|
|||||||
assert.Equal(t, bs.Height(), int64(0), "expecting nil bytes to be unmarshaled alright")
|
assert.Equal(t, bs.Height(), int64(0), "expecting nil bytes to be unmarshaled alright")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBlockStoreGetReader(t *testing.T) {
|
|
||||||
db := db.NewMemDB()
|
|
||||||
// Initial setup
|
|
||||||
db.Set([]byte("Foo"), []byte("Bar"))
|
|
||||||
db.Set([]byte("Foo1"), nil)
|
|
||||||
|
|
||||||
bs := NewBlockStore(db)
|
|
||||||
|
|
||||||
tests := [...]struct {
|
|
||||||
key []byte
|
|
||||||
want []byte
|
|
||||||
}{
|
|
||||||
0: {key: []byte("Foo"), want: []byte("Bar")},
|
|
||||||
1: {key: []byte("KnoxNonExistent"), want: nil},
|
|
||||||
2: {key: []byte("Foo1"), want: []byte{}},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tt := range tests {
|
|
||||||
r := bs.GetReader(tt.key)
|
|
||||||
if r == nil {
|
|
||||||
assert.Nil(t, tt.want, "#%d: expected a non-nil reader", i)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
slurp, err := ioutil.ReadAll(r)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("#%d: unexpected Read err: %v", i, err)
|
|
||||||
} else {
|
|
||||||
assert.Equal(t, slurp, tt.want, "#%d: mismatch", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func freshBlockStore() (*BlockStore, db.DB) {
|
func freshBlockStore() (*BlockStore, db.DB) {
|
||||||
db := db.NewMemDB()
|
db := db.NewMemDB()
|
||||||
return NewBlockStore(db), db
|
return NewBlockStore(db), db
|
||||||
@@ -189,14 +153,14 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
|||||||
parts: validPartSet,
|
parts: validPartSet,
|
||||||
seenCommit: seenCommit1,
|
seenCommit: seenCommit1,
|
||||||
corruptCommitInDB: true, // Corrupt the DB's commit entry
|
corruptCommitInDB: true, // Corrupt the DB's commit entry
|
||||||
wantPanic: "rror reading commit",
|
wantPanic: "Error reading block commit",
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
block: newBlock(&header1, commitAtH10),
|
block: newBlock(&header1, commitAtH10),
|
||||||
parts: validPartSet,
|
parts: validPartSet,
|
||||||
seenCommit: seenCommit1,
|
seenCommit: seenCommit1,
|
||||||
wantPanic: "rror reading block",
|
wantPanic: "Error reading block",
|
||||||
corruptBlockInDB: true, // Corrupt the DB's block entry
|
corruptBlockInDB: true, // Corrupt the DB's block entry
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -215,7 +179,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
|||||||
seenCommit: seenCommit1,
|
seenCommit: seenCommit1,
|
||||||
|
|
||||||
corruptSeenCommitInDB: true,
|
corruptSeenCommitInDB: true,
|
||||||
wantPanic: "rror reading commit",
|
wantPanic: "Error reading block seen commit",
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -305,14 +269,6 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func binarySerializeIt(v interface{}) []byte {
|
|
||||||
var n int
|
|
||||||
var err error
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
wire.WriteBinary(v, buf, &n, &err)
|
|
||||||
return buf.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLoadBlockPart(t *testing.T) {
|
func TestLoadBlockPart(t *testing.T) {
|
||||||
bs, db := freshBlockStore()
|
bs, db := freshBlockStore()
|
||||||
height, index := int64(10), 1
|
height, index := int64(10), 1
|
||||||
@@ -334,7 +290,7 @@ func TestLoadBlockPart(t *testing.T) {
|
|||||||
require.Contains(t, panicErr.Error(), "Error reading block part")
|
require.Contains(t, panicErr.Error(), "Error reading block part")
|
||||||
|
|
||||||
// 3. A good block serialized and saved to the DB should be retrievable
|
// 3. A good block serialized and saved to the DB should be retrievable
|
||||||
db.Set(calcBlockPartKey(height, index), binarySerializeIt(part1))
|
db.Set(calcBlockPartKey(height, index), cdc.MustMarshalBinaryBare(part1))
|
||||||
gotPart, _, panicErr := doFn(loadPart)
|
gotPart, _, panicErr := doFn(loadPart)
|
||||||
require.Nil(t, panicErr, "an existent and proper block should not panic")
|
require.Nil(t, panicErr, "an existent and proper block should not panic")
|
||||||
require.Nil(t, res, "a properly saved block should return a proper block")
|
require.Nil(t, res, "a properly saved block should return a proper block")
|
||||||
@@ -364,11 +320,11 @@ func TestLoadBlockMeta(t *testing.T) {
|
|||||||
|
|
||||||
// 3. A good blockMeta serialized and saved to the DB should be retrievable
|
// 3. A good blockMeta serialized and saved to the DB should be retrievable
|
||||||
meta := &types.BlockMeta{}
|
meta := &types.BlockMeta{}
|
||||||
db.Set(calcBlockMetaKey(height), binarySerializeIt(meta))
|
db.Set(calcBlockMetaKey(height), cdc.MustMarshalBinaryBare(meta))
|
||||||
gotMeta, _, panicErr := doFn(loadMeta)
|
gotMeta, _, panicErr := doFn(loadMeta)
|
||||||
require.Nil(t, panicErr, "an existent and proper block should not panic")
|
require.Nil(t, panicErr, "an existent and proper block should not panic")
|
||||||
require.Nil(t, res, "a properly saved blockMeta should return a proper blocMeta ")
|
require.Nil(t, res, "a properly saved blockMeta should return a proper blocMeta ")
|
||||||
require.Equal(t, binarySerializeIt(meta), binarySerializeIt(gotMeta),
|
require.Equal(t, cdc.MustMarshalBinaryBare(meta), cdc.MustMarshalBinaryBare(gotMeta),
|
||||||
"expecting successful retrieval of previously saved blockMeta")
|
"expecting successful retrieval of previously saved blockMeta")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,6 +341,9 @@ func TestBlockFetchAtHeight(t *testing.T) {
|
|||||||
require.Equal(t, bs.Height(), block.Header.Height, "expecting the new height to be changed")
|
require.Equal(t, bs.Height(), block.Header.Height, "expecting the new height to be changed")
|
||||||
|
|
||||||
blockAtHeight := bs.LoadBlock(bs.Height())
|
blockAtHeight := bs.LoadBlock(bs.Height())
|
||||||
|
bz1 := cdc.MustMarshalBinaryBare(block)
|
||||||
|
bz2 := cdc.MustMarshalBinaryBare(blockAtHeight)
|
||||||
|
require.Equal(t, bz1, bz2)
|
||||||
require.Equal(t, block.Hash(), blockAtHeight.Hash(),
|
require.Equal(t, block.Hash(), blockAtHeight.Hash(),
|
||||||
"expecting a successful load of the last saved block")
|
"expecting a successful load of the last saved block")
|
||||||
|
|
||||||
|
13
blockchain/wire.go
Normal file
13
blockchain/wire.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package blockchain
|
||||||
|
|
||||||
|
import (
|
||||||
|
amino "github.com/tendermint/go-amino"
|
||||||
|
"github.com/tendermint/go-crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cdc = amino.NewCodec()
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RegisterBlockchainMessages(cdc)
|
||||||
|
crypto.RegisterAmino(cdc)
|
||||||
|
}
|
35
circle.yml
35
circle.yml
@@ -1,35 +0,0 @@
|
|||||||
---
|
|
||||||
machine:
|
|
||||||
environment:
|
|
||||||
MACH_PREFIX: tendermint-test-mach
|
|
||||||
DOCKER_VERSION: 1.10.0
|
|
||||||
DOCKER_MACHINE_VERSION: 0.9.0
|
|
||||||
GOPATH: "$HOME/.go_project"
|
|
||||||
PROJECT_PARENT_PATH: "$GOPATH/src/github.com/$CIRCLE_PROJECT_USERNAME"
|
|
||||||
PROJECT_PATH: "$PROJECT_PARENT_PATH/$CIRCLE_PROJECT_REPONAME"
|
|
||||||
PATH: "$HOME/.go_project/bin:${PATH}"
|
|
||||||
hosts:
|
|
||||||
localhost: 127.0.0.1
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
override:
|
|
||||||
- curl -sSL https://s3.amazonaws.com/circle-downloads/install-circleci-docker.sh | sudo bash -s -- $DOCKER_VERSION
|
|
||||||
- sudo start docker
|
|
||||||
- sudo curl -sSL -o /usr/bin/docker-machine "https://github.com/docker/machine/releases/download/v$DOCKER_MACHINE_VERSION/docker-machine-`uname -s`-`uname -m`"; sudo chmod 0755 /usr/bin/docker-machine
|
|
||||||
- mkdir -p "$PROJECT_PARENT_PATH"
|
|
||||||
- ln -sf "$HOME/$CIRCLE_PROJECT_REPONAME/" "$PROJECT_PATH"
|
|
||||||
post:
|
|
||||||
- go version
|
|
||||||
- docker version
|
|
||||||
- docker-machine version
|
|
||||||
|
|
||||||
test:
|
|
||||||
override:
|
|
||||||
- cd "$PROJECT_PATH" && set -o pipefail && make test_integrations 2>&1 | tee test_integrations.log:
|
|
||||||
timeout: 1800
|
|
||||||
post:
|
|
||||||
- cd "$PROJECT_PATH" && mv test_integrations.log "${CIRCLE_ARTIFACTS}"
|
|
||||||
- cd "$PROJECT_PATH" && bash <(curl -s https://codecov.io/bash) -f coverage.txt
|
|
||||||
- cd "$PROJECT_PATH" && mv coverage.txt "${CIRCLE_ARTIFACTS}"
|
|
||||||
- cd "$PROJECT_PATH" && cp test/logs/messages "${CIRCLE_ARTIFACTS}/docker.log"
|
|
||||||
- cd "${CIRCLE_ARTIFACTS}" && tar czf logs.tar.gz *.log
|
|
53
cmd/priv_val_server/main.go
Normal file
53
cmd/priv_val_server/main.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
crypto "github.com/tendermint/go-crypto"
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
|
priv_val "github.com/tendermint/tendermint/types/priv_validator"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
addr = flag.String("addr", ":46659", "Address of client to connect to")
|
||||||
|
chainID = flag.String("chain-id", "mychain", "chain id")
|
||||||
|
privValPath = flag.String("priv", "", "priv val file path")
|
||||||
|
|
||||||
|
logger = log.NewTMLogger(
|
||||||
|
log.NewSyncWriter(os.Stdout),
|
||||||
|
).With("module", "priv_val")
|
||||||
|
)
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
logger.Info(
|
||||||
|
"Starting private validator",
|
||||||
|
"addr", *addr,
|
||||||
|
"chainID", *chainID,
|
||||||
|
"privPath", *privValPath,
|
||||||
|
)
|
||||||
|
|
||||||
|
privVal := priv_val.LoadFilePV(*privValPath)
|
||||||
|
|
||||||
|
rs := priv_val.NewRemoteSigner(
|
||||||
|
logger,
|
||||||
|
*chainID,
|
||||||
|
*addr,
|
||||||
|
privVal,
|
||||||
|
crypto.GenPrivKeyEd25519(),
|
||||||
|
)
|
||||||
|
err := rs.Start()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmn.TrapSignal(func() {
|
||||||
|
err := rs.Stop()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
32
cmd/tendermint/commands/gen_node_key.go
Normal file
32
cmd/tendermint/commands/gen_node_key.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/tendermint/tendermint/p2p"
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenNodeKeyCmd allows the generation of a node key. It prints node's ID to
|
||||||
|
// the standard output.
|
||||||
|
var GenNodeKeyCmd = &cobra.Command{
|
||||||
|
Use: "gen_node_key",
|
||||||
|
Short: "Generate a node key for this node and print its ID",
|
||||||
|
RunE: genNodeKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
func genNodeKey(cmd *cobra.Command, args []string) error {
|
||||||
|
nodeKeyFile := config.NodeKeyFile()
|
||||||
|
if cmn.FileExists(nodeKeyFile) {
|
||||||
|
return fmt.Errorf("node key at %s already exists", nodeKeyFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeKey, err := p2p.LoadOrGenNodeKey(nodeKeyFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(nodeKey.ID())
|
||||||
|
return nil
|
||||||
|
}
|
@@ -1,12 +1,11 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/types"
|
pvm "github.com/tendermint/tendermint/types/priv_validator"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GenValidatorCmd allows the generation of a keypair for a
|
// GenValidatorCmd allows the generation of a keypair for a
|
||||||
@@ -18,11 +17,11 @@ var GenValidatorCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func genValidator(cmd *cobra.Command, args []string) {
|
func genValidator(cmd *cobra.Command, args []string) {
|
||||||
privValidator := types.GenPrivValidatorFS("")
|
pv := pvm.GenFilePV("")
|
||||||
privValidatorJSONBytes, err := json.MarshalIndent(privValidator, "", "\t")
|
jsbz, err := cdc.MarshalJSON(pv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
fmt.Printf(`%v
|
fmt.Printf(`%v
|
||||||
`, string(privValidatorJSONBytes))
|
`, string(jsbz))
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,10 @@ package commands
|
|||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
cfg "github.com/tendermint/tendermint/config"
|
||||||
|
"github.com/tendermint/tendermint/p2p"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
|
pvm "github.com/tendermint/tendermint/types/priv_validator"
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -11,20 +14,34 @@ import (
|
|||||||
var InitFilesCmd = &cobra.Command{
|
var InitFilesCmd = &cobra.Command{
|
||||||
Use: "init",
|
Use: "init",
|
||||||
Short: "Initialize Tendermint",
|
Short: "Initialize Tendermint",
|
||||||
Run: initFiles,
|
RunE: initFiles,
|
||||||
}
|
}
|
||||||
|
|
||||||
func initFiles(cmd *cobra.Command, args []string) {
|
func initFiles(cmd *cobra.Command, args []string) error {
|
||||||
|
return initFilesWithConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initFilesWithConfig(config *cfg.Config) error {
|
||||||
// private validator
|
// private validator
|
||||||
privValFile := config.PrivValidatorFile()
|
privValFile := config.PrivValidatorFile()
|
||||||
var privValidator *types.PrivValidatorFS
|
var pv *pvm.FilePV
|
||||||
if cmn.FileExists(privValFile) {
|
if cmn.FileExists(privValFile) {
|
||||||
privValidator = types.LoadPrivValidatorFS(privValFile)
|
pv = pvm.LoadFilePV(privValFile)
|
||||||
logger.Info("Found private validator", "path", privValFile)
|
logger.Info("Found private validator", "path", privValFile)
|
||||||
} else {
|
} else {
|
||||||
privValidator = types.GenPrivValidatorFS(privValFile)
|
pv = pvm.GenFilePV(privValFile)
|
||||||
privValidator.Save()
|
pv.Save()
|
||||||
logger.Info("Genetated private validator", "path", privValFile)
|
logger.Info("Generated private validator", "path", privValFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeKeyFile := config.NodeKeyFile()
|
||||||
|
if cmn.FileExists(nodeKeyFile) {
|
||||||
|
logger.Info("Found node key", "path", nodeKeyFile)
|
||||||
|
} else {
|
||||||
|
if _, err := p2p.LoadOrGenNodeKey(nodeKeyFile); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.Info("Generated node key", "path", nodeKeyFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// genesis file
|
// genesis file
|
||||||
@@ -36,13 +53,15 @@ func initFiles(cmd *cobra.Command, args []string) {
|
|||||||
ChainID: cmn.Fmt("test-chain-%v", cmn.RandStr(6)),
|
ChainID: cmn.Fmt("test-chain-%v", cmn.RandStr(6)),
|
||||||
}
|
}
|
||||||
genDoc.Validators = []types.GenesisValidator{{
|
genDoc.Validators = []types.GenesisValidator{{
|
||||||
PubKey: privValidator.GetPubKey(),
|
PubKey: pv.GetPubKey(),
|
||||||
Power: 10,
|
Power: 10,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
if err := genDoc.SaveAs(genFile); err != nil {
|
if err := genDoc.SaveAs(genFile); err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
logger.Info("Genetated genesis file", "path", genFile)
|
logger.Info("Generated genesis file", "path", genFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
@@ -31,13 +34,37 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
LiteCmd.Flags().StringVar(&listenAddr, "laddr", ":8888", "Serve the proxy on the given port")
|
LiteCmd.Flags().StringVar(&listenAddr, "laddr", "tcp://localhost:8888", "Serve the proxy on the given address")
|
||||||
LiteCmd.Flags().StringVar(&nodeAddr, "node", "localhost:46657", "Connect to a Tendermint node at this address")
|
LiteCmd.Flags().StringVar(&nodeAddr, "node", "tcp://localhost:46657", "Connect to a Tendermint node at this address")
|
||||||
LiteCmd.Flags().StringVar(&chainID, "chain-id", "tendermint", "Specify the Tendermint chain ID")
|
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().StringVar(&home, "home-dir", ".tendermint-lite", "Specify the home directory")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ensureAddrHasSchemeOrDefaultToTCP(addr string) (string, error) {
|
||||||
|
u, err := url.Parse(addr)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
switch u.Scheme {
|
||||||
|
case "tcp", "unix":
|
||||||
|
case "":
|
||||||
|
u.Scheme = "tcp"
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unknown scheme %q, use either tcp or unix", u.Scheme)
|
||||||
|
}
|
||||||
|
return u.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
func runProxy(cmd *cobra.Command, args []string) error {
|
func runProxy(cmd *cobra.Command, args []string) error {
|
||||||
|
nodeAddr, err := ensureAddrHasSchemeOrDefaultToTCP(nodeAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
listenAddr, err := ensureAddrHasSchemeOrDefaultToTCP(listenAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// First, connect a client
|
// First, connect a client
|
||||||
node := rpcclient.NewHTTP(nodeAddr, "/websocket")
|
node := rpcclient.NewHTTP(nodeAddr, "/websocket")
|
||||||
|
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@@ -22,7 +21,7 @@ func probeUpnp(cmd *cobra.Command, args []string) error {
|
|||||||
fmt.Println("Probe failed: ", err)
|
fmt.Println("Probe failed: ", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("Probe success!")
|
fmt.Println("Probe success!")
|
||||||
jsonBytes, err := json.Marshal(capabilities)
|
jsonBytes, err := cdc.MarshalJSON(capabilities)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/types"
|
pvm "github.com/tendermint/tendermint/types/priv_validator"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ var ResetPrivValidatorCmd = &cobra.Command{
|
|||||||
// ResetAll removes the privValidator files.
|
// ResetAll removes the privValidator files.
|
||||||
// Exported so other CLI tools can use it.
|
// Exported so other CLI tools can use it.
|
||||||
func ResetAll(dbDir, privValFile string, logger log.Logger) {
|
func ResetAll(dbDir, privValFile string, logger log.Logger) {
|
||||||
resetPrivValidatorFS(privValFile, logger)
|
resetFilePV(privValFile, logger)
|
||||||
if err := os.RemoveAll(dbDir); err != nil {
|
if err := os.RemoveAll(dbDir); err != nil {
|
||||||
logger.Error("Error removing directory", "err", err)
|
logger.Error("Error removing directory", "err", err)
|
||||||
return
|
return
|
||||||
@@ -44,18 +44,18 @@ func resetAll(cmd *cobra.Command, args []string) {
|
|||||||
// XXX: this is totally unsafe.
|
// XXX: this is totally unsafe.
|
||||||
// it's only suitable for testnets.
|
// it's only suitable for testnets.
|
||||||
func resetPrivValidator(cmd *cobra.Command, args []string) {
|
func resetPrivValidator(cmd *cobra.Command, args []string) {
|
||||||
resetPrivValidatorFS(config.PrivValidatorFile(), logger)
|
resetFilePV(config.PrivValidatorFile(), logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resetPrivValidatorFS(privValFile string, logger log.Logger) {
|
func resetFilePV(privValFile string, logger log.Logger) {
|
||||||
// Get PrivValidator
|
// Get PrivValidator
|
||||||
if _, err := os.Stat(privValFile); err == nil {
|
if _, err := os.Stat(privValFile); err == nil {
|
||||||
privValidator := types.LoadPrivValidatorFS(privValFile)
|
pv := pvm.LoadFilePV(privValFile)
|
||||||
privValidator.Reset()
|
pv.Reset()
|
||||||
logger.Info("Reset PrivValidator", "file", privValFile)
|
logger.Info("Reset PrivValidator", "file", privValFile)
|
||||||
} else {
|
} else {
|
||||||
privValidator := types.GenPrivValidatorFS(privValFile)
|
pv := pvm.GenFilePV(privValFile)
|
||||||
privValidator.Save()
|
pv.Save()
|
||||||
logger.Info("Generated PrivValidator", "file", privValFile)
|
logger.Info("Generated PrivValidator", "file", privValFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,11 +14,14 @@ func AddNodeFlags(cmd *cobra.Command) {
|
|||||||
// bind flags
|
// bind flags
|
||||||
cmd.Flags().String("moniker", config.Moniker, "Node Name")
|
cmd.Flags().String("moniker", config.Moniker, "Node Name")
|
||||||
|
|
||||||
|
// priv val flags
|
||||||
|
cmd.Flags().String("priv_validator_laddr", config.PrivValidatorListenAddr, "Socket address to listen on for connections from external priv_validator process")
|
||||||
|
|
||||||
// node flags
|
// node flags
|
||||||
cmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing")
|
cmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing")
|
||||||
|
|
||||||
// abci flags
|
// abci flags
|
||||||
cmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or 'nilapp' or 'dummy' for local testing.")
|
cmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or 'nilapp' or 'kvstore' for local testing.")
|
||||||
cmd.Flags().String("abci", config.ABCI, "Specify abci transport (socket | grpc)")
|
cmd.Flags().String("abci", config.ABCI, "Specify abci transport (socket | grpc)")
|
||||||
|
|
||||||
// rpc flags
|
// rpc flags
|
||||||
@@ -28,18 +31,19 @@ func AddNodeFlags(cmd *cobra.Command) {
|
|||||||
|
|
||||||
// p2p flags
|
// p2p flags
|
||||||
cmd.Flags().String("p2p.laddr", config.P2P.ListenAddress, "Node listen address. (0.0.0.0:0 means any interface, any port)")
|
cmd.Flags().String("p2p.laddr", config.P2P.ListenAddress, "Node listen address. (0.0.0.0:0 means any interface, any port)")
|
||||||
cmd.Flags().String("p2p.seeds", config.P2P.Seeds, "Comma delimited host:port seed nodes")
|
cmd.Flags().String("p2p.seeds", config.P2P.Seeds, "Comma-delimited ID@host:port seed nodes")
|
||||||
cmd.Flags().String("p2p.persistent_peers", config.P2P.PersistentPeers, "Comma delimited host:port persistent peers")
|
cmd.Flags().String("p2p.persistent_peers", config.P2P.PersistentPeers, "Comma-delimited ID@host:port persistent peers")
|
||||||
cmd.Flags().Bool("p2p.skip_upnp", config.P2P.SkipUPNP, "Skip UPNP configuration")
|
cmd.Flags().Bool("p2p.skip_upnp", config.P2P.SkipUPNP, "Skip UPNP configuration")
|
||||||
cmd.Flags().Bool("p2p.pex", config.P2P.PexReactor, "Enable/disable Peer-Exchange")
|
cmd.Flags().Bool("p2p.pex", config.P2P.PexReactor, "Enable/disable Peer-Exchange")
|
||||||
cmd.Flags().Bool("p2p.seed_mode", config.P2P.SeedMode, "Enable/disable seed mode")
|
cmd.Flags().Bool("p2p.seed_mode", config.P2P.SeedMode, "Enable/disable seed mode")
|
||||||
|
cmd.Flags().String("p2p.private_peer_ids", config.P2P.PrivatePeerIDs, "Comma-delimited private peer IDs")
|
||||||
|
|
||||||
// consensus flags
|
// consensus flags
|
||||||
cmd.Flags().Bool("consensus.create_empty_blocks", config.Consensus.CreateEmptyBlocks, "Set this to false to only produce blocks when there are txs or when the AppHash changes")
|
cmd.Flags().Bool("consensus.create_empty_blocks", config.Consensus.CreateEmptyBlocks, "Set this to false to only produce blocks when there are txs or when the AppHash changes")
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRunNodeCmd returns the command that allows the CLI to start a
|
// NewRunNodeCmd returns the command that allows the CLI to start a node.
|
||||||
// node. It can be used with a custom PrivValidator and in-process ABCI application.
|
// It can be used with a custom PrivValidator and in-process ABCI application.
|
||||||
func NewRunNodeCmd(nodeProvider nm.NodeProvider) *cobra.Command {
|
func NewRunNodeCmd(nodeProvider nm.NodeProvider) *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "node",
|
Use: "node",
|
||||||
@@ -53,9 +57,8 @@ func NewRunNodeCmd(nodeProvider nm.NodeProvider) *cobra.Command {
|
|||||||
|
|
||||||
if err := n.Start(); err != nil {
|
if err := n.Start(); err != nil {
|
||||||
return fmt.Errorf("Failed to start node: %v", err)
|
return fmt.Errorf("Failed to start node: %v", err)
|
||||||
} else {
|
|
||||||
logger.Info("Started node", "nodeInfo", n.Switch().NodeInfo())
|
|
||||||
}
|
}
|
||||||
|
logger.Info("Started node", "nodeInfo", n.Switch().NodeInfo())
|
||||||
|
|
||||||
// Trap signal, run forever.
|
// Trap signal, run forever.
|
||||||
n.RunForever()
|
n.RunForever()
|
||||||
|
27
cmd/tendermint/commands/show_node_id.go
Normal file
27
cmd/tendermint/commands/show_node_id.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/tendermint/tendermint/p2p"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ShowNodeIDCmd dumps node's ID to the standard output.
|
||||||
|
var ShowNodeIDCmd = &cobra.Command{
|
||||||
|
Use: "show_node_id",
|
||||||
|
Short: "Show this node's ID",
|
||||||
|
RunE: showNodeID,
|
||||||
|
}
|
||||||
|
|
||||||
|
func showNodeID(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
|
nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(nodeKey.ID())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@@ -5,8 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/tendermint/go-wire/data"
|
privval "github.com/tendermint/tendermint/types/priv_validator"
|
||||||
"github.com/tendermint/tendermint/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ShowValidatorCmd adds capabilities for showing the validator info.
|
// ShowValidatorCmd adds capabilities for showing the validator info.
|
||||||
@@ -17,7 +16,7 @@ var ShowValidatorCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func showValidator(cmd *cobra.Command, args []string) {
|
func showValidator(cmd *cobra.Command, args []string) {
|
||||||
privValidator := types.LoadOrGenPrivValidatorFS(config.PrivValidatorFile())
|
privValidator := privval.LoadOrGenFilePV(config.PrivValidatorFile())
|
||||||
pubKeyJSONBytes, _ := data.ToJSON(privValidator.PubKey)
|
pubKeyJSONBytes, _ := cdc.MarshalJSON(privValidator.GetPubKey())
|
||||||
fmt.Println(string(pubKeyJSONBytes))
|
fmt.Println(string(pubKeyJSONBytes))
|
||||||
}
|
}
|
||||||
|
@@ -2,59 +2,103 @@ package commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
cfg "github.com/tendermint/tendermint/config"
|
cfg "github.com/tendermint/tendermint/config"
|
||||||
|
"github.com/tendermint/tendermint/p2p"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
|
pvm "github.com/tendermint/tendermint/types/priv_validator"
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
//flags
|
|
||||||
var (
|
var (
|
||||||
nValidators int
|
nValidators int
|
||||||
dataDir string
|
nNonValidators int
|
||||||
|
outputDir string
|
||||||
|
nodeDirPrefix string
|
||||||
|
|
||||||
|
populatePersistentPeers bool
|
||||||
|
hostnamePrefix string
|
||||||
|
startingIPAddress string
|
||||||
|
p2pPort int
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
nodeDirPerm = 0755
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
TestnetFilesCmd.Flags().IntVar(&nValidators, "n", 4,
|
TestnetFilesCmd.Flags().IntVar(&nValidators, "v", 4,
|
||||||
"Number of validators to initialize the testnet with")
|
"Number of validators to initialize the testnet with")
|
||||||
TestnetFilesCmd.Flags().StringVar(&dataDir, "dir", "mytestnet",
|
TestnetFilesCmd.Flags().IntVar(&nNonValidators, "n", 0,
|
||||||
|
"Number of non-validators to initialize the testnet with")
|
||||||
|
TestnetFilesCmd.Flags().StringVar(&outputDir, "o", "./mytestnet",
|
||||||
"Directory to store initialization data for the testnet")
|
"Directory to store initialization data for the testnet")
|
||||||
|
TestnetFilesCmd.Flags().StringVar(&nodeDirPrefix, "node-dir-prefix", "node",
|
||||||
|
"Prefix the directory name for each node with (node results in node0, node1, ...)")
|
||||||
|
|
||||||
|
TestnetFilesCmd.Flags().BoolVar(&populatePersistentPeers, "populate-persistent-peers", true,
|
||||||
|
"Update config of each node with the list of persistent peers build using either hostname-prefix or starting-ip-address")
|
||||||
|
TestnetFilesCmd.Flags().StringVar(&hostnamePrefix, "hostname-prefix", "node",
|
||||||
|
"Hostname prefix (node results in persistent peers list ID0@node0:46656, ID1@node1:46656, ...)")
|
||||||
|
TestnetFilesCmd.Flags().StringVar(&startingIPAddress, "starting-ip-address", "",
|
||||||
|
"Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:46656, ID1@192.168.0.2:46656, ...)")
|
||||||
|
TestnetFilesCmd.Flags().IntVar(&p2pPort, "p2p-port", 46656,
|
||||||
|
"P2P Port")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestnetFilesCmd allows initialisation of files for a
|
// TestnetFilesCmd allows initialisation of files for a Tendermint testnet.
|
||||||
// Tendermint testnet.
|
|
||||||
var TestnetFilesCmd = &cobra.Command{
|
var TestnetFilesCmd = &cobra.Command{
|
||||||
Use: "testnet",
|
Use: "testnet",
|
||||||
Short: "Initialize files for a Tendermint testnet",
|
Short: "Initialize files for a Tendermint testnet",
|
||||||
Run: testnetFiles,
|
RunE: testnetFiles,
|
||||||
}
|
}
|
||||||
|
|
||||||
func testnetFiles(cmd *cobra.Command, args []string) {
|
func testnetFiles(cmd *cobra.Command, args []string) error {
|
||||||
|
config := cfg.DefaultConfig()
|
||||||
genVals := make([]types.GenesisValidator, nValidators)
|
genVals := make([]types.GenesisValidator, nValidators)
|
||||||
defaultConfig := cfg.DefaultBaseConfig()
|
|
||||||
|
|
||||||
// Initialize core dir and priv_validator.json's
|
|
||||||
for i := 0; i < nValidators; i++ {
|
for i := 0; i < nValidators; i++ {
|
||||||
mach := cmn.Fmt("mach%d", i)
|
nodeDirName := cmn.Fmt("%s%d", nodeDirPrefix, i)
|
||||||
err := initMachCoreDirectory(dataDir, mach)
|
nodeDir := filepath.Join(outputDir, nodeDirName)
|
||||||
|
config.SetRoot(nodeDir)
|
||||||
|
|
||||||
|
err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmn.Exit(err.Error())
|
_ = os.RemoveAll(outputDir)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
// Read priv_validator.json to populate vals
|
|
||||||
privValFile := filepath.Join(dataDir, mach, defaultConfig.PrivValidator)
|
initFilesWithConfig(config)
|
||||||
privVal := types.LoadPrivValidatorFS(privValFile)
|
|
||||||
|
pvFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidator)
|
||||||
|
pv := pvm.LoadFilePV(pvFile)
|
||||||
genVals[i] = types.GenesisValidator{
|
genVals[i] = types.GenesisValidator{
|
||||||
PubKey: privVal.GetPubKey(),
|
PubKey: pv.GetPubKey(),
|
||||||
Power: 1,
|
Power: 1,
|
||||||
Name: mach,
|
Name: nodeDirName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i := 0; i < nNonValidators; i++ {
|
||||||
|
nodeDir := filepath.Join(outputDir, cmn.Fmt("%s%d", nodeDirPrefix, i+nValidators))
|
||||||
|
config.SetRoot(nodeDir)
|
||||||
|
|
||||||
|
err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm)
|
||||||
|
if err != nil {
|
||||||
|
_ = os.RemoveAll(outputDir)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
initFilesWithConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
// Generate genesis doc from generated validators
|
// Generate genesis doc from generated validators
|
||||||
genDoc := &types.GenesisDoc{
|
genDoc := &types.GenesisDoc{
|
||||||
GenesisTime: time.Now(),
|
GenesisTime: time.Now(),
|
||||||
@@ -63,35 +107,65 @@ func testnetFiles(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Write genesis file.
|
// Write genesis file.
|
||||||
for i := 0; i < nValidators; i++ {
|
for i := 0; i < nValidators+nNonValidators; i++ {
|
||||||
mach := cmn.Fmt("mach%d", i)
|
nodeDir := filepath.Join(outputDir, cmn.Fmt("%s%d", nodeDirPrefix, i))
|
||||||
if err := genDoc.SaveAs(filepath.Join(dataDir, mach, defaultConfig.Genesis)); err != nil {
|
if err := genDoc.SaveAs(filepath.Join(nodeDir, config.BaseConfig.Genesis)); err != nil {
|
||||||
panic(err)
|
_ = os.RemoveAll(outputDir)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(cmn.Fmt("Successfully initialized %v node directories", nValidators))
|
if populatePersistentPeers {
|
||||||
|
err := populatePersistentPeersInConfigAndWriteIt(config)
|
||||||
|
if err != nil {
|
||||||
|
_ = os.RemoveAll(outputDir)
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize per-machine core directory
|
fmt.Printf("Successfully initialized %v node directories\n", nValidators+nNonValidators)
|
||||||
func initMachCoreDirectory(base, mach string) error {
|
return nil
|
||||||
dir := filepath.Join(base, mach)
|
}
|
||||||
err := cmn.EnsureDir(dir, 0777)
|
|
||||||
|
func hostnameOrIP(i int) string {
|
||||||
|
if startingIPAddress != "" {
|
||||||
|
ip := net.ParseIP(startingIPAddress)
|
||||||
|
ip = ip.To4()
|
||||||
|
if ip == nil {
|
||||||
|
fmt.Printf("%v: non ipv4 address\n", startingIPAddress)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j < i; j++ {
|
||||||
|
ip[3]++
|
||||||
|
}
|
||||||
|
return ip.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s%d", hostnamePrefix, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func populatePersistentPeersInConfigAndWriteIt(config *cfg.Config) error {
|
||||||
|
persistentPeers := make([]string, nValidators+nNonValidators)
|
||||||
|
for i := 0; i < nValidators+nNonValidators; i++ {
|
||||||
|
nodeDir := filepath.Join(outputDir, cmn.Fmt("%s%d", nodeDirPrefix, i))
|
||||||
|
config.SetRoot(nodeDir)
|
||||||
|
nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
persistentPeers[i] = p2p.IDAddressString(nodeKey.ID(), fmt.Sprintf("%s:%d", hostnameOrIP(i), p2pPort))
|
||||||
|
}
|
||||||
|
persistentPeersList := strings.Join(persistentPeers, ",")
|
||||||
|
|
||||||
|
for i := 0; i < nValidators+nNonValidators; i++ {
|
||||||
|
nodeDir := filepath.Join(outputDir, cmn.Fmt("%s%d", nodeDirPrefix, i))
|
||||||
|
config.SetRoot(nodeDir)
|
||||||
|
config.P2P.PersistentPeers = persistentPeersList
|
||||||
|
|
||||||
|
// overwrite default config
|
||||||
|
cfg.WriteConfigFile(filepath.Join(nodeDir, "config", "config.toml"), config)
|
||||||
|
}
|
||||||
|
|
||||||
// Create priv_validator.json file if not present
|
|
||||||
defaultConfig := cfg.DefaultBaseConfig()
|
|
||||||
ensurePrivValidator(filepath.Join(dir, defaultConfig.PrivValidator))
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func ensurePrivValidator(file string) {
|
|
||||||
if cmn.FileExists(file) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
privValidator := types.GenPrivValidatorFS(file)
|
|
||||||
privValidator.Save()
|
|
||||||
}
|
}
|
||||||
|
12
cmd/tendermint/commands/wire.go
Normal file
12
cmd/tendermint/commands/wire.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
amino "github.com/tendermint/go-amino"
|
||||||
|
"github.com/tendermint/go-crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cdc = amino.NewCodec()
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
crypto.RegisterAmino(cdc)
|
||||||
|
}
|
@@ -24,6 +24,8 @@ func main() {
|
|||||||
cmd.ResetPrivValidatorCmd,
|
cmd.ResetPrivValidatorCmd,
|
||||||
cmd.ShowValidatorCmd,
|
cmd.ShowValidatorCmd,
|
||||||
cmd.TestnetFilesCmd,
|
cmd.TestnetFilesCmd,
|
||||||
|
cmd.ShowNodeIDCmd,
|
||||||
|
cmd.GenNodeKeyCmd,
|
||||||
cmd.VersionCmd)
|
cmd.VersionCmd)
|
||||||
|
|
||||||
// NOTE:
|
// NOTE:
|
||||||
|
23
codecov.yml
Normal file
23
codecov.yml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
coverage:
|
||||||
|
precision: 2
|
||||||
|
round: down
|
||||||
|
range: "70...100"
|
||||||
|
|
||||||
|
status:
|
||||||
|
project:
|
||||||
|
default:
|
||||||
|
threshold: 1%
|
||||||
|
patch: on
|
||||||
|
changes: off
|
||||||
|
|
||||||
|
comment:
|
||||||
|
layout: "diff, files"
|
||||||
|
behavior: default
|
||||||
|
require_changes: no
|
||||||
|
require_base: no
|
||||||
|
require_head: yes
|
||||||
|
|
||||||
|
ignore:
|
||||||
|
- "docs"
|
||||||
|
- "DOCKER"
|
||||||
|
- "scripts"
|
178
config/config.go
178
config/config.go
@@ -20,6 +20,7 @@ var (
|
|||||||
|
|
||||||
defaultConfigFileName = "config.toml"
|
defaultConfigFileName = "config.toml"
|
||||||
defaultGenesisJSONName = "genesis.json"
|
defaultGenesisJSONName = "genesis.json"
|
||||||
|
|
||||||
defaultPrivValName = "priv_validator.json"
|
defaultPrivValName = "priv_validator.json"
|
||||||
defaultNodeKeyName = "node_key.json"
|
defaultNodeKeyName = "node_key.json"
|
||||||
defaultAddrBookName = "addrbook.json"
|
defaultAddrBookName = "addrbook.json"
|
||||||
@@ -103,6 +104,10 @@ type BaseConfig struct {
|
|||||||
// A custom human readable name for this node
|
// A custom human readable name for this node
|
||||||
Moniker string `mapstructure:"moniker"`
|
Moniker string `mapstructure:"moniker"`
|
||||||
|
|
||||||
|
// TCP or UNIX socket address for Tendermint to listen on for
|
||||||
|
// connections from an external PrivValidator process
|
||||||
|
PrivValidatorListenAddr string `mapstructure:"priv_validator_laddr"`
|
||||||
|
|
||||||
// TCP or UNIX socket address of the ABCI application,
|
// TCP or UNIX socket address of the ABCI application,
|
||||||
// or the name of an ABCI application compiled in with the Tendermint binary
|
// or the name of an ABCI application compiled in with the Tendermint binary
|
||||||
ProxyApp string `mapstructure:"proxy_app"`
|
ProxyApp string `mapstructure:"proxy_app"`
|
||||||
@@ -132,10 +137,6 @@ type BaseConfig struct {
|
|||||||
DBPath string `mapstructure:"db_dir"`
|
DBPath string `mapstructure:"db_dir"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c BaseConfig) ChainID() string {
|
|
||||||
return c.chainID
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultBaseConfig returns a default base configuration for a Tendermint node
|
// DefaultBaseConfig returns a default base configuration for a Tendermint node
|
||||||
func DefaultBaseConfig() BaseConfig {
|
func DefaultBaseConfig() BaseConfig {
|
||||||
return BaseConfig{
|
return BaseConfig{
|
||||||
@@ -156,32 +157,36 @@ func DefaultBaseConfig() BaseConfig {
|
|||||||
|
|
||||||
// TestBaseConfig returns a base configuration for testing a Tendermint node
|
// TestBaseConfig returns a base configuration for testing a Tendermint node
|
||||||
func TestBaseConfig() BaseConfig {
|
func TestBaseConfig() BaseConfig {
|
||||||
conf := DefaultBaseConfig()
|
cfg := DefaultBaseConfig()
|
||||||
conf.chainID = "tendermint_test"
|
cfg.chainID = "tendermint_test"
|
||||||
conf.ProxyApp = "dummy"
|
cfg.ProxyApp = "kvstore"
|
||||||
conf.FastSync = false
|
cfg.FastSync = false
|
||||||
conf.DBBackend = "memdb"
|
cfg.DBBackend = "memdb"
|
||||||
return conf
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg BaseConfig) ChainID() string {
|
||||||
|
return cfg.chainID
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenesisFile returns the full path to the genesis.json file
|
// GenesisFile returns the full path to the genesis.json file
|
||||||
func (b BaseConfig) GenesisFile() string {
|
func (cfg BaseConfig) GenesisFile() string {
|
||||||
return rootify(b.Genesis, b.RootDir)
|
return rootify(cfg.Genesis, cfg.RootDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrivValidatorFile returns the full path to the priv_validator.json file
|
// PrivValidatorFile returns the full path to the priv_validator.json file
|
||||||
func (b BaseConfig) PrivValidatorFile() string {
|
func (cfg BaseConfig) PrivValidatorFile() string {
|
||||||
return rootify(b.PrivValidator, b.RootDir)
|
return rootify(cfg.PrivValidator, cfg.RootDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeKeyFile returns the full path to the node_key.json file
|
// NodeKeyFile returns the full path to the node_key.json file
|
||||||
func (b BaseConfig) NodeKeyFile() string {
|
func (cfg BaseConfig) NodeKeyFile() string {
|
||||||
return rootify(b.NodeKey, b.RootDir)
|
return rootify(cfg.NodeKey, cfg.RootDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DBDir returns the full path to the database directory
|
// DBDir returns the full path to the database directory
|
||||||
func (b BaseConfig) DBDir() string {
|
func (cfg BaseConfig) DBDir() string {
|
||||||
return rootify(b.DBPath, b.RootDir)
|
return rootify(cfg.DBPath, cfg.RootDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultLogLevel returns a default log level of "error"
|
// DefaultLogLevel returns a default log level of "error"
|
||||||
@@ -224,11 +229,11 @@ func DefaultRPCConfig() *RPCConfig {
|
|||||||
|
|
||||||
// TestRPCConfig returns a configuration for testing the RPC server
|
// TestRPCConfig returns a configuration for testing the RPC server
|
||||||
func TestRPCConfig() *RPCConfig {
|
func TestRPCConfig() *RPCConfig {
|
||||||
conf := DefaultRPCConfig()
|
cfg := DefaultRPCConfig()
|
||||||
conf.ListenAddress = "tcp://0.0.0.0:36657"
|
cfg.ListenAddress = "tcp://0.0.0.0:36657"
|
||||||
conf.GRPCListenAddress = "tcp://0.0.0.0:36658"
|
cfg.GRPCListenAddress = "tcp://0.0.0.0:36658"
|
||||||
conf.Unsafe = true
|
cfg.Unsafe = true
|
||||||
return conf
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@@ -245,8 +250,8 @@ type P2PConfig struct {
|
|||||||
// We only use these if we can’t connect to peers in the addrbook
|
// We only use these if we can’t connect to peers in the addrbook
|
||||||
Seeds string `mapstructure:"seeds"`
|
Seeds string `mapstructure:"seeds"`
|
||||||
|
|
||||||
// Comma separated list of persistent peers to connect to
|
// Comma separated list of nodes to keep persistent connections to
|
||||||
// We always connect to these
|
// Do not add private peers to this list if you don't want them advertised
|
||||||
PersistentPeers string `mapstructure:"persistent_peers"`
|
PersistentPeers string `mapstructure:"persistent_peers"`
|
||||||
|
|
||||||
// Skip UPNP port forwarding
|
// Skip UPNP port forwarding
|
||||||
@@ -265,7 +270,7 @@ type P2PConfig struct {
|
|||||||
FlushThrottleTimeout int `mapstructure:"flush_throttle_timeout"`
|
FlushThrottleTimeout int `mapstructure:"flush_throttle_timeout"`
|
||||||
|
|
||||||
// Maximum size of a message packet payload, in bytes
|
// Maximum size of a message packet payload, in bytes
|
||||||
MaxMsgPacketPayloadSize int `mapstructure:"max_msg_packet_payload_size"`
|
MaxPacketMsgPayloadSize int `mapstructure:"max_packet_msg_payload_size"`
|
||||||
|
|
||||||
// Rate at which packets can be sent, in bytes/second
|
// Rate at which packets can be sent, in bytes/second
|
||||||
SendRate int64 `mapstructure:"send_rate"`
|
SendRate int64 `mapstructure:"send_rate"`
|
||||||
@@ -281,6 +286,12 @@ type P2PConfig struct {
|
|||||||
//
|
//
|
||||||
// Does not work if the peer-exchange reactor is disabled.
|
// Does not work if the peer-exchange reactor is disabled.
|
||||||
SeedMode bool `mapstructure:"seed_mode"`
|
SeedMode bool `mapstructure:"seed_mode"`
|
||||||
|
|
||||||
|
// Authenticated encryption
|
||||||
|
AuthEnc bool `mapstructure:"auth_enc"`
|
||||||
|
|
||||||
|
// Comma separated list of peer IDs to keep private (will not be gossiped to other peers)
|
||||||
|
PrivatePeerIDs string `mapstructure:"private_peer_ids"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultP2PConfig returns a default configuration for the peer-to-peer layer
|
// DefaultP2PConfig returns a default configuration for the peer-to-peer layer
|
||||||
@@ -291,26 +302,27 @@ func DefaultP2PConfig() *P2PConfig {
|
|||||||
AddrBookStrict: true,
|
AddrBookStrict: true,
|
||||||
MaxNumPeers: 50,
|
MaxNumPeers: 50,
|
||||||
FlushThrottleTimeout: 100,
|
FlushThrottleTimeout: 100,
|
||||||
MaxMsgPacketPayloadSize: 1024, // 1 kB
|
MaxPacketMsgPayloadSize: 1024, // 1 kB
|
||||||
SendRate: 512000, // 500 kB/s
|
SendRate: 512000, // 500 kB/s
|
||||||
RecvRate: 512000, // 500 kB/s
|
RecvRate: 512000, // 500 kB/s
|
||||||
PexReactor: true,
|
PexReactor: true,
|
||||||
SeedMode: false,
|
SeedMode: false,
|
||||||
|
AuthEnc: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestP2PConfig returns a configuration for testing the peer-to-peer layer
|
// TestP2PConfig returns a configuration for testing the peer-to-peer layer
|
||||||
func TestP2PConfig() *P2PConfig {
|
func TestP2PConfig() *P2PConfig {
|
||||||
conf := DefaultP2PConfig()
|
cfg := DefaultP2PConfig()
|
||||||
conf.ListenAddress = "tcp://0.0.0.0:36656"
|
cfg.ListenAddress = "tcp://0.0.0.0:36656"
|
||||||
conf.SkipUPNP = true
|
cfg.SkipUPNP = true
|
||||||
conf.FlushThrottleTimeout = 10
|
cfg.FlushThrottleTimeout = 10
|
||||||
return conf
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddrBookFile returns the full path to the address book
|
// AddrBookFile returns the full path to the address book
|
||||||
func (p *P2PConfig) AddrBookFile() string {
|
func (cfg *P2PConfig) AddrBookFile() string {
|
||||||
return rootify(p.AddrBook, p.RootDir)
|
return rootify(cfg.AddrBook, cfg.RootDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@@ -339,14 +351,14 @@ func DefaultMempoolConfig() *MempoolConfig {
|
|||||||
|
|
||||||
// TestMempoolConfig returns a configuration for testing the Tendermint mempool
|
// TestMempoolConfig returns a configuration for testing the Tendermint mempool
|
||||||
func TestMempoolConfig() *MempoolConfig {
|
func TestMempoolConfig() *MempoolConfig {
|
||||||
config := DefaultMempoolConfig()
|
cfg := DefaultMempoolConfig()
|
||||||
config.CacheSize = 1000
|
cfg.CacheSize = 1000
|
||||||
return config
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
// WalDir returns the full path to the mempool's write-ahead log
|
// WalDir returns the full path to the mempool's write-ahead log
|
||||||
func (m *MempoolConfig) WalDir() string {
|
func (cfg *MempoolConfig) WalDir() string {
|
||||||
return rootify(m.WalPath, m.RootDir)
|
return rootify(cfg.WalPath, cfg.RootDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@@ -385,6 +397,44 @@ type ConsensusConfig struct {
|
|||||||
PeerQueryMaj23SleepDuration int `mapstructure:"peer_query_maj23_sleep_duration"`
|
PeerQueryMaj23SleepDuration int `mapstructure:"peer_query_maj23_sleep_duration"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DefaultConsensusConfig returns a default configuration for the consensus service
|
||||||
|
func DefaultConsensusConfig() *ConsensusConfig {
|
||||||
|
return &ConsensusConfig{
|
||||||
|
WalPath: filepath.Join(defaultDataDir, "cs.wal", "wal"),
|
||||||
|
WalLight: false,
|
||||||
|
TimeoutPropose: 3000,
|
||||||
|
TimeoutProposeDelta: 500,
|
||||||
|
TimeoutPrevote: 1000,
|
||||||
|
TimeoutPrevoteDelta: 500,
|
||||||
|
TimeoutPrecommit: 1000,
|
||||||
|
TimeoutPrecommitDelta: 500,
|
||||||
|
TimeoutCommit: 1000,
|
||||||
|
SkipTimeoutCommit: false,
|
||||||
|
MaxBlockSizeTxs: 10000,
|
||||||
|
MaxBlockSizeBytes: 1, // TODO
|
||||||
|
CreateEmptyBlocks: true,
|
||||||
|
CreateEmptyBlocksInterval: 0,
|
||||||
|
PeerGossipSleepDuration: 100,
|
||||||
|
PeerQueryMaj23SleepDuration: 2000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestConsensusConfig returns a configuration for testing the consensus service
|
||||||
|
func TestConsensusConfig() *ConsensusConfig {
|
||||||
|
cfg := DefaultConsensusConfig()
|
||||||
|
cfg.TimeoutPropose = 100
|
||||||
|
cfg.TimeoutProposeDelta = 1
|
||||||
|
cfg.TimeoutPrevote = 10
|
||||||
|
cfg.TimeoutPrevoteDelta = 1
|
||||||
|
cfg.TimeoutPrecommit = 10
|
||||||
|
cfg.TimeoutPrecommitDelta = 1
|
||||||
|
cfg.TimeoutCommit = 10
|
||||||
|
cfg.SkipTimeoutCommit = true
|
||||||
|
cfg.PeerGossipSleepDuration = 5
|
||||||
|
cfg.PeerQueryMaj23SleepDuration = 250
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
// WaitForTxs returns true if the consensus should wait for transactions before entering the propose step
|
// WaitForTxs returns true if the consensus should wait for transactions before entering the propose step
|
||||||
func (cfg *ConsensusConfig) WaitForTxs() bool {
|
func (cfg *ConsensusConfig) WaitForTxs() bool {
|
||||||
return !cfg.CreateEmptyBlocks || cfg.CreateEmptyBlocksInterval > 0
|
return !cfg.CreateEmptyBlocks || cfg.CreateEmptyBlocksInterval > 0
|
||||||
@@ -425,55 +475,17 @@ func (cfg *ConsensusConfig) PeerQueryMaj23Sleep() time.Duration {
|
|||||||
return time.Duration(cfg.PeerQueryMaj23SleepDuration) * time.Millisecond
|
return time.Duration(cfg.PeerQueryMaj23SleepDuration) * time.Millisecond
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultConsensusConfig returns a default configuration for the consensus service
|
|
||||||
func DefaultConsensusConfig() *ConsensusConfig {
|
|
||||||
return &ConsensusConfig{
|
|
||||||
WalPath: filepath.Join(defaultDataDir, "cs.wal", "wal"),
|
|
||||||
WalLight: false,
|
|
||||||
TimeoutPropose: 3000,
|
|
||||||
TimeoutProposeDelta: 500,
|
|
||||||
TimeoutPrevote: 1000,
|
|
||||||
TimeoutPrevoteDelta: 500,
|
|
||||||
TimeoutPrecommit: 1000,
|
|
||||||
TimeoutPrecommitDelta: 500,
|
|
||||||
TimeoutCommit: 1000,
|
|
||||||
SkipTimeoutCommit: false,
|
|
||||||
MaxBlockSizeTxs: 10000,
|
|
||||||
MaxBlockSizeBytes: 1, // TODO
|
|
||||||
CreateEmptyBlocks: true,
|
|
||||||
CreateEmptyBlocksInterval: 0,
|
|
||||||
PeerGossipSleepDuration: 100,
|
|
||||||
PeerQueryMaj23SleepDuration: 2000,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestConsensusConfig returns a configuration for testing the consensus service
|
|
||||||
func TestConsensusConfig() *ConsensusConfig {
|
|
||||||
config := DefaultConsensusConfig()
|
|
||||||
config.TimeoutPropose = 100
|
|
||||||
config.TimeoutProposeDelta = 1
|
|
||||||
config.TimeoutPrevote = 10
|
|
||||||
config.TimeoutPrevoteDelta = 1
|
|
||||||
config.TimeoutPrecommit = 10
|
|
||||||
config.TimeoutPrecommitDelta = 1
|
|
||||||
config.TimeoutCommit = 10
|
|
||||||
config.SkipTimeoutCommit = true
|
|
||||||
config.PeerGossipSleepDuration = 5
|
|
||||||
config.PeerQueryMaj23SleepDuration = 250
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
|
|
||||||
// WalFile returns the full path to the write-ahead log file
|
// WalFile returns the full path to the write-ahead log file
|
||||||
func (c *ConsensusConfig) WalFile() string {
|
func (cfg *ConsensusConfig) WalFile() string {
|
||||||
if c.walFile != "" {
|
if cfg.walFile != "" {
|
||||||
return c.walFile
|
return cfg.walFile
|
||||||
}
|
}
|
||||||
return rootify(c.WalPath, c.RootDir)
|
return rootify(cfg.WalPath, cfg.RootDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetWalFile sets the path to the write-ahead log file
|
// SetWalFile sets the path to the write-ahead log file
|
||||||
func (c *ConsensusConfig) SetWalFile(walFile string) {
|
func (cfg *ConsensusConfig) SetWalFile(walFile string) {
|
||||||
c.walFile = walFile
|
cfg.walFile = walFile
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@@ -37,16 +37,21 @@ func EnsureRoot(rootDir string) {
|
|||||||
|
|
||||||
// Write default config file if missing.
|
// Write default config file if missing.
|
||||||
if !cmn.FileExists(configFilePath) {
|
if !cmn.FileExists(configFilePath) {
|
||||||
writeConfigFile(configFilePath)
|
writeDefaultCondigFile(configFilePath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: this func should probably be called by cmd/tendermint/commands/init.go
|
// XXX: this func should probably be called by cmd/tendermint/commands/init.go
|
||||||
// alongside the writing of the genesis.json and priv_validator.json
|
// alongside the writing of the genesis.json and priv_validator.json
|
||||||
func writeConfigFile(configFilePath string) {
|
func writeDefaultCondigFile(configFilePath string) {
|
||||||
|
WriteConfigFile(configFilePath, DefaultConfig())
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteConfigFile renders config using the template and writes it to configFilePath.
|
||||||
|
func WriteConfigFile(configFilePath string, config *Config) {
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
|
|
||||||
if err := configTemplate.Execute(&buffer, DefaultConfig()); err != nil {
|
if err := configTemplate.Execute(&buffer, config); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,10 +129,11 @@ unsafe = {{ .RPC.Unsafe }}
|
|||||||
laddr = "{{ .P2P.ListenAddress }}"
|
laddr = "{{ .P2P.ListenAddress }}"
|
||||||
|
|
||||||
# Comma separated list of seed nodes to connect to
|
# Comma separated list of seed nodes to connect to
|
||||||
seeds = ""
|
seeds = "{{ .P2P.Seeds }}"
|
||||||
|
|
||||||
# Comma separated list of nodes to keep persistent connections to
|
# Comma separated list of nodes to keep persistent connections to
|
||||||
persistent_peers = ""
|
# Do not add private peers to this list if you don't want them advertised
|
||||||
|
persistent_peers = "{{ .P2P.PersistentPeers }}"
|
||||||
|
|
||||||
# Path to address book
|
# Path to address book
|
||||||
addr_book_file = "{{ .P2P.AddrBook }}"
|
addr_book_file = "{{ .P2P.AddrBook }}"
|
||||||
@@ -142,7 +148,7 @@ flush_throttle_timeout = {{ .P2P.FlushThrottleTimeout }}
|
|||||||
max_num_peers = {{ .P2P.MaxNumPeers }}
|
max_num_peers = {{ .P2P.MaxNumPeers }}
|
||||||
|
|
||||||
# Maximum size of a message packet payload, in bytes
|
# Maximum size of a message packet payload, in bytes
|
||||||
max_msg_packet_payload_size = {{ .P2P.MaxMsgPacketPayloadSize }}
|
max_packet_msg_payload_size = {{ .P2P.MaxPacketMsgPayloadSize }}
|
||||||
|
|
||||||
# Rate at which packets can be sent, in bytes/second
|
# Rate at which packets can be sent, in bytes/second
|
||||||
send_rate = {{ .P2P.SendRate }}
|
send_rate = {{ .P2P.SendRate }}
|
||||||
@@ -159,6 +165,12 @@ pex = {{ .P2P.PexReactor }}
|
|||||||
# Does not work if the peer-exchange reactor is disabled.
|
# Does not work if the peer-exchange reactor is disabled.
|
||||||
seed_mode = {{ .P2P.SeedMode }}
|
seed_mode = {{ .P2P.SeedMode }}
|
||||||
|
|
||||||
|
# Authenticated encryption
|
||||||
|
auth_enc = {{ .P2P.AuthEnc }}
|
||||||
|
|
||||||
|
# Comma separated list of peer IDs to keep private (will not be gossiped to other peers)
|
||||||
|
private_peer_ids = "{{ .P2P.PrivatePeerIDs }}"
|
||||||
|
|
||||||
##### mempool configuration options #####
|
##### mempool configuration options #####
|
||||||
[mempool]
|
[mempool]
|
||||||
|
|
||||||
@@ -255,7 +267,7 @@ func ResetTestRoot(testName string) *Config {
|
|||||||
|
|
||||||
// Write default config file if missing.
|
// Write default config file if missing.
|
||||||
if !cmn.FileExists(configFilePath) {
|
if !cmn.FileExists(configFilePath) {
|
||||||
writeConfigFile(configFilePath)
|
writeDefaultCondigFile(configFilePath)
|
||||||
}
|
}
|
||||||
if !cmn.FileExists(genesisFilePath) {
|
if !cmn.FileExists(genesisFilePath) {
|
||||||
cmn.MustWriteFile(genesisFilePath, []byte(testGenesis), 0644)
|
cmn.MustWriteFile(genesisFilePath, []byte(testGenesis), 0644)
|
||||||
@@ -273,8 +285,8 @@ var testGenesis = `{
|
|||||||
"validators": [
|
"validators": [
|
||||||
{
|
{
|
||||||
"pub_key": {
|
"pub_key": {
|
||||||
"type": "ed25519",
|
"type": "AC26791624DE60",
|
||||||
"data":"3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
|
"value":"AT/+aaL1eB0477Mud9JMm8Sh8BIvOYlPGC9KkIUmFaE="
|
||||||
},
|
},
|
||||||
"power": 10,
|
"power": 10,
|
||||||
"name": ""
|
"name": ""
|
||||||
@@ -284,14 +296,14 @@ var testGenesis = `{
|
|||||||
}`
|
}`
|
||||||
|
|
||||||
var testPrivValidator = `{
|
var testPrivValidator = `{
|
||||||
"address": "D028C9981F7A87F3093672BF0D5B0E2A1B3ED456",
|
"address": "849CB2C877F87A20925F35D00AE6688342D25B47",
|
||||||
"pub_key": {
|
"pub_key": {
|
||||||
"type": "ed25519",
|
"type": "AC26791624DE60",
|
||||||
"data": "3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
|
"value": "AT/+aaL1eB0477Mud9JMm8Sh8BIvOYlPGC9KkIUmFaE="
|
||||||
},
|
},
|
||||||
"priv_key": {
|
"priv_key": {
|
||||||
"type": "ed25519",
|
"type": "954568A3288910",
|
||||||
"data": "27F82582AEFAE7AB151CFB01C48BB6C1A0DA78F9BDDA979A9F70A84D074EB07D3B3069C422E19688B45CBFAE7BB009FC0FA1B1EA86593519318B7214853803C8"
|
"value": "EVkqJO/jIXp3rkASXfh9YnyToYXRXhBr6g9cQVxPFnQBP/5povV4HTjvsy530kybxKHwEi85iU8YL0qQhSYVoQ=="
|
||||||
},
|
},
|
||||||
"last_height": 0,
|
"last_height": 0,
|
||||||
"last_round": 0,
|
"last_round": 0,
|
||||||
|
@@ -7,7 +7,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
crypto "github.com/tendermint/go-crypto"
|
|
||||||
"github.com/tendermint/tendermint/p2p"
|
"github.com/tendermint/tendermint/p2p"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
@@ -46,9 +45,11 @@ func TestByzantine(t *testing.T) {
|
|||||||
eventChans := make([]chan interface{}, N)
|
eventChans := make([]chan interface{}, N)
|
||||||
reactors := make([]p2p.Reactor, N)
|
reactors := make([]p2p.Reactor, N)
|
||||||
for i := 0; i < N; i++ {
|
for i := 0; i < N; i++ {
|
||||||
|
// make first val byzantine
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
css[i].privValidator = NewByzantinePrivValidator(css[i].privValidator)
|
// NOTE: Now, test validators are MockPV, which by default doesn't
|
||||||
// make byzantine
|
// do any safety checks.
|
||||||
|
css[i].privValidator.(*types.MockPV).DisableChecks()
|
||||||
css[i].decideProposal = func(j int) func(int64, int) {
|
css[i].decideProposal = func(j int) func(int64, int) {
|
||||||
return func(height int64, round int) {
|
return func(height int64, round int) {
|
||||||
byzantineDecideProposalFunc(t, height, round, css[j], switches[j])
|
byzantineDecideProposalFunc(t, height, round, css[j], switches[j])
|
||||||
@@ -74,9 +75,11 @@ func TestByzantine(t *testing.T) {
|
|||||||
var conRI p2p.Reactor // nolint: gotype, gosimple
|
var conRI p2p.Reactor // nolint: gotype, gosimple
|
||||||
conRI = conR
|
conRI = conR
|
||||||
|
|
||||||
|
// make first val byzantine
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
conRI = NewByzantineReactor(conR)
|
conRI = NewByzantineReactor(conR)
|
||||||
}
|
}
|
||||||
|
|
||||||
reactors[i] = conRI
|
reactors[i] = conRI
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,19 +118,19 @@ func TestByzantine(t *testing.T) {
|
|||||||
// and the other block to peers[1] and peers[2].
|
// and the other block to peers[1] and peers[2].
|
||||||
// note peers and switches order don't match.
|
// note peers and switches order don't match.
|
||||||
peers := switches[0].Peers().List()
|
peers := switches[0].Peers().List()
|
||||||
|
|
||||||
|
// partition A
|
||||||
ind0 := getSwitchIndex(switches, peers[0])
|
ind0 := getSwitchIndex(switches, peers[0])
|
||||||
|
|
||||||
|
// partition B
|
||||||
ind1 := getSwitchIndex(switches, peers[1])
|
ind1 := getSwitchIndex(switches, peers[1])
|
||||||
ind2 := getSwitchIndex(switches, peers[2])
|
ind2 := getSwitchIndex(switches, peers[2])
|
||||||
|
|
||||||
// connect the 2 peers in the larger partition
|
|
||||||
p2p.Connect2Switches(switches, ind1, ind2)
|
p2p.Connect2Switches(switches, ind1, ind2)
|
||||||
|
|
||||||
// wait for someone in the big partition to make a block
|
// wait for someone in the big partition (B) to make a block
|
||||||
<-eventChans[ind2]
|
<-eventChans[ind2]
|
||||||
|
|
||||||
t.Log("A block has been committed. Healing partition")
|
t.Log("A block has been committed. Healing partition")
|
||||||
|
|
||||||
// connect the partitions
|
|
||||||
p2p.Connect2Switches(switches, ind0, ind1)
|
p2p.Connect2Switches(switches, ind0, ind1)
|
||||||
p2p.Connect2Switches(switches, ind0, ind2)
|
p2p.Connect2Switches(switches, ind0, ind2)
|
||||||
|
|
||||||
@@ -201,7 +204,7 @@ func byzantineDecideProposalFunc(t *testing.T, height int64, round int, cs *Cons
|
|||||||
func sendProposalAndParts(height int64, round int, cs *ConsensusState, peer p2p.Peer, proposal *types.Proposal, blockHash []byte, parts *types.PartSet) {
|
func sendProposalAndParts(height int64, round int, cs *ConsensusState, peer p2p.Peer, proposal *types.Proposal, blockHash []byte, parts *types.PartSet) {
|
||||||
// proposal
|
// proposal
|
||||||
msg := &ProposalMessage{Proposal: proposal}
|
msg := &ProposalMessage{Proposal: proposal}
|
||||||
peer.Send(DataChannel, struct{ ConsensusMessage }{msg})
|
peer.Send(DataChannel, cdc.MustMarshalBinaryBare(msg))
|
||||||
|
|
||||||
// parts
|
// parts
|
||||||
for i := 0; i < parts.Total(); i++ {
|
for i := 0; i < parts.Total(); i++ {
|
||||||
@@ -211,7 +214,7 @@ func sendProposalAndParts(height int64, round int, cs *ConsensusState, peer p2p.
|
|||||||
Round: round, // This tells peer that this part applies to us.
|
Round: round, // This tells peer that this part applies to us.
|
||||||
Part: part,
|
Part: part,
|
||||||
}
|
}
|
||||||
peer.Send(DataChannel, struct{ ConsensusMessage }{msg})
|
peer.Send(DataChannel, cdc.MustMarshalBinaryBare(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
// votes
|
// votes
|
||||||
@@ -220,8 +223,8 @@ func sendProposalAndParts(height int64, round int, cs *ConsensusState, peer p2p.
|
|||||||
precommit, _ := cs.signVote(types.VoteTypePrecommit, blockHash, parts.Header())
|
precommit, _ := cs.signVote(types.VoteTypePrecommit, blockHash, parts.Header())
|
||||||
cs.mtx.Unlock()
|
cs.mtx.Unlock()
|
||||||
|
|
||||||
peer.Send(VoteChannel, struct{ ConsensusMessage }{&VoteMessage{prevote}})
|
peer.Send(VoteChannel, cdc.MustMarshalBinaryBare(&VoteMessage{prevote}))
|
||||||
peer.Send(VoteChannel, struct{ ConsensusMessage }{&VoteMessage{precommit}})
|
peer.Send(VoteChannel, cdc.MustMarshalBinaryBare(&VoteMessage{precommit}))
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
@@ -262,47 +265,3 @@ func (br *ByzantineReactor) RemovePeer(peer p2p.Peer, reason interface{}) {
|
|||||||
func (br *ByzantineReactor) Receive(chID byte, peer p2p.Peer, msgBytes []byte) {
|
func (br *ByzantineReactor) Receive(chID byte, peer p2p.Peer, msgBytes []byte) {
|
||||||
br.reactor.Receive(chID, peer, msgBytes)
|
br.reactor.Receive(chID, peer, msgBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------
|
|
||||||
// byzantine privValidator
|
|
||||||
|
|
||||||
type ByzantinePrivValidator struct {
|
|
||||||
types.Signer
|
|
||||||
|
|
||||||
pv types.PrivValidator
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a priv validator that will sign anything
|
|
||||||
func NewByzantinePrivValidator(pv types.PrivValidator) *ByzantinePrivValidator {
|
|
||||||
return &ByzantinePrivValidator{
|
|
||||||
Signer: pv.(*types.PrivValidatorFS).Signer,
|
|
||||||
pv: pv,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (privVal *ByzantinePrivValidator) GetAddress() types.Address {
|
|
||||||
return privVal.pv.GetAddress()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (privVal *ByzantinePrivValidator) GetPubKey() crypto.PubKey {
|
|
||||||
return privVal.pv.GetPubKey()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (privVal *ByzantinePrivValidator) SignVote(chainID string, vote *types.Vote) (err error) {
|
|
||||||
vote.Signature, err = privVal.Sign(types.SignBytes(chainID, vote))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (privVal *ByzantinePrivValidator) SignProposal(chainID string, proposal *types.Proposal) (err error) {
|
|
||||||
proposal.Signature, _ = privVal.Sign(types.SignBytes(chainID, proposal))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (privVal *ByzantinePrivValidator) SignHeartbeat(chainID string, heartbeat *types.Heartbeat) (err error) {
|
|
||||||
heartbeat.Signature, _ = privVal.Sign(types.SignBytes(chainID, heartbeat))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (privVal *ByzantinePrivValidator) String() string {
|
|
||||||
return cmn.Fmt("PrivValidator{%X}", privVal.GetAddress())
|
|
||||||
}
|
|
||||||
|
@@ -21,12 +21,13 @@ import (
|
|||||||
"github.com/tendermint/tendermint/p2p"
|
"github.com/tendermint/tendermint/p2p"
|
||||||
sm "github.com/tendermint/tendermint/state"
|
sm "github.com/tendermint/tendermint/state"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
|
pvm "github.com/tendermint/tendermint/types/priv_validator"
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
dbm "github.com/tendermint/tmlibs/db"
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
"github.com/tendermint/abci/example/counter"
|
"github.com/tendermint/abci/example/counter"
|
||||||
"github.com/tendermint/abci/example/dummy"
|
"github.com/tendermint/abci/example/kvstore"
|
||||||
|
|
||||||
"github.com/go-kit/kit/log/term"
|
"github.com/go-kit/kit/log/term"
|
||||||
)
|
)
|
||||||
@@ -50,7 +51,7 @@ func ResetConfig(name string) *cfg.Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------
|
||||||
// validator stub (a dummy consensus peer we control)
|
// validator stub (a kvstore consensus peer we control)
|
||||||
|
|
||||||
type validatorStub struct {
|
type validatorStub struct {
|
||||||
Index int // Validator index. NOTE: we don't assume validator set changes.
|
Index int // Validator index. NOTE: we don't assume validator set changes.
|
||||||
@@ -101,13 +102,13 @@ func signVotes(voteType byte, hash []byte, header types.PartSetHeader, vss ...*v
|
|||||||
|
|
||||||
func incrementHeight(vss ...*validatorStub) {
|
func incrementHeight(vss ...*validatorStub) {
|
||||||
for _, vs := range vss {
|
for _, vs := range vss {
|
||||||
vs.Height += 1
|
vs.Height++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func incrementRound(vss ...*validatorStub) {
|
func incrementRound(vss ...*validatorStub) {
|
||||||
for _, vs := range vss {
|
for _, vs := range vss {
|
||||||
vs.Round += 1
|
vs.Round++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +223,7 @@ func subscribeToVoter(cs *ConsensusState, addr []byte) chan interface{} {
|
|||||||
voteCh := make(chan interface{})
|
voteCh := make(chan interface{})
|
||||||
go func() {
|
go func() {
|
||||||
for v := range voteCh0 {
|
for v := range voteCh0 {
|
||||||
vote := v.(types.TMEventData).Unwrap().(types.EventDataVote)
|
vote := v.(types.EventDataVote)
|
||||||
// we only fire for our own votes
|
// we only fire for our own votes
|
||||||
if bytes.Equal(addr, vote.Vote.ValidatorAddress) {
|
if bytes.Equal(addr, vote.Vote.ValidatorAddress) {
|
||||||
voteCh <- v
|
voteCh <- v
|
||||||
@@ -277,10 +278,10 @@ func newConsensusStateWithConfigAndBlockStore(thisConfig *cfg.Config, state sm.S
|
|||||||
return cs
|
return cs
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadPrivValidator(config *cfg.Config) *types.PrivValidatorFS {
|
func loadPrivValidator(config *cfg.Config) *pvm.FilePV {
|
||||||
privValidatorFile := config.PrivValidatorFile()
|
privValidatorFile := config.PrivValidatorFile()
|
||||||
ensureDir(path.Dir(privValidatorFile), 0700)
|
ensureDir(path.Dir(privValidatorFile), 0700)
|
||||||
privValidator := types.LoadOrGenPrivValidatorFS(privValidatorFile)
|
privValidator := pvm.LoadOrGenFilePV(privValidatorFile)
|
||||||
privValidator.Reset()
|
privValidator.Reset()
|
||||||
return privValidator
|
return privValidator
|
||||||
}
|
}
|
||||||
@@ -378,7 +379,7 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF
|
|||||||
privVal = privVals[i]
|
privVal = privVals[i]
|
||||||
} else {
|
} else {
|
||||||
_, tempFilePath := cmn.Tempfile("priv_validator_")
|
_, tempFilePath := cmn.Tempfile("priv_validator_")
|
||||||
privVal = types.GenPrivValidatorFS(tempFilePath)
|
privVal = pvm.GenFilePV(tempFilePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
app := appFunc()
|
app := appFunc()
|
||||||
@@ -394,7 +395,7 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF
|
|||||||
|
|
||||||
func getSwitchIndex(switches []*p2p.Switch, peer p2p.Peer) int {
|
func getSwitchIndex(switches []*p2p.Switch, peer p2p.Peer) int {
|
||||||
for i, s := range switches {
|
for i, s := range switches {
|
||||||
if bytes.Equal(peer.NodeInfo().PubKey.Address(), s.NodeInfo().PubKey.Address()) {
|
if peer.NodeInfo().ID == s.NodeInfo().ID {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -405,9 +406,9 @@ func getSwitchIndex(switches []*p2p.Switch, peer p2p.Peer) int {
|
|||||||
//-------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------
|
||||||
// genesis
|
// genesis
|
||||||
|
|
||||||
func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []*types.PrivValidatorFS) {
|
func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []types.PrivValidator) {
|
||||||
validators := make([]types.GenesisValidator, numValidators)
|
validators := make([]types.GenesisValidator, numValidators)
|
||||||
privValidators := make([]*types.PrivValidatorFS, numValidators)
|
privValidators := make([]types.PrivValidator, numValidators)
|
||||||
for i := 0; i < numValidators; i++ {
|
for i := 0; i < numValidators; i++ {
|
||||||
val, privVal := types.RandValidator(randPower, minPower)
|
val, privVal := types.RandValidator(randPower, minPower)
|
||||||
validators[i] = types.GenesisValidator{
|
validators[i] = types.GenesisValidator{
|
||||||
@@ -425,7 +426,7 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G
|
|||||||
}, privValidators
|
}, privValidators
|
||||||
}
|
}
|
||||||
|
|
||||||
func randGenesisState(numValidators int, randPower bool, minPower int64) (sm.State, []*types.PrivValidatorFS) {
|
func randGenesisState(numValidators int, randPower bool, minPower int64) (sm.State, []types.PrivValidator) {
|
||||||
genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower)
|
genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower)
|
||||||
s0, _ := sm.MakeGenesisState(genDoc)
|
s0, _ := sm.MakeGenesisState(genDoc)
|
||||||
db := dbm.NewMemDB()
|
db := dbm.NewMemDB()
|
||||||
@@ -488,7 +489,7 @@ func newCounter() abci.Application {
|
|||||||
return counter.NewCounterApplication(true)
|
return counter.NewCounterApplication(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPersistentDummy() abci.Application {
|
func newPersistentKVStore() abci.Application {
|
||||||
dir, _ := ioutil.TempDir("/tmp", "persistent-dummy")
|
dir, _ := ioutil.TempDir("/tmp", "persistent-kvstore")
|
||||||
return dummy.NewPersistentDummyApplication(dir)
|
return kvstore.NewPersistentKVStoreApplication(dir)
|
||||||
}
|
}
|
||||||
|
@@ -108,7 +108,7 @@ func TestMempoolTxConcurrentWithCommit(t *testing.T) {
|
|||||||
ticker := time.NewTicker(time.Second * 30)
|
ticker := time.NewTicker(time.Second * 30)
|
||||||
select {
|
select {
|
||||||
case b := <-newBlockCh:
|
case b := <-newBlockCh:
|
||||||
evt := b.(types.TMEventData).Unwrap().(types.EventDataNewBlock)
|
evt := b.(types.EventDataNewBlock)
|
||||||
nTxs += int(evt.Block.Header.NumTxs)
|
nTxs += int(evt.Block.Header.NumTxs)
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
panic("Timed out waiting to commit blocks with transactions")
|
panic("Timed out waiting to commit blocks with transactions")
|
||||||
@@ -152,6 +152,7 @@ func TestMempoolRmBadTx(t *testing.T) {
|
|||||||
txs := cs.mempool.Reap(1)
|
txs := cs.mempool.Reap(1)
|
||||||
if len(txs) == 0 {
|
if len(txs) == 0 {
|
||||||
emptyMempoolCh <- struct{}{}
|
emptyMempoolCh <- struct{}{}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
time.Sleep(10 * time.Millisecond)
|
time.Sleep(10 * time.Millisecond)
|
||||||
}
|
}
|
||||||
@@ -199,7 +200,7 @@ func (app *CounterApplication) DeliverTx(tx []byte) abci.ResponseDeliverTx {
|
|||||||
Code: code.CodeTypeBadNonce,
|
Code: code.CodeTypeBadNonce,
|
||||||
Log: fmt.Sprintf("Invalid nonce. Expected %v, got %v", app.txCount, txValue)}
|
Log: fmt.Sprintf("Invalid nonce. Expected %v, got %v", app.txCount, txValue)}
|
||||||
}
|
}
|
||||||
app.txCount += 1
|
app.txCount++
|
||||||
return abci.ResponseDeliverTx{Code: code.CodeTypeOK}
|
return abci.ResponseDeliverTx{Code: code.CodeTypeOK}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +211,7 @@ func (app *CounterApplication) CheckTx(tx []byte) abci.ResponseCheckTx {
|
|||||||
Code: code.CodeTypeBadNonce,
|
Code: code.CodeTypeBadNonce,
|
||||||
Log: fmt.Sprintf("Invalid nonce. Expected %v, got %v", app.mempoolTxCount, txValue)}
|
Log: fmt.Sprintf("Invalid nonce. Expected %v, got %v", app.mempoolTxCount, txValue)}
|
||||||
}
|
}
|
||||||
app.mempoolTxCount += 1
|
app.mempoolTxCount++
|
||||||
return abci.ResponseCheckTx{Code: code.CodeTypeOK}
|
return abci.ResponseCheckTx{Code: code.CodeTypeOK}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,9 +225,8 @@ func (app *CounterApplication) Commit() abci.ResponseCommit {
|
|||||||
app.mempoolTxCount = app.txCount
|
app.mempoolTxCount = app.txCount
|
||||||
if app.txCount == 0 {
|
if app.txCount == 0 {
|
||||||
return abci.ResponseCommit{}
|
return abci.ResponseCommit{}
|
||||||
} else {
|
}
|
||||||
hash := make([]byte, 8)
|
hash := make([]byte, 8)
|
||||||
binary.BigEndian.PutUint64(hash, uint64(app.txCount))
|
binary.BigEndian.PutUint64(hash, uint64(app.txCount))
|
||||||
return abci.ResponseCommit{Data: hash}
|
return abci.ResponseCommit{Data: hash}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
package consensus
|
package consensus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
@@ -10,7 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
wire "github.com/tendermint/go-wire"
|
amino "github.com/tendermint/go-amino"
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
@@ -26,7 +25,9 @@ const (
|
|||||||
VoteChannel = byte(0x22)
|
VoteChannel = byte(0x22)
|
||||||
VoteSetBitsChannel = byte(0x23)
|
VoteSetBitsChannel = byte(0x23)
|
||||||
|
|
||||||
maxConsensusMessageSize = 1048576 // 1MB; NOTE/TODO: keep in sync with types.PartSet sizes.
|
maxMsgSize = 1048576 // 1MB; NOTE/TODO: keep in sync with types.PartSet sizes.
|
||||||
|
|
||||||
|
blocksToContributeToBecomeGoodPeer = 10000
|
||||||
)
|
)
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@@ -111,24 +112,28 @@ func (conR *ConsensusReactor) GetChannels() []*p2p.ChannelDescriptor {
|
|||||||
ID: StateChannel,
|
ID: StateChannel,
|
||||||
Priority: 5,
|
Priority: 5,
|
||||||
SendQueueCapacity: 100,
|
SendQueueCapacity: 100,
|
||||||
|
RecvMessageCapacity: maxMsgSize,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: DataChannel, // maybe split between gossiping current block and catchup stuff
|
ID: DataChannel, // maybe split between gossiping current block and catchup stuff
|
||||||
Priority: 10, // once we gossip the whole block there's nothing left to send until next height or round
|
Priority: 10, // once we gossip the whole block there's nothing left to send until next height or round
|
||||||
SendQueueCapacity: 100,
|
SendQueueCapacity: 100,
|
||||||
RecvBufferCapacity: 50 * 4096,
|
RecvBufferCapacity: 50 * 4096,
|
||||||
|
RecvMessageCapacity: maxMsgSize,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: VoteChannel,
|
ID: VoteChannel,
|
||||||
Priority: 5,
|
Priority: 5,
|
||||||
SendQueueCapacity: 100,
|
SendQueueCapacity: 100,
|
||||||
RecvBufferCapacity: 100 * 100,
|
RecvBufferCapacity: 100 * 100,
|
||||||
|
RecvMessageCapacity: maxMsgSize,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: VoteSetBitsChannel,
|
ID: VoteSetBitsChannel,
|
||||||
Priority: 1,
|
Priority: 1,
|
||||||
SendQueueCapacity: 2,
|
SendQueueCapacity: 2,
|
||||||
RecvBufferCapacity: 1024,
|
RecvBufferCapacity: 1024,
|
||||||
|
RecvMessageCapacity: maxMsgSize,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,10 +181,10 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, msg, err := DecodeMessage(msgBytes)
|
msg, err := DecodeMessage(msgBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes)
|
conR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes)
|
||||||
// TODO punish peer?
|
conR.Switch.StopPeerForError(src, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
conR.Logger.Debug("Receive", "src", src, "chId", chID, "msg", msg)
|
conR.Logger.Debug("Receive", "src", src, "chId", chID, "msg", msg)
|
||||||
@@ -222,13 +227,13 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
|||||||
conR.Logger.Error("Bad VoteSetBitsMessage field Type")
|
conR.Logger.Error("Bad VoteSetBitsMessage field Type")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
src.TrySend(VoteSetBitsChannel, struct{ ConsensusMessage }{&VoteSetBitsMessage{
|
src.TrySend(VoteSetBitsChannel, cdc.MustMarshalBinaryBare(&VoteSetBitsMessage{
|
||||||
Height: msg.Height,
|
Height: msg.Height,
|
||||||
Round: msg.Round,
|
Round: msg.Round,
|
||||||
Type: msg.Type,
|
Type: msg.Type,
|
||||||
BlockID: msg.BlockID,
|
BlockID: msg.BlockID,
|
||||||
Votes: ourVotes,
|
Votes: ourVotes,
|
||||||
}})
|
}))
|
||||||
case *ProposalHeartbeatMessage:
|
case *ProposalHeartbeatMessage:
|
||||||
hb := msg.Heartbeat
|
hb := msg.Heartbeat
|
||||||
conR.Logger.Debug("Received proposal heartbeat message",
|
conR.Logger.Debug("Received proposal heartbeat message",
|
||||||
@@ -251,6 +256,9 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
|||||||
ps.ApplyProposalPOLMessage(msg)
|
ps.ApplyProposalPOLMessage(msg)
|
||||||
case *BlockPartMessage:
|
case *BlockPartMessage:
|
||||||
ps.SetHasProposalBlockPart(msg.Height, msg.Round, msg.Part.Index)
|
ps.SetHasProposalBlockPart(msg.Height, msg.Round, msg.Part.Index)
|
||||||
|
if numBlocks := ps.RecordBlockPart(msg); numBlocks%blocksToContributeToBecomeGoodPeer == 0 {
|
||||||
|
conR.Switch.MarkPeerAsGood(src)
|
||||||
|
}
|
||||||
conR.conS.peerMsgQueue <- msgInfo{msg, src.ID()}
|
conR.conS.peerMsgQueue <- msgInfo{msg, src.ID()}
|
||||||
default:
|
default:
|
||||||
conR.Logger.Error(cmn.Fmt("Unknown message type %v", reflect.TypeOf(msg)))
|
conR.Logger.Error(cmn.Fmt("Unknown message type %v", reflect.TypeOf(msg)))
|
||||||
@@ -270,6 +278,9 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
|||||||
ps.EnsureVoteBitArrays(height, valSize)
|
ps.EnsureVoteBitArrays(height, valSize)
|
||||||
ps.EnsureVoteBitArrays(height-1, lastCommitSize)
|
ps.EnsureVoteBitArrays(height-1, lastCommitSize)
|
||||||
ps.SetHasVote(msg.Vote)
|
ps.SetHasVote(msg.Vote)
|
||||||
|
if blocks := ps.RecordVote(msg.Vote); blocks%blocksToContributeToBecomeGoodPeer == 0 {
|
||||||
|
conR.Switch.MarkPeerAsGood(src)
|
||||||
|
}
|
||||||
|
|
||||||
cs.peerMsgQueue <- msgInfo{msg, src.ID()}
|
cs.peerMsgQueue <- msgInfo{msg, src.ID()}
|
||||||
|
|
||||||
@@ -363,27 +374,33 @@ func (conR *ConsensusReactor) startBroadcastRoutine() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
var data interface{}
|
||||||
|
var ok bool
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case data, ok := <-stepsCh:
|
case data, ok = <-stepsCh:
|
||||||
if ok { // a receive from a closed channel returns the zero value immediately
|
if ok { // a receive from a closed channel returns the zero value immediately
|
||||||
edrs := data.(types.TMEventData).Unwrap().(types.EventDataRoundState)
|
edrs := data.(types.EventDataRoundState)
|
||||||
conR.broadcastNewRoundStep(edrs.RoundState.(*cstypes.RoundState))
|
conR.broadcastNewRoundStep(edrs.RoundState.(*cstypes.RoundState))
|
||||||
}
|
}
|
||||||
case data, ok := <-votesCh:
|
case data, ok = <-votesCh:
|
||||||
if ok {
|
if ok {
|
||||||
edv := data.(types.TMEventData).Unwrap().(types.EventDataVote)
|
edv := data.(types.EventDataVote)
|
||||||
conR.broadcastHasVoteMessage(edv.Vote)
|
conR.broadcastHasVoteMessage(edv.Vote)
|
||||||
}
|
}
|
||||||
case data, ok := <-heartbeatsCh:
|
case data, ok = <-heartbeatsCh:
|
||||||
if ok {
|
if ok {
|
||||||
edph := data.(types.TMEventData).Unwrap().(types.EventDataProposalHeartbeat)
|
edph := data.(types.EventDataProposalHeartbeat)
|
||||||
conR.broadcastProposalHeartbeatMessage(edph)
|
conR.broadcastProposalHeartbeatMessage(edph)
|
||||||
}
|
}
|
||||||
case <-conR.Quit():
|
case <-conR.Quit():
|
||||||
conR.eventBus.UnsubscribeAll(ctx, subscriber)
|
conR.eventBus.UnsubscribeAll(ctx, subscriber)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !ok {
|
||||||
|
conR.eventBus.UnsubscribeAll(ctx, subscriber)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -395,16 +412,16 @@ func (conR *ConsensusReactor) broadcastProposalHeartbeatMessage(heartbeat types.
|
|||||||
conR.Logger.Debug("Broadcasting proposal heartbeat message",
|
conR.Logger.Debug("Broadcasting proposal heartbeat message",
|
||||||
"height", hb.Height, "round", hb.Round, "sequence", hb.Sequence)
|
"height", hb.Height, "round", hb.Round, "sequence", hb.Sequence)
|
||||||
msg := &ProposalHeartbeatMessage{hb}
|
msg := &ProposalHeartbeatMessage{hb}
|
||||||
conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{msg})
|
conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conR *ConsensusReactor) broadcastNewRoundStep(rs *cstypes.RoundState) {
|
func (conR *ConsensusReactor) broadcastNewRoundStep(rs *cstypes.RoundState) {
|
||||||
nrsMsg, csMsg := makeRoundStepMessages(rs)
|
nrsMsg, csMsg := makeRoundStepMessages(rs)
|
||||||
if nrsMsg != nil {
|
if nrsMsg != nil {
|
||||||
conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{nrsMsg})
|
conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(nrsMsg))
|
||||||
}
|
}
|
||||||
if csMsg != nil {
|
if csMsg != nil {
|
||||||
conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{csMsg})
|
conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(csMsg))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,7 +433,7 @@ func (conR *ConsensusReactor) broadcastHasVoteMessage(vote *types.Vote) {
|
|||||||
Type: vote.Type,
|
Type: vote.Type,
|
||||||
Index: vote.ValidatorIndex,
|
Index: vote.ValidatorIndex,
|
||||||
}
|
}
|
||||||
conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{msg})
|
conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(msg))
|
||||||
/*
|
/*
|
||||||
// TODO: Make this broadcast more selective.
|
// TODO: Make this broadcast more selective.
|
||||||
for _, peer := range conR.Switch.Peers().List() {
|
for _, peer := range conR.Switch.Peers().List() {
|
||||||
@@ -456,10 +473,10 @@ func (conR *ConsensusReactor) sendNewRoundStepMessages(peer p2p.Peer) {
|
|||||||
rs := conR.conS.GetRoundState()
|
rs := conR.conS.GetRoundState()
|
||||||
nrsMsg, csMsg := makeRoundStepMessages(rs)
|
nrsMsg, csMsg := makeRoundStepMessages(rs)
|
||||||
if nrsMsg != nil {
|
if nrsMsg != nil {
|
||||||
peer.Send(StateChannel, struct{ ConsensusMessage }{nrsMsg})
|
peer.Send(StateChannel, cdc.MustMarshalBinaryBare(nrsMsg))
|
||||||
}
|
}
|
||||||
if csMsg != nil {
|
if csMsg != nil {
|
||||||
peer.Send(StateChannel, struct{ ConsensusMessage }{csMsg})
|
peer.Send(StateChannel, cdc.MustMarshalBinaryBare(csMsg))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -486,7 +503,7 @@ OUTER_LOOP:
|
|||||||
Part: part,
|
Part: part,
|
||||||
}
|
}
|
||||||
logger.Debug("Sending block part", "height", prs.Height, "round", prs.Round)
|
logger.Debug("Sending block part", "height", prs.Height, "round", prs.Round)
|
||||||
if peer.Send(DataChannel, struct{ ConsensusMessage }{msg}) {
|
if peer.Send(DataChannel, cdc.MustMarshalBinaryBare(msg)) {
|
||||||
ps.SetHasProposalBlockPart(prs.Height, prs.Round, index)
|
ps.SetHasProposalBlockPart(prs.Height, prs.Round, index)
|
||||||
}
|
}
|
||||||
continue OUTER_LOOP
|
continue OUTER_LOOP
|
||||||
@@ -530,7 +547,7 @@ OUTER_LOOP:
|
|||||||
{
|
{
|
||||||
msg := &ProposalMessage{Proposal: rs.Proposal}
|
msg := &ProposalMessage{Proposal: rs.Proposal}
|
||||||
logger.Debug("Sending proposal", "height", prs.Height, "round", prs.Round)
|
logger.Debug("Sending proposal", "height", prs.Height, "round", prs.Round)
|
||||||
if peer.Send(DataChannel, struct{ ConsensusMessage }{msg}) {
|
if peer.Send(DataChannel, cdc.MustMarshalBinaryBare(msg)) {
|
||||||
ps.SetHasProposal(rs.Proposal)
|
ps.SetHasProposal(rs.Proposal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -545,7 +562,7 @@ OUTER_LOOP:
|
|||||||
ProposalPOL: rs.Votes.Prevotes(rs.Proposal.POLRound).BitArray(),
|
ProposalPOL: rs.Votes.Prevotes(rs.Proposal.POLRound).BitArray(),
|
||||||
}
|
}
|
||||||
logger.Debug("Sending POL", "height", prs.Height, "round", prs.Round)
|
logger.Debug("Sending POL", "height", prs.Height, "round", prs.Round)
|
||||||
peer.Send(DataChannel, struct{ ConsensusMessage }{msg})
|
peer.Send(DataChannel, cdc.MustMarshalBinaryBare(msg))
|
||||||
}
|
}
|
||||||
continue OUTER_LOOP
|
continue OUTER_LOOP
|
||||||
}
|
}
|
||||||
@@ -588,17 +605,15 @@ func (conR *ConsensusReactor) gossipDataForCatchup(logger log.Logger, rs *cstype
|
|||||||
Part: part,
|
Part: part,
|
||||||
}
|
}
|
||||||
logger.Debug("Sending block part for catchup", "round", prs.Round, "index", index)
|
logger.Debug("Sending block part for catchup", "round", prs.Round, "index", index)
|
||||||
if peer.Send(DataChannel, struct{ ConsensusMessage }{msg}) {
|
if peer.Send(DataChannel, cdc.MustMarshalBinaryBare(msg)) {
|
||||||
ps.SetHasProposalBlockPart(prs.Height, prs.Round, index)
|
ps.SetHasProposalBlockPart(prs.Height, prs.Round, index)
|
||||||
} else {
|
} else {
|
||||||
logger.Debug("Sending block part for catchup failed")
|
logger.Debug("Sending block part for catchup failed")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
} else {
|
}
|
||||||
//logger.Info("No parts to send in catch-up, sleeping")
|
//logger.Info("No parts to send in catch-up, sleeping")
|
||||||
time.Sleep(conR.conS.config.PeerGossipSleep())
|
time.Sleep(conR.conS.config.PeerGossipSleep())
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conR *ConsensusReactor) gossipVotesRoutine(peer p2p.Peer, ps *PeerState) {
|
func (conR *ConsensusReactor) gossipVotesRoutine(peer p2p.Peer, ps *PeerState) {
|
||||||
@@ -727,12 +742,12 @@ OUTER_LOOP:
|
|||||||
prs := ps.GetRoundState()
|
prs := ps.GetRoundState()
|
||||||
if rs.Height == prs.Height {
|
if rs.Height == prs.Height {
|
||||||
if maj23, ok := rs.Votes.Prevotes(prs.Round).TwoThirdsMajority(); ok {
|
if maj23, ok := rs.Votes.Prevotes(prs.Round).TwoThirdsMajority(); ok {
|
||||||
peer.TrySend(StateChannel, struct{ ConsensusMessage }{&VoteSetMaj23Message{
|
peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
|
||||||
Height: prs.Height,
|
Height: prs.Height,
|
||||||
Round: prs.Round,
|
Round: prs.Round,
|
||||||
Type: types.VoteTypePrevote,
|
Type: types.VoteTypePrevote,
|
||||||
BlockID: maj23,
|
BlockID: maj23,
|
||||||
}})
|
}))
|
||||||
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
|
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -744,12 +759,12 @@ OUTER_LOOP:
|
|||||||
prs := ps.GetRoundState()
|
prs := ps.GetRoundState()
|
||||||
if rs.Height == prs.Height {
|
if rs.Height == prs.Height {
|
||||||
if maj23, ok := rs.Votes.Precommits(prs.Round).TwoThirdsMajority(); ok {
|
if maj23, ok := rs.Votes.Precommits(prs.Round).TwoThirdsMajority(); ok {
|
||||||
peer.TrySend(StateChannel, struct{ ConsensusMessage }{&VoteSetMaj23Message{
|
peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
|
||||||
Height: prs.Height,
|
Height: prs.Height,
|
||||||
Round: prs.Round,
|
Round: prs.Round,
|
||||||
Type: types.VoteTypePrecommit,
|
Type: types.VoteTypePrecommit,
|
||||||
BlockID: maj23,
|
BlockID: maj23,
|
||||||
}})
|
}))
|
||||||
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
|
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -761,12 +776,12 @@ OUTER_LOOP:
|
|||||||
prs := ps.GetRoundState()
|
prs := ps.GetRoundState()
|
||||||
if rs.Height == prs.Height && prs.ProposalPOLRound >= 0 {
|
if rs.Height == prs.Height && prs.ProposalPOLRound >= 0 {
|
||||||
if maj23, ok := rs.Votes.Prevotes(prs.ProposalPOLRound).TwoThirdsMajority(); ok {
|
if maj23, ok := rs.Votes.Prevotes(prs.ProposalPOLRound).TwoThirdsMajority(); ok {
|
||||||
peer.TrySend(StateChannel, struct{ ConsensusMessage }{&VoteSetMaj23Message{
|
peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
|
||||||
Height: prs.Height,
|
Height: prs.Height,
|
||||||
Round: prs.ProposalPOLRound,
|
Round: prs.ProposalPOLRound,
|
||||||
Type: types.VoteTypePrevote,
|
Type: types.VoteTypePrevote,
|
||||||
BlockID: maj23,
|
BlockID: maj23,
|
||||||
}})
|
}))
|
||||||
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
|
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -780,12 +795,12 @@ OUTER_LOOP:
|
|||||||
prs := ps.GetRoundState()
|
prs := ps.GetRoundState()
|
||||||
if prs.CatchupCommitRound != -1 && 0 < prs.Height && prs.Height <= conR.conS.blockStore.Height() {
|
if prs.CatchupCommitRound != -1 && 0 < prs.Height && prs.Height <= conR.conS.blockStore.Height() {
|
||||||
commit := conR.conS.LoadCommit(prs.Height)
|
commit := conR.conS.LoadCommit(prs.Height)
|
||||||
peer.TrySend(StateChannel, struct{ ConsensusMessage }{&VoteSetMaj23Message{
|
peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
|
||||||
Height: prs.Height,
|
Height: prs.Height,
|
||||||
Round: commit.Round(),
|
Round: commit.Round(),
|
||||||
Type: types.VoteTypePrecommit,
|
Type: types.VoteTypePrecommit,
|
||||||
BlockID: commit.BlockID,
|
BlockID: commit.BlockID,
|
||||||
}})
|
}))
|
||||||
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
|
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -823,14 +838,29 @@ var (
|
|||||||
ErrPeerStateInvalidStartTime = errors.New("Error peer state invalid startTime")
|
ErrPeerStateInvalidStartTime = errors.New("Error peer state invalid startTime")
|
||||||
)
|
)
|
||||||
|
|
||||||
// PeerState contains the known state of a peer, including its connection
|
// PeerState contains the known state of a peer, including its connection and
|
||||||
// and threadsafe access to its PeerRoundState.
|
// threadsafe access to its PeerRoundState.
|
||||||
type PeerState struct {
|
type PeerState struct {
|
||||||
Peer p2p.Peer
|
Peer p2p.Peer
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
|
|
||||||
mtx sync.Mutex
|
mtx sync.Mutex
|
||||||
cstypes.PeerRoundState
|
cstypes.PeerRoundState
|
||||||
|
|
||||||
|
stats *peerStateStats
|
||||||
|
}
|
||||||
|
|
||||||
|
// peerStateStats holds internal statistics for a peer.
|
||||||
|
type peerStateStats struct {
|
||||||
|
lastVoteHeight int64
|
||||||
|
votes int
|
||||||
|
|
||||||
|
lastBlockPartHeight int64
|
||||||
|
blockParts int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pss peerStateStats) String() string {
|
||||||
|
return fmt.Sprintf("peerStateStats{votes: %d, blockParts: %d}", pss.votes, pss.blockParts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPeerState returns a new PeerState for the given Peer
|
// NewPeerState returns a new PeerState for the given Peer
|
||||||
@@ -844,15 +874,18 @@ func NewPeerState(peer p2p.Peer) *PeerState {
|
|||||||
LastCommitRound: -1,
|
LastCommitRound: -1,
|
||||||
CatchupCommitRound: -1,
|
CatchupCommitRound: -1,
|
||||||
},
|
},
|
||||||
|
stats: &peerStateStats{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLogger allows to set a logger on the peer state. Returns the peer state
|
||||||
|
// itself.
|
||||||
func (ps *PeerState) SetLogger(logger log.Logger) *PeerState {
|
func (ps *PeerState) SetLogger(logger log.Logger) *PeerState {
|
||||||
ps.logger = logger
|
ps.logger = logger
|
||||||
return ps
|
return ps
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRoundState returns an atomic snapshot of the PeerRoundState.
|
// GetRoundState returns an shallow copy of the PeerRoundState.
|
||||||
// There's no point in mutating it since it won't change PeerState.
|
// There's no point in mutating it since it won't change PeerState.
|
||||||
func (ps *PeerState) GetRoundState() *cstypes.PeerRoundState {
|
func (ps *PeerState) GetRoundState() *cstypes.PeerRoundState {
|
||||||
ps.mtx.Lock()
|
ps.mtx.Lock()
|
||||||
@@ -862,6 +895,14 @@ func (ps *PeerState) GetRoundState() *cstypes.PeerRoundState {
|
|||||||
return &prs
|
return &prs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRoundStateJSON returns a json of PeerRoundState, marshalled using go-amino.
|
||||||
|
func (ps *PeerState) GetRoundStateJSON() ([]byte, error) {
|
||||||
|
ps.mtx.Lock()
|
||||||
|
defer ps.mtx.Unlock()
|
||||||
|
|
||||||
|
return cdc.MarshalJSON(ps.PeerRoundState)
|
||||||
|
}
|
||||||
|
|
||||||
// GetHeight returns an atomic snapshot of the PeerRoundState's height
|
// GetHeight returns an atomic snapshot of the PeerRoundState's height
|
||||||
// used by the mempool to ensure peers are caught up before broadcasting new txs
|
// used by the mempool to ensure peers are caught up before broadcasting new txs
|
||||||
func (ps *PeerState) GetHeight() int64 {
|
func (ps *PeerState) GetHeight() int64 {
|
||||||
@@ -920,7 +961,7 @@ func (ps *PeerState) PickSendVote(votes types.VoteSetReader) bool {
|
|||||||
if vote, ok := ps.PickVoteToSend(votes); ok {
|
if vote, ok := ps.PickVoteToSend(votes); ok {
|
||||||
msg := &VoteMessage{vote}
|
msg := &VoteMessage{vote}
|
||||||
ps.logger.Debug("Sending vote message", "ps", ps, "vote", vote)
|
ps.logger.Debug("Sending vote message", "ps", ps, "vote", vote)
|
||||||
return ps.Peer.Send(VoteChannel, struct{ ConsensusMessage }{msg})
|
return ps.Peer.Send(VoteChannel, cdc.MustMarshalBinaryBare(msg))
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -1024,7 +1065,7 @@ func (ps *PeerState) ensureCatchupCommitRound(height int64, round int, numValida
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnsureVoteVitArrays ensures the bit-arrays have been allocated for tracking
|
// EnsureVoteBitArrays ensures the bit-arrays have been allocated for tracking
|
||||||
// what votes this peer has received.
|
// what votes this peer has received.
|
||||||
// NOTE: It's important to make sure that numValidators actually matches
|
// NOTE: It's important to make sure that numValidators actually matches
|
||||||
// what the node sees as the number of validators for height.
|
// what the node sees as the number of validators for height.
|
||||||
@@ -1055,6 +1096,56 @@ func (ps *PeerState) ensureVoteBitArrays(height int64, numValidators int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecordVote updates internal statistics for this peer by recording the vote.
|
||||||
|
// It returns the total number of votes (1 per block). This essentially means
|
||||||
|
// the number of blocks for which peer has been sending us votes.
|
||||||
|
func (ps *PeerState) RecordVote(vote *types.Vote) int {
|
||||||
|
ps.mtx.Lock()
|
||||||
|
defer ps.mtx.Unlock()
|
||||||
|
|
||||||
|
if ps.stats.lastVoteHeight >= vote.Height {
|
||||||
|
return ps.stats.votes
|
||||||
|
}
|
||||||
|
ps.stats.lastVoteHeight = vote.Height
|
||||||
|
ps.stats.votes++
|
||||||
|
return ps.stats.votes
|
||||||
|
}
|
||||||
|
|
||||||
|
// VotesSent returns the number of blocks for which peer has been sending us
|
||||||
|
// votes.
|
||||||
|
func (ps *PeerState) VotesSent() int {
|
||||||
|
ps.mtx.Lock()
|
||||||
|
defer ps.mtx.Unlock()
|
||||||
|
|
||||||
|
return ps.stats.votes
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecordBlockPart updates internal statistics for this peer by recording the
|
||||||
|
// block part. It returns the total number of block parts (1 per block). This
|
||||||
|
// essentially means the number of blocks for which peer has been sending us
|
||||||
|
// block parts.
|
||||||
|
func (ps *PeerState) RecordBlockPart(bp *BlockPartMessage) int {
|
||||||
|
ps.mtx.Lock()
|
||||||
|
defer ps.mtx.Unlock()
|
||||||
|
|
||||||
|
if ps.stats.lastBlockPartHeight >= bp.Height {
|
||||||
|
return ps.stats.blockParts
|
||||||
|
}
|
||||||
|
|
||||||
|
ps.stats.lastBlockPartHeight = bp.Height
|
||||||
|
ps.stats.blockParts++
|
||||||
|
return ps.stats.blockParts
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockPartsSent returns the number of blocks for which peer has been sending
|
||||||
|
// us block parts.
|
||||||
|
func (ps *PeerState) BlockPartsSent() int {
|
||||||
|
ps.mtx.Lock()
|
||||||
|
defer ps.mtx.Unlock()
|
||||||
|
|
||||||
|
return ps.stats.blockParts
|
||||||
|
}
|
||||||
|
|
||||||
// SetHasVote sets the given vote as known by the peer
|
// SetHasVote sets the given vote as known by the peer
|
||||||
func (ps *PeerState) SetHasVote(vote *types.Vote) {
|
func (ps *PeerState) SetHasVote(vote *types.Vote) {
|
||||||
ps.mtx.Lock()
|
ps.mtx.Lock()
|
||||||
@@ -1203,54 +1294,41 @@ func (ps *PeerState) StringIndented(indent string) string {
|
|||||||
return fmt.Sprintf(`PeerState{
|
return fmt.Sprintf(`PeerState{
|
||||||
%s Key %v
|
%s Key %v
|
||||||
%s PRS %v
|
%s PRS %v
|
||||||
|
%s Stats %v
|
||||||
%s}`,
|
%s}`,
|
||||||
indent, ps.Peer.ID(),
|
indent, ps.Peer.ID(),
|
||||||
indent, ps.PeerRoundState.StringIndented(indent+" "),
|
indent, ps.PeerRoundState.StringIndented(indent+" "),
|
||||||
|
indent, ps.stats,
|
||||||
indent)
|
indent)
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Messages
|
// Messages
|
||||||
|
|
||||||
const (
|
|
||||||
msgTypeNewRoundStep = byte(0x01)
|
|
||||||
msgTypeCommitStep = byte(0x02)
|
|
||||||
msgTypeProposal = byte(0x11)
|
|
||||||
msgTypeProposalPOL = byte(0x12)
|
|
||||||
msgTypeBlockPart = byte(0x13) // both block & POL
|
|
||||||
msgTypeVote = byte(0x14)
|
|
||||||
msgTypeHasVote = byte(0x15)
|
|
||||||
msgTypeVoteSetMaj23 = byte(0x16)
|
|
||||||
msgTypeVoteSetBits = byte(0x17)
|
|
||||||
|
|
||||||
msgTypeProposalHeartbeat = byte(0x20)
|
|
||||||
)
|
|
||||||
|
|
||||||
// ConsensusMessage is a message that can be sent and received on the ConsensusReactor
|
// ConsensusMessage is a message that can be sent and received on the ConsensusReactor
|
||||||
type ConsensusMessage interface{}
|
type ConsensusMessage interface{}
|
||||||
|
|
||||||
var _ = wire.RegisterInterface(
|
func RegisterConsensusMessages(cdc *amino.Codec) {
|
||||||
struct{ ConsensusMessage }{},
|
cdc.RegisterInterface((*ConsensusMessage)(nil), nil)
|
||||||
wire.ConcreteType{&NewRoundStepMessage{}, msgTypeNewRoundStep},
|
cdc.RegisterConcrete(&NewRoundStepMessage{}, "tendermint/NewRoundStepMessage", nil)
|
||||||
wire.ConcreteType{&CommitStepMessage{}, msgTypeCommitStep},
|
cdc.RegisterConcrete(&CommitStepMessage{}, "tendermint/CommitStep", nil)
|
||||||
wire.ConcreteType{&ProposalMessage{}, msgTypeProposal},
|
cdc.RegisterConcrete(&ProposalMessage{}, "tendermint/Proposal", nil)
|
||||||
wire.ConcreteType{&ProposalPOLMessage{}, msgTypeProposalPOL},
|
cdc.RegisterConcrete(&ProposalPOLMessage{}, "tendermint/ProposalPOL", nil)
|
||||||
wire.ConcreteType{&BlockPartMessage{}, msgTypeBlockPart},
|
cdc.RegisterConcrete(&BlockPartMessage{}, "tendermint/BlockPart", nil)
|
||||||
wire.ConcreteType{&VoteMessage{}, msgTypeVote},
|
cdc.RegisterConcrete(&VoteMessage{}, "tendermint/Vote", nil)
|
||||||
wire.ConcreteType{&HasVoteMessage{}, msgTypeHasVote},
|
cdc.RegisterConcrete(&HasVoteMessage{}, "tendermint/HasVote", nil)
|
||||||
wire.ConcreteType{&VoteSetMaj23Message{}, msgTypeVoteSetMaj23},
|
cdc.RegisterConcrete(&VoteSetMaj23Message{}, "tendermint/VoteSetMaj23", nil)
|
||||||
wire.ConcreteType{&VoteSetBitsMessage{}, msgTypeVoteSetBits},
|
cdc.RegisterConcrete(&VoteSetBitsMessage{}, "tendermint/VoteSetBits", nil)
|
||||||
wire.ConcreteType{&ProposalHeartbeatMessage{}, msgTypeProposalHeartbeat},
|
cdc.RegisterConcrete(&ProposalHeartbeatMessage{}, "tendermint/ProposalHeartbeat", nil)
|
||||||
)
|
}
|
||||||
|
|
||||||
// DecodeMessage decodes the given bytes into a ConsensusMessage.
|
// DecodeMessage decodes the given bytes into a ConsensusMessage.
|
||||||
// TODO: check for unnecessary extra bytes at the end.
|
func DecodeMessage(bz []byte) (msg ConsensusMessage, err error) {
|
||||||
func DecodeMessage(bz []byte) (msgType byte, msg ConsensusMessage, err error) {
|
if len(bz) > maxMsgSize {
|
||||||
msgType = bz[0]
|
return msg, fmt.Errorf("Msg exceeds max size (%d > %d)",
|
||||||
n := new(int)
|
len(bz), maxMsgSize)
|
||||||
r := bytes.NewReader(bz)
|
}
|
||||||
msgI := wire.ReadBinary(struct{ ConsensusMessage }{}, r, maxConsensusMessageSize, n, &err)
|
err = cdc.UnmarshalBinaryBare(bz, &msg)
|
||||||
msg = msgI.(struct{ ConsensusMessage }).ConsensusMessage
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,11 +10,13 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tendermint/abci/example/dummy"
|
"github.com/tendermint/abci/example/kvstore"
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
cfg "github.com/tendermint/tendermint/config"
|
cfg "github.com/tendermint/tendermint/config"
|
||||||
"github.com/tendermint/tendermint/p2p"
|
"github.com/tendermint/tendermint/p2p"
|
||||||
|
p2pdummy "github.com/tendermint/tendermint/p2p/dummy"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -121,13 +123,119 @@ func TestReactorProposalHeartbeats(t *testing.T) {
|
|||||||
}, css)
|
}, css)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test we record block parts from other peers
|
||||||
|
func TestReactorRecordsBlockParts(t *testing.T) {
|
||||||
|
// create dummy peer
|
||||||
|
peer := p2pdummy.NewPeer()
|
||||||
|
ps := NewPeerState(peer).SetLogger(log.TestingLogger())
|
||||||
|
peer.Set(types.PeerStateKey, ps)
|
||||||
|
|
||||||
|
// create reactor
|
||||||
|
css := randConsensusNet(1, "consensus_reactor_records_block_parts_test", newMockTickerFunc(true), newPersistentKVStore)
|
||||||
|
reactor := NewConsensusReactor(css[0], false) // so we dont start the consensus states
|
||||||
|
reactor.SetEventBus(css[0].eventBus)
|
||||||
|
reactor.SetLogger(log.TestingLogger())
|
||||||
|
sw := p2p.MakeSwitch(cfg.DefaultP2PConfig(), 1, "testing", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch { return sw })
|
||||||
|
reactor.SetSwitch(sw)
|
||||||
|
err := reactor.Start()
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer reactor.Stop()
|
||||||
|
|
||||||
|
// 1) new block part
|
||||||
|
parts := types.NewPartSetFromData(cmn.RandBytes(100), 10)
|
||||||
|
msg := &BlockPartMessage{
|
||||||
|
Height: 2,
|
||||||
|
Round: 0,
|
||||||
|
Part: parts.GetPart(0),
|
||||||
|
}
|
||||||
|
bz, err := cdc.MarshalBinaryBare(msg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
reactor.Receive(DataChannel, peer, bz)
|
||||||
|
require.Equal(t, 1, ps.BlockPartsSent(), "number of block parts sent should have increased by 1")
|
||||||
|
|
||||||
|
// 2) block part with the same height, but different round
|
||||||
|
msg.Round = 1
|
||||||
|
|
||||||
|
bz, err = cdc.MarshalBinaryBare(msg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
reactor.Receive(DataChannel, peer, bz)
|
||||||
|
require.Equal(t, 1, ps.BlockPartsSent(), "number of block parts sent should stay the same")
|
||||||
|
|
||||||
|
// 3) block part from earlier height
|
||||||
|
msg.Height = 1
|
||||||
|
msg.Round = 0
|
||||||
|
|
||||||
|
bz, err = cdc.MarshalBinaryBare(msg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
reactor.Receive(DataChannel, peer, bz)
|
||||||
|
require.Equal(t, 1, ps.BlockPartsSent(), "number of block parts sent should stay the same")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test we record votes from other peers
|
||||||
|
func TestReactorRecordsVotes(t *testing.T) {
|
||||||
|
// create dummy peer
|
||||||
|
peer := p2pdummy.NewPeer()
|
||||||
|
ps := NewPeerState(peer).SetLogger(log.TestingLogger())
|
||||||
|
peer.Set(types.PeerStateKey, ps)
|
||||||
|
|
||||||
|
// create reactor
|
||||||
|
css := randConsensusNet(1, "consensus_reactor_records_votes_test", newMockTickerFunc(true), newPersistentKVStore)
|
||||||
|
reactor := NewConsensusReactor(css[0], false) // so we dont start the consensus states
|
||||||
|
reactor.SetEventBus(css[0].eventBus)
|
||||||
|
reactor.SetLogger(log.TestingLogger())
|
||||||
|
sw := p2p.MakeSwitch(cfg.DefaultP2PConfig(), 1, "testing", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch { return sw })
|
||||||
|
reactor.SetSwitch(sw)
|
||||||
|
err := reactor.Start()
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer reactor.Stop()
|
||||||
|
_, val := css[0].state.Validators.GetByIndex(0)
|
||||||
|
|
||||||
|
// 1) new vote
|
||||||
|
vote := &types.Vote{
|
||||||
|
ValidatorIndex: 0,
|
||||||
|
ValidatorAddress: val.Address,
|
||||||
|
Height: 2,
|
||||||
|
Round: 0,
|
||||||
|
Timestamp: time.Now().UTC(),
|
||||||
|
Type: types.VoteTypePrevote,
|
||||||
|
BlockID: types.BlockID{},
|
||||||
|
}
|
||||||
|
bz, err := cdc.MarshalBinaryBare(&VoteMessage{vote})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
reactor.Receive(VoteChannel, peer, bz)
|
||||||
|
assert.Equal(t, 1, ps.VotesSent(), "number of votes sent should have increased by 1")
|
||||||
|
|
||||||
|
// 2) vote with the same height, but different round
|
||||||
|
vote.Round = 1
|
||||||
|
|
||||||
|
bz, err = cdc.MarshalBinaryBare(&VoteMessage{vote})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
reactor.Receive(VoteChannel, peer, bz)
|
||||||
|
assert.Equal(t, 1, ps.VotesSent(), "number of votes sent should stay the same")
|
||||||
|
|
||||||
|
// 3) vote from earlier height
|
||||||
|
vote.Height = 1
|
||||||
|
vote.Round = 0
|
||||||
|
|
||||||
|
bz, err = cdc.MarshalBinaryBare(&VoteMessage{vote})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
reactor.Receive(VoteChannel, peer, bz)
|
||||||
|
assert.Equal(t, 1, ps.VotesSent(), "number of votes sent should stay the same")
|
||||||
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------
|
//-------------------------------------------------------------
|
||||||
// ensure we can make blocks despite cycling a validator set
|
// ensure we can make blocks despite cycling a validator set
|
||||||
|
|
||||||
func TestReactorVotingPowerChange(t *testing.T) {
|
func TestReactorVotingPowerChange(t *testing.T) {
|
||||||
nVals := 4
|
nVals := 4
|
||||||
logger := log.TestingLogger()
|
logger := log.TestingLogger()
|
||||||
css := randConsensusNet(nVals, "consensus_voting_power_changes_test", newMockTickerFunc(true), newPersistentDummy)
|
css := randConsensusNet(nVals, "consensus_voting_power_changes_test", newMockTickerFunc(true), newPersistentKVStore)
|
||||||
reactors, eventChans, eventBuses := startConsensusNet(t, css, nVals)
|
reactors, eventChans, eventBuses := startConsensusNet(t, css, nVals)
|
||||||
defer stopConsensusNet(logger, reactors, eventBuses)
|
defer stopConsensusNet(logger, reactors, eventBuses)
|
||||||
|
|
||||||
@@ -146,7 +254,7 @@ func TestReactorVotingPowerChange(t *testing.T) {
|
|||||||
logger.Debug("---------------------------- Testing changing the voting power of one validator a few times")
|
logger.Debug("---------------------------- Testing changing the voting power of one validator a few times")
|
||||||
|
|
||||||
val1PubKey := css[0].privValidator.GetPubKey()
|
val1PubKey := css[0].privValidator.GetPubKey()
|
||||||
updateValidatorTx := dummy.MakeValSetChangeTx(val1PubKey.Bytes(), 25)
|
updateValidatorTx := kvstore.MakeValSetChangeTx(val1PubKey.Bytes(), 25)
|
||||||
previousTotalVotingPower := css[0].GetRoundState().LastValidators.TotalVotingPower()
|
previousTotalVotingPower := css[0].GetRoundState().LastValidators.TotalVotingPower()
|
||||||
|
|
||||||
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx)
|
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx)
|
||||||
@@ -158,7 +266,7 @@ func TestReactorVotingPowerChange(t *testing.T) {
|
|||||||
t.Fatalf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[0].GetRoundState().LastValidators.TotalVotingPower())
|
t.Fatalf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[0].GetRoundState().LastValidators.TotalVotingPower())
|
||||||
}
|
}
|
||||||
|
|
||||||
updateValidatorTx = dummy.MakeValSetChangeTx(val1PubKey.Bytes(), 2)
|
updateValidatorTx = kvstore.MakeValSetChangeTx(val1PubKey.Bytes(), 2)
|
||||||
previousTotalVotingPower = css[0].GetRoundState().LastValidators.TotalVotingPower()
|
previousTotalVotingPower = css[0].GetRoundState().LastValidators.TotalVotingPower()
|
||||||
|
|
||||||
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx)
|
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx)
|
||||||
@@ -170,7 +278,7 @@ func TestReactorVotingPowerChange(t *testing.T) {
|
|||||||
t.Fatalf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[0].GetRoundState().LastValidators.TotalVotingPower())
|
t.Fatalf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[0].GetRoundState().LastValidators.TotalVotingPower())
|
||||||
}
|
}
|
||||||
|
|
||||||
updateValidatorTx = dummy.MakeValSetChangeTx(val1PubKey.Bytes(), 26)
|
updateValidatorTx = kvstore.MakeValSetChangeTx(val1PubKey.Bytes(), 26)
|
||||||
previousTotalVotingPower = css[0].GetRoundState().LastValidators.TotalVotingPower()
|
previousTotalVotingPower = css[0].GetRoundState().LastValidators.TotalVotingPower()
|
||||||
|
|
||||||
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx)
|
waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx)
|
||||||
@@ -186,7 +294,7 @@ func TestReactorVotingPowerChange(t *testing.T) {
|
|||||||
func TestReactorValidatorSetChanges(t *testing.T) {
|
func TestReactorValidatorSetChanges(t *testing.T) {
|
||||||
nPeers := 7
|
nPeers := 7
|
||||||
nVals := 4
|
nVals := 4
|
||||||
css := randConsensusNetWithPeers(nVals, nPeers, "consensus_val_set_changes_test", newMockTickerFunc(true), newPersistentDummy)
|
css := randConsensusNetWithPeers(nVals, nPeers, "consensus_val_set_changes_test", newMockTickerFunc(true), newPersistentKVStore)
|
||||||
|
|
||||||
logger := log.TestingLogger()
|
logger := log.TestingLogger()
|
||||||
|
|
||||||
@@ -208,7 +316,7 @@ func TestReactorValidatorSetChanges(t *testing.T) {
|
|||||||
logger.Info("---------------------------- Testing adding one validator")
|
logger.Info("---------------------------- Testing adding one validator")
|
||||||
|
|
||||||
newValidatorPubKey1 := css[nVals].privValidator.GetPubKey()
|
newValidatorPubKey1 := css[nVals].privValidator.GetPubKey()
|
||||||
newValidatorTx1 := dummy.MakeValSetChangeTx(newValidatorPubKey1.Bytes(), testMinPower)
|
newValidatorTx1 := kvstore.MakeValSetChangeTx(newValidatorPubKey1.Bytes(), testMinPower)
|
||||||
|
|
||||||
// wait till everyone makes block 2
|
// wait till everyone makes block 2
|
||||||
// ensure the commit includes all validators
|
// ensure the commit includes all validators
|
||||||
@@ -234,7 +342,7 @@ func TestReactorValidatorSetChanges(t *testing.T) {
|
|||||||
logger.Info("---------------------------- Testing changing the voting power of one validator")
|
logger.Info("---------------------------- Testing changing the voting power of one validator")
|
||||||
|
|
||||||
updateValidatorPubKey1 := css[nVals].privValidator.GetPubKey()
|
updateValidatorPubKey1 := css[nVals].privValidator.GetPubKey()
|
||||||
updateValidatorTx1 := dummy.MakeValSetChangeTx(updateValidatorPubKey1.Bytes(), 25)
|
updateValidatorTx1 := kvstore.MakeValSetChangeTx(updateValidatorPubKey1.Bytes(), 25)
|
||||||
previousTotalVotingPower := css[nVals].GetRoundState().LastValidators.TotalVotingPower()
|
previousTotalVotingPower := css[nVals].GetRoundState().LastValidators.TotalVotingPower()
|
||||||
|
|
||||||
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, updateValidatorTx1)
|
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, updateValidatorTx1)
|
||||||
@@ -250,10 +358,10 @@ func TestReactorValidatorSetChanges(t *testing.T) {
|
|||||||
logger.Info("---------------------------- Testing adding two validators at once")
|
logger.Info("---------------------------- Testing adding two validators at once")
|
||||||
|
|
||||||
newValidatorPubKey2 := css[nVals+1].privValidator.GetPubKey()
|
newValidatorPubKey2 := css[nVals+1].privValidator.GetPubKey()
|
||||||
newValidatorTx2 := dummy.MakeValSetChangeTx(newValidatorPubKey2.Bytes(), testMinPower)
|
newValidatorTx2 := kvstore.MakeValSetChangeTx(newValidatorPubKey2.Bytes(), testMinPower)
|
||||||
|
|
||||||
newValidatorPubKey3 := css[nVals+2].privValidator.GetPubKey()
|
newValidatorPubKey3 := css[nVals+2].privValidator.GetPubKey()
|
||||||
newValidatorTx3 := dummy.MakeValSetChangeTx(newValidatorPubKey3.Bytes(), testMinPower)
|
newValidatorTx3 := kvstore.MakeValSetChangeTx(newValidatorPubKey3.Bytes(), testMinPower)
|
||||||
|
|
||||||
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, newValidatorTx2, newValidatorTx3)
|
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, newValidatorTx2, newValidatorTx3)
|
||||||
waitForAndValidateBlockWithTx(t, nPeers, activeVals, eventChans, css, newValidatorTx2, newValidatorTx3)
|
waitForAndValidateBlockWithTx(t, nPeers, activeVals, eventChans, css, newValidatorTx2, newValidatorTx3)
|
||||||
@@ -265,8 +373,8 @@ func TestReactorValidatorSetChanges(t *testing.T) {
|
|||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
logger.Info("---------------------------- Testing removing two validators at once")
|
logger.Info("---------------------------- Testing removing two validators at once")
|
||||||
|
|
||||||
removeValidatorTx2 := dummy.MakeValSetChangeTx(newValidatorPubKey2.Bytes(), 0)
|
removeValidatorTx2 := kvstore.MakeValSetChangeTx(newValidatorPubKey2.Bytes(), 0)
|
||||||
removeValidatorTx3 := dummy.MakeValSetChangeTx(newValidatorPubKey3.Bytes(), 0)
|
removeValidatorTx3 := kvstore.MakeValSetChangeTx(newValidatorPubKey3.Bytes(), 0)
|
||||||
|
|
||||||
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, removeValidatorTx2, removeValidatorTx3)
|
waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, removeValidatorTx2, removeValidatorTx3)
|
||||||
waitForAndValidateBlockWithTx(t, nPeers, activeVals, eventChans, css, removeValidatorTx2, removeValidatorTx3)
|
waitForAndValidateBlockWithTx(t, nPeers, activeVals, eventChans, css, removeValidatorTx2, removeValidatorTx3)
|
||||||
@@ -301,7 +409,7 @@ func waitForAndValidateBlock(t *testing.T, n int, activeVals map[string]struct{}
|
|||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
newBlock := newBlockI.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block
|
newBlock := newBlockI.(types.EventDataNewBlock).Block
|
||||||
css[j].Logger.Debug("waitForAndValidateBlock: Got block", "height", newBlock.Height)
|
css[j].Logger.Debug("waitForAndValidateBlock: Got block", "height", newBlock.Height)
|
||||||
err := validateBlock(newBlock, activeVals)
|
err := validateBlock(newBlock, activeVals)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
@@ -322,7 +430,7 @@ func waitForAndValidateBlockWithTx(t *testing.T, n int, activeVals map[string]st
|
|||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
newBlock := newBlockI.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block
|
newBlock := newBlockI.(types.EventDataNewBlock).Block
|
||||||
css[j].Logger.Debug("waitForAndValidateBlockWithTx: Got block", "height", newBlock.Height)
|
css[j].Logger.Debug("waitForAndValidateBlockWithTx: Got block", "height", newBlock.Height)
|
||||||
err := validateBlock(newBlock, activeVals)
|
err := validateBlock(newBlock, activeVals)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
@@ -332,7 +440,7 @@ func waitForAndValidateBlockWithTx(t *testing.T, n int, activeVals map[string]st
|
|||||||
// but they should be in order.
|
// but they should be in order.
|
||||||
for _, tx := range newBlock.Data.Txs {
|
for _, tx := range newBlock.Data.Txs {
|
||||||
assert.EqualValues(t, txs[ntxs], tx)
|
assert.EqualValues(t, txs[ntxs], tx)
|
||||||
ntxs += 1
|
ntxs++
|
||||||
}
|
}
|
||||||
|
|
||||||
if ntxs == len(txs) {
|
if ntxs == len(txs) {
|
||||||
@@ -354,7 +462,7 @@ func waitForBlockWithUpdatedValsAndValidateIt(t *testing.T, n int, updatedVals m
|
|||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
newBlock = newBlockI.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block
|
newBlock = newBlockI.(types.EventDataNewBlock).Block
|
||||||
if newBlock.LastCommit.Size() == len(updatedVals) {
|
if newBlock.LastCommit.Size() == len(updatedVals) {
|
||||||
css[j].Logger.Debug("waitForBlockWithUpdatedValsAndValidateIt: Got block", "height", newBlock.Height)
|
css[j].Logger.Debug("waitForBlockWithUpdatedValsAndValidateIt: Got block", "height", newBlock.Height)
|
||||||
break LOOP
|
break LOOP
|
||||||
|
@@ -2,6 +2,7 @@ package consensus
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
"io"
|
"io"
|
||||||
@@ -111,7 +112,7 @@ func (cs *ConsensusState) catchupReplay(csHeight int64) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if found {
|
if found {
|
||||||
return fmt.Errorf("WAL should not contain #ENDHEIGHT %d.", csHeight)
|
return fmt.Errorf("WAL should not contain #ENDHEIGHT %d", csHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search for last height marker
|
// Search for last height marker
|
||||||
@@ -124,7 +125,7 @@ func (cs *ConsensusState) catchupReplay(csHeight int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
return fmt.Errorf("Cannot replay height %d. WAL does not contain #ENDHEIGHT for %d.", csHeight, csHeight-1)
|
return fmt.Errorf("Cannot replay height %d. WAL does not contain #ENDHEIGHT for %d", csHeight, csHeight-1)
|
||||||
}
|
}
|
||||||
defer gr.Close() // nolint: errcheck
|
defer gr.Close() // nolint: errcheck
|
||||||
|
|
||||||
@@ -190,13 +191,23 @@ type Handshaker struct {
|
|||||||
stateDB dbm.DB
|
stateDB dbm.DB
|
||||||
initialState sm.State
|
initialState sm.State
|
||||||
store types.BlockStore
|
store types.BlockStore
|
||||||
|
appState json.RawMessage
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
|
|
||||||
nBlocks int // number of blocks applied to the state
|
nBlocks int // number of blocks applied to the state
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandshaker(stateDB dbm.DB, state sm.State, store types.BlockStore) *Handshaker {
|
func NewHandshaker(stateDB dbm.DB, state sm.State,
|
||||||
return &Handshaker{stateDB, state, store, log.NewNopLogger(), 0}
|
store types.BlockStore, appState json.RawMessage) *Handshaker {
|
||||||
|
|
||||||
|
return &Handshaker{
|
||||||
|
stateDB: stateDB,
|
||||||
|
initialState: state,
|
||||||
|
store: store,
|
||||||
|
appState: appState,
|
||||||
|
logger: log.NewNopLogger(),
|
||||||
|
nBlocks: 0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handshaker) SetLogger(l log.Logger) {
|
func (h *Handshaker) SetLogger(l log.Logger) {
|
||||||
@@ -249,9 +260,12 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
|
|||||||
// If appBlockHeight == 0 it means that we are at genesis and hence should send InitChain
|
// If appBlockHeight == 0 it means that we are at genesis and hence should send InitChain
|
||||||
if appBlockHeight == 0 {
|
if appBlockHeight == 0 {
|
||||||
validators := types.TM2PB.Validators(state.Validators)
|
validators := types.TM2PB.Validators(state.Validators)
|
||||||
// TODO: get the genesis bytes (https://github.com/tendermint/tendermint/issues/1224)
|
req := abci.RequestInitChain{
|
||||||
var genesisBytes []byte
|
Validators: validators,
|
||||||
if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{validators, genesisBytes}); err != nil {
|
AppStateBytes: h.appState,
|
||||||
|
}
|
||||||
|
_, err := proxyApp.Consensus().InitChainSync(req)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,7 +352,7 @@ func (h *Handshaker) replayBlocks(state sm.State, proxyApp proxy.AppConns, appBl
|
|||||||
var err error
|
var err error
|
||||||
finalBlock := storeBlockHeight
|
finalBlock := storeBlockHeight
|
||||||
if mutateState {
|
if mutateState {
|
||||||
finalBlock -= 1
|
finalBlock--
|
||||||
}
|
}
|
||||||
for i := appBlockHeight + 1; i <= finalBlock; i++ {
|
for i := appBlockHeight + 1; i <= finalBlock; i++ {
|
||||||
h.logger.Info("Applying block", "height", i)
|
h.logger.Info("Applying block", "height", i)
|
||||||
@@ -348,7 +362,7 @@ func (h *Handshaker) replayBlocks(state sm.State, proxyApp proxy.AppConns, appBl
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
h.nBlocks += 1
|
h.nBlocks++
|
||||||
}
|
}
|
||||||
|
|
||||||
if mutateState {
|
if mutateState {
|
||||||
@@ -376,7 +390,7 @@ func (h *Handshaker) replayBlock(state sm.State, height int64, proxyApp proxy.Ap
|
|||||||
return sm.State{}, err
|
return sm.State{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
h.nBlocks += 1
|
h.nBlocks++
|
||||||
|
|
||||||
return state, nil
|
return state, nil
|
||||||
}
|
}
|
||||||
@@ -415,7 +429,7 @@ type mockProxyApp struct {
|
|||||||
|
|
||||||
func (mock *mockProxyApp) DeliverTx(tx []byte) abci.ResponseDeliverTx {
|
func (mock *mockProxyApp) DeliverTx(tx []byte) abci.ResponseDeliverTx {
|
||||||
r := mock.abciResponses.DeliverTx[mock.txCount]
|
r := mock.abciResponses.DeliverTx[mock.txCount]
|
||||||
mock.txCount += 1
|
mock.txCount++
|
||||||
return *r
|
return *r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -87,9 +87,9 @@ func (cs *ConsensusState) ReplayFile(file string, console bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if nextN > 0 {
|
if nextN > 0 {
|
||||||
nextN -= 1
|
nextN--
|
||||||
}
|
}
|
||||||
pb.count += 1
|
pb.count++
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -153,7 +153,7 @@ func (pb *playback) replayReset(count int, newStepCh chan interface{}) error {
|
|||||||
if err := pb.cs.readReplayMessage(msg, newStepCh); err != nil {
|
if err := pb.cs.readReplayMessage(msg, newStepCh); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
pb.count += 1
|
pb.count++
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -197,14 +197,13 @@ func (pb *playback) replayConsoleLoop() int {
|
|||||||
|
|
||||||
if len(tokens) == 1 {
|
if len(tokens) == 1 {
|
||||||
return 0
|
return 0
|
||||||
} else {
|
}
|
||||||
i, err := strconv.Atoi(tokens[1])
|
i, err := strconv.Atoi(tokens[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("next takes an integer argument")
|
fmt.Println("next takes an integer argument")
|
||||||
} else {
|
} else {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
case "back":
|
case "back":
|
||||||
// "back" -> go back one message
|
// "back" -> go back one message
|
||||||
@@ -287,14 +286,19 @@ func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusCo
|
|||||||
|
|
||||||
// Get State
|
// Get State
|
||||||
stateDB := dbm.NewDB("state", dbType, config.DBDir())
|
stateDB := dbm.NewDB("state", dbType, config.DBDir())
|
||||||
state, err := sm.MakeGenesisStateFromFile(config.GenesisFile())
|
gdoc, err := sm.MakeGenesisDocFromFile(config.GenesisFile())
|
||||||
|
if err != nil {
|
||||||
|
cmn.Exit(err.Error())
|
||||||
|
}
|
||||||
|
state, err := sm.MakeGenesisState(gdoc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmn.Exit(err.Error())
|
cmn.Exit(err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create proxyAppConn connection (consensus, mempool, query)
|
// Create proxyAppConn connection (consensus, mempool, query)
|
||||||
clientCreator := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir())
|
clientCreator := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir())
|
||||||
proxyApp := proxy.NewAppConns(clientCreator, NewHandshaker(stateDB, state, blockStore))
|
proxyApp := proxy.NewAppConns(clientCreator,
|
||||||
|
NewHandshaker(stateDB, state, blockStore, gdoc.AppState()))
|
||||||
err = proxyApp.Start()
|
err = proxyApp.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmn.Exit(cmn.Fmt("Error starting proxy app conns: %v", err))
|
cmn.Exit(cmn.Fmt("Error starting proxy app conns: %v", err))
|
||||||
|
@@ -15,10 +15,9 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/tendermint/abci/example/dummy"
|
"github.com/tendermint/abci/example/kvstore"
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
crypto "github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
wire "github.com/tendermint/go-wire"
|
|
||||||
auto "github.com/tendermint/tmlibs/autofile"
|
auto "github.com/tendermint/tmlibs/autofile"
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
dbm "github.com/tendermint/tmlibs/db"
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
@@ -27,6 +26,7 @@ import (
|
|||||||
"github.com/tendermint/tendermint/proxy"
|
"github.com/tendermint/tendermint/proxy"
|
||||||
sm "github.com/tendermint/tendermint/state"
|
sm "github.com/tendermint/tendermint/state"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
|
pvm "github.com/tendermint/tendermint/types/priv_validator"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -55,12 +55,12 @@ func startNewConsensusStateAndWaitForBlock(t *testing.T, lastBlockHeight int64,
|
|||||||
logger := log.TestingLogger()
|
logger := log.TestingLogger()
|
||||||
state, _ := sm.LoadStateFromDBOrGenesisFile(stateDB, consensusReplayConfig.GenesisFile())
|
state, _ := sm.LoadStateFromDBOrGenesisFile(stateDB, consensusReplayConfig.GenesisFile())
|
||||||
privValidator := loadPrivValidator(consensusReplayConfig)
|
privValidator := loadPrivValidator(consensusReplayConfig)
|
||||||
cs := newConsensusStateWithConfigAndBlockStore(consensusReplayConfig, state, privValidator, dummy.NewDummyApplication(), blockDB)
|
cs := newConsensusStateWithConfigAndBlockStore(consensusReplayConfig, state, privValidator, kvstore.NewKVStoreApplication(), blockDB)
|
||||||
cs.SetLogger(logger)
|
cs.SetLogger(logger)
|
||||||
|
|
||||||
bytes, _ := ioutil.ReadFile(cs.config.WalFile())
|
bytes, _ := ioutil.ReadFile(cs.config.WalFile())
|
||||||
// fmt.Printf("====== WAL: \n\r%s\n", bytes)
|
// fmt.Printf("====== WAL: \n\r%s\n", bytes)
|
||||||
t.Logf("====== WAL: \n\r%s\n", bytes)
|
t.Logf("====== WAL: \n\r%X\n", bytes)
|
||||||
|
|
||||||
err := cs.Start()
|
err := cs.Start()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -141,7 +141,7 @@ LOOP:
|
|||||||
state, _ := sm.MakeGenesisStateFromFile(consensusReplayConfig.GenesisFile())
|
state, _ := sm.MakeGenesisStateFromFile(consensusReplayConfig.GenesisFile())
|
||||||
privValidator := loadPrivValidator(consensusReplayConfig)
|
privValidator := loadPrivValidator(consensusReplayConfig)
|
||||||
blockDB := dbm.NewMemDB()
|
blockDB := dbm.NewMemDB()
|
||||||
cs := newConsensusStateWithConfigAndBlockStore(consensusReplayConfig, state, privValidator, dummy.NewDummyApplication(), blockDB)
|
cs := newConsensusStateWithConfigAndBlockStore(consensusReplayConfig, state, privValidator, kvstore.NewKVStoreApplication(), blockDB)
|
||||||
cs.SetLogger(logger)
|
cs.SetLogger(logger)
|
||||||
|
|
||||||
// start sending transactions
|
// start sending transactions
|
||||||
@@ -325,7 +325,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
|
|||||||
walFile := tempWALWithData(walBody)
|
walFile := tempWALWithData(walBody)
|
||||||
config.Consensus.SetWalFile(walFile)
|
config.Consensus.SetWalFile(walFile)
|
||||||
|
|
||||||
privVal := types.LoadPrivValidatorFS(config.PrivValidatorFile())
|
privVal := pvm.LoadFilePV(config.PrivValidatorFile())
|
||||||
|
|
||||||
wal, err := NewWAL(walFile, false)
|
wal, err := NewWAL(walFile, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -351,8 +351,8 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
|
|||||||
latestAppHash := state.AppHash
|
latestAppHash := state.AppHash
|
||||||
|
|
||||||
// make a new client creator
|
// make a new client creator
|
||||||
dummyApp := dummy.NewPersistentDummyApplication(path.Join(config.DBDir(), "2"))
|
kvstoreApp := kvstore.NewPersistentKVStoreApplication(path.Join(config.DBDir(), "2"))
|
||||||
clientCreator2 := proxy.NewLocalClientCreator(dummyApp)
|
clientCreator2 := proxy.NewLocalClientCreator(kvstoreApp)
|
||||||
if nBlocks > 0 {
|
if nBlocks > 0 {
|
||||||
// run nBlocks against a new client to build up the app state.
|
// run nBlocks against a new client to build up the app state.
|
||||||
// use a throwaway tendermint state
|
// use a throwaway tendermint state
|
||||||
@@ -362,7 +362,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// now start the app using the handshake - it should sync
|
// now start the app using the handshake - it should sync
|
||||||
handshaker := NewHandshaker(stateDB, state, store)
|
handshaker := NewHandshaker(stateDB, state, store, nil)
|
||||||
proxyApp := proxy.NewAppConns(clientCreator2, handshaker)
|
proxyApp := proxy.NewAppConns(clientCreator2, handshaker)
|
||||||
if err := proxyApp.Start(); err != nil {
|
if err := proxyApp.Start(); err != nil {
|
||||||
t.Fatalf("Error starting proxy app connections: %v", err)
|
t.Fatalf("Error starting proxy app connections: %v", err)
|
||||||
@@ -382,9 +382,9 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
|
|||||||
|
|
||||||
expectedBlocksToSync := NUM_BLOCKS - nBlocks
|
expectedBlocksToSync := NUM_BLOCKS - nBlocks
|
||||||
if nBlocks == NUM_BLOCKS && mode > 0 {
|
if nBlocks == NUM_BLOCKS && mode > 0 {
|
||||||
expectedBlocksToSync += 1
|
expectedBlocksToSync++
|
||||||
} else if nBlocks > 0 && mode == 1 {
|
} else if nBlocks > 0 && mode == 1 {
|
||||||
expectedBlocksToSync += 1
|
expectedBlocksToSync++
|
||||||
}
|
}
|
||||||
|
|
||||||
if handshaker.NBlocks() != expectedBlocksToSync {
|
if handshaker.NBlocks() != expectedBlocksToSync {
|
||||||
@@ -412,9 +412,9 @@ func buildAppStateFromChain(proxyApp proxy.AppConns, stateDB dbm.DB,
|
|||||||
}
|
}
|
||||||
defer proxyApp.Stop()
|
defer proxyApp.Stop()
|
||||||
|
|
||||||
validators := types.TM2PB.Validators(state.Validators)
|
|
||||||
// TODO: get the genesis bytes (https://github.com/tendermint/tendermint/issues/1224)
|
// TODO: get the genesis bytes (https://github.com/tendermint/tendermint/issues/1224)
|
||||||
var genesisBytes []byte
|
var genesisBytes []byte
|
||||||
|
validators := types.TM2PB.Validators(state.Validators)
|
||||||
if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{validators, genesisBytes}); err != nil {
|
if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{validators, genesisBytes}); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -432,7 +432,7 @@ func buildAppStateFromChain(proxyApp proxy.AppConns, stateDB dbm.DB,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if mode == 2 {
|
if mode == 2 {
|
||||||
// update the dummy height and apphash
|
// update the kvstore height and apphash
|
||||||
// as if we ran commit but not
|
// as if we ran commit but not
|
||||||
state = applyBlock(stateDB, state, chain[nBlocks-1], proxyApp)
|
state = applyBlock(stateDB, state, chain[nBlocks-1], proxyApp)
|
||||||
}
|
}
|
||||||
@@ -442,16 +442,16 @@ func buildAppStateFromChain(proxyApp proxy.AppConns, stateDB dbm.DB,
|
|||||||
|
|
||||||
func buildTMStateFromChain(config *cfg.Config, stateDB dbm.DB, state sm.State, chain []*types.Block, mode uint) sm.State {
|
func buildTMStateFromChain(config *cfg.Config, stateDB dbm.DB, state sm.State, chain []*types.Block, mode uint) sm.State {
|
||||||
// run the whole chain against this client to build up the tendermint state
|
// run the whole chain against this client to build up the tendermint state
|
||||||
clientCreator := proxy.NewLocalClientCreator(dummy.NewPersistentDummyApplication(path.Join(config.DBDir(), "1")))
|
clientCreator := proxy.NewLocalClientCreator(kvstore.NewPersistentKVStoreApplication(path.Join(config.DBDir(), "1")))
|
||||||
proxyApp := proxy.NewAppConns(clientCreator, nil) // sm.NewHandshaker(config, state, store, ReplayLastBlock))
|
proxyApp := proxy.NewAppConns(clientCreator, nil) // sm.NewHandshaker(config, state, store, ReplayLastBlock))
|
||||||
if err := proxyApp.Start(); err != nil {
|
if err := proxyApp.Start(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
defer proxyApp.Stop()
|
defer proxyApp.Stop()
|
||||||
|
|
||||||
validators := types.TM2PB.Validators(state.Validators)
|
|
||||||
// TODO: get the genesis bytes (https://github.com/tendermint/tendermint/issues/1224)
|
// TODO: get the genesis bytes (https://github.com/tendermint/tendermint/issues/1224)
|
||||||
var genesisBytes []byte
|
var genesisBytes []byte
|
||||||
|
validators := types.TM2PB.Validators(state.Validators)
|
||||||
if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{validators, genesisBytes}); err != nil {
|
if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{validators, genesisBytes}); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -519,8 +519,8 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
|
|||||||
case EndHeightMessage:
|
case EndHeightMessage:
|
||||||
// if its not the first one, we have a full block
|
// if its not the first one, we have a full block
|
||||||
if thisBlockParts != nil {
|
if thisBlockParts != nil {
|
||||||
var n int
|
var block = new(types.Block)
|
||||||
block := wire.ReadBinary(&types.Block{}, thisBlockParts.GetReader(), 0, &n, &err).(*types.Block)
|
_, err = cdc.UnmarshalBinaryReader(thisBlockParts.GetReader(), block, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -533,7 +533,7 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
|
|||||||
}
|
}
|
||||||
blocks = append(blocks, block)
|
blocks = append(blocks, block)
|
||||||
commits = append(commits, thisBlockCommit)
|
commits = append(commits, thisBlockCommit)
|
||||||
height += 1
|
height++
|
||||||
}
|
}
|
||||||
case *types.PartSetHeader:
|
case *types.PartSetHeader:
|
||||||
thisBlockParts = types.NewPartSetFromHeader(*p)
|
thisBlockParts = types.NewPartSetFromHeader(*p)
|
||||||
@@ -552,8 +552,8 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// grab the last block too
|
// grab the last block too
|
||||||
var n int
|
var block = new(types.Block)
|
||||||
block := wire.ReadBinary(&types.Block{}, thisBlockParts.GetReader(), 0, &n, &err).(*types.Block)
|
_, err = cdc.UnmarshalBinaryReader(thisBlockParts.GetReader(), block, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@@ -10,8 +10,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
fail "github.com/ebuchman/fail-test"
|
fail "github.com/ebuchman/fail-test"
|
||||||
|
|
||||||
wire "github.com/tendermint/go-wire"
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
@@ -170,18 +168,23 @@ func (cs *ConsensusState) GetState() sm.State {
|
|||||||
return cs.state.Copy()
|
return cs.state.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRoundState returns a copy of the internal consensus state.
|
// GetRoundState returns a shallow copy of the internal consensus state.
|
||||||
func (cs *ConsensusState) GetRoundState() *cstypes.RoundState {
|
func (cs *ConsensusState) GetRoundState() *cstypes.RoundState {
|
||||||
cs.mtx.Lock()
|
cs.mtx.Lock()
|
||||||
defer cs.mtx.Unlock()
|
defer cs.mtx.Unlock()
|
||||||
return cs.getRoundState()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cs *ConsensusState) getRoundState() *cstypes.RoundState {
|
|
||||||
rs := cs.RoundState // copy
|
rs := cs.RoundState // copy
|
||||||
return &rs
|
return &rs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetRoundStateJSON returns a json of RoundState, marshalled using go-amino.
|
||||||
|
func (cs *ConsensusState) GetRoundStateJSON() ([]byte, error) {
|
||||||
|
cs.mtx.Lock()
|
||||||
|
defer cs.mtx.Unlock()
|
||||||
|
|
||||||
|
return cdc.MarshalJSON(cs.RoundState)
|
||||||
|
}
|
||||||
|
|
||||||
// GetValidators returns a copy of the current validators.
|
// GetValidators returns a copy of the current validators.
|
||||||
func (cs *ConsensusState) GetValidators() (int64, []*types.Validator) {
|
func (cs *ConsensusState) GetValidators() (int64, []*types.Validator) {
|
||||||
cs.mtx.Lock()
|
cs.mtx.Lock()
|
||||||
@@ -477,6 +480,9 @@ func (cs *ConsensusState) updateToState(state sm.State) {
|
|||||||
cs.LockedRound = 0
|
cs.LockedRound = 0
|
||||||
cs.LockedBlock = nil
|
cs.LockedBlock = nil
|
||||||
cs.LockedBlockParts = nil
|
cs.LockedBlockParts = nil
|
||||||
|
cs.ValidRound = 0
|
||||||
|
cs.ValidBlock = nil
|
||||||
|
cs.ValidBlockParts = nil
|
||||||
cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators)
|
cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators)
|
||||||
cs.CommitRound = -1
|
cs.CommitRound = -1
|
||||||
cs.LastCommit = lastPrecommits
|
cs.LastCommit = lastPrecommits
|
||||||
@@ -491,7 +497,7 @@ func (cs *ConsensusState) updateToState(state sm.State) {
|
|||||||
func (cs *ConsensusState) newStep() {
|
func (cs *ConsensusState) newStep() {
|
||||||
rs := cs.RoundStateEvent()
|
rs := cs.RoundStateEvent()
|
||||||
cs.wal.Save(rs)
|
cs.wal.Save(rs)
|
||||||
cs.nSteps += 1
|
cs.nSteps++
|
||||||
// newStep is called by updateToStep in NewConsensusState before the eventBus is set!
|
// newStep is called by updateToStep in NewConsensusState before the eventBus is set!
|
||||||
if cs.eventBus != nil {
|
if cs.eventBus != nil {
|
||||||
cs.eventBus.PublishEventNewRoundStep(rs)
|
cs.eventBus.PublishEventNewRoundStep(rs)
|
||||||
@@ -580,6 +586,10 @@ func (cs *ConsensusState) handleMsg(mi msgInfo) {
|
|||||||
err := cs.tryAddVote(msg.Vote, peerID)
|
err := cs.tryAddVote(msg.Vote, peerID)
|
||||||
if err == ErrAddingVote {
|
if err == ErrAddingVote {
|
||||||
// TODO: punish peer
|
// TODO: punish peer
|
||||||
|
// We probably don't want to stop the peer here. The vote does not
|
||||||
|
// necessarily comes from a malicious peer but can be just broadcasted by
|
||||||
|
// a typical peer.
|
||||||
|
// https://github.com/tendermint/tendermint/issues/1281
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: the vote is broadcast to peers by the reactor listening
|
// NOTE: the vote is broadcast to peers by the reactor listening
|
||||||
@@ -713,11 +723,7 @@ func (cs *ConsensusState) needProofBlock(height int64) bool {
|
|||||||
func (cs *ConsensusState) proposalHeartbeat(height int64, round int) {
|
func (cs *ConsensusState) proposalHeartbeat(height int64, round int) {
|
||||||
counter := 0
|
counter := 0
|
||||||
addr := cs.privValidator.GetAddress()
|
addr := cs.privValidator.GetAddress()
|
||||||
valIndex, v := cs.Validators.GetByAddress(addr)
|
valIndex, _ := cs.Validators.GetByAddress(addr)
|
||||||
if v == nil {
|
|
||||||
// not a validator
|
|
||||||
valIndex = -1
|
|
||||||
}
|
|
||||||
chainID := cs.state.ChainID
|
chainID := cs.state.ChainID
|
||||||
for {
|
for {
|
||||||
rs := cs.GetRoundState()
|
rs := cs.GetRoundState()
|
||||||
@@ -734,7 +740,7 @@ func (cs *ConsensusState) proposalHeartbeat(height int64, round int) {
|
|||||||
}
|
}
|
||||||
cs.privValidator.SignHeartbeat(chainID, heartbeat)
|
cs.privValidator.SignHeartbeat(chainID, heartbeat)
|
||||||
cs.eventBus.PublishEventProposalHeartbeat(types.EventDataProposalHeartbeat{heartbeat})
|
cs.eventBus.PublishEventProposalHeartbeat(types.EventDataProposalHeartbeat{heartbeat})
|
||||||
counter += 1
|
counter++
|
||||||
time.Sleep(proposalHeartbeatIntervalSeconds * time.Second)
|
time.Sleep(proposalHeartbeatIntervalSeconds * time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -773,7 +779,7 @@ func (cs *ConsensusState) enterPropose(height int64, round int) {
|
|||||||
|
|
||||||
// if not a validator, we're done
|
// if not a validator, we're done
|
||||||
if !cs.Validators.HasAddress(cs.privValidator.GetAddress()) {
|
if !cs.Validators.HasAddress(cs.privValidator.GetAddress()) {
|
||||||
cs.Logger.Debug("This node is not a validator")
|
cs.Logger.Debug("This node is not a validator", "addr", cs.privValidator.GetAddress(), "vals", cs.Validators)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cs.Logger.Debug("This node is a validator")
|
cs.Logger.Debug("This node is a validator")
|
||||||
@@ -798,6 +804,9 @@ func (cs *ConsensusState) defaultDecideProposal(height int64, round int) {
|
|||||||
if cs.LockedBlock != nil {
|
if cs.LockedBlock != nil {
|
||||||
// If we're locked onto a block, just choose that.
|
// If we're locked onto a block, just choose that.
|
||||||
block, blockParts = cs.LockedBlock, cs.LockedBlockParts
|
block, blockParts = cs.LockedBlock, cs.LockedBlockParts
|
||||||
|
} else if cs.ValidBlock != nil {
|
||||||
|
// If there is valid block, choose that.
|
||||||
|
block, blockParts = cs.ValidBlock, cs.ValidBlockParts
|
||||||
} else {
|
} else {
|
||||||
// Create a new proposal block from state/txs from the mempool.
|
// Create a new proposal block from state/txs from the mempool.
|
||||||
block, blockParts = cs.createProposalBlock()
|
block, blockParts = cs.createProposalBlock()
|
||||||
@@ -842,10 +851,10 @@ func (cs *ConsensusState) isProposalComplete() bool {
|
|||||||
// make sure we have the prevotes from it too
|
// make sure we have the prevotes from it too
|
||||||
if cs.Proposal.POLRound < 0 {
|
if cs.Proposal.POLRound < 0 {
|
||||||
return true
|
return true
|
||||||
} else {
|
}
|
||||||
// if this is false the proposer is lying or we haven't received the POL yet
|
// if this is false the proposer is lying or we haven't received the POL yet
|
||||||
return cs.Votes.Prevotes(cs.Proposal.POLRound).HasTwoThirdsMajority()
|
return cs.Votes.Prevotes(cs.Proposal.POLRound).HasTwoThirdsMajority()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the next block to propose and return it.
|
// Create the next block to propose and return it.
|
||||||
@@ -1263,7 +1272,7 @@ func (cs *ConsensusState) defaultSetProposal(proposal *types.Proposal) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify signature
|
// Verify signature
|
||||||
if !cs.Validators.GetProposer().PubKey.VerifyBytes(types.SignBytes(cs.state.ChainID, proposal), proposal.Signature) {
|
if !cs.Validators.GetProposer().PubKey.VerifyBytes(proposal.SignBytes(cs.state.ChainID), proposal.Signature) {
|
||||||
return ErrInvalidProposalSignature
|
return ErrInvalidProposalSignature
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1291,10 +1300,10 @@ func (cs *ConsensusState) addProposalBlockPart(height int64, part *types.Part, v
|
|||||||
}
|
}
|
||||||
if added && cs.ProposalBlockParts.IsComplete() {
|
if added && cs.ProposalBlockParts.IsComplete() {
|
||||||
// Added and completed!
|
// Added and completed!
|
||||||
var n int
|
_, err = cdc.UnmarshalBinaryReader(cs.ProposalBlockParts.GetReader(), &cs.ProposalBlock, int64(cs.state.ConsensusParams.BlockSize.MaxBytes))
|
||||||
var err error
|
if err != nil {
|
||||||
cs.ProposalBlock = wire.ReadBinary(&types.Block{}, cs.ProposalBlockParts.GetReader(),
|
return true, err
|
||||||
cs.state.ConsensusParams.BlockSize.MaxBytes, &n, &err).(*types.Block)
|
}
|
||||||
// NOTE: it's possible to receive complete proposal blocks for future rounds without having the proposal
|
// NOTE: it's possible to receive complete proposal blocks for future rounds without having the proposal
|
||||||
cs.Logger.Info("Received complete proposal block", "height", cs.ProposalBlock.Height, "hash", cs.ProposalBlock.Hash())
|
cs.Logger.Info("Received complete proposal block", "height", cs.ProposalBlock.Height, "hash", cs.ProposalBlock.Hash())
|
||||||
if cs.Step == cstypes.RoundStepPropose && cs.isProposalComplete() {
|
if cs.Step == cstypes.RoundStepPropose && cs.isProposalComplete() {
|
||||||
@@ -1304,7 +1313,7 @@ func (cs *ConsensusState) addProposalBlockPart(height int64, part *types.Part, v
|
|||||||
// If we're waiting on the proposal block...
|
// If we're waiting on the proposal block...
|
||||||
cs.tryFinalizeCommit(height)
|
cs.tryFinalizeCommit(height)
|
||||||
}
|
}
|
||||||
return true, err
|
return true, nil
|
||||||
}
|
}
|
||||||
return added, nil
|
return added, nil
|
||||||
}
|
}
|
||||||
@@ -1349,7 +1358,10 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
|
|||||||
return added, ErrVoteHeightMismatch
|
return added, ErrVoteHeightMismatch
|
||||||
}
|
}
|
||||||
added, err = cs.LastCommit.AddVote(vote)
|
added, err = cs.LastCommit.AddVote(vote)
|
||||||
if added {
|
if !added {
|
||||||
|
return added, err
|
||||||
|
}
|
||||||
|
|
||||||
cs.Logger.Info(cmn.Fmt("Added to lastPrecommits: %v", cs.LastCommit.StringShort()))
|
cs.Logger.Info(cmn.Fmt("Added to lastPrecommits: %v", cs.LastCommit.StringShort()))
|
||||||
cs.eventBus.PublishEventVote(types.EventDataVote{vote})
|
cs.eventBus.PublishEventVote(types.EventDataVote{vote})
|
||||||
|
|
||||||
@@ -1359,29 +1371,38 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
|
|||||||
// cs.scheduleTimeout(time.Duration(0), cs.Height, 0, cstypes.RoundStepNewHeight)
|
// cs.scheduleTimeout(time.Duration(0), cs.Height, 0, cstypes.RoundStepNewHeight)
|
||||||
cs.enterNewRound(cs.Height, 0)
|
cs.enterNewRound(cs.Height, 0)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// A prevote/precommit for this height?
|
// Height mismatch is ignored.
|
||||||
if vote.Height == cs.Height {
|
// Not necessarily a bad peer, but not favourable behaviour.
|
||||||
|
if vote.Height != cs.Height {
|
||||||
|
err = ErrVoteHeightMismatch
|
||||||
|
cs.Logger.Info("Vote ignored and not added", "voteHeight", vote.Height, "csHeight", cs.Height, "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
height := cs.Height
|
height := cs.Height
|
||||||
added, err = cs.Votes.AddVote(vote, peerID)
|
added, err = cs.Votes.AddVote(vote, peerID)
|
||||||
if added {
|
if !added {
|
||||||
|
// Either duplicate, or error upon cs.Votes.AddByIndex()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
cs.eventBus.PublishEventVote(types.EventDataVote{vote})
|
cs.eventBus.PublishEventVote(types.EventDataVote{vote})
|
||||||
|
|
||||||
switch vote.Type {
|
switch vote.Type {
|
||||||
case types.VoteTypePrevote:
|
case types.VoteTypePrevote:
|
||||||
prevotes := cs.Votes.Prevotes(vote.Round)
|
prevotes := cs.Votes.Prevotes(vote.Round)
|
||||||
cs.Logger.Info("Added to prevote", "vote", vote, "prevotes", prevotes.StringShort())
|
cs.Logger.Info("Added to prevote", "vote", vote, "prevotes", prevotes.StringShort())
|
||||||
|
blockID, ok := prevotes.TwoThirdsMajority()
|
||||||
// First, unlock if prevotes is a valid POL.
|
// First, unlock if prevotes is a valid POL.
|
||||||
// >> lockRound < POLRound <= unlockOrChangeLockRound (see spec)
|
// >> lockRound < POLRound <= unlockOrChangeLockRound (see spec)
|
||||||
// NOTE: If (lockRound < POLRound) but !(POLRound <= unlockOrChangeLockRound),
|
// NOTE: If (lockRound < POLRound) but !(POLRound <= unlockOrChangeLockRound),
|
||||||
// we'll still enterNewRound(H,vote.R) and enterPrecommit(H,vote.R) to process it
|
// we'll still enterNewRound(H,vote.R) and enterPrecommit(H,vote.R) to process it
|
||||||
// there.
|
// there.
|
||||||
if (cs.LockedBlock != nil) && (cs.LockedRound < vote.Round) && (vote.Round <= cs.Round) {
|
if (cs.LockedBlock != nil) && (cs.LockedRound < vote.Round) && (vote.Round <= cs.Round) {
|
||||||
blockID, ok := prevotes.TwoThirdsMajority()
|
|
||||||
if ok && !cs.LockedBlock.HashesTo(blockID.Hash) {
|
if ok && !cs.LockedBlock.HashesTo(blockID.Hash) {
|
||||||
cs.Logger.Info("Unlocking because of POL.", "lockedRound", cs.LockedRound, "POLRound", vote.Round)
|
cs.Logger.Info("Unlocking because of POL.", "lockedRound", cs.LockedRound, "POLRound", vote.Round)
|
||||||
cs.LockedRound = 0
|
cs.LockedRound = 0
|
||||||
@@ -1390,6 +1411,18 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
|
|||||||
cs.eventBus.PublishEventUnlock(cs.RoundStateEvent())
|
cs.eventBus.PublishEventUnlock(cs.RoundStateEvent())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Update ValidBlock
|
||||||
|
if ok && !blockID.IsZero() && !cs.ValidBlock.HashesTo(blockID.Hash) && vote.Round > cs.ValidRound {
|
||||||
|
// update valid value
|
||||||
|
if cs.ProposalBlock.HashesTo(blockID.Hash) {
|
||||||
|
cs.ValidRound = vote.Round
|
||||||
|
cs.ValidBlock = cs.ProposalBlock
|
||||||
|
cs.ValidBlockParts = cs.ProposalBlockParts
|
||||||
|
}
|
||||||
|
//TODO: We might want to update ValidBlock also in case we don't have that block yet,
|
||||||
|
// and obtain the required block using gossiping
|
||||||
|
}
|
||||||
|
|
||||||
if cs.Round <= vote.Round && prevotes.HasTwoThirdsAny() {
|
if cs.Round <= vote.Round && prevotes.HasTwoThirdsAny() {
|
||||||
// Round-skip over to PrevoteWait or goto Precommit.
|
// Round-skip over to PrevoteWait or goto Precommit.
|
||||||
cs.enterNewRound(height, vote.Round) // if the vote is ahead of us
|
cs.enterNewRound(height, vote.Round) // if the vote is ahead of us
|
||||||
@@ -1431,17 +1464,9 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
|
|||||||
cs.enterPrecommitWait(height, vote.Round)
|
cs.enterPrecommitWait(height, vote.Round)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
cmn.PanicSanity(cmn.Fmt("Unexpected vote type %X", vote.Type)) // Should not happen.
|
panic(cmn.Fmt("Unexpected vote type %X", vote.Type)) // go-wire should prevent this.
|
||||||
}
|
|
||||||
}
|
|
||||||
// Either duplicate, or error upon cs.Votes.AddByIndex()
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
err = ErrVoteHeightMismatch
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Height mismatch, bad peer?
|
|
||||||
cs.Logger.Info("Vote ignored and not added", "voteHeight", vote.Height, "csHeight", cs.Height, "err", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1472,13 +1497,12 @@ func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header types.Part
|
|||||||
cs.sendInternalMessage(msgInfo{&VoteMessage{vote}, ""})
|
cs.sendInternalMessage(msgInfo{&VoteMessage{vote}, ""})
|
||||||
cs.Logger.Info("Signed and pushed vote", "height", cs.Height, "round", cs.Round, "vote", vote, "err", err)
|
cs.Logger.Info("Signed and pushed vote", "height", cs.Height, "round", cs.Round, "vote", vote, "err", err)
|
||||||
return vote
|
return vote
|
||||||
} else {
|
}
|
||||||
//if !cs.replayMode {
|
//if !cs.replayMode {
|
||||||
cs.Logger.Error("Error signing vote", "height", cs.Height, "round", cs.Round, "vote", vote, "err", err)
|
cs.Logger.Error("Error signing vote", "height", cs.Height, "round", cs.Round, "vote", vote, "err", err)
|
||||||
//}
|
//}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
|
|
||||||
|
@@ -261,7 +261,7 @@ func TestStateFullRound1(t *testing.T) {
|
|||||||
|
|
||||||
// grab proposal
|
// grab proposal
|
||||||
re := <-propCh
|
re := <-propCh
|
||||||
propBlockHash := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState).ProposalBlock.Hash()
|
propBlockHash := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState).ProposalBlock.Hash()
|
||||||
|
|
||||||
<-voteCh // wait for prevote
|
<-voteCh // wait for prevote
|
||||||
validatePrevote(t, cs, round, vss[0], propBlockHash)
|
validatePrevote(t, cs, round, vss[0], propBlockHash)
|
||||||
@@ -356,7 +356,7 @@ func TestStateLockNoPOL(t *testing.T) {
|
|||||||
cs1.startRoutines(0)
|
cs1.startRoutines(0)
|
||||||
|
|
||||||
re := <-proposalCh
|
re := <-proposalCh
|
||||||
rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
rs := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
||||||
theBlockHash := rs.ProposalBlock.Hash()
|
theBlockHash := rs.ProposalBlock.Hash()
|
||||||
|
|
||||||
<-voteCh // prevote
|
<-voteCh // prevote
|
||||||
@@ -396,7 +396,7 @@ func TestStateLockNoPOL(t *testing.T) {
|
|||||||
|
|
||||||
// now we're on a new round and not the proposer, so wait for timeout
|
// now we're on a new round and not the proposer, so wait for timeout
|
||||||
re = <-timeoutProposeCh
|
re = <-timeoutProposeCh
|
||||||
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
||||||
|
|
||||||
if rs.ProposalBlock != nil {
|
if rs.ProposalBlock != nil {
|
||||||
panic("Expected proposal block to be nil")
|
panic("Expected proposal block to be nil")
|
||||||
@@ -409,7 +409,7 @@ func TestStateLockNoPOL(t *testing.T) {
|
|||||||
validatePrevote(t, cs1, 1, vss[0], rs.LockedBlock.Hash())
|
validatePrevote(t, cs1, 1, vss[0], rs.LockedBlock.Hash())
|
||||||
|
|
||||||
// add a conflicting prevote from the other validator
|
// add a conflicting prevote from the other validator
|
||||||
signAddVotes(cs1, types.VoteTypePrevote, hash, rs.ProposalBlock.MakePartSet(partSize).Header(), vs2)
|
signAddVotes(cs1, types.VoteTypePrevote, hash, rs.LockedBlock.MakePartSet(partSize).Header(), vs2)
|
||||||
<-voteCh
|
<-voteCh
|
||||||
|
|
||||||
// now we're going to enter prevote again, but with invalid args
|
// now we're going to enter prevote again, but with invalid args
|
||||||
@@ -424,7 +424,7 @@ func TestStateLockNoPOL(t *testing.T) {
|
|||||||
|
|
||||||
// add conflicting precommit from vs2
|
// add conflicting precommit from vs2
|
||||||
// NOTE: in practice we should never get to a point where there are precommits for different blocks at the same round
|
// NOTE: in practice we should never get to a point where there are precommits for different blocks at the same round
|
||||||
signAddVotes(cs1, types.VoteTypePrecommit, hash, rs.ProposalBlock.MakePartSet(partSize).Header(), vs2)
|
signAddVotes(cs1, types.VoteTypePrecommit, hash, rs.LockedBlock.MakePartSet(partSize).Header(), vs2)
|
||||||
<-voteCh
|
<-voteCh
|
||||||
|
|
||||||
// (note we're entering precommit for a second time this round, but with invalid args
|
// (note we're entering precommit for a second time this round, but with invalid args
|
||||||
@@ -440,7 +440,7 @@ func TestStateLockNoPOL(t *testing.T) {
|
|||||||
incrementRound(vs2)
|
incrementRound(vs2)
|
||||||
|
|
||||||
re = <-proposalCh
|
re = <-proposalCh
|
||||||
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
||||||
|
|
||||||
// now we're on a new round and are the proposer
|
// now we're on a new round and are the proposer
|
||||||
if !bytes.Equal(rs.ProposalBlock.Hash(), rs.LockedBlock.Hash()) {
|
if !bytes.Equal(rs.ProposalBlock.Hash(), rs.LockedBlock.Hash()) {
|
||||||
@@ -529,7 +529,7 @@ func TestStateLockPOLRelock(t *testing.T) {
|
|||||||
|
|
||||||
<-newRoundCh
|
<-newRoundCh
|
||||||
re := <-proposalCh
|
re := <-proposalCh
|
||||||
rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
rs := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
||||||
theBlockHash := rs.ProposalBlock.Hash()
|
theBlockHash := rs.ProposalBlock.Hash()
|
||||||
|
|
||||||
<-voteCh // prevote
|
<-voteCh // prevote
|
||||||
@@ -605,9 +605,9 @@ func TestStateLockPOLRelock(t *testing.T) {
|
|||||||
discardFromChan(voteCh, 2)
|
discardFromChan(voteCh, 2)
|
||||||
|
|
||||||
be := <-newBlockCh
|
be := <-newBlockCh
|
||||||
b := be.(types.TMEventData).Unwrap().(types.EventDataNewBlockHeader)
|
b := be.(types.EventDataNewBlockHeader)
|
||||||
re = <-newRoundCh
|
re = <-newRoundCh
|
||||||
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
||||||
if rs.Height != 2 {
|
if rs.Height != 2 {
|
||||||
panic("Expected height to increment")
|
panic("Expected height to increment")
|
||||||
}
|
}
|
||||||
@@ -643,7 +643,7 @@ func TestStateLockPOLUnlock(t *testing.T) {
|
|||||||
startTestRound(cs1, cs1.Height, 0)
|
startTestRound(cs1, cs1.Height, 0)
|
||||||
<-newRoundCh
|
<-newRoundCh
|
||||||
re := <-proposalCh
|
re := <-proposalCh
|
||||||
rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
rs := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
||||||
theBlockHash := rs.ProposalBlock.Hash()
|
theBlockHash := rs.ProposalBlock.Hash()
|
||||||
|
|
||||||
<-voteCh // prevote
|
<-voteCh // prevote
|
||||||
@@ -669,7 +669,7 @@ func TestStateLockPOLUnlock(t *testing.T) {
|
|||||||
|
|
||||||
// timeout to new round
|
// timeout to new round
|
||||||
re = <-timeoutWaitCh
|
re = <-timeoutWaitCh
|
||||||
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
||||||
lockedBlockHash := rs.LockedBlock.Hash()
|
lockedBlockHash := rs.LockedBlock.Hash()
|
||||||
|
|
||||||
//XXX: this isnt guaranteed to get there before the timeoutPropose ...
|
//XXX: this isnt guaranteed to get there before the timeoutPropose ...
|
||||||
@@ -731,7 +731,7 @@ func TestStateLockPOLSafety1(t *testing.T) {
|
|||||||
startTestRound(cs1, cs1.Height, 0)
|
startTestRound(cs1, cs1.Height, 0)
|
||||||
<-newRoundCh
|
<-newRoundCh
|
||||||
re := <-proposalCh
|
re := <-proposalCh
|
||||||
rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
rs := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
||||||
propBlock := rs.ProposalBlock
|
propBlock := rs.ProposalBlock
|
||||||
|
|
||||||
<-voteCh // prevote
|
<-voteCh // prevote
|
||||||
@@ -781,7 +781,7 @@ func TestStateLockPOLSafety1(t *testing.T) {
|
|||||||
re = <-proposalCh
|
re = <-proposalCh
|
||||||
}
|
}
|
||||||
|
|
||||||
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
||||||
|
|
||||||
if rs.LockedBlock != nil {
|
if rs.LockedBlock != nil {
|
||||||
panic("we should not be locked!")
|
panic("we should not be locked!")
|
||||||
@@ -1033,7 +1033,7 @@ func TestStateHalt1(t *testing.T) {
|
|||||||
startTestRound(cs1, cs1.Height, 0)
|
startTestRound(cs1, cs1.Height, 0)
|
||||||
<-newRoundCh
|
<-newRoundCh
|
||||||
re := <-proposalCh
|
re := <-proposalCh
|
||||||
rs := re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
rs := re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
||||||
propBlock := rs.ProposalBlock
|
propBlock := rs.ProposalBlock
|
||||||
propBlockParts := propBlock.MakePartSet(partSize)
|
propBlockParts := propBlock.MakePartSet(partSize)
|
||||||
|
|
||||||
@@ -1056,7 +1056,7 @@ func TestStateHalt1(t *testing.T) {
|
|||||||
// timeout to new round
|
// timeout to new round
|
||||||
<-timeoutWaitCh
|
<-timeoutWaitCh
|
||||||
re = <-newRoundCh
|
re = <-newRoundCh
|
||||||
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
||||||
|
|
||||||
t.Log("### ONTO ROUND 1")
|
t.Log("### ONTO ROUND 1")
|
||||||
/*Round2
|
/*Round2
|
||||||
@@ -1074,7 +1074,7 @@ func TestStateHalt1(t *testing.T) {
|
|||||||
// receiving that precommit should take us straight to commit
|
// receiving that precommit should take us straight to commit
|
||||||
<-newBlockCh
|
<-newBlockCh
|
||||||
re = <-newRoundCh
|
re = <-newRoundCh
|
||||||
rs = re.(types.TMEventData).Unwrap().(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
rs = re.(types.EventDataRoundState).RoundState.(*cstypes.RoundState)
|
||||||
|
|
||||||
if rs.Height != 2 {
|
if rs.Height != 2 {
|
||||||
panic("expected height to increment")
|
panic("expected height to increment")
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -15,6 +16,10 @@ type RoundVoteSet struct {
|
|||||||
Precommits *types.VoteSet
|
Precommits *types.VoteSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
GotVoteFromUnwantedRoundError = errors.New("Peer has sent a vote that does not match our round for more than one round")
|
||||||
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Keeps track of all VoteSets from round 0 to round 'round'.
|
Keeps track of all VoteSets from round 0 to round 'round'.
|
||||||
|
|
||||||
@@ -117,10 +122,8 @@ func (hvs *HeightVoteSet) AddVote(vote *types.Vote, peerID p2p.ID) (added bool,
|
|||||||
voteSet = hvs.getVoteSet(vote.Round, vote.Type)
|
voteSet = hvs.getVoteSet(vote.Round, vote.Type)
|
||||||
hvs.peerCatchupRounds[peerID] = append(rndz, vote.Round)
|
hvs.peerCatchupRounds[peerID] = append(rndz, vote.Round)
|
||||||
} else {
|
} else {
|
||||||
// Peer has sent a vote that does not match our round,
|
// punish peer
|
||||||
// for more than one round. Bad peer!
|
err = GotVoteFromUnwantedRoundError
|
||||||
// TODO punish peer.
|
|
||||||
// log.Warn("Deal with peer giving votes from unwanted rounds")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,5 +221,5 @@ func (hvs *HeightVoteSet) SetPeerMaj23(round int, type_ byte, peerID p2p.ID, blo
|
|||||||
if voteSet == nil {
|
if voteSet == nil {
|
||||||
return nil // something we don't know about yet
|
return nil // something we don't know about yet
|
||||||
}
|
}
|
||||||
return voteSet.SetPeerMaj23(peerID, blockID)
|
return voteSet.SetPeerMaj23(types.P2PID(peerID), blockID)
|
||||||
}
|
}
|
||||||
|
@@ -34,8 +34,8 @@ func TestPeerCatchupRounds(t *testing.T) {
|
|||||||
|
|
||||||
vote1001_0 := makeVoteHR(t, 1, 1001, privVals, 0)
|
vote1001_0 := makeVoteHR(t, 1, 1001, privVals, 0)
|
||||||
added, err = hvs.AddVote(vote1001_0, "peer1")
|
added, err = hvs.AddVote(vote1001_0, "peer1")
|
||||||
if err != nil {
|
if err != GotVoteFromUnwantedRoundError {
|
||||||
t.Error("AddVote error", err)
|
t.Errorf("Expected GotVoteFromUnwantedRoundError, but got %v", err)
|
||||||
}
|
}
|
||||||
if added {
|
if added {
|
||||||
t.Error("Expected to *not* add vote from peer, too many catchup rounds.")
|
t.Error("Expected to *not* add vote from peer, too many catchup rounds.")
|
||||||
@@ -48,7 +48,7 @@ func TestPeerCatchupRounds(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeVoteHR(t *testing.T, height int64, round int, privVals []*types.PrivValidatorFS, valIndex int) *types.Vote {
|
func makeVoteHR(t *testing.T, height int64, round int, privVals []types.PrivValidator, valIndex int) *types.Vote {
|
||||||
privVal := privVals[valIndex]
|
privVal := privVals[valIndex]
|
||||||
vote := &types.Vote{
|
vote := &types.Vote{
|
||||||
ValidatorAddress: privVal.GetAddress(),
|
ValidatorAddress: privVal.GetAddress(),
|
||||||
|
@@ -52,9 +52,6 @@ func (rs RoundStepType) String() string {
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
// RoundState defines the internal consensus state.
|
// RoundState defines the internal consensus state.
|
||||||
// It is Immutable when returned from ConsensusState.GetRoundState()
|
|
||||||
// TODO: Actually, only the top pointer is copied,
|
|
||||||
// so access to field pointers is still racey
|
|
||||||
// NOTE: Not thread safe. Should only be manipulated by functions downstream
|
// NOTE: Not thread safe. Should only be manipulated by functions downstream
|
||||||
// of the cs.receiveRoutine
|
// of the cs.receiveRoutine
|
||||||
type RoundState struct {
|
type RoundState struct {
|
||||||
@@ -70,6 +67,9 @@ type RoundState struct {
|
|||||||
LockedRound int
|
LockedRound int
|
||||||
LockedBlock *types.Block
|
LockedBlock *types.Block
|
||||||
LockedBlockParts *types.PartSet
|
LockedBlockParts *types.PartSet
|
||||||
|
ValidRound int
|
||||||
|
ValidBlock *types.Block
|
||||||
|
ValidBlockParts *types.PartSet
|
||||||
Votes *HeightVoteSet
|
Votes *HeightVoteSet
|
||||||
CommitRound int //
|
CommitRound int //
|
||||||
LastCommit *types.VoteSet // Last precommits at Height-1
|
LastCommit *types.VoteSet // Last precommits at Height-1
|
||||||
@@ -106,6 +106,8 @@ func (rs *RoundState) StringIndented(indent string) string {
|
|||||||
%s ProposalBlock: %v %v
|
%s ProposalBlock: %v %v
|
||||||
%s LockedRound: %v
|
%s LockedRound: %v
|
||||||
%s LockedBlock: %v %v
|
%s LockedBlock: %v %v
|
||||||
|
%s ValidRound: %v
|
||||||
|
%s ValidBlock: %v %v
|
||||||
%s Votes: %v
|
%s Votes: %v
|
||||||
%s LastCommit: %v
|
%s LastCommit: %v
|
||||||
%s LastValidators:%v
|
%s LastValidators:%v
|
||||||
@@ -118,6 +120,8 @@ func (rs *RoundState) StringIndented(indent string) string {
|
|||||||
indent, rs.ProposalBlockParts.StringShort(), rs.ProposalBlock.StringShort(),
|
indent, rs.ProposalBlockParts.StringShort(), rs.ProposalBlock.StringShort(),
|
||||||
indent, rs.LockedRound,
|
indent, rs.LockedRound,
|
||||||
indent, rs.LockedBlockParts.StringShort(), rs.LockedBlock.StringShort(),
|
indent, rs.LockedBlockParts.StringShort(), rs.LockedBlock.StringShort(),
|
||||||
|
indent, rs.ValidRound,
|
||||||
|
indent, rs.ValidBlockParts.StringShort(), rs.ValidBlock.StringShort(),
|
||||||
indent, rs.Votes.StringIndented(indent+" "),
|
indent, rs.Votes.StringIndented(indent+" "),
|
||||||
indent, rs.LastCommit.StringShort(),
|
indent, rs.LastCommit.StringShort(),
|
||||||
indent, rs.LastValidators.StringIndented(indent+" "),
|
indent, rs.LastValidators.StringIndented(indent+" "),
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
package consensus
|
package consensus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
@@ -11,7 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
wire "github.com/tendermint/go-wire"
|
amino "github.com/tendermint/go-amino"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
auto "github.com/tendermint/tmlibs/autofile"
|
auto "github.com/tendermint/tmlibs/autofile"
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
@@ -38,13 +37,13 @@ type EndHeightMessage struct {
|
|||||||
|
|
||||||
type WALMessage interface{}
|
type WALMessage interface{}
|
||||||
|
|
||||||
var _ = wire.RegisterInterface(
|
func RegisterWALMessages(cdc *amino.Codec) {
|
||||||
struct{ WALMessage }{},
|
cdc.RegisterInterface((*WALMessage)(nil), nil)
|
||||||
wire.ConcreteType{types.EventDataRoundState{}, 0x01},
|
cdc.RegisterConcrete(types.EventDataRoundState{}, "tendermint/wal/EventDataRoundState", nil)
|
||||||
wire.ConcreteType{msgInfo{}, 0x02},
|
cdc.RegisterConcrete(msgInfo{}, "tendermint/wal/MsgInfo", nil)
|
||||||
wire.ConcreteType{timeoutInfo{}, 0x03},
|
cdc.RegisterConcrete(timeoutInfo{}, "tendermint/wal/TimeoutInfo", nil)
|
||||||
wire.ConcreteType{EndHeightMessage{}, 0x04},
|
cdc.RegisterConcrete(EndHeightMessage{}, "tendermint/wal/EndHeightMessage", nil)
|
||||||
)
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------
|
//--------------------------------------------------------
|
||||||
// Simple write-ahead logger
|
// Simple write-ahead logger
|
||||||
@@ -193,7 +192,7 @@ func (wal *baseWAL) SearchForEndHeight(height int64, options *WALSearchOptions)
|
|||||||
|
|
||||||
// A WALEncoder writes custom-encoded WAL messages to an output stream.
|
// A WALEncoder writes custom-encoded WAL messages to an output stream.
|
||||||
//
|
//
|
||||||
// Format: 4 bytes CRC sum + 4 bytes length + arbitrary-length value (go-wire encoded)
|
// Format: 4 bytes CRC sum + 4 bytes length + arbitrary-length value (go-amino encoded)
|
||||||
type WALEncoder struct {
|
type WALEncoder struct {
|
||||||
wr io.Writer
|
wr io.Writer
|
||||||
}
|
}
|
||||||
@@ -205,7 +204,7 @@ func NewWALEncoder(wr io.Writer) *WALEncoder {
|
|||||||
|
|
||||||
// Encode writes the custom encoding of v to the stream.
|
// Encode writes the custom encoding of v to the stream.
|
||||||
func (enc *WALEncoder) Encode(v *TimedWALMessage) error {
|
func (enc *WALEncoder) Encode(v *TimedWALMessage) error {
|
||||||
data := wire.BinaryBytes(v)
|
data := cdc.MustMarshalBinaryBare(v)
|
||||||
|
|
||||||
crc := crc32.Checksum(data, crc32c)
|
crc := crc32.Checksum(data, crc32c)
|
||||||
length := uint32(len(data))
|
length := uint32(len(data))
|
||||||
@@ -298,9 +297,8 @@ func (dec *WALDecoder) Decode() (*TimedWALMessage, error) {
|
|||||||
return nil, DataCorruptionError{fmt.Errorf("checksums do not match: (read: %v, actual: %v)", crc, actualCRC)}
|
return nil, DataCorruptionError{fmt.Errorf("checksums do not match: (read: %v, actual: %v)", crc, actualCRC)}
|
||||||
}
|
}
|
||||||
|
|
||||||
var nn int
|
var res = new(TimedWALMessage) // nolint: gosimple
|
||||||
var res *TimedWALMessage // nolint: gosimple
|
err = cdc.UnmarshalBinaryBare(data, res)
|
||||||
res = wire.ReadBinary(&TimedWALMessage{}, bytes.NewBuffer(data), int(length), &nn, &err).(*TimedWALMessage)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, DataCorruptionError{fmt.Errorf("failed to decode data: %v", err)}
|
return nil, DataCorruptionError{fmt.Errorf("failed to decode data: %v", err)}
|
||||||
}
|
}
|
||||||
|
@@ -4,19 +4,19 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/tendermint/abci/example/dummy"
|
"github.com/tendermint/abci/example/kvstore"
|
||||||
bc "github.com/tendermint/tendermint/blockchain"
|
bc "github.com/tendermint/tendermint/blockchain"
|
||||||
cfg "github.com/tendermint/tendermint/config"
|
cfg "github.com/tendermint/tendermint/config"
|
||||||
"github.com/tendermint/tendermint/proxy"
|
"github.com/tendermint/tendermint/proxy"
|
||||||
sm "github.com/tendermint/tendermint/state"
|
sm "github.com/tendermint/tendermint/state"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
|
pvm "github.com/tendermint/tendermint/types/priv_validator"
|
||||||
auto "github.com/tendermint/tmlibs/autofile"
|
auto "github.com/tendermint/tmlibs/autofile"
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
"github.com/tendermint/tmlibs/db"
|
"github.com/tendermint/tmlibs/db"
|
||||||
@@ -25,13 +25,13 @@ import (
|
|||||||
|
|
||||||
// WALWithNBlocks generates a consensus WAL. It does this by spining up a
|
// WALWithNBlocks generates a consensus WAL. It does this by spining up a
|
||||||
// stripped down version of node (proxy app, event bus, consensus state) with a
|
// stripped down version of node (proxy app, event bus, consensus state) with a
|
||||||
// persistent dummy application and special consensus wal instance
|
// persistent kvstore application and special consensus wal instance
|
||||||
// (byteBufferWAL) and waits until numBlocks are created. Then it returns a WAL
|
// (byteBufferWAL) and waits until numBlocks are created. Then it returns a WAL
|
||||||
// content.
|
// content.
|
||||||
func WALWithNBlocks(numBlocks int) (data []byte, err error) {
|
func WALWithNBlocks(numBlocks int) (data []byte, err error) {
|
||||||
config := getConfig()
|
config := getConfig()
|
||||||
|
|
||||||
app := dummy.NewPersistentDummyApplication(filepath.Join(config.DBDir(), "wal_generator"))
|
app := kvstore.NewPersistentKVStoreApplication(filepath.Join(config.DBDir(), "wal_generator"))
|
||||||
|
|
||||||
logger := log.TestingLogger().With("wal_generator", "wal_generator")
|
logger := log.TestingLogger().With("wal_generator", "wal_generator")
|
||||||
logger.Info("generating WAL (last height msg excluded)", "numBlocks", numBlocks)
|
logger.Info("generating WAL (last height msg excluded)", "numBlocks", numBlocks)
|
||||||
@@ -40,7 +40,7 @@ func WALWithNBlocks(numBlocks int) (data []byte, err error) {
|
|||||||
// COPY PASTE FROM node.go WITH A FEW MODIFICATIONS
|
// COPY PASTE FROM node.go WITH A FEW MODIFICATIONS
|
||||||
// NOTE: we can't import node package because of circular dependency
|
// NOTE: we can't import node package because of circular dependency
|
||||||
privValidatorFile := config.PrivValidatorFile()
|
privValidatorFile := config.PrivValidatorFile()
|
||||||
privValidator := types.LoadOrGenPrivValidatorFS(privValidatorFile)
|
privValidator := pvm.LoadOrGenFilePV(privValidatorFile)
|
||||||
genDoc, err := types.GenesisDocFromFile(config.GenesisFile())
|
genDoc, err := types.GenesisDocFromFile(config.GenesisFile())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to read genesis file")
|
return nil, errors.Wrap(err, "failed to read genesis file")
|
||||||
@@ -52,7 +52,7 @@ func WALWithNBlocks(numBlocks int) (data []byte, err error) {
|
|||||||
return nil, errors.Wrap(err, "failed to make genesis state")
|
return nil, errors.Wrap(err, "failed to make genesis state")
|
||||||
}
|
}
|
||||||
blockStore := bc.NewBlockStore(blockStoreDB)
|
blockStore := bc.NewBlockStore(blockStoreDB)
|
||||||
handshaker := NewHandshaker(stateDB, state, blockStore)
|
handshaker := NewHandshaker(stateDB, state, blockStore, genDoc.AppState())
|
||||||
proxyApp := proxy.NewAppConns(proxy.NewLocalClientCreator(app), handshaker)
|
proxyApp := proxy.NewAppConns(proxy.NewLocalClientCreator(app), handshaker)
|
||||||
proxyApp.SetLogger(logger.With("module", "proxy"))
|
proxyApp.SetLogger(logger.With("module", "proxy"))
|
||||||
if err := proxyApp.Start(); err != nil {
|
if err := proxyApp.Start(); err != nil {
|
||||||
@@ -116,7 +116,7 @@ func makePathname() string {
|
|||||||
func randPort() int {
|
func randPort() int {
|
||||||
// returns between base and base + spread
|
// returns between base and base + spread
|
||||||
base, spread := 20000, 20000
|
base, spread := 20000, 20000
|
||||||
return base + rand.Intn(spread)
|
return base + cmn.RandIntn(spread)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeAddrs() (string, string, string) {
|
func makeAddrs() (string, string, string) {
|
||||||
|
@@ -3,11 +3,10 @@ package consensus
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"sync"
|
// "sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
wire "github.com/tendermint/go-wire"
|
|
||||||
"github.com/tendermint/tendermint/consensus/types"
|
"github.com/tendermint/tendermint/consensus/types"
|
||||||
tmtypes "github.com/tendermint/tendermint/types"
|
tmtypes "github.com/tendermint/tendermint/types"
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
@@ -36,7 +35,7 @@ func TestWALEncoderDecoder(t *testing.T) {
|
|||||||
decoded, err := dec.Decode()
|
decoded, err := dec.Decode()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, msg.Time.Truncate(time.Millisecond), decoded.Time)
|
assert.Equal(t, msg.Time.UTC(), decoded.Time)
|
||||||
assert.Equal(t, msg.Msg, decoded.Msg)
|
assert.Equal(t, msg.Msg, decoded.Msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,6 +67,7 @@ func TestWALSearchForEndHeight(t *testing.T) {
|
|||||||
assert.Equal(t, rs.Height, h+1, cmn.Fmt("wrong height"))
|
assert.Equal(t, rs.Height, h+1, cmn.Fmt("wrong height"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
var initOnce sync.Once
|
var initOnce sync.Once
|
||||||
|
|
||||||
func registerInterfacesOnce() {
|
func registerInterfacesOnce() {
|
||||||
@@ -78,6 +78,7 @@ func registerInterfacesOnce() {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
func nBytes(n int) []byte {
|
func nBytes(n int) []byte {
|
||||||
buf := make([]byte, n)
|
buf := make([]byte, n)
|
||||||
@@ -86,7 +87,7 @@ func nBytes(n int) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func benchmarkWalDecode(b *testing.B, n int) {
|
func benchmarkWalDecode(b *testing.B, n int) {
|
||||||
registerInterfacesOnce()
|
// registerInterfacesOnce()
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
enc := NewWALEncoder(buf)
|
enc := NewWALEncoder(buf)
|
||||||
|
14
consensus/wire.go
Normal file
14
consensus/wire.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package consensus
|
||||||
|
|
||||||
|
import (
|
||||||
|
amino "github.com/tendermint/go-amino"
|
||||||
|
"github.com/tendermint/go-crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cdc = amino.NewCodec()
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RegisterConsensusMessages(cdc)
|
||||||
|
RegisterWALMessages(cdc)
|
||||||
|
crypto.RegisterAmino(cdc)
|
||||||
|
}
|
68
docker-compose.yml
Normal file
68
docker-compose.yml
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
node0:
|
||||||
|
container_name: node0
|
||||||
|
image: "tendermint/localnode"
|
||||||
|
ports:
|
||||||
|
- "46656-46657:46656-46657"
|
||||||
|
environment:
|
||||||
|
- ID=0
|
||||||
|
- LOG=${LOG:-tendermint.log}
|
||||||
|
volumes:
|
||||||
|
- ${FOLDER:-./build}:/tendermint:Z
|
||||||
|
networks:
|
||||||
|
localnet:
|
||||||
|
ipv4_address: 192.167.10.2
|
||||||
|
|
||||||
|
node1:
|
||||||
|
container_name: node1
|
||||||
|
image: "tendermint/localnode"
|
||||||
|
ports:
|
||||||
|
- "46659-46660:46656-46657"
|
||||||
|
environment:
|
||||||
|
- ID=1
|
||||||
|
- LOG=${LOG:-tendermint.log}
|
||||||
|
volumes:
|
||||||
|
- ${FOLDER:-./build}:/tendermint:Z
|
||||||
|
networks:
|
||||||
|
localnet:
|
||||||
|
ipv4_address: 192.167.10.3
|
||||||
|
|
||||||
|
node2:
|
||||||
|
container_name: node2
|
||||||
|
image: "tendermint/localnode"
|
||||||
|
environment:
|
||||||
|
- ID=2
|
||||||
|
- LOG=${LOG:-tendermint.log}
|
||||||
|
ports:
|
||||||
|
- "46661-46662:46656-46657"
|
||||||
|
volumes:
|
||||||
|
- ${FOLDER:-./build}:/tendermint:Z
|
||||||
|
networks:
|
||||||
|
localnet:
|
||||||
|
ipv4_address: 192.167.10.4
|
||||||
|
|
||||||
|
node3:
|
||||||
|
container_name: node3
|
||||||
|
image: "tendermint/localnode"
|
||||||
|
environment:
|
||||||
|
- ID=3
|
||||||
|
- LOG=${LOG:-tendermint.log}
|
||||||
|
ports:
|
||||||
|
- "46663-46664:46656-46657"
|
||||||
|
volumes:
|
||||||
|
- ${FOLDER:-./build}:/tendermint:Z
|
||||||
|
networks:
|
||||||
|
localnet:
|
||||||
|
ipv4_address: 192.167.10.5
|
||||||
|
|
||||||
|
networks:
|
||||||
|
localnet:
|
||||||
|
driver: bridge
|
||||||
|
ipam:
|
||||||
|
driver: default
|
||||||
|
config:
|
||||||
|
-
|
||||||
|
subnet: 192.167.10.0/16
|
||||||
|
|
7
docker-compose/Makefile
Normal file
7
docker-compose/Makefile
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Makefile for the "localnode" docker image.
|
||||||
|
|
||||||
|
all:
|
||||||
|
docker build --tag tendermint/localnode localnode
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
|
40
docker-compose/README.rst
Normal file
40
docker-compose/README.rst
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
localnode
|
||||||
|
=========
|
||||||
|
|
||||||
|
It is assumed that you have already `setup docker <https://docs.docker.com/engine/installation/>`__.
|
||||||
|
|
||||||
|
Description
|
||||||
|
-----------
|
||||||
|
Image for local testnets.
|
||||||
|
|
||||||
|
Add the tendermint binary to the image by attaching it in a folder to the `/tendermint` mount point.
|
||||||
|
|
||||||
|
It assumes that the configuration was created by the `tendermint testnet` command and it is also attached to the `/tendermint` mount point.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
This example builds a linux tendermint binary under the `build/` folder, creates tendermint configuration for a single-node validator and runs the node:
|
||||||
|
```
|
||||||
|
cd $GOPATH/src/github.com/tendermint/tendermint
|
||||||
|
|
||||||
|
#Build binary
|
||||||
|
make build-linux
|
||||||
|
|
||||||
|
#Create configuration
|
||||||
|
docker run -e LOG="stdout" -v `pwd`/build:/tendermint tendermint/localnode testnet --o . --v 1
|
||||||
|
|
||||||
|
#Run the node
|
||||||
|
docker run -v `pwd`/build:/tendermint tendermint/localnode
|
||||||
|
```
|
||||||
|
|
||||||
|
Logging
|
||||||
|
-------
|
||||||
|
Log is saved under the attached volume, in the `tendermint.log` file. If the `LOG` environment variable is set to `stdout` at start, the log is not saved, but printed on the screen.
|
||||||
|
|
||||||
|
Special binaries
|
||||||
|
----------------
|
||||||
|
If you have multiple binaries with different names, you can specify which one to run with the BINARY environment variable. The path of the binary is relative to the attached volume.
|
||||||
|
|
||||||
|
docker-compose.yml
|
||||||
|
==================
|
||||||
|
This file creates a 4-node network using the localnode image. The nodes of the network are exposed to the host machine on ports 46656-46657, 46659-46660, 46661-46662, 46663-46664 respectively.
|
||||||
|
|
16
docker-compose/localnode/Dockerfile
Normal file
16
docker-compose/localnode/Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
FROM alpine:3.7
|
||||||
|
MAINTAINER Greg Szabo <greg@tendermint.com>
|
||||||
|
|
||||||
|
RUN apk update && \
|
||||||
|
apk upgrade && \
|
||||||
|
apk --no-cache add curl jq file
|
||||||
|
|
||||||
|
VOLUME [ /tendermint ]
|
||||||
|
WORKDIR /tendermint
|
||||||
|
EXPOSE 46656 46657
|
||||||
|
ENTRYPOINT ["/usr/bin/wrapper.sh"]
|
||||||
|
CMD ["node", "--proxy_app dummy"]
|
||||||
|
STOPSIGNAL SIGTERM
|
||||||
|
|
||||||
|
COPY wrapper.sh /usr/bin/wrapper.sh
|
||||||
|
|
33
docker-compose/localnode/wrapper.sh
Executable file
33
docker-compose/localnode/wrapper.sh
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
##
|
||||||
|
## Input parameters
|
||||||
|
##
|
||||||
|
BINARY=/tendermint/${BINARY:-tendermint}
|
||||||
|
ID=${ID:-0}
|
||||||
|
LOG=${LOG:-tendermint.log}
|
||||||
|
|
||||||
|
##
|
||||||
|
## Assert linux binary
|
||||||
|
##
|
||||||
|
if ! [ -f "${BINARY}" ]; then
|
||||||
|
echo "The binary `basename ${BINARY}` cannot be found. Please add the binary to the shared folder. Please use the BINARY environment variable if the name of the binary is not 'tendermint' E.g.: -e BINARY=tendermint_my_test_version"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
BINARY_CHECK="`file $BINARY | grep 'ELF 64-bit LSB executable, x86-64'`"
|
||||||
|
if [ -z "${BINARY_CHECK}" ]; then
|
||||||
|
echo "Binary needs to be OS linux, ARCH amd64"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
##
|
||||||
|
## Run binary with all parameters
|
||||||
|
##
|
||||||
|
export TMHOME="/tendermint/node${ID}"
|
||||||
|
|
||||||
|
if [ -d "${TMHOME}/${LOG}" ]; then
|
||||||
|
"$BINARY" $@ | tee "${TMHOME}/${LOG}"
|
||||||
|
else
|
||||||
|
"$BINARY" $@
|
||||||
|
fi
|
||||||
|
|
@@ -16,15 +16,15 @@ Next, install the ``abci-cli`` tool and example applications:
|
|||||||
|
|
||||||
go get -u github.com/tendermint/abci/cmd/abci-cli
|
go get -u github.com/tendermint/abci/cmd/abci-cli
|
||||||
|
|
||||||
If this fails, you may need to use ``glide`` to get vendored
|
If this fails, you may need to use `dep <https://github.com/golang/dep>`__ to get vendored
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
go get github.com/Masterminds/glide
|
|
||||||
cd $GOPATH/src/github.com/tendermint/abci
|
cd $GOPATH/src/github.com/tendermint/abci
|
||||||
glide install
|
make get_tools
|
||||||
go install ./cmd/abci-cli
|
make get_vendor_deps
|
||||||
|
make install
|
||||||
|
|
||||||
Now run ``abci-cli`` to see the list of commands:
|
Now run ``abci-cli`` to see the list of commands:
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ Now run ``abci-cli`` to see the list of commands:
|
|||||||
console Start an interactive abci console for multiple commands
|
console Start an interactive abci console for multiple commands
|
||||||
counter ABCI demo example
|
counter ABCI demo example
|
||||||
deliver_tx Deliver a new tx to the application
|
deliver_tx Deliver a new tx to the application
|
||||||
dummy ABCI demo example
|
kvstore ABCI demo example
|
||||||
echo Have the application echo a message
|
echo Have the application echo a message
|
||||||
help Help about any command
|
help Help about any command
|
||||||
info Get some info about the application
|
info Get some info about the application
|
||||||
@@ -56,8 +56,8 @@ Now run ``abci-cli`` to see the list of commands:
|
|||||||
Use "abci-cli [command] --help" for more information about a command.
|
Use "abci-cli [command] --help" for more information about a command.
|
||||||
|
|
||||||
|
|
||||||
Dummy - First Example
|
KVStore - First Example
|
||||||
---------------------
|
-----------------------
|
||||||
|
|
||||||
The ``abci-cli`` tool lets us send ABCI messages to our application, to
|
The ``abci-cli`` tool lets us send ABCI messages to our application, to
|
||||||
help build and debug them.
|
help build and debug them.
|
||||||
@@ -66,8 +66,8 @@ The most important messages are ``deliver_tx``, ``check_tx``, and
|
|||||||
``commit``, but there are others for convenience, configuration, and
|
``commit``, but there are others for convenience, configuration, and
|
||||||
information purposes.
|
information purposes.
|
||||||
|
|
||||||
We'll start a dummy application, which was installed at the same time as
|
We'll start a kvstore application, which was installed at the same time as
|
||||||
``abci-cli`` above. The dummy just stores transactions in a merkle tree.
|
``abci-cli`` above. The kvstore just stores transactions in a merkle tree.
|
||||||
|
|
||||||
Its code can be found `here <https://github.com/tendermint/abci/blob/master/cmd/abci-cli/abci-cli.go>`__ and looks like:
|
Its code can be found `here <https://github.com/tendermint/abci/blob/master/cmd/abci-cli/abci-cli.go>`__ and looks like:
|
||||||
|
|
||||||
@@ -75,20 +75,20 @@ Its code can be found `here <https://github.com/tendermint/abci/blob/master/cmd/
|
|||||||
|
|
||||||
.. container:: header
|
.. container:: header
|
||||||
|
|
||||||
**Show/Hide Dummy Example**
|
**Show/Hide KVStore Example**
|
||||||
|
|
||||||
.. code-block:: go
|
.. code-block:: go
|
||||||
|
|
||||||
func cmdDummy(cmd *cobra.Command, args []string) error {
|
func cmdKVStore(cmd *cobra.Command, args []string) error {
|
||||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||||
|
|
||||||
// Create the application - in memory or persisted to disk
|
// Create the application - in memory or persisted to disk
|
||||||
var app types.Application
|
var app types.Application
|
||||||
if flagPersist == "" {
|
if flagPersist == "" {
|
||||||
app = dummy.NewDummyApplication()
|
app = kvstore.NewKVStoreApplication()
|
||||||
} else {
|
} else {
|
||||||
app = dummy.NewPersistentDummyApplication(flagPersist)
|
app = kvstore.NewPersistentKVStoreApplication(flagPersist)
|
||||||
app.(*dummy.PersistentDummyApplication).SetLogger(logger.With("module", "dummy"))
|
app.(*kvstore.PersistentKVStoreApplication).SetLogger(logger.With("module", "kvstore"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the listener
|
// Start the listener
|
||||||
@@ -113,7 +113,7 @@ Start by running:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
abci-cli dummy
|
abci-cli kvstore
|
||||||
|
|
||||||
And in another terminal, run
|
And in another terminal, run
|
||||||
|
|
||||||
@@ -229,7 +229,7 @@ Counter - Another Example
|
|||||||
Now that we've got the hang of it, let's try another application, the
|
Now that we've got the hang of it, let's try another application, the
|
||||||
"counter" app.
|
"counter" app.
|
||||||
|
|
||||||
Like the dummy app, its code can be found `here <https://github.com/tendermint/abci/blob/master/cmd/abci-cli/abci-cli.go>`__ and looks like:
|
Like the kvstore app, its code can be found `here <https://github.com/tendermint/abci/blob/master/cmd/abci-cli/abci-cli.go>`__ and looks like:
|
||||||
|
|
||||||
.. container:: toggle
|
.. container:: toggle
|
||||||
|
|
||||||
@@ -288,7 +288,7 @@ other peers.
|
|||||||
In this instance of the counter app, ``check_tx`` only allows
|
In this instance of the counter app, ``check_tx`` only allows
|
||||||
transactions whose integer is greater than the last committed one.
|
transactions whose integer is greater than the last committed one.
|
||||||
|
|
||||||
Let's kill the console and the dummy application, and start the counter
|
Let's kill the console and the kvstore application, and start the counter
|
||||||
app:
|
app:
|
||||||
|
|
||||||
::
|
::
|
||||||
@@ -328,7 +328,7 @@ In another window, start the ``abci-cli console``:
|
|||||||
-> data.hex: 0x7B22686173686573223A302C22747873223A327D
|
-> data.hex: 0x7B22686173686573223A302C22747873223A327D
|
||||||
|
|
||||||
This is a very simple application, but between ``counter`` and
|
This is a very simple application, but between ``counter`` and
|
||||||
``dummy``, its easy to see how you can build out arbitrary application
|
``kvstore``, its easy to see how you can build out arbitrary application
|
||||||
states on top of the ABCI. `Hyperledger's
|
states on top of the ABCI. `Hyperledger's
|
||||||
Burrow <https://github.com/hyperledger/burrow>`__ also runs atop ABCI,
|
Burrow <https://github.com/hyperledger/burrow>`__ also runs atop ABCI,
|
||||||
bringing with it Ethereum-like accounts, the Ethereum virtual-machine,
|
bringing with it Ethereum-like accounts, the Ethereum virtual-machine,
|
||||||
|
@@ -142,10 +142,10 @@ It is unlikely that you will need to implement a client. For details of
|
|||||||
our client, see
|
our client, see
|
||||||
`here <https://github.com/tendermint/abci/tree/master/client>`__.
|
`here <https://github.com/tendermint/abci/tree/master/client>`__.
|
||||||
|
|
||||||
Most of the examples below are from `dummy application
|
Most of the examples below are from `kvstore application
|
||||||
<https://github.com/tendermint/abci/blob/master/example/dummy/dummy.go>`__,
|
<https://github.com/tendermint/abci/blob/master/example/kvstore/kvstore.go>`__,
|
||||||
which is a part of the abci repo. `persistent_dummy application
|
which is a part of the abci repo. `persistent_kvstore application
|
||||||
<https://github.com/tendermint/abci/blob/master/example/dummy/persistent_dummy.go>`__
|
<https://github.com/tendermint/abci/blob/master/example/kvstore/persistent_kvstore.go>`__
|
||||||
is used to show ``BeginBlock``, ``EndBlock`` and ``InitChain``
|
is used to show ``BeginBlock``, ``EndBlock`` and ``InitChain``
|
||||||
example implementations.
|
example implementations.
|
||||||
|
|
||||||
@@ -202,7 +202,7 @@ mempool state.
|
|||||||
|
|
||||||
.. code-block:: go
|
.. code-block:: go
|
||||||
|
|
||||||
func (app *DummyApplication) CheckTx(tx []byte) types.Result {
|
func (app *KVStoreApplication) CheckTx(tx []byte) types.Result {
|
||||||
return types.OK
|
return types.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,7 +263,7 @@ merkle root of the data returned by the DeliverTx requests, or both.
|
|||||||
.. code-block:: go
|
.. code-block:: go
|
||||||
|
|
||||||
// tx is either "key=value" or just arbitrary bytes
|
// tx is either "key=value" or just arbitrary bytes
|
||||||
func (app *DummyApplication) DeliverTx(tx []byte) types.Result {
|
func (app *KVStoreApplication) DeliverTx(tx []byte) types.Result {
|
||||||
parts := strings.Split(string(tx), "=")
|
parts := strings.Split(string(tx), "=")
|
||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
app.state.Set([]byte(parts[0]), []byte(parts[1]))
|
app.state.Set([]byte(parts[0]), []byte(parts[1]))
|
||||||
@@ -327,7 +327,7 @@ job of the `Handshake <#handshake>`__.
|
|||||||
|
|
||||||
.. code-block:: go
|
.. code-block:: go
|
||||||
|
|
||||||
func (app *DummyApplication) Commit() types.Result {
|
func (app *KVStoreApplication) Commit() types.Result {
|
||||||
hash := app.state.Hash()
|
hash := app.state.Hash()
|
||||||
return types.NewResultOK(hash, "")
|
return types.NewResultOK(hash, "")
|
||||||
}
|
}
|
||||||
@@ -369,7 +369,7 @@ pick up from when it restarts. See information on the Handshake, below.
|
|||||||
.. code-block:: go
|
.. code-block:: go
|
||||||
|
|
||||||
// Track the block hash and header information
|
// Track the block hash and header information
|
||||||
func (app *PersistentDummyApplication) BeginBlock(params types.RequestBeginBlock) {
|
func (app *PersistentKVStoreApplication) BeginBlock(params types.RequestBeginBlock) {
|
||||||
// update latest block info
|
// update latest block info
|
||||||
app.blockHeader = params.Header
|
app.blockHeader = params.Header
|
||||||
|
|
||||||
@@ -423,7 +423,7 @@ for details on how it tracks validators.
|
|||||||
.. code-block:: go
|
.. code-block:: go
|
||||||
|
|
||||||
// Update the validator set
|
// Update the validator set
|
||||||
func (app *PersistentDummyApplication) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
|
func (app *PersistentKVStoreApplication) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
|
||||||
return types.ResponseEndBlock{ValidatorUpdates: app.ValUpdates}
|
return types.ResponseEndBlock{ValidatorUpdates: app.ValUpdates}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,7 +477,7 @@ Note: these query formats are subject to change!
|
|||||||
|
|
||||||
.. code-block:: go
|
.. code-block:: go
|
||||||
|
|
||||||
func (app *DummyApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
|
func (app *KVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
|
||||||
if reqQuery.Prove {
|
if reqQuery.Prove {
|
||||||
value, proof, exists := app.state.Proof(reqQuery.Data)
|
value, proof, exists := app.state.Proof(reqQuery.Data)
|
||||||
resQuery.Index = -1 // TODO make Proof return index
|
resQuery.Index = -1 // TODO make Proof return index
|
||||||
@@ -561,7 +561,7 @@ all blocks.
|
|||||||
|
|
||||||
.. code-block:: go
|
.. code-block:: go
|
||||||
|
|
||||||
func (app *DummyApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
|
func (app *KVStoreApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
|
||||||
return types.ResponseInfo{Data: cmn.Fmt("{\"size\":%v}", app.state.Size())}
|
return types.ResponseInfo{Data: cmn.Fmt("{\"size\":%v}", app.state.Size())}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -595,7 +595,7 @@ consensus params.
|
|||||||
.. code-block:: go
|
.. code-block:: go
|
||||||
|
|
||||||
// Save the validators in the merkle tree
|
// Save the validators in the merkle tree
|
||||||
func (app *PersistentDummyApplication) InitChain(params types.RequestInitChain) {
|
func (app *PersistentKVStoreApplication) InitChain(params types.RequestInitChain) {
|
||||||
for _, v := range params.Validators {
|
for _, v := range params.Validators {
|
||||||
r := app.updateValidator(v)
|
r := app.updateValidator(v)
|
||||||
if r.IsErr() {
|
if r.IsErr() {
|
||||||
|
29
docs/architecture/adr-008-priv-validator.md
Normal file
29
docs/architecture/adr-008-priv-validator.md
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# ADR 008: SocketPV
|
||||||
|
|
||||||
|
Tendermint node's should support only two in-process PrivValidator
|
||||||
|
implementations:
|
||||||
|
|
||||||
|
- FilePV uses an unencrypted private key in a "priv_validator.json" file - no
|
||||||
|
configuration required (just `tendermint init`).
|
||||||
|
- SocketPV uses a socket to send signing requests to another process - user is
|
||||||
|
responsible for starting that process themselves.
|
||||||
|
|
||||||
|
The SocketPV address can be provided via flags at the command line - doing so
|
||||||
|
will cause Tendermint to ignore any "priv_validator.json" file and to listen on
|
||||||
|
the given address for incoming connections from an external priv_validator
|
||||||
|
process. It will halt any operation until at least one external process
|
||||||
|
succesfully connected.
|
||||||
|
|
||||||
|
The external priv_validator process will dial the address to connect to
|
||||||
|
Tendermint, and then Tendermint will send requests on the ensuing connection to
|
||||||
|
sign votes and proposals. Thus the external process initiates the connection,
|
||||||
|
but the Tendermint process makes all requests. In a later stage we're going to
|
||||||
|
support multiple validators for fault tolerance. To prevent double signing they
|
||||||
|
need to be synced, which is deferred to an external solution (see #1185).
|
||||||
|
|
||||||
|
In addition, Tendermint will provide implementations that can be run in that
|
||||||
|
external process. These include:
|
||||||
|
|
||||||
|
- FilePV will encrypt the private key, and the user must enter password to
|
||||||
|
decrypt key when process is started.
|
||||||
|
- LedgerPV uses a Ledger Nano S to handle all signing.
|
15
docs/conf.py
15
docs/conf.py
@@ -41,15 +41,15 @@ templates_path = ['_templates']
|
|||||||
# The suffix(es) of source filenames.
|
# The suffix(es) of source filenames.
|
||||||
# You can specify multiple suffix as a list of string:
|
# You can specify multiple suffix as a list of string:
|
||||||
#
|
#
|
||||||
source_suffix = ['.rst', '.md']
|
#source_suffix = ['.rst', '.md']
|
||||||
# source_suffix = '.rst'
|
source_suffix = '.rst'
|
||||||
|
|
||||||
# The master toctree document.
|
# The master toctree document.
|
||||||
master_doc = 'index'
|
master_doc = 'index'
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = u'Tendermint'
|
project = u'Tendermint'
|
||||||
copyright = u'2017, The Authors'
|
copyright = u'2018, The Authors'
|
||||||
author = u'Tendermint'
|
author = u'Tendermint'
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
@@ -71,7 +71,7 @@ language = None
|
|||||||
# List of patterns, relative to source directory, that match files and
|
# List of patterns, relative to source directory, that match files and
|
||||||
# directories to ignore when looking for source files.
|
# directories to ignore when looking for source files.
|
||||||
# This patterns also effect to html_static_path and html_extra_path
|
# This patterns also effect to html_static_path and html_extra_path
|
||||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'architecture']
|
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'architecture', 'specification/new-spec', 'examples']
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
pygments_style = 'sphinx'
|
pygments_style = 'sphinx'
|
||||||
@@ -196,13 +196,12 @@ urllib.urlretrieve(tools_repo+tools_branch+'/mintnet-kubernetes/assets/statefuls
|
|||||||
urllib.urlretrieve(tools_repo+tools_branch+'/mintnet-kubernetes/assets/t_plus_k.png', filename=assets_dir+'/t_plus_k.png')
|
urllib.urlretrieve(tools_repo+tools_branch+'/mintnet-kubernetes/assets/t_plus_k.png', filename=assets_dir+'/t_plus_k.png')
|
||||||
|
|
||||||
urllib.urlretrieve(tools_repo+tools_branch+'/terraform-digitalocean/README.rst', filename=tools_dir+'/terraform-digitalocean.rst')
|
urllib.urlretrieve(tools_repo+tools_branch+'/terraform-digitalocean/README.rst', filename=tools_dir+'/terraform-digitalocean.rst')
|
||||||
urllib.urlretrieve(tools_repo+tools_branch+'/tm-bench/README.rst', filename=tools_dir+'/benchmarking-and-monitoring.rst')
|
urllib.urlretrieve(tools_repo+tools_branch+'/tm-bench/README.rst', filename=tools_dir+'/benchmarking.rst')
|
||||||
# the readme for below is included in tm-bench
|
urllib.urlretrieve('https://raw.githubusercontent.com/tendermint/tools/master/tm-monitor/README.rst', filename='tools/monitoring.rst')
|
||||||
# urllib.urlretrieve('https://raw.githubusercontent.com/tendermint/tools/master/tm-monitor/README.rst', filename='tools/tm-monitor.rst')
|
|
||||||
|
|
||||||
#### abci spec #################################
|
#### abci spec #################################
|
||||||
|
|
||||||
abci_repo = "https://raw.githubusercontent.com/tendermint/abci/"
|
abci_repo = "https://raw.githubusercontent.com/tendermint/abci/"
|
||||||
abci_branch = "spec-docs"
|
abci_branch = "develop"
|
||||||
|
|
||||||
urllib.urlretrieve(abci_repo+abci_branch+'/specification.rst', filename='abci-spec.rst')
|
urllib.urlretrieve(abci_repo+abci_branch+'/specification.rst', filename='abci-spec.rst')
|
||||||
|
@@ -11,26 +11,26 @@ Manual Deployments
|
|||||||
|
|
||||||
It's relatively easy to setup a Tendermint cluster manually. The only
|
It's relatively easy to setup a Tendermint cluster manually. The only
|
||||||
requirements for a particular Tendermint node are a private key for the
|
requirements for a particular Tendermint node are a private key for the
|
||||||
validator, stored as ``priv_validator.json``, and a list of the public
|
validator, stored as ``priv_validator.json``, a node key, stored as
|
||||||
keys of all validators, stored as ``genesis.json``. These files should
|
``node_key.json`` and a list of the public keys of all validators, stored as
|
||||||
be stored in ``~/.tendermint/config``, or wherever the ``$TMHOME`` variable
|
``genesis.json``. These files should be stored in ``~/.tendermint/config``, or
|
||||||
might be set to.
|
wherever the ``$TMHOME`` variable might be set to.
|
||||||
|
|
||||||
Here are the steps to setting up a testnet manually:
|
Here are the steps to setting up a testnet manually:
|
||||||
|
|
||||||
1) Provision nodes on your cloud provider of choice
|
1) Provision nodes on your cloud provider of choice
|
||||||
2) Install Tendermint and the application of interest on all nodes
|
2) Install Tendermint and the application of interest on all nodes
|
||||||
3) Generate a private key for each validator using
|
3) Generate a private key and a node key for each validator using
|
||||||
``tendermint gen_validator``
|
``tendermint init``
|
||||||
4) Compile a list of public keys for each validator into a
|
4) Compile a list of public keys for each validator into a
|
||||||
``genesis.json`` file.
|
``genesis.json`` file and replace the existing file with it.
|
||||||
5) Run ``tendermint node --p2p.persistent_peers=< peer addresses >`` on each node,
|
5) Run ``tendermint node --p2p.persistent_peers=< peer addresses >`` on each node,
|
||||||
where ``< peer addresses >`` is a comma separated list of the IP:PORT
|
where ``< peer addresses >`` is a comma separated list of the IP:PORT
|
||||||
combination for each node. The default port for Tendermint is
|
combination for each node. The default port for Tendermint is
|
||||||
``46656``. Thus, if the IP addresses of your nodes were
|
``46656``. Thus, if the IP addresses of your nodes were
|
||||||
``192.168.0.1, 192.168.0.2, 192.168.0.3, 192.168.0.4``, the command
|
``192.168.0.1, 192.168.0.2, 192.168.0.3, 192.168.0.4``, the command
|
||||||
would look like:
|
would look like:
|
||||||
``tendermint node --p2p.persistent_peers=192.168.0.1:46656,192.168.0.2:46656,192.168.0.3:46656,192.168.0.4:46656``.
|
``tendermint node --p2p.persistent_peers=96663a3dd0d7b9d17d4c8211b191af259621c693@192.168.0.1:46656, 429fcf25974313b95673f58d77eacdd434402665@192.168.0.2:46656, 0491d373a8e0fcf1023aaf18c51d6a1d0d4f31bd@192.168.0.3:46656, f9baeaa15fedf5e1ef7448dd60f46c01f1a9e9c4@192.168.0.4:46656``.
|
||||||
|
|
||||||
After a few seconds, all the nodes should connect to each other and start
|
After a few seconds, all the nodes should connect to each other and start
|
||||||
making blocks! For more information, see the Tendermint Networks section
|
making blocks! For more information, see the Tendermint Networks section
|
||||||
|
8
docs/determinism.rst
Normal file
8
docs/determinism.rst
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
On Determinism
|
||||||
|
==============
|
||||||
|
|
||||||
|
Arguably, the most difficult part of blockchain programming is determinism - that is, ensuring that sources of indeterminism do not creep into the design of such systems.
|
||||||
|
|
||||||
|
See `this issue <https://github.com/tendermint/abci/issues/56>`__ for more information on the potential sources of indeterminism.
|
||||||
|
|
||||||
|
|
@@ -5,7 +5,7 @@ The growing list of applications built using various pieces of the Tendermint st
|
|||||||
|
|
||||||
* https://tendermint.com/ecosystem
|
* https://tendermint.com/ecosystem
|
||||||
|
|
||||||
We thank the community for their contributions thus far and welcome the addition of new projects. A pull request can be submitted to `this file <https://github.com/tendermint/tendermint/blob/master/docs/ecosystem.rst>`__ to include your project.
|
We thank the community for their contributions thus far and welcome the addition of new projects. A pull request can be submitted to `this file <https://github.com/tendermint/aib-data/blob/master/json/ecosystem.json>`__ to include your project.
|
||||||
|
|
||||||
Other Tools
|
Other Tools
|
||||||
-----------
|
-----------
|
||||||
|
@@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
This is a quick start guide. If you have a vague idea about how Tendermint works
|
This is a quick start guide. If you have a vague idea about how Tendermint
|
||||||
and want to get started right away, continue. Otherwise, [review the documentation](http://tendermint.readthedocs.io/en/master/)
|
works and want to get started right away, continue. Otherwise, [review the
|
||||||
|
documentation](http://tendermint.readthedocs.io/en/master/).
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
@@ -12,7 +13,7 @@ and want to get started right away, continue. Otherwise, [review the documentati
|
|||||||
On a fresh Ubuntu 16.04 machine can be done with [this script](https://git.io/vNLfY), like so:
|
On a fresh Ubuntu 16.04 machine can be done with [this script](https://git.io/vNLfY), like so:
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -L https://git.io/vNLfY | bash
|
curl -L https://git.io/vxWlX | bash
|
||||||
source ~/.profile
|
source ~/.profile
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ Confirm installation:
|
|||||||
|
|
||||||
```
|
```
|
||||||
$ tendermint version
|
$ tendermint version
|
||||||
0.15.0-381fe19
|
0.18.0-XXXXXXX
|
||||||
```
|
```
|
||||||
|
|
||||||
## Initialization
|
## Initialization
|
||||||
@@ -71,7 +72,7 @@ Configuring a cluster is covered further below.
|
|||||||
Start tendermint with a simple in-process application:
|
Start tendermint with a simple in-process application:
|
||||||
|
|
||||||
```
|
```
|
||||||
tendermint node --proxy_app=dummy
|
tendermint node --proxy_app=kvstore
|
||||||
```
|
```
|
||||||
|
|
||||||
and blocks will start to stream in:
|
and blocks will start to stream in:
|
||||||
@@ -89,7 +90,7 @@ curl -s localhost:46657/status
|
|||||||
|
|
||||||
### Sending Transactions
|
### Sending Transactions
|
||||||
|
|
||||||
With the dummy app running, we can send transactions:
|
With the kvstore app running, we can send transactions:
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -s 'localhost:46657/broadcast_tx_commit?tx="abcd"'
|
curl -s 'localhost:46657/broadcast_tx_commit?tx="abcd"'
|
||||||
@@ -117,7 +118,9 @@ where the value is returned in hex.
|
|||||||
|
|
||||||
## Cluster of Nodes
|
## Cluster of Nodes
|
||||||
|
|
||||||
First create four Ubuntu cloud machines. The following was tested on Digital Ocean Ubuntu 16.04 x64 (3GB/1CPU, 20GB SSD). We'll refer to their respective IP addresses below as IP1, IP2, IP3, IP4.
|
First create four Ubuntu cloud machines. The following was tested on Digital
|
||||||
|
Ocean Ubuntu 16.04 x64 (3GB/1CPU, 20GB SSD). We'll refer to their respective IP
|
||||||
|
addresses below as IP1, IP2, IP3, IP4.
|
||||||
|
|
||||||
Then, `ssh` into each machine, and execute [this script](https://git.io/vNLfY):
|
Then, `ssh` into each machine, and execute [this script](https://git.io/vNLfY):
|
||||||
|
|
||||||
@@ -131,12 +134,16 @@ This will install `go` and other dependencies, get the Tendermint source code, t
|
|||||||
Next, `cd` into `docs/examples`. Each command below should be run from each node, in sequence:
|
Next, `cd` into `docs/examples`. Each command below should be run from each node, in sequence:
|
||||||
|
|
||||||
```
|
```
|
||||||
tendermint node --home ./node1 --proxy_app=dummy --p2p.seeds IP1:46656,IP2:46656,IP3:46656,IP4:46656
|
tendermint node --home ./node1 --proxy_app=kvstore --p2p.persistent_peers="3a558bd6f8c97453aa6c2372bb800e8b6ed8e6db@IP1:46656,ccf30d873fddda10a495f42687c8f33472a6569f@IP2:46656,9a4c3de5d6788a76c6ee3cd9ff41e3b45b4cfd14@IP3:46656,58e6f2ab297b3ceae107ba4c8c2898da5c009ff4@IP4:46656"
|
||||||
tendermint node --home ./node2 --proxy_app=dummy --p2p.seeds IP1:46656,IP2:46656,IP3:46656,IP4:46656
|
tendermint node --home ./node2 --proxy_app=kvstore --p2p.persistent_peers="3a558bd6f8c97453aa6c2372bb800e8b6ed8e6db@IP1:46656,ccf30d873fddda10a495f42687c8f33472a6569f@IP2:46656,9a4c3de5d6788a76c6ee3cd9ff41e3b45b4cfd14@IP3:46656,58e6f2ab297b3ceae107ba4c8c2898da5c009ff4@IP4:46656"
|
||||||
tendermint node --home ./node3 --proxy_app=dummy --p2p.seeds IP1:46656,IP2:46656,IP3:46656,IP4:46656
|
tendermint node --home ./node3 --proxy_app=kvstore --p2p.persistent_peers="3a558bd6f8c97453aa6c2372bb800e8b6ed8e6db@IP1:46656,ccf30d873fddda10a495f42687c8f33472a6569f@IP2:46656,9a4c3de5d6788a76c6ee3cd9ff41e3b45b4cfd14@IP3:46656,58e6f2ab297b3ceae107ba4c8c2898da5c009ff4@IP4:46656"
|
||||||
tendermint node --home ./node4 --proxy_app=dummy --p2p.seeds IP1:46656,IP2:46656,IP3:46656,IP4:46656
|
tendermint node --home ./node4 --proxy_app=kvstore --p2p.persistent_peers="3a558bd6f8c97453aa6c2372bb800e8b6ed8e6db@IP1:46656,ccf30d873fddda10a495f42687c8f33472a6569f@IP2:46656,9a4c3de5d6788a76c6ee3cd9ff41e3b45b4cfd14@IP3:46656,58e6f2ab297b3ceae107ba4c8c2898da5c009ff4@IP4:46656"
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that after the third node is started, blocks will start to stream in because >2/3 of validators (defined in the `genesis.json`) have come online. Seeds can also be specified in the `config.toml`. See [this PR](https://github.com/tendermint/tendermint/pull/792) for more information about configuration options.
|
Note that after the third node is started, blocks will start to stream in
|
||||||
|
because >2/3 of validators (defined in the `genesis.json`) have come online.
|
||||||
|
Seeds can also be specified in the `config.toml`. See [this
|
||||||
|
PR](https://github.com/tendermint/tendermint/pull/792) for more information
|
||||||
|
about configuration options.
|
||||||
|
|
||||||
Transactions can then be sent as covered in the single, local node example above.
|
Transactions can then be sent as covered in the single, local node example above.
|
||||||
|
@@ -4,8 +4,8 @@
|
|||||||
# and has only been tested on Digital Ocean
|
# and has only been tested on Digital Ocean
|
||||||
|
|
||||||
# get and unpack golang
|
# get and unpack golang
|
||||||
curl -O https://storage.googleapis.com/golang/go1.9.2.linux-amd64.tar.gz
|
curl -O https://storage.googleapis.com/golang/go1.10.linux-amd64.tar.gz
|
||||||
tar -xvf go1.9.2.linux-amd64.tar.gz
|
tar -xvf go1.10.linux-amd64.tar.gz
|
||||||
|
|
||||||
apt install make
|
apt install make
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ go get $REPO
|
|||||||
cd $GOPATH/src/$REPO
|
cd $GOPATH/src/$REPO
|
||||||
|
|
||||||
## build
|
## build
|
||||||
git checkout v0.15.0
|
git checkout v0.18.0
|
||||||
make get_tools
|
make get_tools
|
||||||
make get_vendor_deps
|
make get_vendor_deps
|
||||||
make install
|
make install
|
6
docs/examples/node1/node_key.json
Normal file
6
docs/examples/node1/node_key.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"priv_key" : {
|
||||||
|
"data" : "DA9BAABEA7211A6D93D9A1986B4279EAB3021FAA1653D459D53E6AB4D1CFB4C69BF7D52E48CF00AC5779AA0A6D3C368955D5636A677F72370B8ED19989714CFC",
|
||||||
|
"type" : "ed25519"
|
||||||
|
}
|
||||||
|
}
|
6
docs/examples/node2/node_key.json
Normal file
6
docs/examples/node2/node_key.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"priv_key" : {
|
||||||
|
"data" : "F7BCABA165DFC0DDD50AE563EFB285BAA236EA805D35612504238A36EFA105958756442B1D9F942D7ABD259F2D59671657B6378E9C7194342A7AAA47A66D1E95",
|
||||||
|
"type" : "ed25519"
|
||||||
|
}
|
||||||
|
}
|
6
docs/examples/node3/node_key.json
Normal file
6
docs/examples/node3/node_key.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"priv_key" : {
|
||||||
|
"data" : "95136FCC97E4446B3141EDF9841078107ECE755E99925D79CCBF91085492680B3CA1034D9917DF1DED4E4AB2D9BC225919F6CB2176F210D2368697CC339DF4E7",
|
||||||
|
"type" : "ed25519"
|
||||||
|
}
|
||||||
|
}
|
6
docs/examples/node4/node_key.json
Normal file
6
docs/examples/node4/node_key.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"priv_key" : {
|
||||||
|
"data" : "8895D6C9A1B46AB83A8E2BAE2121B8C3E245B9E9126EBD797FEAC5058285F2F64FDE2E8182C88AD5185A49D837C581465D57BD478C41865A66D7D9742D8AEF57",
|
||||||
|
"type" : "ed25519"
|
||||||
|
}
|
||||||
|
}
|
@@ -27,38 +27,38 @@ Then run
|
|||||||
|
|
||||||
go get -u github.com/tendermint/abci/cmd/abci-cli
|
go get -u github.com/tendermint/abci/cmd/abci-cli
|
||||||
|
|
||||||
If there is an error, install and run the ``glide`` tool to pin the
|
If there is an error, install and run the `dep <https://github.com/golang/dep>`__ tool to pin the
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
go get github.com/Masterminds/glide
|
|
||||||
cd $GOPATH/src/github.com/tendermint/abci
|
cd $GOPATH/src/github.com/tendermint/abci
|
||||||
glide install
|
make get_tools
|
||||||
go install ./cmd/abci-cli
|
make get_vendor_deps
|
||||||
|
make install
|
||||||
|
|
||||||
Now you should have the ``abci-cli`` installed; you'll see
|
Now you should have the ``abci-cli`` installed; you'll see
|
||||||
a couple of commands (``counter`` and ``dummy``) that are
|
a couple of commands (``counter`` and ``kvstore``) that are
|
||||||
example applications written in Go. See below for an application
|
example applications written in Go. See below for an application
|
||||||
written in JavaScript.
|
written in JavaScript.
|
||||||
|
|
||||||
Now, let's run some apps!
|
Now, let's run some apps!
|
||||||
|
|
||||||
Dummy - A First Example
|
KVStore - A First Example
|
||||||
-----------------------
|
-------------------------
|
||||||
|
|
||||||
The dummy app is a `Merkle
|
The kvstore app is a `Merkle
|
||||||
tree <https://en.wikipedia.org/wiki/Merkle_tree>`__ that just stores all
|
tree <https://en.wikipedia.org/wiki/Merkle_tree>`__ that just stores all
|
||||||
transactions. If the transaction contains an ``=``, e.g. ``key=value``,
|
transactions. If the transaction contains an ``=``, e.g. ``key=value``,
|
||||||
then the ``value`` is stored under the ``key`` in the Merkle tree.
|
then the ``value`` is stored under the ``key`` in the Merkle tree.
|
||||||
Otherwise, the full transaction bytes are stored as the key and the
|
Otherwise, the full transaction bytes are stored as the key and the
|
||||||
value.
|
value.
|
||||||
|
|
||||||
Let's start a dummy application.
|
Let's start a kvstore application.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
abci-cli dummy
|
abci-cli kvstore
|
||||||
|
|
||||||
In another terminal, we can start Tendermint. If you have never run
|
In another terminal, we can start Tendermint. If you have never run
|
||||||
Tendermint before, use:
|
Tendermint before, use:
|
||||||
@@ -81,11 +81,10 @@ Tendermint node as follows:
|
|||||||
|
|
||||||
curl -s localhost:46657/status
|
curl -s localhost:46657/status
|
||||||
|
|
||||||
The ``-s`` just silences ``curl``. For nicer output, pipe the result
|
The ``-s`` just silences ``curl``. For nicer output, pipe the result into a
|
||||||
into a tool like `jq <https://stedolan.github.io/jq/>`__ or
|
tool like `jq <https://stedolan.github.io/jq/>`__ or ``json_pp``.
|
||||||
`jsonpp <https://github.com/jmhodges/jsonpp>`__.
|
|
||||||
|
|
||||||
Now let's send some transactions to the dummy.
|
Now let's send some transactions to the kvstore.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -104,17 +103,23 @@ like:
|
|||||||
"id": "",
|
"id": "",
|
||||||
"result": {
|
"result": {
|
||||||
"check_tx": {
|
"check_tx": {
|
||||||
"code": 0,
|
"fee": {}
|
||||||
"data": "",
|
|
||||||
"log": ""
|
|
||||||
},
|
},
|
||||||
"deliver_tx": {
|
"deliver_tx": {
|
||||||
"code": 0,
|
"tags": [
|
||||||
"data": "",
|
{
|
||||||
"log": ""
|
"key": "YXBwLmNyZWF0b3I=",
|
||||||
|
"value": "amFl"
|
||||||
},
|
},
|
||||||
"hash": "2B8EC32BA2579B3B8606E42C06DE2F7AFA2556EF",
|
{
|
||||||
"height": 154
|
"key": "YXBwLmtleQ==",
|
||||||
|
"value": "YWJjZA=="
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"fee": {}
|
||||||
|
},
|
||||||
|
"hash": "9DF66553F98DE3C26E3C3317A3E4CED54F714E39",
|
||||||
|
"height": 14
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,20 +139,17 @@ The result should look like:
|
|||||||
"id": "",
|
"id": "",
|
||||||
"result": {
|
"result": {
|
||||||
"response": {
|
"response": {
|
||||||
"code": 0,
|
"log": "exists",
|
||||||
"index": 0,
|
"index": "-1",
|
||||||
"key": "",
|
"key": "YWJjZA==",
|
||||||
"value": "61626364",
|
"value": "YWJjZA=="
|
||||||
"proof": "",
|
|
||||||
"height": 0,
|
|
||||||
"log": "exists"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Note the ``value`` in the result (``61626364``); this is the
|
Note the ``value`` in the result (``YWJjZA==``); this is the
|
||||||
hex-encoding of the ASCII of ``abcd``. You can verify this in
|
base64-encoding of the ASCII of ``abcd``. You can verify this in
|
||||||
a python 2 shell by running ``"61626364".decode('hex')`` or in python 3 shell by running ``import codecs; codecs.decode("61626364", 'hex').decode('ascii')``. Stay
|
a python 2 shell by running ``"61626364".decode('base64')`` or in python 3 shell by running ``import codecs; codecs.decode("61626364", 'base64').decode('ascii')``. Stay
|
||||||
tuned for a future release that `makes this output more human-readable <https://github.com/tendermint/abci/issues/32>`__.
|
tuned for a future release that `makes this output more human-readable <https://github.com/tendermint/abci/issues/32>`__.
|
||||||
|
|
||||||
Now let's try setting a different key and value:
|
Now let's try setting a different key and value:
|
||||||
@@ -157,7 +159,7 @@ Now let's try setting a different key and value:
|
|||||||
curl -s 'localhost:46657/broadcast_tx_commit?tx="name=satoshi"'
|
curl -s 'localhost:46657/broadcast_tx_commit?tx="name=satoshi"'
|
||||||
|
|
||||||
Now if we query for ``name``, we should get ``satoshi``, or
|
Now if we query for ``name``, we should get ``satoshi``, or
|
||||||
``7361746F736869`` in hex:
|
``c2F0b3NoaQ==`` in base64:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -192,7 +194,7 @@ In this instance of the counter app, with ``serial=on``, ``CheckTx``
|
|||||||
only allows transactions whose integer is greater than the last
|
only allows transactions whose integer is greater than the last
|
||||||
committed one.
|
committed one.
|
||||||
|
|
||||||
Let's kill the previous instance of ``tendermint`` and the ``dummy``
|
Let's kill the previous instance of ``tendermint`` and the ``kvstore``
|
||||||
application, and start the counter app. We can enable ``serial=on`` with
|
application, and start the counter app. We can enable ``serial=on`` with
|
||||||
a flag:
|
a flag:
|
||||||
|
|
||||||
@@ -226,17 +228,15 @@ the number ``1``. If instead, we try to send a ``5``, we get an error:
|
|||||||
"id": "",
|
"id": "",
|
||||||
"result": {
|
"result": {
|
||||||
"check_tx": {
|
"check_tx": {
|
||||||
"code": 0,
|
"fee": {}
|
||||||
"data": "",
|
|
||||||
"log": ""
|
|
||||||
},
|
},
|
||||||
"deliver_tx": {
|
"deliver_tx": {
|
||||||
"code": 3,
|
"code": 2,
|
||||||
"data": "",
|
"log": "Invalid nonce. Expected 1, got 5",
|
||||||
"log": "Invalid nonce. Expected 1, got 5"
|
"fee": {}
|
||||||
},
|
},
|
||||||
"hash": "33B93DFF98749B0D6996A70F64071347060DC19C",
|
"hash": "33B93DFF98749B0D6996A70F64071347060DC19C",
|
||||||
"height": 38
|
"height": 34
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,17 +250,13 @@ But if we send a ``1``, it works again:
|
|||||||
"id": "",
|
"id": "",
|
||||||
"result": {
|
"result": {
|
||||||
"check_tx": {
|
"check_tx": {
|
||||||
"code": 0,
|
"fee": {}
|
||||||
"data": "",
|
|
||||||
"log": ""
|
|
||||||
},
|
},
|
||||||
"deliver_tx": {
|
"deliver_tx": {
|
||||||
"code": 0,
|
"fee": {}
|
||||||
"data": "",
|
|
||||||
"log": ""
|
|
||||||
},
|
},
|
||||||
"hash": "F17854A977F6FA7EEA1BD758E296710B86F72F3D",
|
"hash": "F17854A977F6FA7EEA1BD758E296710B86F72F3D",
|
||||||
"height": 87
|
"height": 60
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,7 +309,7 @@ Neat, eh?
|
|||||||
Basecoin - A More Interesting Example
|
Basecoin - A More Interesting Example
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
We saved the best for last; the `Cosmos SDK <https://github.com/cosmos/cosmos-sdk>`__ is a general purpose framework for building cryptocurrencies. Unlike the ``dummy`` and ``counter``, which are strictly for example purposes. The reference implementation of Cosmos SDK is ``basecoin``, which demonstrates how to use the building blocks of the Cosmos SDK.
|
We saved the best for last; the `Cosmos SDK <https://github.com/cosmos/cosmos-sdk>`__ is a general purpose framework for building cryptocurrencies. Unlike the ``kvstore`` and ``counter``, which are strictly for example purposes. The reference implementation of Cosmos SDK is ``basecoin``, which demonstrates how to use the building blocks of the Cosmos SDK.
|
||||||
|
|
||||||
The default ``basecoin`` application is a multi-asset cryptocurrency
|
The default ``basecoin`` application is a multi-asset cryptocurrency
|
||||||
that supports inter-blockchain communication (IBC). For more details on how
|
that supports inter-blockchain communication (IBC). For more details on how
|
||||||
|
@@ -5,7 +5,7 @@ Walk through example
|
|||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
We first create three connections (mempool, consensus and query) to the
|
We first create three connections (mempool, consensus and query) to the
|
||||||
application (locally running dummy in this case).
|
application (running ``kvstore`` locally in this case).
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ Next we replay all the messages from the WAL.
|
|||||||
::
|
::
|
||||||
|
|
||||||
I[10-04|13:54:30.391] Starting RPC HTTP server on tcp socket 0.0.0.0:46657 module=rpc-server
|
I[10-04|13:54:30.391] Starting RPC HTTP server on tcp socket 0.0.0.0:46657 module=rpc-server
|
||||||
I[10-04|13:54:30.392] Started node module=main nodeInfo="NodeInfo{pk: PubKeyEd25519{DF22D7C92C91082324A1312F092AA1DA197FA598DBBFB6526E177003C4D6FD66}, moniker: anonymous, network: test-chain-3MNw2N [remote , listen 10.0.2.15:46656], version: 0.11.0-10f361fc ([wire_version=0.6.2 p2p_version=0.5.0 consensus_version=v1/0.2.2 rpc_version=0.7.0/3 tx_index=on rpc_addr=tcp://0.0.0.0:46657])}"
|
I[10-04|13:54:30.392] Started node module=main nodeInfo="NodeInfo{id: DF22D7C92C91082324A1312F092AA1DA197FA598DBBFB6526E, moniker: anonymous, network: test-chain-3MNw2N [remote , listen 10.0.2.15:46656], version: 0.11.0-10f361fc ([wire_version=0.6.2 p2p_version=0.5.0 consensus_version=v1/0.2.2 rpc_version=0.7.0/3 tx_index=on rpc_addr=tcp://0.0.0.0:46657])}"
|
||||||
|
|
||||||
Next follows a standard block creation cycle, where we enter a new round,
|
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
|
propose a block, receive more than 2/3 of prevotes, then precommits and finally
|
||||||
|
@@ -44,7 +44,8 @@ Tendermint Tools
|
|||||||
tools/docker.rst
|
tools/docker.rst
|
||||||
tools/mintnet-kubernetes.rst
|
tools/mintnet-kubernetes.rst
|
||||||
tools/terraform-digitalocean.rst
|
tools/terraform-digitalocean.rst
|
||||||
tools/benchmarking-and-monitoring.rst
|
tools/benchmarking.rst
|
||||||
|
tools/monitoring.rst
|
||||||
|
|
||||||
Tendermint 102
|
Tendermint 102
|
||||||
--------------
|
--------------
|
||||||
@@ -65,10 +66,11 @@ Tendermint 201
|
|||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
specification.rst
|
specification.rst
|
||||||
|
determinism.rst
|
||||||
|
|
||||||
* For a deeper dive, see `this thesis <https://atrium.lib.uoguelph.ca/xmlui/handle/10214/9769>`__.
|
* For a deeper dive, see `this thesis <https://atrium.lib.uoguelph.ca/xmlui/handle/10214/9769>`__.
|
||||||
* There is also the `original whitepaper <https://tendermint.com/static/docs/tendermint.pdf>`__, though it is now quite outdated.
|
* There is also the `original whitepaper <https://tendermint.com/static/docs/tendermint.pdf>`__, though it is now quite outdated.
|
||||||
* Readers might also be interested in the `Cosmos Whitepaper <https://cosmos.network/whitepaper>`__ which describes Tendermint, ABCI, and how to build a scalable, heterogeneous, cryptocurrency network.
|
* Readers might also be interested in the `Cosmos Whitepaper <https://cosmos.network/whitepaper>`__ which describes Tendermint, ABCI, and how to build a scalable, heterogeneous, cryptocurrency network.
|
||||||
* For example applications and related software built by the Tendermint team and other, see the `software ecosystem <https://tendermint.com/ecosystem>`__.
|
* For example applications and related software built by the Tendermint team and other, see the `software ecosystem <https://tendermint.com/ecosystem>`__.
|
||||||
|
|
||||||
Join the `Cosmos and Tendermint Rocket Chat <https://cosmos.rocket.chat>`__ to ask questions and discuss projects.
|
Join the `community <https://cosmos.network/community>`__ to ask questions and discuss projects.
|
||||||
|
@@ -4,12 +4,12 @@ Install Tendermint
|
|||||||
From Binary
|
From Binary
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
To download pre-built binaries, see the `Download page <https://tendermint.com/download>`__.
|
To download pre-built binaries, see the `Download page <https://tendermint.com/downloads>`__.
|
||||||
|
|
||||||
From Source
|
From Source
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
You'll need ``go``, maybe ``glide``, and the Tendermint source code.
|
You'll need ``go``, maybe `dep <https://github.com/golang/dep>`__, and the Tendermint source code.
|
||||||
|
|
||||||
Install Go
|
Install Go
|
||||||
^^^^^^^^^^
|
^^^^^^^^^^
|
||||||
@@ -31,21 +31,21 @@ installation worked.
|
|||||||
|
|
||||||
If the installation failed, a dependency may have been updated and become
|
If the installation failed, a dependency may have been updated and become
|
||||||
incompatible with the latest Tendermint master branch. We solve this
|
incompatible with the latest Tendermint master branch. We solve this
|
||||||
using the ``glide`` tool for dependency management.
|
using the ``dep`` tool for dependency management.
|
||||||
|
|
||||||
First, install ``glide``:
|
First, install ``dep``:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
go get github.com/Masterminds/glide
|
cd $GOPATH/src/github.com/tendermint/tendermint
|
||||||
|
make get_tools
|
||||||
|
|
||||||
Now we can fetch the correct versions of each dependency by running:
|
Now we can fetch the correct versions of each dependency by running:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
cd $GOPATH/src/github.com/tendermint/tendermint
|
make get_vendor_deps
|
||||||
glide install
|
make install
|
||||||
go install ./cmd/tendermint
|
|
||||||
|
|
||||||
Note that even though ``go get`` originally failed, the repository was
|
Note that even though ``go get`` originally failed, the repository was
|
||||||
still cloned to the correct location in the ``$GOPATH``.
|
still cloned to the correct location in the ``$GOPATH``.
|
||||||
@@ -60,7 +60,7 @@ If you already have Tendermint installed, and you make updates, simply
|
|||||||
::
|
::
|
||||||
|
|
||||||
cd $GOPATH/src/github.com/tendermint/tendermint
|
cd $GOPATH/src/github.com/tendermint/tendermint
|
||||||
go install ./cmd/tendermint
|
make install
|
||||||
|
|
||||||
To upgrade, there are a few options:
|
To upgrade, there are a few options:
|
||||||
|
|
||||||
@@ -72,18 +72,18 @@ To upgrade, there are a few options:
|
|||||||
its dependencies
|
its dependencies
|
||||||
- fetch and checkout the latest master branch in
|
- fetch and checkout the latest master branch in
|
||||||
``$GOPATH/src/github.com/tendermint/tendermint``, and then run
|
``$GOPATH/src/github.com/tendermint/tendermint``, and then run
|
||||||
``glide install && go install ./cmd/tendermint`` as above.
|
``make get_vendor_deps && make install`` as above.
|
||||||
|
|
||||||
Note the first two options should usually work, but may fail. If they
|
Note the first two options should usually work, but may fail. If they
|
||||||
do, use ``glide``, as above:
|
do, use ``dep``, as above:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
cd $GOPATH/src/github.com/tendermint/tendermint
|
cd $GOPATH/src/github.com/tendermint/tendermint
|
||||||
glide install
|
make get_vendor_deps
|
||||||
go install ./cmd/tendermint
|
make install
|
||||||
|
|
||||||
Since the third option just uses ``glide`` right away, it should always
|
Since the third option just uses ``dep`` right away, it should always
|
||||||
work.
|
work.
|
||||||
|
|
||||||
Troubleshooting
|
Troubleshooting
|
||||||
@@ -96,8 +96,9 @@ If ``go get`` failing bothers you, fetch the code using ``git``:
|
|||||||
mkdir -p $GOPATH/src/github.com/tendermint
|
mkdir -p $GOPATH/src/github.com/tendermint
|
||||||
git clone https://github.com/tendermint/tendermint $GOPATH/src/github.com/tendermint/tendermint
|
git clone https://github.com/tendermint/tendermint $GOPATH/src/github.com/tendermint/tendermint
|
||||||
cd $GOPATH/src/github.com/tendermint/tendermint
|
cd $GOPATH/src/github.com/tendermint/tendermint
|
||||||
glide install
|
make get_tools
|
||||||
go install ./cmd/tendermint
|
make get_vendor_deps
|
||||||
|
make install
|
||||||
|
|
||||||
Run
|
Run
|
||||||
^^^
|
^^^
|
||||||
@@ -107,4 +108,4 @@ To start a one-node blockchain with a simple in-process application:
|
|||||||
::
|
::
|
||||||
|
|
||||||
tendermint init
|
tendermint init
|
||||||
tendermint node --proxy_app=dummy
|
tendermint node --proxy_app=kvstore
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
Specification
|
Specification
|
||||||
#############
|
#############
|
||||||
|
|
||||||
Here you'll find details of the Tendermint specification. See `the spec repo <https://github.com/tendermint/spec>`__ for upcoming material. Tendermint's types are produced by `godoc <https://godoc.org/github.com/tendermint/tendermint/types>`__.
|
Here you'll find details of the Tendermint specification. Tendermint's types are produced by `godoc <https://godoc.org/github.com/tendermint/tendermint/types>`__.
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
@@ -10,6 +10,7 @@ Here you'll find details of the Tendermint specification. See `the spec repo <ht
|
|||||||
specification/block-structure.rst
|
specification/block-structure.rst
|
||||||
specification/byzantine-consensus-algorithm.rst
|
specification/byzantine-consensus-algorithm.rst
|
||||||
specification/configuration.rst
|
specification/configuration.rst
|
||||||
|
specification/corruption.rst
|
||||||
specification/fast-sync.rst
|
specification/fast-sync.rst
|
||||||
specification/genesis.rst
|
specification/genesis.rst
|
||||||
specification/light-client-protocol.rst
|
specification/light-client-protocol.rst
|
||||||
|
@@ -329,11 +329,11 @@ collateral on all other forks. Clients should verify the signatures on
|
|||||||
the reorg-proposal, verify any evidence, and make a judgement or prompt
|
the reorg-proposal, verify any evidence, and make a judgement or prompt
|
||||||
the end-user for a decision. For example, a phone wallet app may prompt
|
the end-user for a decision. For example, a phone wallet app may prompt
|
||||||
the user with a security warning, while a refrigerator may accept any
|
the user with a security warning, while a refrigerator may accept any
|
||||||
reorg-proposal signed by +½ of the original validators.
|
reorg-proposal signed by +1/2 of the original validators.
|
||||||
|
|
||||||
No non-synchronous Byzantine fault-tolerant algorithm can come to
|
No non-synchronous Byzantine fault-tolerant algorithm can come to
|
||||||
consensus when ⅓+ of validators are dishonest, yet a fork assumes that
|
consensus when 1/3+ of validators are dishonest, yet a fork assumes that
|
||||||
⅓+ of validators have already been dishonest by double-signing or
|
1/3+ of validators have already been dishonest by double-signing or
|
||||||
lock-changing without justification. So, signing the reorg-proposal is a
|
lock-changing without justification. So, signing the reorg-proposal is a
|
||||||
coordination problem that cannot be solved by any non-synchronous
|
coordination problem that cannot be solved by any non-synchronous
|
||||||
protocol (i.e. automatically, and without making assumptions about the
|
protocol (i.e. automatically, and without making assumptions about the
|
||||||
|
@@ -89,6 +89,7 @@ like the file below, however, double check by inspecting the
|
|||||||
seeds = ""
|
seeds = ""
|
||||||
|
|
||||||
# Comma separated list of nodes to keep persistent connections to
|
# Comma separated list of nodes to keep persistent connections to
|
||||||
|
# Do not add private peers to this list if you don't want them advertised
|
||||||
persistent_peers = ""
|
persistent_peers = ""
|
||||||
|
|
||||||
# Path to address book
|
# Path to address book
|
||||||
@@ -121,6 +122,12 @@ like the file below, however, double check by inspecting the
|
|||||||
# Does not work if the peer-exchange reactor is disabled.
|
# Does not work if the peer-exchange reactor is disabled.
|
||||||
seed_mode = false
|
seed_mode = false
|
||||||
|
|
||||||
|
# Authenticated encryption
|
||||||
|
auth_enc = true
|
||||||
|
|
||||||
|
# Comma separated list of peer IDs to keep private (will not be gossiped to other peers)
|
||||||
|
private_peer_ids = ""
|
||||||
|
|
||||||
##### mempool configuration options #####
|
##### mempool configuration options #####
|
||||||
[mempool]
|
[mempool]
|
||||||
|
|
||||||
|
@@ -5,12 +5,6 @@ The genesis.json file in ``$TMHOME/config`` defines the initial TendermintCore
|
|||||||
state upon genesis of the blockchain (`see
|
state upon genesis of the blockchain (`see
|
||||||
definition <https://github.com/tendermint/tendermint/blob/master/types/genesis.go>`__).
|
definition <https://github.com/tendermint/tendermint/blob/master/types/genesis.go>`__).
|
||||||
|
|
||||||
NOTE: This does not (yet) specify the application state (e.g. initial
|
|
||||||
distribution of tokens). Currently we leave it up to the application to
|
|
||||||
load the initial application genesis state. In the future, we may
|
|
||||||
include genesis SetOption messages that get passed from TendermintCore
|
|
||||||
to the app upon genesis.
|
|
||||||
|
|
||||||
Fields
|
Fields
|
||||||
~~~~~~
|
~~~~~~
|
||||||
|
|
||||||
@@ -24,8 +18,9 @@ Fields
|
|||||||
- ``power``: The validator's voting power.
|
- ``power``: The validator's voting power.
|
||||||
- ``name``: Name of the validator (optional).
|
- ``name``: Name of the validator (optional).
|
||||||
- ``app_hash``: The expected application hash (as returned by the
|
- ``app_hash``: The expected application hash (as returned by the
|
||||||
``Commit`` ABCI message) upon genesis. If the app's hash does not
|
``ResponseInfo`` ABCI message) upon genesis. If the app's hash does not
|
||||||
match, a warning message is printed.
|
match, Tendermint will panic.
|
||||||
|
- ``app_state``: The application state (e.g. initial distribution of tokens).
|
||||||
|
|
||||||
Sample genesis.json
|
Sample genesis.json
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
@@ -69,5 +64,8 @@ Sample genesis.json
|
|||||||
"name": "mach4"
|
"name": "mach4"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"app_hash": "15005165891224E721CB664D15CB972240F5703F"
|
"app_hash": "15005165891224E721CB664D15CB972240F5703F",
|
||||||
|
"app_state": {
|
||||||
|
{"account": "Bob", "coins": 5000}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,11 +4,6 @@ This is a markdown specification of the Tendermint blockchain.
|
|||||||
It defines the base data structures, how they are validated,
|
It defines the base data structures, how they are validated,
|
||||||
and how they are communicated over the network.
|
and how they are communicated over the network.
|
||||||
|
|
||||||
XXX: this spec is a work in progress and not yet complete - see github
|
|
||||||
[issues](https://github.com/tendermint/tendermint/issues) and
|
|
||||||
[pull requests](https://github.com/tendermint/tendermint/pulls)
|
|
||||||
for more details.
|
|
||||||
|
|
||||||
If you find discrepancies between the spec and the code that
|
If you find discrepancies between the spec and the code that
|
||||||
do not have an associated issue or pull request on github,
|
do not have an associated issue or pull request on github,
|
||||||
please submit them to our [bug bounty](https://tendermint.com/security)!
|
please submit them to our [bug bounty](https://tendermint.com/security)!
|
||||||
@@ -24,18 +19,16 @@ please submit them to our [bug bounty](https://tendermint.com/security)!
|
|||||||
|
|
||||||
### P2P and Network Protocols
|
### P2P and Network Protocols
|
||||||
|
|
||||||
TODO: update links
|
- [The Base P2P Layer](p2p): multiplex the protocols ("reactors") on authenticated and encrypted TCP connections
|
||||||
|
- [Peer Exchange (PEX)](reactors/pex): gossip known peer addresses so peers can find each other
|
||||||
- [The Base P2P Layer](p2p/README.md): multiplex the protocols ("reactors") on authenticated and encrypted TCP connections
|
- [Block Sync](reactors/block_sync): gossip blocks so peers can catch up quickly
|
||||||
- [Peer Exchange (PEX)](pex/README.md): gossip known peer addresses so peers can find each other
|
- [Consensus](reactors/consensus): gossip votes and block parts so new blocks can be committed
|
||||||
- [Block Sync](block_sync/README.md): gossip blocks so peers can catch up quickly
|
- [Mempool](reactors/mempool): gossip transactions so they get included in blocks
|
||||||
- [Consensus](consensus/README.md): gossip votes and block parts so new blocks can be committed
|
- Evidence: TODO
|
||||||
- [Mempool](mempool/README.md): gossip transactions so they get included in blocks
|
|
||||||
- [Evidence](evidence/README.md): TODO
|
|
||||||
|
|
||||||
### More
|
### More
|
||||||
- [Light Client](light_client/README.md): TODO
|
- Light Client: TODO
|
||||||
- [Persistence](persistence/README.md): TODO
|
- Persistence: TODO
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
|
@@ -16,7 +16,21 @@ In the context of Tendermint, time is of type int64 and denotes UNIX time in mil
|
|||||||
corresponds to the number of milliseconds since January 1, 1970. Before defining rules that need to be enforced by the
|
corresponds to the number of milliseconds since January 1, 1970. Before defining rules that need to be enforced by the
|
||||||
Tendermint consensus protocol, so the properties above holds, we introduce the following definition:
|
Tendermint consensus protocol, so the properties above holds, we introduce the following definition:
|
||||||
|
|
||||||
- median of a set of `Vote` messages is equal to the median of `Vote.Time` fields of the corresponding `Vote` messages
|
- median of a set of `Vote` messages is equal to the median of `Vote.Time` fields of the corresponding `Vote` messages,
|
||||||
|
where the value of `Vote.Time` is counted number of times proportional to the process voting power. As in Tendermint
|
||||||
|
the voting power is not uniform (one process one vote), a vote message is actually an aggregator of the same votes whose
|
||||||
|
number is equal to the voting power of the process that has casted the corresponding votes message.
|
||||||
|
|
||||||
|
Let's consider the following example:
|
||||||
|
- we have four processes p1, p2, p3 and p4, with the following voting power distribution (p1, 23), (p2, 27), (p3, 10)
|
||||||
|
and (p4, 10). The total voting power is 70 (`N = 3f+1`, where `N` is the total voting power, and `f` is the maximum voting
|
||||||
|
power of the faulty processes), so we assume that the faulty processes have at most 23 of voting power.
|
||||||
|
Furthermore, we have the following vote messages in some LastCommit field (we ignore all fields except Time field):
|
||||||
|
- (p1, 100), (p2, 98), (p3, 1000), (p4, 500). We assume that p3 and p4 are faulty processes. Let's assume that the
|
||||||
|
`block.LastCommit` message contains votes of processes p2, p3 and p4. Median is then chosen the following way:
|
||||||
|
the value 98 is counted 27 times, the value 1000 is counted 10 times and the value 500 is counted also 10 times.
|
||||||
|
So the median value will be the value 98. No matter what set of messages with at least `2f+1` voting power we
|
||||||
|
choose, the median value will always be between the values sent by correct processes.
|
||||||
|
|
||||||
We ensure Time Monotonicity and Time Validity properties by the following rules:
|
We ensure Time Monotonicity and Time Validity properties by the following rules:
|
||||||
|
|
||||||
|
114
docs/specification/new-spec/light-client.md
Normal file
114
docs/specification/new-spec/light-client.md
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
# Light client
|
||||||
|
|
||||||
|
A light client is a process that connects to the Tendermint Full Node(s) and then tries to verify the Merkle proofs
|
||||||
|
about the blockchain application. In this document we describe mechanisms that ensures that the Tendermint light client
|
||||||
|
has the same level of security as Full Node processes (without being itself a Full Node).
|
||||||
|
|
||||||
|
To be able to validate a Merkle proof, a light client needs to validate the blockchain header that contains the root app hash.
|
||||||
|
Validating a blockchain header in Tendermint consists in verifying that the header is committed (signed) by >2/3 of the
|
||||||
|
voting power of the corresponding validator set. As the validator set is a dynamic set (it is changing), one of the
|
||||||
|
core functionality of the light client is updating the current validator set, that is then used to verify the
|
||||||
|
blockchain header, and further the corresponding Merkle proofs.
|
||||||
|
|
||||||
|
For the purpose of this light client specification, we assume that the Tendermint Full Node exposes the following functions over
|
||||||
|
Tendermint RPC:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
Header(height int64) (SignedHeader, error) // returns signed header for the given height
|
||||||
|
Validators(height int64) (ResultValidators, error) // returns validator set for the given height
|
||||||
|
LastHeader(valSetNumber int64) (SignedHeader, error) // returns last header signed by the validator set with the given validator set number
|
||||||
|
|
||||||
|
type SignedHeader struct {
|
||||||
|
Header Header
|
||||||
|
Commit Commit
|
||||||
|
ValSetNumber int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResultValidators struct {
|
||||||
|
BlockHeight int64
|
||||||
|
Validators []Validator
|
||||||
|
// time the current validator set is initialised, i.e, time of the last validator change before header BlockHeight
|
||||||
|
ValSetTime int64
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We assume that Tendermint keeps track of the validator set changes and that each time a validator set is changed it is
|
||||||
|
being assigned the next sequence number. We can call this number the validator set sequence number. Tendermint also remembers
|
||||||
|
the Time from the header when the next validator set is initialised (starts to be in power), and we refer to this time
|
||||||
|
as validator set init time.
|
||||||
|
Furthermore, we assume that each validator set change is signed (committed) by the current validator set. More precisely,
|
||||||
|
given a block `H` that contains transactions that are modifying the current validator set, the Merkle root hash of the next
|
||||||
|
validator set (modified based on transactions from block H) will be in block `H+1` (and signed by the current validator
|
||||||
|
set), and then starting from the block `H+2`, it will be signed by the next validator set.
|
||||||
|
|
||||||
|
Note that the real Tendermint RPC API is slightly different (for example, response messages contain more data and function
|
||||||
|
names are slightly different); we shortened (and modified) it for the purpose of this document to make the spec more
|
||||||
|
clear and simple. Furthermore, note that in case of the third function, the returned header has `ValSetNumber` equals to
|
||||||
|
`valSetNumber+1`.
|
||||||
|
|
||||||
|
|
||||||
|
Locally, light client manages the following state:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
valSet []Validator // current validator set (last known and verified validator set)
|
||||||
|
valSetNumber int64 // sequence number of the current validator set
|
||||||
|
valSetHash []byte // hash of the current validator set
|
||||||
|
valSetTime int64 // time when the current validator set is initialised
|
||||||
|
```
|
||||||
|
|
||||||
|
The light client is initialised with the trusted validator set, for example based on the known validator set hash,
|
||||||
|
validator set sequence number and the validator set init time.
|
||||||
|
The core of the light client logic is captured by the VerifyAndUpdate function that is used to 1) verify if the given header is valid,
|
||||||
|
and 2) update the validator set (when the given header is valid and it is more recent than the seen headers).
|
||||||
|
|
||||||
|
```golang
|
||||||
|
VerifyAndUpdate(signedHeader SignedHeader):
|
||||||
|
assertThat signedHeader.valSetNumber >= valSetNumber
|
||||||
|
if isValid(signedHeader) and signedHeader.Header.Time <= valSetTime + UNBONDING_PERIOD then
|
||||||
|
setValidatorSet(signedHeader)
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
updateValidatorSet(signedHeader.ValSetNumber)
|
||||||
|
return VerifyAndUpdate(signedHeader)
|
||||||
|
|
||||||
|
isValid(signedHeader SignedHeader):
|
||||||
|
valSetOfTheHeader = Validators(signedHeader.Header.Height)
|
||||||
|
assertThat Hash(valSetOfTheHeader) == signedHeader.Header.ValSetHash
|
||||||
|
assertThat signedHeader is passing basic validation
|
||||||
|
if votingPower(signedHeader.Commit) > 2/3 * votingPower(valSetOfTheHeader) then return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
|
||||||
|
setValidatorSet(signedHeader SignedHeader):
|
||||||
|
nextValSet = Validators(signedHeader.Header.Height)
|
||||||
|
assertThat Hash(nextValSet) == signedHeader.Header.ValidatorsHash
|
||||||
|
valSet = nextValSet.Validators
|
||||||
|
valSetHash = signedHeader.Header.ValidatorsHash
|
||||||
|
valSetNumber = signedHeader.ValSetNumber
|
||||||
|
valSetTime = nextValSet.ValSetTime
|
||||||
|
|
||||||
|
votingPower(commit Commit):
|
||||||
|
votingPower = 0
|
||||||
|
for each precommit in commit.Precommits do:
|
||||||
|
if precommit.ValidatorAddress is in valSet and signature of the precommit verifies then
|
||||||
|
votingPower += valSet[precommit.ValidatorAddress].VotingPower
|
||||||
|
return votingPower
|
||||||
|
|
||||||
|
votingPower(validatorSet []Validator):
|
||||||
|
for each validator in validatorSet do:
|
||||||
|
votingPower += validator.VotingPower
|
||||||
|
return votingPower
|
||||||
|
|
||||||
|
updateValidatorSet(valSetNumberOfTheHeader):
|
||||||
|
while valSetNumber != valSetNumberOfTheHeader do
|
||||||
|
signedHeader = LastHeader(valSetNumber)
|
||||||
|
if isValid(signedHeader) then
|
||||||
|
setValidatorSet(signedHeader)
|
||||||
|
else return error
|
||||||
|
return
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that in the logic above we assume that the light client will always go upward with respect to header verifications,
|
||||||
|
i.e., that it will always be used to verify more recent headers. In case a light client needs to be used to verify older
|
||||||
|
headers (go backward) the same mechanisms and similar logic can be used. In case a call to the FullNode or subsequent
|
||||||
|
checks fail, a light client need to implement some recovery strategy, for example connecting to other FullNode.
|
@@ -83,7 +83,7 @@ The Tendermint Version Handshake allows the peers to exchange their NodeInfo:
|
|||||||
|
|
||||||
```golang
|
```golang
|
||||||
type NodeInfo struct {
|
type NodeInfo struct {
|
||||||
PubKey crypto.PubKey
|
ID p2p.ID
|
||||||
Moniker string
|
Moniker string
|
||||||
Network string
|
Network string
|
||||||
RemoteAddr string
|
RemoteAddr string
|
||||||
@@ -95,7 +95,7 @@ type NodeInfo struct {
|
|||||||
```
|
```
|
||||||
|
|
||||||
The connection is disconnected if:
|
The connection is disconnected if:
|
||||||
- `peer.NodeInfo.PubKey != peer.PubKey`
|
- `peer.NodeInfo.ID` is not equal `peerConn.ID`
|
||||||
- `peer.NodeInfo.Version` is not formatted as `X.X.X` where X are integers known as Major, Minor, and Revision
|
- `peer.NodeInfo.Version` is not formatted as `X.X.X` where X are integers known as Major, Minor, and Revision
|
||||||
- `peer.NodeInfo.Version` Major is not the same as ours
|
- `peer.NodeInfo.Version` Major is not the same as ours
|
||||||
- `peer.NodeInfo.Version` Minor is not the same as ours
|
- `peer.NodeInfo.Version` Minor is not the same as ours
|
||||||
|
@@ -31,7 +31,7 @@ Updates (state transitions) happen on timeouts, complete proposals, and 2/3 majo
|
|||||||
It receives messages from peers, internal validators and from Timeout Ticker
|
It receives messages from peers, internal validators and from Timeout Ticker
|
||||||
and invokes the corresponding handlers, potentially updating the RoundState.
|
and invokes the corresponding handlers, potentially updating the RoundState.
|
||||||
The details of the protocol (together with formal proofs of correctness) implemented by the Receive Routine are
|
The details of the protocol (together with formal proofs of correctness) implemented by the Receive Routine are
|
||||||
discussed in separate document (see [spec](https://github.com/tendermint/spec)). For understanding of this document
|
discussed in separate document. For understanding of this document
|
||||||
it is sufficient to understand that the Receive Routine manages and updates RoundState data structure that is
|
it is sufficient to understand that the Receive Routine manages and updates RoundState data structure that is
|
||||||
then extensively used by the gossip routines to determine what information should be sent to peer processes.
|
then extensively used by the gossip routines to determine what information should be sent to peer processes.
|
||||||
|
|
||||||
|
@@ -11,7 +11,7 @@ next block should be; a validator might vote with a `VoteMessage` for a differen
|
|||||||
round, enough number of processes vote for the same block, then this block is committed and later
|
round, enough number of processes vote for the same block, then this block is committed and later
|
||||||
added to the blockchain. `ProposalMessage` and `VoteMessage` are signed by the private key of the
|
added to the blockchain. `ProposalMessage` and `VoteMessage` are signed by the private key of the
|
||||||
validator. The internals of the protocol and how it ensures safety and liveness properties are
|
validator. The internals of the protocol and how it ensures safety and liveness properties are
|
||||||
explained [here](https://github.com/tendermint/spec).
|
explained in a forthcoming document.
|
||||||
|
|
||||||
For efficiency reasons, validators in Tendermint consensus protocol do not agree directly on the
|
For efficiency reasons, validators in Tendermint consensus protocol do not agree directly on the
|
||||||
block as the block size is big, i.e., they don't embed the block inside `Proposal` and
|
block as the block size is big, i.e., they don't embed the block inside `Proposal` and
|
||||||
|
@@ -57,10 +57,17 @@ a trust metric (see below), but it's best to start with something simple.
|
|||||||
## Select Peers to Dial
|
## Select Peers to Dial
|
||||||
|
|
||||||
When we need more peers, we pick them randomly from the addrbook with some
|
When we need more peers, we pick them randomly from the addrbook with some
|
||||||
configurable bias for unvetted peers. The bias should be lower when we have fewer peers,
|
configurable bias for unvetted peers. The bias should be lower when we have fewer peers
|
||||||
and can increase as we obtain more, ensuring that our first peers are more trustworthy,
|
and can increase as we obtain more, ensuring that our first peers are more trustworthy,
|
||||||
but always giving us the chance to discover new good peers.
|
but always giving us the chance to discover new good peers.
|
||||||
|
|
||||||
|
We track the last time we dialed a peer and the number of unsuccessful attempts
|
||||||
|
we've made. If too many attempts are made, we mark the peer as bad.
|
||||||
|
|
||||||
|
Connection attempts are made with exponential backoff (plus jitter). Because
|
||||||
|
the selection process happens every `ensurePeersPeriod`, we might not end up
|
||||||
|
dialing a peer for much longer than the backoff duration.
|
||||||
|
|
||||||
## Select Peers to Exchange
|
## Select Peers to Exchange
|
||||||
|
|
||||||
When we’re asked for peers, we select them as follows:
|
When we’re asked for peers, we select them as follows:
|
||||||
|
@@ -97,6 +97,7 @@ An HTTP Get request to the root RPC endpoint (e.g.
|
|||||||
http://localhost:46657/genesis
|
http://localhost:46657/genesis
|
||||||
http://localhost:46657/net_info
|
http://localhost:46657/net_info
|
||||||
http://localhost:46657/num_unconfirmed_txs
|
http://localhost:46657/num_unconfirmed_txs
|
||||||
|
http://localhost:46657/health
|
||||||
http://localhost:46657/status
|
http://localhost:46657/status
|
||||||
http://localhost:46657/unconfirmed_txs
|
http://localhost:46657/unconfirmed_txs
|
||||||
http://localhost:46657/unsafe_flush_mempool
|
http://localhost:46657/unsafe_flush_mempool
|
||||||
|
@@ -62,6 +62,13 @@ such as the Web-of-Trust or Certificate Authorities. In our case, we can
|
|||||||
use the blockchain itself as a certificate authority to ensure that we
|
use the blockchain itself as a certificate authority to ensure that we
|
||||||
are connected to at least one validator.
|
are connected to at least one validator.
|
||||||
|
|
||||||
|
Config
|
||||||
|
------
|
||||||
|
|
||||||
|
Authenticated encryption is enabled by default. If you wish to use another
|
||||||
|
authentication scheme or your peers are connected via VPN, you can turn it off
|
||||||
|
by setting ``auth_enc`` to ``false`` in the config file.
|
||||||
|
|
||||||
Additional Reading
|
Additional Reading
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
@@ -41,18 +41,18 @@ To run a Tendermint node, use
|
|||||||
tendermint node
|
tendermint node
|
||||||
|
|
||||||
By default, Tendermint will try to connect to an ABCI application on
|
By default, Tendermint will try to connect to an ABCI application on
|
||||||
`127.0.0.1:46658 <127.0.0.1:46658>`__. If you have the ``dummy`` ABCI
|
`127.0.0.1:46658 <127.0.0.1:46658>`__. If you have the ``kvstore`` ABCI
|
||||||
app installed, run it in another window. If you don't, kill Tendermint
|
app installed, run it in another window. If you don't, kill Tendermint
|
||||||
and run an in-process version with
|
and run an in-process version of the ``kvstore`` app:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
tendermint node --proxy_app=dummy
|
tendermint node --proxy_app=kvstore
|
||||||
|
|
||||||
After a few seconds you should see blocks start streaming in. Note that
|
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.
|
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 dummy, counter, and nil
|
Tendermint supports in-process versions of the ``counter``, ``kvstore`` and ``nil``
|
||||||
apps that ship as examples in the `ABCI
|
apps that ship as examples in the `ABCI
|
||||||
repository <https://github.com/tendermint/abci>`__. It's easy to compile
|
repository <https://github.com/tendermint/abci>`__. It's easy to compile
|
||||||
your own app in-process with Tendermint if it's written in Go. If your
|
your own app in-process with Tendermint if it's written in Go. If your
|
||||||
@@ -74,20 +74,17 @@ RPC server, for example:
|
|||||||
|
|
||||||
curl http://localhost:46657/broadcast_tx_commit?tx=\"abcd\"
|
curl http://localhost:46657/broadcast_tx_commit?tx=\"abcd\"
|
||||||
|
|
||||||
For handling responses, we recommend you `install the jsonpp
|
|
||||||
tool <http://jmhodges.github.io/jsonpp/>`__ to pretty print the JSON.
|
|
||||||
|
|
||||||
We can see the chain's status at the ``/status`` end-point:
|
We can see the chain's status at the ``/status`` end-point:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
curl http://localhost:46657/status | jsonpp
|
curl http://localhost:46657/status | json_pp
|
||||||
|
|
||||||
and the ``latest_app_hash`` in particular:
|
and the ``latest_app_hash`` in particular:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
curl http://localhost:46657/status | jsonpp | grep app_hash
|
curl http://localhost:46657/status | json_pp | grep latest_app_hash
|
||||||
|
|
||||||
Visit http://localhost:46657 in your browser to see the list of other
|
Visit http://localhost:46657 in your browser to see the list of other
|
||||||
endpoints. Some take no arguments (like ``/status``), while others
|
endpoints. Some take no arguments (like ``/status``), while others
|
||||||
@@ -260,19 +257,19 @@ When ``tendermint init`` is run, both a ``genesis.json`` and
|
|||||||
::
|
::
|
||||||
|
|
||||||
{
|
{
|
||||||
"app_hash": "",
|
|
||||||
"chain_id": "test-chain-HZw6TB",
|
|
||||||
"genesis_time": "0001-01-01T00:00:00.000Z",
|
|
||||||
"validators" : [
|
"validators" : [
|
||||||
{
|
{
|
||||||
|
"pub_key" : {
|
||||||
|
"value" : "h3hk+QE8c6QLTySp8TcfzclJw/BG79ziGB/pIA+DfPE=",
|
||||||
|
"type" : "AC26791624DE60"
|
||||||
|
},
|
||||||
"power" : 10,
|
"power" : 10,
|
||||||
"name": "",
|
"name" : ""
|
||||||
"pub_key": [
|
|
||||||
1,
|
|
||||||
"5770B4DD55B3E08B7F5711C48B516347D8C33F47C30C226315D21AA64E0DFF2E"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"app_hash" : "",
|
||||||
|
"chain_id" : "test-chain-rDlYSN",
|
||||||
|
"genesis_time" : "0001-01-01T00:00:00Z"
|
||||||
}
|
}
|
||||||
|
|
||||||
And the ``priv_validator.json``:
|
And the ``priv_validator.json``:
|
||||||
@@ -280,20 +277,18 @@ And the ``priv_validator.json``:
|
|||||||
::
|
::
|
||||||
|
|
||||||
{
|
{
|
||||||
"address": "4F4D895F882A18E1D1FC608D102601DA8D3570E5",
|
|
||||||
"last_height": 0,
|
|
||||||
"last_round": 0,
|
|
||||||
"last_signature": null,
|
|
||||||
"last_signbytes": "",
|
|
||||||
"last_step" : 0,
|
"last_step" : 0,
|
||||||
"priv_key": [
|
"last_round" : 0,
|
||||||
1,
|
"address" : "B788DEDE4F50AD8BC9462DE76741CCAFF87D51E2",
|
||||||
"F9FA3CD435BDAE54D0BCA8F1BC289D718C23D855C6DB21E8543F5E4F457E62805770B4DD55B3E08B7F5711C48B516347D8C33F47C30C226315D21AA64E0DFF2E"
|
"pub_key" : {
|
||||||
],
|
"value" : "h3hk+QE8c6QLTySp8TcfzclJw/BG79ziGB/pIA+DfPE=",
|
||||||
"pub_key": [
|
"type" : "AC26791624DE60"
|
||||||
1,
|
},
|
||||||
"5770B4DD55B3E08B7F5711C48B516347D8C33F47C30C226315D21AA64E0DFF2E"
|
"last_height" : 0,
|
||||||
]
|
"priv_key" : {
|
||||||
|
"value" : "JPivl82x+LfVkp8i3ztoTjY6c6GJ4pBxQexErOCyhwqHeGT5ATxzpAtPJKnxNx/NyUnD8Ebv3OIYH+kgD4N88Q==",
|
||||||
|
"type" : "954568A3288910"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
The ``priv_validator.json`` actually contains a private key, and should
|
The ``priv_validator.json`` actually contains a private key, and should
|
||||||
@@ -334,14 +329,14 @@ For instance,
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
tendermint node --p2p.seeds "1.2.3.4:46656,5.6.7.8:46656"
|
tendermint node --p2p.seeds "f9baeaa15fedf5e1ef7448dd60f46c01f1a9e9c4@1.2.3.4:46656,0491d373a8e0fcf1023aaf18c51d6a1d0d4f31bd@5.6.7.8:46656"
|
||||||
|
|
||||||
Alternatively, you can use the ``/dial_seeds`` endpoint of the RPC to
|
Alternatively, you can use the ``/dial_seeds`` endpoint of the RPC to
|
||||||
specify seeds for a running node to connect to:
|
specify seeds for a running node to connect to:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
curl 'localhost:46657/dial_seeds?seeds=\["1.2.3.4:46656","5.6.7.8:46656"\]'
|
curl 'localhost:46657/dial_seeds?seeds=\["f9baeaa15fedf5e1ef7448dd60f46c01f1a9e9c4@1.2.3.4:46656","0491d373a8e0fcf1023aaf18c51d6a1d0d4f31bd@5.6.7.8:46656"\]'
|
||||||
|
|
||||||
Note, if the peer-exchange protocol (PEX) is enabled (default), you should not
|
Note, if the peer-exchange protocol (PEX) is enabled (default), you should not
|
||||||
normally need seeds after the first start. Peers will be gossipping about known
|
normally need seeds after the first start. Peers will be gossipping about known
|
||||||
@@ -355,8 +350,8 @@ core instance.
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
tendermint node --p2p.persistent_peers "10.11.12.13:46656,10.11.12.14:46656"
|
tendermint node --p2p.persistent_peers "429fcf25974313b95673f58d77eacdd434402665@10.11.12.13:46656,96663a3dd0d7b9d17d4c8211b191af259621c693@10.11.12.14:46656"
|
||||||
curl 'localhost:46657/dial_peers?persistent=true&peers=\["1.2.3.4:46656","5.6.7.8:46656"\]'
|
curl 'localhost:46657/dial_peers?persistent=true&peers=\["429fcf25974313b95673f58d77eacdd434402665@10.11.12.13:46656","96663a3dd0d7b9d17d4c8211b191af259621c693@10.11.12.14:46656"\]'
|
||||||
|
|
||||||
Adding a Non-Validator
|
Adding a Non-Validator
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@@ -387,20 +382,18 @@ Now we can update our genesis file. For instance, if the new
|
|||||||
::
|
::
|
||||||
|
|
||||||
{
|
{
|
||||||
"address": "AC379688105901436A34A65F185C115B8BB277A1",
|
"address" : "5AF49D2A2D4F5AD4C7C8C4CC2FB020131E9C4902",
|
||||||
"last_height": 0,
|
"pub_key" : {
|
||||||
"last_round": 0,
|
"value" : "l9X9+fjkeBzDfPGbUM7AMIRE6uJN78zN5+lk5OYotek=",
|
||||||
"last_signature": null,
|
"type" : "AC26791624DE60"
|
||||||
"last_signbytes": "",
|
},
|
||||||
|
"priv_key" : {
|
||||||
|
"value" : "EDJY9W6zlAw+su6ITgTKg2nTZcHAH1NMTW5iwlgmNDuX1f35+OR4HMN88ZtQzsAwhETq4k3vzM3n6WTk5ii16Q==",
|
||||||
|
"type" : "954568A3288910"
|
||||||
|
},
|
||||||
"last_step" : 0,
|
"last_step" : 0,
|
||||||
"priv_key": [
|
"last_round" : 0,
|
||||||
1,
|
"last_height" : 0
|
||||||
"0D2ED337D748ADF79BE28559B9E59EBE1ABBA0BAFE6D65FCB9797985329B950C8F2B5AACAACC9FCE41881349743B0CFDE190DF0177744568D4E82A18F0B7DF94"
|
|
||||||
],
|
|
||||||
"pub_key": [
|
|
||||||
1,
|
|
||||||
"8F2B5AACAACC9FCE41881349743B0CFDE190DF0177744568D4E82A18F0B7DF94"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
then the new ``genesis.json`` will be:
|
then the new ``genesis.json`` will be:
|
||||||
@@ -408,27 +401,27 @@ then the new ``genesis.json`` will be:
|
|||||||
::
|
::
|
||||||
|
|
||||||
{
|
{
|
||||||
"app_hash": "",
|
|
||||||
"chain_id": "test-chain-HZw6TB",
|
|
||||||
"genesis_time": "0001-01-01T00:00:00.000Z",
|
|
||||||
"validators" : [
|
"validators" : [
|
||||||
{
|
{
|
||||||
|
"pub_key" : {
|
||||||
|
"value" : "h3hk+QE8c6QLTySp8TcfzclJw/BG79ziGB/pIA+DfPE=",
|
||||||
|
"type" : "AC26791624DE60"
|
||||||
|
},
|
||||||
"power" : 10,
|
"power" : 10,
|
||||||
"name": "",
|
"name" : ""
|
||||||
"pub_key": [
|
|
||||||
1,
|
|
||||||
"5770B4DD55B3E08B7F5711C48B516347D8C33F47C30C226315D21AA64E0DFF2E"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"pub_key" : {
|
||||||
|
"value" : "l9X9+fjkeBzDfPGbUM7AMIRE6uJN78zN5+lk5OYotek=",
|
||||||
|
"type" : "AC26791624DE60"
|
||||||
|
},
|
||||||
"power" : 10,
|
"power" : 10,
|
||||||
"name": "",
|
"name" : ""
|
||||||
"pub_key": [
|
|
||||||
1,
|
|
||||||
"8F2B5AACAACC9FCE41881349743B0CFDE190DF0177744568D4E82A18F0B7DF94"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"app_hash" : "",
|
||||||
|
"chain_id" : "test-chain-rDlYSN",
|
||||||
|
"genesis_time" : "0001-01-01T00:00:00Z"
|
||||||
}
|
}
|
||||||
|
|
||||||
Update the ``genesis.json`` in ``~/.tendermint/config``. Copy the genesis file
|
Update the ``genesis.json`` in ``~/.tendermint/config``. Copy the genesis file
|
||||||
|
@@ -1,12 +1,11 @@
|
|||||||
package evidence
|
package evidence
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
wire "github.com/tendermint/go-wire"
|
amino "github.com/tendermint/go-amino"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/p2p"
|
"github.com/tendermint/tendermint/p2p"
|
||||||
@@ -16,7 +15,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
EvidenceChannel = byte(0x38)
|
EvidenceChannel = byte(0x38)
|
||||||
|
|
||||||
maxEvidenceMessageSize = 1048576 // 1MB TODO make it configurable
|
maxMsgSize = 1048576 // 1MB TODO make it configurable
|
||||||
broadcastEvidenceIntervalS = 60 // broadcast uncommitted evidence this often
|
broadcastEvidenceIntervalS = 60 // broadcast uncommitted evidence this often
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -68,7 +67,7 @@ func (evR *EvidenceReactor) AddPeer(peer p2p.Peer) {
|
|||||||
// the rest will be sent by the broadcastRoutine
|
// the rest will be sent by the broadcastRoutine
|
||||||
evidences := evR.evpool.PriorityEvidence()
|
evidences := evR.evpool.PriorityEvidence()
|
||||||
msg := &EvidenceListMessage{evidences}
|
msg := &EvidenceListMessage{evidences}
|
||||||
success := peer.Send(EvidenceChannel, struct{ EvidenceMessage }{msg})
|
success := peer.Send(EvidenceChannel, cdc.MustMarshalBinaryBare(msg))
|
||||||
if !success {
|
if !success {
|
||||||
// TODO: remove peer ?
|
// TODO: remove peer ?
|
||||||
}
|
}
|
||||||
@@ -82,9 +81,10 @@ func (evR *EvidenceReactor) RemovePeer(peer p2p.Peer, reason interface{}) {
|
|||||||
// Receive implements Reactor.
|
// Receive implements Reactor.
|
||||||
// It adds any received evidence to the evpool.
|
// It adds any received evidence to the evpool.
|
||||||
func (evR *EvidenceReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
|
func (evR *EvidenceReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
|
||||||
_, msg, err := DecodeMessage(msgBytes)
|
msg, err := DecodeMessage(msgBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evR.Logger.Error("Error decoding message", "err", err)
|
evR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes)
|
||||||
|
evR.Switch.StopPeerForError(src, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
evR.Logger.Debug("Receive", "src", src, "chId", chID, "msg", msg)
|
evR.Logger.Debug("Receive", "src", src, "chId", chID, "msg", msg)
|
||||||
@@ -95,7 +95,8 @@ func (evR *EvidenceReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
|
|||||||
err := evR.evpool.AddEvidence(ev)
|
err := evR.evpool.AddEvidence(ev)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evR.Logger.Info("Evidence is not valid", "evidence", msg.Evidence, "err", err)
|
evR.Logger.Info("Evidence is not valid", "evidence", msg.Evidence, "err", err)
|
||||||
// TODO: punish peer
|
// punish peer
|
||||||
|
evR.Switch.StopPeerForError(src, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -117,7 +118,7 @@ func (evR *EvidenceReactor) broadcastRoutine() {
|
|||||||
case evidence := <-evR.evpool.EvidenceChan():
|
case evidence := <-evR.evpool.EvidenceChan():
|
||||||
// broadcast some new evidence
|
// broadcast some new evidence
|
||||||
msg := &EvidenceListMessage{[]types.Evidence{evidence}}
|
msg := &EvidenceListMessage{[]types.Evidence{evidence}}
|
||||||
evR.Switch.Broadcast(EvidenceChannel, struct{ EvidenceMessage }{msg})
|
evR.Switch.Broadcast(EvidenceChannel, cdc.MustMarshalBinaryBare(msg))
|
||||||
|
|
||||||
// TODO: Broadcast runs asynchronously, so this should wait on the successChan
|
// TODO: Broadcast runs asynchronously, so this should wait on the successChan
|
||||||
// in another routine before marking to be proper.
|
// in another routine before marking to be proper.
|
||||||
@@ -125,7 +126,7 @@ func (evR *EvidenceReactor) broadcastRoutine() {
|
|||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
// broadcast all pending evidence
|
// broadcast all pending evidence
|
||||||
msg := &EvidenceListMessage{evR.evpool.PendingEvidence()}
|
msg := &EvidenceListMessage{evR.evpool.PendingEvidence()}
|
||||||
evR.Switch.Broadcast(EvidenceChannel, struct{ EvidenceMessage }{msg})
|
evR.Switch.Broadcast(EvidenceChannel, cdc.MustMarshalBinaryBare(msg))
|
||||||
case <-evR.Quit():
|
case <-evR.Quit():
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -135,24 +136,22 @@ func (evR *EvidenceReactor) broadcastRoutine() {
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Messages
|
// Messages
|
||||||
|
|
||||||
const (
|
|
||||||
msgTypeEvidence = byte(0x01)
|
|
||||||
)
|
|
||||||
|
|
||||||
// EvidenceMessage is a message sent or received by the EvidenceReactor.
|
// EvidenceMessage is a message sent or received by the EvidenceReactor.
|
||||||
type EvidenceMessage interface{}
|
type EvidenceMessage interface{}
|
||||||
|
|
||||||
var _ = wire.RegisterInterface(
|
func RegisterEvidenceMessages(cdc *amino.Codec) {
|
||||||
struct{ EvidenceMessage }{},
|
cdc.RegisterInterface((*EvidenceMessage)(nil), nil)
|
||||||
wire.ConcreteType{&EvidenceListMessage{}, msgTypeEvidence},
|
cdc.RegisterConcrete(&EvidenceListMessage{},
|
||||||
)
|
"tendermint/evidence/EvidenceListMessage", nil)
|
||||||
|
}
|
||||||
|
|
||||||
// DecodeMessage decodes a byte-array into a EvidenceMessage.
|
// DecodeMessage decodes a byte-array into a EvidenceMessage.
|
||||||
func DecodeMessage(bz []byte) (msgType byte, msg EvidenceMessage, err error) {
|
func DecodeMessage(bz []byte) (msg EvidenceMessage, err error) {
|
||||||
msgType = bz[0]
|
if len(bz) > maxMsgSize {
|
||||||
n := new(int)
|
return msg, fmt.Errorf("Msg exceeds max size (%d > %d)",
|
||||||
r := bytes.NewReader(bz)
|
len(bz), maxMsgSize)
|
||||||
msg = wire.ReadBinary(struct{ EvidenceMessage }{}, r, maxEvidenceMessageSize, n, &err).(struct{ EvidenceMessage }).EvidenceMessage
|
}
|
||||||
|
err = cdc.UnmarshalBinaryBare(bz, &msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,7 +3,6 @@ package evidence
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
wire "github.com/tendermint/go-wire"
|
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
dbm "github.com/tendermint/tmlibs/db"
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
)
|
)
|
||||||
@@ -104,7 +103,10 @@ func (store *EvidenceStore) ListEvidence(prefixKey string) (evidence []types.Evi
|
|||||||
val := iter.Value()
|
val := iter.Value()
|
||||||
|
|
||||||
var ei EvidenceInfo
|
var ei EvidenceInfo
|
||||||
wire.ReadBinaryBytes(val, &ei)
|
err := cdc.UnmarshalBinaryBare(val, &ei)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
evidence = append(evidence, ei.Evidence)
|
evidence = append(evidence, ei.Evidence)
|
||||||
}
|
}
|
||||||
return evidence
|
return evidence
|
||||||
@@ -119,7 +121,10 @@ func (store *EvidenceStore) GetEvidence(height int64, hash []byte) *EvidenceInfo
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var ei EvidenceInfo
|
var ei EvidenceInfo
|
||||||
wire.ReadBinaryBytes(val, &ei)
|
err := cdc.UnmarshalBinaryBare(val, &ei)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
return &ei
|
return &ei
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +142,7 @@ func (store *EvidenceStore) AddNewEvidence(evidence types.Evidence, priority int
|
|||||||
Priority: priority,
|
Priority: priority,
|
||||||
Evidence: evidence,
|
Evidence: evidence,
|
||||||
}
|
}
|
||||||
eiBytes := wire.BinaryBytes(ei)
|
eiBytes := cdc.MustMarshalBinaryBare(ei)
|
||||||
|
|
||||||
// add it to the store
|
// add it to the store
|
||||||
key := keyOutqueue(evidence, priority)
|
key := keyOutqueue(evidence, priority)
|
||||||
@@ -171,7 +176,7 @@ func (store *EvidenceStore) MarkEvidenceAsCommitted(evidence types.Evidence) {
|
|||||||
ei.Committed = true
|
ei.Committed = true
|
||||||
|
|
||||||
lookupKey := keyLookup(evidence)
|
lookupKey := keyLookup(evidence)
|
||||||
store.db.SetSync(lookupKey, wire.BinaryBytes(ei))
|
store.db.SetSync(lookupKey, cdc.MustMarshalBinaryBare(ei))
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------
|
//---------------------------------------------------
|
||||||
@@ -181,6 +186,9 @@ func (store *EvidenceStore) getEvidenceInfo(evidence types.Evidence) EvidenceInf
|
|||||||
key := keyLookup(evidence)
|
key := keyLookup(evidence)
|
||||||
var ei EvidenceInfo
|
var ei EvidenceInfo
|
||||||
b := store.db.Get(key)
|
b := store.db.Get(key)
|
||||||
wire.ReadBinaryBytes(b, &ei)
|
err := cdc.UnmarshalBinaryBare(b, &ei)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
return ei
|
return ei
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
wire "github.com/tendermint/go-wire"
|
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
dbm "github.com/tendermint/tmlibs/db"
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
)
|
)
|
||||||
@@ -108,15 +107,3 @@ func TestStorePriority(t *testing.T) {
|
|||||||
assert.Equal(ev, cases[i].ev)
|
assert.Equal(ev, cases[i].ev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------
|
|
||||||
const (
|
|
||||||
evidenceTypeMockGood = byte(0x01)
|
|
||||||
evidenceTypeMockBad = byte(0x02)
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ = wire.RegisterInterface(
|
|
||||||
struct{ types.Evidence }{},
|
|
||||||
wire.ConcreteType{types.MockGoodEvidence{}, evidenceTypeMockGood},
|
|
||||||
wire.ConcreteType{types.MockBadEvidence{}, evidenceTypeMockBad},
|
|
||||||
)
|
|
||||||
|
25
evidence/wire.go
Normal file
25
evidence/wire.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package evidence
|
||||||
|
|
||||||
|
import (
|
||||||
|
amino "github.com/tendermint/go-amino"
|
||||||
|
"github.com/tendermint/go-crypto"
|
||||||
|
"github.com/tendermint/tendermint/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cdc = amino.NewCodec()
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RegisterEvidenceMessages(cdc)
|
||||||
|
crypto.RegisterAmino(cdc)
|
||||||
|
types.RegisterEvidences(cdc)
|
||||||
|
RegisterMockEvidences(cdc) // For testing
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------
|
||||||
|
|
||||||
|
func RegisterMockEvidences(cdc *amino.Codec) {
|
||||||
|
cdc.RegisterConcrete(types.MockGoodEvidence{},
|
||||||
|
"tendermint/MockGoodEvidence", nil)
|
||||||
|
cdc.RegisterConcrete(types.MockBadEvidence{},
|
||||||
|
"tendermint/MockBadEvidence", nil)
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user