mirror of
https://github.com/fluencelabs/tendermint
synced 2025-07-29 11:11:55 +00:00
Compare commits
156 Commits
0.19.0-rc2
...
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 | ||
|
4930b61a38 | ||
|
9cc2cf362f | ||
|
ed93fb34ab | ||
|
3d32474da8 | ||
|
3233c318ea | ||
|
c9a263c589 | ||
|
6e39ec6e26 | ||
|
d38a6cc7ea | ||
|
7f6ee7a46b | ||
|
34b77fcad4 | ||
|
3b3f45d49b | ||
|
3284a13fee | ||
|
fc9ffee2e3 | ||
|
3a672cb2a9 | ||
|
4b8e342309 | ||
|
5a2fa71b03 | ||
|
9a57ef9cbf | ||
|
59ca9bf480 | ||
|
7cce07bc99 | ||
|
0ae66f75ce | ||
|
cee7b5cb54 | ||
|
1585152341 | ||
|
8e699c2bfd | ||
|
904a3115a6 | ||
|
a506cf47ad | ||
|
7689c15413 | ||
|
f907113c19 | ||
|
140f962201 | ||
|
c23d907f12 | ||
|
ed782e7508 | ||
|
0732526465 | ||
|
39a4963782 | ||
|
37ce6b195a | ||
|
7aa6d36258 | ||
|
991017fc41 | ||
|
5f548c7679 | ||
|
d14aacf03e | ||
|
39ff4d22e9 | ||
|
8462493cbf | ||
|
47b8bd1728 | ||
|
657fd671ea | ||
|
315c475b79 | ||
|
b800b4ec1d | ||
|
208ac32fa2 | ||
|
641476d40f | ||
|
491c8ab4c1 | ||
|
5ef8a6e887 | ||
|
d694d47d22 | ||
|
ecdc1b9bb0 | ||
|
9c757108ca | ||
|
5243e54641 | ||
|
70e7454c21 | ||
|
2644a529f0 | ||
|
eaee98ee1f | ||
|
575a46d9d4 | ||
|
bcadbd1b10 | ||
|
ead9daf1ba | ||
|
22949e6dfd | ||
|
49986b05bc | ||
|
2fa7af4614 | ||
|
2d857c4b1b | ||
|
2b63f57b4c | ||
|
4085c72496 | ||
|
6f9956990c | ||
|
9bf5862def | ||
|
e1d98bb7f6 | ||
|
e5cd006bce | ||
|
58242e1b63 | ||
|
4e86835163 | ||
|
ab4ac04c88 | ||
|
2c1887a635 | ||
|
1c82281b77 | ||
|
43ac92b615 | ||
|
8813684040 | ||
|
58f36bb321 | ||
|
4c2f56626a | ||
|
e3337d764a | ||
|
214817ed17 | ||
|
116a4ec705 | ||
|
bbaad22982 | ||
|
a7250af303 | ||
|
6545a21369 | ||
|
416f03c05b | ||
|
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 | ||
|
2a258a2c3f | ||
|
a40518c7da | ||
|
31deaa4a79 | ||
|
736ea055a8 | ||
|
a39aec0bae | ||
|
8bef3eb1f4 | ||
|
8723c91db9 |
@@ -130,19 +130,6 @@ jobs:
|
|||||||
paths:
|
paths:
|
||||||
- "profiles/*"
|
- "profiles/*"
|
||||||
|
|
||||||
test_libs:
|
|
||||||
<<: *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/test_libs.sh
|
|
||||||
|
|
||||||
test_persistence:
|
test_persistence:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
steps:
|
steps:
|
||||||
@@ -205,14 +192,6 @@ workflows:
|
|||||||
- test_cover:
|
- test_cover:
|
||||||
requires:
|
requires:
|
||||||
- setup_dependencies
|
- setup_dependencies
|
||||||
- test_libs:
|
|
||||||
filters:
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- develop
|
|
||||||
- master
|
|
||||||
requires:
|
|
||||||
- setup_dependencies
|
|
||||||
- test_persistence:
|
- test_persistence:
|
||||||
requires:
|
requires:
|
||||||
- setup_abci
|
- setup_abci
|
||||||
|
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
|
||||||
|
82
CHANGELOG.md
82
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,19 +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.17.0 (TBD)
|
## 0.19.0 (April 13th, 2018)
|
||||||
|
|
||||||
BREAKING:
|
BREAKING:
|
||||||
- [genesis] rename `app_options` to `app_state`
|
- [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:
|
IMPROVEMENTS:
|
||||||
- [config] exposed `auth_enc` flag to enable/disable encryption
|
- [config] trim whitespace from elements of lists (like `persistent_peers`)
|
||||||
- [p2p] when `auth_enc` is true, all dialed peers must have a node ID in their address
|
- [rpc] `/tx_search` results are sorted by height
|
||||||
- [all] renamed `dummy` (`persistent_dummy`) to `kvstore`
|
- [p2p] do not try to connect to ourselves (ok, maybe only once)
|
||||||
(`persistent_kvstore`) (name "dummy" is deprecated and will not work in
|
- [p2p] seeds respond with a bias towards good peers
|
||||||
release after this one)
|
|
||||||
|
|
||||||
## 0.16.0 (February 20th, 2017)
|
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
|
||||||
|
@@ -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
|
||||||
|
@@ -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)
|
||||||
|
44
Gopkg.lock
generated
44
Gopkg.lock
generated
@@ -105,7 +105,7 @@
|
|||||||
"json/scanner",
|
"json/scanner",
|
||||||
"json/token"
|
"json/token"
|
||||||
]
|
]
|
||||||
revision = "f40e974e75af4e271d97ce0fc917af5898ae7bda"
|
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/inconshreveable/mousetrap"
|
name = "github.com/inconshreveable/mousetrap"
|
||||||
@@ -159,7 +159,7 @@
|
|||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/rcrowley/go-metrics"
|
name = "github.com/rcrowley/go-metrics"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "8732c616f52954686704c8645fe1a9d59e9df7c1"
|
revision = "d932a24a8ccb8fcadc993e5c6c58f93dac168294"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/spf13/afero"
|
name = "github.com/spf13/afero"
|
||||||
@@ -167,8 +167,8 @@
|
|||||||
".",
|
".",
|
||||||
"mem"
|
"mem"
|
||||||
]
|
]
|
||||||
revision = "bb8f1927f2a9d3ab41c9340aa034f6b803f4359c"
|
revision = "63644898a8da0bc22138abf860edaf5277b6102e"
|
||||||
version = "v1.0.2"
|
version = "v1.1.0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/spf13/cast"
|
name = "github.com/spf13/cast"
|
||||||
@@ -179,8 +179,8 @@
|
|||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/spf13/cobra"
|
name = "github.com/spf13/cobra"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b"
|
revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4"
|
||||||
version = "v0.0.1"
|
version = "v0.0.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
@@ -191,8 +191,8 @@
|
|||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/spf13/pflag"
|
name = "github.com/spf13/pflag"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66"
|
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
|
||||||
version = "v1.0.0"
|
version = "v1.0.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/spf13/viper"
|
name = "github.com/spf13/viper"
|
||||||
@@ -226,7 +226,7 @@
|
|||||||
"leveldb/table",
|
"leveldb/table",
|
||||||
"leveldb/util"
|
"leveldb/util"
|
||||||
]
|
]
|
||||||
revision = "169b1b37be738edb2813dab48c97a549bcf99bb5"
|
revision = "714f901b98fdb3aa954b4193d8cbd64a28d80cad"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/tendermint/abci"
|
name = "github.com/tendermint/abci"
|
||||||
@@ -238,8 +238,8 @@
|
|||||||
"server",
|
"server",
|
||||||
"types"
|
"types"
|
||||||
]
|
]
|
||||||
revision = "c62aed95f2ce399ec815b0cafe478af002cdc4e6"
|
revision = "78a8905690ef54f9d57e3b2b0ee7ad3a04ef3f1f"
|
||||||
version = "v0.10.3-dev"
|
version = "v0.10.3"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
@@ -260,8 +260,8 @@
|
|||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/tendermint/go-crypto"
|
name = "github.com/tendermint/go-crypto"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "5d5f580f49ca66c13400938c64334186068c8b7c"
|
revision = "915416979bf70efa4bcbf1c6cd5d64c5fff9fc19"
|
||||||
version = "v0.6.1"
|
version = "v0.6.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/tendermint/go-wire"
|
name = "github.com/tendermint/go-wire"
|
||||||
@@ -285,8 +285,8 @@
|
|||||||
"pubsub/query",
|
"pubsub/query",
|
||||||
"test"
|
"test"
|
||||||
]
|
]
|
||||||
revision = "2e24b64fc121dcdf1cabceab8dc2f7257675483c"
|
revision = "97e1f1ad3f510048929a51475811a18686c894df"
|
||||||
version = "0.8.1"
|
version = "0.8.2-rc0"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
@@ -301,7 +301,7 @@
|
|||||||
"ripemd160",
|
"ripemd160",
|
||||||
"salsa20/salsa"
|
"salsa20/salsa"
|
||||||
]
|
]
|
||||||
revision = "88942b9c40a4c9d203b82b3731787b672d6e809b"
|
revision = "d6449816ce06963d9d136eee5a56fca5b0616e7e"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
@@ -315,13 +315,13 @@
|
|||||||
"lex/httplex",
|
"lex/httplex",
|
||||||
"trace"
|
"trace"
|
||||||
]
|
]
|
||||||
revision = "6078986fec03a1dcc236c34816c71b0e05018fda"
|
revision = "61147c48b25b599e5b561d2e9c4f3e1ef489ca41"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/sys"
|
name = "golang.org/x/sys"
|
||||||
packages = ["unix"]
|
packages = ["unix"]
|
||||||
revision = "91ee8cde435411ca3f1cd365e8f20131aed4d0a1"
|
revision = "3b87a42e500a6dc65dae1a55d0b641295971163e"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "golang.org/x/text"
|
name = "golang.org/x/text"
|
||||||
@@ -348,7 +348,7 @@
|
|||||||
branch = "master"
|
branch = "master"
|
||||||
name = "google.golang.org/genproto"
|
name = "google.golang.org/genproto"
|
||||||
packages = ["googleapis/rpc/status"]
|
packages = ["googleapis/rpc/status"]
|
||||||
revision = "ab0870e398d5dd054b868c0db1481ab029b9a9f2"
|
revision = "51d0944304c3cbce4afe9e5247e21100037bff78"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "google.golang.org/grpc"
|
name = "google.golang.org/grpc"
|
||||||
@@ -377,12 +377,12 @@
|
|||||||
[[projects]]
|
[[projects]]
|
||||||
name = "gopkg.in/yaml.v2"
|
name = "gopkg.in/yaml.v2"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "7f97868eec74b32b0982dd158a51a446d1da7eb5"
|
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
|
||||||
version = "v2.1.1"
|
version = "v2.2.1"
|
||||||
|
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "ed1149ed5293b7b28b7505627648c6a1152aaff0ed06a3849995b29751ae00f3"
|
inputs-digest = "e70f8692c825e80ae8510546e297840b9560d00e11b2272749a55cc2ffd147f0"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
26
Gopkg.toml
26
Gopkg.toml
@@ -35,23 +35,23 @@
|
|||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/go-kit/kit"
|
name = "github.com/go-kit/kit"
|
||||||
version = "0.6.0"
|
version = "~0.6.0"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/gogo/protobuf"
|
name = "github.com/gogo/protobuf"
|
||||||
version = "1.0.0"
|
version = "~1.0.0"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/golang/protobuf"
|
name = "github.com/golang/protobuf"
|
||||||
version = "1.0.0"
|
version = "~1.0.0"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/gorilla/websocket"
|
name = "github.com/gorilla/websocket"
|
||||||
version = "1.2.0"
|
version = "~1.2.0"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/pkg/errors"
|
name = "github.com/pkg/errors"
|
||||||
version = "0.8.0"
|
version = "~0.8.0"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/rcrowley/go-metrics"
|
name = "github.com/rcrowley/go-metrics"
|
||||||
@@ -59,35 +59,35 @@
|
|||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/spf13/cobra"
|
name = "github.com/spf13/cobra"
|
||||||
version = "0.0.1"
|
version = "~0.0.1"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/spf13/viper"
|
name = "github.com/spf13/viper"
|
||||||
version = "1.0.0"
|
version = "~1.0.0"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/stretchr/testify"
|
name = "github.com/stretchr/testify"
|
||||||
version = "1.2.1"
|
version = "~1.2.1"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/tendermint/abci"
|
name = "github.com/tendermint/abci"
|
||||||
version = "0.10.3-dev"
|
version = "~0.10.3"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/tendermint/go-crypto"
|
name = "github.com/tendermint/go-crypto"
|
||||||
version = "0.6.1"
|
version = "~0.6.2"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/tendermint/go-amino"
|
name = "github.com/tendermint/go-amino"
|
||||||
version = "0.9.6"
|
version = "~0.9.6"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/tendermint/tmlibs"
|
name = "github.com/tendermint/tmlibs"
|
||||||
version = "0.8.1"
|
version = "~0.8.2-rc0"
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "google.golang.org/grpc"
|
name = "google.golang.org/grpc"
|
||||||
version = "1.7.3"
|
version = "~1.7.3"
|
||||||
|
|
||||||
[prune]
|
[prune]
|
||||||
go-tests = true
|
go-tests = true
|
||||||
|
31
Makefile
31
Makefile
@@ -14,13 +14,13 @@ 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
|
||||||
@@ -119,11 +119,6 @@ test_integrations:
|
|||||||
make test_persistence
|
make test_persistence
|
||||||
make test_p2p
|
make test_p2p
|
||||||
|
|
||||||
test_libs:
|
|
||||||
# checkout every github.com/tendermint dir and run its tests
|
|
||||||
# NOTE: on release-* or master branches only (set by Jenkins)
|
|
||||||
docker run --name run_libs -t tester bash test/test_libs.sh
|
|
||||||
|
|
||||||
test_release:
|
test_release:
|
||||||
@go test -tags release $(PACKAGES)
|
@go test -tags release $(PACKAGES)
|
||||||
|
|
||||||
@@ -183,7 +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_cover test_apps test_persistence test_p2p test test_race test_libs test_integrations test_release test100 vagrant_test fmt
|
.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
|
||||||
|
|
||||||
|
@@ -4,7 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tendermint/go-amino"
|
amino "github.com/tendermint/go-amino"
|
||||||
"github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
|
|
||||||
proto "github.com/tendermint/tendermint/benchmarks/proto"
|
proto "github.com/tendermint/tendermint/benchmarks/proto"
|
||||||
@@ -16,20 +16,24 @@ func BenchmarkEncodeStatusWire(b *testing.B) {
|
|||||||
b.StopTimer()
|
b.StopTimer()
|
||||||
cdc := amino.NewCodec()
|
cdc := amino.NewCodec()
|
||||||
ctypes.RegisterAmino(cdc)
|
ctypes.RegisterAmino(cdc)
|
||||||
pubKey := crypto.GenPrivKeyEd25519().PubKey()
|
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()
|
||||||
|
|
||||||
@@ -48,9 +52,9 @@ func BenchmarkEncodeNodeInfoWire(b *testing.B) {
|
|||||||
b.StopTimer()
|
b.StopTimer()
|
||||||
cdc := amino.NewCodec()
|
cdc := amino.NewCodec()
|
||||||
ctypes.RegisterAmino(cdc)
|
ctypes.RegisterAmino(cdc)
|
||||||
pubKey := crypto.GenPrivKeyEd25519().PubKey()
|
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",
|
||||||
@@ -73,9 +77,9 @@ func BenchmarkEncodeNodeInfoBinary(b *testing.B) {
|
|||||||
b.StopTimer()
|
b.StopTimer()
|
||||||
cdc := amino.NewCodec()
|
cdc := amino.NewCodec()
|
||||||
ctypes.RegisterAmino(cdc)
|
ctypes.RegisterAmino(cdc)
|
||||||
pubKey := crypto.GenPrivKeyEd25519().PubKey()
|
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",
|
||||||
@@ -94,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().(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;
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tendermint/go-amino"
|
amino "github.com/tendermint/go-amino"
|
||||||
"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"
|
||||||
@@ -30,7 +30,7 @@ const (
|
|||||||
// NOTE: keep up to date with bcBlockResponseMessage
|
// NOTE: keep up to date with bcBlockResponseMessage
|
||||||
bcBlockResponseMessagePrefixSize = 4
|
bcBlockResponseMessagePrefixSize = 4
|
||||||
bcBlockResponseMessageFieldKeySize = 1
|
bcBlockResponseMessageFieldKeySize = 1
|
||||||
maxMessageSize = types.MaxBlockSizeBytes +
|
maxMsgSize = types.MaxBlockSizeBytes +
|
||||||
bcBlockResponseMessagePrefixSize +
|
bcBlockResponseMessagePrefixSize +
|
||||||
bcBlockResponseMessageFieldKeySize
|
bcBlockResponseMessageFieldKeySize
|
||||||
)
|
)
|
||||||
@@ -75,9 +75,9 @@ func NewBlockchainReactor(state sm.State, blockExec *sm.BlockExecutor, store *Bl
|
|||||||
store.Height()))
|
store.Height()))
|
||||||
}
|
}
|
||||||
|
|
||||||
const cap = 1000 // must be bigger than peers count
|
const capacity = 1000 // must be bigger than peers count
|
||||||
requestsCh := make(chan BlockRequest, cap)
|
requestsCh := make(chan BlockRequest, capacity)
|
||||||
errorsCh := make(chan peerError, cap) // so we don't block in #Receive#pool.AddBlock
|
errorsCh := make(chan peerError, capacity) // so we don't block in #Receive#pool.AddBlock
|
||||||
|
|
||||||
pool := NewBlockPool(
|
pool := NewBlockPool(
|
||||||
store.Height()+1,
|
store.Height()+1,
|
||||||
@@ -133,7 +133,7 @@ func (bcR *BlockchainReactor) GetChannels() []*p2p.ChannelDescriptor {
|
|||||||
Priority: 10,
|
Priority: 10,
|
||||||
SendQueueCapacity: 1000,
|
SendQueueCapacity: 1000,
|
||||||
RecvBufferCapacity: 50 * 4096,
|
RecvBufferCapacity: 50 * 4096,
|
||||||
RecvMessageCapacity: maxMessageSize,
|
RecvMessageCapacity: maxMsgSize,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -345,6 +345,10 @@ func RegisterBlockchainMessages(cdc *amino.Codec) {
|
|||||||
// DecodeMessage decodes BlockchainMessage.
|
// DecodeMessage decodes BlockchainMessage.
|
||||||
// TODO: ensure that bz is completely read.
|
// TODO: ensure that bz is completely read.
|
||||||
func DecodeMessage(bz []byte) (msg BlockchainMessage, err error) {
|
func DecodeMessage(bz []byte) (msg BlockchainMessage, err error) {
|
||||||
|
if len(bz) > maxMsgSize {
|
||||||
|
return msg, fmt.Errorf("Msg exceeds max size (%d > %d)",
|
||||||
|
len(bz), maxMsgSize)
|
||||||
|
}
|
||||||
err = cdc.UnmarshalBinaryBare(bz, &msg)
|
err = cdc.UnmarshalBinaryBare(bz, &msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = cmn.ErrorWrap(err, "DecodeMessage() had bytes left over")
|
err = cmn.ErrorWrap(err, "DecodeMessage() had bytes left over")
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
package blockchain
|
package blockchain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -218,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))
|
||||||
}
|
}
|
||||||
@@ -240,7 +239,7 @@ 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 {
|
||||||
panic(fmt.Sprintf("Could not unmarshal bytes: %X", bytes))
|
panic(fmt.Sprintf("Could not unmarshal bytes: %X", bytes))
|
||||||
}
|
}
|
||||||
|
@@ -31,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
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package blockchain
|
package blockchain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tendermint/go-amino"
|
amino "github.com/tendermint/go-amino"
|
||||||
"github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
@@ -3,6 +3,8 @@ 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"
|
pvm "github.com/tendermint/tendermint/types/priv_validator"
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
@@ -12,10 +14,14 @@ 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 pv *pvm.FilePV
|
var pv *pvm.FilePV
|
||||||
@@ -28,6 +34,16 @@ func initFiles(cmd *cobra.Command, args []string) {
|
|||||||
logger.Info("Generated 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
|
||||||
genFile := config.GenesisFile()
|
genFile := config.GenesisFile()
|
||||||
if cmn.FileExists(genFile) {
|
if cmn.FileExists(genFile) {
|
||||||
@@ -42,8 +58,10 @@ func initFiles(cmd *cobra.Command, args []string) {
|
|||||||
}}
|
}}
|
||||||
|
|
||||||
if err := genDoc.SaveAs(genFile); err != nil {
|
if err := genDoc.SaveAs(genFile); err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
logger.Info("Generated genesis file", "path", genFile)
|
logger.Info("Generated genesis file", "path", genFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -34,14 +34,14 @@ 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", "tcp://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) {
|
func ensureAddrHasSchemeOrDefaultToTCP(addr string) (string, error) {
|
||||||
u, err := url.Parse(nodeAddr)
|
u, err := url.Parse(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
@@ -31,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",
|
||||||
@@ -56,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()
|
||||||
|
@@ -16,10 +16,12 @@ var ShowNodeIDCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func showNodeID(cmd *cobra.Command, args []string) error {
|
func showNodeID(cmd *cobra.Command, args []string) error {
|
||||||
nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile())
|
|
||||||
|
nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println(nodeKey.ID())
|
fmt.Println(nodeKey.ID())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@ package commands
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
privval "github.com/tendermint/tendermint/types/priv_validator"
|
privval "github.com/tendermint/tendermint/types/priv_validator"
|
||||||
|
@@ -2,60 +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"
|
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
|
|
||||||
pvFile := filepath.Join(dataDir, mach, defaultConfig.PrivValidator)
|
initFilesWithConfig(config)
|
||||||
|
|
||||||
|
pvFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidator)
|
||||||
pv := pvm.LoadFilePV(pvFile)
|
pv := pvm.LoadFilePV(pvFile)
|
||||||
genVals[i] = types.GenesisValidator{
|
genVals[i] = types.GenesisValidator{
|
||||||
PubKey: pv.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(),
|
||||||
@@ -64,36 +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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Successfully initialized %v node directories\n", nValidators+nNonValidators)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize per-machine core directory
|
func hostnameOrIP(i int) string {
|
||||||
func initMachCoreDirectory(base, mach string) error {
|
if startingIPAddress != "" {
|
||||||
// Create priv_validator.json file if not present
|
ip := net.ParseIP(startingIPAddress)
|
||||||
defaultConfig := cfg.DefaultBaseConfig()
|
ip = ip.To4()
|
||||||
dir := filepath.Join(base, mach)
|
if ip == nil {
|
||||||
pvPath := filepath.Join(dir, defaultConfig.PrivValidator)
|
fmt.Printf("%v: non ipv4 address\n", startingIPAddress)
|
||||||
dir = filepath.Dir(pvPath)
|
os.Exit(1)
|
||||||
err := cmn.EnsureDir(dir, 0700)
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
ensurePrivValidator(pvPath)
|
persistentPeers[i] = p2p.IDAddressString(nodeKey.ID(), fmt.Sprintf("%s:%d", hostnameOrIP(i), p2pPort))
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func ensurePrivValidator(file string) {
|
|
||||||
if cmn.FileExists(file) {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
pv := pvm.GenFilePV(file)
|
persistentPeersList := strings.Join(persistentPeers, ",")
|
||||||
pv.Save()
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tendermint/go-amino"
|
amino "github.com/tendermint/go-amino"
|
||||||
"github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -25,6 +25,7 @@ func main() {
|
|||||||
cmd.ShowValidatorCmd,
|
cmd.ShowValidatorCmd,
|
||||||
cmd.TestnetFilesCmd,
|
cmd.TestnetFilesCmd,
|
||||||
cmd.ShowNodeIDCmd,
|
cmd.ShowNodeIDCmd,
|
||||||
|
cmd.GenNodeKeyCmd,
|
||||||
cmd.VersionCmd)
|
cmd.VersionCmd)
|
||||||
|
|
||||||
// NOTE:
|
// NOTE:
|
||||||
|
@@ -16,3 +16,8 @@ comment:
|
|||||||
require_changes: no
|
require_changes: no
|
||||||
require_base: no
|
require_base: no
|
||||||
require_head: yes
|
require_head: yes
|
||||||
|
|
||||||
|
ignore:
|
||||||
|
- "docs"
|
||||||
|
- "DOCKER"
|
||||||
|
- "scripts"
|
||||||
|
165
config/config.go
165
config/config.go
@@ -137,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{
|
||||||
@@ -161,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 = "kvstore"
|
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"
|
||||||
@@ -229,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
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@@ -250,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
|
||||||
@@ -289,6 +289,9 @@ type P2PConfig struct {
|
|||||||
|
|
||||||
// Authenticated encryption
|
// Authenticated encryption
|
||||||
AuthEnc bool `mapstructure:"auth_enc"`
|
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
|
||||||
@@ -310,16 +313,16 @@ func DefaultP2PConfig() *P2PConfig {
|
|||||||
|
|
||||||
// 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@@ -348,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@@ -394,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
|
||||||
@@ -434,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 }}"
|
||||||
@@ -162,6 +168,9 @@ seed_mode = {{ .P2P.SeedMode }}
|
|||||||
# Authenticated encryption
|
# Authenticated encryption
|
||||||
auth_enc = {{ .P2P.AuthEnc }}
|
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]
|
||||||
|
|
||||||
@@ -258,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)
|
||||||
|
@@ -102,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++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/tendermint/go-amino"
|
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"
|
||||||
|
|
||||||
@@ -25,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
|
||||||
)
|
)
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@@ -110,28 +112,28 @@ func (conR *ConsensusReactor) GetChannels() []*p2p.ChannelDescriptor {
|
|||||||
ID: StateChannel,
|
ID: StateChannel,
|
||||||
Priority: 5,
|
Priority: 5,
|
||||||
SendQueueCapacity: 100,
|
SendQueueCapacity: 100,
|
||||||
RecvMessageCapacity: maxConsensusMessageSize,
|
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: maxConsensusMessageSize,
|
RecvMessageCapacity: maxMsgSize,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: VoteChannel,
|
ID: VoteChannel,
|
||||||
Priority: 5,
|
Priority: 5,
|
||||||
SendQueueCapacity: 100,
|
SendQueueCapacity: 100,
|
||||||
RecvBufferCapacity: 100 * 100,
|
RecvBufferCapacity: 100 * 100,
|
||||||
RecvMessageCapacity: maxConsensusMessageSize,
|
RecvMessageCapacity: maxMsgSize,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: VoteSetBitsChannel,
|
ID: VoteSetBitsChannel,
|
||||||
Priority: 1,
|
Priority: 1,
|
||||||
SendQueueCapacity: 2,
|
SendQueueCapacity: 2,
|
||||||
RecvBufferCapacity: 1024,
|
RecvBufferCapacity: 1024,
|
||||||
RecvMessageCapacity: maxConsensusMessageSize,
|
RecvMessageCapacity: maxMsgSize,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -254,7 +256,7 @@ 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 > 10000 {
|
if numBlocks := ps.RecordBlockPart(msg); numBlocks%blocksToContributeToBecomeGoodPeer == 0 {
|
||||||
conR.Switch.MarkPeerAsGood(src)
|
conR.Switch.MarkPeerAsGood(src)
|
||||||
}
|
}
|
||||||
conR.conS.peerMsgQueue <- msgInfo{msg, src.ID()}
|
conR.conS.peerMsgQueue <- msgInfo{msg, src.ID()}
|
||||||
@@ -276,7 +278,7 @@ 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 > 10000 {
|
if blocks := ps.RecordVote(msg.Vote); blocks%blocksToContributeToBecomeGoodPeer == 0 {
|
||||||
conR.Switch.MarkPeerAsGood(src)
|
conR.Switch.MarkPeerAsGood(src)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,19 +374,21 @@ 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.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.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.EventDataProposalHeartbeat)
|
edph := data.(types.EventDataProposalHeartbeat)
|
||||||
conR.broadcastProposalHeartbeatMessage(edph)
|
conR.broadcastProposalHeartbeatMessage(edph)
|
||||||
@@ -393,6 +397,10 @@ func (conR *ConsensusReactor) startBroadcastRoutine() error {
|
|||||||
conR.eventBus.UnsubscribeAll(ctx, subscriber)
|
conR.eventBus.UnsubscribeAll(ctx, subscriber)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if !ok {
|
||||||
|
conR.eventBus.UnsubscribeAll(ctx, subscriber)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -603,11 +611,9 @@ func (conR *ConsensusReactor) gossipDataForCatchup(logger log.Logger, rs *cstype
|
|||||||
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) {
|
||||||
@@ -832,8 +838,8 @@ 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
|
||||||
@@ -853,6 +859,10 @@ type peerStateStats struct {
|
|||||||
blockParts int
|
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
|
||||||
func NewPeerState(peer p2p.Peer) *PeerState {
|
func NewPeerState(peer p2p.Peer) *PeerState {
|
||||||
return &PeerState{
|
return &PeerState{
|
||||||
@@ -868,12 +878,14 @@ func NewPeerState(peer p2p.Peer) *PeerState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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()
|
||||||
@@ -883,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 {
|
||||||
@@ -1045,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.
|
||||||
@@ -1083,27 +1103,46 @@ func (ps *PeerState) RecordVote(vote *types.Vote) int {
|
|||||||
ps.mtx.Lock()
|
ps.mtx.Lock()
|
||||||
defer ps.mtx.Unlock()
|
defer ps.mtx.Unlock()
|
||||||
|
|
||||||
if ps.stats.lastVoteHeight == vote.Height {
|
if ps.stats.lastVoteHeight >= vote.Height {
|
||||||
return ps.stats.votes
|
return ps.stats.votes
|
||||||
}
|
}
|
||||||
ps.stats.lastVoteHeight = vote.Height
|
ps.stats.lastVoteHeight = vote.Height
|
||||||
ps.stats.votes += 1
|
ps.stats.votes++
|
||||||
return ps.stats.votes
|
return ps.stats.votes
|
||||||
}
|
}
|
||||||
|
|
||||||
// RecordVote updates internal statistics for this peer by recording the block part.
|
// VotesSent returns the number of blocks for which peer has been sending us
|
||||||
// It returns the total number of block parts (1 per block). This essentially means
|
// votes.
|
||||||
// the number of blocks for which peer has been sending us block parts.
|
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 {
|
func (ps *PeerState) RecordBlockPart(bp *BlockPartMessage) int {
|
||||||
ps.mtx.Lock()
|
ps.mtx.Lock()
|
||||||
defer ps.mtx.Unlock()
|
defer ps.mtx.Unlock()
|
||||||
|
|
||||||
if ps.stats.lastBlockPartHeight == bp.Height {
|
if ps.stats.lastBlockPartHeight >= bp.Height {
|
||||||
return ps.stats.blockParts
|
return ps.stats.blockParts
|
||||||
}
|
}
|
||||||
|
|
||||||
ps.stats.lastBlockPartHeight = bp.Height
|
ps.stats.lastBlockPartHeight = bp.Height
|
||||||
ps.stats.blockParts += 1
|
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
|
return ps.stats.blockParts
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1255,9 +1294,11 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1283,6 +1324,10 @@ func RegisterConsensusMessages(cdc *amino.Codec) {
|
|||||||
|
|
||||||
// DecodeMessage decodes the given bytes into a ConsensusMessage.
|
// DecodeMessage decodes the given bytes into a ConsensusMessage.
|
||||||
func DecodeMessage(bz []byte) (msg ConsensusMessage, err error) {
|
func DecodeMessage(bz []byte) (msg ConsensusMessage, err error) {
|
||||||
|
if len(bz) > maxMsgSize {
|
||||||
|
return msg, fmt.Errorf("Msg exceeds max size (%d > %d)",
|
||||||
|
len(bz), maxMsgSize)
|
||||||
|
}
|
||||||
err = cdc.UnmarshalBinaryBare(bz, &msg)
|
err = cdc.UnmarshalBinaryBare(bz, &msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@@ -11,10 +11,12 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tendermint/abci/example/kvstore"
|
"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,6 +123,112 @@ 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
|
||||||
|
|
||||||
@@ -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) {
|
||||||
|
@@ -112,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
|
||||||
@@ -125,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
|
||||||
|
|
||||||
@@ -352,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)
|
||||||
@@ -362,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 {
|
||||||
@@ -390,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
|
||||||
}
|
}
|
||||||
@@ -429,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
|
||||||
@@ -299,7 +298,7 @@ func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusCo
|
|||||||
// 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,
|
proxyApp := proxy.NewAppConns(clientCreator,
|
||||||
NewHandshaker(stateDB, state, blockStore, gdoc.AppState))
|
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))
|
||||||
|
@@ -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 {
|
||||||
@@ -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)
|
||||||
|
@@ -168,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()
|
||||||
@@ -492,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)
|
||||||
@@ -718,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()
|
||||||
@@ -739,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -850,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.
|
||||||
@@ -1357,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})
|
||||||
|
|
||||||
@@ -1367,16 +1371,25 @@ 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 {
|
||||||
@@ -1451,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1492,12 +1497,11 @@ 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
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
|
@@ -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 {
|
||||||
|
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"github.com/tendermint/go-amino"
|
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"
|
||||||
|
@@ -4,7 +4,6 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -53,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, genDoc.AppState)
|
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 {
|
||||||
@@ -117,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) {
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package consensus
|
package consensus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tendermint/go-amino"
|
amino "github.com/tendermint/go-amino"
|
||||||
"github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
@@ -196,9 +196,8 @@ 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 #################################
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
@@ -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=kvstore --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=kvstore --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=kvstore --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=kvstore --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"
|
||||||
|
}
|
||||||
|
}
|
@@ -81,9 +81,8 @@ 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 kvstore.
|
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:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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
|
||||||
--------------
|
--------------
|
||||||
|
@@ -4,7 +4,7 @@ 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
|
||||||
-----------
|
-----------
|
||||||
@@ -37,13 +37,13 @@ First, install ``dep``:
|
|||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
cd $GOPATH/src/github.com/tendermint/tendermint
|
||||||
make get_tools
|
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
|
make get_vendor_deps
|
||||||
make install
|
make install
|
||||||
|
|
||||||
@@ -96,6 +96,7 @@ 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
|
||||||
|
make get_tools
|
||||||
make get_vendor_deps
|
make get_vendor_deps
|
||||||
make install
|
make install
|
||||||
|
|
||||||
|
@@ -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
|
||||||
@@ -124,6 +125,9 @@ like the file below, however, double check by inspecting the
|
|||||||
# Authenticated encryption
|
# Authenticated encryption
|
||||||
auth_enc = true
|
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]
|
||||||
|
|
||||||
|
@@ -18,8 +18,8 @@ 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).
|
- ``app_state``: The application state (e.g. initial distribution of tokens).
|
||||||
|
|
||||||
Sample genesis.json
|
Sample genesis.json
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
@@ -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": "",
|
"validators" : [
|
||||||
"chain_id": "test-chain-HZw6TB",
|
|
||||||
"genesis_time": "0001-01-01T00:00:00.000Z",
|
|
||||||
"validators": [
|
|
||||||
{
|
{
|
||||||
"power": 10,
|
"pub_key" : {
|
||||||
"name": "",
|
"value" : "h3hk+QE8c6QLTySp8TcfzclJw/BG79ziGB/pIA+DfPE=",
|
||||||
"pub_key": [
|
"type" : "AC26791624DE60"
|
||||||
1,
|
},
|
||||||
"5770B4DD55B3E08B7F5711C48B516347D8C33F47C30C226315D21AA64E0DFF2E"
|
"power" : 10,
|
||||||
]
|
"name" : ""
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"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_step" : 0,
|
||||||
"last_height": 0,
|
"last_round" : 0,
|
||||||
"last_round": 0,
|
"address" : "B788DEDE4F50AD8BC9462DE76741CCAFF87D51E2",
|
||||||
"last_signature": null,
|
"pub_key" : {
|
||||||
"last_signbytes": "",
|
"value" : "h3hk+QE8c6QLTySp8TcfzclJw/BG79ziGB/pIA+DfPE=",
|
||||||
"last_step": 0,
|
"type" : "AC26791624DE60"
|
||||||
"priv_key": [
|
},
|
||||||
1,
|
"last_height" : 0,
|
||||||
"F9FA3CD435BDAE54D0BCA8F1BC289D718C23D855C6DB21E8543F5E4F457E62805770B4DD55B3E08B7F5711C48B516347D8C33F47C30C226315D21AA64E0DFF2E"
|
"priv_key" : {
|
||||||
],
|
"value" : "JPivl82x+LfVkp8i3ztoTjY6c6GJ4pBxQexErOCyhwqHeGT5ATxzpAtPJKnxNx/NyUnD8Ebv3OIYH+kgD4N88Q==",
|
||||||
"pub_key": [
|
"type" : "954568A3288910"
|
||||||
1,
|
}
|
||||||
"5770B4DD55B3E08B7F5711C48B516347D8C33F47C30C226315D21AA64E0DFF2E"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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": "",
|
},
|
||||||
"last_step": 0,
|
"priv_key" : {
|
||||||
"priv_key": [
|
"value" : "EDJY9W6zlAw+su6ITgTKg2nTZcHAH1NMTW5iwlgmNDuX1f35+OR4HMN88ZtQzsAwhETq4k3vzM3n6WTk5ii16Q==",
|
||||||
1,
|
"type" : "954568A3288910"
|
||||||
"0D2ED337D748ADF79BE28559B9E59EBE1ABBA0BAFE6D65FCB9797985329B950C8F2B5AACAACC9FCE41881349743B0CFDE190DF0177744568D4E82A18F0B7DF94"
|
},
|
||||||
],
|
"last_step" : 0,
|
||||||
"pub_key": [
|
"last_round" : 0,
|
||||||
1,
|
"last_height" : 0
|
||||||
"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": "",
|
"validators" : [
|
||||||
"chain_id": "test-chain-HZw6TB",
|
|
||||||
"genesis_time": "0001-01-01T00:00:00.000Z",
|
|
||||||
"validators": [
|
|
||||||
{
|
{
|
||||||
"power": 10,
|
"pub_key" : {
|
||||||
"name": "",
|
"value" : "h3hk+QE8c6QLTySp8TcfzclJw/BG79ziGB/pIA+DfPE=",
|
||||||
"pub_key": [
|
"type" : "AC26791624DE60"
|
||||||
1,
|
},
|
||||||
"5770B4DD55B3E08B7F5711C48B516347D8C33F47C30C226315D21AA64E0DFF2E"
|
"power" : 10,
|
||||||
]
|
"name" : ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"power": 10,
|
"pub_key" : {
|
||||||
"name": "",
|
"value" : "l9X9+fjkeBzDfPGbUM7AMIRE6uJN78zN5+lk5OYotek=",
|
||||||
"pub_key": [
|
"type" : "AC26791624DE60"
|
||||||
1,
|
},
|
||||||
"8F2B5AACAACC9FCE41881349743B0CFDE190DF0177744568D4E82A18F0B7DF94"
|
"power" : 10,
|
||||||
]
|
"name" : ""
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"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
|
||||||
|
@@ -2,11 +2,12 @@ package evidence
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/tendermint/go-amino"
|
|
||||||
"github.com/tendermint/tmlibs/log"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
amino "github.com/tendermint/go-amino"
|
||||||
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/p2p"
|
"github.com/tendermint/tendermint/p2p"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
@@ -14,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
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -146,6 +147,10 @@ func RegisterEvidenceMessages(cdc *amino.Codec) {
|
|||||||
|
|
||||||
// DecodeMessage decodes a byte-array into a EvidenceMessage.
|
// DecodeMessage decodes a byte-array into a EvidenceMessage.
|
||||||
func DecodeMessage(bz []byte) (msg EvidenceMessage, err error) {
|
func DecodeMessage(bz []byte) (msg EvidenceMessage, err error) {
|
||||||
|
if len(bz) > maxMsgSize {
|
||||||
|
return msg, fmt.Errorf("Msg exceeds max size (%d > %d)",
|
||||||
|
len(bz), maxMsgSize)
|
||||||
|
}
|
||||||
err = cdc.UnmarshalBinaryBare(bz, &msg)
|
err = cdc.UnmarshalBinaryBare(bz, &msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package evidence
|
package evidence
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tendermint/go-amino"
|
amino "github.com/tendermint/go-amino"
|
||||||
"github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
@@ -93,7 +93,7 @@ func (p *provider) GetLatestCommit() (*ctypes.ResultCommit, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return p.node.Commit(&status.LatestBlockHeight)
|
return p.node.Commit(&status.SyncInfo.LatestBlockHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommitFromResult ...
|
// CommitFromResult ...
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package files
|
package files
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tendermint/go-amino"
|
amino "github.com/tendermint/go-amino"
|
||||||
"github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -11,11 +11,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func ValidateBlockMeta(meta *types.BlockMeta, check lite.Commit) error {
|
func ValidateBlockMeta(meta *types.BlockMeta, check lite.Commit) error {
|
||||||
|
if meta == nil {
|
||||||
|
return errors.New("expecting a non-nil BlockMeta")
|
||||||
|
}
|
||||||
// TODO: check the BlockID??
|
// TODO: check the BlockID??
|
||||||
return ValidateHeader(meta.Header, check)
|
return ValidateHeader(meta.Header, check)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidateBlock(meta *types.Block, check lite.Commit) error {
|
func ValidateBlock(meta *types.Block, check lite.Commit) error {
|
||||||
|
if meta == nil {
|
||||||
|
return errors.New("expecting a non-nil Block")
|
||||||
|
}
|
||||||
err := ValidateHeader(meta.Header, check)
|
err := ValidateHeader(meta.Header, check)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -27,6 +33,9 @@ func ValidateBlock(meta *types.Block, check lite.Commit) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ValidateHeader(head *types.Header, check lite.Commit) error {
|
func ValidateHeader(head *types.Header, check lite.Commit) error {
|
||||||
|
if head == nil {
|
||||||
|
return errors.New("expecting a non-nil Header")
|
||||||
|
}
|
||||||
// make sure they are for the same height (obvious fail)
|
// make sure they are for the same height (obvious fail)
|
||||||
if head.Height != check.Height() {
|
if head.Height != check.Height() {
|
||||||
return certerr.ErrHeightMismatch(head.Height, check.Height())
|
return certerr.ErrHeightMismatch(head.Height, check.Height())
|
||||||
|
@@ -3,7 +3,7 @@ package proxy
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/tendermint/go-amino"
|
amino "github.com/tendermint/go-amino"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||||
|
218
lite/proxy/validate_test.go
Normal file
218
lite/proxy/validate_test.go
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
package proxy_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/tendermint/tendermint/lite"
|
||||||
|
"github.com/tendermint/tendermint/lite/proxy"
|
||||||
|
"github.com/tendermint/tendermint/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
deadBeefTxs = types.Txs{[]byte("DE"), []byte("AD"), []byte("BE"), []byte("EF")}
|
||||||
|
deadBeefHash = deadBeefTxs.Hash()
|
||||||
|
testTime1 = time.Date(2018, 1, 1, 1, 1, 1, 1, time.UTC)
|
||||||
|
testTime2 = time.Date(2017, 1, 2, 1, 1, 1, 1, time.UTC)
|
||||||
|
)
|
||||||
|
|
||||||
|
var hdrHeight11 = &types.Header{
|
||||||
|
Height: 11,
|
||||||
|
Time: testTime1,
|
||||||
|
ValidatorsHash: []byte("Tendermint"),
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateBlock(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
block *types.Block
|
||||||
|
commit lite.Commit
|
||||||
|
wantErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
block: nil, wantErr: "non-nil Block",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
block: &types.Block{}, wantErr: "nil Header",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
block: &types.Block{Header: new(types.Header)},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Start Header.Height mismatch test
|
||||||
|
{
|
||||||
|
block: &types.Block{Header: &types.Header{Height: 10}},
|
||||||
|
commit: lite.Commit{Header: &types.Header{Height: 11}},
|
||||||
|
wantErr: "don't match - 10 vs 11",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
block: &types.Block{Header: &types.Header{Height: 11}},
|
||||||
|
commit: lite.Commit{Header: &types.Header{Height: 11}},
|
||||||
|
},
|
||||||
|
// End Header.Height mismatch test
|
||||||
|
|
||||||
|
// Start Header.Hash mismatch test
|
||||||
|
{
|
||||||
|
block: &types.Block{Header: hdrHeight11},
|
||||||
|
commit: lite.Commit{Header: &types.Header{Height: 11}},
|
||||||
|
wantErr: "Headers don't match",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
block: &types.Block{Header: hdrHeight11},
|
||||||
|
commit: lite.Commit{Header: hdrHeight11},
|
||||||
|
},
|
||||||
|
// End Header.Hash mismatch test
|
||||||
|
|
||||||
|
// Start Header.Data hash mismatch test
|
||||||
|
{
|
||||||
|
block: &types.Block{
|
||||||
|
Header: &types.Header{Height: 11},
|
||||||
|
Data: &types.Data{Txs: []types.Tx{[]byte("0xDE"), []byte("AD")}},
|
||||||
|
},
|
||||||
|
commit: lite.Commit{
|
||||||
|
Header: &types.Header{Height: 11},
|
||||||
|
Commit: &types.Commit{BlockID: types.BlockID{Hash: []byte("0xDEADBEEF")}},
|
||||||
|
},
|
||||||
|
wantErr: "Data hash doesn't match header",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
block: &types.Block{
|
||||||
|
Header: &types.Header{Height: 11, DataHash: deadBeefHash},
|
||||||
|
Data: &types.Data{Txs: deadBeefTxs},
|
||||||
|
},
|
||||||
|
commit: lite.Commit{
|
||||||
|
Header: &types.Header{Height: 11},
|
||||||
|
Commit: &types.Commit{BlockID: types.BlockID{Hash: []byte("DEADBEEF")}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// End Header.Data hash mismatch test
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
err := proxy.ValidateBlock(tt.block, tt.commit)
|
||||||
|
if tt.wantErr != "" {
|
||||||
|
if err == nil {
|
||||||
|
assert.FailNowf(t, "Unexpectedly passed", "#%d", i)
|
||||||
|
} else {
|
||||||
|
assert.Contains(t, err.Error(), tt.wantErr, "#%d should contain the substring\n\n", i)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Nil(t, err, "#%d: expecting a nil error", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateBlockMeta(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
meta *types.BlockMeta
|
||||||
|
commit lite.Commit
|
||||||
|
wantErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
meta: nil, wantErr: "non-nil BlockMeta",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
meta: &types.BlockMeta{}, wantErr: "non-nil Header",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
meta: &types.BlockMeta{Header: new(types.Header)},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Start Header.Height mismatch test
|
||||||
|
{
|
||||||
|
meta: &types.BlockMeta{Header: &types.Header{Height: 10}},
|
||||||
|
commit: lite.Commit{Header: &types.Header{Height: 11}},
|
||||||
|
wantErr: "don't match - 10 vs 11",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
meta: &types.BlockMeta{Header: &types.Header{Height: 11}},
|
||||||
|
commit: lite.Commit{Header: &types.Header{Height: 11}},
|
||||||
|
},
|
||||||
|
// End Header.Height mismatch test
|
||||||
|
|
||||||
|
// Start Headers don't match test
|
||||||
|
{
|
||||||
|
meta: &types.BlockMeta{Header: hdrHeight11},
|
||||||
|
commit: lite.Commit{Header: &types.Header{Height: 11}},
|
||||||
|
wantErr: "Headers don't match",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
meta: &types.BlockMeta{Header: hdrHeight11},
|
||||||
|
commit: lite.Commit{Header: hdrHeight11},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
meta: &types.BlockMeta{
|
||||||
|
Header: &types.Header{
|
||||||
|
Height: 11,
|
||||||
|
ValidatorsHash: []byte("lite-test"),
|
||||||
|
// TODO: should be able to use empty time after Amino upgrade
|
||||||
|
Time: testTime1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
commit: lite.Commit{
|
||||||
|
Header: &types.Header{Height: 11, DataHash: deadBeefHash},
|
||||||
|
},
|
||||||
|
wantErr: "Headers don't match",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
meta: &types.BlockMeta{
|
||||||
|
Header: &types.Header{
|
||||||
|
Height: 11, DataHash: deadBeefHash,
|
||||||
|
ValidatorsHash: []byte("Tendermint"),
|
||||||
|
Time: testTime1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
commit: lite.Commit{
|
||||||
|
Header: &types.Header{
|
||||||
|
Height: 11, DataHash: deadBeefHash,
|
||||||
|
ValidatorsHash: []byte("Tendermint"),
|
||||||
|
Time: testTime2,
|
||||||
|
},
|
||||||
|
Commit: &types.Commit{BlockID: types.BlockID{Hash: []byte("DEADBEEF")}},
|
||||||
|
},
|
||||||
|
wantErr: "Headers don't match",
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
meta: &types.BlockMeta{
|
||||||
|
Header: &types.Header{
|
||||||
|
Height: 11, DataHash: deadBeefHash,
|
||||||
|
ValidatorsHash: []byte("Tendermint"),
|
||||||
|
Time: testTime2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
commit: lite.Commit{
|
||||||
|
Header: &types.Header{
|
||||||
|
Height: 11, DataHash: deadBeefHash,
|
||||||
|
ValidatorsHash: []byte("Tendermint-x"),
|
||||||
|
Time: testTime2,
|
||||||
|
},
|
||||||
|
Commit: &types.Commit{BlockID: types.BlockID{Hash: []byte("DEADBEEF")}},
|
||||||
|
},
|
||||||
|
wantErr: "Headers don't match",
|
||||||
|
},
|
||||||
|
// End Headers don't match test
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
err := proxy.ValidateBlockMeta(tt.meta, tt.commit)
|
||||||
|
if tt.wantErr != "" {
|
||||||
|
if err == nil {
|
||||||
|
assert.FailNowf(t, "Unexpectedly passed", "#%d: wanted error %q", i, tt.wantErr)
|
||||||
|
} else {
|
||||||
|
assert.Contains(t, err.Error(), tt.wantErr, "#%d should contain the substring\n\n", i)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Nil(t, err, "#%d: expecting a nil error", i)
|
||||||
|
}
|
||||||
|
}
|
@@ -6,7 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
"github.com/tendermint/go-amino"
|
amino "github.com/tendermint/go-amino"
|
||||||
"github.com/tendermint/tmlibs/clist"
|
"github.com/tendermint/tmlibs/clist"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
MempoolChannel = byte(0x30)
|
MempoolChannel = byte(0x30)
|
||||||
|
|
||||||
maxMempoolMessageSize = 1048576 // 1MB TODO make it configurable
|
maxMsgSize = 1048576 // 1MB TODO make it configurable
|
||||||
peerCatchupSleepIntervalMS = 100 // If peer is behind, sleep this amount
|
peerCatchupSleepIntervalMS = 100 // If peer is behind, sleep this amount
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -167,6 +167,10 @@ func RegisterMempoolMessages(cdc *amino.Codec) {
|
|||||||
|
|
||||||
// DecodeMessage decodes a byte-array into a MempoolMessage.
|
// DecodeMessage decodes a byte-array into a MempoolMessage.
|
||||||
func DecodeMessage(bz []byte) (msg MempoolMessage, err error) {
|
func DecodeMessage(bz []byte) (msg MempoolMessage, err error) {
|
||||||
|
if len(bz) > maxMsgSize {
|
||||||
|
return msg, fmt.Errorf("Msg exceeds max size (%d > %d)",
|
||||||
|
len(bz), maxMsgSize)
|
||||||
|
}
|
||||||
err = cdc.UnmarshalBinaryBare(bz, &msg)
|
err = cdc.UnmarshalBinaryBare(bz, &msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
package mempool
|
package mempool
|
||||||
|
|
||||||
import (
|
import amino "github.com/tendermint/go-amino"
|
||||||
"github.com/tendermint/go-amino"
|
|
||||||
)
|
|
||||||
|
|
||||||
var cdc = amino.NewCodec()
|
var cdc = amino.NewCodec()
|
||||||
|
|
||||||
|
39
node/node.go
39
node/node.go
@@ -2,12 +2,10 @@ package node
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
abci "github.com/tendermint/abci/types"
|
abci "github.com/tendermint/abci/types"
|
||||||
amino "github.com/tendermint/go-amino"
|
amino "github.com/tendermint/go-amino"
|
||||||
@@ -163,7 +161,7 @@ func NewNode(config *cfg.Config,
|
|||||||
// and sync tendermint and the app by performing a handshake
|
// and sync tendermint and the app by performing a handshake
|
||||||
// and replaying any necessary blocks
|
// and replaying any necessary blocks
|
||||||
consensusLogger := logger.With("module", "consensus")
|
consensusLogger := logger.With("module", "consensus")
|
||||||
handshaker := cs.NewHandshaker(stateDB, state, blockStore, genDoc.AppState)
|
handshaker := cs.NewHandshaker(stateDB, state, blockStore, genDoc.AppState())
|
||||||
handshaker.SetLogger(consensusLogger)
|
handshaker.SetLogger(consensusLogger)
|
||||||
proxyApp := proxy.NewAppConns(clientCreator, handshaker)
|
proxyApp := proxy.NewAppConns(clientCreator, handshaker)
|
||||||
proxyApp.SetLogger(logger.With("module", "proxy"))
|
proxyApp.SetLogger(logger.With("module", "proxy"))
|
||||||
@@ -278,12 +276,11 @@ func NewNode(config *cfg.Config,
|
|||||||
trustMetricStore = trust.NewTrustMetricStore(trustHistoryDB, trust.DefaultConfig())
|
trustMetricStore = trust.NewTrustMetricStore(trustHistoryDB, trust.DefaultConfig())
|
||||||
trustMetricStore.SetLogger(p2pLogger)
|
trustMetricStore.SetLogger(p2pLogger)
|
||||||
|
|
||||||
var seeds []string
|
|
||||||
if config.P2P.Seeds != "" {
|
|
||||||
seeds = strings.Split(config.P2P.Seeds, ",")
|
|
||||||
}
|
|
||||||
pexReactor := pex.NewPEXReactor(addrBook,
|
pexReactor := pex.NewPEXReactor(addrBook,
|
||||||
&pex.PEXReactorConfig{Seeds: seeds, SeedMode: config.P2P.SeedMode})
|
&pex.PEXReactorConfig{
|
||||||
|
Seeds: cmn.SplitAndTrim(config.P2P.Seeds, ",", " "),
|
||||||
|
SeedMode: config.P2P.SeedMode,
|
||||||
|
PrivatePeerIDs: cmn.SplitAndTrim(config.P2P.PrivatePeerIDs, ",", " ")})
|
||||||
pexReactor.SetLogger(p2pLogger)
|
pexReactor.SetLogger(p2pLogger)
|
||||||
sw.AddReactor("PEX", pexReactor)
|
sw.AddReactor("PEX", pexReactor)
|
||||||
}
|
}
|
||||||
@@ -333,7 +330,7 @@ func NewNode(config *cfg.Config,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if config.TxIndex.IndexTags != "" {
|
if config.TxIndex.IndexTags != "" {
|
||||||
txIndexer = kv.NewTxIndex(store, kv.IndexTags(strings.Split(config.TxIndex.IndexTags, ",")))
|
txIndexer = kv.NewTxIndex(store, kv.IndexTags(cmn.SplitAndTrim(config.TxIndex.IndexTags, ",", " ")))
|
||||||
} else if config.TxIndex.IndexAllTags {
|
} else if config.TxIndex.IndexAllTags {
|
||||||
txIndexer = kv.NewTxIndex(store, kv.IndexAllTags())
|
txIndexer = kv.NewTxIndex(store, kv.IndexAllTags())
|
||||||
} else {
|
} else {
|
||||||
@@ -408,9 +405,14 @@ func (n *Node) OnStart() error {
|
|||||||
}
|
}
|
||||||
n.Logger.Info("P2P Node ID", "ID", nodeKey.ID(), "file", n.config.NodeKeyFile())
|
n.Logger.Info("P2P Node ID", "ID", nodeKey.ID(), "file", n.config.NodeKeyFile())
|
||||||
|
|
||||||
// Start the switch
|
nodeInfo := n.makeNodeInfo(nodeKey.ID())
|
||||||
n.sw.SetNodeInfo(n.makeNodeInfo(nodeKey.PubKey()))
|
n.sw.SetNodeInfo(nodeInfo)
|
||||||
n.sw.SetNodeKey(nodeKey)
|
n.sw.SetNodeKey(nodeKey)
|
||||||
|
|
||||||
|
// Add ourselves to addrbook to prevent dialing ourselves
|
||||||
|
n.addrBook.AddOurAddress(nodeInfo.NetAddress())
|
||||||
|
|
||||||
|
// Start the switch
|
||||||
err = n.sw.Start()
|
err = n.sw.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -418,7 +420,7 @@ func (n *Node) OnStart() error {
|
|||||||
|
|
||||||
// Always connect to persistent peers
|
// Always connect to persistent peers
|
||||||
if n.config.P2P.PersistentPeers != "" {
|
if n.config.P2P.PersistentPeers != "" {
|
||||||
err = n.sw.DialPeersAsync(n.addrBook, strings.Split(n.config.P2P.PersistentPeers, ","), true)
|
err = n.sw.DialPeersAsync(n.addrBook, cmn.SplitAndTrim(n.config.P2P.PersistentPeers, ",", " "), true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -489,7 +491,7 @@ func (n *Node) ConfigureRPC() {
|
|||||||
|
|
||||||
func (n *Node) startRPC() ([]net.Listener, error) {
|
func (n *Node) startRPC() ([]net.Listener, error) {
|
||||||
n.ConfigureRPC()
|
n.ConfigureRPC()
|
||||||
listenAddrs := strings.Split(n.config.RPC.ListenAddress, ",")
|
listenAddrs := cmn.SplitAndTrim(n.config.RPC.ListenAddress, ",", " ")
|
||||||
coreCodec := amino.NewCodec()
|
coreCodec := amino.NewCodec()
|
||||||
ctypes.RegisterAmino(coreCodec)
|
ctypes.RegisterAmino(coreCodec)
|
||||||
|
|
||||||
@@ -577,13 +579,13 @@ func (n *Node) ProxyApp() proxy.AppConns {
|
|||||||
return n.proxyApp
|
return n.proxyApp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) makeNodeInfo(pubKey crypto.PubKey) p2p.NodeInfo {
|
func (n *Node) makeNodeInfo(nodeID p2p.ID) p2p.NodeInfo {
|
||||||
txIndexerStatus := "on"
|
txIndexerStatus := "on"
|
||||||
if _, ok := n.txIndexer.(*null.TxIndex); ok {
|
if _, ok := n.txIndexer.(*null.TxIndex); ok {
|
||||||
txIndexerStatus = "off"
|
txIndexerStatus = "off"
|
||||||
}
|
}
|
||||||
nodeInfo := p2p.NodeInfo{
|
nodeInfo := p2p.NodeInfo{
|
||||||
PubKey: pubKey,
|
ID: nodeID,
|
||||||
Network: n.genesisDoc.ChainID,
|
Network: n.genesisDoc.ChainID,
|
||||||
Version: version.Version,
|
Version: version.Version,
|
||||||
Channels: []byte{
|
Channels: []byte{
|
||||||
@@ -639,19 +641,18 @@ func loadGenesisDoc(db dbm.DB) (*types.GenesisDoc, error) {
|
|||||||
bytes := db.Get(genesisDocKey)
|
bytes := db.Get(genesisDocKey)
|
||||||
if len(bytes) == 0 {
|
if len(bytes) == 0 {
|
||||||
return nil, errors.New("Genesis doc not found")
|
return nil, errors.New("Genesis doc not found")
|
||||||
} else {
|
}
|
||||||
var genDoc *types.GenesisDoc
|
var genDoc *types.GenesisDoc
|
||||||
err := json.Unmarshal(bytes, &genDoc)
|
err := cdc.UnmarshalJSON(bytes, &genDoc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmn.PanicCrisis(fmt.Sprintf("Failed to load genesis doc due to unmarshaling error: %v (bytes: %X)", err, bytes))
|
cmn.PanicCrisis(fmt.Sprintf("Failed to load genesis doc due to unmarshaling error: %v (bytes: %X)", err, bytes))
|
||||||
}
|
}
|
||||||
return genDoc, nil
|
return genDoc, nil
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// panics if failed to marshal the given genesis document
|
// panics if failed to marshal the given genesis document
|
||||||
func saveGenesisDoc(db dbm.DB, genDoc *types.GenesisDoc) {
|
func saveGenesisDoc(db dbm.DB, genDoc *types.GenesisDoc) {
|
||||||
bytes, err := json.Marshal(genDoc)
|
bytes, err := cdc.MarshalJSON(genDoc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cmn.PanicCrisis(fmt.Sprintf("Failed to save genesis doc due to marshaling error: %v", err))
|
cmn.PanicCrisis(fmt.Sprintf("Failed to save genesis doc due to marshaling error: %v", err))
|
||||||
}
|
}
|
||||||
|
12
node/wire.go
Normal file
12
node/wire.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package node
|
||||||
|
|
||||||
|
import (
|
||||||
|
amino "github.com/tendermint/go-amino"
|
||||||
|
crypto "github.com/tendermint/go-crypto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cdc = amino.NewCodec()
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
crypto.RegisterAmino(cdc)
|
||||||
|
}
|
@@ -47,7 +47,7 @@ func NewBaseReactor(name string, impl Reactor) *BaseReactor {
|
|||||||
func (br *BaseReactor) SetSwitch(sw *Switch) {
|
func (br *BaseReactor) SetSwitch(sw *Switch) {
|
||||||
br.Switch = sw
|
br.Switch = sw
|
||||||
}
|
}
|
||||||
func (_ *BaseReactor) GetChannels() []*conn.ChannelDescriptor { return nil }
|
func (*BaseReactor) GetChannels() []*conn.ChannelDescriptor { return nil }
|
||||||
func (_ *BaseReactor) AddPeer(peer Peer) {}
|
func (*BaseReactor) AddPeer(peer Peer) {}
|
||||||
func (_ *BaseReactor) RemovePeer(peer Peer, reason interface{}) {}
|
func (*BaseReactor) RemovePeer(peer Peer, reason interface{}) {}
|
||||||
func (_ *BaseReactor) Receive(chID byte, peer Peer, msgBytes []byte) {}
|
func (*BaseReactor) Receive(chID byte, peer Peer, msgBytes []byte) {}
|
||||||
|
@@ -432,9 +432,8 @@ func (c *MConnection) sendPacketMsg() bool {
|
|||||||
// Nothing to send?
|
// Nothing to send?
|
||||||
if leastChannel == nil {
|
if leastChannel == nil {
|
||||||
return true
|
return true
|
||||||
} else {
|
|
||||||
// c.Logger.Info("Found a PacketMsg to send")
|
|
||||||
}
|
}
|
||||||
|
// c.Logger.Info("Found a msgPacket to send")
|
||||||
|
|
||||||
// Make & send a PacketMsg from this channel
|
// Make & send a PacketMsg from this channel
|
||||||
_n, err := leastChannel.writePacketMsgTo(c.bufConnWriter)
|
_n, err := leastChannel.writePacketMsgTo(c.bufConnWriter)
|
||||||
|
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/tendermint/go-amino"
|
amino "github.com/tendermint/go-amino"
|
||||||
"github.com/tendermint/tmlibs/log"
|
"github.com/tendermint/tmlibs/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -111,7 +111,7 @@ func (sc *SecretConnection) RemotePubKey() crypto.PubKey {
|
|||||||
// CONTRACT: data smaller than dataMaxSize is read atomically.
|
// CONTRACT: data smaller than dataMaxSize is read atomically.
|
||||||
func (sc *SecretConnection) Write(data []byte) (n int, err error) {
|
func (sc *SecretConnection) Write(data []byte) (n int, err error) {
|
||||||
for 0 < len(data) {
|
for 0 < len(data) {
|
||||||
var frame []byte = make([]byte, totalFrameSize)
|
var frame = make([]byte, totalFrameSize)
|
||||||
var chunk []byte
|
var chunk []byte
|
||||||
if dataMaxSize < len(data) {
|
if dataMaxSize < len(data) {
|
||||||
chunk = data[:dataMaxSize]
|
chunk = data[:dataMaxSize]
|
||||||
@@ -134,9 +134,8 @@ func (sc *SecretConnection) Write(data []byte) (n int, err error) {
|
|||||||
_, err := sc.conn.Write(sealedFrame)
|
_, err := sc.conn.Write(sealedFrame)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return n, err
|
return n, err
|
||||||
} else {
|
|
||||||
n += len(chunk)
|
|
||||||
}
|
}
|
||||||
|
n += len(chunk)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -229,7 +228,6 @@ func shareEphPubKey(conn io.ReadWriteCloser, locEphPub *[32]byte) (remEphPub *[3
|
|||||||
// Otherwise:
|
// Otherwise:
|
||||||
var _remEphPub = trs.FirstValue().([32]byte)
|
var _remEphPub = trs.FirstValue().([32]byte)
|
||||||
return &_remEphPub, nil
|
return &_remEphPub, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func computeSharedSecret(remPubKey, locPrivKey *[32]byte) (shrSecret *[32]byte) {
|
func computeSharedSecret(remPubKey, locPrivKey *[32]byte) (shrSecret *[32]byte) {
|
||||||
@@ -342,7 +340,7 @@ func incr2Nonce(nonce *[24]byte) {
|
|||||||
// increment nonce big-endian by 1 with wraparound.
|
// increment nonce big-endian by 1 with wraparound.
|
||||||
func incrNonce(nonce *[24]byte) {
|
func incrNonce(nonce *[24]byte) {
|
||||||
for i := 23; 0 <= i; i-- {
|
for i := 23; 0 <= i; i-- {
|
||||||
nonce[i] += 1
|
nonce[i]++
|
||||||
if nonce[i] != 0 {
|
if nonce[i] != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@@ -73,6 +73,7 @@ func makeSecretConnPair(tb testing.TB) (fooSecConn, barSecConn *SecretConnection
|
|||||||
return nil, nil, false
|
return nil, nil, false
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
require.Nil(tb, trs.FirstError())
|
require.Nil(tb, trs.FirstError())
|
||||||
require.True(tb, ok, "Unexpected task abortion")
|
require.True(tb, ok, "Unexpected task abortion")
|
||||||
|
|
||||||
@@ -181,7 +182,7 @@ func TestSecretConnectionReadWrite(t *testing.T) {
|
|||||||
var readCount = 0
|
var readCount = 0
|
||||||
for _, readChunk := range reads {
|
for _, readChunk := range reads {
|
||||||
read += readChunk
|
read += readChunk
|
||||||
readCount += 1
|
readCount++
|
||||||
if len(write) <= len(read) {
|
if len(write) <= len(read) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package conn
|
package conn
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tendermint/go-amino"
|
amino "github.com/tendermint/go-amino"
|
||||||
"github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
72
p2p/dummy/peer.go
Normal file
72
p2p/dummy/peer.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package dummy
|
||||||
|
|
||||||
|
import (
|
||||||
|
p2p "github.com/tendermint/tendermint/p2p"
|
||||||
|
tmconn "github.com/tendermint/tendermint/p2p/conn"
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
type peer struct {
|
||||||
|
cmn.BaseService
|
||||||
|
kv map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ p2p.Peer = (*peer)(nil)
|
||||||
|
|
||||||
|
// NewPeer creates new dummy peer.
|
||||||
|
func NewPeer() *peer {
|
||||||
|
p := &peer{
|
||||||
|
kv: make(map[string]interface{}),
|
||||||
|
}
|
||||||
|
p.BaseService = *cmn.NewBaseService(nil, "peer", p)
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID always returns dummy.
|
||||||
|
func (p *peer) ID() p2p.ID {
|
||||||
|
return p2p.ID("dummy")
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOutbound always returns false.
|
||||||
|
func (p *peer) IsOutbound() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPersistent always returns false.
|
||||||
|
func (p *peer) IsPersistent() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeInfo always returns empty node info.
|
||||||
|
func (p *peer) NodeInfo() p2p.NodeInfo {
|
||||||
|
return p2p.NodeInfo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status always returns empry connection status.
|
||||||
|
func (p *peer) Status() tmconn.ConnectionStatus {
|
||||||
|
return tmconn.ConnectionStatus{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send does not do anything and just returns true.
|
||||||
|
func (p *peer) Send(byte, []byte) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrySend does not do anything and just returns true.
|
||||||
|
func (p *peer) TrySend(byte, []byte) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set records value under key specified in the map.
|
||||||
|
func (p *peer) Set(key string, value interface{}) {
|
||||||
|
p.kv[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns a value associated with the key. Nil is returned if no value
|
||||||
|
// found.
|
||||||
|
func (p *peer) Get(key string) interface{} {
|
||||||
|
if value, ok := p.kv[key]; ok {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@@ -1,10 +1,11 @@
|
|||||||
package p2p
|
package p2p
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -124,7 +125,7 @@ func (fc *FuzzedConnection) SetWriteDeadline(t time.Time) error {
|
|||||||
|
|
||||||
func (fc *FuzzedConnection) randomDuration() time.Duration {
|
func (fc *FuzzedConnection) randomDuration() time.Duration {
|
||||||
maxDelayMillis := int(fc.config.MaxDelay.Nanoseconds() / 1000)
|
maxDelayMillis := int(fc.config.MaxDelay.Nanoseconds() / 1000)
|
||||||
return time.Millisecond * time.Duration(rand.Int()%maxDelayMillis) // nolint: gas
|
return time.Millisecond * time.Duration(cmn.RandInt()%maxDelayMillis) // nolint: gas
|
||||||
}
|
}
|
||||||
|
|
||||||
// implements the fuzz (delay, kill conn)
|
// implements the fuzz (delay, kill conn)
|
||||||
@@ -137,7 +138,7 @@ func (fc *FuzzedConnection) fuzz() bool {
|
|||||||
switch fc.config.Mode {
|
switch fc.config.Mode {
|
||||||
case FuzzModeDrop:
|
case FuzzModeDrop:
|
||||||
// randomly drop the r/w, drop the conn, or sleep
|
// randomly drop the r/w, drop the conn, or sleep
|
||||||
r := rand.Float64()
|
r := cmn.RandFloat64()
|
||||||
if r <= fc.config.ProbDropRW {
|
if r <= fc.config.ProbDropRW {
|
||||||
return true
|
return true
|
||||||
} else if r < fc.config.ProbDropRW+fc.config.ProbDropConn {
|
} else if r < fc.config.ProbDropRW+fc.config.ProbDropConn {
|
||||||
|
@@ -47,17 +47,16 @@ func PubKeyToID(pubKey crypto.PubKey) ID {
|
|||||||
// If the file does not exist, it generates and saves a new NodeKey.
|
// If the file does not exist, it generates and saves a new NodeKey.
|
||||||
func LoadOrGenNodeKey(filePath string) (*NodeKey, error) {
|
func LoadOrGenNodeKey(filePath string) (*NodeKey, error) {
|
||||||
if cmn.FileExists(filePath) {
|
if cmn.FileExists(filePath) {
|
||||||
nodeKey, err := loadNodeKey(filePath)
|
nodeKey, err := LoadNodeKey(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return nodeKey, nil
|
return nodeKey, nil
|
||||||
} else {
|
|
||||||
return genNodeKey(filePath)
|
|
||||||
}
|
}
|
||||||
|
return genNodeKey(filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadNodeKey(filePath string) (*NodeKey, error) {
|
func LoadNodeKey(filePath string) (*NodeKey, error) {
|
||||||
jsonBytes, err := ioutil.ReadFile(filePath)
|
jsonBytes, err := ioutil.ReadFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -65,7 +64,7 @@ func loadNodeKey(filePath string) (*NodeKey, error) {
|
|||||||
nodeKey := new(NodeKey)
|
nodeKey := new(NodeKey)
|
||||||
err = cdc.UnmarshalJSON(jsonBytes, nodeKey)
|
err = cdc.UnmarshalJSON(jsonBytes, nodeKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Error reading NodeKey from %v: %v\n", filePath, err)
|
return nil, fmt.Errorf("Error reading NodeKey from %v: %v", filePath, err)
|
||||||
}
|
}
|
||||||
return nodeKey, nil
|
return nodeKey, nil
|
||||||
}
|
}
|
||||||
|
@@ -72,7 +72,7 @@ func NewDefaultListener(protocol string, lAddr string, skipUPNP bool, logger log
|
|||||||
|
|
||||||
// Determine internal address...
|
// Determine internal address...
|
||||||
var intAddr *NetAddress
|
var intAddr *NetAddress
|
||||||
intAddr, err = NewNetAddressString(lAddr)
|
intAddr, err = NewNetAddressStringWithOptionalID(lAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@@ -48,33 +48,45 @@ func NewNetAddress(id ID, addr net.Addr) *NetAddress {
|
|||||||
}
|
}
|
||||||
ip := tcpAddr.IP
|
ip := tcpAddr.IP
|
||||||
port := uint16(tcpAddr.Port)
|
port := uint16(tcpAddr.Port)
|
||||||
netAddr := NewNetAddressIPPort(ip, port)
|
na := NewNetAddressIPPort(ip, port)
|
||||||
netAddr.ID = id
|
na.ID = id
|
||||||
return netAddr
|
return na
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNetAddressString returns a new NetAddress using the provided
|
// NewNetAddressString returns a new NetAddress using the provided address in
|
||||||
// address in the form of "ID@IP:Port", where the ID is optional.
|
// the form of "ID@IP:Port".
|
||||||
// Also resolves the host if host is not an IP.
|
// Also resolves the host if host is not an IP.
|
||||||
func NewNetAddressString(addr string) (*NetAddress, error) {
|
func NewNetAddressString(addr string) (*NetAddress, error) {
|
||||||
addr = removeProtocolIfDefined(addr)
|
spl := strings.Split(addr, "@")
|
||||||
|
if len(spl) < 2 {
|
||||||
|
return nil, fmt.Errorf("Address (%s) does not contain ID", addr)
|
||||||
|
}
|
||||||
|
return NewNetAddressStringWithOptionalID(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNetAddressStringWithOptionalID returns a new NetAddress using the
|
||||||
|
// provided address in the form of "ID@IP:Port", where the ID is optional.
|
||||||
|
// Also resolves the host if host is not an IP.
|
||||||
|
func NewNetAddressStringWithOptionalID(addr string) (*NetAddress, error) {
|
||||||
|
addrWithoutProtocol := removeProtocolIfDefined(addr)
|
||||||
|
|
||||||
var id ID
|
var id ID
|
||||||
spl := strings.Split(addr, "@")
|
spl := strings.Split(addrWithoutProtocol, "@")
|
||||||
if len(spl) == 2 {
|
if len(spl) == 2 {
|
||||||
idStr := spl[0]
|
idStr := spl[0]
|
||||||
idBytes, err := hex.DecodeString(idStr)
|
idBytes, err := hex.DecodeString(idStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, cmn.ErrorWrap(err, fmt.Sprintf("Address (%s) contains invalid ID", addr))
|
return nil, cmn.ErrorWrap(err, fmt.Sprintf("Address (%s) contains invalid ID", addrWithoutProtocol))
|
||||||
}
|
}
|
||||||
if len(idBytes) != IDByteLength {
|
if len(idBytes) != IDByteLength {
|
||||||
return nil, fmt.Errorf("Address (%s) contains ID of invalid length (%d). Should be %d hex-encoded bytes",
|
return nil, fmt.Errorf("Address (%s) contains ID of invalid length (%d). Should be %d hex-encoded bytes",
|
||||||
addr, len(idBytes), IDByteLength)
|
addrWithoutProtocol, len(idBytes), IDByteLength)
|
||||||
}
|
|
||||||
id, addr = ID(idStr), spl[1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
host, portStr, err := net.SplitHostPort(addr)
|
id, addrWithoutProtocol = ID(idStr), spl[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
host, portStr, err := net.SplitHostPort(addrWithoutProtocol)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -119,11 +131,10 @@ func NewNetAddressStrings(addrs []string) ([]*NetAddress, []error) {
|
|||||||
// NewNetAddressIPPort returns a new NetAddress using the provided IP
|
// NewNetAddressIPPort returns a new NetAddress using the provided IP
|
||||||
// and port number.
|
// and port number.
|
||||||
func NewNetAddressIPPort(ip net.IP, port uint16) *NetAddress {
|
func NewNetAddressIPPort(ip net.IP, port uint16) *NetAddress {
|
||||||
na := &NetAddress{
|
return &NetAddress{
|
||||||
IP: ip,
|
IP: ip,
|
||||||
Port: port,
|
Port: port,
|
||||||
}
|
}
|
||||||
return na
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals reports whether na and other are the same addresses,
|
// Equals reports whether na and other are the same addresses,
|
||||||
@@ -293,7 +304,7 @@ func (na *NetAddress) RFC6145() bool { return rfc6145.Contains(na.IP) }
|
|||||||
func removeProtocolIfDefined(addr string) string {
|
func removeProtocolIfDefined(addr string) string {
|
||||||
if strings.Contains(addr, "://") {
|
if strings.Contains(addr, "://") {
|
||||||
return strings.Split(addr, "://")[1]
|
return strings.Split(addr, "://")[1]
|
||||||
} else {
|
|
||||||
return addr
|
|
||||||
}
|
}
|
||||||
|
return addr
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -9,20 +9,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestNewNetAddress(t *testing.T) {
|
func TestNewNetAddress(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
|
||||||
|
|
||||||
tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8080")
|
tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8080")
|
||||||
require.Nil(err)
|
require.Nil(t, err)
|
||||||
addr := NewNetAddress("", tcpAddr)
|
addr := NewNetAddress("", tcpAddr)
|
||||||
|
|
||||||
assert.Equal("127.0.0.1:8080", addr.String())
|
assert.Equal(t, "127.0.0.1:8080", addr.String())
|
||||||
|
|
||||||
assert.NotPanics(func() {
|
assert.NotPanics(t, func() {
|
||||||
NewNetAddress("", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8000})
|
NewNetAddress("", &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8000})
|
||||||
}, "Calling NewNetAddress with UDPAddr should not panic in testing")
|
}, "Calling NewNetAddress with UDPAddr should not panic in testing")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewNetAddressString(t *testing.T) {
|
func TestNewNetAddressStringWithOptionalID(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
addr string
|
addr string
|
||||||
expected string
|
expected string
|
||||||
@@ -57,6 +55,28 @@ func TestNewNetAddressString(t *testing.T) {
|
|||||||
{" @ ", "", false},
|
{" @ ", "", false},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
addr, err := NewNetAddressStringWithOptionalID(tc.addr)
|
||||||
|
if tc.correct {
|
||||||
|
if assert.Nil(t, err, tc.addr) {
|
||||||
|
assert.Equal(t, tc.expected, addr.String())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert.NotNil(t, err, tc.addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewNetAddressString(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
addr string
|
||||||
|
expected string
|
||||||
|
correct bool
|
||||||
|
}{
|
||||||
|
{"127.0.0.1:8080", "127.0.0.1:8080", false},
|
||||||
|
{"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", true},
|
||||||
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
addr, err := NewNetAddressString(tc.addr)
|
addr, err := NewNetAddressString(tc.addr)
|
||||||
if tc.correct {
|
if tc.correct {
|
||||||
@@ -70,23 +90,22 @@ func TestNewNetAddressString(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNewNetAddressStrings(t *testing.T) {
|
func TestNewNetAddressStrings(t *testing.T) {
|
||||||
addrs, errs := NewNetAddressStrings([]string{"127.0.0.1:8080", "127.0.0.2:8080"})
|
addrs, errs := NewNetAddressStrings([]string{
|
||||||
assert.Len(t, errs, 0)
|
"127.0.0.1:8080",
|
||||||
|
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
|
||||||
|
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeed@127.0.0.2:8080"})
|
||||||
|
assert.Len(t, errs, 1)
|
||||||
assert.Equal(t, 2, len(addrs))
|
assert.Equal(t, 2, len(addrs))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewNetAddressIPPort(t *testing.T) {
|
func TestNewNetAddressIPPort(t *testing.T) {
|
||||||
assert := assert.New(t)
|
|
||||||
addr := NewNetAddressIPPort(net.ParseIP("127.0.0.1"), 8080)
|
addr := NewNetAddressIPPort(net.ParseIP("127.0.0.1"), 8080)
|
||||||
|
assert.Equal(t, "127.0.0.1:8080", addr.String())
|
||||||
assert.Equal("127.0.0.1:8080", addr.String())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNetAddressProperties(t *testing.T) {
|
func TestNetAddressProperties(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
|
||||||
|
|
||||||
// TODO add more test cases
|
// TODO add more test cases
|
||||||
tests := []struct {
|
testCases := []struct {
|
||||||
addr string
|
addr string
|
||||||
valid bool
|
valid bool
|
||||||
local bool
|
local bool
|
||||||
@@ -96,21 +115,19 @@ func TestNetAddressProperties(t *testing.T) {
|
|||||||
{"ya.ru:80", true, false, true},
|
{"ya.ru:80", true, false, true},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, t := range tests {
|
for _, tc := range testCases {
|
||||||
addr, err := NewNetAddressString(t.addr)
|
addr, err := NewNetAddressStringWithOptionalID(tc.addr)
|
||||||
require.Nil(err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t.valid, addr.Valid())
|
assert.Equal(t, tc.valid, addr.Valid())
|
||||||
assert.Equal(t.local, addr.Local())
|
assert.Equal(t, tc.local, addr.Local())
|
||||||
assert.Equal(t.routable, addr.Routable())
|
assert.Equal(t, tc.routable, addr.Routable())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNetAddressReachabilityTo(t *testing.T) {
|
func TestNetAddressReachabilityTo(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
|
||||||
|
|
||||||
// TODO add more test cases
|
// TODO add more test cases
|
||||||
tests := []struct {
|
testCases := []struct {
|
||||||
addr string
|
addr string
|
||||||
other string
|
other string
|
||||||
reachability int
|
reachability int
|
||||||
@@ -119,13 +136,13 @@ func TestNetAddressReachabilityTo(t *testing.T) {
|
|||||||
{"ya.ru:80", "127.0.0.1:8080", 1},
|
{"ya.ru:80", "127.0.0.1:8080", 1},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, t := range tests {
|
for _, tc := range testCases {
|
||||||
addr, err := NewNetAddressString(t.addr)
|
addr, err := NewNetAddressStringWithOptionalID(tc.addr)
|
||||||
require.Nil(err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
other, err := NewNetAddressString(t.other)
|
other, err := NewNetAddressStringWithOptionalID(tc.other)
|
||||||
require.Nil(err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
assert.Equal(t.reachability, addr.ReachabilityTo(other))
|
assert.Equal(t, tc.reachability, addr.ReachabilityTo(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,8 +3,6 @@ package p2p
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -20,7 +18,7 @@ func MaxNodeInfoSize() int {
|
|||||||
// between two peers during the Tendermint P2P handshake.
|
// between two peers during the Tendermint P2P handshake.
|
||||||
type NodeInfo struct {
|
type NodeInfo struct {
|
||||||
// Authenticate
|
// Authenticate
|
||||||
PubKey crypto.PubKey `json:"pub_key"` // authenticated pubkey
|
ID ID `json:"id"` // authenticated identifier
|
||||||
ListenAddr string `json:"listen_addr"` // accepting incoming
|
ListenAddr string `json:"listen_addr"` // accepting incoming
|
||||||
|
|
||||||
// Check compatibility
|
// Check compatibility
|
||||||
@@ -107,18 +105,12 @@ OUTER_LOOP:
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (info NodeInfo) ID() ID {
|
|
||||||
return PubKeyToID(info.PubKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetAddress returns a NetAddress derived from the NodeInfo -
|
// NetAddress returns a NetAddress derived from the NodeInfo -
|
||||||
// it includes the authenticated peer ID and the self-reported
|
// it includes the authenticated peer ID and the self-reported
|
||||||
// ListenAddr. Note that the ListenAddr is not authenticated and
|
// ListenAddr. Note that the ListenAddr is not authenticated and
|
||||||
// may not match that address actually dialed if its an outbound peer.
|
// may not match that address actually dialed if its an outbound peer.
|
||||||
func (info NodeInfo) NetAddress() *NetAddress {
|
func (info NodeInfo) NetAddress() *NetAddress {
|
||||||
id := PubKeyToID(info.PubKey)
|
netAddr, err := NewNetAddressString(IDAddressString(info.ID, info.ListenAddr))
|
||||||
addr := info.ListenAddr
|
|
||||||
netAddr, err := NewNetAddressString(IDAddressString(id, addr))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // everything should be well formed by now
|
panic(err) // everything should be well formed by now
|
||||||
}
|
}
|
||||||
@@ -126,7 +118,8 @@ func (info NodeInfo) NetAddress() *NetAddress {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (info NodeInfo) String() string {
|
func (info NodeInfo) String() string {
|
||||||
return fmt.Sprintf("NodeInfo{pk: %v, moniker: %v, network: %v [listen %v], version: %v (%v)}", info.PubKey, info.Moniker, info.Network, info.ListenAddr, info.Version, info.Other)
|
return fmt.Sprintf("NodeInfo{id: %v, moniker: %v, network: %v [listen %v], version: %v (%v)}",
|
||||||
|
info.ID, info.Moniker, info.Network, info.ListenAddr, info.Version, info.Other)
|
||||||
}
|
}
|
||||||
|
|
||||||
func splitVersion(version string) (string, string, string, error) {
|
func splitVersion(version string) (string, string, string, error) {
|
||||||
|
@@ -202,7 +202,7 @@ func (p *peer) OnStop() {
|
|||||||
|
|
||||||
// ID returns the peer's ID - the hex encoded hash of its pubkey.
|
// ID returns the peer's ID - the hex encoded hash of its pubkey.
|
||||||
func (p *peer) ID() ID {
|
func (p *peer) ID() ID {
|
||||||
return p.nodeInfo.ID()
|
return p.nodeInfo.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsOutbound returns true if the connection is outbound, false otherwise.
|
// IsOutbound returns true if the connection is outbound, false otherwise.
|
||||||
@@ -351,7 +351,9 @@ func createMConnection(conn net.Conn, p *peer, reactorsByCh map[byte]Reactor, ch
|
|||||||
onReceive := func(chID byte, msgBytes []byte) {
|
onReceive := func(chID byte, msgBytes []byte) {
|
||||||
reactor := reactorsByCh[chID]
|
reactor := reactorsByCh[chID]
|
||||||
if reactor == nil {
|
if reactor == nil {
|
||||||
onPeerError(p, fmt.Errorf("Unknown channel %X", chID))
|
// Note that its ok to panic here as it's caught in the conn._recover,
|
||||||
|
// which does onPeerError.
|
||||||
|
panic(cmn.Fmt("Unknown channel %X", chID))
|
||||||
}
|
}
|
||||||
reactor.Receive(chID, p, msgBytes)
|
reactor.Receive(chID, p, msgBytes)
|
||||||
}
|
}
|
||||||
|
@@ -68,9 +68,8 @@ func (ps *PeerSet) Get(peerKey ID) Peer {
|
|||||||
item, ok := ps.lookup[peerKey]
|
item, ok := ps.lookup[peerKey]
|
||||||
if ok {
|
if ok {
|
||||||
return item.peer
|
return item.peer
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove discards peer by its Key, if the peer was previously memoized.
|
// Remove discards peer by its Key, if the peer was previously memoized.
|
||||||
|
@@ -13,11 +13,11 @@ import (
|
|||||||
|
|
||||||
// Returns an empty kvstore peer
|
// Returns an empty kvstore peer
|
||||||
func randPeer() *peer {
|
func randPeer() *peer {
|
||||||
pubKey := crypto.GenPrivKeyEd25519().PubKey()
|
nodeKey := NodeKey{PrivKey: crypto.GenPrivKeyEd25519()}
|
||||||
return &peer{
|
return &peer{
|
||||||
nodeInfo: NodeInfo{
|
nodeInfo: NodeInfo{
|
||||||
|
ID: nodeKey.ID(),
|
||||||
ListenAddr: cmn.Fmt("%v.%v.%v.%v:46656", rand.Int()%256, rand.Int()%256, rand.Int()%256, rand.Int()%256),
|
ListenAddr: cmn.Fmt("%v.%v.%v.%v:46656", rand.Int()%256, rand.Int()%256, rand.Int()%256, rand.Int()%256),
|
||||||
PubKey: pubKey,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,7 +115,7 @@ func TestPeerSetAddDuplicate(t *testing.T) {
|
|||||||
errsTally := make(map[error]int)
|
errsTally := make(map[error]int)
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
err := <-errsChan
|
err := <-errsChan
|
||||||
errsTally[err] += 1
|
errsTally[err]++
|
||||||
}
|
}
|
||||||
|
|
||||||
// Our next procedure is to ensure that only one addition
|
// Our next procedure is to ensure that only one addition
|
||||||
|
@@ -95,7 +95,7 @@ func createOutboundPeerAndPerformHandshake(addr *NetAddress, config *PeerConfig)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
nodeInfo, err := pc.HandshakeTimeout(NodeInfo{
|
nodeInfo, err := pc.HandshakeTimeout(NodeInfo{
|
||||||
PubKey: pk.PubKey(),
|
ID: addr.ID,
|
||||||
Moniker: "host_peer",
|
Moniker: "host_peer",
|
||||||
Network: "testing",
|
Network: "testing",
|
||||||
Version: "123.123.123",
|
Version: "123.123.123",
|
||||||
@@ -140,6 +140,8 @@ func (p *remotePeer) Stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *remotePeer) accept(l net.Listener) {
|
func (p *remotePeer) accept(l net.Listener) {
|
||||||
|
conns := []net.Conn{}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
conn, err := l.Accept()
|
conn, err := l.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -150,7 +152,7 @@ func (p *remotePeer) accept(l net.Listener) {
|
|||||||
golog.Fatalf("Failed to create a peer: %+v", err)
|
golog.Fatalf("Failed to create a peer: %+v", err)
|
||||||
}
|
}
|
||||||
_, err = pc.HandshakeTimeout(NodeInfo{
|
_, err = pc.HandshakeTimeout(NodeInfo{
|
||||||
PubKey: p.PrivKey.PubKey(),
|
ID: p.Addr().ID,
|
||||||
Moniker: "remote_peer",
|
Moniker: "remote_peer",
|
||||||
Network: "testing",
|
Network: "testing",
|
||||||
Version: "123.123.123",
|
Version: "123.123.123",
|
||||||
@@ -160,11 +162,16 @@ func (p *remotePeer) accept(l net.Listener) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
golog.Fatalf("Failed to perform handshake: %+v", err)
|
golog.Fatalf("Failed to perform handshake: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conns = append(conns, conn)
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-p.quit:
|
case <-p.quit:
|
||||||
|
for _, conn := range conns {
|
||||||
if err := conn.Close(); err != nil {
|
if err := conn.Close(); err != nil {
|
||||||
golog.Fatal(err)
|
golog.Fatal(err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,6 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -33,24 +32,33 @@ type AddrBook interface {
|
|||||||
|
|
||||||
// Add our own addresses so we don't later add ourselves
|
// Add our own addresses so we don't later add ourselves
|
||||||
AddOurAddress(*p2p.NetAddress)
|
AddOurAddress(*p2p.NetAddress)
|
||||||
|
// Check if it is our address
|
||||||
|
OurAddress(*p2p.NetAddress) bool
|
||||||
|
|
||||||
// Add and remove an address
|
// Add and remove an address
|
||||||
AddAddress(addr *p2p.NetAddress, src *p2p.NetAddress) error
|
AddAddress(addr *p2p.NetAddress, src *p2p.NetAddress) error
|
||||||
RemoveAddress(addr *p2p.NetAddress)
|
RemoveAddress(*p2p.NetAddress)
|
||||||
|
|
||||||
|
// Check if the address is in the book
|
||||||
|
HasAddress(*p2p.NetAddress) bool
|
||||||
|
|
||||||
// Do we need more peers?
|
// Do we need more peers?
|
||||||
NeedMoreAddrs() bool
|
NeedMoreAddrs() bool
|
||||||
|
|
||||||
// Pick an address to dial
|
// Pick an address to dial
|
||||||
PickAddress(newBias int) *p2p.NetAddress
|
PickAddress(biasTowardsNewAddrs int) *p2p.NetAddress
|
||||||
|
|
||||||
// Mark address
|
// Mark address
|
||||||
MarkGood(*p2p.NetAddress)
|
MarkGood(*p2p.NetAddress)
|
||||||
MarkAttempt(*p2p.NetAddress)
|
MarkAttempt(*p2p.NetAddress)
|
||||||
MarkBad(*p2p.NetAddress)
|
MarkBad(*p2p.NetAddress)
|
||||||
|
|
||||||
|
IsGood(*p2p.NetAddress) bool
|
||||||
|
|
||||||
// Send a selection of addresses to peers
|
// Send a selection of addresses to peers
|
||||||
GetSelection() []*p2p.NetAddress
|
GetSelection() []*p2p.NetAddress
|
||||||
|
// Send a selection of addresses with bias
|
||||||
|
GetSelectionWithBias(biasTowardsNewAddrs int) []*p2p.NetAddress
|
||||||
|
|
||||||
// TODO: remove
|
// TODO: remove
|
||||||
ListOfKnownAddresses() []*knownAddress
|
ListOfKnownAddresses() []*knownAddress
|
||||||
@@ -73,8 +81,8 @@ type addrBook struct {
|
|||||||
|
|
||||||
// accessed concurrently
|
// accessed concurrently
|
||||||
mtx sync.Mutex
|
mtx sync.Mutex
|
||||||
rand *rand.Rand
|
rand *cmn.Rand
|
||||||
ourAddrs map[string]*p2p.NetAddress
|
ourAddrs map[string]struct{}
|
||||||
addrLookup map[p2p.ID]*knownAddress // new & old
|
addrLookup map[p2p.ID]*knownAddress // new & old
|
||||||
bucketsOld []map[string]*knownAddress
|
bucketsOld []map[string]*knownAddress
|
||||||
bucketsNew []map[string]*knownAddress
|
bucketsNew []map[string]*knownAddress
|
||||||
@@ -88,8 +96,8 @@ type addrBook struct {
|
|||||||
// Use Start to begin processing asynchronous address updates.
|
// Use Start to begin processing asynchronous address updates.
|
||||||
func NewAddrBook(filePath string, routabilityStrict bool) *addrBook {
|
func NewAddrBook(filePath string, routabilityStrict bool) *addrBook {
|
||||||
am := &addrBook{
|
am := &addrBook{
|
||||||
rand: rand.New(rand.NewSource(time.Now().UnixNano())), // TODO: seed from outside
|
rand: cmn.NewRand(),
|
||||||
ourAddrs: make(map[string]*p2p.NetAddress),
|
ourAddrs: make(map[string]struct{}),
|
||||||
addrLookup: make(map[p2p.ID]*knownAddress),
|
addrLookup: make(map[p2p.ID]*knownAddress),
|
||||||
filePath: filePath,
|
filePath: filePath,
|
||||||
routabilityStrict: routabilityStrict,
|
routabilityStrict: routabilityStrict,
|
||||||
@@ -150,7 +158,15 @@ func (a *addrBook) AddOurAddress(addr *p2p.NetAddress) {
|
|||||||
a.mtx.Lock()
|
a.mtx.Lock()
|
||||||
defer a.mtx.Unlock()
|
defer a.mtx.Unlock()
|
||||||
a.Logger.Info("Add our address to book", "addr", addr)
|
a.Logger.Info("Add our address to book", "addr", addr)
|
||||||
a.ourAddrs[addr.String()] = addr
|
a.ourAddrs[addr.String()] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OurAddress returns true if it is our address.
|
||||||
|
func (a *addrBook) OurAddress(addr *p2p.NetAddress) bool {
|
||||||
|
a.mtx.Lock()
|
||||||
|
_, ok := a.ourAddrs[addr.String()]
|
||||||
|
a.mtx.Unlock()
|
||||||
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddAddress implements AddrBook - adds the given address as received from the given source.
|
// AddAddress implements AddrBook - adds the given address as received from the given source.
|
||||||
@@ -173,6 +189,22 @@ func (a *addrBook) RemoveAddress(addr *p2p.NetAddress) {
|
|||||||
a.removeFromAllBuckets(ka)
|
a.removeFromAllBuckets(ka)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsGood returns true if peer was ever marked as good and haven't
|
||||||
|
// done anything wrong since then.
|
||||||
|
func (a *addrBook) IsGood(addr *p2p.NetAddress) bool {
|
||||||
|
a.mtx.Lock()
|
||||||
|
defer a.mtx.Unlock()
|
||||||
|
return a.addrLookup[addr.ID].isOld()
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasAddress returns true if the address is in the book.
|
||||||
|
func (a *addrBook) HasAddress(addr *p2p.NetAddress) bool {
|
||||||
|
a.mtx.Lock()
|
||||||
|
defer a.mtx.Unlock()
|
||||||
|
ka := a.addrLookup[addr.ID]
|
||||||
|
return ka != nil
|
||||||
|
}
|
||||||
|
|
||||||
// NeedMoreAddrs implements AddrBook - returns true if there are not have enough addresses in the book.
|
// NeedMoreAddrs implements AddrBook - returns true if there are not have enough addresses in the book.
|
||||||
func (a *addrBook) NeedMoreAddrs() bool {
|
func (a *addrBook) NeedMoreAddrs() bool {
|
||||||
return a.Size() < needAddressThreshold
|
return a.Size() < needAddressThreshold
|
||||||
@@ -180,27 +212,27 @@ func (a *addrBook) NeedMoreAddrs() bool {
|
|||||||
|
|
||||||
// PickAddress implements AddrBook. It picks an address to connect to.
|
// PickAddress implements AddrBook. It picks an address to connect to.
|
||||||
// The address is picked randomly from an old or new bucket according
|
// The address is picked randomly from an old or new bucket according
|
||||||
// to the newBias argument, which must be between [0, 100] (or else is truncated to that range)
|
// to the biasTowardsNewAddrs argument, which must be between [0, 100] (or else is truncated to that range)
|
||||||
// and determines how biased we are to pick an address from a new bucket.
|
// and determines how biased we are to pick an address from a new bucket.
|
||||||
// PickAddress returns nil if the AddrBook is empty or if we try to pick
|
// PickAddress returns nil if the AddrBook is empty or if we try to pick
|
||||||
// from an empty bucket.
|
// from an empty bucket.
|
||||||
func (a *addrBook) PickAddress(newBias int) *p2p.NetAddress {
|
func (a *addrBook) PickAddress(biasTowardsNewAddrs int) *p2p.NetAddress {
|
||||||
a.mtx.Lock()
|
a.mtx.Lock()
|
||||||
defer a.mtx.Unlock()
|
defer a.mtx.Unlock()
|
||||||
|
|
||||||
if a.size() == 0 {
|
if a.size() == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if newBias > 100 {
|
if biasTowardsNewAddrs > 100 {
|
||||||
newBias = 100
|
biasTowardsNewAddrs = 100
|
||||||
}
|
}
|
||||||
if newBias < 0 {
|
if biasTowardsNewAddrs < 0 {
|
||||||
newBias = 0
|
biasTowardsNewAddrs = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bias between new and old addresses.
|
// Bias between new and old addresses.
|
||||||
oldCorrelation := math.Sqrt(float64(a.nOld)) * (100.0 - float64(newBias))
|
oldCorrelation := math.Sqrt(float64(a.nOld)) * (100.0 - float64(biasTowardsNewAddrs))
|
||||||
newCorrelation := math.Sqrt(float64(a.nNew)) * float64(newBias)
|
newCorrelation := math.Sqrt(float64(a.nNew)) * float64(biasTowardsNewAddrs)
|
||||||
|
|
||||||
// pick a random peer from a random bucket
|
// pick a random peer from a random bucket
|
||||||
var bucket map[string]*knownAddress
|
var bucket map[string]*knownAddress
|
||||||
@@ -287,7 +319,7 @@ func (a *addrBook) GetSelection() []*p2p.NetAddress {
|
|||||||
// XXX: What's the point of this if we already loop randomly through addrLookup ?
|
// XXX: What's the point of this if we already loop randomly through addrLookup ?
|
||||||
for i := 0; i < numAddresses; i++ {
|
for i := 0; i < numAddresses; i++ {
|
||||||
// pick a number between current index and the end
|
// pick a number between current index and the end
|
||||||
j := rand.Intn(len(allAddr)-i) + i
|
j := cmn.RandIntn(len(allAddr)-i) + i
|
||||||
allAddr[i], allAddr[j] = allAddr[j], allAddr[i]
|
allAddr[i], allAddr[j] = allAddr[j], allAddr[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,6 +327,100 @@ func (a *addrBook) GetSelection() []*p2p.NetAddress {
|
|||||||
return allAddr[:numAddresses]
|
return allAddr[:numAddresses]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSelectionWithBias implements AddrBook.
|
||||||
|
// It randomly selects some addresses (old & new). Suitable for peer-exchange protocols.
|
||||||
|
//
|
||||||
|
// Each address is picked randomly from an old or new bucket according to the
|
||||||
|
// biasTowardsNewAddrs argument, which must be between [0, 100] (or else is truncated to
|
||||||
|
// that range) and determines how biased we are to pick an address from a new
|
||||||
|
// bucket.
|
||||||
|
func (a *addrBook) GetSelectionWithBias(biasTowardsNewAddrs int) []*p2p.NetAddress {
|
||||||
|
a.mtx.Lock()
|
||||||
|
defer a.mtx.Unlock()
|
||||||
|
|
||||||
|
if a.size() == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if biasTowardsNewAddrs > 100 {
|
||||||
|
biasTowardsNewAddrs = 100
|
||||||
|
}
|
||||||
|
if biasTowardsNewAddrs < 0 {
|
||||||
|
biasTowardsNewAddrs = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
numAddresses := cmn.MaxInt(
|
||||||
|
cmn.MinInt(minGetSelection, a.size()),
|
||||||
|
a.size()*getSelectionPercent/100)
|
||||||
|
numAddresses = cmn.MinInt(maxGetSelection, numAddresses)
|
||||||
|
|
||||||
|
selection := make([]*p2p.NetAddress, numAddresses)
|
||||||
|
|
||||||
|
oldBucketToAddrsMap := make(map[int]map[string]struct{})
|
||||||
|
var oldIndex int
|
||||||
|
newBucketToAddrsMap := make(map[int]map[string]struct{})
|
||||||
|
var newIndex int
|
||||||
|
|
||||||
|
selectionIndex := 0
|
||||||
|
ADDRS_LOOP:
|
||||||
|
for selectionIndex < numAddresses {
|
||||||
|
pickFromOldBucket := int((float64(selectionIndex)/float64(numAddresses))*100) >= biasTowardsNewAddrs
|
||||||
|
pickFromOldBucket = (pickFromOldBucket && a.nOld > 0) || a.nNew == 0
|
||||||
|
bucket := make(map[string]*knownAddress)
|
||||||
|
|
||||||
|
// loop until we pick a random non-empty bucket
|
||||||
|
for len(bucket) == 0 {
|
||||||
|
if pickFromOldBucket {
|
||||||
|
oldIndex = a.rand.Intn(len(a.bucketsOld))
|
||||||
|
bucket = a.bucketsOld[oldIndex]
|
||||||
|
} else {
|
||||||
|
newIndex = a.rand.Intn(len(a.bucketsNew))
|
||||||
|
bucket = a.bucketsNew[newIndex]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pick a random index
|
||||||
|
randIndex := a.rand.Intn(len(bucket))
|
||||||
|
|
||||||
|
// loop over the map to return that index
|
||||||
|
var selectedAddr *p2p.NetAddress
|
||||||
|
for _, ka := range bucket {
|
||||||
|
if randIndex == 0 {
|
||||||
|
selectedAddr = ka.Addr
|
||||||
|
break
|
||||||
|
}
|
||||||
|
randIndex--
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have selected the address before, restart the loop
|
||||||
|
// otherwise, record it and continue
|
||||||
|
if pickFromOldBucket {
|
||||||
|
if addrsMap, ok := oldBucketToAddrsMap[oldIndex]; ok {
|
||||||
|
if _, ok = addrsMap[selectedAddr.String()]; ok {
|
||||||
|
continue ADDRS_LOOP
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
oldBucketToAddrsMap[oldIndex] = make(map[string]struct{})
|
||||||
|
}
|
||||||
|
oldBucketToAddrsMap[oldIndex][selectedAddr.String()] = struct{}{}
|
||||||
|
} else {
|
||||||
|
if addrsMap, ok := newBucketToAddrsMap[newIndex]; ok {
|
||||||
|
if _, ok = addrsMap[selectedAddr.String()]; ok {
|
||||||
|
continue ADDRS_LOOP
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newBucketToAddrsMap[newIndex] = make(map[string]struct{})
|
||||||
|
}
|
||||||
|
newBucketToAddrsMap[newIndex][selectedAddr.String()] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
selection[selectionIndex] = selectedAddr
|
||||||
|
selectionIndex++
|
||||||
|
}
|
||||||
|
|
||||||
|
return selection
|
||||||
|
}
|
||||||
|
|
||||||
// ListOfKnownAddresses returns the new and old addresses.
|
// ListOfKnownAddresses returns the new and old addresses.
|
||||||
func (a *addrBook) ListOfKnownAddresses() []*knownAddress {
|
func (a *addrBook) ListOfKnownAddresses() []*knownAddress {
|
||||||
a.mtx.Lock()
|
a.mtx.Lock()
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user