mirror of
https://github.com/fluencelabs/tendermint
synced 2025-07-16 12:51:59 +00:00
Compare commits
97 Commits
v0.27.0-rc
...
release/v0
Author | SHA1 | Date | |
---|---|---|---|
|
a97d6995c9 | ||
|
d9d4f3e629 | ||
|
7a8aeff4b0 | ||
|
de5a6010f0 | ||
|
da95f4aa6d | ||
|
4f8769175e | ||
|
40c887baf7 | ||
|
d3e8889411 | ||
|
d17969e378 | ||
|
07263298bd | ||
|
5a2e69df81 | ||
|
f5f1416a14 | ||
|
4d36647eea | ||
|
8fd8f800d0 | ||
|
87991059aa | ||
|
c69dbb25ce | ||
|
bc8874020f | ||
|
55d7238708 | ||
|
4a037f9fe6 | ||
|
aa40cfcbb9 | ||
|
6d6d103f15 | ||
|
239ebe2076 | ||
|
0cba0e11b5 | ||
|
d4e6720541 | ||
|
dcb8f88525 | ||
|
a2a62c9be6 | ||
|
3191ee8bad | ||
|
308b7e3bbe | ||
|
73ea5effe5 | ||
|
d1afa0ed6c | ||
|
ca00cd6a78 | ||
|
4daca1a634 | ||
|
bc00a032c1 | ||
|
5f4d8e031e | ||
|
7b2c4bb493 | ||
|
5f93220c61 | ||
|
ec53ce359b | ||
|
1f68318875 | ||
|
1ccc0918f5 | ||
|
fc031d980b | ||
|
1895cde590 | ||
|
be00cd1add | ||
|
a6011c007d | ||
|
ef94a322b8 | ||
|
7f607d0ce2 | ||
|
81c51cd4fc | ||
|
51094f9417 | ||
|
7644d27307 | ||
|
764cfe33aa | ||
|
616c3a4bae | ||
|
04e97f599a | ||
|
56a4fb4d72 | ||
|
49017a5787 | ||
|
6a80412a01 | ||
|
2348f38927 | ||
|
41e2eeee9c | ||
|
a88e283a9d | ||
|
1e1ca15bcc | ||
|
c6604b5a9b | ||
|
c510f823e7 | ||
|
daddebac29 | ||
|
30f346fe44 | ||
|
4d8f29f79c | ||
|
2182f6a702 | ||
|
a06912b579 | ||
|
0ff715125b | ||
|
385977d5e8 | ||
|
0138530df2 | ||
|
0533c73a50 | ||
|
1beb45511c | ||
|
4a568fcedb | ||
|
b3141d7d02 | ||
|
9a6dd96cba | ||
|
9fa959619a | ||
|
1f09818770 | ||
|
e4806f980b | ||
|
b53a2712df | ||
|
a75dab492c | ||
|
7c9e767e1f | ||
|
f82a8ff73a | ||
|
ae275d791e | ||
|
f5cca9f121 | ||
|
3fbe9f235a | ||
|
f7e463f6d3 | ||
|
bc2a9b20c0 | ||
|
9e075d8dd5 | ||
|
8003786c9a | ||
|
2594cec116 | ||
|
df32ea4be5 | ||
|
f69e2c6d6c | ||
|
d5d0d2bd77 | ||
|
41eaf0e31d | ||
|
68b467886a | ||
|
2f64717bb5 | ||
|
c4a1cfc5c2 | ||
|
0f96bea41d | ||
|
9c236ffd6c |
@@ -3,10 +3,17 @@ version: 2
|
|||||||
defaults: &defaults
|
defaults: &defaults
|
||||||
working_directory: /go/src/github.com/tendermint/tendermint
|
working_directory: /go/src/github.com/tendermint/tendermint
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.10.3
|
- image: circleci/golang:1.11.4
|
||||||
environment:
|
environment:
|
||||||
GOBIN: /tmp/workspace/bin
|
GOBIN: /tmp/workspace/bin
|
||||||
|
|
||||||
|
docs_update_config: &docs_update_config
|
||||||
|
working_directory: ~/repo
|
||||||
|
docker:
|
||||||
|
- image: tendermint/docs_deployment
|
||||||
|
environment:
|
||||||
|
AWS_REGION: us-east-1
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
setup_dependencies:
|
setup_dependencies:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
@@ -339,10 +346,25 @@ jobs:
|
|||||||
name: upload
|
name: upload
|
||||||
command: bash .circleci/codecov.sh -f coverage.txt
|
command: bash .circleci/codecov.sh -f coverage.txt
|
||||||
|
|
||||||
|
deploy_docs:
|
||||||
|
<<: *docs_update_config
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: Trigger website build
|
||||||
|
command: |
|
||||||
|
chamber exec tendermint -- start_website_build
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
test-suite:
|
test-suite:
|
||||||
jobs:
|
jobs:
|
||||||
|
- deploy_docs:
|
||||||
|
filters:
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
- setup_dependencies
|
- setup_dependencies
|
||||||
- lint:
|
- lint:
|
||||||
requires:
|
requires:
|
||||||
|
208
CHANGELOG.md
208
CHANGELOG.md
@@ -1,5 +1,203 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v0.29.0
|
||||||
|
|
||||||
|
*January 21, 2019*
|
||||||
|
|
||||||
|
Special thanks to external contributors on this release:
|
||||||
|
@bradyjoestar, @kunaldhariwal, @gauthamzz, @hrharder
|
||||||
|
|
||||||
|
This release is primarily about making some breaking changes to
|
||||||
|
the Block protocol version before Cosmos launch, and to fixing more issues
|
||||||
|
in the proposer selection algorithm discovered on Cosmos testnets.
|
||||||
|
|
||||||
|
The Block protocol changes include using a standard Merkle tree format (RFC 6962),
|
||||||
|
fixing some inconsistencies between field orders in Vote and Proposal structs,
|
||||||
|
and constraining the hash of the ConsensusParams to include only a few fields.
|
||||||
|
|
||||||
|
The proposer selection algorithm saw significant progress,
|
||||||
|
including a [formal proof by @cwgoes for the base-case in Idris](https://github.com/cwgoes/tm-proposer-idris)
|
||||||
|
and a [much more detailed specification (still in progress) by
|
||||||
|
@ancazamfir](https://github.com/tendermint/tendermint/pull/3140).
|
||||||
|
|
||||||
|
Fixes to the proposer selection algorithm include normalizing the proposer
|
||||||
|
priorities to mitigate the effects of large changes to the validator set.
|
||||||
|
That said, we just discovered [another bug](https://github.com/tendermint/tendermint/issues/3181),
|
||||||
|
which will be fixed in the next breaking release.
|
||||||
|
|
||||||
|
While we are trying to stabilize the Block protocol to preserve compatibility
|
||||||
|
with old chains, there may be some final changes yet to come before Cosmos
|
||||||
|
launch as we continue to audit and test the software.
|
||||||
|
|
||||||
|
Friendly reminder, we have a [bug bounty
|
||||||
|
program](https://hackerone.com/tendermint).
|
||||||
|
|
||||||
|
### BREAKING CHANGES:
|
||||||
|
|
||||||
|
* CLI/RPC/Config
|
||||||
|
|
||||||
|
* Apps
|
||||||
|
- [state] [\#3049](https://github.com/tendermint/tendermint/issues/3049) Total voting power of the validator set is upper bounded by
|
||||||
|
`MaxInt64 / 8`. Apps must ensure they do not return changes to the validator
|
||||||
|
set that cause this maximum to be exceeded.
|
||||||
|
|
||||||
|
* Go API
|
||||||
|
- [node] [\#3082](https://github.com/tendermint/tendermint/issues/3082) MetricsProvider now requires you to pass a chain ID
|
||||||
|
- [types] [\#2713](https://github.com/tendermint/tendermint/issues/2713) Rename `TxProof.LeafHash` to `TxProof.Leaf`
|
||||||
|
- [crypto/merkle] [\#2713](https://github.com/tendermint/tendermint/issues/2713) `SimpleProof.Verify` takes a `leaf` instead of a
|
||||||
|
`leafHash` and performs the hashing itself
|
||||||
|
|
||||||
|
* Blockchain Protocol
|
||||||
|
* [crypto/merkle] [\#2713](https://github.com/tendermint/tendermint/issues/2713) Merkle trees now match the RFC 6962 specification
|
||||||
|
* [types] [\#3078](https://github.com/tendermint/tendermint/issues/3078) Re-order Timestamp and BlockID in CanonicalVote so it's
|
||||||
|
consistent with CanonicalProposal (BlockID comes
|
||||||
|
first)
|
||||||
|
* [types] [\#3165](https://github.com/tendermint/tendermint/issues/3165) Hash of ConsensusParams only includes BlockSize.MaxBytes and
|
||||||
|
BlockSize.MaxGas
|
||||||
|
|
||||||
|
* P2P Protocol
|
||||||
|
- [consensus] [\#3049](https://github.com/tendermint/tendermint/issues/3049) Normalize priorities to not exceed `2*TotalVotingPower` to mitigate unfair proposer selection
|
||||||
|
heavily preferring earlier joined validators in the case of an early bonded large validator unbonding
|
||||||
|
|
||||||
|
### FEATURES:
|
||||||
|
|
||||||
|
### IMPROVEMENTS:
|
||||||
|
- [rpc] [\#3065](https://github.com/tendermint/tendermint/issues/3065) Return maxPerPage (100), not defaultPerPage (30) if `per_page` is greater than the max 100.
|
||||||
|
- [instrumentation] [\#3082](https://github.com/tendermint/tendermint/issues/3082) Add `chain_id` label for all metrics
|
||||||
|
|
||||||
|
### BUG FIXES:
|
||||||
|
- [crypto] [\#3164](https://github.com/tendermint/tendermint/issues/3164) Update `btcd` fork for rare signRFC6979 bug
|
||||||
|
- [lite] [\#3171](https://github.com/tendermint/tendermint/issues/3171) Fix verifying large validator set changes
|
||||||
|
- [log] [\#3125](https://github.com/tendermint/tendermint/issues/3125) Fix year format
|
||||||
|
- [mempool] [\#3168](https://github.com/tendermint/tendermint/issues/3168) Limit tx size to fit in the max reactor msg size
|
||||||
|
- [scripts] [\#3147](https://github.com/tendermint/tendermint/issues/3147) Fix json2wal for large block parts (@bradyjoestar)
|
||||||
|
|
||||||
|
## v0.28.1
|
||||||
|
|
||||||
|
*January 18th, 2019*
|
||||||
|
|
||||||
|
Special thanks to external contributors on this release:
|
||||||
|
@HaoyangLiu
|
||||||
|
|
||||||
|
Friendly reminder, we have a [bug bounty
|
||||||
|
program](https://hackerone.com/tendermint).
|
||||||
|
|
||||||
|
### BUG FIXES:
|
||||||
|
- [consensus] Fix consensus halt from proposing blocks with too much evidence
|
||||||
|
|
||||||
|
## v0.28.0
|
||||||
|
|
||||||
|
*January 16th, 2019*
|
||||||
|
|
||||||
|
Special thanks to external contributors on this release:
|
||||||
|
@fmauricios, @gianfelipe93, @husio, @needkane, @srmo, @yutianwu
|
||||||
|
|
||||||
|
This release is primarily about upgrades to the `privval` system -
|
||||||
|
separating the `priv_validator.json` into distinct config and data files, and
|
||||||
|
refactoring the socket validator to support reconnections.
|
||||||
|
|
||||||
|
**Note:** Please backup your existing `priv_validator.json` before using this
|
||||||
|
version.
|
||||||
|
|
||||||
|
See [UPGRADING.md](UPGRADING.md) for more details.
|
||||||
|
|
||||||
|
### BREAKING CHANGES:
|
||||||
|
|
||||||
|
* CLI/RPC/Config
|
||||||
|
- [cli] Removed `--proxy_app=dummy` option. Use `kvstore` (`persistent_kvstore`) instead.
|
||||||
|
- [cli] Renamed `--proxy_app=nilapp` to `--proxy_app=noop`.
|
||||||
|
- [config] [\#2992](https://github.com/tendermint/tendermint/issues/2992) `allow_duplicate_ip` is now set to false
|
||||||
|
- [privval] [\#1181](https://github.com/tendermint/tendermint/issues/1181) Split `priv_validator.json` into immutable (`config/priv_validator_key.json`) and mutable (`data/priv_validator_state.json`) parts (@yutianwu)
|
||||||
|
- [privval] [\#2926](https://github.com/tendermint/tendermint/issues/2926) Split up `PubKeyMsg` into `PubKeyRequest` and `PubKeyResponse` to be consistent with other message types
|
||||||
|
- [privval] [\#2923](https://github.com/tendermint/tendermint/issues/2923) Listen for unix socket connections instead of dialing them
|
||||||
|
|
||||||
|
* Apps
|
||||||
|
|
||||||
|
* Go API
|
||||||
|
- [types] [\#2981](https://github.com/tendermint/tendermint/issues/2981) Remove `PrivValidator.GetAddress()`
|
||||||
|
|
||||||
|
* Blockchain Protocol
|
||||||
|
|
||||||
|
* P2P Protocol
|
||||||
|
|
||||||
|
### FEATURES:
|
||||||
|
- [rpc] [\#3052](https://github.com/tendermint/tendermint/issues/3052) Include peer's remote IP in `/net_info`
|
||||||
|
|
||||||
|
### IMPROVEMENTS:
|
||||||
|
- [consensus] [\#3086](https://github.com/tendermint/tendermint/issues/3086) Log peerID on ignored votes (@srmo)
|
||||||
|
- [docs] [\#3061](https://github.com/tendermint/tendermint/issues/3061) Added specification for signing consensus msgs at
|
||||||
|
./docs/spec/consensus/signing.md
|
||||||
|
- [privval] [\#2948](https://github.com/tendermint/tendermint/issues/2948) Memoize pubkey so it's only requested once on startup
|
||||||
|
- [privval] [\#2923](https://github.com/tendermint/tendermint/issues/2923) Retry RemoteSigner connections on error
|
||||||
|
|
||||||
|
### BUG FIXES:
|
||||||
|
|
||||||
|
- [build] [\#3085](https://github.com/tendermint/tendermint/issues/3085) Fix `Version` field in build scripts (@husio)
|
||||||
|
- [crypto/multisig] [\#3102](https://github.com/tendermint/tendermint/issues/3102) Fix multisig keys address length
|
||||||
|
- [crypto/encoding] [\#3101](https://github.com/tendermint/tendermint/issues/3101) Fix `PubKeyMultisigThreshold` unmarshalling into `crypto.PubKey` interface
|
||||||
|
- [p2p/conn] [\#3111](https://github.com/tendermint/tendermint/issues/3111) Make SecretConnection thread safe
|
||||||
|
- [rpc] [\#3053](https://github.com/tendermint/tendermint/issues/3053) Fix internal error in `/tx_search` when results are empty
|
||||||
|
(@gianfelipe93)
|
||||||
|
- [types] [\#2926](https://github.com/tendermint/tendermint/issues/2926) Do not panic if retrieving the privval's public key fails
|
||||||
|
|
||||||
|
## v0.27.4
|
||||||
|
|
||||||
|
*December 21st, 2018*
|
||||||
|
|
||||||
|
### BUG FIXES:
|
||||||
|
|
||||||
|
- [mempool] [\#3036](https://github.com/tendermint/tendermint/issues/3036) Fix
|
||||||
|
LRU cache by popping the least recently used item when the cache is full,
|
||||||
|
not the most recently used one!
|
||||||
|
|
||||||
|
## v0.27.3
|
||||||
|
|
||||||
|
*December 16th, 2018*
|
||||||
|
|
||||||
|
### BREAKING CHANGES:
|
||||||
|
|
||||||
|
* Go API
|
||||||
|
- [dep] [\#3027](https://github.com/tendermint/tendermint/issues/3027) Revert to mainline Go crypto library, eliminating the modified
|
||||||
|
`bcrypt.GenerateFromPassword`
|
||||||
|
|
||||||
|
## v0.27.2
|
||||||
|
|
||||||
|
*December 16th, 2018*
|
||||||
|
|
||||||
|
### IMPROVEMENTS:
|
||||||
|
|
||||||
|
- [node] [\#3025](https://github.com/tendermint/tendermint/issues/3025) Validate NodeInfo addresses on startup.
|
||||||
|
|
||||||
|
### BUG FIXES:
|
||||||
|
|
||||||
|
- [p2p] [\#3025](https://github.com/tendermint/tendermint/pull/3025) Revert to using defers in addrbook. Fixes deadlocks in pex and consensus upon invalid ExternalAddr/ListenAddr configuration.
|
||||||
|
|
||||||
|
## v0.27.1
|
||||||
|
|
||||||
|
*December 15th, 2018*
|
||||||
|
|
||||||
|
Special thanks to external contributors on this release:
|
||||||
|
@danil-lashin, @hleb-albau, @james-ray, @leo-xinwang
|
||||||
|
|
||||||
|
### FEATURES:
|
||||||
|
- [rpc] [\#2964](https://github.com/tendermint/tendermint/issues/2964) Add `UnconfirmedTxs(limit)` and `NumUnconfirmedTxs()` methods to HTTP/Local clients (@danil-lashin)
|
||||||
|
- [docs] [\#3004](https://github.com/tendermint/tendermint/issues/3004) Enable full-text search on docs pages
|
||||||
|
|
||||||
|
### IMPROVEMENTS:
|
||||||
|
- [consensus] [\#2971](https://github.com/tendermint/tendermint/issues/2971) Return error if ValidatorSet is empty after InitChain
|
||||||
|
(@leo-xinwang)
|
||||||
|
- [ci/cd] [\#3005](https://github.com/tendermint/tendermint/issues/3005) Updated CircleCI job to trigger website build when docs are updated
|
||||||
|
- [docs] Various updates
|
||||||
|
|
||||||
|
### BUG FIXES:
|
||||||
|
- [cmd] [\#2983](https://github.com/tendermint/tendermint/issues/2983) `testnet` command always sets `addr_book_strict = false`
|
||||||
|
- [config] [\#2980](https://github.com/tendermint/tendermint/issues/2980) Fix CORS options formatting
|
||||||
|
- [kv indexer] [\#2912](https://github.com/tendermint/tendermint/issues/2912) Don't ignore key when executing CONTAINS
|
||||||
|
- [mempool] [\#2961](https://github.com/tendermint/tendermint/issues/2961) Call `notifyTxsAvailable` if there're txs left after committing a block, but recheck=false
|
||||||
|
- [mempool] [\#2994](https://github.com/tendermint/tendermint/issues/2994) Reject txs with negative GasWanted
|
||||||
|
- [p2p] [\#2990](https://github.com/tendermint/tendermint/issues/2990) Fix a bug where seeds don't disconnect from a peer after 3h
|
||||||
|
- [consensus] [\#3006](https://github.com/tendermint/tendermint/issues/3006) Save state after InitChain only when stateHeight is also 0 (@james-ray)
|
||||||
|
|
||||||
## v0.27.0
|
## v0.27.0
|
||||||
|
|
||||||
*December 5th, 2018*
|
*December 5th, 2018*
|
||||||
@@ -46,17 +244,17 @@ message.
|
|||||||
### IMPROVEMENTS:
|
### IMPROVEMENTS:
|
||||||
|
|
||||||
- [state] [\#2929](https://github.com/tendermint/tendermint/issues/2929) Minor refactor of updateState logic (@danil-lashin)
|
- [state] [\#2929](https://github.com/tendermint/tendermint/issues/2929) Minor refactor of updateState logic (@danil-lashin)
|
||||||
- [node] \#2959 Allow node to start even if software's BlockProtocol is
|
- [node] [\#2959](https://github.com/tendermint/tendermint/issues/2959) Allow node to start even if software's BlockProtocol is
|
||||||
different from state's BlockProtocol
|
different from state's BlockProtocol
|
||||||
- [pex] \#2959 Pex reactor logger uses `module=pex`
|
- [pex] [\#2959](https://github.com/tendermint/tendermint/issues/2959) Pex reactor logger uses `module=pex`
|
||||||
|
|
||||||
### BUG FIXES:
|
### BUG FIXES:
|
||||||
|
|
||||||
- [p2p] \#2968 Panic on transport error rather than continuing to run but not
|
- [p2p] [\#2968](https://github.com/tendermint/tendermint/issues/2968) Panic on transport error rather than continuing to run but not
|
||||||
accept new connections
|
accept new connections
|
||||||
- [p2p] \#2969 Fix mismatch in peer count between `/net_info` and the prometheus
|
- [p2p] [\#2969](https://github.com/tendermint/tendermint/issues/2969) Fix mismatch in peer count between `/net_info` and the prometheus
|
||||||
metrics
|
metrics
|
||||||
- [rpc] \#2408 `/broadcast_tx_commit`: Fix "interface conversion: interface {} in nil, not EventDataTx" panic (could happen if somebody sent a tx using `/broadcast_tx_commit` while Tendermint was being stopped)
|
- [rpc] [\#2408](https://github.com/tendermint/tendermint/issues/2408) `/broadcast_tx_commit`: Fix "interface conversion: interface {} in nil, not EventDataTx" panic (could happen if somebody sent a tx using `/broadcast_tx_commit` while Tendermint was being stopped)
|
||||||
- [state] [\#2785](https://github.com/tendermint/tendermint/issues/2785) Fix accum for new validators to be `-1.125*totalVotingPower`
|
- [state] [\#2785](https://github.com/tendermint/tendermint/issues/2785) Fix accum for new validators to be `-1.125*totalVotingPower`
|
||||||
instead of 0, forcing them to wait before becoming the proposer. Also:
|
instead of 0, forcing them to wait before becoming the proposer. Also:
|
||||||
- do not batch clip
|
- do not batch clip
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
## v0.27.1
|
## v0.30.0
|
||||||
|
|
||||||
*TBD*
|
*TBD*
|
||||||
|
|
||||||
|
@@ -3,7 +3,7 @@ set -e
|
|||||||
|
|
||||||
# Get the tag from the version, or try to figure it out.
|
# Get the tag from the version, or try to figure it out.
|
||||||
if [ -z "$TAG" ]; then
|
if [ -z "$TAG" ]; then
|
||||||
TAG=$(awk -F\" '/Version =/ { print $2; exit }' < ../version/version.go)
|
TAG=$(awk -F\" '/TMCoreSemVer =/ { print $2; exit }' < ../version/version.go)
|
||||||
fi
|
fi
|
||||||
if [ -z "$TAG" ]; then
|
if [ -z "$TAG" ]; then
|
||||||
echo "Please specify a tag."
|
echo "Please specify a tag."
|
||||||
|
@@ -3,7 +3,7 @@ set -e
|
|||||||
|
|
||||||
# Get the tag from the version, or try to figure it out.
|
# Get the tag from the version, or try to figure it out.
|
||||||
if [ -z "$TAG" ]; then
|
if [ -z "$TAG" ]; then
|
||||||
TAG=$(awk -F\" '/Version =/ { print $2; exit }' < ../version/version.go)
|
TAG=$(awk -F\" '/TMCoreSemVer =/ { print $2; exit }' < ../version/version.go)
|
||||||
fi
|
fi
|
||||||
if [ -z "$TAG" ]; then
|
if [ -z "$TAG" ]; then
|
||||||
echo "Please specify a tag."
|
echo "Please specify a tag."
|
||||||
|
10
Gopkg.lock
generated
10
Gopkg.lock
generated
@@ -361,11 +361,12 @@
|
|||||||
revision = "6b91fda63f2e36186f1c9d0e48578defb69c5d43"
|
revision = "6b91fda63f2e36186f1c9d0e48578defb69c5d43"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:605b6546f3f43745695298ec2d342d3e952b6d91cdf9f349bea9315f677d759f"
|
digest = "1:83f5e189eea2baad419a6a410984514266ff690075759c87e9ede596809bd0b8"
|
||||||
name = "github.com/tendermint/btcd"
|
name = "github.com/tendermint/btcd"
|
||||||
packages = ["btcec"]
|
packages = ["btcec"]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df"
|
revision = "80daadac05d1cd29571fccf27002d79667a88b58"
|
||||||
|
version = "v0.1.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:ad9c4c1a4e7875330b1f62906f2830f043a23edb5db997e3a5ac5d3e6eadf80a"
|
digest = "1:ad9c4c1a4e7875330b1f62906f2830f043a23edb5db997e3a5ac5d3e6eadf80a"
|
||||||
@@ -376,7 +377,7 @@
|
|||||||
version = "v0.14.1"
|
version = "v0.14.1"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:72b71e3a29775e5752ed7a8012052a3dee165e27ec18cedddae5288058f09acf"
|
digest = "1:00d2b3e64cdc3fa69aa250dfbe4cc38c4837d4f37e62279be2ae52107ffbbb44"
|
||||||
name = "golang.org/x/crypto"
|
name = "golang.org/x/crypto"
|
||||||
packages = [
|
packages = [
|
||||||
"bcrypt",
|
"bcrypt",
|
||||||
@@ -397,8 +398,7 @@
|
|||||||
"salsa20/salsa",
|
"salsa20/salsa",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "3764759f34a542a3aef74d6b02e35be7ab893bba"
|
revision = "505ab145d0a99da450461ae2c1a9f6cd10d1f447"
|
||||||
source = "github.com/tendermint/crypto"
|
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1"
|
digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1"
|
||||||
|
10
Gopkg.toml
10
Gopkg.toml
@@ -75,14 +75,17 @@
|
|||||||
name = "github.com/prometheus/client_golang"
|
name = "github.com/prometheus/client_golang"
|
||||||
version = "^0.9.1"
|
version = "^0.9.1"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/tendermint/btcd"
|
||||||
|
version = "v0.1.1"
|
||||||
|
|
||||||
###################################
|
###################################
|
||||||
## Some repos dont have releases.
|
## Some repos dont have releases.
|
||||||
## Pin to revision
|
## Pin to revision
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "golang.org/x/crypto"
|
name = "golang.org/x/crypto"
|
||||||
source = "github.com/tendermint/crypto"
|
revision = "505ab145d0a99da450461ae2c1a9f6cd10d1f447"
|
||||||
revision = "3764759f34a542a3aef74d6b02e35be7ab893bba"
|
|
||||||
|
|
||||||
[[override]]
|
[[override]]
|
||||||
name = "github.com/jmhodges/levigo"
|
name = "github.com/jmhodges/levigo"
|
||||||
@@ -93,9 +96,6 @@
|
|||||||
name = "github.com/btcsuite/btcutil"
|
name = "github.com/btcsuite/btcutil"
|
||||||
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
|
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
|
||||||
|
|
||||||
[[constraint]]
|
|
||||||
name = "github.com/tendermint/btcd"
|
|
||||||
revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df"
|
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/rcrowley/go-metrics"
|
name = "github.com/rcrowley/go-metrics"
|
||||||
|
3
Makefile
3
Makefile
@@ -292,8 +292,7 @@ build-linux:
|
|||||||
GOOS=linux GOARCH=amd64 $(MAKE) build
|
GOOS=linux GOARCH=amd64 $(MAKE) build
|
||||||
|
|
||||||
build-docker-localnode:
|
build-docker-localnode:
|
||||||
cd networks/local
|
@cd networks/local && make
|
||||||
make
|
|
||||||
|
|
||||||
# Run a 4-node testnet locally
|
# Run a 4-node testnet locally
|
||||||
localnet-start: localnet-stop
|
localnet-start: localnet-stop
|
||||||
|
95
README.md
95
README.md
@@ -1,8 +1,8 @@
|
|||||||
# Tendermint
|
# Tendermint
|
||||||
|
|
||||||
[Byzantine-Fault Tolerant](https://en.wikipedia.org/wiki/Byzantine_fault_tolerance)
|
[Byzantine-Fault Tolerant](https://en.wikipedia.org/wiki/Byzantine_fault_tolerance)
|
||||||
[State Machine Replication](https://en.wikipedia.org/wiki/State_machine_replication).
|
[State Machines](https://en.wikipedia.org/wiki/State_machine_replication).
|
||||||
Or [Blockchain](https://en.wikipedia.org/wiki/Blockchain_(database)) for short.
|
Or [Blockchain](https://en.wikipedia.org/wiki/Blockchain_(database)), for short.
|
||||||
|
|
||||||
[](https://github.com/tendermint/tendermint/releases/latest)
|
[](https://github.com/tendermint/tendermint/releases/latest)
|
||||||
[
|
|||||||
- [Remote cluster using terraform and ansible](/docs/networks/terraform-and-ansible.md)
|
- [Remote cluster using terraform and ansible](/docs/networks/terraform-and-ansible.md)
|
||||||
- [Join the Cosmos testnet](https://cosmos.network/testnet)
|
- [Join the Cosmos testnet](https://cosmos.network/testnet)
|
||||||
|
|
||||||
## Resources
|
|
||||||
|
|
||||||
### Tendermint Core
|
|
||||||
|
|
||||||
For details about the blockchain data structures and the p2p protocols, see the
|
|
||||||
the [Tendermint specification](/docs/spec).
|
|
||||||
|
|
||||||
For details on using the software, see the [documentation](/docs/) which is also
|
|
||||||
hosted at: https://tendermint.com/docs/
|
|
||||||
|
|
||||||
### Tools
|
|
||||||
|
|
||||||
Benchmarking and monitoring is provided by `tm-bench` and `tm-monitor`, respectively.
|
|
||||||
Their code is found [here](/tools) and these binaries need to be built seperately.
|
|
||||||
Additional documentation is found [here](/docs/tools).
|
|
||||||
|
|
||||||
### Sub-projects
|
|
||||||
|
|
||||||
* [Amino](http://github.com/tendermint/go-amino), a reflection-based improvement on proto3
|
|
||||||
* [IAVL](http://github.com/tendermint/iavl), Merkleized IAVL+ Tree implementation
|
|
||||||
|
|
||||||
### Applications
|
|
||||||
|
|
||||||
* [Cosmos SDK](http://github.com/cosmos/cosmos-sdk); a cryptocurrency application framework
|
|
||||||
* [Ethermint](http://github.com/cosmos/ethermint); Ethereum on Tendermint
|
|
||||||
* [Many more](https://tendermint.com/ecosystem)
|
|
||||||
|
|
||||||
### Research
|
|
||||||
|
|
||||||
* [The latest gossip on BFT consensus](https://arxiv.org/abs/1807.04938)
|
|
||||||
* [Master's Thesis on Tendermint](https://atrium.lib.uoguelph.ca/xmlui/handle/10214/9769)
|
|
||||||
* [Original Whitepaper](https://tendermint.com/static/docs/tendermint.pdf)
|
|
||||||
* [Blog](https://blog.cosmos.network/tendermint/home)
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Yay open source! Please see our [contributing guidelines](CONTRIBUTING.md).
|
Please abide by the [Code of Conduct](CODE_OF_CONDUCT.md) in all interactions,
|
||||||
|
and the [contributing guidelines](CONTRIBUTING.md) when submitting code.
|
||||||
|
|
||||||
|
Join the larger community on the [forum](https://forum.cosmos.network/) and the [chat](https://riot.im/app/#/room/#tendermint:matrix.org).
|
||||||
|
|
||||||
|
To learn more about the structure of the software, watch the [Developer
|
||||||
|
Sessions](https://www.youtube.com/playlist?list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv)
|
||||||
|
and read some [Architectural
|
||||||
|
Decision Records](https://github.com/tendermint/tendermint/tree/master/docs/architecture).
|
||||||
|
|
||||||
|
Learn more by reading the code and comparing it to the
|
||||||
|
[specification](https://github.com/tendermint/tendermint/tree/develop/docs/spec).
|
||||||
|
|
||||||
## Versioning
|
## Versioning
|
||||||
|
|
||||||
### SemVer
|
### Semantic Versioning
|
||||||
|
|
||||||
Tendermint uses [SemVer](http://semver.org/) to determine when and how the version changes.
|
Tendermint uses [Semantic Versioning](http://semver.org/) to determine when and how the version changes.
|
||||||
According to SemVer, anything in the public API can change at any time before version 1.0.0
|
According to SemVer, anything in the public API can change at any time before version 1.0.0
|
||||||
|
|
||||||
To provide some stability to Tendermint users in these 0.X.X days, the MINOR version is used
|
To provide some stability to Tendermint users in these 0.X.X days, the MINOR version is used
|
||||||
@@ -145,8 +122,40 @@ data into the new chain.
|
|||||||
However, any bump in the PATCH version should be compatible with existing histories
|
However, any bump in the PATCH version should be compatible with existing histories
|
||||||
(if not please open an [issue](https://github.com/tendermint/tendermint/issues)).
|
(if not please open an [issue](https://github.com/tendermint/tendermint/issues)).
|
||||||
|
|
||||||
For more information on upgrading, see [here](./UPGRADING.md)
|
For more information on upgrading, see [UPGRADING.md](./UPGRADING.md)
|
||||||
|
|
||||||
## Code of Conduct
|
## Resources
|
||||||
|
|
||||||
|
### Tendermint Core
|
||||||
|
|
||||||
|
For details about the blockchain data structures and the p2p protocols, see the
|
||||||
|
[Tendermint specification](/docs/spec).
|
||||||
|
|
||||||
|
For details on using the software, see the [documentation](/docs/) which is also
|
||||||
|
hosted at: https://tendermint.com/docs/
|
||||||
|
|
||||||
|
### Tools
|
||||||
|
|
||||||
|
Benchmarking and monitoring is provided by `tm-bench` and `tm-monitor`, respectively.
|
||||||
|
Their code is found [here](/tools) and these binaries need to be built seperately.
|
||||||
|
Additional documentation is found [here](/docs/tools).
|
||||||
|
|
||||||
|
### Sub-projects
|
||||||
|
|
||||||
|
* [Amino](http://github.com/tendermint/go-amino), reflection-based proto3, with
|
||||||
|
interfaces
|
||||||
|
* [IAVL](http://github.com/tendermint/iavl), Merkleized IAVL+ Tree implementation
|
||||||
|
|
||||||
|
### Applications
|
||||||
|
|
||||||
|
* [Cosmos SDK](http://github.com/cosmos/cosmos-sdk); a cryptocurrency application framework
|
||||||
|
* [Ethermint](http://github.com/cosmos/ethermint); Ethereum on Tendermint
|
||||||
|
* [Many more](https://tendermint.com/ecosystem)
|
||||||
|
|
||||||
|
### Research
|
||||||
|
|
||||||
|
* [The latest gossip on BFT consensus](https://arxiv.org/abs/1807.04938)
|
||||||
|
* [Master's Thesis on Tendermint](https://atrium.lib.uoguelph.ca/xmlui/handle/10214/9769)
|
||||||
|
* [Original Whitepaper](https://tendermint.com/static/docs/tendermint.pdf)
|
||||||
|
* [Blog](https://blog.cosmos.network/tendermint/home)
|
||||||
|
|
||||||
Please read, understand and adhere to our [code of conduct](CODE_OF_CONDUCT.md).
|
|
||||||
|
71
UPGRADING.md
71
UPGRADING.md
@@ -3,6 +3,77 @@
|
|||||||
This guide provides steps to be followed when you upgrade your applications to
|
This guide provides steps to be followed when you upgrade your applications to
|
||||||
a newer version of Tendermint Core.
|
a newer version of Tendermint Core.
|
||||||
|
|
||||||
|
## v0.29.0
|
||||||
|
|
||||||
|
This release contains some breaking changes to the block and p2p protocols,
|
||||||
|
and will not be compatible with any previous versions of the software, primarily
|
||||||
|
due to changes in how various data structures are hashed.
|
||||||
|
|
||||||
|
Any implementations of Tendermint blockchain verification, including lite clients,
|
||||||
|
will need to be updated. For specific details:
|
||||||
|
- [Merkle tree](./docs/spec/blockchain/encoding.md#merkle-trees)
|
||||||
|
- [ConsensusParams](./docs/spec/blockchain/state.md#consensusparams)
|
||||||
|
|
||||||
|
There was also a small change to field ordering in the vote struct. Any
|
||||||
|
implementations of an out-of-process validator (like a Key-Management Server)
|
||||||
|
will need to be updated. For specific details:
|
||||||
|
- [Vote](https://github.com/tendermint/tendermint/blob/develop/docs/spec/consensus/signing.md#votes)
|
||||||
|
|
||||||
|
Finally, the proposer selection algorithm continues to evolve. See the
|
||||||
|
[work-in-progress
|
||||||
|
specification](https://github.com/tendermint/tendermint/pull/3140).
|
||||||
|
|
||||||
|
For everything else, please see the [CHANGELOG](./CHANGELOG.md#v0.29.0).
|
||||||
|
|
||||||
|
## v0.28.0
|
||||||
|
|
||||||
|
This release breaks the format for the `priv_validator.json` file
|
||||||
|
and the protocol used for the external validator process.
|
||||||
|
It is compatible with v0.27.0 blockchains (neither the BlockProtocol nor the
|
||||||
|
P2PProtocol have changed).
|
||||||
|
|
||||||
|
Please read carefully for details about upgrading.
|
||||||
|
|
||||||
|
**Note:** Backup your `config/priv_validator.json`
|
||||||
|
before proceeding.
|
||||||
|
|
||||||
|
### `priv_validator.json`
|
||||||
|
|
||||||
|
The `config/priv_validator.json` is now two files:
|
||||||
|
`config/priv_validator_key.json` and `data/priv_validator_state.json`.
|
||||||
|
The former contains the key material, the later contains the details on the last
|
||||||
|
message signed.
|
||||||
|
|
||||||
|
When running v0.28.0 for the first time, it will back up any pre-existing
|
||||||
|
`priv_validator.json` file and proceed to split it into the two new files.
|
||||||
|
Upgrading should happen automatically without problem.
|
||||||
|
|
||||||
|
To upgrade manually, use the provided `privValUpgrade.go` script, with exact paths for the old
|
||||||
|
`priv_validator.json` and the locations for the two new files. It's recomended
|
||||||
|
to use the default paths, of `config/priv_validator_key.json` and
|
||||||
|
`data/priv_validator_state.json`, respectively:
|
||||||
|
|
||||||
|
```
|
||||||
|
go run scripts/privValUpgrade.go <old-path> <new-key-path> <new-state-path>
|
||||||
|
```
|
||||||
|
|
||||||
|
### External validator signers
|
||||||
|
|
||||||
|
The Unix and TCP implementations of the remote signing validator
|
||||||
|
have been consolidated into a single implementation.
|
||||||
|
Thus in both cases, the external process is expected to dial
|
||||||
|
Tendermint. This is different from how Unix sockets used to work, where
|
||||||
|
Tendermint dialed the external process.
|
||||||
|
|
||||||
|
The `PubKeyMsg` was also split into separate `Request` and `Response` types
|
||||||
|
for consistency with other messages.
|
||||||
|
|
||||||
|
Note that the TCP sockets don't yet use a persistent key,
|
||||||
|
so while they're encrypted, they can't yet be properly authenticated.
|
||||||
|
See [#3105](https://github.com/tendermint/tendermint/issues/3105).
|
||||||
|
Note the Unix socket has neither encryption nor authentication, but will
|
||||||
|
add a shared-secret in [#3099](https://github.com/tendermint/tendermint/issues/3099).
|
||||||
|
|
||||||
## v0.27.0
|
## v0.27.0
|
||||||
|
|
||||||
This release contains some breaking changes to the block and p2p protocols,
|
This release contains some breaking changes to the block and p2p protocols,
|
||||||
|
@@ -58,7 +58,7 @@ var RootCmd = &cobra.Command{
|
|||||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
switch cmd.Use {
|
switch cmd.Use {
|
||||||
case "counter", "kvstore", "dummy": // for the examples apps, don't pre-run
|
case "counter", "kvstore": // for the examples apps, don't pre-run
|
||||||
return nil
|
return nil
|
||||||
case "version": // skip running for version command
|
case "version": // skip running for version command
|
||||||
return nil
|
return nil
|
||||||
@@ -127,10 +127,6 @@ func addCounterFlags() {
|
|||||||
counterCmd.PersistentFlags().BoolVarP(&flagSerial, "serial", "", false, "enforce incrementing (serial) transactions")
|
counterCmd.PersistentFlags().BoolVarP(&flagSerial, "serial", "", false, "enforce incrementing (serial) transactions")
|
||||||
}
|
}
|
||||||
|
|
||||||
func addDummyFlags() {
|
|
||||||
dummyCmd.PersistentFlags().StringVarP(&flagPersist, "persist", "", "", "directory to use for a database")
|
|
||||||
}
|
|
||||||
|
|
||||||
func addKVStoreFlags() {
|
func addKVStoreFlags() {
|
||||||
kvstoreCmd.PersistentFlags().StringVarP(&flagPersist, "persist", "", "", "directory to use for a database")
|
kvstoreCmd.PersistentFlags().StringVarP(&flagPersist, "persist", "", "", "directory to use for a database")
|
||||||
}
|
}
|
||||||
@@ -152,10 +148,6 @@ func addCommands() {
|
|||||||
// examples
|
// examples
|
||||||
addCounterFlags()
|
addCounterFlags()
|
||||||
RootCmd.AddCommand(counterCmd)
|
RootCmd.AddCommand(counterCmd)
|
||||||
// deprecated, left for backwards compatibility
|
|
||||||
addDummyFlags()
|
|
||||||
RootCmd.AddCommand(dummyCmd)
|
|
||||||
// replaces dummy, see issue #196
|
|
||||||
addKVStoreFlags()
|
addKVStoreFlags()
|
||||||
RootCmd.AddCommand(kvstoreCmd)
|
RootCmd.AddCommand(kvstoreCmd)
|
||||||
}
|
}
|
||||||
@@ -291,18 +283,6 @@ var counterCmd = &cobra.Command{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// deprecated, left for backwards compatibility
|
|
||||||
var dummyCmd = &cobra.Command{
|
|
||||||
Use: "dummy",
|
|
||||||
Deprecated: "use: [abci-cli kvstore] instead",
|
|
||||||
Short: "ABCI demo example",
|
|
||||||
Long: "ABCI demo example",
|
|
||||||
Args: cobra.ExactArgs(0),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
return cmdKVStore(cmd, args)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var kvstoreCmd = &cobra.Command{
|
var kvstoreCmd = &cobra.Command{
|
||||||
Use: "kvstore",
|
Use: "kvstore",
|
||||||
Short: "ABCI demo example",
|
Short: "ABCI demo example",
|
||||||
|
@@ -432,11 +432,7 @@ type bcBlockResponseMessage struct {
|
|||||||
|
|
||||||
// ValidateBasic performs basic validation.
|
// ValidateBasic performs basic validation.
|
||||||
func (m *bcBlockResponseMessage) ValidateBasic() error {
|
func (m *bcBlockResponseMessage) ValidateBasic() error {
|
||||||
if err := m.Block.ValidateBasic(); err != nil {
|
return m.Block.ValidateBasic()
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *bcBlockResponseMessage) String() string {
|
func (m *bcBlockResponseMessage) String() string {
|
||||||
|
@@ -42,7 +42,7 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G
|
|||||||
}
|
}
|
||||||
|
|
||||||
func makeVote(header *types.Header, blockID types.BlockID, valset *types.ValidatorSet, privVal types.PrivValidator) *types.Vote {
|
func makeVote(header *types.Header, blockID types.BlockID, valset *types.ValidatorSet, privVal types.PrivValidator) *types.Vote {
|
||||||
addr := privVal.GetAddress()
|
addr := privVal.GetPubKey().Address()
|
||||||
idx, _ := valset.GetByAddress(addr)
|
idx, _ := valset.GetByAddress(addr)
|
||||||
vote := &types.Vote{
|
vote := &types.Vote{
|
||||||
ValidatorAddress: addr,
|
ValidatorAddress: addr,
|
||||||
|
@@ -311,7 +311,7 @@ func TestLoadBlockPart(t *testing.T) {
|
|||||||
gotPart, _, panicErr := doFn(loadPart)
|
gotPart, _, panicErr := doFn(loadPart)
|
||||||
require.Nil(t, panicErr, "an existent and proper block should not panic")
|
require.Nil(t, panicErr, "an existent and proper block should not panic")
|
||||||
require.Nil(t, res, "a properly saved block should return a proper block")
|
require.Nil(t, res, "a properly saved block should return a proper block")
|
||||||
require.Equal(t, gotPart.(*types.Part).Hash(), part1.Hash(),
|
require.Equal(t, gotPart.(*types.Part), part1,
|
||||||
"expecting successful retrieval of previously saved block")
|
"expecting successful retrieval of previously saved block")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||||
cmn "github.com/tendermint/tendermint/libs/common"
|
cmn "github.com/tendermint/tendermint/libs/common"
|
||||||
@@ -13,9 +14,10 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var (
|
var (
|
||||||
addr = flag.String("addr", ":26659", "Address of client to connect to")
|
addr = flag.String("addr", ":26659", "Address of client to connect to")
|
||||||
chainID = flag.String("chain-id", "mychain", "chain id")
|
chainID = flag.String("chain-id", "mychain", "chain id")
|
||||||
privValPath = flag.String("priv", "", "priv val file path")
|
privValKeyPath = flag.String("priv-key", "", "priv val key file path")
|
||||||
|
privValStatePath = flag.String("priv-state", "", "priv val state file path")
|
||||||
|
|
||||||
logger = log.NewTMLogger(
|
logger = log.NewTMLogger(
|
||||||
log.NewSyncWriter(os.Stdout),
|
log.NewSyncWriter(os.Stdout),
|
||||||
@@ -27,18 +29,26 @@ func main() {
|
|||||||
"Starting private validator",
|
"Starting private validator",
|
||||||
"addr", *addr,
|
"addr", *addr,
|
||||||
"chainID", *chainID,
|
"chainID", *chainID,
|
||||||
"privPath", *privValPath,
|
"privKeyPath", *privValKeyPath,
|
||||||
|
"privStatePath", *privValStatePath,
|
||||||
)
|
)
|
||||||
|
|
||||||
pv := privval.LoadFilePV(*privValPath)
|
pv := privval.LoadFilePV(*privValKeyPath, *privValStatePath)
|
||||||
|
|
||||||
rs := privval.NewRemoteSigner(
|
var dialer privval.Dialer
|
||||||
logger,
|
protocol, address := cmn.ProtocolAndAddress(*addr)
|
||||||
*chainID,
|
switch protocol {
|
||||||
*addr,
|
case "unix":
|
||||||
pv,
|
dialer = privval.DialUnixFn(address)
|
||||||
ed25519.GenPrivKey(),
|
case "tcp":
|
||||||
)
|
connTimeout := 3 * time.Second // TODO
|
||||||
|
dialer = privval.DialTCPFn(address, connTimeout, ed25519.GenPrivKey())
|
||||||
|
default:
|
||||||
|
logger.Error("Unknown protocol", "protocol", protocol)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
rs := privval.NewRemoteSigner(logger, *chainID, pv, dialer)
|
||||||
err := rs.Start()
|
err := rs.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@@ -17,7 +17,7 @@ var GenValidatorCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func genValidator(cmd *cobra.Command, args []string) {
|
func genValidator(cmd *cobra.Command, args []string) {
|
||||||
pv := privval.GenFilePV("")
|
pv := privval.GenFilePV("", "")
|
||||||
jsbz, err := cdc.MarshalJSON(pv)
|
jsbz, err := cdc.MarshalJSON(pv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@@ -4,7 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
cfg "github.com/tendermint/tendermint/config"
|
cfg "github.com/tendermint/tendermint/config"
|
||||||
cmn "github.com/tendermint/tendermint/libs/common"
|
cmn "github.com/tendermint/tendermint/libs/common"
|
||||||
"github.com/tendermint/tendermint/p2p"
|
"github.com/tendermint/tendermint/p2p"
|
||||||
@@ -26,15 +25,18 @@ func initFiles(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
func initFilesWithConfig(config *cfg.Config) error {
|
func initFilesWithConfig(config *cfg.Config) error {
|
||||||
// private validator
|
// private validator
|
||||||
privValFile := config.PrivValidatorFile()
|
privValKeyFile := config.PrivValidatorKeyFile()
|
||||||
|
privValStateFile := config.PrivValidatorStateFile()
|
||||||
var pv *privval.FilePV
|
var pv *privval.FilePV
|
||||||
if cmn.FileExists(privValFile) {
|
if cmn.FileExists(privValKeyFile) {
|
||||||
pv = privval.LoadFilePV(privValFile)
|
pv = privval.LoadFilePV(privValKeyFile, privValStateFile)
|
||||||
logger.Info("Found private validator", "path", privValFile)
|
logger.Info("Found private validator", "keyFile", privValKeyFile,
|
||||||
|
"stateFile", privValStateFile)
|
||||||
} else {
|
} else {
|
||||||
pv = privval.GenFilePV(privValFile)
|
pv = privval.GenFilePV(privValKeyFile, privValStateFile)
|
||||||
pv.Save()
|
pv.Save()
|
||||||
logger.Info("Generated private validator", "path", privValFile)
|
logger.Info("Generated private validator", "keyFile", privValKeyFile,
|
||||||
|
"stateFile", privValStateFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeKeyFile := config.NodeKeyFile()
|
nodeKeyFile := config.NodeKeyFile()
|
||||||
@@ -57,9 +59,10 @@ func initFilesWithConfig(config *cfg.Config) error {
|
|||||||
GenesisTime: tmtime.Now(),
|
GenesisTime: tmtime.Now(),
|
||||||
ConsensusParams: types.DefaultConsensusParams(),
|
ConsensusParams: types.DefaultConsensusParams(),
|
||||||
}
|
}
|
||||||
|
key := pv.GetPubKey()
|
||||||
genDoc.Validators = []types.GenesisValidator{{
|
genDoc.Validators = []types.GenesisValidator{{
|
||||||
Address: pv.GetPubKey().Address(),
|
Address: key.Address(),
|
||||||
PubKey: pv.GetPubKey(),
|
PubKey: key,
|
||||||
Power: 10,
|
Power: 10,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
cmn "github.com/tendermint/tendermint/libs/common"
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
"github.com/tendermint/tendermint/privval"
|
"github.com/tendermint/tendermint/privval"
|
||||||
)
|
)
|
||||||
@@ -27,36 +28,41 @@ var ResetPrivValidatorCmd = &cobra.Command{
|
|||||||
// XXX: this is totally unsafe.
|
// XXX: this is totally unsafe.
|
||||||
// it's only suitable for testnets.
|
// it's only suitable for testnets.
|
||||||
func resetAll(cmd *cobra.Command, args []string) {
|
func resetAll(cmd *cobra.Command, args []string) {
|
||||||
ResetAll(config.DBDir(), config.P2P.AddrBookFile(), config.PrivValidatorFile(), logger)
|
ResetAll(config.DBDir(), config.P2P.AddrBookFile(), config.PrivValidatorKeyFile(),
|
||||||
|
config.PrivValidatorStateFile(), logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: this is totally unsafe.
|
// XXX: this is totally unsafe.
|
||||||
// it's only suitable for testnets.
|
// it's only suitable for testnets.
|
||||||
func resetPrivValidator(cmd *cobra.Command, args []string) {
|
func resetPrivValidator(cmd *cobra.Command, args []string) {
|
||||||
resetFilePV(config.PrivValidatorFile(), logger)
|
resetFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile(), logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetAll removes the privValidator and address book files plus all data.
|
// ResetAll removes address book files plus all data, and resets the privValdiator data.
|
||||||
// Exported so other CLI tools can use it.
|
// Exported so other CLI tools can use it.
|
||||||
func ResetAll(dbDir, addrBookFile, privValFile string, logger log.Logger) {
|
func ResetAll(dbDir, addrBookFile, privValKeyFile, privValStateFile string, logger log.Logger) {
|
||||||
resetFilePV(privValFile, logger)
|
|
||||||
removeAddrBook(addrBookFile, logger)
|
removeAddrBook(addrBookFile, logger)
|
||||||
if err := os.RemoveAll(dbDir); err == nil {
|
if err := os.RemoveAll(dbDir); err == nil {
|
||||||
logger.Info("Removed all blockchain history", "dir", dbDir)
|
logger.Info("Removed all blockchain history", "dir", dbDir)
|
||||||
} else {
|
} else {
|
||||||
logger.Error("Error removing all blockchain history", "dir", dbDir, "err", err)
|
logger.Error("Error removing all blockchain history", "dir", dbDir, "err", err)
|
||||||
}
|
}
|
||||||
|
// recreate the dbDir since the privVal state needs to live there
|
||||||
|
cmn.EnsureDir(dbDir, 0700)
|
||||||
|
resetFilePV(privValKeyFile, privValStateFile, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resetFilePV(privValFile string, logger log.Logger) {
|
func resetFilePV(privValKeyFile, privValStateFile string, logger log.Logger) {
|
||||||
if _, err := os.Stat(privValFile); err == nil {
|
if _, err := os.Stat(privValKeyFile); err == nil {
|
||||||
pv := privval.LoadFilePV(privValFile)
|
pv := privval.LoadFilePVEmptyState(privValKeyFile, privValStateFile)
|
||||||
pv.Reset()
|
pv.Reset()
|
||||||
logger.Info("Reset private validator file to genesis state", "file", privValFile)
|
logger.Info("Reset private validator file to genesis state", "keyFile", privValKeyFile,
|
||||||
|
"stateFile", privValStateFile)
|
||||||
} else {
|
} else {
|
||||||
pv := privval.GenFilePV(privValFile)
|
pv := privval.GenFilePV(privValKeyFile, privValStateFile)
|
||||||
pv.Save()
|
pv.Save()
|
||||||
logger.Info("Generated private validator file", "file", privValFile)
|
logger.Info("Generated private validator file", "file", "keyFile", privValKeyFile,
|
||||||
|
"stateFile", privValStateFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,7 +24,7 @@ func AddNodeFlags(cmd *cobra.Command) {
|
|||||||
cmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing")
|
cmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing")
|
||||||
|
|
||||||
// abci flags
|
// abci flags
|
||||||
cmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or 'nilapp' or 'kvstore' for local testing.")
|
cmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or one of: 'kvstore', 'persistent_kvstore', 'counter', 'counter_serial' or 'noop' for local testing.")
|
||||||
cmd.Flags().String("abci", config.ABCI, "Specify abci transport (socket | grpc)")
|
cmd.Flags().String("abci", config.ABCI, "Specify abci transport (socket | grpc)")
|
||||||
|
|
||||||
// rpc flags
|
// rpc flags
|
||||||
|
@@ -16,7 +16,7 @@ var ShowValidatorCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func showValidator(cmd *cobra.Command, args []string) {
|
func showValidator(cmd *cobra.Command, args []string) {
|
||||||
privValidator := privval.LoadOrGenFilePV(config.PrivValidatorFile())
|
privValidator := privval.LoadOrGenFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile())
|
||||||
pubKeyJSONBytes, _ := cdc.MarshalJSON(privValidator.GetPubKey())
|
pubKeyJSONBytes, _ := cdc.MarshalJSON(privValidator.GetPubKey())
|
||||||
fmt.Println(string(pubKeyJSONBytes))
|
fmt.Println(string(pubKeyJSONBytes))
|
||||||
}
|
}
|
||||||
|
@@ -85,11 +85,18 @@ func testnetFiles(cmd *cobra.Command, args []string) error {
|
|||||||
_ = os.RemoveAll(outputDir)
|
_ = os.RemoveAll(outputDir)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = os.MkdirAll(filepath.Join(nodeDir, "data"), nodeDirPerm)
|
||||||
|
if err != nil {
|
||||||
|
_ = os.RemoveAll(outputDir)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
initFilesWithConfig(config)
|
initFilesWithConfig(config)
|
||||||
|
|
||||||
pvFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidator)
|
pvKeyFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidatorKey)
|
||||||
pv := privval.LoadFilePV(pvFile)
|
pvStateFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidatorState)
|
||||||
|
|
||||||
|
pv := privval.LoadFilePV(pvKeyFile, pvStateFile)
|
||||||
genVals[i] = types.GenesisValidator{
|
genVals[i] = types.GenesisValidator{
|
||||||
Address: pv.GetPubKey().Address(),
|
Address: pv.GetPubKey().Address(),
|
||||||
PubKey: pv.GetPubKey(),
|
PubKey: pv.GetPubKey(),
|
||||||
@@ -127,14 +134,32 @@ func testnetFiles(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gather persistent peer addresses.
|
||||||
|
var (
|
||||||
|
persistentPeers string
|
||||||
|
err error
|
||||||
|
)
|
||||||
if populatePersistentPeers {
|
if populatePersistentPeers {
|
||||||
err := populatePersistentPeersInConfigAndWriteIt(config)
|
persistentPeers, err = persistentPeersString(config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = os.RemoveAll(outputDir)
|
_ = os.RemoveAll(outputDir)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Overwrite default config.
|
||||||
|
for i := 0; i < nValidators+nNonValidators; i++ {
|
||||||
|
nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i))
|
||||||
|
config.SetRoot(nodeDir)
|
||||||
|
config.P2P.AddrBookStrict = false
|
||||||
|
config.P2P.AllowDuplicateIP = true
|
||||||
|
if populatePersistentPeers {
|
||||||
|
config.P2P.PersistentPeers = persistentPeers
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.WriteConfigFile(filepath.Join(nodeDir, "config", "config.toml"), config)
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Printf("Successfully initialized %v node directories\n", nValidators+nNonValidators)
|
fmt.Printf("Successfully initialized %v node directories\n", nValidators+nNonValidators)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -157,28 +182,16 @@ func hostnameOrIP(i int) string {
|
|||||||
return fmt.Sprintf("%s%d", hostnamePrefix, i)
|
return fmt.Sprintf("%s%d", hostnamePrefix, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func populatePersistentPeersInConfigAndWriteIt(config *cfg.Config) error {
|
func persistentPeersString(config *cfg.Config) (string, error) {
|
||||||
persistentPeers := make([]string, nValidators+nNonValidators)
|
persistentPeers := make([]string, nValidators+nNonValidators)
|
||||||
for i := 0; i < nValidators+nNonValidators; i++ {
|
for i := 0; i < nValidators+nNonValidators; i++ {
|
||||||
nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i))
|
nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i))
|
||||||
config.SetRoot(nodeDir)
|
config.SetRoot(nodeDir)
|
||||||
nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile())
|
nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
persistentPeers[i] = p2p.IDAddressString(nodeKey.ID(), fmt.Sprintf("%s:%d", hostnameOrIP(i), p2pPort))
|
persistentPeers[i] = p2p.IDAddressString(nodeKey.ID(), fmt.Sprintf("%s:%d", hostnameOrIP(i), p2pPort))
|
||||||
}
|
}
|
||||||
persistentPeersList := strings.Join(persistentPeers, ",")
|
return strings.Join(persistentPeers, ","), nil
|
||||||
|
|
||||||
for i := 0; i < nValidators+nNonValidators; i++ {
|
|
||||||
nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i))
|
|
||||||
config.SetRoot(nodeDir)
|
|
||||||
config.P2P.PersistentPeers = persistentPeersList
|
|
||||||
config.P2P.AddrBookStrict = false
|
|
||||||
|
|
||||||
// overwrite default config
|
|
||||||
cfg.WriteConfigFile(filepath.Join(nodeDir, "config", "config.toml"), config)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
@@ -35,15 +35,24 @@ var (
|
|||||||
defaultConfigFileName = "config.toml"
|
defaultConfigFileName = "config.toml"
|
||||||
defaultGenesisJSONName = "genesis.json"
|
defaultGenesisJSONName = "genesis.json"
|
||||||
|
|
||||||
defaultPrivValName = "priv_validator.json"
|
defaultPrivValKeyName = "priv_validator_key.json"
|
||||||
|
defaultPrivValStateName = "priv_validator_state.json"
|
||||||
|
|
||||||
defaultNodeKeyName = "node_key.json"
|
defaultNodeKeyName = "node_key.json"
|
||||||
defaultAddrBookName = "addrbook.json"
|
defaultAddrBookName = "addrbook.json"
|
||||||
|
|
||||||
defaultConfigFilePath = filepath.Join(defaultConfigDir, defaultConfigFileName)
|
defaultConfigFilePath = filepath.Join(defaultConfigDir, defaultConfigFileName)
|
||||||
defaultGenesisJSONPath = filepath.Join(defaultConfigDir, defaultGenesisJSONName)
|
defaultGenesisJSONPath = filepath.Join(defaultConfigDir, defaultGenesisJSONName)
|
||||||
defaultPrivValPath = filepath.Join(defaultConfigDir, defaultPrivValName)
|
defaultPrivValKeyPath = filepath.Join(defaultConfigDir, defaultPrivValKeyName)
|
||||||
defaultNodeKeyPath = filepath.Join(defaultConfigDir, defaultNodeKeyName)
|
defaultPrivValStatePath = filepath.Join(defaultDataDir, defaultPrivValStateName)
|
||||||
defaultAddrBookPath = filepath.Join(defaultConfigDir, defaultAddrBookName)
|
|
||||||
|
defaultNodeKeyPath = filepath.Join(defaultConfigDir, defaultNodeKeyName)
|
||||||
|
defaultAddrBookPath = filepath.Join(defaultConfigDir, defaultAddrBookName)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
oldPrivVal = "priv_validator.json"
|
||||||
|
oldPrivValPath = filepath.Join(defaultConfigDir, oldPrivVal)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config defines the top level configuration for a Tendermint node
|
// Config defines the top level configuration for a Tendermint node
|
||||||
@@ -160,7 +169,10 @@ type BaseConfig struct {
|
|||||||
Genesis string `mapstructure:"genesis_file"`
|
Genesis string `mapstructure:"genesis_file"`
|
||||||
|
|
||||||
// Path to the JSON file containing the private key to use as a validator in the consensus protocol
|
// Path to the JSON file containing the private key to use as a validator in the consensus protocol
|
||||||
PrivValidator string `mapstructure:"priv_validator_file"`
|
PrivValidatorKey string `mapstructure:"priv_validator_key_file"`
|
||||||
|
|
||||||
|
// Path to the JSON file containing the last sign state of a validator
|
||||||
|
PrivValidatorState string `mapstructure:"priv_validator_state_file"`
|
||||||
|
|
||||||
// TCP or UNIX socket address for Tendermint to listen on for
|
// TCP or UNIX socket address for Tendermint to listen on for
|
||||||
// connections from an external PrivValidator process
|
// connections from an external PrivValidator process
|
||||||
@@ -183,19 +195,20 @@ type BaseConfig struct {
|
|||||||
// 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{
|
||||||
Genesis: defaultGenesisJSONPath,
|
Genesis: defaultGenesisJSONPath,
|
||||||
PrivValidator: defaultPrivValPath,
|
PrivValidatorKey: defaultPrivValKeyPath,
|
||||||
NodeKey: defaultNodeKeyPath,
|
PrivValidatorState: defaultPrivValStatePath,
|
||||||
Moniker: defaultMoniker,
|
NodeKey: defaultNodeKeyPath,
|
||||||
ProxyApp: "tcp://127.0.0.1:26658",
|
Moniker: defaultMoniker,
|
||||||
ABCI: "socket",
|
ProxyApp: "tcp://127.0.0.1:26658",
|
||||||
LogLevel: DefaultPackageLogLevels(),
|
ABCI: "socket",
|
||||||
LogFormat: LogFormatPlain,
|
LogLevel: DefaultPackageLogLevels(),
|
||||||
ProfListenAddress: "",
|
LogFormat: LogFormatPlain,
|
||||||
FastSync: true,
|
ProfListenAddress: "",
|
||||||
FilterPeers: false,
|
FastSync: true,
|
||||||
DBBackend: "leveldb",
|
FilterPeers: false,
|
||||||
DBPath: "data",
|
DBBackend: "leveldb",
|
||||||
|
DBPath: "data",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,9 +231,20 @@ func (cfg BaseConfig) GenesisFile() string {
|
|||||||
return rootify(cfg.Genesis, cfg.RootDir)
|
return rootify(cfg.Genesis, cfg.RootDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrivValidatorFile returns the full path to the priv_validator.json file
|
// PrivValidatorKeyFile returns the full path to the priv_validator_key.json file
|
||||||
func (cfg BaseConfig) PrivValidatorFile() string {
|
func (cfg BaseConfig) PrivValidatorKeyFile() string {
|
||||||
return rootify(cfg.PrivValidator, cfg.RootDir)
|
return rootify(cfg.PrivValidatorKey, cfg.RootDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivValidatorFile returns the full path to the priv_validator_state.json file
|
||||||
|
func (cfg BaseConfig) PrivValidatorStateFile() string {
|
||||||
|
return rootify(cfg.PrivValidatorState, cfg.RootDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OldPrivValidatorFile returns the full path of the priv_validator.json from pre v0.28.0.
|
||||||
|
// TODO: eventually remove.
|
||||||
|
func (cfg BaseConfig) OldPrivValidatorFile() string {
|
||||||
|
return rootify(oldPrivValPath, cfg.RootDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeKeyFile returns the full path to the node_key.json file
|
// NodeKeyFile returns the full path to the node_key.json file
|
||||||
@@ -283,7 +307,7 @@ type RPCConfig struct {
|
|||||||
|
|
||||||
// Maximum number of simultaneous connections.
|
// Maximum number of simultaneous connections.
|
||||||
// Does not include RPC (HTTP&WebSocket) connections. See max_open_connections
|
// Does not include RPC (HTTP&WebSocket) connections. See max_open_connections
|
||||||
// If you want to accept more significant number than the default, make sure
|
// If you want to accept a larger number than the default, make sure
|
||||||
// you increase your OS limits.
|
// you increase your OS limits.
|
||||||
// 0 - unlimited.
|
// 0 - unlimited.
|
||||||
GRPCMaxOpenConnections int `mapstructure:"grpc_max_open_connections"`
|
GRPCMaxOpenConnections int `mapstructure:"grpc_max_open_connections"`
|
||||||
@@ -293,7 +317,7 @@ type RPCConfig struct {
|
|||||||
|
|
||||||
// Maximum number of simultaneous connections (including WebSocket).
|
// Maximum number of simultaneous connections (including WebSocket).
|
||||||
// Does not include gRPC connections. See grpc_max_open_connections
|
// Does not include gRPC connections. See grpc_max_open_connections
|
||||||
// If you want to accept more significant number than the default, make sure
|
// If you want to accept a larger number than the default, make sure
|
||||||
// you increase your OS limits.
|
// you increase your OS limits.
|
||||||
// 0 - unlimited.
|
// 0 - unlimited.
|
||||||
// Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}
|
// Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}
|
||||||
@@ -434,7 +458,7 @@ func DefaultP2PConfig() *P2PConfig {
|
|||||||
RecvRate: 5120000, // 5 mB/s
|
RecvRate: 5120000, // 5 mB/s
|
||||||
PexReactor: true,
|
PexReactor: true,
|
||||||
SeedMode: false,
|
SeedMode: false,
|
||||||
AllowDuplicateIP: true, // so non-breaking yet
|
AllowDuplicateIP: false,
|
||||||
HandshakeTimeout: 20 * time.Second,
|
HandshakeTimeout: 20 * time.Second,
|
||||||
DialTimeout: 3 * time.Second,
|
DialTimeout: 3 * time.Second,
|
||||||
TestDialFail: false,
|
TestDialFail: false,
|
||||||
@@ -774,12 +798,12 @@ type InstrumentationConfig struct {
|
|||||||
PrometheusListenAddr string `mapstructure:"prometheus_listen_addr"`
|
PrometheusListenAddr string `mapstructure:"prometheus_listen_addr"`
|
||||||
|
|
||||||
// Maximum number of simultaneous connections.
|
// Maximum number of simultaneous connections.
|
||||||
// If you want to accept more significant number than the default, make sure
|
// If you want to accept a larger number than the default, make sure
|
||||||
// you increase your OS limits.
|
// you increase your OS limits.
|
||||||
// 0 - unlimited.
|
// 0 - unlimited.
|
||||||
MaxOpenConnections int `mapstructure:"max_open_connections"`
|
MaxOpenConnections int `mapstructure:"max_open_connections"`
|
||||||
|
|
||||||
// Tendermint instrumentation namespace.
|
// Instrumentation namespace.
|
||||||
Namespace string `mapstructure:"namespace"`
|
Namespace string `mapstructure:"namespace"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -95,7 +95,10 @@ log_format = "{{ .BaseConfig.LogFormat }}"
|
|||||||
genesis_file = "{{ js .BaseConfig.Genesis }}"
|
genesis_file = "{{ js .BaseConfig.Genesis }}"
|
||||||
|
|
||||||
# Path to the JSON file containing the private key to use as a validator in the consensus protocol
|
# Path to the JSON file containing the private key to use as a validator in the consensus protocol
|
||||||
priv_validator_file = "{{ js .BaseConfig.PrivValidator }}"
|
priv_validator_key_file = "{{ js .BaseConfig.PrivValidatorKey }}"
|
||||||
|
|
||||||
|
# Path to the JSON file containing the last sign state of a validator
|
||||||
|
priv_validator_state_file = "{{ js .BaseConfig.PrivValidatorState }}"
|
||||||
|
|
||||||
# TCP or UNIX socket address for Tendermint to listen on for
|
# TCP or UNIX socket address for Tendermint to listen on for
|
||||||
# connections from an external PrivValidator process
|
# connections from an external PrivValidator process
|
||||||
@@ -125,13 +128,13 @@ laddr = "{{ .RPC.ListenAddress }}"
|
|||||||
# A list of origins a cross-domain request can be executed from
|
# A list of origins a cross-domain request can be executed from
|
||||||
# Default value '[]' disables cors support
|
# Default value '[]' disables cors support
|
||||||
# Use '["*"]' to allow any origin
|
# Use '["*"]' to allow any origin
|
||||||
cors_allowed_origins = "{{ .RPC.CORSAllowedOrigins }}"
|
cors_allowed_origins = [{{ range .RPC.CORSAllowedOrigins }}{{ printf "%q, " . }}{{end}}]
|
||||||
|
|
||||||
# A list of methods the client is allowed to use with cross-domain requests
|
# A list of methods the client is allowed to use with cross-domain requests
|
||||||
cors_allowed_methods = "{{ .RPC.CORSAllowedMethods }}"
|
cors_allowed_methods = [{{ range .RPC.CORSAllowedMethods }}{{ printf "%q, " . }}{{end}}]
|
||||||
|
|
||||||
# A list of non simple headers the client is allowed to use with cross-domain requests
|
# A list of non simple headers the client is allowed to use with cross-domain requests
|
||||||
cors_allowed_headers = "{{ .RPC.CORSAllowedHeaders }}"
|
cors_allowed_headers = [{{ range .RPC.CORSAllowedHeaders }}{{ printf "%q, " . }}{{end}}]
|
||||||
|
|
||||||
# TCP or UNIX socket address for the gRPC server to listen on
|
# TCP or UNIX socket address for the gRPC server to listen on
|
||||||
# NOTE: This server only supports /broadcast_tx_commit
|
# NOTE: This server only supports /broadcast_tx_commit
|
||||||
@@ -139,7 +142,7 @@ grpc_laddr = "{{ .RPC.GRPCListenAddress }}"
|
|||||||
|
|
||||||
# Maximum number of simultaneous connections.
|
# Maximum number of simultaneous connections.
|
||||||
# Does not include RPC (HTTP&WebSocket) connections. See max_open_connections
|
# Does not include RPC (HTTP&WebSocket) connections. See max_open_connections
|
||||||
# If you want to accept more significant number than the default, make sure
|
# If you want to accept a larger number than the default, make sure
|
||||||
# you increase your OS limits.
|
# you increase your OS limits.
|
||||||
# 0 - unlimited.
|
# 0 - unlimited.
|
||||||
# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}
|
# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}
|
||||||
@@ -151,7 +154,7 @@ unsafe = {{ .RPC.Unsafe }}
|
|||||||
|
|
||||||
# Maximum number of simultaneous connections (including WebSocket).
|
# Maximum number of simultaneous connections (including WebSocket).
|
||||||
# Does not include gRPC connections. See grpc_max_open_connections
|
# Does not include gRPC connections. See grpc_max_open_connections
|
||||||
# If you want to accept more significant number than the default, make sure
|
# If you want to accept a larger number than the default, make sure
|
||||||
# you increase your OS limits.
|
# you increase your OS limits.
|
||||||
# 0 - unlimited.
|
# 0 - unlimited.
|
||||||
# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}
|
# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}
|
||||||
@@ -269,8 +272,8 @@ blocktime_iota = "{{ .Consensus.BlockTimeIota }}"
|
|||||||
# What indexer to use for transactions
|
# What indexer to use for transactions
|
||||||
#
|
#
|
||||||
# Options:
|
# Options:
|
||||||
# 1) "null" (default)
|
# 1) "null"
|
||||||
# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend).
|
# 2) "kv" (default) - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend).
|
||||||
indexer = "{{ .TxIndex.Indexer }}"
|
indexer = "{{ .TxIndex.Indexer }}"
|
||||||
|
|
||||||
# Comma-separated list of tags to index (by default the only tag is "tx.hash")
|
# Comma-separated list of tags to index (by default the only tag is "tx.hash")
|
||||||
@@ -302,7 +305,7 @@ prometheus = {{ .Instrumentation.Prometheus }}
|
|||||||
prometheus_listen_addr = "{{ .Instrumentation.PrometheusListenAddr }}"
|
prometheus_listen_addr = "{{ .Instrumentation.PrometheusListenAddr }}"
|
||||||
|
|
||||||
# Maximum number of simultaneous connections.
|
# Maximum number of simultaneous connections.
|
||||||
# If you want to accept more significant number than the default, make sure
|
# If you want to accept a larger number than the default, make sure
|
||||||
# you increase your OS limits.
|
# you increase your OS limits.
|
||||||
# 0 - unlimited.
|
# 0 - unlimited.
|
||||||
max_open_connections = {{ .Instrumentation.MaxOpenConnections }}
|
max_open_connections = {{ .Instrumentation.MaxOpenConnections }}
|
||||||
@@ -342,7 +345,8 @@ func ResetTestRoot(testName string) *Config {
|
|||||||
baseConfig := DefaultBaseConfig()
|
baseConfig := DefaultBaseConfig()
|
||||||
configFilePath := filepath.Join(rootDir, defaultConfigFilePath)
|
configFilePath := filepath.Join(rootDir, defaultConfigFilePath)
|
||||||
genesisFilePath := filepath.Join(rootDir, baseConfig.Genesis)
|
genesisFilePath := filepath.Join(rootDir, baseConfig.Genesis)
|
||||||
privFilePath := filepath.Join(rootDir, baseConfig.PrivValidator)
|
privKeyFilePath := filepath.Join(rootDir, baseConfig.PrivValidatorKey)
|
||||||
|
privStateFilePath := filepath.Join(rootDir, baseConfig.PrivValidatorState)
|
||||||
|
|
||||||
// Write default config file if missing.
|
// Write default config file if missing.
|
||||||
if !cmn.FileExists(configFilePath) {
|
if !cmn.FileExists(configFilePath) {
|
||||||
@@ -352,7 +356,8 @@ func ResetTestRoot(testName string) *Config {
|
|||||||
cmn.MustWriteFile(genesisFilePath, []byte(testGenesis), 0644)
|
cmn.MustWriteFile(genesisFilePath, []byte(testGenesis), 0644)
|
||||||
}
|
}
|
||||||
// we always overwrite the priv val
|
// we always overwrite the priv val
|
||||||
cmn.MustWriteFile(privFilePath, []byte(testPrivValidator), 0644)
|
cmn.MustWriteFile(privKeyFilePath, []byte(testPrivValidatorKey), 0644)
|
||||||
|
cmn.MustWriteFile(privStateFilePath, []byte(testPrivValidatorState), 0644)
|
||||||
|
|
||||||
config := TestConfig().SetRoot(rootDir)
|
config := TestConfig().SetRoot(rootDir)
|
||||||
return config
|
return config
|
||||||
@@ -374,7 +379,7 @@ var testGenesis = `{
|
|||||||
"app_hash": ""
|
"app_hash": ""
|
||||||
}`
|
}`
|
||||||
|
|
||||||
var testPrivValidator = `{
|
var testPrivValidatorKey = `{
|
||||||
"address": "A3258DCBF45DCA0DF052981870F2D1441A36D145",
|
"address": "A3258DCBF45DCA0DF052981870F2D1441A36D145",
|
||||||
"pub_key": {
|
"pub_key": {
|
||||||
"type": "tendermint/PubKeyEd25519",
|
"type": "tendermint/PubKeyEd25519",
|
||||||
@@ -383,8 +388,11 @@ var testPrivValidator = `{
|
|||||||
"priv_key": {
|
"priv_key": {
|
||||||
"type": "tendermint/PrivKeyEd25519",
|
"type": "tendermint/PrivKeyEd25519",
|
||||||
"value": "EVkqJO/jIXp3rkASXfh9YnyToYXRXhBr6g9cQVxPFnQBP/5povV4HTjvsy530kybxKHwEi85iU8YL0qQhSYVoQ=="
|
"value": "EVkqJO/jIXp3rkASXfh9YnyToYXRXhBr6g9cQVxPFnQBP/5povV4HTjvsy530kybxKHwEi85iU8YL0qQhSYVoQ=="
|
||||||
},
|
}
|
||||||
"last_height": "0",
|
}`
|
||||||
"last_round": "0",
|
|
||||||
"last_step": 0
|
var testPrivValidatorState = `{
|
||||||
|
"height": "0",
|
||||||
|
"round": "0",
|
||||||
|
"step": 0
|
||||||
}`
|
}`
|
||||||
|
@@ -60,7 +60,7 @@ func TestEnsureTestRoot(t *testing.T) {
|
|||||||
|
|
||||||
// TODO: make sure the cfg returned and testconfig are the same!
|
// TODO: make sure the cfg returned and testconfig are the same!
|
||||||
baseConfig := DefaultBaseConfig()
|
baseConfig := DefaultBaseConfig()
|
||||||
ensureFiles(t, rootDir, defaultDataDir, baseConfig.Genesis, baseConfig.PrivValidator)
|
ensureFiles(t, rootDir, defaultDataDir, baseConfig.Genesis, baseConfig.PrivValidatorKey, baseConfig.PrivValidatorState)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkConfig(configFile string) bool {
|
func checkConfig(configFile string) bool {
|
||||||
|
@@ -6,14 +6,18 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-kit/kit/log/term"
|
||||||
|
|
||||||
abcicli "github.com/tendermint/tendermint/abci/client"
|
abcicli "github.com/tendermint/tendermint/abci/client"
|
||||||
|
"github.com/tendermint/tendermint/abci/example/counter"
|
||||||
|
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
bc "github.com/tendermint/tendermint/blockchain"
|
bc "github.com/tendermint/tendermint/blockchain"
|
||||||
cfg "github.com/tendermint/tendermint/config"
|
cfg "github.com/tendermint/tendermint/config"
|
||||||
@@ -27,11 +31,6 @@ import (
|
|||||||
sm "github.com/tendermint/tendermint/state"
|
sm "github.com/tendermint/tendermint/state"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
tmtime "github.com/tendermint/tendermint/types/time"
|
tmtime "github.com/tendermint/tendermint/types/time"
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/abci/example/counter"
|
|
||||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
|
||||||
|
|
||||||
"github.com/go-kit/kit/log/term"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -72,9 +71,10 @@ func NewValidatorStub(privValidator types.PrivValidator, valIndex int) *validato
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (vs *validatorStub) signVote(voteType types.SignedMsgType, hash []byte, header types.PartSetHeader) (*types.Vote, error) {
|
func (vs *validatorStub) signVote(voteType types.SignedMsgType, hash []byte, header types.PartSetHeader) (*types.Vote, error) {
|
||||||
|
addr := vs.PrivValidator.GetPubKey().Address()
|
||||||
vote := &types.Vote{
|
vote := &types.Vote{
|
||||||
ValidatorIndex: vs.Index,
|
ValidatorIndex: vs.Index,
|
||||||
ValidatorAddress: vs.PrivValidator.GetAddress(),
|
ValidatorAddress: addr,
|
||||||
Height: vs.Height,
|
Height: vs.Height,
|
||||||
Round: vs.Round,
|
Round: vs.Round,
|
||||||
Timestamp: tmtime.Now(),
|
Timestamp: tmtime.Now(),
|
||||||
@@ -151,8 +151,9 @@ func signAddVotes(to *ConsensusState, voteType types.SignedMsgType, hash []byte,
|
|||||||
|
|
||||||
func validatePrevote(t *testing.T, cs *ConsensusState, round int, privVal *validatorStub, blockHash []byte) {
|
func validatePrevote(t *testing.T, cs *ConsensusState, round int, privVal *validatorStub, blockHash []byte) {
|
||||||
prevotes := cs.Votes.Prevotes(round)
|
prevotes := cs.Votes.Prevotes(round)
|
||||||
|
address := privVal.GetPubKey().Address()
|
||||||
var vote *types.Vote
|
var vote *types.Vote
|
||||||
if vote = prevotes.GetByAddress(privVal.GetAddress()); vote == nil {
|
if vote = prevotes.GetByAddress(address); vote == nil {
|
||||||
panic("Failed to find prevote from validator")
|
panic("Failed to find prevote from validator")
|
||||||
}
|
}
|
||||||
if blockHash == nil {
|
if blockHash == nil {
|
||||||
@@ -168,8 +169,9 @@ func validatePrevote(t *testing.T, cs *ConsensusState, round int, privVal *valid
|
|||||||
|
|
||||||
func validateLastPrecommit(t *testing.T, cs *ConsensusState, privVal *validatorStub, blockHash []byte) {
|
func validateLastPrecommit(t *testing.T, cs *ConsensusState, privVal *validatorStub, blockHash []byte) {
|
||||||
votes := cs.LastCommit
|
votes := cs.LastCommit
|
||||||
|
address := privVal.GetPubKey().Address()
|
||||||
var vote *types.Vote
|
var vote *types.Vote
|
||||||
if vote = votes.GetByAddress(privVal.GetAddress()); vote == nil {
|
if vote = votes.GetByAddress(address); vote == nil {
|
||||||
panic("Failed to find precommit from validator")
|
panic("Failed to find precommit from validator")
|
||||||
}
|
}
|
||||||
if !bytes.Equal(vote.BlockID.Hash, blockHash) {
|
if !bytes.Equal(vote.BlockID.Hash, blockHash) {
|
||||||
@@ -179,8 +181,9 @@ func validateLastPrecommit(t *testing.T, cs *ConsensusState, privVal *validatorS
|
|||||||
|
|
||||||
func validatePrecommit(t *testing.T, cs *ConsensusState, thisRound, lockRound int, privVal *validatorStub, votedBlockHash, lockedBlockHash []byte) {
|
func validatePrecommit(t *testing.T, cs *ConsensusState, thisRound, lockRound int, privVal *validatorStub, votedBlockHash, lockedBlockHash []byte) {
|
||||||
precommits := cs.Votes.Precommits(thisRound)
|
precommits := cs.Votes.Precommits(thisRound)
|
||||||
|
address := privVal.GetPubKey().Address()
|
||||||
var vote *types.Vote
|
var vote *types.Vote
|
||||||
if vote = precommits.GetByAddress(privVal.GetAddress()); vote == nil {
|
if vote = precommits.GetByAddress(address); vote == nil {
|
||||||
panic("Failed to find precommit from validator")
|
panic("Failed to find precommit from validator")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,9 +284,10 @@ func newConsensusStateWithConfigAndBlockStore(thisConfig *cfg.Config, state sm.S
|
|||||||
}
|
}
|
||||||
|
|
||||||
func loadPrivValidator(config *cfg.Config) *privval.FilePV {
|
func loadPrivValidator(config *cfg.Config) *privval.FilePV {
|
||||||
privValidatorFile := config.PrivValidatorFile()
|
privValidatorKeyFile := config.PrivValidatorKeyFile()
|
||||||
ensureDir(path.Dir(privValidatorFile), 0700)
|
ensureDir(filepath.Dir(privValidatorKeyFile), 0700)
|
||||||
privValidator := privval.LoadOrGenFilePV(privValidatorFile)
|
privValidatorStateFile := config.PrivValidatorStateFile()
|
||||||
|
privValidator := privval.LoadOrGenFilePV(privValidatorKeyFile, privValidatorStateFile)
|
||||||
privValidator.Reset()
|
privValidator.Reset()
|
||||||
return privValidator
|
return privValidator
|
||||||
}
|
}
|
||||||
@@ -591,7 +595,7 @@ func randConsensusNet(nValidators int, testName string, tickerFunc func() Timeou
|
|||||||
for _, opt := range configOpts {
|
for _, opt := range configOpts {
|
||||||
opt(thisConfig)
|
opt(thisConfig)
|
||||||
}
|
}
|
||||||
ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
|
ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
|
||||||
app := appFunc()
|
app := appFunc()
|
||||||
vals := types.TM2PB.ValidatorUpdates(state.Validators)
|
vals := types.TM2PB.ValidatorUpdates(state.Validators)
|
||||||
app.InitChain(abci.RequestInitChain{Validators: vals})
|
app.InitChain(abci.RequestInitChain{Validators: vals})
|
||||||
@@ -612,16 +616,21 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF
|
|||||||
stateDB := dbm.NewMemDB() // each state needs its own db
|
stateDB := dbm.NewMemDB() // each state needs its own db
|
||||||
state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
|
state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
|
||||||
thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
|
thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
|
||||||
ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
|
ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
|
||||||
var privVal types.PrivValidator
|
var privVal types.PrivValidator
|
||||||
if i < nValidators {
|
if i < nValidators {
|
||||||
privVal = privVals[i]
|
privVal = privVals[i]
|
||||||
} else {
|
} else {
|
||||||
tempFile, err := ioutil.TempFile("", "priv_validator_")
|
tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
privVal = privval.GenFilePV(tempFile.Name())
|
tempStateFile, err := ioutil.TempFile("", "priv_validator_state_")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
privVal = privval.GenFilePV(tempKeyFile.Name(), tempStateFile.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
app := appFunc()
|
app := appFunc()
|
||||||
|
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/tendermint/tendermint/abci/example/code"
|
"github.com/tendermint/tendermint/abci/example/code"
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
sm "github.com/tendermint/tendermint/state"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,12 +18,17 @@ func init() {
|
|||||||
config = ResetConfig("consensus_mempool_test")
|
config = ResetConfig("consensus_mempool_test")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for testing
|
||||||
|
func assertMempool(txn txNotifier) sm.Mempool {
|
||||||
|
return txn.(sm.Mempool)
|
||||||
|
}
|
||||||
|
|
||||||
func TestMempoolNoProgressUntilTxsAvailable(t *testing.T) {
|
func TestMempoolNoProgressUntilTxsAvailable(t *testing.T) {
|
||||||
config := ResetConfig("consensus_mempool_txs_available_test")
|
config := ResetConfig("consensus_mempool_txs_available_test")
|
||||||
config.Consensus.CreateEmptyBlocks = false
|
config.Consensus.CreateEmptyBlocks = false
|
||||||
state, privVals := randGenesisState(1, false, 10)
|
state, privVals := randGenesisState(1, false, 10)
|
||||||
cs := newConsensusStateWithConfig(config, state, privVals[0], NewCounterApplication())
|
cs := newConsensusStateWithConfig(config, state, privVals[0], NewCounterApplication())
|
||||||
cs.mempool.EnableTxsAvailable()
|
assertMempool(cs.txNotifier).EnableTxsAvailable()
|
||||||
height, round := cs.Height, cs.Round
|
height, round := cs.Height, cs.Round
|
||||||
newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock)
|
newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock)
|
||||||
startTestRound(cs, height, round)
|
startTestRound(cs, height, round)
|
||||||
@@ -40,7 +46,7 @@ func TestMempoolProgressAfterCreateEmptyBlocksInterval(t *testing.T) {
|
|||||||
config.Consensus.CreateEmptyBlocksInterval = ensureTimeout
|
config.Consensus.CreateEmptyBlocksInterval = ensureTimeout
|
||||||
state, privVals := randGenesisState(1, false, 10)
|
state, privVals := randGenesisState(1, false, 10)
|
||||||
cs := newConsensusStateWithConfig(config, state, privVals[0], NewCounterApplication())
|
cs := newConsensusStateWithConfig(config, state, privVals[0], NewCounterApplication())
|
||||||
cs.mempool.EnableTxsAvailable()
|
assertMempool(cs.txNotifier).EnableTxsAvailable()
|
||||||
height, round := cs.Height, cs.Round
|
height, round := cs.Height, cs.Round
|
||||||
newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock)
|
newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock)
|
||||||
startTestRound(cs, height, round)
|
startTestRound(cs, height, round)
|
||||||
@@ -55,7 +61,7 @@ func TestMempoolProgressInHigherRound(t *testing.T) {
|
|||||||
config.Consensus.CreateEmptyBlocks = false
|
config.Consensus.CreateEmptyBlocks = false
|
||||||
state, privVals := randGenesisState(1, false, 10)
|
state, privVals := randGenesisState(1, false, 10)
|
||||||
cs := newConsensusStateWithConfig(config, state, privVals[0], NewCounterApplication())
|
cs := newConsensusStateWithConfig(config, state, privVals[0], NewCounterApplication())
|
||||||
cs.mempool.EnableTxsAvailable()
|
assertMempool(cs.txNotifier).EnableTxsAvailable()
|
||||||
height, round := cs.Height, cs.Round
|
height, round := cs.Height, cs.Round
|
||||||
newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock)
|
newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock)
|
||||||
newRoundCh := subscribe(cs.eventBus, types.EventQueryNewRound)
|
newRoundCh := subscribe(cs.eventBus, types.EventQueryNewRound)
|
||||||
@@ -91,7 +97,7 @@ func deliverTxsRange(cs *ConsensusState, start, end int) {
|
|||||||
for i := start; i < end; i++ {
|
for i := start; i < end; i++ {
|
||||||
txBytes := make([]byte, 8)
|
txBytes := make([]byte, 8)
|
||||||
binary.BigEndian.PutUint64(txBytes, uint64(i))
|
binary.BigEndian.PutUint64(txBytes, uint64(i))
|
||||||
err := cs.mempool.CheckTx(txBytes, nil)
|
err := assertMempool(cs.txNotifier).CheckTx(txBytes, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("Error after CheckTx: %v", err))
|
panic(fmt.Sprintf("Error after CheckTx: %v", err))
|
||||||
}
|
}
|
||||||
@@ -141,7 +147,7 @@ func TestMempoolRmBadTx(t *testing.T) {
|
|||||||
// Try to send the tx through the mempool.
|
// Try to send the tx through the mempool.
|
||||||
// CheckTx should not err, but the app should return a bad abci code
|
// CheckTx should not err, but the app should return a bad abci code
|
||||||
// and the tx should get removed from the pool
|
// and the tx should get removed from the pool
|
||||||
err := cs.mempool.CheckTx(txBytes, func(r *abci.Response) {
|
err := assertMempool(cs.txNotifier).CheckTx(txBytes, func(r *abci.Response) {
|
||||||
if r.GetCheckTx().Code != code.CodeTypeBadNonce {
|
if r.GetCheckTx().Code != code.CodeTypeBadNonce {
|
||||||
t.Fatalf("expected checktx to return bad nonce, got %v", r)
|
t.Fatalf("expected checktx to return bad nonce, got %v", r)
|
||||||
}
|
}
|
||||||
@@ -153,7 +159,7 @@ func TestMempoolRmBadTx(t *testing.T) {
|
|||||||
|
|
||||||
// check for the tx
|
// check for the tx
|
||||||
for {
|
for {
|
||||||
txs := cs.mempool.ReapMaxBytesMaxGas(int64(len(txBytes)), -1)
|
txs := assertMempool(cs.txNotifier).ReapMaxBytesMaxGas(int64(len(txBytes)), -1)
|
||||||
if len(txs) == 0 {
|
if len(txs) == 0 {
|
||||||
emptyMempoolCh <- struct{}{}
|
emptyMempoolCh <- struct{}{}
|
||||||
return
|
return
|
||||||
|
@@ -8,7 +8,11 @@ import (
|
|||||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const MetricsSubsystem = "consensus"
|
const (
|
||||||
|
// MetricsSubsystem is a subsystem shared by all metrics exposed by this
|
||||||
|
// package.
|
||||||
|
MetricsSubsystem = "consensus"
|
||||||
|
)
|
||||||
|
|
||||||
// Metrics contains metrics exposed by this package.
|
// Metrics contains metrics exposed by this package.
|
||||||
type Metrics struct {
|
type Metrics struct {
|
||||||
@@ -50,101 +54,107 @@ type Metrics struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PrometheusMetrics returns Metrics build using Prometheus client library.
|
// PrometheusMetrics returns Metrics build using Prometheus client library.
|
||||||
func PrometheusMetrics(namespace string) *Metrics {
|
// Optionally, labels can be provided along with their values ("foo",
|
||||||
|
// "fooValue").
|
||||||
|
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||||
|
labels := []string{}
|
||||||
|
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||||
|
labels = append(labels, labelsAndValues[i])
|
||||||
|
}
|
||||||
return &Metrics{
|
return &Metrics{
|
||||||
Height: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
Height: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: MetricsSubsystem,
|
Subsystem: MetricsSubsystem,
|
||||||
Name: "height",
|
Name: "height",
|
||||||
Help: "Height of the chain.",
|
Help: "Height of the chain.",
|
||||||
}, []string{}),
|
}, labels).With(labelsAndValues...),
|
||||||
Rounds: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
Rounds: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: MetricsSubsystem,
|
Subsystem: MetricsSubsystem,
|
||||||
Name: "rounds",
|
Name: "rounds",
|
||||||
Help: "Number of rounds.",
|
Help: "Number of rounds.",
|
||||||
}, []string{}),
|
}, labels).With(labelsAndValues...),
|
||||||
|
|
||||||
Validators: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
Validators: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: MetricsSubsystem,
|
Subsystem: MetricsSubsystem,
|
||||||
Name: "validators",
|
Name: "validators",
|
||||||
Help: "Number of validators.",
|
Help: "Number of validators.",
|
||||||
}, []string{}),
|
}, labels).With(labelsAndValues...),
|
||||||
ValidatorsPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
ValidatorsPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: MetricsSubsystem,
|
Subsystem: MetricsSubsystem,
|
||||||
Name: "validators_power",
|
Name: "validators_power",
|
||||||
Help: "Total power of all validators.",
|
Help: "Total power of all validators.",
|
||||||
}, []string{}),
|
}, labels).With(labelsAndValues...),
|
||||||
MissingValidators: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
MissingValidators: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: MetricsSubsystem,
|
Subsystem: MetricsSubsystem,
|
||||||
Name: "missing_validators",
|
Name: "missing_validators",
|
||||||
Help: "Number of validators who did not sign.",
|
Help: "Number of validators who did not sign.",
|
||||||
}, []string{}),
|
}, labels).With(labelsAndValues...),
|
||||||
MissingValidatorsPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
MissingValidatorsPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: MetricsSubsystem,
|
Subsystem: MetricsSubsystem,
|
||||||
Name: "missing_validators_power",
|
Name: "missing_validators_power",
|
||||||
Help: "Total power of the missing validators.",
|
Help: "Total power of the missing validators.",
|
||||||
}, []string{}),
|
}, labels).With(labelsAndValues...),
|
||||||
ByzantineValidators: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
ByzantineValidators: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: MetricsSubsystem,
|
Subsystem: MetricsSubsystem,
|
||||||
Name: "byzantine_validators",
|
Name: "byzantine_validators",
|
||||||
Help: "Number of validators who tried to double sign.",
|
Help: "Number of validators who tried to double sign.",
|
||||||
}, []string{}),
|
}, labels).With(labelsAndValues...),
|
||||||
ByzantineValidatorsPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
ByzantineValidatorsPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: MetricsSubsystem,
|
Subsystem: MetricsSubsystem,
|
||||||
Name: "byzantine_validators_power",
|
Name: "byzantine_validators_power",
|
||||||
Help: "Total power of the byzantine validators.",
|
Help: "Total power of the byzantine validators.",
|
||||||
}, []string{}),
|
}, labels).With(labelsAndValues...),
|
||||||
|
|
||||||
BlockIntervalSeconds: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
BlockIntervalSeconds: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: MetricsSubsystem,
|
Subsystem: MetricsSubsystem,
|
||||||
Name: "block_interval_seconds",
|
Name: "block_interval_seconds",
|
||||||
Help: "Time between this and the last block.",
|
Help: "Time between this and the last block.",
|
||||||
}, []string{}),
|
}, labels).With(labelsAndValues...),
|
||||||
|
|
||||||
NumTxs: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
NumTxs: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: MetricsSubsystem,
|
Subsystem: MetricsSubsystem,
|
||||||
Name: "num_txs",
|
Name: "num_txs",
|
||||||
Help: "Number of transactions.",
|
Help: "Number of transactions.",
|
||||||
}, []string{}),
|
}, labels).With(labelsAndValues...),
|
||||||
BlockSizeBytes: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
BlockSizeBytes: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: MetricsSubsystem,
|
Subsystem: MetricsSubsystem,
|
||||||
Name: "block_size_bytes",
|
Name: "block_size_bytes",
|
||||||
Help: "Size of the block.",
|
Help: "Size of the block.",
|
||||||
}, []string{}),
|
}, labels).With(labelsAndValues...),
|
||||||
TotalTxs: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
TotalTxs: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: MetricsSubsystem,
|
Subsystem: MetricsSubsystem,
|
||||||
Name: "total_txs",
|
Name: "total_txs",
|
||||||
Help: "Total number of transactions.",
|
Help: "Total number of transactions.",
|
||||||
}, []string{}),
|
}, labels).With(labelsAndValues...),
|
||||||
CommittedHeight: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
CommittedHeight: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: MetricsSubsystem,
|
Subsystem: MetricsSubsystem,
|
||||||
Name: "latest_block_height",
|
Name: "latest_block_height",
|
||||||
Help: "The latest block height.",
|
Help: "The latest block height.",
|
||||||
}, []string{}),
|
}, labels).With(labelsAndValues...),
|
||||||
FastSyncing: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
FastSyncing: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: MetricsSubsystem,
|
Subsystem: MetricsSubsystem,
|
||||||
Name: "fast_syncing",
|
Name: "fast_syncing",
|
||||||
Help: "Whether or not a node is fast syncing. 1 if yes, 0 if no.",
|
Help: "Whether or not a node is fast syncing. 1 if yes, 0 if no.",
|
||||||
}, []string{}),
|
}, labels).With(labelsAndValues...),
|
||||||
BlockParts: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
BlockParts: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: MetricsSubsystem,
|
Subsystem: MetricsSubsystem,
|
||||||
Name: "block_parts",
|
Name: "block_parts",
|
||||||
Help: "Number of blockparts transmitted by peer.",
|
Help: "Number of blockparts transmitted by peer.",
|
||||||
}, []string{"peer_id"}),
|
}, append(labels, "peer_id")).With(labelsAndValues...),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -143,7 +143,8 @@ func TestReactorWithEvidence(t *testing.T) {
|
|||||||
// mock the evidence pool
|
// mock the evidence pool
|
||||||
// everyone includes evidence of another double signing
|
// everyone includes evidence of another double signing
|
||||||
vIdx := (i + 1) % nValidators
|
vIdx := (i + 1) % nValidators
|
||||||
evpool := newMockEvidencePool(privVals[vIdx].GetAddress())
|
addr := privVals[vIdx].GetPubKey().Address()
|
||||||
|
evpool := newMockEvidencePool(addr)
|
||||||
|
|
||||||
// Make ConsensusState
|
// Make ConsensusState
|
||||||
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyAppConnCon, mempool, evpool)
|
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyAppConnCon, mempool, evpool)
|
||||||
@@ -224,7 +225,7 @@ func TestReactorCreatesBlockWhenEmptyBlocksFalse(t *testing.T) {
|
|||||||
defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses)
|
defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses)
|
||||||
|
|
||||||
// send a tx
|
// send a tx
|
||||||
if err := css[3].mempool.CheckTx([]byte{1, 2, 3}, nil); err != nil {
|
if err := assertMempool(css[3].txNotifier).CheckTx([]byte{1, 2, 3}, nil); err != nil {
|
||||||
//t.Fatal(err)
|
//t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,7 +269,8 @@ func TestReactorVotingPowerChange(t *testing.T) {
|
|||||||
// map of active validators
|
// map of active validators
|
||||||
activeVals := make(map[string]struct{})
|
activeVals := make(map[string]struct{})
|
||||||
for i := 0; i < nVals; i++ {
|
for i := 0; i < nVals; i++ {
|
||||||
activeVals[string(css[i].privValidator.GetAddress())] = struct{}{}
|
addr := css[i].privValidator.GetPubKey().Address()
|
||||||
|
activeVals[string(addr)] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait till everyone makes block 1
|
// wait till everyone makes block 1
|
||||||
@@ -331,7 +333,8 @@ func TestReactorValidatorSetChanges(t *testing.T) {
|
|||||||
// map of active validators
|
// map of active validators
|
||||||
activeVals := make(map[string]struct{})
|
activeVals := make(map[string]struct{})
|
||||||
for i := 0; i < nVals; i++ {
|
for i := 0; i < nVals; i++ {
|
||||||
activeVals[string(css[i].privValidator.GetAddress())] = struct{}{}
|
addr := css[i].privValidator.GetPubKey().Address()
|
||||||
|
activeVals[string(addr)] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait till everyone makes block 1
|
// wait till everyone makes block 1
|
||||||
@@ -445,7 +448,7 @@ func waitForAndValidateBlock(t *testing.T, n int, activeVals map[string]struct{}
|
|||||||
err := validateBlock(newBlock, activeVals)
|
err := validateBlock(newBlock, activeVals)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
for _, tx := range txs {
|
for _, tx := range txs {
|
||||||
err := css[j].mempool.CheckTx(tx, nil)
|
err := assertMempool(css[j].txNotifier).CheckTx(tx, nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
}
|
}
|
||||||
}, css)
|
}, css)
|
||||||
|
@@ -11,7 +11,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
"github.com/tendermint/tendermint/version"
|
|
||||||
//auto "github.com/tendermint/tendermint/libs/autofile"
|
//auto "github.com/tendermint/tendermint/libs/autofile"
|
||||||
cmn "github.com/tendermint/tendermint/libs/common"
|
cmn "github.com/tendermint/tendermint/libs/common"
|
||||||
dbm "github.com/tendermint/tendermint/libs/db"
|
dbm "github.com/tendermint/tendermint/libs/db"
|
||||||
@@ -20,6 +19,7 @@ import (
|
|||||||
"github.com/tendermint/tendermint/proxy"
|
"github.com/tendermint/tendermint/proxy"
|
||||||
sm "github.com/tendermint/tendermint/state"
|
sm "github.com/tendermint/tendermint/state"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
|
"github.com/tendermint/tendermint/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
var crc32c = crc32.MakeTable(crc32.Castagnoli)
|
var crc32c = crc32.MakeTable(crc32.Castagnoli)
|
||||||
@@ -247,6 +247,7 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error {
|
|||||||
|
|
||||||
// Set AppVersion on the state.
|
// Set AppVersion on the state.
|
||||||
h.initialState.Version.Consensus.App = version.Protocol(res.AppVersion)
|
h.initialState.Version.Consensus.App = version.Protocol(res.AppVersion)
|
||||||
|
sm.SaveState(h.stateDB, h.initialState)
|
||||||
|
|
||||||
// Replay blocks up to the latest in the blockstore.
|
// Replay blocks up to the latest in the blockstore.
|
||||||
_, err = h.ReplayBlocks(h.initialState, appHash, blockHeight, proxyApp)
|
_, err = h.ReplayBlocks(h.initialState, appHash, blockHeight, proxyApp)
|
||||||
@@ -295,19 +296,27 @@ func (h *Handshaker) ReplayBlocks(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the app returned validators or consensus params, update the state.
|
if stateBlockHeight == 0 { //we only update state when we are in initial state
|
||||||
if len(res.Validators) > 0 {
|
// If the app returned validators or consensus params, update the state.
|
||||||
vals, err := types.PB2TM.ValidatorUpdates(res.Validators)
|
if len(res.Validators) > 0 {
|
||||||
if err != nil {
|
vals, err := types.PB2TM.ValidatorUpdates(res.Validators)
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
state.Validators = types.NewValidatorSet(vals)
|
||||||
|
state.NextValidators = types.NewValidatorSet(vals)
|
||||||
|
} else {
|
||||||
|
// If validator set is not set in genesis and still empty after InitChain, exit.
|
||||||
|
if len(h.genDoc.Validators) == 0 {
|
||||||
|
return nil, fmt.Errorf("Validator set is nil in genesis and still empty after InitChain")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
state.Validators = types.NewValidatorSet(vals)
|
|
||||||
state.NextValidators = types.NewValidatorSet(vals)
|
if res.ConsensusParams != nil {
|
||||||
|
state.ConsensusParams = types.PB2TM.ConsensusParams(res.ConsensusParams)
|
||||||
|
}
|
||||||
|
sm.SaveState(h.stateDB, state)
|
||||||
}
|
}
|
||||||
if res.ConsensusParams != nil {
|
|
||||||
state.ConsensusParams = types.PB2TM.ConsensusParams(res.ConsensusParams)
|
|
||||||
}
|
|
||||||
sm.SaveState(h.stateDB, state)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// First handle edge cases and constraints on the storeBlockHeight.
|
// First handle edge cases and constraints on the storeBlockHeight.
|
||||||
|
@@ -137,7 +137,7 @@ func (pb *playback) replayReset(count int, newStepCh chan interface{}) error {
|
|||||||
pb.cs.Wait()
|
pb.cs.Wait()
|
||||||
|
|
||||||
newCS := NewConsensusState(pb.cs.config, pb.genesisState.Copy(), pb.cs.blockExec,
|
newCS := NewConsensusState(pb.cs.config, pb.genesisState.Copy(), pb.cs.blockExec,
|
||||||
pb.cs.blockStore, pb.cs.mempool, pb.cs.evpool)
|
pb.cs.blockStore, pb.cs.txNotifier, pb.cs.evpool)
|
||||||
newCS.SetEventBus(pb.cs.eventBus)
|
newCS.SetEventBus(pb.cs.eventBus)
|
||||||
newCS.startForReplay()
|
newCS.startForReplay()
|
||||||
|
|
||||||
|
@@ -87,7 +87,7 @@ func sendTxs(cs *ConsensusState, ctx context.Context) {
|
|||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
tx := []byte{byte(i)}
|
tx := []byte{byte(i)}
|
||||||
cs.mempool.CheckTx(tx, nil)
|
assertMempool(cs.txNotifier).CheckTx(tx, nil)
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -319,7 +319,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
|
|||||||
walFile := tempWALWithData(walBody)
|
walFile := tempWALWithData(walBody)
|
||||||
config.Consensus.SetWalFile(walFile)
|
config.Consensus.SetWalFile(walFile)
|
||||||
|
|
||||||
privVal := privval.LoadFilePV(config.PrivValidatorFile())
|
privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile())
|
||||||
|
|
||||||
wal, err := NewWAL(walFile)
|
wal, err := NewWAL(walFile)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -633,7 +633,7 @@ func TestInitChainUpdateValidators(t *testing.T) {
|
|||||||
clientCreator := proxy.NewLocalClientCreator(app)
|
clientCreator := proxy.NewLocalClientCreator(app)
|
||||||
|
|
||||||
config := ResetConfig("proxy_test_")
|
config := ResetConfig("proxy_test_")
|
||||||
privVal := privval.LoadFilePV(config.PrivValidatorFile())
|
privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile())
|
||||||
stateDB, state, store := stateAndStore(config, privVal.GetPubKey(), 0x0)
|
stateDB, state, store := stateAndStore(config, privVal.GetPubKey(), 0x0)
|
||||||
|
|
||||||
oldValAddr := state.Validators.Validators[0].Address
|
oldValAddr := state.Validators.Validators[0].Address
|
||||||
@@ -659,12 +659,6 @@ func TestInitChainUpdateValidators(t *testing.T) {
|
|||||||
assert.Equal(t, newValAddr, expectValAddr)
|
assert.Equal(t, newValAddr, expectValAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newInitChainApp(vals []abci.ValidatorUpdate) *initChainApp {
|
|
||||||
return &initChainApp{
|
|
||||||
vals: vals,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns the vals on InitChain
|
// returns the vals on InitChain
|
||||||
type initChainApp struct {
|
type initChainApp struct {
|
||||||
abci.BaseApplication
|
abci.BaseApplication
|
||||||
|
@@ -2,13 +2,14 @@ package consensus
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
cmn "github.com/tendermint/tendermint/libs/common"
|
cmn "github.com/tendermint/tendermint/libs/common"
|
||||||
"github.com/tendermint/tendermint/libs/fail"
|
"github.com/tendermint/tendermint/libs/fail"
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
@@ -56,6 +57,16 @@ func (ti *timeoutInfo) String() string {
|
|||||||
return fmt.Sprintf("%v ; %d/%d %v", ti.Duration, ti.Height, ti.Round, ti.Step)
|
return fmt.Sprintf("%v ; %d/%d %v", ti.Duration, ti.Height, ti.Round, ti.Step)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// interface to the mempool
|
||||||
|
type txNotifier interface {
|
||||||
|
TxsAvailable() <-chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// interface to the evidence pool
|
||||||
|
type evidencePool interface {
|
||||||
|
AddEvidence(types.Evidence) error
|
||||||
|
}
|
||||||
|
|
||||||
// ConsensusState handles execution of the consensus algorithm.
|
// ConsensusState handles execution of the consensus algorithm.
|
||||||
// It processes votes and proposals, and upon reaching agreement,
|
// It processes votes and proposals, and upon reaching agreement,
|
||||||
// commits blocks to the chain and executes them against the application.
|
// commits blocks to the chain and executes them against the application.
|
||||||
@@ -67,11 +78,18 @@ type ConsensusState struct {
|
|||||||
config *cfg.ConsensusConfig
|
config *cfg.ConsensusConfig
|
||||||
privValidator types.PrivValidator // for signing votes
|
privValidator types.PrivValidator // for signing votes
|
||||||
|
|
||||||
// services for creating and executing blocks
|
// store blocks and commits
|
||||||
blockExec *sm.BlockExecutor
|
|
||||||
blockStore sm.BlockStore
|
blockStore sm.BlockStore
|
||||||
mempool sm.Mempool
|
|
||||||
evpool sm.EvidencePool
|
// create and execute blocks
|
||||||
|
blockExec *sm.BlockExecutor
|
||||||
|
|
||||||
|
// notify us if txs are available
|
||||||
|
txNotifier txNotifier
|
||||||
|
|
||||||
|
// add evidence to the pool
|
||||||
|
// when it's detected
|
||||||
|
evpool evidencePool
|
||||||
|
|
||||||
// internal state
|
// internal state
|
||||||
mtx sync.RWMutex
|
mtx sync.RWMutex
|
||||||
@@ -127,15 +145,15 @@ func NewConsensusState(
|
|||||||
state sm.State,
|
state sm.State,
|
||||||
blockExec *sm.BlockExecutor,
|
blockExec *sm.BlockExecutor,
|
||||||
blockStore sm.BlockStore,
|
blockStore sm.BlockStore,
|
||||||
mempool sm.Mempool,
|
txNotifier txNotifier,
|
||||||
evpool sm.EvidencePool,
|
evpool evidencePool,
|
||||||
options ...StateOption,
|
options ...StateOption,
|
||||||
) *ConsensusState {
|
) *ConsensusState {
|
||||||
cs := &ConsensusState{
|
cs := &ConsensusState{
|
||||||
config: config,
|
config: config,
|
||||||
blockExec: blockExec,
|
blockExec: blockExec,
|
||||||
blockStore: blockStore,
|
blockStore: blockStore,
|
||||||
mempool: mempool,
|
txNotifier: txNotifier,
|
||||||
peerMsgQueue: make(chan msgInfo, msgQueueSize),
|
peerMsgQueue: make(chan msgInfo, msgQueueSize),
|
||||||
internalMsgQueue: make(chan msgInfo, msgQueueSize),
|
internalMsgQueue: make(chan msgInfo, msgQueueSize),
|
||||||
timeoutTicker: NewTimeoutTicker(),
|
timeoutTicker: NewTimeoutTicker(),
|
||||||
@@ -483,7 +501,7 @@ func (cs *ConsensusState) updateToState(state sm.State) {
|
|||||||
// If state isn't further out than cs.state, just ignore.
|
// If state isn't further out than cs.state, just ignore.
|
||||||
// This happens when SwitchToConsensus() is called in the reactor.
|
// This happens when SwitchToConsensus() is called in the reactor.
|
||||||
// We don't want to reset e.g. the Votes, but we still want to
|
// We don't want to reset e.g. the Votes, but we still want to
|
||||||
// signal the new round step, because other services (eg. mempool)
|
// signal the new round step, because other services (eg. txNotifier)
|
||||||
// depend on having an up-to-date peer state!
|
// depend on having an up-to-date peer state!
|
||||||
if !cs.state.IsEmpty() && (state.LastBlockHeight <= cs.state.LastBlockHeight) {
|
if !cs.state.IsEmpty() && (state.LastBlockHeight <= cs.state.LastBlockHeight) {
|
||||||
cs.Logger.Info("Ignoring updateToState()", "newHeight", state.LastBlockHeight+1, "oldHeight", cs.state.LastBlockHeight+1)
|
cs.Logger.Info("Ignoring updateToState()", "newHeight", state.LastBlockHeight+1, "oldHeight", cs.state.LastBlockHeight+1)
|
||||||
@@ -598,7 +616,7 @@ func (cs *ConsensusState) receiveRoutine(maxSteps int) {
|
|||||||
var mi msgInfo
|
var mi msgInfo
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-cs.mempool.TxsAvailable():
|
case <-cs.txNotifier.TxsAvailable():
|
||||||
cs.handleTxsAvailable()
|
cs.handleTxsAvailable()
|
||||||
case mi = <-cs.peerMsgQueue:
|
case mi = <-cs.peerMsgQueue:
|
||||||
cs.wal.Write(mi)
|
cs.wal.Write(mi)
|
||||||
@@ -829,13 +847,14 @@ func (cs *ConsensusState) enterPropose(height int64, round int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if not a validator, we're done
|
// if not a validator, we're done
|
||||||
if !cs.Validators.HasAddress(cs.privValidator.GetAddress()) {
|
address := cs.privValidator.GetPubKey().Address()
|
||||||
logger.Debug("This node is not a validator", "addr", cs.privValidator.GetAddress(), "vals", cs.Validators)
|
if !cs.Validators.HasAddress(address) {
|
||||||
|
logger.Debug("This node is not a validator", "addr", address, "vals", cs.Validators)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
logger.Debug("This node is a validator")
|
logger.Debug("This node is a validator")
|
||||||
|
|
||||||
if cs.isProposer() {
|
if cs.isProposer(address) {
|
||||||
logger.Info("enterPropose: Our turn to propose", "proposer", cs.Validators.GetProposer().Address, "privValidator", cs.privValidator)
|
logger.Info("enterPropose: Our turn to propose", "proposer", cs.Validators.GetProposer().Address, "privValidator", cs.privValidator)
|
||||||
cs.decideProposal(height, round)
|
cs.decideProposal(height, round)
|
||||||
} else {
|
} else {
|
||||||
@@ -843,8 +862,8 @@ func (cs *ConsensusState) enterPropose(height int64, round int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *ConsensusState) isProposer() bool {
|
func (cs *ConsensusState) isProposer(address []byte) bool {
|
||||||
return bytes.Equal(cs.Validators.GetProposer().Address, cs.privValidator.GetAddress())
|
return bytes.Equal(cs.Validators.GetProposer().Address, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *ConsensusState) defaultDecideProposal(height int64, round int) {
|
func (cs *ConsensusState) defaultDecideProposal(height int64, round int) {
|
||||||
@@ -919,20 +938,8 @@ func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
maxBytes := cs.state.ConsensusParams.BlockSize.MaxBytes
|
proposerAddr := cs.privValidator.GetPubKey().Address()
|
||||||
maxGas := cs.state.ConsensusParams.BlockSize.MaxGas
|
return cs.blockExec.CreateProposalBlock(cs.Height, cs.state, commit, proposerAddr)
|
||||||
// bound evidence to 1/10th of the block
|
|
||||||
evidence := cs.evpool.PendingEvidence(types.MaxEvidenceBytesPerBlock(maxBytes))
|
|
||||||
// Mempool validated transactions
|
|
||||||
txs := cs.mempool.ReapMaxBytesMaxGas(types.MaxDataBytes(
|
|
||||||
maxBytes,
|
|
||||||
cs.state.Validators.Size(),
|
|
||||||
len(evidence),
|
|
||||||
), maxGas)
|
|
||||||
proposerAddr := cs.privValidator.GetAddress()
|
|
||||||
block, parts := cs.state.MakeBlock(cs.Height, txs, commit, evidence, proposerAddr)
|
|
||||||
|
|
||||||
return block, parts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enter: `timeoutPropose` after entering Propose.
|
// Enter: `timeoutPropose` after entering Propose.
|
||||||
@@ -1474,7 +1481,8 @@ func (cs *ConsensusState) tryAddVote(vote *types.Vote, peerID p2p.ID) (bool, err
|
|||||||
if err == ErrVoteHeightMismatch {
|
if err == ErrVoteHeightMismatch {
|
||||||
return added, err
|
return added, err
|
||||||
} else if voteErr, ok := err.(*types.ErrVoteConflictingVotes); ok {
|
} else if voteErr, ok := err.(*types.ErrVoteConflictingVotes); ok {
|
||||||
if bytes.Equal(vote.ValidatorAddress, cs.privValidator.GetAddress()) {
|
addr := cs.privValidator.GetPubKey().Address()
|
||||||
|
if bytes.Equal(vote.ValidatorAddress, addr) {
|
||||||
cs.Logger.Error("Found conflicting vote from ourselves. Did you unsafe_reset a validator?", "height", vote.Height, "round", vote.Round, "type", vote.Type)
|
cs.Logger.Error("Found conflicting vote from ourselves. Did you unsafe_reset a validator?", "height", vote.Height, "round", vote.Round, "type", vote.Type)
|
||||||
return added, err
|
return added, err
|
||||||
}
|
}
|
||||||
@@ -1526,7 +1534,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
|
|||||||
// Not necessarily a bad peer, but not favourable behaviour.
|
// Not necessarily a bad peer, but not favourable behaviour.
|
||||||
if vote.Height != cs.Height {
|
if vote.Height != cs.Height {
|
||||||
err = ErrVoteHeightMismatch
|
err = ErrVoteHeightMismatch
|
||||||
cs.Logger.Info("Vote ignored and not added", "voteHeight", vote.Height, "csHeight", cs.Height, "err", err)
|
cs.Logger.Info("Vote ignored and not added", "voteHeight", vote.Height, "csHeight", cs.Height, "peerID", peerID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1639,7 +1647,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cs *ConsensusState) signVote(type_ types.SignedMsgType, hash []byte, header types.PartSetHeader) (*types.Vote, error) {
|
func (cs *ConsensusState) signVote(type_ types.SignedMsgType, hash []byte, header types.PartSetHeader) (*types.Vote, error) {
|
||||||
addr := cs.privValidator.GetAddress()
|
addr := cs.privValidator.GetPubKey().Address()
|
||||||
valIndex, _ := cs.Validators.GetByAddress(addr)
|
valIndex, _ := cs.Validators.GetByAddress(addr)
|
||||||
|
|
||||||
vote := &types.Vote{
|
vote := &types.Vote{
|
||||||
@@ -1675,7 +1683,7 @@ func (cs *ConsensusState) voteTime() time.Time {
|
|||||||
// sign the vote and publish on internalMsgQueue
|
// sign the vote and publish on internalMsgQueue
|
||||||
func (cs *ConsensusState) signAddVote(type_ types.SignedMsgType, hash []byte, header types.PartSetHeader) *types.Vote {
|
func (cs *ConsensusState) signAddVote(type_ types.SignedMsgType, hash []byte, header types.PartSetHeader) *types.Vote {
|
||||||
// if we don't have a key or we're not in the validator set, do nothing
|
// if we don't have a key or we're not in the validator set, do nothing
|
||||||
if cs.privValidator == nil || !cs.Validators.HasAddress(cs.privValidator.GetAddress()) {
|
if cs.privValidator == nil || !cs.Validators.HasAddress(cs.privValidator.GetPubKey().Address()) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
vote, err := cs.signVote(type_, hash, header)
|
vote, err := cs.signVote(type_, hash, header)
|
||||||
|
@@ -73,7 +73,8 @@ func TestStateProposerSelection0(t *testing.T) {
|
|||||||
|
|
||||||
// Commit a block and ensure proposer for the next height is correct.
|
// Commit a block and ensure proposer for the next height is correct.
|
||||||
prop := cs1.GetRoundState().Validators.GetProposer()
|
prop := cs1.GetRoundState().Validators.GetProposer()
|
||||||
if !bytes.Equal(prop.Address, cs1.privValidator.GetAddress()) {
|
address := cs1.privValidator.GetPubKey().Address()
|
||||||
|
if !bytes.Equal(prop.Address, address) {
|
||||||
t.Fatalf("expected proposer to be validator %d. Got %X", 0, prop.Address)
|
t.Fatalf("expected proposer to be validator %d. Got %X", 0, prop.Address)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +88,8 @@ func TestStateProposerSelection0(t *testing.T) {
|
|||||||
ensureNewRound(newRoundCh, height+1, 0)
|
ensureNewRound(newRoundCh, height+1, 0)
|
||||||
|
|
||||||
prop = cs1.GetRoundState().Validators.GetProposer()
|
prop = cs1.GetRoundState().Validators.GetProposer()
|
||||||
if !bytes.Equal(prop.Address, vss[1].GetAddress()) {
|
addr := vss[1].GetPubKey().Address()
|
||||||
|
if !bytes.Equal(prop.Address, addr) {
|
||||||
panic(fmt.Sprintf("expected proposer to be validator %d. Got %X", 1, prop.Address))
|
panic(fmt.Sprintf("expected proposer to be validator %d. Got %X", 1, prop.Address))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,7 +112,8 @@ func TestStateProposerSelection2(t *testing.T) {
|
|||||||
// everyone just votes nil. we get a new proposer each round
|
// everyone just votes nil. we get a new proposer each round
|
||||||
for i := 0; i < len(vss); i++ {
|
for i := 0; i < len(vss); i++ {
|
||||||
prop := cs1.GetRoundState().Validators.GetProposer()
|
prop := cs1.GetRoundState().Validators.GetProposer()
|
||||||
correctProposer := vss[(i+round)%len(vss)].GetAddress()
|
addr := vss[(i+round)%len(vss)].GetPubKey().Address()
|
||||||
|
correctProposer := addr
|
||||||
if !bytes.Equal(prop.Address, correctProposer) {
|
if !bytes.Equal(prop.Address, correctProposer) {
|
||||||
panic(fmt.Sprintf("expected RoundState.Validators.GetProposer() to be validator %d. Got %X", (i+2)%len(vss), prop.Address))
|
panic(fmt.Sprintf("expected RoundState.Validators.GetProposer() to be validator %d. Got %X", (i+2)%len(vss), prop.Address))
|
||||||
}
|
}
|
||||||
@@ -505,7 +508,8 @@ func TestStateLockPOLRelock(t *testing.T) {
|
|||||||
|
|
||||||
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
||||||
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
|
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
|
||||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
addr := cs1.privValidator.GetPubKey().Address()
|
||||||
|
voteCh := subscribeToVoter(cs1, addr)
|
||||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||||
newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlockHeader)
|
newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlockHeader)
|
||||||
|
|
||||||
@@ -596,7 +600,8 @@ func TestStateLockPOLUnlock(t *testing.T) {
|
|||||||
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
||||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||||
unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock)
|
unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock)
|
||||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
addr := cs1.privValidator.GetPubKey().Address()
|
||||||
|
voteCh := subscribeToVoter(cs1, addr)
|
||||||
|
|
||||||
// everything done from perspective of cs1
|
// everything done from perspective of cs1
|
||||||
|
|
||||||
@@ -689,7 +694,8 @@ func TestStateLockPOLSafety1(t *testing.T) {
|
|||||||
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
|
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
|
||||||
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
||||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
addr := cs1.privValidator.GetPubKey().Address()
|
||||||
|
voteCh := subscribeToVoter(cs1, addr)
|
||||||
|
|
||||||
// start round and wait for propose and prevote
|
// start round and wait for propose and prevote
|
||||||
startTestRound(cs1, cs1.Height, round)
|
startTestRound(cs1, cs1.Height, round)
|
||||||
@@ -805,7 +811,8 @@ func TestStateLockPOLSafety2(t *testing.T) {
|
|||||||
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
||||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||||
unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock)
|
unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock)
|
||||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
addr := cs1.privValidator.GetPubKey().Address()
|
||||||
|
voteCh := subscribeToVoter(cs1, addr)
|
||||||
|
|
||||||
// the block for R0: gets polkad but we miss it
|
// the block for R0: gets polkad but we miss it
|
||||||
// (even though we signed it, shhh)
|
// (even though we signed it, shhh)
|
||||||
@@ -896,7 +903,8 @@ func TestProposeValidBlock(t *testing.T) {
|
|||||||
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
|
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
|
||||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||||
unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock)
|
unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock)
|
||||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
addr := cs1.privValidator.GetPubKey().Address()
|
||||||
|
voteCh := subscribeToVoter(cs1, addr)
|
||||||
|
|
||||||
// start round and wait for propose and prevote
|
// start round and wait for propose and prevote
|
||||||
startTestRound(cs1, cs1.Height, round)
|
startTestRound(cs1, cs1.Height, round)
|
||||||
@@ -982,7 +990,8 @@ func TestSetValidBlockOnDelayedPrevote(t *testing.T) {
|
|||||||
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
||||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||||
validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock)
|
validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock)
|
||||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
addr := cs1.privValidator.GetPubKey().Address()
|
||||||
|
voteCh := subscribeToVoter(cs1, addr)
|
||||||
|
|
||||||
// start round and wait for propose and prevote
|
// start round and wait for propose and prevote
|
||||||
startTestRound(cs1, cs1.Height, round)
|
startTestRound(cs1, cs1.Height, round)
|
||||||
@@ -1041,7 +1050,8 @@ func TestSetValidBlockOnDelayedProposal(t *testing.T) {
|
|||||||
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
|
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
|
||||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||||
validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock)
|
validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock)
|
||||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
addr := cs1.privValidator.GetPubKey().Address()
|
||||||
|
voteCh := subscribeToVoter(cs1, addr)
|
||||||
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
|
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
|
||||||
|
|
||||||
round = round + 1 // move to round in which P0 is not proposer
|
round = round + 1 // move to round in which P0 is not proposer
|
||||||
@@ -1111,7 +1121,8 @@ func TestWaitingTimeoutProposeOnNewRound(t *testing.T) {
|
|||||||
|
|
||||||
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
|
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
|
||||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
addr := cs1.privValidator.GetPubKey().Address()
|
||||||
|
voteCh := subscribeToVoter(cs1, addr)
|
||||||
|
|
||||||
// start round
|
// start round
|
||||||
startTestRound(cs1, height, round)
|
startTestRound(cs1, height, round)
|
||||||
@@ -1144,7 +1155,8 @@ func TestRoundSkipOnNilPolkaFromHigherRound(t *testing.T) {
|
|||||||
|
|
||||||
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
||||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
addr := cs1.privValidator.GetPubKey().Address()
|
||||||
|
voteCh := subscribeToVoter(cs1, addr)
|
||||||
|
|
||||||
// start round
|
// start round
|
||||||
startTestRound(cs1, height, round)
|
startTestRound(cs1, height, round)
|
||||||
@@ -1177,7 +1189,8 @@ func TestWaitTimeoutProposeOnNilPolkaForTheCurrentRound(t *testing.T) {
|
|||||||
|
|
||||||
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
|
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
|
||||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
addr := cs1.privValidator.GetPubKey().Address()
|
||||||
|
voteCh := subscribeToVoter(cs1, addr)
|
||||||
|
|
||||||
// start round in which PO is not proposer
|
// start round in which PO is not proposer
|
||||||
startTestRound(cs1, height, round)
|
startTestRound(cs1, height, round)
|
||||||
@@ -1361,7 +1374,8 @@ func TestStateHalt1(t *testing.T) {
|
|||||||
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
|
||||||
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
|
||||||
newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlock)
|
newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlock)
|
||||||
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
|
addr := cs1.privValidator.GetPubKey().Address()
|
||||||
|
voteCh := subscribeToVoter(cs1, addr)
|
||||||
|
|
||||||
// start round and wait for propose and prevote
|
// start round and wait for propose and prevote
|
||||||
startTestRound(cs1, height, round)
|
startTestRound(cs1, height, round)
|
||||||
|
@@ -50,8 +50,9 @@ func TestPeerCatchupRounds(t *testing.T) {
|
|||||||
|
|
||||||
func makeVoteHR(t *testing.T, height int64, round int, privVals []types.PrivValidator, valIndex int) *types.Vote {
|
func makeVoteHR(t *testing.T, height int64, round int, privVals []types.PrivValidator, valIndex int) *types.Vote {
|
||||||
privVal := privVals[valIndex]
|
privVal := privVals[valIndex]
|
||||||
|
addr := privVal.GetPubKey().Address()
|
||||||
vote := &types.Vote{
|
vote := &types.Vote{
|
||||||
ValidatorAddress: privVal.GetAddress(),
|
ValidatorAddress: addr,
|
||||||
ValidatorIndex: valIndex,
|
ValidatorIndex: valIndex,
|
||||||
Height: height,
|
Height: height,
|
||||||
Round: round,
|
Round: round,
|
||||||
|
@@ -40,8 +40,9 @@ func WALGenerateNBlocks(wr io.Writer, numBlocks int) (err error) {
|
|||||||
// COPY PASTE FROM node.go WITH A FEW MODIFICATIONS
|
// COPY PASTE FROM node.go WITH A FEW MODIFICATIONS
|
||||||
// NOTE: we can't import node package because of circular dependency.
|
// NOTE: we can't import node package because of circular dependency.
|
||||||
// NOTE: we don't do handshake so need to set state.Version.Consensus.App directly.
|
// NOTE: we don't do handshake so need to set state.Version.Consensus.App directly.
|
||||||
privValidatorFile := config.PrivValidatorFile()
|
privValidatorKeyFile := config.PrivValidatorKeyFile()
|
||||||
privValidator := privval.LoadOrGenFilePV(privValidatorFile)
|
privValidatorStateFile := config.PrivValidatorStateFile()
|
||||||
|
privValidator := privval.LoadOrGenFilePV(privValidatorKeyFile, privValidatorStateFile)
|
||||||
genDoc, err := types.GenesisDocFromFile(config.GenesisFile())
|
genDoc, err := types.GenesisDocFromFile(config.GenesisFile())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to read genesis file")
|
return errors.Wrap(err, "failed to read genesis file")
|
||||||
|
@@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
||||||
"golang.org/x/crypto/openpgp/armor" // forked to github.com/tendermint/crypto
|
"golang.org/x/crypto/openpgp/armor"
|
||||||
)
|
)
|
||||||
|
|
||||||
func EncodeArmor(blockType string, headers map[string]string, data []byte) string {
|
func EncodeArmor(blockType string, headers map[string]string, data []byte) string {
|
||||||
|
@@ -7,7 +7,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
amino "github.com/tendermint/go-amino"
|
amino "github.com/tendermint/go-amino"
|
||||||
"golang.org/x/crypto/ed25519" // forked to github.com/tendermint/crypto
|
"golang.org/x/crypto/ed25519"
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/crypto"
|
"github.com/tendermint/tendermint/crypto"
|
||||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||||
@@ -18,8 +18,8 @@ import (
|
|||||||
var _ crypto.PrivKey = PrivKeyEd25519{}
|
var _ crypto.PrivKey = PrivKeyEd25519{}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
PrivKeyAminoRoute = "tendermint/PrivKeyEd25519"
|
PrivKeyAminoName = "tendermint/PrivKeyEd25519"
|
||||||
PubKeyAminoRoute = "tendermint/PubKeyEd25519"
|
PubKeyAminoName = "tendermint/PubKeyEd25519"
|
||||||
// Size of an Edwards25519 signature. Namely the size of a compressed
|
// Size of an Edwards25519 signature. Namely the size of a compressed
|
||||||
// Edwards25519 point, and a field element. Both of which are 32 bytes.
|
// Edwards25519 point, and a field element. Both of which are 32 bytes.
|
||||||
SignatureSize = 64
|
SignatureSize = 64
|
||||||
@@ -30,11 +30,11 @@ var cdc = amino.NewCodec()
|
|||||||
func init() {
|
func init() {
|
||||||
cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
|
cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
|
||||||
cdc.RegisterConcrete(PubKeyEd25519{},
|
cdc.RegisterConcrete(PubKeyEd25519{},
|
||||||
PubKeyAminoRoute, nil)
|
PubKeyAminoName, nil)
|
||||||
|
|
||||||
cdc.RegisterInterface((*crypto.PrivKey)(nil), nil)
|
cdc.RegisterInterface((*crypto.PrivKey)(nil), nil)
|
||||||
cdc.RegisterConcrete(PrivKeyEd25519{},
|
cdc.RegisterConcrete(PrivKeyEd25519{},
|
||||||
PrivKeyAminoRoute, nil)
|
PrivKeyAminoName, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrivKeyEd25519 implements crypto.PrivKey.
|
// PrivKeyEd25519 implements crypto.PrivKey.
|
||||||
|
@@ -12,11 +12,11 @@ import (
|
|||||||
|
|
||||||
var cdc = amino.NewCodec()
|
var cdc = amino.NewCodec()
|
||||||
|
|
||||||
// routeTable is used to map public key concrete types back
|
// nameTable is used to map public key concrete types back
|
||||||
// to their amino routes. This should eventually be handled
|
// to their registered amino names. This should eventually be handled
|
||||||
// by amino. Example usage:
|
// by amino. Example usage:
|
||||||
// routeTable[reflect.TypeOf(ed25519.PubKeyEd25519{})] = ed25519.PubKeyAminoRoute
|
// nameTable[reflect.TypeOf(ed25519.PubKeyEd25519{})] = ed25519.PubKeyAminoName
|
||||||
var routeTable = make(map[reflect.Type]string, 3)
|
var nameTable = make(map[reflect.Type]string, 3)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
// NOTE: It's important that there be no conflicts here,
|
// NOTE: It's important that there be no conflicts here,
|
||||||
@@ -29,16 +29,16 @@ func init() {
|
|||||||
|
|
||||||
// TODO: Have amino provide a way to go from concrete struct to route directly.
|
// TODO: Have amino provide a way to go from concrete struct to route directly.
|
||||||
// Its currently a private API
|
// Its currently a private API
|
||||||
routeTable[reflect.TypeOf(ed25519.PubKeyEd25519{})] = ed25519.PubKeyAminoRoute
|
nameTable[reflect.TypeOf(ed25519.PubKeyEd25519{})] = ed25519.PubKeyAminoName
|
||||||
routeTable[reflect.TypeOf(secp256k1.PubKeySecp256k1{})] = secp256k1.PubKeyAminoRoute
|
nameTable[reflect.TypeOf(secp256k1.PubKeySecp256k1{})] = secp256k1.PubKeyAminoName
|
||||||
routeTable[reflect.TypeOf(&multisig.PubKeyMultisigThreshold{})] = multisig.PubKeyMultisigThresholdAminoRoute
|
nameTable[reflect.TypeOf(multisig.PubKeyMultisigThreshold{})] = multisig.PubKeyMultisigThresholdAminoRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
// PubkeyAminoRoute returns the amino route of a pubkey
|
// PubkeyAminoName returns the amino route of a pubkey
|
||||||
// cdc is currently passed in, as eventually this will not be using
|
// cdc is currently passed in, as eventually this will not be using
|
||||||
// a package level codec.
|
// a package level codec.
|
||||||
func PubkeyAminoRoute(cdc *amino.Codec, key crypto.PubKey) (string, bool) {
|
func PubkeyAminoName(cdc *amino.Codec, key crypto.PubKey) (string, bool) {
|
||||||
route, found := routeTable[reflect.TypeOf(key)]
|
route, found := nameTable[reflect.TypeOf(key)]
|
||||||
return route, found
|
return route, found
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,17 +47,17 @@ func RegisterAmino(cdc *amino.Codec) {
|
|||||||
// These are all written here instead of
|
// These are all written here instead of
|
||||||
cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
|
cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
|
||||||
cdc.RegisterConcrete(ed25519.PubKeyEd25519{},
|
cdc.RegisterConcrete(ed25519.PubKeyEd25519{},
|
||||||
ed25519.PubKeyAminoRoute, nil)
|
ed25519.PubKeyAminoName, nil)
|
||||||
cdc.RegisterConcrete(secp256k1.PubKeySecp256k1{},
|
cdc.RegisterConcrete(secp256k1.PubKeySecp256k1{},
|
||||||
secp256k1.PubKeyAminoRoute, nil)
|
secp256k1.PubKeyAminoName, nil)
|
||||||
cdc.RegisterConcrete(multisig.PubKeyMultisigThreshold{},
|
cdc.RegisterConcrete(multisig.PubKeyMultisigThreshold{},
|
||||||
multisig.PubKeyMultisigThresholdAminoRoute, nil)
|
multisig.PubKeyMultisigThresholdAminoRoute, nil)
|
||||||
|
|
||||||
cdc.RegisterInterface((*crypto.PrivKey)(nil), nil)
|
cdc.RegisterInterface((*crypto.PrivKey)(nil), nil)
|
||||||
cdc.RegisterConcrete(ed25519.PrivKeyEd25519{},
|
cdc.RegisterConcrete(ed25519.PrivKeyEd25519{},
|
||||||
ed25519.PrivKeyAminoRoute, nil)
|
ed25519.PrivKeyAminoName, nil)
|
||||||
cdc.RegisterConcrete(secp256k1.PrivKeySecp256k1{},
|
cdc.RegisterConcrete(secp256k1.PrivKeySecp256k1{},
|
||||||
secp256k1.PrivKeyAminoRoute, nil)
|
secp256k1.PrivKeyAminoName, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PrivKeyFromBytes(privKeyBytes []byte) (privKey crypto.PrivKey, err error) {
|
func PrivKeyFromBytes(privKeyBytes []byte) (privKey crypto.PrivKey, err error) {
|
||||||
|
@@ -128,18 +128,18 @@ func TestPubKeyInvalidDataProperReturnsEmpty(t *testing.T) {
|
|||||||
require.Nil(t, pk)
|
require.Nil(t, pk)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPubkeyAminoRoute(t *testing.T) {
|
func TestPubkeyAminoName(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
key crypto.PubKey
|
key crypto.PubKey
|
||||||
want string
|
want string
|
||||||
found bool
|
found bool
|
||||||
}{
|
}{
|
||||||
{ed25519.PubKeyEd25519{}, ed25519.PubKeyAminoRoute, true},
|
{ed25519.PubKeyEd25519{}, ed25519.PubKeyAminoName, true},
|
||||||
{secp256k1.PubKeySecp256k1{}, secp256k1.PubKeyAminoRoute, true},
|
{secp256k1.PubKeySecp256k1{}, secp256k1.PubKeyAminoName, true},
|
||||||
{&multisig.PubKeyMultisigThreshold{}, multisig.PubKeyMultisigThresholdAminoRoute, true},
|
{multisig.PubKeyMultisigThreshold{}, multisig.PubKeyMultisigThresholdAminoRoute, true},
|
||||||
}
|
}
|
||||||
for i, tc := range tests {
|
for i, tc := range tests {
|
||||||
got, found := PubkeyAminoRoute(cdc, tc.key)
|
got, found := PubkeyAminoName(cdc, tc.key)
|
||||||
require.Equal(t, tc.found, found, "not equal on tc %d", i)
|
require.Equal(t, tc.found, found, "not equal on tc %d", i)
|
||||||
if tc.found {
|
if tc.found {
|
||||||
require.Equal(t, tc.want, got, "not equal on tc %d", i)
|
require.Equal(t, tc.want, got, "not equal on tc %d", i)
|
||||||
|
@@ -3,7 +3,7 @@ package crypto
|
|||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
|
|
||||||
"golang.org/x/crypto/ripemd160" // forked to github.com/tendermint/crypto
|
"golang.org/x/crypto/ripemd160"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Sha256(bytes []byte) []byte {
|
func Sha256(bytes []byte) []byte {
|
||||||
|
21
crypto/merkle/hash.go
Normal file
21
crypto/merkle/hash.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package merkle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: make these have a large predefined capacity
|
||||||
|
var (
|
||||||
|
leafPrefix = []byte{0}
|
||||||
|
innerPrefix = []byte{1}
|
||||||
|
)
|
||||||
|
|
||||||
|
// returns tmhash(0x00 || leaf)
|
||||||
|
func leafHash(leaf []byte) []byte {
|
||||||
|
return tmhash.Sum(append(leafPrefix, leaf...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns tmhash(0x01 || left || right)
|
||||||
|
func innerHash(left []byte, right []byte) []byte {
|
||||||
|
return tmhash.Sum(append(innerPrefix, append(left, right...)...))
|
||||||
|
}
|
@@ -71,11 +71,11 @@ func (op SimpleValueOp) Run(args [][]byte) ([][]byte, error) {
|
|||||||
hasher.Write(value) // does not error
|
hasher.Write(value) // does not error
|
||||||
vhash := hasher.Sum(nil)
|
vhash := hasher.Sum(nil)
|
||||||
|
|
||||||
|
bz := new(bytes.Buffer)
|
||||||
// Wrap <op.Key, vhash> to hash the KVPair.
|
// Wrap <op.Key, vhash> to hash the KVPair.
|
||||||
hasher = tmhash.New()
|
encodeByteSlice(bz, []byte(op.key)) // does not error
|
||||||
encodeByteSlice(hasher, []byte(op.key)) // does not error
|
encodeByteSlice(bz, []byte(vhash)) // does not error
|
||||||
encodeByteSlice(hasher, []byte(vhash)) // does not error
|
kvhash := leafHash(bz.Bytes())
|
||||||
kvhash := hasher.Sum(nil)
|
|
||||||
|
|
||||||
if !bytes.Equal(kvhash, op.Proof.LeafHash) {
|
if !bytes.Equal(kvhash, op.Proof.LeafHash) {
|
||||||
return nil, cmn.NewError("leaf hash mismatch: want %X got %X", op.Proof.LeafHash, kvhash)
|
return nil, cmn.NewError("leaf hash mismatch: want %X got %X", op.Proof.LeafHash, kvhash)
|
||||||
|
97
crypto/merkle/rfc6962_test.go
Normal file
97
crypto/merkle/rfc6962_test.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package merkle
|
||||||
|
|
||||||
|
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
// These tests were taken from https://github.com/google/trillian/blob/master/merkle/rfc6962/rfc6962_test.go,
|
||||||
|
// and consequently fall under the above license.
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRFC6962Hasher(t *testing.T) {
|
||||||
|
_, leafHashTrail := trailsFromByteSlices([][]byte{[]byte("L123456")})
|
||||||
|
leafHash := leafHashTrail.Hash
|
||||||
|
_, leafHashTrail = trailsFromByteSlices([][]byte{[]byte{}})
|
||||||
|
emptyLeafHash := leafHashTrail.Hash
|
||||||
|
for _, tc := range []struct {
|
||||||
|
desc string
|
||||||
|
got []byte
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
// Since creating a merkle tree of no leaves is unsupported here, we skip
|
||||||
|
// the corresponding trillian test vector.
|
||||||
|
|
||||||
|
// Check that the empty hash is not the same as the hash of an empty leaf.
|
||||||
|
// echo -n 00 | xxd -r -p | sha256sum
|
||||||
|
{
|
||||||
|
desc: "RFC6962 Empty Leaf",
|
||||||
|
want: "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d"[:tmhash.Size*2],
|
||||||
|
got: emptyLeafHash,
|
||||||
|
},
|
||||||
|
// echo -n 004C313233343536 | xxd -r -p | sha256sum
|
||||||
|
{
|
||||||
|
desc: "RFC6962 Leaf",
|
||||||
|
want: "395aa064aa4c29f7010acfe3f25db9485bbd4b91897b6ad7ad547639252b4d56"[:tmhash.Size*2],
|
||||||
|
got: leafHash,
|
||||||
|
},
|
||||||
|
// echo -n 014E3132334E343536 | xxd -r -p | sha256sum
|
||||||
|
{
|
||||||
|
desc: "RFC6962 Node",
|
||||||
|
want: "aa217fe888e47007fa15edab33c2b492a722cb106c64667fc2b044444de66bbb"[:tmhash.Size*2],
|
||||||
|
got: innerHash([]byte("N123"), []byte("N456")),
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
|
wantBytes, err := hex.DecodeString(tc.want)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("hex.DecodeString(%x): %v", tc.want, err)
|
||||||
|
}
|
||||||
|
if got, want := tc.got, wantBytes; !bytes.Equal(got, want) {
|
||||||
|
t.Errorf("got %x, want %x", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRFC6962HasherCollisions(t *testing.T) {
|
||||||
|
// Check that different leaves have different hashes.
|
||||||
|
leaf1, leaf2 := []byte("Hello"), []byte("World")
|
||||||
|
_, leafHashTrail := trailsFromByteSlices([][]byte{leaf1})
|
||||||
|
hash1 := leafHashTrail.Hash
|
||||||
|
_, leafHashTrail = trailsFromByteSlices([][]byte{leaf2})
|
||||||
|
hash2 := leafHashTrail.Hash
|
||||||
|
if bytes.Equal(hash1, hash2) {
|
||||||
|
t.Errorf("Leaf hashes should differ, but both are %x", hash1)
|
||||||
|
}
|
||||||
|
// Compute an intermediate subtree hash.
|
||||||
|
_, subHash1Trail := trailsFromByteSlices([][]byte{hash1, hash2})
|
||||||
|
subHash1 := subHash1Trail.Hash
|
||||||
|
// Check that this is not the same as a leaf hash of their concatenation.
|
||||||
|
preimage := append(hash1, hash2...)
|
||||||
|
_, forgedHashTrail := trailsFromByteSlices([][]byte{preimage})
|
||||||
|
forgedHash := forgedHashTrail.Hash
|
||||||
|
if bytes.Equal(subHash1, forgedHash) {
|
||||||
|
t.Errorf("Hasher is not second-preimage resistant")
|
||||||
|
}
|
||||||
|
// Swap the order of nodes and check that the hash is different.
|
||||||
|
_, subHash2Trail := trailsFromByteSlices([][]byte{hash2, hash1})
|
||||||
|
subHash2 := subHash2Trail.Hash
|
||||||
|
if bytes.Equal(subHash1, subHash2) {
|
||||||
|
t.Errorf("Subtree hash does not depend on the order of leaves")
|
||||||
|
}
|
||||||
|
}
|
@@ -13,14 +13,14 @@ func TestSimpleMap(t *testing.T) {
|
|||||||
values []string // each string gets converted to []byte in test
|
values []string // each string gets converted to []byte in test
|
||||||
want string
|
want string
|
||||||
}{
|
}{
|
||||||
{[]string{"key1"}, []string{"value1"}, "321d150de16dceb51c72981b432b115045383259b1a550adf8dc80f927508967"},
|
{[]string{"key1"}, []string{"value1"}, "a44d3cc7daba1a4600b00a2434b30f8b970652169810d6dfa9fb1793a2189324"},
|
||||||
{[]string{"key1"}, []string{"value2"}, "2a9e4baf321eac99f6eecc3406603c14bc5e85bb7b80483cbfc75b3382d24a2f"},
|
{[]string{"key1"}, []string{"value2"}, "0638e99b3445caec9d95c05e1a3fc1487b4ddec6a952ff337080360b0dcc078c"},
|
||||||
// swap order with 2 keys
|
// swap order with 2 keys
|
||||||
{[]string{"key1", "key2"}, []string{"value1", "value2"}, "c4d8913ab543ba26aa970646d4c99a150fd641298e3367cf68ca45fb45a49881"},
|
{[]string{"key1", "key2"}, []string{"value1", "value2"}, "8fd19b19e7bb3f2b3ee0574027d8a5a4cec370464ea2db2fbfa5c7d35bb0cff3"},
|
||||||
{[]string{"key2", "key1"}, []string{"value2", "value1"}, "c4d8913ab543ba26aa970646d4c99a150fd641298e3367cf68ca45fb45a49881"},
|
{[]string{"key2", "key1"}, []string{"value2", "value1"}, "8fd19b19e7bb3f2b3ee0574027d8a5a4cec370464ea2db2fbfa5c7d35bb0cff3"},
|
||||||
// swap order with 3 keys
|
// swap order with 3 keys
|
||||||
{[]string{"key1", "key2", "key3"}, []string{"value1", "value2", "value3"}, "b23cef00eda5af4548a213a43793f2752d8d9013b3f2b64bc0523a4791196268"},
|
{[]string{"key1", "key2", "key3"}, []string{"value1", "value2", "value3"}, "1dd674ec6782a0d586a903c9c63326a41cbe56b3bba33ed6ff5b527af6efb3dc"},
|
||||||
{[]string{"key1", "key3", "key2"}, []string{"value1", "value3", "value2"}, "b23cef00eda5af4548a213a43793f2752d8d9013b3f2b64bc0523a4791196268"},
|
{[]string{"key1", "key3", "key2"}, []string{"value1", "value3", "value2"}, "1dd674ec6782a0d586a903c9c63326a41cbe56b3bba33ed6ff5b527af6efb3dc"},
|
||||||
}
|
}
|
||||||
for i, tc := range tests {
|
for i, tc := range tests {
|
||||||
db := newSimpleMap()
|
db := newSimpleMap()
|
||||||
|
@@ -5,7 +5,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
|
||||||
cmn "github.com/tendermint/tendermint/libs/common"
|
cmn "github.com/tendermint/tendermint/libs/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -67,7 +66,8 @@ func SimpleProofsFromMap(m map[string][]byte) (rootHash []byte, proofs map[strin
|
|||||||
|
|
||||||
// Verify that the SimpleProof proves the root hash.
|
// Verify that the SimpleProof proves the root hash.
|
||||||
// Check sp.Index/sp.Total manually if needed
|
// Check sp.Index/sp.Total manually if needed
|
||||||
func (sp *SimpleProof) Verify(rootHash []byte, leafHash []byte) error {
|
func (sp *SimpleProof) Verify(rootHash []byte, leaf []byte) error {
|
||||||
|
leafHash := leafHash(leaf)
|
||||||
if sp.Total < 0 {
|
if sp.Total < 0 {
|
||||||
return errors.New("Proof total must be positive")
|
return errors.New("Proof total must be positive")
|
||||||
}
|
}
|
||||||
@@ -128,19 +128,19 @@ func computeHashFromAunts(index int, total int, leafHash []byte, innerHashes [][
|
|||||||
if len(innerHashes) == 0 {
|
if len(innerHashes) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
numLeft := (total + 1) / 2
|
numLeft := getSplitPoint(total)
|
||||||
if index < numLeft {
|
if index < numLeft {
|
||||||
leftHash := computeHashFromAunts(index, numLeft, leafHash, innerHashes[:len(innerHashes)-1])
|
leftHash := computeHashFromAunts(index, numLeft, leafHash, innerHashes[:len(innerHashes)-1])
|
||||||
if leftHash == nil {
|
if leftHash == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return simpleHashFromTwoHashes(leftHash, innerHashes[len(innerHashes)-1])
|
return innerHash(leftHash, innerHashes[len(innerHashes)-1])
|
||||||
}
|
}
|
||||||
rightHash := computeHashFromAunts(index-numLeft, total-numLeft, leafHash, innerHashes[:len(innerHashes)-1])
|
rightHash := computeHashFromAunts(index-numLeft, total-numLeft, leafHash, innerHashes[:len(innerHashes)-1])
|
||||||
if rightHash == nil {
|
if rightHash == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return simpleHashFromTwoHashes(innerHashes[len(innerHashes)-1], rightHash)
|
return innerHash(innerHashes[len(innerHashes)-1], rightHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,12 +182,13 @@ func trailsFromByteSlices(items [][]byte) (trails []*SimpleProofNode, root *Simp
|
|||||||
case 0:
|
case 0:
|
||||||
return nil, nil
|
return nil, nil
|
||||||
case 1:
|
case 1:
|
||||||
trail := &SimpleProofNode{tmhash.Sum(items[0]), nil, nil, nil}
|
trail := &SimpleProofNode{leafHash(items[0]), nil, nil, nil}
|
||||||
return []*SimpleProofNode{trail}, trail
|
return []*SimpleProofNode{trail}, trail
|
||||||
default:
|
default:
|
||||||
lefts, leftRoot := trailsFromByteSlices(items[:(len(items)+1)/2])
|
k := getSplitPoint(len(items))
|
||||||
rights, rightRoot := trailsFromByteSlices(items[(len(items)+1)/2:])
|
lefts, leftRoot := trailsFromByteSlices(items[:k])
|
||||||
rootHash := simpleHashFromTwoHashes(leftRoot.Hash, rightRoot.Hash)
|
rights, rightRoot := trailsFromByteSlices(items[k:])
|
||||||
|
rootHash := innerHash(leftRoot.Hash, rightRoot.Hash)
|
||||||
root := &SimpleProofNode{rootHash, nil, nil, nil}
|
root := &SimpleProofNode{rootHash, nil, nil, nil}
|
||||||
leftRoot.Parent = root
|
leftRoot.Parent = root
|
||||||
leftRoot.Right = rightRoot
|
leftRoot.Right = rightRoot
|
||||||
|
@@ -1,23 +1,9 @@
|
|||||||
package merkle
|
package merkle
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
"math/bits"
|
||||||
)
|
)
|
||||||
|
|
||||||
// simpleHashFromTwoHashes is the basic operation of the Merkle tree: Hash(left | right).
|
|
||||||
func simpleHashFromTwoHashes(left, right []byte) []byte {
|
|
||||||
var hasher = tmhash.New()
|
|
||||||
err := encodeByteSlice(hasher, left)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
err = encodeByteSlice(hasher, right)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return hasher.Sum(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SimpleHashFromByteSlices computes a Merkle tree where the leaves are the byte slice,
|
// SimpleHashFromByteSlices computes a Merkle tree where the leaves are the byte slice,
|
||||||
// in the provided order.
|
// in the provided order.
|
||||||
func SimpleHashFromByteSlices(items [][]byte) []byte {
|
func SimpleHashFromByteSlices(items [][]byte) []byte {
|
||||||
@@ -25,11 +11,12 @@ func SimpleHashFromByteSlices(items [][]byte) []byte {
|
|||||||
case 0:
|
case 0:
|
||||||
return nil
|
return nil
|
||||||
case 1:
|
case 1:
|
||||||
return tmhash.Sum(items[0])
|
return leafHash(items[0])
|
||||||
default:
|
default:
|
||||||
left := SimpleHashFromByteSlices(items[:(len(items)+1)/2])
|
k := getSplitPoint(len(items))
|
||||||
right := SimpleHashFromByteSlices(items[(len(items)+1)/2:])
|
left := SimpleHashFromByteSlices(items[:k])
|
||||||
return simpleHashFromTwoHashes(left, right)
|
right := SimpleHashFromByteSlices(items[k:])
|
||||||
|
return innerHash(left, right)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,3 +31,17 @@ func SimpleHashFromMap(m map[string][]byte) []byte {
|
|||||||
}
|
}
|
||||||
return sm.Hash()
|
return sm.Hash()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getSplitPoint returns the largest power of 2 less than length
|
||||||
|
func getSplitPoint(length int) int {
|
||||||
|
if length < 1 {
|
||||||
|
panic("Trying to split a tree with size < 1")
|
||||||
|
}
|
||||||
|
uLength := uint(length)
|
||||||
|
bitlen := bits.Len(uLength)
|
||||||
|
k := 1 << uint(bitlen-1)
|
||||||
|
if k == length {
|
||||||
|
k >>= 1
|
||||||
|
}
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
@@ -34,7 +34,6 @@ func TestSimpleProof(t *testing.T) {
|
|||||||
|
|
||||||
// For each item, check the trail.
|
// For each item, check the trail.
|
||||||
for i, item := range items {
|
for i, item := range items {
|
||||||
itemHash := tmhash.Sum(item)
|
|
||||||
proof := proofs[i]
|
proof := proofs[i]
|
||||||
|
|
||||||
// Check total/index
|
// Check total/index
|
||||||
@@ -43,30 +42,53 @@ func TestSimpleProof(t *testing.T) {
|
|||||||
require.Equal(t, proof.Total, total, "Unmatched totals: %d vs %d", proof.Total, total)
|
require.Equal(t, proof.Total, total, "Unmatched totals: %d vs %d", proof.Total, total)
|
||||||
|
|
||||||
// Verify success
|
// Verify success
|
||||||
err := proof.Verify(rootHash, itemHash)
|
err := proof.Verify(rootHash, item)
|
||||||
require.NoError(t, err, "Verificatior failed: %v.", err)
|
require.NoError(t, err, "Verification failed: %v.", err)
|
||||||
|
|
||||||
// Trail too long should make it fail
|
// Trail too long should make it fail
|
||||||
origAunts := proof.Aunts
|
origAunts := proof.Aunts
|
||||||
proof.Aunts = append(proof.Aunts, cmn.RandBytes(32))
|
proof.Aunts = append(proof.Aunts, cmn.RandBytes(32))
|
||||||
err = proof.Verify(rootHash, itemHash)
|
err = proof.Verify(rootHash, item)
|
||||||
require.Error(t, err, "Expected verification to fail for wrong trail length")
|
require.Error(t, err, "Expected verification to fail for wrong trail length")
|
||||||
|
|
||||||
proof.Aunts = origAunts
|
proof.Aunts = origAunts
|
||||||
|
|
||||||
// Trail too short should make it fail
|
// Trail too short should make it fail
|
||||||
proof.Aunts = proof.Aunts[0 : len(proof.Aunts)-1]
|
proof.Aunts = proof.Aunts[0 : len(proof.Aunts)-1]
|
||||||
err = proof.Verify(rootHash, itemHash)
|
err = proof.Verify(rootHash, item)
|
||||||
require.Error(t, err, "Expected verification to fail for wrong trail length")
|
require.Error(t, err, "Expected verification to fail for wrong trail length")
|
||||||
|
|
||||||
proof.Aunts = origAunts
|
proof.Aunts = origAunts
|
||||||
|
|
||||||
// Mutating the itemHash should make it fail.
|
// Mutating the itemHash should make it fail.
|
||||||
err = proof.Verify(rootHash, MutateByteSlice(itemHash))
|
err = proof.Verify(rootHash, MutateByteSlice(item))
|
||||||
require.Error(t, err, "Expected verification to fail for mutated leaf hash")
|
require.Error(t, err, "Expected verification to fail for mutated leaf hash")
|
||||||
|
|
||||||
// Mutating the rootHash should make it fail.
|
// Mutating the rootHash should make it fail.
|
||||||
err = proof.Verify(MutateByteSlice(rootHash), itemHash)
|
err = proof.Verify(MutateByteSlice(rootHash), item)
|
||||||
require.Error(t, err, "Expected verification to fail for mutated root hash")
|
require.Error(t, err, "Expected verification to fail for mutated root hash")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_getSplitPoint(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
length int
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{1, 0},
|
||||||
|
{2, 1},
|
||||||
|
{3, 2},
|
||||||
|
{4, 2},
|
||||||
|
{5, 4},
|
||||||
|
{10, 8},
|
||||||
|
{20, 16},
|
||||||
|
{100, 64},
|
||||||
|
{255, 128},
|
||||||
|
{256, 128},
|
||||||
|
{257, 256},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
got := getSplitPoint(tt.length)
|
||||||
|
require.Equal(t, tt.want, got, "getSplitPoint(%d) = %v, want %v", tt.length, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -2,7 +2,6 @@ package multisig
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tendermint/tendermint/crypto"
|
"github.com/tendermint/tendermint/crypto"
|
||||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// PubKeyMultisigThreshold implements a K of N threshold multisig.
|
// PubKeyMultisigThreshold implements a K of N threshold multisig.
|
||||||
@@ -11,7 +10,7 @@ type PubKeyMultisigThreshold struct {
|
|||||||
PubKeys []crypto.PubKey `json:"pubkeys"`
|
PubKeys []crypto.PubKey `json:"pubkeys"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ crypto.PubKey = &PubKeyMultisigThreshold{}
|
var _ crypto.PubKey = PubKeyMultisigThreshold{}
|
||||||
|
|
||||||
// NewPubKeyMultisigThreshold returns a new PubKeyMultisigThreshold.
|
// NewPubKeyMultisigThreshold returns a new PubKeyMultisigThreshold.
|
||||||
// Panics if len(pubkeys) < k or 0 >= k.
|
// Panics if len(pubkeys) < k or 0 >= k.
|
||||||
@@ -22,7 +21,7 @@ func NewPubKeyMultisigThreshold(k int, pubkeys []crypto.PubKey) crypto.PubKey {
|
|||||||
if len(pubkeys) < k {
|
if len(pubkeys) < k {
|
||||||
panic("threshold k of n multisignature: len(pubkeys) < k")
|
panic("threshold k of n multisignature: len(pubkeys) < k")
|
||||||
}
|
}
|
||||||
return &PubKeyMultisigThreshold{uint(k), pubkeys}
|
return PubKeyMultisigThreshold{uint(k), pubkeys}
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyBytes expects sig to be an amino encoded version of a MultiSignature.
|
// VerifyBytes expects sig to be an amino encoded version of a MultiSignature.
|
||||||
@@ -31,8 +30,8 @@ func NewPubKeyMultisigThreshold(k int, pubkeys []crypto.PubKey) crypto.PubKey {
|
|||||||
// and all signatures are valid. (Not just k of the signatures)
|
// and all signatures are valid. (Not just k of the signatures)
|
||||||
// The multisig uses a bitarray, so multiple signatures for the same key is not
|
// The multisig uses a bitarray, so multiple signatures for the same key is not
|
||||||
// a concern.
|
// a concern.
|
||||||
func (pk *PubKeyMultisigThreshold) VerifyBytes(msg []byte, marshalledSig []byte) bool {
|
func (pk PubKeyMultisigThreshold) VerifyBytes(msg []byte, marshalledSig []byte) bool {
|
||||||
var sig *Multisignature
|
var sig Multisignature
|
||||||
err := cdc.UnmarshalBinaryBare(marshalledSig, &sig)
|
err := cdc.UnmarshalBinaryBare(marshalledSig, &sig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
@@ -64,19 +63,19 @@ func (pk *PubKeyMultisigThreshold) VerifyBytes(msg []byte, marshalledSig []byte)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Bytes returns the amino encoded version of the PubKeyMultisigThreshold
|
// Bytes returns the amino encoded version of the PubKeyMultisigThreshold
|
||||||
func (pk *PubKeyMultisigThreshold) Bytes() []byte {
|
func (pk PubKeyMultisigThreshold) Bytes() []byte {
|
||||||
return cdc.MustMarshalBinaryBare(pk)
|
return cdc.MustMarshalBinaryBare(pk)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Address returns tmhash(PubKeyMultisigThreshold.Bytes())
|
// Address returns tmhash(PubKeyMultisigThreshold.Bytes())
|
||||||
func (pk *PubKeyMultisigThreshold) Address() crypto.Address {
|
func (pk PubKeyMultisigThreshold) Address() crypto.Address {
|
||||||
return crypto.Address(tmhash.Sum(pk.Bytes()))
|
return crypto.AddressHash(pk.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals returns true iff pk and other both have the same number of keys, and
|
// Equals returns true iff pk and other both have the same number of keys, and
|
||||||
// all constituent keys are the same, and in the same order.
|
// all constituent keys are the same, and in the same order.
|
||||||
func (pk *PubKeyMultisigThreshold) Equals(other crypto.PubKey) bool {
|
func (pk PubKeyMultisigThreshold) Equals(other crypto.PubKey) bool {
|
||||||
otherKey, sameType := other.(*PubKeyMultisigThreshold)
|
otherKey, sameType := other.(PubKeyMultisigThreshold)
|
||||||
if !sameType {
|
if !sameType {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@@ -82,7 +82,7 @@ func TestMultiSigPubKeyEquality(t *testing.T) {
|
|||||||
msg := []byte{1, 2, 3, 4}
|
msg := []byte{1, 2, 3, 4}
|
||||||
pubkeys, _ := generatePubKeysAndSignatures(5, msg)
|
pubkeys, _ := generatePubKeysAndSignatures(5, msg)
|
||||||
multisigKey := NewPubKeyMultisigThreshold(2, pubkeys)
|
multisigKey := NewPubKeyMultisigThreshold(2, pubkeys)
|
||||||
var unmarshalledMultisig *PubKeyMultisigThreshold
|
var unmarshalledMultisig PubKeyMultisigThreshold
|
||||||
cdc.MustUnmarshalBinaryBare(multisigKey.Bytes(), &unmarshalledMultisig)
|
cdc.MustUnmarshalBinaryBare(multisigKey.Bytes(), &unmarshalledMultisig)
|
||||||
require.True(t, multisigKey.Equals(unmarshalledMultisig))
|
require.True(t, multisigKey.Equals(unmarshalledMultisig))
|
||||||
|
|
||||||
@@ -95,6 +95,29 @@ func TestMultiSigPubKeyEquality(t *testing.T) {
|
|||||||
require.False(t, multisigKey.Equals(multisigKey2))
|
require.False(t, multisigKey.Equals(multisigKey2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAddress(t *testing.T) {
|
||||||
|
msg := []byte{1, 2, 3, 4}
|
||||||
|
pubkeys, _ := generatePubKeysAndSignatures(5, msg)
|
||||||
|
multisigKey := NewPubKeyMultisigThreshold(2, pubkeys)
|
||||||
|
require.Len(t, multisigKey.Address().Bytes(), 20)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPubKeyMultisigThresholdAminoToIface(t *testing.T) {
|
||||||
|
msg := []byte{1, 2, 3, 4}
|
||||||
|
pubkeys, _ := generatePubKeysAndSignatures(5, msg)
|
||||||
|
multisigKey := NewPubKeyMultisigThreshold(2, pubkeys)
|
||||||
|
|
||||||
|
ab, err := cdc.MarshalBinaryLengthPrefixed(multisigKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
// like other crypto.Pubkey implementations (e.g. ed25519.PubKeyEd25519),
|
||||||
|
// PubKeyMultisigThreshold should be deserializable into a crypto.PubKey:
|
||||||
|
var pubKey crypto.PubKey
|
||||||
|
err = cdc.UnmarshalBinaryLengthPrefixed(ab, &pubKey)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, multisigKey, pubKey)
|
||||||
|
}
|
||||||
|
|
||||||
func generatePubKeysAndSignatures(n int, msg []byte) (pubkeys []crypto.PubKey, signatures [][]byte) {
|
func generatePubKeysAndSignatures(n int, msg []byte) (pubkeys []crypto.PubKey, signatures [][]byte) {
|
||||||
pubkeys = make([]crypto.PubKey, n)
|
pubkeys = make([]crypto.PubKey, n)
|
||||||
signatures = make([][]byte, n)
|
signatures = make([][]byte, n)
|
||||||
|
@@ -20,7 +20,7 @@ func init() {
|
|||||||
cdc.RegisterConcrete(PubKeyMultisigThreshold{},
|
cdc.RegisterConcrete(PubKeyMultisigThreshold{},
|
||||||
PubKeyMultisigThresholdAminoRoute, nil)
|
PubKeyMultisigThresholdAminoRoute, nil)
|
||||||
cdc.RegisterConcrete(ed25519.PubKeyEd25519{},
|
cdc.RegisterConcrete(ed25519.PubKeyEd25519{},
|
||||||
ed25519.PubKeyAminoRoute, nil)
|
ed25519.PubKeyAminoName, nil)
|
||||||
cdc.RegisterConcrete(secp256k1.PubKeySecp256k1{},
|
cdc.RegisterConcrete(secp256k1.PubKeySecp256k1{},
|
||||||
secp256k1.PubKeyAminoRoute, nil)
|
secp256k1.PubKeyAminoName, nil)
|
||||||
}
|
}
|
||||||
|
@@ -9,15 +9,15 @@ import (
|
|||||||
|
|
||||||
secp256k1 "github.com/tendermint/btcd/btcec"
|
secp256k1 "github.com/tendermint/btcd/btcec"
|
||||||
amino "github.com/tendermint/go-amino"
|
amino "github.com/tendermint/go-amino"
|
||||||
"golang.org/x/crypto/ripemd160" // forked to github.com/tendermint/crypto
|
"golang.org/x/crypto/ripemd160"
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/crypto"
|
"github.com/tendermint/tendermint/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
const (
|
const (
|
||||||
PrivKeyAminoRoute = "tendermint/PrivKeySecp256k1"
|
PrivKeyAminoName = "tendermint/PrivKeySecp256k1"
|
||||||
PubKeyAminoRoute = "tendermint/PubKeySecp256k1"
|
PubKeyAminoName = "tendermint/PubKeySecp256k1"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cdc = amino.NewCodec()
|
var cdc = amino.NewCodec()
|
||||||
@@ -25,11 +25,11 @@ var cdc = amino.NewCodec()
|
|||||||
func init() {
|
func init() {
|
||||||
cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
|
cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
|
||||||
cdc.RegisterConcrete(PubKeySecp256k1{},
|
cdc.RegisterConcrete(PubKeySecp256k1{},
|
||||||
PubKeyAminoRoute, nil)
|
PubKeyAminoName, nil)
|
||||||
|
|
||||||
cdc.RegisterInterface((*crypto.PrivKey)(nil), nil)
|
cdc.RegisterInterface((*crypto.PrivKey)(nil), nil)
|
||||||
cdc.RegisterConcrete(PrivKeySecp256k1{},
|
cdc.RegisterConcrete(PrivKeySecp256k1{},
|
||||||
PrivKeyAminoRoute, nil)
|
PrivKeyAminoName, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
@@ -8,7 +8,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"golang.org/x/crypto/chacha20poly1305" // forked to github.com/tendermint/crypto
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Implements crypto.AEAD
|
// Implements crypto.AEAD
|
||||||
|
@@ -4,7 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"golang.org/x/crypto/nacl/secretbox" // forked to github.com/tendermint/crypto
|
"golang.org/x/crypto/nacl/secretbox"
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/crypto"
|
"github.com/tendermint/tendermint/crypto"
|
||||||
cmn "github.com/tendermint/tendermint/libs/common"
|
cmn "github.com/tendermint/tendermint/libs/common"
|
||||||
|
@@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"golang.org/x/crypto/bcrypt" // forked to github.com/tendermint/crypto
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/crypto"
|
"github.com/tendermint/tendermint/crypto"
|
||||||
)
|
)
|
||||||
@@ -30,9 +30,7 @@ func TestSimpleWithKDF(t *testing.T) {
|
|||||||
|
|
||||||
plaintext := []byte("sometext")
|
plaintext := []byte("sometext")
|
||||||
secretPass := []byte("somesecret")
|
secretPass := []byte("somesecret")
|
||||||
salt := []byte("somesaltsomesalt") // len 16
|
secret, err := bcrypt.GenerateFromPassword(secretPass, 12)
|
||||||
// NOTE: we use a fork of x/crypto so we can inject our own randomness for salt
|
|
||||||
secret, err := bcrypt.GenerateFromPassword(salt, secretPass, 12)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
@@ -8,8 +8,21 @@ module.exports = {
|
|||||||
lineNumbers: true
|
lineNumbers: true
|
||||||
},
|
},
|
||||||
themeConfig: {
|
themeConfig: {
|
||||||
lastUpdated: "Last Updated",
|
repo: "tendermint/tendermint",
|
||||||
nav: [{ text: "Back to Tendermint", link: "https://tendermint.com" }],
|
editLinks: true,
|
||||||
|
docsDir: "docs",
|
||||||
|
docsBranch: "develop",
|
||||||
|
editLinkText: 'Edit this page on Github',
|
||||||
|
lastUpdated: true,
|
||||||
|
algolia: {
|
||||||
|
apiKey: '59f0e2deb984aa9cdf2b3a5fd24ac501',
|
||||||
|
indexName: 'tendermint',
|
||||||
|
debug: false
|
||||||
|
},
|
||||||
|
nav: [
|
||||||
|
{ text: "Back to Tendermint", link: "https://tendermint.com" },
|
||||||
|
{ text: "RPC", link: "../rpc/" }
|
||||||
|
],
|
||||||
sidebar: [
|
sidebar: [
|
||||||
{
|
{
|
||||||
title: "Introduction",
|
title: "Introduction",
|
||||||
@@ -21,6 +34,20 @@ module.exports = {
|
|||||||
"/introduction/what-is-tendermint"
|
"/introduction/what-is-tendermint"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Apps",
|
||||||
|
collapsable: false,
|
||||||
|
children: [
|
||||||
|
"/app-dev/getting-started",
|
||||||
|
"/app-dev/abci-cli",
|
||||||
|
"/app-dev/app-architecture",
|
||||||
|
"/app-dev/app-development",
|
||||||
|
"/app-dev/subscribing-to-events-via-websocket",
|
||||||
|
"/app-dev/indexing-transactions",
|
||||||
|
"/app-dev/abci-spec",
|
||||||
|
"/app-dev/ecosystem"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "Tendermint Core",
|
title: "Tendermint Core",
|
||||||
collapsable: false,
|
collapsable: false,
|
||||||
@@ -39,15 +66,6 @@ module.exports = {
|
|||||||
"/tendermint-core/validators"
|
"/tendermint-core/validators"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: "Tools",
|
|
||||||
collapsable: false,
|
|
||||||
children: [
|
|
||||||
"/tools/",
|
|
||||||
"/tools/benchmarking",
|
|
||||||
"/tools/monitoring"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: "Networks",
|
title: "Networks",
|
||||||
collapsable: false,
|
collapsable: false,
|
||||||
@@ -58,18 +76,13 @@ module.exports = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Apps",
|
title: "Tools",
|
||||||
collapsable: false,
|
collapsable: false,
|
||||||
children: [
|
children: [
|
||||||
"/app-dev/getting-started",
|
"/tools/",
|
||||||
"/app-dev/abci-cli",
|
"/tools/benchmarking",
|
||||||
"/app-dev/app-architecture",
|
"/tools/monitoring"
|
||||||
"/app-dev/app-development",
|
]
|
||||||
"/app-dev/subscribing-to-events-via-websocket",
|
|
||||||
"/app-dev/indexing-transactions",
|
|
||||||
"/app-dev/abci-spec",
|
|
||||||
"/app-dev/ecosystem"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Tendermint Spec",
|
title: "Tendermint Spec",
|
||||||
|
@@ -12,10 +12,10 @@ respectively.
|
|||||||
|
|
||||||
## How It Works
|
## How It Works
|
||||||
|
|
||||||
There is a Jenkins job listening for changes in the `/docs` directory, on both
|
There is a CircleCI job listening for changes in the `/docs` directory, on both
|
||||||
the `master` and `develop` branches. Any updates to files in this directory
|
the `master` and `develop` branches. Any updates to files in this directory
|
||||||
on those branches will automatically trigger a website deployment. Under the hood,
|
on those branches will automatically trigger a website deployment. Under the hood,
|
||||||
a private website repository has make targets consumed by a standard Jenkins task.
|
the private website repository has a `make build-docs` target consumed by a CircleCI job in that repo.
|
||||||
|
|
||||||
## README
|
## README
|
||||||
|
|
||||||
@@ -93,6 +93,10 @@ python -m SimpleHTTPServer 8080
|
|||||||
|
|
||||||
then navigate to localhost:8080 in your browser.
|
then navigate to localhost:8080 in your browser.
|
||||||
|
|
||||||
|
## Search
|
||||||
|
|
||||||
|
We are using [Algolia](https://www.algolia.com) to power full-text search. This uses a public API search-only key in the `config.js` as well as a [tendermint.json](https://github.com/algolia/docsearch-configs/blob/master/configs/tendermint.json) configuration file that we can update with PRs.
|
||||||
|
|
||||||
## Consistency
|
## Consistency
|
||||||
|
|
||||||
Because the build processes are identical (as is the information contained herein), this file should be kept in sync as
|
Because the build processes are identical (as is the information contained herein), this file should be kept in sync as
|
||||||
|
@@ -5,7 +5,7 @@ Tendermint blockchain application.
|
|||||||
|
|
||||||
The following diagram provides a superb example:
|
The following diagram provides a superb example:
|
||||||
|
|
||||||
<https://drive.google.com/open?id=1yR2XpRi9YCY9H9uMfcw8-RMJpvDyvjz9>
|

|
||||||
|
|
||||||
The end-user application here is the Cosmos Voyager, at the bottom left.
|
The end-user application here is the Cosmos Voyager, at the bottom left.
|
||||||
Voyager communicates with a REST API exposed by a local Light-Client
|
Voyager communicates with a REST API exposed by a local Light-Client
|
||||||
@@ -45,6 +45,6 @@ Tendermint.
|
|||||||
See the following for more extensive documentation:
|
See the following for more extensive documentation:
|
||||||
|
|
||||||
- [Interchain Standard for the Light-Client REST API](https://github.com/cosmos/cosmos-sdk/pull/1028)
|
- [Interchain Standard for the Light-Client REST API](https://github.com/cosmos/cosmos-sdk/pull/1028)
|
||||||
- [Tendermint RPC Docs](https://tendermint.github.io/slate/)
|
- [Tendermint RPC Docs](https://tendermint.com/rpc/)
|
||||||
- [Tendermint in Production](../tendermint-core/running-in-production.md)
|
- [Tendermint in Production](../tendermint-core/running-in-production.md)
|
||||||
- [ABCI spec](./abci-spec.md)
|
- [ABCI spec](./abci-spec.md)
|
||||||
|
@@ -63,6 +63,13 @@
|
|||||||
"author": "Zach Balder",
|
"author": "Zach Balder",
|
||||||
"description": "Public service reporting and tracking"
|
"description": "Public service reporting and tracking"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "ParadigmCore",
|
||||||
|
"url": "https://github.com/ParadigmFoundation/ParadigmCore",
|
||||||
|
"language": "TypeScript",
|
||||||
|
"author": "Paradigm Labs",
|
||||||
|
"description": "Reference implementation of the Paradigm Protocol, and OrderStream network client."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Passchain",
|
"name": "Passchain",
|
||||||
"url": "https://github.com/trusch/passchain",
|
"url": "https://github.com/trusch/passchain",
|
||||||
@@ -181,5 +188,13 @@
|
|||||||
"language": "Javascript",
|
"language": "Javascript",
|
||||||
"author": "Dennis McKinnon"
|
"author": "Dennis McKinnon"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"aminoLibraries": [
|
||||||
|
{
|
||||||
|
"name": "JS-Amino",
|
||||||
|
"url": "https://github.com/TanNgocDo/Js-Amino",
|
||||||
|
"language": "Javascript",
|
||||||
|
"author": "TanNgocDo"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,9 @@
|
|||||||
# Ecosystem
|
# Ecosystem
|
||||||
|
|
||||||
The growing list of applications built using various pieces of the
|
The growing list of applications built using various pieces of the
|
||||||
Tendermint stack can be found at:
|
Tendermint stack can be found at the [ecosystem page](https://tendermint.com/ecosystem).
|
||||||
|
|
||||||
- https://tendermint.com/ecosystem
|
We thank the community for their contributions and welcome the
|
||||||
|
|
||||||
We thank the community for their contributions thus far and welcome the
|
|
||||||
addition of new projects. A pull request can be submitted to [this
|
addition of new projects. A pull request can be submitted to [this
|
||||||
file](https://github.com/tendermint/tendermint/blob/master/docs/app-dev/ecosystem.json)
|
file](https://github.com/tendermint/tendermint/blob/master/docs/app-dev/ecosystem.json)
|
||||||
to include your project.
|
to include your project.
|
||||||
|
@@ -78,7 +78,7 @@ endpoint:
|
|||||||
curl "localhost:26657/tx_search?query=\"account.name='igor'\"&prove=true"
|
curl "localhost:26657/tx_search?query=\"account.name='igor'\"&prove=true"
|
||||||
```
|
```
|
||||||
|
|
||||||
Check out [API docs](https://tendermint.github.io/slate/?shell#txsearch)
|
Check out [API docs](https://tendermint.com/rpc/#txsearch)
|
||||||
for more information on query syntax and other options.
|
for more information on query syntax and other options.
|
||||||
|
|
||||||
## Subscribing to transactions
|
## Subscribing to transactions
|
||||||
@@ -97,5 +97,5 @@ by providing a query to `/subscribe` RPC endpoint.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Check out [API docs](https://tendermint.github.io/slate/#subscribe) for
|
Check out [API docs](https://tendermint.com/rpc/#subscribe) for
|
||||||
more information on query syntax and other options.
|
more information on query syntax and other options.
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
28-08-2018: Third version after Ethan's comments
|
28-08-2018: Third version after Ethan's comments
|
||||||
30-08-2018: AminoOverheadForBlock => MaxAminoOverheadForBlock
|
30-08-2018: AminoOverheadForBlock => MaxAminoOverheadForBlock
|
||||||
31-08-2018: Bounding evidence and chain ID
|
31-08-2018: Bounding evidence and chain ID
|
||||||
|
13-01-2019: Add section on MaxBytes vs MaxDataBytes
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
@@ -20,6 +21,32 @@ We should just remove MaxTxs all together and stick with MaxBytes, and have a
|
|||||||
But we can't just reap BlockSize.MaxBytes, since MaxBytes is for the entire block,
|
But we can't just reap BlockSize.MaxBytes, since MaxBytes is for the entire block,
|
||||||
not for the txs inside the block. There's extra amino overhead + the actual
|
not for the txs inside the block. There's extra amino overhead + the actual
|
||||||
headers on top of the actual transactions + evidence + last commit.
|
headers on top of the actual transactions + evidence + last commit.
|
||||||
|
We could also consider using a MaxDataBytes instead of or in addition to MaxBytes.
|
||||||
|
|
||||||
|
## MaxBytes vs MaxDataBytes
|
||||||
|
|
||||||
|
The [PR #3045](https://github.com/tendermint/tendermint/pull/3045) suggested
|
||||||
|
additional clarity/justification was necessary here, wither respect to the use
|
||||||
|
of MaxDataBytes in addition to, or instead of, MaxBytes.
|
||||||
|
|
||||||
|
MaxBytes provides a clear limit on the total size of a block that requires no
|
||||||
|
additional calculation if you want to use it to bound resource usage, and there
|
||||||
|
has been considerable discussions about optimizing tendermint around 1MB blocks.
|
||||||
|
Regardless, we need some maximum on the size of a block so we can avoid
|
||||||
|
unmarshalling blocks that are too big during the consensus, and it seems more
|
||||||
|
straightforward to provide a single fixed number for this rather than a
|
||||||
|
computation of "MaxDataBytes + everything else you need to make room for
|
||||||
|
(signatures, evidence, header)". MaxBytes provides a simple bound so we can
|
||||||
|
always say "blocks are less than X MB".
|
||||||
|
|
||||||
|
Having both MaxBytes and MaxDataBytes feels like unnecessary complexity. It's
|
||||||
|
not particularly surprising for MaxBytes to imply the maximum size of the
|
||||||
|
entire block (not just txs), one just has to know that a block includes header,
|
||||||
|
txs, evidence, votes. For more fine grained control over the txs included in the
|
||||||
|
block, there is the MaxGas. In practice, the MaxGas may be expected to do most of
|
||||||
|
the tx throttling, and the MaxBytes to just serve as an upper bound on the total
|
||||||
|
size. Applications can use MaxGas as a MaxDataBytes by just taking the gas for
|
||||||
|
every tx to be its size in bytes.
|
||||||
|
|
||||||
## Proposed solution
|
## Proposed solution
|
||||||
|
|
||||||
@@ -61,7 +88,7 @@ MaxXXX stayed the same.
|
|||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
Proposed.
|
Accepted.
|
||||||
|
|
||||||
## Consequences
|
## Consequences
|
||||||
|
|
||||||
|
@@ -126,6 +126,312 @@ func TestConsensusXXX(t *testing.T) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Consensus Executor
|
||||||
|
|
||||||
|
## Consensus Core
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Event interface{}
|
||||||
|
|
||||||
|
type EventNewHeight struct {
|
||||||
|
Height int64
|
||||||
|
ValidatorId int
|
||||||
|
}
|
||||||
|
|
||||||
|
type EventNewRound HeightAndRound
|
||||||
|
|
||||||
|
type EventProposal struct {
|
||||||
|
Height int64
|
||||||
|
Round int
|
||||||
|
Timestamp Time
|
||||||
|
BlockID BlockID
|
||||||
|
POLRound int
|
||||||
|
Sender int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Majority23PrevotesBlock struct {
|
||||||
|
Height int64
|
||||||
|
Round int
|
||||||
|
BlockID BlockID
|
||||||
|
}
|
||||||
|
|
||||||
|
type Majority23PrecommitBlock struct {
|
||||||
|
Height int64
|
||||||
|
Round int
|
||||||
|
BlockID BlockID
|
||||||
|
}
|
||||||
|
|
||||||
|
type HeightAndRound struct {
|
||||||
|
Height int64
|
||||||
|
Round int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Majority23PrevotesAny HeightAndRound
|
||||||
|
type Majority23PrecommitAny HeightAndRound
|
||||||
|
type TimeoutPropose HeightAndRound
|
||||||
|
type TimeoutPrevotes HeightAndRound
|
||||||
|
type TimeoutPrecommit HeightAndRound
|
||||||
|
|
||||||
|
|
||||||
|
type Message interface{}
|
||||||
|
|
||||||
|
type MessageProposal struct {
|
||||||
|
Height int64
|
||||||
|
Round int
|
||||||
|
BlockID BlockID
|
||||||
|
POLRound int
|
||||||
|
}
|
||||||
|
|
||||||
|
type VoteType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
VoteTypeUnknown VoteType = iota
|
||||||
|
Prevote
|
||||||
|
Precommit
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
type MessageVote struct {
|
||||||
|
Height int64
|
||||||
|
Round int
|
||||||
|
BlockID BlockID
|
||||||
|
Type VoteType
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type MessageDecision struct {
|
||||||
|
Height int64
|
||||||
|
Round int
|
||||||
|
BlockID BlockID
|
||||||
|
}
|
||||||
|
|
||||||
|
type TriggerTimeout struct {
|
||||||
|
Height int64
|
||||||
|
Round int
|
||||||
|
Duration Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type RoundStep int
|
||||||
|
|
||||||
|
const (
|
||||||
|
RoundStepUnknown RoundStep = iota
|
||||||
|
RoundStepPropose
|
||||||
|
RoundStepPrevote
|
||||||
|
RoundStepPrecommit
|
||||||
|
RoundStepCommit
|
||||||
|
)
|
||||||
|
|
||||||
|
type State struct {
|
||||||
|
Height int64
|
||||||
|
Round int
|
||||||
|
Step RoundStep
|
||||||
|
LockedValue BlockID
|
||||||
|
LockedRound int
|
||||||
|
ValidValue BlockID
|
||||||
|
ValidRound int
|
||||||
|
ValidatorId int
|
||||||
|
ValidatorSetSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
func proposer(height int64, round int) int {}
|
||||||
|
func getValue() BlockID {}
|
||||||
|
|
||||||
|
func Consensus(event Event, state State) (State, Message, TriggerTimeout) {
|
||||||
|
msg = nil
|
||||||
|
timeout = nil
|
||||||
|
switch event := event.(type) {
|
||||||
|
case EventNewHeight:
|
||||||
|
if event.Height > state.Height {
|
||||||
|
state.Height = event.Height
|
||||||
|
state.Round = -1
|
||||||
|
state.Step = RoundStepPropose
|
||||||
|
state.LockedValue = nil
|
||||||
|
state.LockedRound = -1
|
||||||
|
state.ValidValue = nil
|
||||||
|
state.ValidRound = -1
|
||||||
|
state.ValidatorId = event.ValidatorId
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
|
||||||
|
case EventNewRound:
|
||||||
|
if event.Height == state.Height and event.Round > state.Round {
|
||||||
|
state.Round = eventRound
|
||||||
|
state.Step = RoundStepPropose
|
||||||
|
if proposer(state.Height, state.Round) == state.ValidatorId {
|
||||||
|
proposal = state.ValidValue
|
||||||
|
if proposal == nil {
|
||||||
|
proposal = getValue()
|
||||||
|
}
|
||||||
|
msg = MessageProposal { state.Height, state.Round, proposal, state.ValidRound }
|
||||||
|
}
|
||||||
|
timeout = TriggerTimeout { state.Height, state.Round, timeoutPropose(state.Round) }
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
|
||||||
|
case EventProposal:
|
||||||
|
if event.Height == state.Height and event.Round == state.Round and
|
||||||
|
event.Sender == proposal(state.Height, state.Round) and state.Step == RoundStepPropose {
|
||||||
|
if event.POLRound >= state.LockedRound or event.BlockID == state.BlockID or state.LockedRound == -1 {
|
||||||
|
msg = MessageVote { state.Height, state.Round, event.BlockID, Prevote }
|
||||||
|
}
|
||||||
|
state.Step = RoundStepPrevote
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
|
||||||
|
case TimeoutPropose:
|
||||||
|
if event.Height == state.Height and event.Round == state.Round and state.Step == RoundStepPropose {
|
||||||
|
msg = MessageVote { state.Height, state.Round, nil, Prevote }
|
||||||
|
state.Step = RoundStepPrevote
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
|
||||||
|
case Majority23PrevotesBlock:
|
||||||
|
if event.Height == state.Height and event.Round == state.Round and state.Step >= RoundStepPrevote and event.Round > state.ValidRound {
|
||||||
|
state.ValidRound = event.Round
|
||||||
|
state.ValidValue = event.BlockID
|
||||||
|
if state.Step == RoundStepPrevote {
|
||||||
|
state.LockedRound = event.Round
|
||||||
|
state.LockedValue = event.BlockID
|
||||||
|
msg = MessageVote { state.Height, state.Round, event.BlockID, Precommit }
|
||||||
|
state.Step = RoundStepPrecommit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
|
||||||
|
case Majority23PrevotesAny:
|
||||||
|
if event.Height == state.Height and event.Round == state.Round and state.Step == RoundStepPrevote {
|
||||||
|
timeout = TriggerTimeout { state.Height, state.Round, timeoutPrevote(state.Round) }
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
|
||||||
|
case TimeoutPrevote:
|
||||||
|
if event.Height == state.Height and event.Round == state.Round and state.Step == RoundStepPrevote {
|
||||||
|
msg = MessageVote { state.Height, state.Round, nil, Precommit }
|
||||||
|
state.Step = RoundStepPrecommit
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
|
||||||
|
case Majority23PrecommitBlock:
|
||||||
|
if event.Height == state.Height {
|
||||||
|
state.Step = RoundStepCommit
|
||||||
|
state.LockedValue = event.BlockID
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
|
||||||
|
case Majority23PrecommitAny:
|
||||||
|
if event.Height == state.Height and event.Round == state.Round {
|
||||||
|
timeout = TriggerTimeout { state.Height, state.Round, timeoutPrecommit(state.Round) }
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
|
||||||
|
case TimeoutPrecommit:
|
||||||
|
if event.Height == state.Height and event.Round == state.Round {
|
||||||
|
state.Round = state.Round + 1
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConsensusExecutor() {
|
||||||
|
proposal = nil
|
||||||
|
votes = HeightVoteSet { Height: 1 }
|
||||||
|
state = State {
|
||||||
|
Height: 1
|
||||||
|
Round: 0
|
||||||
|
Step: RoundStepPropose
|
||||||
|
LockedValue: nil
|
||||||
|
LockedRound: -1
|
||||||
|
ValidValue: nil
|
||||||
|
ValidRound: -1
|
||||||
|
}
|
||||||
|
|
||||||
|
event = EventNewHeight {1, id}
|
||||||
|
state, msg, timeout = Consensus(event, state)
|
||||||
|
|
||||||
|
event = EventNewRound {state.Height, 0}
|
||||||
|
state, msg, timeout = Consensus(event, state)
|
||||||
|
|
||||||
|
if msg != nil {
|
||||||
|
send msg
|
||||||
|
}
|
||||||
|
|
||||||
|
if timeout != nil {
|
||||||
|
trigger timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case message := <- msgCh:
|
||||||
|
switch msg := message.(type) {
|
||||||
|
case MessageProposal:
|
||||||
|
|
||||||
|
case MessageVote:
|
||||||
|
if msg.Height == state.Height {
|
||||||
|
newVote = votes.AddVote(msg)
|
||||||
|
if newVote {
|
||||||
|
switch msg.Type {
|
||||||
|
case Prevote:
|
||||||
|
prevotes = votes.Prevotes(msg.Round)
|
||||||
|
if prevotes.WeakCertificate() and msg.Round > state.Round {
|
||||||
|
event = EventNewRound { msg.Height, msg.Round }
|
||||||
|
state, msg, timeout = Consensus(event, state)
|
||||||
|
state = handleStateChange(state, msg, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
if blockID, ok = prevotes.TwoThirdsMajority(); ok and blockID != nil {
|
||||||
|
if msg.Round == state.Round and hasBlock(blockID) {
|
||||||
|
event = Majority23PrevotesBlock { msg.Height, msg.Round, blockID }
|
||||||
|
state, msg, timeout = Consensus(event, state)
|
||||||
|
state = handleStateChange(state, msg, timeout)
|
||||||
|
}
|
||||||
|
if proposal != nil and proposal.POLRound == msg.Round and hasBlock(blockID) {
|
||||||
|
event = EventProposal {
|
||||||
|
Height: state.Height
|
||||||
|
Round: state.Round
|
||||||
|
BlockID: blockID
|
||||||
|
POLRound: proposal.POLRound
|
||||||
|
Sender: message.Sender
|
||||||
|
}
|
||||||
|
state, msg, timeout = Consensus(event, state)
|
||||||
|
state = handleStateChange(state, msg, timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if prevotes.HasTwoThirdsAny() and msg.Round == state.Round {
|
||||||
|
event = Majority23PrevotesAny { msg.Height, msg.Round, blockID }
|
||||||
|
state, msg, timeout = Consensus(event, state)
|
||||||
|
state = handleStateChange(state, msg, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
case Precommit:
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case timeout := <- timeoutCh:
|
||||||
|
|
||||||
|
case block := <- blockCh:
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleStateChange(state, msg, timeout) State {
|
||||||
|
if state.Step == Commit {
|
||||||
|
state = ExecuteBlock(state.LockedValue)
|
||||||
|
}
|
||||||
|
if msg != nil {
|
||||||
|
send msg
|
||||||
|
}
|
||||||
|
if timeout != nil {
|
||||||
|
trigger timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
### Implementation roadmap
|
### Implementation roadmap
|
||||||
|
|
||||||
* implement proposed implementation
|
* implement proposed implementation
|
||||||
|
BIN
docs/imgs/cosmos-tendermint-stack-4k.jpg
Normal file
BIN
docs/imgs/cosmos-tendermint-stack-4k.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 625 KiB |
@@ -70,10 +70,6 @@ Tendermint is in essence similar software, but with two key differences:
|
|||||||
the application logic that's right for them, from key-value store to
|
the application logic that's right for them, from key-value store to
|
||||||
cryptocurrency to e-voting platform and beyond.
|
cryptocurrency to e-voting platform and beyond.
|
||||||
|
|
||||||
The layout of this Tendermint website content is also ripped directly
|
|
||||||
and without shame from [consul.io](https://www.consul.io/) and the other
|
|
||||||
[Hashicorp sites](https://www.hashicorp.com/#tools).
|
|
||||||
|
|
||||||
### Bitcoin, Ethereum, etc.
|
### Bitcoin, Ethereum, etc.
|
||||||
|
|
||||||
Tendermint emerged in the tradition of cryptocurrencies like Bitcoin,
|
Tendermint emerged in the tradition of cryptocurrencies like Bitcoin,
|
||||||
|
@@ -1,20 +1,17 @@
|
|||||||
# Docker Compose
|
# Docker Compose
|
||||||
|
|
||||||
With Docker Compose, we can spin up local testnets in a single command:
|
With Docker Compose, you can spin up local testnets with a single command.
|
||||||
|
|
||||||
```
|
|
||||||
make localnet-start
|
|
||||||
```
|
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- [Install tendermint](/docs/install.md)
|
1. [Install tendermint](/docs/introduction/install.md)
|
||||||
- [Install docker](https://docs.docker.com/engine/installation/)
|
2. [Install docker](https://docs.docker.com/engine/installation/)
|
||||||
- [Install docker-compose](https://docs.docker.com/compose/install/)
|
3. [Install docker-compose](https://docs.docker.com/compose/install/)
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
Build the `tendermint` binary and the `tendermint/localnode` docker image.
|
Build the `tendermint` binary and, optionally, the `tendermint/localnode`
|
||||||
|
docker image.
|
||||||
|
|
||||||
Note the binary will be mounted into the container so it can be updated without
|
Note the binary will be mounted into the container so it can be updated without
|
||||||
rebuilding the image.
|
rebuilding the image.
|
||||||
@@ -25,11 +22,10 @@ cd $GOPATH/src/github.com/tendermint/tendermint
|
|||||||
# Build the linux binary in ./build
|
# Build the linux binary in ./build
|
||||||
make build-linux
|
make build-linux
|
||||||
|
|
||||||
# Build tendermint/localnode image
|
# (optionally) Build tendermint/localnode image
|
||||||
make build-docker-localnode
|
make build-docker-localnode
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Run a testnet
|
## Run a testnet
|
||||||
|
|
||||||
To start a 4 node testnet run:
|
To start a 4 node testnet run:
|
||||||
@@ -38,9 +34,13 @@ To start a 4 node testnet run:
|
|||||||
make localnet-start
|
make localnet-start
|
||||||
```
|
```
|
||||||
|
|
||||||
The nodes bind their RPC servers to ports 26657, 26660, 26662, and 26664 on the host.
|
The nodes bind their RPC servers to ports 26657, 26660, 26662, and 26664 on the
|
||||||
|
host.
|
||||||
|
|
||||||
This file creates a 4-node network using the localnode image.
|
This file creates a 4-node network using the localnode image.
|
||||||
The nodes of the network expose their P2P and RPC endpoints to the host machine on ports 26656-26657, 26659-26660, 26661-26662, and 26663-26664 respectively.
|
|
||||||
|
The nodes of the network expose their P2P and RPC endpoints to the host machine
|
||||||
|
on ports 26656-26657, 26659-26660, 26661-26662, and 26663-26664 respectively.
|
||||||
|
|
||||||
To update the binary, just rebuild it and restart the nodes:
|
To update the binary, just rebuild it and restart the nodes:
|
||||||
|
|
||||||
@@ -52,34 +52,40 @@ make localnet-start
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
The `make localnet-start` creates files for a 4-node testnet in `./build` by calling the `tendermint testnet` command.
|
The `make localnet-start` creates files for a 4-node testnet in `./build` by
|
||||||
|
calling the `tendermint testnet` command.
|
||||||
|
|
||||||
The `./build` directory is mounted to the `/tendermint` mount point to attach the binary and config files to the container.
|
The `./build` directory is mounted to the `/tendermint` mount point to attach
|
||||||
|
the binary and config files to the container.
|
||||||
|
|
||||||
For instance, to create a single node testnet:
|
To change the number of validators / non-validators change the `localnet-start` Makefile target:
|
||||||
|
|
||||||
|
```
|
||||||
|
localnet-start: localnet-stop
|
||||||
|
@if ! [ -f build/node0/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/tendermint:Z tendermint/localnode testnet --v 5 --n 3 --o . --populate-persistent-peers --starting-ip-address 192.167.10.2 ; fi
|
||||||
|
docker-compose up
|
||||||
|
```
|
||||||
|
|
||||||
|
The command now will generate config files for 5 validators and 3
|
||||||
|
non-validators network.
|
||||||
|
|
||||||
|
Before running it, don't forget to cleanup the old files:
|
||||||
|
|
||||||
```
|
```
|
||||||
cd $GOPATH/src/github.com/tendermint/tendermint
|
cd $GOPATH/src/github.com/tendermint/tendermint
|
||||||
|
|
||||||
# Clear the build folder
|
# Clear the build folder
|
||||||
rm -rf ./build
|
rm -rf ./build/node*
|
||||||
|
|
||||||
# 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
|
## 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.
|
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
|
## 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.
|
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.
|
||||||
|
4670
docs/package-lock.json
generated
4670
docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"dependencies": {
|
|
||||||
"prettier": "^1.13.7",
|
|
||||||
"remark-cli": "^5.0.0",
|
|
||||||
"remark-lint-no-dead-urls": "^0.3.0",
|
|
||||||
"remark-lint-write-good": "^1.0.3",
|
|
||||||
"textlint": "^10.2.1",
|
|
||||||
"textlint-rule-stop-words": "^1.0.3"
|
|
||||||
},
|
|
||||||
"name": "tendermint",
|
|
||||||
"description": "Tendermint Core Documentation",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"main": "README.md",
|
|
||||||
"devDependencies": {},
|
|
||||||
"scripts": {
|
|
||||||
"lint:json": "prettier \"**/*.json\" --write",
|
|
||||||
"lint:md": "prettier \"**/*.md\" --write && remark . && textlint \"md/**\"",
|
|
||||||
"lint": "yarn lint:json && yarn lint:md"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git+https://github.com/tendermint/tendermint.git"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"tendermint",
|
|
||||||
"blockchain"
|
|
||||||
],
|
|
||||||
"author": "Tendermint",
|
|
||||||
"license": "ISC",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/tendermint/tendermint/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://tendermint.com/docs/",
|
|
||||||
"remarkConfig": {
|
|
||||||
"plugins": [
|
|
||||||
"remark-lint-no-dead-urls",
|
|
||||||
"remark-lint-write-good"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@@ -14,31 +14,31 @@ please submit them to our [bug bounty](https://tendermint.com/security)!
|
|||||||
|
|
||||||
### Data Structures
|
### Data Structures
|
||||||
|
|
||||||
- [Encoding and Digests](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/encoding.md)
|
- [Encoding and Digests](./blockchain/encoding.md)
|
||||||
- [Blockchain](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/blockchain.md)
|
- [Blockchain](./blockchain/blockchain.md)
|
||||||
- [State](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/state.md)
|
- [State](./blockchain/state.md)
|
||||||
|
|
||||||
### Consensus Protocol
|
### Consensus Protocol
|
||||||
|
|
||||||
- [Consensus Algorithm](/docs/spec/consensus/consensus.md)
|
- [Consensus Algorithm](./consensus/consensus.md)
|
||||||
- [Creating a proposal](/docs/spec/consensus/creating-proposal.md)
|
- [Creating a proposal](./consensus/creating-proposal.md)
|
||||||
- [Time](/docs/spec/consensus/bft-time.md)
|
- [Time](./consensus/bft-time.md)
|
||||||
- [Light-Client](/docs/spec/consensus/light-client.md)
|
- [Light-Client](./consensus/light-client.md)
|
||||||
|
|
||||||
### P2P and Network Protocols
|
### P2P and Network Protocols
|
||||||
|
|
||||||
- [The Base P2P Layer](https://github.com/tendermint/tendermint/tree/master/docs/spec/p2p): multiplex the protocols ("reactors") on authenticated and encrypted TCP connections
|
- [The Base P2P Layer](./p2p/): multiplex the protocols ("reactors") on authenticated and encrypted TCP connections
|
||||||
- [Peer Exchange (PEX)](https://github.com/tendermint/tendermint/tree/master/docs/spec/reactors/pex): gossip known peer addresses so peers can find each other
|
- [Peer Exchange (PEX)](./reactors/pex/): gossip known peer addresses so peers can find each other
|
||||||
- [Block Sync](https://github.com/tendermint/tendermint/tree/master/docs/spec/reactors/block_sync): gossip blocks so peers can catch up quickly
|
- [Block Sync](./reactors/block_sync/): gossip blocks so peers can catch up quickly
|
||||||
- [Consensus](https://github.com/tendermint/tendermint/tree/master/docs/spec/reactors/consensus): gossip votes and block parts so new blocks can be committed
|
- [Consensus](./reactors/consensus/): gossip votes and block parts so new blocks can be committed
|
||||||
- [Mempool](https://github.com/tendermint/tendermint/tree/master/docs/spec/reactors/mempool): gossip transactions so they get included in blocks
|
- [Mempool](./reactors/mempool/): gossip transactions so they get included in blocks
|
||||||
- Evidence: Forthcoming, see [this issue](https://github.com/tendermint/tendermint/issues/2329).
|
- [Evidence](./reactors/evidence/): sending invalid evidence will stop the peer
|
||||||
|
|
||||||
### Software
|
### Software
|
||||||
|
|
||||||
- [ABCI](/docs/spec/software/abci.md): Details about interactions between the
|
- [ABCI](./software/abci.md): Details about interactions between the
|
||||||
application and consensus engine over ABCI
|
application and consensus engine over ABCI
|
||||||
- [Write-Ahead Log](/docs/spec/software/wal.md): Details about how the consensus
|
- [Write-Ahead Log](./software/wal.md): Details about how the consensus
|
||||||
engine preserves data and recovers from crash failures
|
engine preserves data and recovers from crash failures
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
@@ -166,6 +166,11 @@ the tags will be hashed into the next block header.
|
|||||||
The application may set the validator set during InitChain, and update it during
|
The application may set the validator set during InitChain, and update it during
|
||||||
EndBlock.
|
EndBlock.
|
||||||
|
|
||||||
|
Note that the maximum total power of the validator set is bounded by
|
||||||
|
`MaxTotalVotingPower = MaxInt64 / 8`. Applications are responsible for ensuring
|
||||||
|
they do not make changes to the validator set that cause it to exceed this
|
||||||
|
limit.
|
||||||
|
|
||||||
### InitChain
|
### InitChain
|
||||||
|
|
||||||
ResponseInitChain can return a list of validators.
|
ResponseInitChain can return a list of validators.
|
||||||
@@ -206,6 +211,7 @@ following rules:
|
|||||||
- if the validator does not already exist, it will be added to the validator
|
- if the validator does not already exist, it will be added to the validator
|
||||||
set with the given power
|
set with the given power
|
||||||
- if the validator does already exist, its power will be adjusted to the given power
|
- if the validator does already exist, its power will be adjusted to the given power
|
||||||
|
- the total power of the new validator set must not exceed MaxTotalVotingPower
|
||||||
|
|
||||||
Note the updates returned in block `H` will only take effect at block `H+2`.
|
Note the updates returned in block `H` will only take effect at block `H+2`.
|
||||||
|
|
||||||
@@ -407,21 +413,22 @@ If `storeBlockHeight == stateBlockHeight && appBlockHeight < storeBlockHeight`,
|
|||||||
replay all blocks in full from `appBlockHeight` to `storeBlockHeight`.
|
replay all blocks in full from `appBlockHeight` to `storeBlockHeight`.
|
||||||
This happens if we completed processing the block, but the app forgot its height.
|
This happens if we completed processing the block, but the app forgot its height.
|
||||||
|
|
||||||
If `storeBlockHeight == stateBlockHeight && appBlockHeight == storeBlockHeight`, we're done
|
If `storeBlockHeight == stateBlockHeight && appBlockHeight == storeBlockHeight`, we're done.
|
||||||
This happens if we crashed at an opportune spot.
|
This happens if we crashed at an opportune spot.
|
||||||
|
|
||||||
If `storeBlockHeight == stateBlockHeight+1`
|
If `storeBlockHeight == stateBlockHeight+1`
|
||||||
This happens if we started processing the block but didn't finish.
|
This happens if we started processing the block but didn't finish.
|
||||||
|
|
||||||
If `appBlockHeight < stateBlockHeight`
|
If `appBlockHeight < stateBlockHeight`
|
||||||
replay all blocks in full from `appBlockHeight` to `storeBlockHeight-1`,
|
replay all blocks in full from `appBlockHeight` to `storeBlockHeight-1`,
|
||||||
and replay the block at `storeBlockHeight` using the WAL.
|
and replay the block at `storeBlockHeight` using the WAL.
|
||||||
This happens if the app forgot the last block it committed.
|
This happens if the app forgot the last block it committed.
|
||||||
|
|
||||||
If `appBlockHeight == stateBlockHeight`,
|
If `appBlockHeight == stateBlockHeight`,
|
||||||
replay the last block (storeBlockHeight) in full.
|
replay the last block (storeBlockHeight) in full.
|
||||||
This happens if we crashed before the app finished Commit
|
This happens if we crashed before the app finished Commit
|
||||||
|
|
||||||
|
If `appBlockHeight == storeBlockHeight`
|
||||||
|
update the state using the saved ABCI responses but dont run the block against the real app.
|
||||||
|
This happens if we crashed after the app finished Commit but before Tendermint saved the state.
|
||||||
|
|
||||||
If appBlockHeight == storeBlockHeight {
|
|
||||||
update the state using the saved ABCI responses but dont run the block against the real app.
|
|
||||||
This happens if we crashed after the app finished Commit but before Tendermint saved the state.
|
|
||||||
|
@@ -51,7 +51,7 @@ type Header struct {
|
|||||||
|
|
||||||
// hashes of block data
|
// hashes of block data
|
||||||
LastCommitHash []byte // commit from validators from the last block
|
LastCommitHash []byte // commit from validators from the last block
|
||||||
DataHash []byte // Merkle root of transactions
|
DataHash []byte // MerkleRoot of transaction hashes
|
||||||
|
|
||||||
// hashes from the app output from the prev block
|
// hashes from the app output from the prev block
|
||||||
ValidatorsHash []byte // validators for the current block
|
ValidatorsHash []byte // validators for the current block
|
||||||
@@ -83,25 +83,27 @@ type Version struct {
|
|||||||
## BlockID
|
## BlockID
|
||||||
|
|
||||||
The `BlockID` contains two distinct Merkle roots of the block.
|
The `BlockID` contains two distinct Merkle roots of the block.
|
||||||
The first, used as the block's main hash, is the Merkle root
|
The first, used as the block's main hash, is the MerkleRoot
|
||||||
of all the fields in the header. The second, used for secure gossipping of
|
of all the fields in the header (ie. `MerkleRoot(header)`.
|
||||||
the block during consensus, is the Merkle root of the complete serialized block
|
The second, used for secure gossipping of the block during consensus,
|
||||||
cut into parts. The `BlockID` includes these two hashes, as well as the number of
|
is the MerkleRoot of the complete serialized block
|
||||||
parts.
|
cut into parts (ie. `MerkleRoot(MakeParts(block))`).
|
||||||
|
The `BlockID` includes these two hashes, as well as the number of
|
||||||
|
parts (ie. `len(MakeParts(block))`)
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type BlockID struct {
|
type BlockID struct {
|
||||||
Hash []byte
|
Hash []byte
|
||||||
Parts PartsHeader
|
PartsHeader PartSetHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
type PartsHeader struct {
|
type PartSetHeader struct {
|
||||||
Hash []byte
|
|
||||||
Total int32
|
Total int32
|
||||||
|
Hash []byte
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
TODO: link to details of merkle sums.
|
See [MerkleRoot](/docs/spec/blockchain/encoding.md#MerkleRoot) for details.
|
||||||
|
|
||||||
## Time
|
## Time
|
||||||
|
|
||||||
@@ -109,10 +111,6 @@ Tendermint uses the
|
|||||||
[Google.Protobuf.WellKnownTypes.Timestamp](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/timestamp)
|
[Google.Protobuf.WellKnownTypes.Timestamp](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/timestamp)
|
||||||
format, which uses two integers, one for Seconds and for Nanoseconds.
|
format, which uses two integers, one for Seconds and for Nanoseconds.
|
||||||
|
|
||||||
NOTE: there is currently a small divergence between Tendermint and the
|
|
||||||
Google.Protobuf.WellKnownTypes.Timestamp that should be resolved. See [this
|
|
||||||
issue](https://github.com/tendermint/go-amino/issues/223) for details.
|
|
||||||
|
|
||||||
## Data
|
## Data
|
||||||
|
|
||||||
Data is just a wrapper for a list of transactions, where transactions are
|
Data is just a wrapper for a list of transactions, where transactions are
|
||||||
@@ -146,12 +144,12 @@ The vote includes information about the validator signing it.
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
type Vote struct {
|
type Vote struct {
|
||||||
Type SignedMsgType // byte
|
Type byte
|
||||||
Height int64
|
Height int64
|
||||||
Round int
|
Round int
|
||||||
Timestamp time.Time
|
|
||||||
BlockID BlockID
|
BlockID BlockID
|
||||||
ValidatorAddress Address
|
Timestamp Time
|
||||||
|
ValidatorAddress []byte
|
||||||
ValidatorIndex int
|
ValidatorIndex int
|
||||||
Signature []byte
|
Signature []byte
|
||||||
}
|
}
|
||||||
@@ -164,8 +162,8 @@ a _precommit_ has `vote.Type == 2`.
|
|||||||
## Signature
|
## Signature
|
||||||
|
|
||||||
Signatures in Tendermint are raw bytes representing the underlying signature.
|
Signatures in Tendermint are raw bytes representing the underlying signature.
|
||||||
The only signature scheme currently supported for Tendermint validators is
|
|
||||||
ED25519. The signature is the raw 64-byte ED25519 signature.
|
See the [signature spec](/docs/spec/blockchain/encoding.md#key-types) for more.
|
||||||
|
|
||||||
## EvidenceData
|
## EvidenceData
|
||||||
|
|
||||||
@@ -192,6 +190,8 @@ type DuplicateVoteEvidence struct {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
See the [pubkey spec](/docs/spec/blockchain/encoding.md#key-types) for more.
|
||||||
|
|
||||||
## Validation
|
## Validation
|
||||||
|
|
||||||
Here we describe the validation rules for every element in a block.
|
Here we describe the validation rules for every element in a block.
|
||||||
@@ -209,7 +209,7 @@ the current version of the `state` corresponds to the state
|
|||||||
after executing transactions from the `prevBlock`.
|
after executing transactions from the `prevBlock`.
|
||||||
Elements of an object are accessed as expected,
|
Elements of an object are accessed as expected,
|
||||||
ie. `block.Header`.
|
ie. `block.Header`.
|
||||||
See [here](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/state.md) for the definition of `state`.
|
See the [definition of `State`](/docs/spec/blockchain/state.md).
|
||||||
|
|
||||||
### Header
|
### Header
|
||||||
|
|
||||||
@@ -230,7 +230,7 @@ The block version must match the state version.
|
|||||||
len(block.ChainID) < 50
|
len(block.ChainID) < 50
|
||||||
```
|
```
|
||||||
|
|
||||||
ChainID must be maximum 50 UTF-8 symbols.
|
ChainID must be less than 50 bytes.
|
||||||
|
|
||||||
### Height
|
### Height
|
||||||
|
|
||||||
@@ -288,28 +288,25 @@ The first block has `block.Header.TotalTxs = block.Header.NumberTxs`.
|
|||||||
LastBlockID is the previous block's BlockID:
|
LastBlockID is the previous block's BlockID:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
prevBlockParts := MakeParts(prevBlock, state.LastConsensusParams.BlockGossip.BlockPartSize)
|
prevBlockParts := MakeParts(prevBlock)
|
||||||
block.Header.LastBlockID == BlockID {
|
block.Header.LastBlockID == BlockID {
|
||||||
Hash: SimpleMerkleRoot(prevBlock.Header),
|
Hash: MerkleRoot(prevBlock.Header),
|
||||||
PartsHeader{
|
PartsHeader{
|
||||||
Hash: SimpleMerkleRoot(prevBlockParts),
|
Hash: MerkleRoot(prevBlockParts),
|
||||||
Total: len(prevBlockParts),
|
Total: len(prevBlockParts),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: it depends on the ConsensusParams,
|
|
||||||
which are held in the `state` and may be updated by the application.
|
|
||||||
|
|
||||||
The first block has `block.Header.LastBlockID == BlockID{}`.
|
The first block has `block.Header.LastBlockID == BlockID{}`.
|
||||||
|
|
||||||
### LastCommitHash
|
### LastCommitHash
|
||||||
|
|
||||||
```go
|
```go
|
||||||
block.Header.LastCommitHash == SimpleMerkleRoot(block.LastCommit)
|
block.Header.LastCommitHash == MerkleRoot(block.LastCommit.Precommits)
|
||||||
```
|
```
|
||||||
|
|
||||||
Simple Merkle root of the votes included in the block.
|
MerkleRoot of the votes included in the block.
|
||||||
These are the votes that committed the previous block.
|
These are the votes that committed the previous block.
|
||||||
|
|
||||||
The first block has `block.Header.LastCommitHash == []byte{}`
|
The first block has `block.Header.LastCommitHash == []byte{}`
|
||||||
@@ -317,37 +314,42 @@ The first block has `block.Header.LastCommitHash == []byte{}`
|
|||||||
### DataHash
|
### DataHash
|
||||||
|
|
||||||
```go
|
```go
|
||||||
block.Header.DataHash == SimpleMerkleRoot(block.Txs.Txs)
|
block.Header.DataHash == MerkleRoot(Hashes(block.Txs.Txs))
|
||||||
```
|
```
|
||||||
|
|
||||||
Simple Merkle root of the transactions included in the block.
|
MerkleRoot of the hashes of transactions included in the block.
|
||||||
|
|
||||||
|
Note the transactions are hashed before being included in the Merkle tree,
|
||||||
|
so the leaves of the Merkle tree are the hashes, not the transactions
|
||||||
|
themselves. This is because transaction hashes are regularly used as identifiers for
|
||||||
|
transactions.
|
||||||
|
|
||||||
### ValidatorsHash
|
### ValidatorsHash
|
||||||
|
|
||||||
```go
|
```go
|
||||||
block.ValidatorsHash == SimpleMerkleRoot(state.Validators)
|
block.ValidatorsHash == MerkleRoot(state.Validators)
|
||||||
```
|
```
|
||||||
|
|
||||||
Simple Merkle root of the current validator set that is committing the block.
|
MerkleRoot of the current validator set that is committing the block.
|
||||||
This can be used to validate the `LastCommit` included in the next block.
|
This can be used to validate the `LastCommit` included in the next block.
|
||||||
|
|
||||||
### NextValidatorsHash
|
### NextValidatorsHash
|
||||||
|
|
||||||
```go
|
```go
|
||||||
block.NextValidatorsHash == SimpleMerkleRoot(state.NextValidators)
|
block.NextValidatorsHash == MerkleRoot(state.NextValidators)
|
||||||
```
|
```
|
||||||
|
|
||||||
Simple Merkle root of the next validator set that will be the validator set that commits the next block.
|
MerkleRoot of the next validator set that will be the validator set that commits the next block.
|
||||||
This is included so that the current validator set gets a chance to sign the
|
This is included so that the current validator set gets a chance to sign the
|
||||||
next validator sets Merkle root.
|
next validator sets Merkle root.
|
||||||
|
|
||||||
### ConsensusParamsHash
|
### ConsensusHash
|
||||||
|
|
||||||
```go
|
```go
|
||||||
block.ConsensusParamsHash == TMHASH(amino(state.ConsensusParams))
|
block.ConsensusHash == state.ConsensusParams.Hash()
|
||||||
```
|
```
|
||||||
|
|
||||||
Hash of the amino-encoded consensus parameters.
|
Hash of the amino-encoding of a subset of the consensus parameters.
|
||||||
|
|
||||||
### AppHash
|
### AppHash
|
||||||
|
|
||||||
@@ -362,20 +364,20 @@ The first block has `block.Header.AppHash == []byte{}`.
|
|||||||
### LastResultsHash
|
### LastResultsHash
|
||||||
|
|
||||||
```go
|
```go
|
||||||
block.ResultsHash == SimpleMerkleRoot(state.LastResults)
|
block.ResultsHash == MerkleRoot(state.LastResults)
|
||||||
```
|
```
|
||||||
|
|
||||||
Simple Merkle root of the results of the transactions in the previous block.
|
MerkleRoot of the results of the transactions in the previous block.
|
||||||
|
|
||||||
The first block has `block.Header.ResultsHash == []byte{}`.
|
The first block has `block.Header.ResultsHash == []byte{}`.
|
||||||
|
|
||||||
## EvidenceHash
|
## EvidenceHash
|
||||||
|
|
||||||
```go
|
```go
|
||||||
block.EvidenceHash == SimpleMerkleRoot(block.Evidence)
|
block.EvidenceHash == MerkleRoot(block.Evidence)
|
||||||
```
|
```
|
||||||
|
|
||||||
Simple Merkle root of the evidence of Byzantine behaviour included in this block.
|
MerkleRoot of the evidence of Byzantine behaviour included in this block.
|
||||||
|
|
||||||
### ProposerAddress
|
### ProposerAddress
|
||||||
|
|
||||||
|
@@ -30,6 +30,12 @@ For example, the byte-array `[0xA, 0xB]` would be encoded as `0x020A0B`,
|
|||||||
while a byte-array containing 300 entires beginning with `[0xA, 0xB, ...]` would
|
while a byte-array containing 300 entires beginning with `[0xA, 0xB, ...]` would
|
||||||
be encoded as `0xAC020A0B...` where `0xAC02` is the UVarint encoding of 300.
|
be encoded as `0xAC020A0B...` where `0xAC02` is the UVarint encoding of 300.
|
||||||
|
|
||||||
|
## Hashing
|
||||||
|
|
||||||
|
Tendermint uses `SHA256` as its hash function.
|
||||||
|
Objects are always Amino encoded before being hashed.
|
||||||
|
So `SHA256(obj)` is short for `SHA256(AminoEncode(obj))`.
|
||||||
|
|
||||||
## Public Key Cryptography
|
## Public Key Cryptography
|
||||||
|
|
||||||
Tendermint uses Amino to distinguish between different types of private keys,
|
Tendermint uses Amino to distinguish between different types of private keys,
|
||||||
@@ -68,23 +74,27 @@ For example, the 33-byte (or 0x21-byte in hex) Secp256k1 pubkey
|
|||||||
would be encoded as
|
would be encoded as
|
||||||
`EB5AE98721020BD40F225A57ED383B440CF073BC5539D0341F5767D2BF2D78406D00475A2EE9`
|
`EB5AE98721020BD40F225A57ED383B440CF073BC5539D0341F5767D2BF2D78406D00475A2EE9`
|
||||||
|
|
||||||
### Addresses
|
### Key Types
|
||||||
|
|
||||||
Addresses for each public key types are computed as follows:
|
Each type specifies it's own pubkey, address, and signature format.
|
||||||
|
|
||||||
#### Ed25519
|
#### Ed25519
|
||||||
|
|
||||||
First 20-bytes of the SHA256 hash of the raw 32-byte public key:
|
TODO: pubkey
|
||||||
|
|
||||||
|
The address is the first 20-bytes of the SHA256 hash of the raw 32-byte public key:
|
||||||
|
|
||||||
```
|
```
|
||||||
address = SHA256(pubkey)[:20]
|
address = SHA256(pubkey)[:20]
|
||||||
```
|
```
|
||||||
|
|
||||||
NOTE: before v0.22.0, this was the RIPEMD160 of the Amino encoded public key.
|
The signature is the raw 64-byte ED25519 signature.
|
||||||
|
|
||||||
#### Secp256k1
|
#### Secp256k1
|
||||||
|
|
||||||
RIPEMD160 hash of the SHA256 hash of the OpenSSL compressed public key:
|
TODO: pubkey
|
||||||
|
|
||||||
|
The address is the RIPEMD160 hash of the SHA256 hash of the OpenSSL compressed public key:
|
||||||
|
|
||||||
```
|
```
|
||||||
address = RIPEMD160(SHA256(pubkey))
|
address = RIPEMD160(SHA256(pubkey))
|
||||||
@@ -92,12 +102,21 @@ address = RIPEMD160(SHA256(pubkey))
|
|||||||
|
|
||||||
This is the same as Bitcoin.
|
This is the same as Bitcoin.
|
||||||
|
|
||||||
|
The signature is the 64-byte concatenation of ECDSA `r` and `s` (ie. `r || s`),
|
||||||
|
where `s` is lexicographically less than its inverse, to prevent malleability.
|
||||||
|
This is like Ethereum, but without the extra byte for pubkey recovery, since
|
||||||
|
Tendermint assumes the pubkey is always provided anyway.
|
||||||
|
|
||||||
|
#### Multisig
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
## Other Common Types
|
## Other Common Types
|
||||||
|
|
||||||
### BitArray
|
### BitArray
|
||||||
|
|
||||||
The BitArray is used in block headers and some consensus messages to signal
|
The BitArray is used in some consensus messages to represent votes received from
|
||||||
whether or not something was done by each validator. BitArray is represented
|
validators, or parts received in a block. It is represented
|
||||||
with a struct containing the number of bits (`Bits`) and the bit-array itself
|
with a struct containing the number of bits (`Bits`) and the bit-array itself
|
||||||
encoded in base64 (`Elems`).
|
encoded in base64 (`Elems`).
|
||||||
|
|
||||||
@@ -119,24 +138,27 @@ representing `1` and `0`. Ie. the BitArray `10110` would be JSON encoded as
|
|||||||
Part is used to break up blocks into pieces that can be gossiped in parallel
|
Part is used to break up blocks into pieces that can be gossiped in parallel
|
||||||
and securely verified using a Merkle tree of the parts.
|
and securely verified using a Merkle tree of the parts.
|
||||||
|
|
||||||
Part contains the index of the part in the larger set (`Index`), the actual
|
Part contains the index of the part (`Index`), the actual
|
||||||
underlying data of the part (`Bytes`), and a simple Merkle proof that the part is contained in
|
underlying data of the part (`Bytes`), and a Merkle proof that the part is contained in
|
||||||
the larger set (`Proof`).
|
the set (`Proof`).
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Part struct {
|
type Part struct {
|
||||||
Index int
|
Index int
|
||||||
Bytes byte[]
|
Bytes []byte
|
||||||
Proof byte[]
|
Proof SimpleProof
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
See details of SimpleProof, below.
|
||||||
|
|
||||||
### MakeParts
|
### MakeParts
|
||||||
|
|
||||||
Encode an object using Amino and slice it into parts.
|
Encode an object using Amino and slice it into parts.
|
||||||
|
Tendermint uses a part size of 65536 bytes.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func MakeParts(obj interface{}, partSize int) []Part
|
func MakeParts(block Block) []Part
|
||||||
```
|
```
|
||||||
|
|
||||||
## Merkle Trees
|
## Merkle Trees
|
||||||
@@ -144,12 +166,17 @@ func MakeParts(obj interface{}, partSize int) []Part
|
|||||||
For an overview of Merkle trees, see
|
For an overview of Merkle trees, see
|
||||||
[wikipedia](https://en.wikipedia.org/wiki/Merkle_tree)
|
[wikipedia](https://en.wikipedia.org/wiki/Merkle_tree)
|
||||||
|
|
||||||
A Simple Tree is a simple compact binary tree for a static list of items. Simple Merkle trees are used in numerous places in Tendermint to compute a cryptographic digest of a data structure. In a Simple Tree, the transactions and validation signatures of a block are hashed using this simple merkle tree logic.
|
We use the RFC 6962 specification of a merkle tree, with sha256 as the hash function.
|
||||||
|
Merkle trees are used throughout Tendermint to compute a cryptographic digest of a data structure.
|
||||||
|
The differences between RFC 6962 and the simplest form a merkle tree are that:
|
||||||
|
|
||||||
If the number of items is not a power of two, the tree will not be full
|
1) leaf nodes and inner nodes have different hashes.
|
||||||
and some leaf nodes will be at different levels. Simple Tree tries to
|
This is for "second pre-image resistance", to prevent the proof to an inner node being valid as the proof of a leaf.
|
||||||
keep both sides of the tree the same size, but the left side may be one
|
The leaf nodes are `SHA256(0x00 || leaf_data)`, and inner nodes are `SHA256(0x01 || left_hash || right_hash)`.
|
||||||
greater, for example:
|
|
||||||
|
2) When the number of items isn't a power of two, the left half of the tree is as big as it could be.
|
||||||
|
(The smallest power of two less than the number of items) This allows new leaves to be added with less
|
||||||
|
recomputation. For example:
|
||||||
|
|
||||||
```
|
```
|
||||||
Simple Tree with 6 items Simple Tree with 7 items
|
Simple Tree with 6 items Simple Tree with 7 items
|
||||||
@@ -163,68 +190,79 @@ greater, for example:
|
|||||||
/ \ / \ / \ / \
|
/ \ / \ / \ / \
|
||||||
/ \ / \ / \ / \
|
/ \ / \ / \ / \
|
||||||
/ \ / \ / \ / \
|
/ \ / \ / \ / \
|
||||||
* h2 * h5 * * * h6
|
* * h4 h5 * * * h6
|
||||||
/ \ / \ / \ / \ / \
|
/ \ / \ / \ / \ / \
|
||||||
h0 h1 h3 h4 h0 h1 h2 h3 h4 h5
|
h0 h1 h2 h3 h0 h1 h2 h3 h4 h5
|
||||||
```
|
```
|
||||||
|
|
||||||
Tendermint always uses the `TMHASH` hash function, which is equivalent to
|
### MerkleRoot
|
||||||
SHA256:
|
|
||||||
|
|
||||||
```
|
The function `MerkleRoot` is a simple recursive function defined as follows:
|
||||||
func TMHASH(bz []byte) []byte {
|
|
||||||
return SHA256(bz)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Simple Merkle Root
|
|
||||||
|
|
||||||
The function `SimpleMerkleRoot` is a simple recursive function defined as follows:
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func SimpleMerkleRoot(hashes [][]byte) []byte{
|
// SHA256(0x00 || leaf)
|
||||||
switch len(hashes) {
|
func leafHash(leaf []byte) []byte {
|
||||||
case 0:
|
return tmhash.Sum(append(0x00, leaf...))
|
||||||
return nil
|
|
||||||
case 1:
|
|
||||||
return hashes[0]
|
|
||||||
default:
|
|
||||||
left := SimpleMerkleRoot(hashes[:(len(hashes)+1)/2])
|
|
||||||
right := SimpleMerkleRoot(hashes[(len(hashes)+1)/2:])
|
|
||||||
return SimpleConcatHash(left, right)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func SimpleConcatHash(left, right []byte) []byte{
|
// SHA256(0x01 || left || right)
|
||||||
left = encodeByteSlice(left)
|
func innerHash(left []byte, right []byte) []byte {
|
||||||
right = encodeByteSlice(right)
|
return tmhash.Sum(append(0x01, append(left, right...)...))
|
||||||
return TMHASH(append(left, right))
|
}
|
||||||
|
|
||||||
|
// largest power of 2 less than k
|
||||||
|
func getSplitPoint(k int) { ... }
|
||||||
|
|
||||||
|
func MerkleRoot(items [][]byte) []byte{
|
||||||
|
switch len(items) {
|
||||||
|
case 0:
|
||||||
|
return nil
|
||||||
|
case 1:
|
||||||
|
return leafHash(leafs[0])
|
||||||
|
default:
|
||||||
|
k := getSplitPoint(len(items))
|
||||||
|
left := MerkleRoot(items[:k])
|
||||||
|
right := MerkleRoot(items[k:])
|
||||||
|
return innerHash(left, right)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the leaves are Amino encoded as byte-arrays (ie. simple Uvarint length
|
Note: `MerkleRoot` operates on items which are arbitrary byte arrays, not
|
||||||
prefix) before being concatenated together and hashed.
|
necessarily hashes. For items which need to be hashed first, we introduce the
|
||||||
|
`Hashes` function:
|
||||||
|
|
||||||
Note: we will abuse notion and invoke `SimpleMerkleRoot` with arguments of type `struct` or type `[]struct`.
|
```
|
||||||
For `struct` arguments, we compute a `[][]byte` containing the hash of each
|
func Hashes(items [][]byte) [][]byte {
|
||||||
|
return SHA256 of each item
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: we will abuse notion and invoke `MerkleRoot` with arguments of type `struct` or type `[]struct`.
|
||||||
|
For `struct` arguments, we compute a `[][]byte` containing the amino encoding of each
|
||||||
field in the struct, in the same order the fields appear in the struct.
|
field in the struct, in the same order the fields appear in the struct.
|
||||||
For `[]struct` arguments, we compute a `[][]byte` by hashing the individual `struct` elements.
|
For `[]struct` arguments, we compute a `[][]byte` by amino encoding the individual `struct` elements.
|
||||||
|
|
||||||
### Simple Merkle Proof
|
### Simple Merkle Proof
|
||||||
|
|
||||||
Proof that a leaf is in a Merkle tree consists of a simple structure:
|
Proof that a leaf is in a Merkle tree is composed as follows:
|
||||||
|
|
||||||
```
|
```golang
|
||||||
type SimpleProof struct {
|
type SimpleProof struct {
|
||||||
|
Total int
|
||||||
|
Index int
|
||||||
|
LeafHash []byte
|
||||||
Aunts [][]byte
|
Aunts [][]byte
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Which is verified using the following:
|
Which is verified as follows:
|
||||||
|
|
||||||
```
|
```golang
|
||||||
func (proof SimpleProof) Verify(index, total int, leafHash, rootHash []byte) bool {
|
func (proof SimpleProof) Verify(rootHash []byte, leaf []byte) bool {
|
||||||
computedHash := computeHashFromAunts(index, total, leafHash, proof.Aunts)
|
assert(proof.LeafHash, leafHash(leaf)
|
||||||
|
|
||||||
|
computedHash := computeHashFromAunts(proof.Index, proof.Total, proof.LeafHash, proof.Aunts)
|
||||||
return computedHash == rootHash
|
return computedHash == rootHash
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,26 +276,18 @@ func computeHashFromAunts(index, total int, leafHash []byte, innerHashes [][]byt
|
|||||||
|
|
||||||
assert(len(innerHashes) > 0)
|
assert(len(innerHashes) > 0)
|
||||||
|
|
||||||
numLeft := (total + 1) / 2
|
numLeft := getSplitPoint(total) // largest power of 2 less than total
|
||||||
if index < numLeft {
|
if index < numLeft {
|
||||||
leftHash := computeHashFromAunts(index, numLeft, leafHash, innerHashes[:len(innerHashes)-1])
|
leftHash := computeHashFromAunts(index, numLeft, leafHash, innerHashes[:len(innerHashes)-1])
|
||||||
assert(leftHash != nil)
|
assert(leftHash != nil)
|
||||||
return SimpleHashFromTwoHashes(leftHash, innerHashes[len(innerHashes)-1])
|
return innerHash(leftHash, innerHashes[len(innerHashes)-1])
|
||||||
}
|
}
|
||||||
rightHash := computeHashFromAunts(index-numLeft, total-numLeft, leafHash, innerHashes[:len(innerHashes)-1])
|
rightHash := computeHashFromAunts(index-numLeft, total-numLeft, leafHash, innerHashes[:len(innerHashes)-1])
|
||||||
assert(rightHash != nil)
|
assert(rightHash != nil)
|
||||||
return SimpleHashFromTwoHashes(innerHashes[len(innerHashes)-1], rightHash)
|
return innerHash(innerHashes[len(innerHashes)-1], rightHash)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Simple Tree with Dictionaries
|
|
||||||
|
|
||||||
The Simple Tree is used to merkelize a list of items, so to merkelize a
|
|
||||||
(short) dictionary of key-value pairs, encode the dictionary as an
|
|
||||||
ordered list of `KVPair` structs. The block hash is such a hash
|
|
||||||
derived from all the fields of the block `Header`. The state hash is
|
|
||||||
similarly derived.
|
|
||||||
|
|
||||||
### IAVL+ Tree
|
### IAVL+ Tree
|
||||||
|
|
||||||
Because Tendermint only uses a Simple Merkle Tree, application developers are expect to use their own Merkle tree in their applications. For example, the IAVL+ Tree - an immutable self-balancing binary tree for persisting application state is used by the [Cosmos SDK](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/sdk/core/multistore.md)
|
Because Tendermint only uses a Simple Merkle Tree, application developers are expect to use their own Merkle tree in their applications. For example, the IAVL+ Tree - an immutable self-balancing binary tree for persisting application state is used by the [Cosmos SDK](https://github.com/cosmos/cosmos-sdk/blob/develop/docs/sdk/core/multistore.md)
|
||||||
@@ -301,12 +331,14 @@ type CanonicalVote struct {
|
|||||||
Type byte
|
Type byte
|
||||||
Height int64 `binary:"fixed64"`
|
Height int64 `binary:"fixed64"`
|
||||||
Round int64 `binary:"fixed64"`
|
Round int64 `binary:"fixed64"`
|
||||||
Timestamp time.Time
|
|
||||||
BlockID CanonicalBlockID
|
BlockID CanonicalBlockID
|
||||||
|
Timestamp time.Time
|
||||||
ChainID string
|
ChainID string
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The field ordering and the fixed sized encoding for the first three fields is optimized to ease parsing of SignBytes
|
The field ordering and the fixed sized encoding for the first three fields is optimized to ease parsing of SignBytes
|
||||||
in HSMs. It creates fixed offsets for relevant fields that need to be read in this context.
|
in HSMs. It creates fixed offsets for relevant fields that need to be read in this context.
|
||||||
See [#1622](https://github.com/tendermint/tendermint/issues/1622) for more details.
|
For more details, see the [signing spec](/docs/spec/consensus/signing.md).
|
||||||
|
Also, see the motivating discussion in
|
||||||
|
[#1622](https://github.com/tendermint/tendermint/issues/1622).
|
||||||
|
@@ -60,7 +60,7 @@ When hashing the Validator struct, the address is not included,
|
|||||||
because it is redundant with the pubkey.
|
because it is redundant with the pubkey.
|
||||||
|
|
||||||
The `state.Validators`, `state.LastValidators`, and `state.NextValidators`, must always by sorted by validator address,
|
The `state.Validators`, `state.LastValidators`, and `state.NextValidators`, must always by sorted by validator address,
|
||||||
so that there is a canonical order for computing the SimpleMerkleRoot.
|
so that there is a canonical order for computing the MerkleRoot.
|
||||||
|
|
||||||
We also define a `TotalVotingPower` function, to return the total voting power:
|
We also define a `TotalVotingPower` function, to return the total voting power:
|
||||||
|
|
||||||
@@ -78,6 +78,8 @@ func TotalVotingPower(vals []Validators) int64{
|
|||||||
|
|
||||||
ConsensusParams define various limits for blockchain data structures.
|
ConsensusParams define various limits for blockchain data structures.
|
||||||
Like validator sets, they are set during genesis and can be updated by the application through ABCI.
|
Like validator sets, they are set during genesis and can be updated by the application through ABCI.
|
||||||
|
When hashed, only a subset of the params are included, to allow the params to
|
||||||
|
evolve without breaking the header.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type ConsensusParams struct {
|
type ConsensusParams struct {
|
||||||
@@ -86,6 +88,18 @@ type ConsensusParams struct {
|
|||||||
Validator
|
Validator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type hashedParams struct {
|
||||||
|
BlockMaxBytes int64
|
||||||
|
BlockMaxGas int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (params ConsensusParams) Hash() []byte {
|
||||||
|
SHA256(hashedParams{
|
||||||
|
BlockMaxBytes: params.BlockSize.MaxBytes,
|
||||||
|
BlockMaxGas: params.BlockSize.MaxGas,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type BlockSize struct {
|
type BlockSize struct {
|
||||||
MaxBytes int64
|
MaxBytes int64
|
||||||
MaxGas int64
|
MaxGas int64
|
||||||
|
205
docs/spec/consensus/signing.md
Normal file
205
docs/spec/consensus/signing.md
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
# Validator Signing
|
||||||
|
|
||||||
|
Here we specify the rules for validating a proposal and vote before signing.
|
||||||
|
First we include some general notes on validating data structures common to both types.
|
||||||
|
We then provide specific validation rules for each. Finally, we include validation rules to prevent double-sigining.
|
||||||
|
|
||||||
|
## SignedMsgType
|
||||||
|
|
||||||
|
The `SignedMsgType` is a single byte that refers to the type of the message
|
||||||
|
being signed. It is defined in Go as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
// SignedMsgType is a type of signed message in the consensus.
|
||||||
|
type SignedMsgType byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Votes
|
||||||
|
PrevoteType SignedMsgType = 0x01
|
||||||
|
PrecommitType SignedMsgType = 0x02
|
||||||
|
|
||||||
|
// Proposals
|
||||||
|
ProposalType SignedMsgType = 0x20
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
All signed messages must correspond to one of these types.
|
||||||
|
|
||||||
|
## Timestamp
|
||||||
|
|
||||||
|
Timestamp validation is subtle and there are currently no bounds placed on the
|
||||||
|
timestamp included in a proposal or vote. It is expected that validators will honestly
|
||||||
|
report their local clock time. The median of all timestamps
|
||||||
|
included in a commit is used as the timestamp for the next block height.
|
||||||
|
|
||||||
|
Timestamps are expected to be strictly monotonic for a given validator, though
|
||||||
|
this is not currently enforced.
|
||||||
|
|
||||||
|
## ChainID
|
||||||
|
|
||||||
|
ChainID is an unstructured string with a max length of 50-bytes.
|
||||||
|
In the future, the ChainID may become structured, and may take on longer lengths.
|
||||||
|
For now, it is recommended that signers be configured for a particular ChainID,
|
||||||
|
and to only sign votes and proposals corresponding to that ChainID.
|
||||||
|
|
||||||
|
## BlockID
|
||||||
|
|
||||||
|
BlockID is the structure used to represent the block:
|
||||||
|
|
||||||
|
```
|
||||||
|
type BlockID struct {
|
||||||
|
Hash []byte
|
||||||
|
PartsHeader PartSetHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
type PartSetHeader struct {
|
||||||
|
Hash []byte
|
||||||
|
Total int
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To be included in a valid vote or proposal, BlockID must either represent a `nil` block, or a complete one.
|
||||||
|
We introduce two methods, `BlockID.IsZero()` and `BlockID.IsComplete()` for these cases, respectively.
|
||||||
|
|
||||||
|
`BlockID.IsZero()` returns true for BlockID `b` if each of the following
|
||||||
|
are true:
|
||||||
|
|
||||||
|
```
|
||||||
|
b.Hash == nil
|
||||||
|
b.PartsHeader.Total == 0
|
||||||
|
b.PartsHeader.Hash == nil
|
||||||
|
```
|
||||||
|
|
||||||
|
`BlockID.IsComplete()` returns true for BlockID `b` if each of the following
|
||||||
|
are true:
|
||||||
|
|
||||||
|
```
|
||||||
|
len(b.Hash) == 32
|
||||||
|
b.PartsHeader.Total > 0
|
||||||
|
len(b.PartsHeader.Hash) == 32
|
||||||
|
```
|
||||||
|
|
||||||
|
## Proposals
|
||||||
|
|
||||||
|
The structure of a proposal for signing looks like:
|
||||||
|
|
||||||
|
```
|
||||||
|
type CanonicalProposal struct {
|
||||||
|
Type SignedMsgType // type alias for byte
|
||||||
|
Height int64 `binary:"fixed64"`
|
||||||
|
Round int64 `binary:"fixed64"`
|
||||||
|
POLRound int64 `binary:"fixed64"`
|
||||||
|
BlockID BlockID
|
||||||
|
Timestamp time.Time
|
||||||
|
ChainID string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
A proposal is valid if each of the following lines evaluates to true for proposal `p`:
|
||||||
|
|
||||||
|
```
|
||||||
|
p.Type == 0x20
|
||||||
|
p.Height > 0
|
||||||
|
p.Round >= 0
|
||||||
|
p.POLRound >= -1
|
||||||
|
p.BlockID.IsComplete()
|
||||||
|
```
|
||||||
|
|
||||||
|
In other words, a proposal is valid for signing if it contains the type of a Proposal
|
||||||
|
(0x20), has a positive, non-zero height, a
|
||||||
|
non-negative round, a POLRound not less than -1, and a complete BlockID.
|
||||||
|
|
||||||
|
## Votes
|
||||||
|
|
||||||
|
The structure of a vote for signing looks like:
|
||||||
|
|
||||||
|
```
|
||||||
|
type CanonicalVote struct {
|
||||||
|
Type SignedMsgType // type alias for byte
|
||||||
|
Height int64 `binary:"fixed64"`
|
||||||
|
Round int64 `binary:"fixed64"`
|
||||||
|
BlockID BlockID
|
||||||
|
Timestamp time.Time
|
||||||
|
ChainID string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
A vote is valid if each of the following lines evaluates to true for vote `v`:
|
||||||
|
|
||||||
|
```
|
||||||
|
v.Type == 0x1 || v.Type == 0x2
|
||||||
|
v.Height > 0
|
||||||
|
v.Round >= 0
|
||||||
|
v.BlockID.IsZero() || v.BlockID.IsComplete()
|
||||||
|
```
|
||||||
|
|
||||||
|
In other words, a vote is valid for signing if it contains the type of a Prevote
|
||||||
|
or Precommit (0x1 or 0x2, respectively), has a positive, non-zero height, a
|
||||||
|
non-negative round, and an empty or valid BlockID.
|
||||||
|
|
||||||
|
## Invalid Votes and Proposals
|
||||||
|
|
||||||
|
Votes and proposals which do not satisfy the above rules are considered invalid.
|
||||||
|
Peers gossipping invalid votes and proposals may be disconnected from other peers on the network.
|
||||||
|
Note, however, that there is not currently any explicit mechanism to punish validators signing votes or proposals that fail
|
||||||
|
these basic validation rules.
|
||||||
|
|
||||||
|
## Double Signing
|
||||||
|
|
||||||
|
Signers must be careful not to sign conflicting messages, also known as "double signing" or "equivocating".
|
||||||
|
Tendermint has mechanisms to publish evidence of validators that signed conflicting votes, so they can be punished
|
||||||
|
by the application. Note Tendermint does not currently handle evidence of conflciting proposals, though it may in the future.
|
||||||
|
|
||||||
|
### State
|
||||||
|
|
||||||
|
To prevent such double signing, signers must track the height, round, and type of the last message signed.
|
||||||
|
Assume the signer keeps the following state, `s`:
|
||||||
|
|
||||||
|
```
|
||||||
|
type LastSigned struct {
|
||||||
|
Height int64
|
||||||
|
Round int64
|
||||||
|
Type SignedMsgType // byte
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
After signing a vote or proposal `m`, the signer sets:
|
||||||
|
|
||||||
|
```
|
||||||
|
s.Height = m.Height
|
||||||
|
s.Round = m.Round
|
||||||
|
s.Type = m.Type
|
||||||
|
```
|
||||||
|
|
||||||
|
### Proposals
|
||||||
|
|
||||||
|
A signer should only sign a proposal `p` if any of the following lines are true:
|
||||||
|
|
||||||
|
```
|
||||||
|
p.Height > s.Height
|
||||||
|
p.Height == s.Height && p.Round > s.Round
|
||||||
|
```
|
||||||
|
|
||||||
|
In other words, a proposal should only be signed if it's at a higher height, or a higher round for the same height.
|
||||||
|
Once a proposal or vote has been signed for a given height and round, a proposal should never be signed for the same height and round.
|
||||||
|
|
||||||
|
### Votes
|
||||||
|
|
||||||
|
A signer should only sign a vote `v` if any of the following lines are true:
|
||||||
|
|
||||||
|
```
|
||||||
|
v.Height > s.Height
|
||||||
|
v.Height == s.Height && v.Round > s.Round
|
||||||
|
v.Height == s.Height && v.Round == s.Round && v.Step == 0x1 && s.Step == 0x20
|
||||||
|
v.Height == s.Height && v.Round == s.Round && v.Step == 0x2 && s.Step != 0x2
|
||||||
|
```
|
||||||
|
|
||||||
|
In other words, a vote should only be signed if it's:
|
||||||
|
|
||||||
|
- at a higher height
|
||||||
|
- at a higher round for the same height
|
||||||
|
- a prevote for the same height and round where we haven't signed a prevote or precommit (but have signed a proposal)
|
||||||
|
- a precommit for the same height and round where we haven't signed a precommit (but have signed a proposal and/or a prevote)
|
||||||
|
|
||||||
|
This means that once a validator signs a prevote for a given height and round, the only other message it can sign for that height and round is a precommit.
|
||||||
|
And once a validator signs a precommit for a given height and round, it must not sign any other message for that same height and round.
|
@@ -36,22 +36,26 @@ db_backend = "leveldb"
|
|||||||
# Database directory
|
# Database directory
|
||||||
db_dir = "data"
|
db_dir = "data"
|
||||||
|
|
||||||
# Output level for logging
|
# Output level for logging, including package level options
|
||||||
log_level = "state:info,*:error"
|
log_level = "main:info,state:info,*:error"
|
||||||
|
|
||||||
# Output format: 'plain' (colored text) or 'json'
|
# Output format: 'plain' (colored text) or 'json'
|
||||||
log_format = "plain"
|
log_format = "plain"
|
||||||
|
|
||||||
##### additional base config options #####
|
##### additional base config options #####
|
||||||
|
|
||||||
# The ID of the chain to join (should be signed with every transaction and vote)
|
|
||||||
chain_id = ""
|
|
||||||
|
|
||||||
# Path to the JSON file containing the initial validator set and other meta data
|
# Path to the JSON file containing the initial validator set and other meta data
|
||||||
genesis_file = "genesis.json"
|
genesis_file = "config/genesis.json"
|
||||||
|
|
||||||
# Path to the JSON file containing the private key to use as a validator in the consensus protocol
|
# Path to the JSON file containing the private key to use as a validator in the consensus protocol
|
||||||
priv_validator_file = "priv_validator.json"
|
priv_validator_file = "config/priv_validator.json"
|
||||||
|
|
||||||
|
# TCP or UNIX socket address for Tendermint to listen on for
|
||||||
|
# connections from an external PrivValidator process
|
||||||
|
priv_validator_laddr = ""
|
||||||
|
|
||||||
|
# Path to the JSON file containing the private key to use for node authentication in the p2p protocol
|
||||||
|
node_key_file = "config/node_key.json"
|
||||||
|
|
||||||
# Mechanism to connect to the ABCI application: socket | grpc
|
# Mechanism to connect to the ABCI application: socket | grpc
|
||||||
abci = "socket"
|
abci = "socket"
|
||||||
@@ -74,13 +78,13 @@ laddr = "tcp://0.0.0.0:26657"
|
|||||||
# A list of origins a cross-domain request can be executed from
|
# A list of origins a cross-domain request can be executed from
|
||||||
# Default value '[]' disables cors support
|
# Default value '[]' disables cors support
|
||||||
# Use '["*"]' to allow any origin
|
# Use '["*"]' to allow any origin
|
||||||
cors_allowed_origins = "[]"
|
cors_allowed_origins = []
|
||||||
|
|
||||||
# A list of methods the client is allowed to use with cross-domain requests
|
# A list of methods the client is allowed to use with cross-domain requests
|
||||||
cors_allowed_methods = "[HEAD GET POST]"
|
cors_allowed_methods = ["HEAD", "GET", "POST"]
|
||||||
|
|
||||||
# A list of non simple headers the client is allowed to use with cross-domain requests
|
# A list of non simple headers the client is allowed to use with cross-domain requests
|
||||||
cors_allowed_headers = "[Origin Accept Content-Type X-Requested-With X-Server-Time]"
|
cors_allowed_headers = ["Origin", "Accept", "Content-Type", "X-Requested-With", "X-Server-Time"]
|
||||||
|
|
||||||
# TCP or UNIX socket address for the gRPC server to listen on
|
# TCP or UNIX socket address for the gRPC server to listen on
|
||||||
# NOTE: This server only supports /broadcast_tx_commit
|
# NOTE: This server only supports /broadcast_tx_commit
|
||||||
@@ -88,7 +92,7 @@ grpc_laddr = ""
|
|||||||
|
|
||||||
# Maximum number of simultaneous connections.
|
# Maximum number of simultaneous connections.
|
||||||
# Does not include RPC (HTTP&WebSocket) connections. See max_open_connections
|
# Does not include RPC (HTTP&WebSocket) connections. See max_open_connections
|
||||||
# If you want to accept more significant number than the default, make sure
|
# If you want to accept a larger number than the default, make sure
|
||||||
# you increase your OS limits.
|
# you increase your OS limits.
|
||||||
# 0 - unlimited.
|
# 0 - unlimited.
|
||||||
# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}
|
# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}
|
||||||
@@ -100,7 +104,7 @@ unsafe = false
|
|||||||
|
|
||||||
# Maximum number of simultaneous connections (including WebSocket).
|
# Maximum number of simultaneous connections (including WebSocket).
|
||||||
# Does not include gRPC connections. See grpc_max_open_connections
|
# Does not include gRPC connections. See grpc_max_open_connections
|
||||||
# If you want to accept more significant number than the default, make sure
|
# If you want to accept a larger number than the default, make sure
|
||||||
# you increase your OS limits.
|
# you increase your OS limits.
|
||||||
# 0 - unlimited.
|
# 0 - unlimited.
|
||||||
# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}
|
# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}
|
||||||
@@ -113,6 +117,12 @@ max_open_connections = 900
|
|||||||
# Address to listen for incoming connections
|
# Address to listen for incoming connections
|
||||||
laddr = "tcp://0.0.0.0:26656"
|
laddr = "tcp://0.0.0.0:26656"
|
||||||
|
|
||||||
|
# Address to advertise to peers for them to dial
|
||||||
|
# If empty, will use the same port as the laddr,
|
||||||
|
# and will introspect on the listener or use UPnP
|
||||||
|
# to figure out the address.
|
||||||
|
external_address = ""
|
||||||
|
|
||||||
# Comma separated list of seed nodes to connect to
|
# Comma separated list of seed nodes to connect to
|
||||||
seeds = ""
|
seeds = ""
|
||||||
|
|
||||||
@@ -123,7 +133,7 @@ persistent_peers = ""
|
|||||||
upnp = false
|
upnp = false
|
||||||
|
|
||||||
# Path to address book
|
# Path to address book
|
||||||
addr_book_file = "addrbook.json"
|
addr_book_file = "config/addrbook.json"
|
||||||
|
|
||||||
# Set true for strict address routability rules
|
# Set true for strict address routability rules
|
||||||
# Set false for private or local networks
|
# Set false for private or local networks
|
||||||
@@ -160,7 +170,7 @@ seed_mode = false
|
|||||||
private_peer_ids = ""
|
private_peer_ids = ""
|
||||||
|
|
||||||
# Toggle to disable guard against peers connecting from the same ip.
|
# Toggle to disable guard against peers connecting from the same ip.
|
||||||
allow_duplicate_ip = true
|
allow_duplicate_ip = false
|
||||||
|
|
||||||
# Peer connection configuration.
|
# Peer connection configuration.
|
||||||
handshake_timeout = "20s"
|
handshake_timeout = "20s"
|
||||||
@@ -171,26 +181,26 @@ dial_timeout = "3s"
|
|||||||
|
|
||||||
recheck = true
|
recheck = true
|
||||||
broadcast = true
|
broadcast = true
|
||||||
wal_dir = "data/mempool.wal"
|
wal_dir = ""
|
||||||
|
|
||||||
# size of the mempool
|
# size of the mempool
|
||||||
size = 100000
|
size = 5000
|
||||||
|
|
||||||
# size of the cache (used to filter transactions we saw earlier)
|
# size of the cache (used to filter transactions we saw earlier)
|
||||||
cache_size = 100000
|
cache_size = 10000
|
||||||
|
|
||||||
##### consensus configuration options #####
|
##### consensus configuration options #####
|
||||||
[consensus]
|
[consensus]
|
||||||
|
|
||||||
wal_file = "data/cs.wal/wal"
|
wal_file = "data/cs.wal/wal"
|
||||||
|
|
||||||
timeout_propose = "3000ms"
|
timeout_propose = "3s"
|
||||||
timeout_propose_delta = "500ms"
|
timeout_propose_delta = "500ms"
|
||||||
timeout_prevote = "1000ms"
|
timeout_prevote = "1s"
|
||||||
timeout_prevote_delta = "500ms"
|
timeout_prevote_delta = "500ms"
|
||||||
timeout_precommit = "1000ms"
|
timeout_precommit = "1s"
|
||||||
timeout_precommit_delta = "500ms"
|
timeout_precommit_delta = "500ms"
|
||||||
timeout_commit = "1000ms"
|
timeout_commit = "1s"
|
||||||
|
|
||||||
# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0)
|
# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0)
|
||||||
skip_timeout_commit = false
|
skip_timeout_commit = false
|
||||||
@@ -201,10 +211,10 @@ create_empty_blocks_interval = "0s"
|
|||||||
|
|
||||||
# Reactor sleep duration parameters
|
# Reactor sleep duration parameters
|
||||||
peer_gossip_sleep_duration = "100ms"
|
peer_gossip_sleep_duration = "100ms"
|
||||||
peer_query_maj23_sleep_duration = "2000ms"
|
peer_query_maj23_sleep_duration = "2s"
|
||||||
|
|
||||||
# Block time parameters. Corresponds to the minimum time increment between consecutive blocks.
|
# Block time parameters. Corresponds to the minimum time increment between consecutive blocks.
|
||||||
blocktime_iota = "1000ms"
|
blocktime_iota = "1s"
|
||||||
|
|
||||||
##### transactions indexer configuration options #####
|
##### transactions indexer configuration options #####
|
||||||
[tx_index]
|
[tx_index]
|
||||||
@@ -245,7 +255,7 @@ prometheus = false
|
|||||||
prometheus_listen_addr = ":26660"
|
prometheus_listen_addr = ":26660"
|
||||||
|
|
||||||
# Maximum number of simultaneous connections.
|
# Maximum number of simultaneous connections.
|
||||||
# If you want to accept a more significant number than the default, make sure
|
# If you want to accept a larger number than the default, make sure
|
||||||
# you increase your OS limits.
|
# you increase your OS limits.
|
||||||
# 0 - unlimited.
|
# 0 - unlimited.
|
||||||
max_open_connections = 3
|
max_open_connections = 3
|
||||||
|
@@ -113,7 +113,7 @@ blocks are produced regularly, even if there are no transactions. See
|
|||||||
_No Empty Blocks_, below, to modify this setting.
|
_No Empty Blocks_, below, to modify this setting.
|
||||||
|
|
||||||
Tendermint supports in-process versions of the `counter`, `kvstore` and
|
Tendermint supports in-process versions of the `counter`, `kvstore` and
|
||||||
`nil` apps that ship as examples with `abci-cli`. It's easy to compile
|
`noop` apps that ship as examples with `abci-cli`. It's easy to compile
|
||||||
your own app in-process with Tendermint if it's written in Go. If your
|
your own app in-process with Tendermint if it's written in Go. If your
|
||||||
app is not written in Go, simply run it in another process, and use the
|
app is not written in Go, simply run it in another process, and use the
|
||||||
`--proxy_app` flag to specify the address of the socket it is listening
|
`--proxy_app` flag to specify the address of the socket it is listening
|
||||||
|
2611
docs/yarn.lock
2611
docs/yarn.lock
File diff suppressed because it is too large
Load Diff
@@ -57,10 +57,10 @@ func (evpool *EvidencePool) PriorityEvidence() []types.Evidence {
|
|||||||
return evpool.evidenceStore.PriorityEvidence()
|
return evpool.evidenceStore.PriorityEvidence()
|
||||||
}
|
}
|
||||||
|
|
||||||
// PendingEvidence returns uncommitted evidence up to maxBytes.
|
// PendingEvidence returns up to maxNum uncommitted evidence.
|
||||||
// If maxBytes is -1, all evidence is returned.
|
// If maxNum is -1, all evidence is returned.
|
||||||
func (evpool *EvidencePool) PendingEvidence(maxBytes int64) []types.Evidence {
|
func (evpool *EvidencePool) PendingEvidence(maxNum int64) []types.Evidence {
|
||||||
return evpool.evidenceStore.PendingEvidence(maxBytes)
|
return evpool.evidenceStore.PendingEvidence(maxNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
// State returns the current state of the evpool.
|
// State returns the current state of the evpool.
|
||||||
|
@@ -86,26 +86,26 @@ func (store *EvidenceStore) PriorityEvidence() (evidence []types.Evidence) {
|
|||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
// PendingEvidence returns known uncommitted evidence up to maxBytes.
|
// PendingEvidence returns up to maxNum known, uncommitted evidence.
|
||||||
// If maxBytes is -1, all evidence is returned.
|
// If maxNum is -1, all evidence is returned.
|
||||||
func (store *EvidenceStore) PendingEvidence(maxBytes int64) (evidence []types.Evidence) {
|
func (store *EvidenceStore) PendingEvidence(maxNum int64) (evidence []types.Evidence) {
|
||||||
return store.listEvidence(baseKeyPending, maxBytes)
|
return store.listEvidence(baseKeyPending, maxNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
// listEvidence lists the evidence for the given prefix key up to maxBytes.
|
// listEvidence lists up to maxNum pieces of evidence for the given prefix key.
|
||||||
// It is wrapped by PriorityEvidence and PendingEvidence for convenience.
|
// It is wrapped by PriorityEvidence and PendingEvidence for convenience.
|
||||||
// If maxBytes is -1, there's no cap on the size of returned evidence.
|
// If maxNum is -1, there's no cap on the size of returned evidence.
|
||||||
func (store *EvidenceStore) listEvidence(prefixKey string, maxBytes int64) (evidence []types.Evidence) {
|
func (store *EvidenceStore) listEvidence(prefixKey string, maxNum int64) (evidence []types.Evidence) {
|
||||||
var bytes int64
|
var count int64
|
||||||
iter := dbm.IteratePrefix(store.db, []byte(prefixKey))
|
iter := dbm.IteratePrefix(store.db, []byte(prefixKey))
|
||||||
defer iter.Close()
|
defer iter.Close()
|
||||||
for ; iter.Valid(); iter.Next() {
|
for ; iter.Valid(); iter.Next() {
|
||||||
val := iter.Value()
|
val := iter.Value()
|
||||||
|
|
||||||
if maxBytes > 0 && bytes+int64(len(val)) > maxBytes {
|
if count == maxNum {
|
||||||
return evidence
|
return evidence
|
||||||
}
|
}
|
||||||
bytes += int64(len(val))
|
count++
|
||||||
|
|
||||||
var ei EvidenceInfo
|
var ei EvidenceInfo
|
||||||
err := cdc.UnmarshalBinaryBare(val, &ei)
|
err := cdc.UnmarshalBinaryBare(val, &ei)
|
||||||
|
@@ -13,3 +13,8 @@ func init() {
|
|||||||
cryptoAmino.RegisterAmino(cdc)
|
cryptoAmino.RegisterAmino(cdc)
|
||||||
types.RegisterEvidences(cdc)
|
types.RegisterEvidences(cdc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For testing purposes only
|
||||||
|
func RegisterMockEvidences() {
|
||||||
|
types.RegisterMockEvidences(cdc)
|
||||||
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package log
|
package log
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -19,12 +20,22 @@ var (
|
|||||||
// inside a test (not in the init func) because
|
// inside a test (not in the init func) because
|
||||||
// verbose flag only set at the time of testing.
|
// verbose flag only set at the time of testing.
|
||||||
func TestingLogger() Logger {
|
func TestingLogger() Logger {
|
||||||
|
return TestingLoggerWithOutput(os.Stdout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestingLoggerWOutput returns a TMLogger which writes to (w io.Writer) if testing being run
|
||||||
|
// with the verbose (-v) flag, NopLogger otherwise.
|
||||||
|
//
|
||||||
|
// Note that the call to TestingLoggerWithOutput(w io.Writer) must be made
|
||||||
|
// inside a test (not in the init func) because
|
||||||
|
// verbose flag only set at the time of testing.
|
||||||
|
func TestingLoggerWithOutput(w io.Writer) Logger {
|
||||||
if _testingLogger != nil {
|
if _testingLogger != nil {
|
||||||
return _testingLogger
|
return _testingLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
if testing.Verbose() {
|
if testing.Verbose() {
|
||||||
_testingLogger = NewTMLogger(NewSyncWriter(os.Stdout))
|
_testingLogger = NewTMLogger(NewSyncWriter(w))
|
||||||
} else {
|
} else {
|
||||||
_testingLogger = NewNopLogger()
|
_testingLogger = NewNopLogger()
|
||||||
}
|
}
|
||||||
|
@@ -90,7 +90,7 @@ func (l tmfmtLogger) Log(keyvals ...interface{}) error {
|
|||||||
// D - first character of the level, uppercase (ASCII only)
|
// D - first character of the level, uppercase (ASCII only)
|
||||||
// [2016-05-02|11:06:44.322] - our time format (see https://golang.org/src/time/format.go)
|
// [2016-05-02|11:06:44.322] - our time format (see https://golang.org/src/time/format.go)
|
||||||
// Stopping ... - message
|
// Stopping ... - message
|
||||||
enc.buf.WriteString(fmt.Sprintf("%c[%s] %-44s ", lvl[0]-32, time.Now().Format("2016-01-02|15:04:05.000"), msg))
|
enc.buf.WriteString(fmt.Sprintf("%c[%s] %-44s ", lvl[0]-32, time.Now().Format("2006-01-02|15:04:05.000"), msg))
|
||||||
|
|
||||||
if module != unknown {
|
if module != unknown {
|
||||||
enc.buf.WriteString("module=" + module + " ")
|
enc.buf.WriteString("module=" + module + " ")
|
||||||
|
@@ -35,34 +35,40 @@ func NewBaseVerifier(chainID string, height int64, valset *types.ValidatorSet) *
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Implements Verifier.
|
// Implements Verifier.
|
||||||
func (bc *BaseVerifier) ChainID() string {
|
func (bv *BaseVerifier) ChainID() string {
|
||||||
return bc.chainID
|
return bv.chainID
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements Verifier.
|
// Implements Verifier.
|
||||||
func (bc *BaseVerifier) Verify(signedHeader types.SignedHeader) error {
|
func (bv *BaseVerifier) Verify(signedHeader types.SignedHeader) error {
|
||||||
|
|
||||||
// We can't verify commits older than bc.height.
|
// We can't verify commits for a different chain.
|
||||||
if signedHeader.Height < bc.height {
|
if signedHeader.ChainID != bv.chainID {
|
||||||
|
return cmn.NewError("BaseVerifier chainID is %v, cannot verify chainID %v",
|
||||||
|
bv.chainID, signedHeader.ChainID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can't verify commits older than bv.height.
|
||||||
|
if signedHeader.Height < bv.height {
|
||||||
return cmn.NewError("BaseVerifier height is %v, cannot verify height %v",
|
return cmn.NewError("BaseVerifier height is %v, cannot verify height %v",
|
||||||
bc.height, signedHeader.Height)
|
bv.height, signedHeader.Height)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can't verify with the wrong validator set.
|
// We can't verify with the wrong validator set.
|
||||||
if !bytes.Equal(signedHeader.ValidatorsHash,
|
if !bytes.Equal(signedHeader.ValidatorsHash,
|
||||||
bc.valset.Hash()) {
|
bv.valset.Hash()) {
|
||||||
return lerr.ErrUnexpectedValidators(signedHeader.ValidatorsHash, bc.valset.Hash())
|
return lerr.ErrUnexpectedValidators(signedHeader.ValidatorsHash, bv.valset.Hash())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do basic sanity checks.
|
// Do basic sanity checks.
|
||||||
err := signedHeader.ValidateBasic(bc.chainID)
|
err := signedHeader.ValidateBasic(bv.chainID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cmn.ErrorWrap(err, "in verify")
|
return cmn.ErrorWrap(err, "in verify")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check commit signatures.
|
// Check commit signatures.
|
||||||
err = bc.valset.VerifyCommit(
|
err = bv.valset.VerifyCommit(
|
||||||
bc.chainID, signedHeader.Commit.BlockID,
|
bv.chainID, signedHeader.Commit.BlockID,
|
||||||
signedHeader.Height, signedHeader.Commit)
|
signedHeader.Height, signedHeader.Commit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cmn.ErrorWrap(err, "in verify")
|
return cmn.ErrorWrap(err, "in verify")
|
||||||
|
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FullCommit is a signed header (the block header and a commit that signs it),
|
// FullCommit contains a SignedHeader (the block header and a commit that signs it),
|
||||||
// the validator set which signed the commit, and the next validator set. The
|
// the validator set which signed the commit, and the next validator set. The
|
||||||
// next validator set (which is proven from the block header) allows us to
|
// next validator set (which is proven from the block header) allows us to
|
||||||
// revert to block-by-block updating of lite Verifier's latest validator set,
|
// revert to block-by-block updating of lite Verifier's latest validator set,
|
||||||
|
@@ -13,6 +13,9 @@ import (
|
|||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ PersistentProvider = (*DBProvider)(nil)
|
||||||
|
|
||||||
|
// DBProvider stores commits and validator sets in a DB.
|
||||||
type DBProvider struct {
|
type DBProvider struct {
|
||||||
logger log.Logger
|
logger log.Logger
|
||||||
label string
|
label string
|
||||||
|
@@ -53,10 +53,6 @@ SignedHeader, and that the SignedHeader was to be signed by the exact given
|
|||||||
validator set, and that the height of the commit is at least height (or
|
validator set, and that the height of the commit is at least height (or
|
||||||
greater).
|
greater).
|
||||||
|
|
||||||
SignedHeader.Commit may be signed by a different validator set, it can get
|
|
||||||
verified with a BaseVerifier as long as sufficient signatures from the
|
|
||||||
previous validator set are present in the commit.
|
|
||||||
|
|
||||||
DynamicVerifier - this Verifier implements an auto-update and persistence
|
DynamicVerifier - this Verifier implements an auto-update and persistence
|
||||||
strategy to verify any SignedHeader of the blockchain.
|
strategy to verify any SignedHeader of the blockchain.
|
||||||
|
|
||||||
|
@@ -18,12 +18,17 @@ var _ Verifier = (*DynamicVerifier)(nil)
|
|||||||
// "source" provider to obtain the needed FullCommits to securely sync with
|
// "source" provider to obtain the needed FullCommits to securely sync with
|
||||||
// validator set changes. It stores properly validated data on the
|
// validator set changes. It stores properly validated data on the
|
||||||
// "trusted" local system.
|
// "trusted" local system.
|
||||||
|
// TODO: make this single threaded and create a new
|
||||||
|
// ConcurrentDynamicVerifier that wraps it with concurrency.
|
||||||
|
// see https://github.com/tendermint/tendermint/issues/3170
|
||||||
type DynamicVerifier struct {
|
type DynamicVerifier struct {
|
||||||
logger log.Logger
|
|
||||||
chainID string
|
chainID string
|
||||||
// These are only properly validated data, from local system.
|
logger log.Logger
|
||||||
|
|
||||||
|
// Already validated, stored locally
|
||||||
trusted PersistentProvider
|
trusted PersistentProvider
|
||||||
// This is a source of new info, like a node rpc, or other import method.
|
|
||||||
|
// New info, like a node rpc, or other import method.
|
||||||
source Provider
|
source Provider
|
||||||
|
|
||||||
// pending map to synchronize concurrent verification requests
|
// pending map to synchronize concurrent verification requests
|
||||||
@@ -35,8 +40,8 @@ type DynamicVerifier struct {
|
|||||||
// trusted provider to store validated data and the source provider to
|
// trusted provider to store validated data and the source provider to
|
||||||
// obtain missing data (e.g. FullCommits).
|
// obtain missing data (e.g. FullCommits).
|
||||||
//
|
//
|
||||||
// The trusted provider should a CacheProvider, MemProvider or
|
// The trusted provider should be a DBProvider.
|
||||||
// files.Provider. The source provider should be a client.HTTPProvider.
|
// The source provider should be a client.HTTPProvider.
|
||||||
func NewDynamicVerifier(chainID string, trusted PersistentProvider, source Provider) *DynamicVerifier {
|
func NewDynamicVerifier(chainID string, trusted PersistentProvider, source Provider) *DynamicVerifier {
|
||||||
return &DynamicVerifier{
|
return &DynamicVerifier{
|
||||||
logger: log.NewNopLogger(),
|
logger: log.NewNopLogger(),
|
||||||
@@ -47,68 +52,71 @@ func NewDynamicVerifier(chainID string, trusted PersistentProvider, source Provi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *DynamicVerifier) SetLogger(logger log.Logger) {
|
func (dv *DynamicVerifier) SetLogger(logger log.Logger) {
|
||||||
logger = logger.With("module", "lite")
|
logger = logger.With("module", "lite")
|
||||||
ic.logger = logger
|
dv.logger = logger
|
||||||
ic.trusted.SetLogger(logger)
|
dv.trusted.SetLogger(logger)
|
||||||
ic.source.SetLogger(logger)
|
dv.source.SetLogger(logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements Verifier.
|
// Implements Verifier.
|
||||||
func (ic *DynamicVerifier) ChainID() string {
|
func (dv *DynamicVerifier) ChainID() string {
|
||||||
return ic.chainID
|
return dv.chainID
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements Verifier.
|
// Implements Verifier.
|
||||||
//
|
//
|
||||||
// If the validators have changed since the last known time, it looks to
|
// If the validators have changed since the last known time, it looks to
|
||||||
// ic.trusted and ic.source to prove the new validators. On success, it will
|
// dv.trusted and dv.source to prove the new validators. On success, it will
|
||||||
// try to store the SignedHeader in ic.trusted if the next
|
// try to store the SignedHeader in dv.trusted if the next
|
||||||
// validator can be sourced.
|
// validator can be sourced.
|
||||||
func (ic *DynamicVerifier) Verify(shdr types.SignedHeader) error {
|
func (dv *DynamicVerifier) Verify(shdr types.SignedHeader) error {
|
||||||
|
|
||||||
// Performs synchronization for multi-threads verification at the same height.
|
// Performs synchronization for multi-threads verification at the same height.
|
||||||
ic.mtx.Lock()
|
dv.mtx.Lock()
|
||||||
if pending := ic.pendingVerifications[shdr.Height]; pending != nil {
|
if pending := dv.pendingVerifications[shdr.Height]; pending != nil {
|
||||||
ic.mtx.Unlock()
|
dv.mtx.Unlock()
|
||||||
<-pending // pending is chan struct{}
|
<-pending // pending is chan struct{}
|
||||||
} else {
|
} else {
|
||||||
pending := make(chan struct{})
|
pending := make(chan struct{})
|
||||||
ic.pendingVerifications[shdr.Height] = pending
|
dv.pendingVerifications[shdr.Height] = pending
|
||||||
defer func() {
|
defer func() {
|
||||||
close(pending)
|
close(pending)
|
||||||
ic.mtx.Lock()
|
dv.mtx.Lock()
|
||||||
delete(ic.pendingVerifications, shdr.Height)
|
delete(dv.pendingVerifications, shdr.Height)
|
||||||
ic.mtx.Unlock()
|
dv.mtx.Unlock()
|
||||||
}()
|
}()
|
||||||
ic.mtx.Unlock()
|
dv.mtx.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
//Get the exact trusted commit for h, and if it is
|
//Get the exact trusted commit for h, and if it is
|
||||||
// equal to shdr, then don't even verify it,
|
// equal to shdr, then it's already trusted, so
|
||||||
// and just return nil.
|
// just return nil.
|
||||||
trustedFCSameHeight, err := ic.trusted.LatestFullCommit(ic.chainID, shdr.Height, shdr.Height)
|
trustedFCSameHeight, err := dv.trusted.LatestFullCommit(dv.chainID, shdr.Height, shdr.Height)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// If loading trust commit successfully, and trust commit equal to shdr, then don't verify it,
|
// If loading trust commit successfully, and trust commit equal to shdr, then don't verify it,
|
||||||
// just return nil.
|
// just return nil.
|
||||||
if bytes.Equal(trustedFCSameHeight.SignedHeader.Hash(), shdr.Hash()) {
|
if bytes.Equal(trustedFCSameHeight.SignedHeader.Hash(), shdr.Hash()) {
|
||||||
ic.logger.Info(fmt.Sprintf("Load full commit at height %d from cache, there is not need to verify.", shdr.Height))
|
dv.logger.Info(fmt.Sprintf("Load full commit at height %d from cache, there is not need to verify.", shdr.Height))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else if !lerr.IsErrCommitNotFound(err) {
|
} else if !lerr.IsErrCommitNotFound(err) {
|
||||||
// Return error if it is not CommitNotFound error
|
// Return error if it is not CommitNotFound error
|
||||||
ic.logger.Info(fmt.Sprintf("Encountered unknown error in loading full commit at height %d.", shdr.Height))
|
dv.logger.Info(fmt.Sprintf("Encountered unknown error in loading full commit at height %d.", shdr.Height))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the latest known full commit <= h-1 from our trusted providers.
|
// Get the latest known full commit <= h-1 from our trusted providers.
|
||||||
// The full commit at h-1 contains the valset to sign for h.
|
// The full commit at h-1 contains the valset to sign for h.
|
||||||
h := shdr.Height - 1
|
prevHeight := shdr.Height - 1
|
||||||
trustedFC, err := ic.trusted.LatestFullCommit(ic.chainID, 1, h)
|
trustedFC, err := dv.trusted.LatestFullCommit(dv.chainID, 1, prevHeight)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if trustedFC.Height() == h {
|
// sync up to the prevHeight and assert our latest NextValidatorSet
|
||||||
|
// is the ValidatorSet for the SignedHeader
|
||||||
|
if trustedFC.Height() == prevHeight {
|
||||||
// Return error if valset doesn't match.
|
// Return error if valset doesn't match.
|
||||||
if !bytes.Equal(
|
if !bytes.Equal(
|
||||||
trustedFC.NextValidators.Hash(),
|
trustedFC.NextValidators.Hash(),
|
||||||
@@ -118,11 +126,12 @@ func (ic *DynamicVerifier) Verify(shdr types.SignedHeader) error {
|
|||||||
shdr.Header.ValidatorsHash)
|
shdr.Header.ValidatorsHash)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If valset doesn't match...
|
// If valset doesn't match, try to update
|
||||||
if !bytes.Equal(trustedFC.NextValidators.Hash(),
|
if !bytes.Equal(
|
||||||
|
trustedFC.NextValidators.Hash(),
|
||||||
shdr.Header.ValidatorsHash) {
|
shdr.Header.ValidatorsHash) {
|
||||||
// ... update.
|
// ... update.
|
||||||
trustedFC, err = ic.updateToHeight(h)
|
trustedFC, err = dv.updateToHeight(prevHeight)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -137,14 +146,21 @@ func (ic *DynamicVerifier) Verify(shdr types.SignedHeader) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify the signed header using the matching valset.
|
// Verify the signed header using the matching valset.
|
||||||
cert := NewBaseVerifier(ic.chainID, trustedFC.Height()+1, trustedFC.NextValidators)
|
cert := NewBaseVerifier(dv.chainID, trustedFC.Height()+1, trustedFC.NextValidators)
|
||||||
err = cert.Verify(shdr)
|
err = cert.Verify(shdr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// By now, the SignedHeader is fully validated and we're synced up to
|
||||||
|
// SignedHeader.Height - 1. To sync to SignedHeader.Height, we need
|
||||||
|
// the validator set at SignedHeader.Height + 1 so we can verify the
|
||||||
|
// SignedHeader.NextValidatorSet.
|
||||||
|
// TODO: is the ValidateFull below mostly redundant with the BaseVerifier.Verify above?
|
||||||
|
// See https://github.com/tendermint/tendermint/issues/3174.
|
||||||
|
|
||||||
// Get the next validator set.
|
// Get the next validator set.
|
||||||
nextValset, err := ic.source.ValidatorSet(ic.chainID, shdr.Height+1)
|
nextValset, err := dv.source.ValidatorSet(dv.chainID, shdr.Height+1)
|
||||||
if lerr.IsErrUnknownValidators(err) {
|
if lerr.IsErrUnknownValidators(err) {
|
||||||
// Ignore this error.
|
// Ignore this error.
|
||||||
return nil
|
return nil
|
||||||
@@ -160,31 +176,31 @@ func (ic *DynamicVerifier) Verify(shdr types.SignedHeader) error {
|
|||||||
}
|
}
|
||||||
// Validate the full commit. This checks the cryptographic
|
// Validate the full commit. This checks the cryptographic
|
||||||
// signatures of Commit against Validators.
|
// signatures of Commit against Validators.
|
||||||
if err := nfc.ValidateFull(ic.chainID); err != nil {
|
if err := nfc.ValidateFull(dv.chainID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Trust it.
|
// Trust it.
|
||||||
return ic.trusted.SaveFullCommit(nfc)
|
return dv.trusted.SaveFullCommit(nfc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// verifyAndSave will verify if this is a valid source full commit given the
|
// verifyAndSave will verify if this is a valid source full commit given the
|
||||||
// best match trusted full commit, and if good, persist to ic.trusted.
|
// best match trusted full commit, and if good, persist to dv.trusted.
|
||||||
// Returns ErrTooMuchChange when >2/3 of trustedFC did not sign sourceFC.
|
// Returns ErrTooMuchChange when >2/3 of trustedFC did not sign sourceFC.
|
||||||
// Panics if trustedFC.Height() >= sourceFC.Height().
|
// Panics if trustedFC.Height() >= sourceFC.Height().
|
||||||
func (ic *DynamicVerifier) verifyAndSave(trustedFC, sourceFC FullCommit) error {
|
func (dv *DynamicVerifier) verifyAndSave(trustedFC, sourceFC FullCommit) error {
|
||||||
if trustedFC.Height() >= sourceFC.Height() {
|
if trustedFC.Height() >= sourceFC.Height() {
|
||||||
panic("should not happen")
|
panic("should not happen")
|
||||||
}
|
}
|
||||||
err := trustedFC.NextValidators.VerifyFutureCommit(
|
err := trustedFC.NextValidators.VerifyFutureCommit(
|
||||||
sourceFC.Validators,
|
sourceFC.Validators,
|
||||||
ic.chainID, sourceFC.SignedHeader.Commit.BlockID,
|
dv.chainID, sourceFC.SignedHeader.Commit.BlockID,
|
||||||
sourceFC.SignedHeader.Height, sourceFC.SignedHeader.Commit,
|
sourceFC.SignedHeader.Height, sourceFC.SignedHeader.Commit,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ic.trusted.SaveFullCommit(sourceFC)
|
return dv.trusted.SaveFullCommit(sourceFC)
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateToHeight will use divide-and-conquer to find a path to h.
|
// updateToHeight will use divide-and-conquer to find a path to h.
|
||||||
@@ -192,29 +208,30 @@ func (ic *DynamicVerifier) verifyAndSave(trustedFC, sourceFC FullCommit) error {
|
|||||||
// for height h, using repeated applications of bisection if necessary.
|
// for height h, using repeated applications of bisection if necessary.
|
||||||
//
|
//
|
||||||
// Returns ErrCommitNotFound if source provider doesn't have the commit for h.
|
// Returns ErrCommitNotFound if source provider doesn't have the commit for h.
|
||||||
func (ic *DynamicVerifier) updateToHeight(h int64) (FullCommit, error) {
|
func (dv *DynamicVerifier) updateToHeight(h int64) (FullCommit, error) {
|
||||||
|
|
||||||
// Fetch latest full commit from source.
|
// Fetch latest full commit from source.
|
||||||
sourceFC, err := ic.source.LatestFullCommit(ic.chainID, h, h)
|
sourceFC, err := dv.source.LatestFullCommit(dv.chainID, h, h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return FullCommit{}, err
|
return FullCommit{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate the full commit. This checks the cryptographic
|
|
||||||
// signatures of Commit against Validators.
|
|
||||||
if err := sourceFC.ValidateFull(ic.chainID); err != nil {
|
|
||||||
return FullCommit{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If sourceFC.Height() != h, we can't do it.
|
// If sourceFC.Height() != h, we can't do it.
|
||||||
if sourceFC.Height() != h {
|
if sourceFC.Height() != h {
|
||||||
return FullCommit{}, lerr.ErrCommitNotFound()
|
return FullCommit{}, lerr.ErrCommitNotFound()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate the full commit. This checks the cryptographic
|
||||||
|
// signatures of Commit against Validators.
|
||||||
|
if err := sourceFC.ValidateFull(dv.chainID); err != nil {
|
||||||
|
return FullCommit{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify latest FullCommit against trusted FullCommits
|
||||||
FOR_LOOP:
|
FOR_LOOP:
|
||||||
for {
|
for {
|
||||||
// Fetch latest full commit from trusted.
|
// Fetch latest full commit from trusted.
|
||||||
trustedFC, err := ic.trusted.LatestFullCommit(ic.chainID, 1, h)
|
trustedFC, err := dv.trusted.LatestFullCommit(dv.chainID, 1, h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return FullCommit{}, err
|
return FullCommit{}, err
|
||||||
}
|
}
|
||||||
@@ -224,21 +241,21 @@ FOR_LOOP:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to update to full commit with checks.
|
// Try to update to full commit with checks.
|
||||||
err = ic.verifyAndSave(trustedFC, sourceFC)
|
err = dv.verifyAndSave(trustedFC, sourceFC)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// All good!
|
// All good!
|
||||||
return sourceFC, nil
|
return sourceFC, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle special case when err is ErrTooMuchChange.
|
// Handle special case when err is ErrTooMuchChange.
|
||||||
if lerr.IsErrTooMuchChange(err) {
|
if types.IsErrTooMuchChange(err) {
|
||||||
// Divide and conquer.
|
// Divide and conquer.
|
||||||
start, end := trustedFC.Height(), sourceFC.Height()
|
start, end := trustedFC.Height(), sourceFC.Height()
|
||||||
if !(start < end) {
|
if !(start < end) {
|
||||||
panic("should not happen")
|
panic("should not happen")
|
||||||
}
|
}
|
||||||
mid := (start + end) / 2
|
mid := (start + end) / 2
|
||||||
_, err = ic.updateToHeight(mid)
|
_, err = dv.updateToHeight(mid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return FullCommit{}, err
|
return FullCommit{}, err
|
||||||
}
|
}
|
||||||
@@ -249,8 +266,8 @@ FOR_LOOP:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *DynamicVerifier) LastTrustedHeight() int64 {
|
func (dv *DynamicVerifier) LastTrustedHeight() int64 {
|
||||||
fc, err := ic.trusted.LatestFullCommit(ic.chainID, 1, 1<<63-1)
|
fc, err := dv.trusted.LatestFullCommit(dv.chainID, 1, 1<<63-1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("should not happen")
|
panic("should not happen")
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
dbm "github.com/tendermint/tendermint/libs/db"
|
dbm "github.com/tendermint/tendermint/libs/db"
|
||||||
log "github.com/tendermint/tendermint/libs/log"
|
log "github.com/tendermint/tendermint/libs/log"
|
||||||
|
"github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInquirerValidPath(t *testing.T) {
|
func TestInquirerValidPath(t *testing.T) {
|
||||||
@@ -70,6 +71,70 @@ func TestInquirerValidPath(t *testing.T) {
|
|||||||
assert.Nil(err, "%+v", err)
|
assert.Nil(err, "%+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDynamicVerify(t *testing.T) {
|
||||||
|
trust := NewDBProvider("trust", dbm.NewMemDB())
|
||||||
|
source := NewDBProvider("source", dbm.NewMemDB())
|
||||||
|
|
||||||
|
// 10 commits with one valset, 1 to change,
|
||||||
|
// 10 commits with the next one
|
||||||
|
n1, n2 := 10, 10
|
||||||
|
nCommits := n1 + n2 + 1
|
||||||
|
maxHeight := int64(nCommits)
|
||||||
|
fcz := make([]FullCommit, nCommits)
|
||||||
|
|
||||||
|
// gen the 2 val sets
|
||||||
|
chainID := "dynamic-verifier"
|
||||||
|
power := int64(10)
|
||||||
|
keys1 := genPrivKeys(5)
|
||||||
|
vals1 := keys1.ToValidators(power, 0)
|
||||||
|
keys2 := genPrivKeys(5)
|
||||||
|
vals2 := keys2.ToValidators(power, 0)
|
||||||
|
|
||||||
|
// make some commits with the first
|
||||||
|
for i := 0; i < n1; i++ {
|
||||||
|
fcz[i] = makeFullCommit(int64(i), keys1, vals1, vals1, chainID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the val set
|
||||||
|
fcz[n1] = makeFullCommit(int64(n1), keys1, vals1, vals2, chainID)
|
||||||
|
|
||||||
|
// make some commits with the new one
|
||||||
|
for i := n1 + 1; i < nCommits; i++ {
|
||||||
|
fcz[i] = makeFullCommit(int64(i), keys2, vals2, vals2, chainID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save everything in the source
|
||||||
|
for _, fc := range fcz {
|
||||||
|
source.SaveFullCommit(fc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize a Verifier with the initial state.
|
||||||
|
err := trust.SaveFullCommit(fcz[0])
|
||||||
|
require.Nil(t, err)
|
||||||
|
ver := NewDynamicVerifier(chainID, trust, source)
|
||||||
|
ver.SetLogger(log.TestingLogger())
|
||||||
|
|
||||||
|
// fetch the latest from the source
|
||||||
|
latestFC, err := source.LatestFullCommit(chainID, 1, maxHeight)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// try to update to the latest
|
||||||
|
err = ver.Verify(latestFC.SignedHeader)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeFullCommit(height int64, keys privKeys, vals, nextVals *types.ValidatorSet, chainID string) FullCommit {
|
||||||
|
height += 1
|
||||||
|
consHash := []byte("special-params")
|
||||||
|
appHash := []byte(fmt.Sprintf("h=%d", height))
|
||||||
|
resHash := []byte(fmt.Sprintf("res=%d", height))
|
||||||
|
return keys.GenFullCommit(
|
||||||
|
chainID, height, nil,
|
||||||
|
vals, nextVals,
|
||||||
|
appHash, consHash, resHash, 0, len(keys))
|
||||||
|
}
|
||||||
|
|
||||||
func TestInquirerVerifyHistorical(t *testing.T) {
|
func TestInquirerVerifyHistorical(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert, require := assert.New(t), require.New(t)
|
||||||
trust := NewDBProvider("trust", dbm.NewMemDB())
|
trust := NewDBProvider("trust", dbm.NewMemDB())
|
||||||
|
@@ -25,12 +25,6 @@ func (e errUnexpectedValidators) Error() string {
|
|||||||
e.got, e.want)
|
e.got, e.want)
|
||||||
}
|
}
|
||||||
|
|
||||||
type errTooMuchChange struct{}
|
|
||||||
|
|
||||||
func (e errTooMuchChange) Error() string {
|
|
||||||
return "Insufficient signatures to validate due to valset changes"
|
|
||||||
}
|
|
||||||
|
|
||||||
type errUnknownValidators struct {
|
type errUnknownValidators struct {
|
||||||
chainID string
|
chainID string
|
||||||
height int64
|
height int64
|
||||||
@@ -85,22 +79,6 @@ func IsErrUnexpectedValidators(err error) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------
|
|
||||||
// ErrTooMuchChange
|
|
||||||
|
|
||||||
// ErrTooMuchChange indicates that the underlying validator set was changed by >1/3.
|
|
||||||
func ErrTooMuchChange() error {
|
|
||||||
return cmn.ErrorWrap(errTooMuchChange{}, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsErrTooMuchChange(err error) bool {
|
|
||||||
if err_, ok := err.(cmn.Error); ok {
|
|
||||||
_, ok := err_.Data().(errTooMuchChange)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------
|
//-----------------
|
||||||
// ErrUnknownValidators
|
// ErrUnknownValidators
|
||||||
|
|
||||||
|
@@ -6,6 +6,8 @@ import (
|
|||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ PersistentProvider = (*multiProvider)(nil)
|
||||||
|
|
||||||
// multiProvider allows you to place one or more caches in front of a source
|
// multiProvider allows you to place one or more caches in front of a source
|
||||||
// Provider. It runs through them in order until a match is found.
|
// Provider. It runs through them in order until a match is found.
|
||||||
type multiProvider struct {
|
type multiProvider struct {
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package lite
|
package lite
|
||||||
|
|
||||||
import (
|
import (
|
||||||
log "github.com/tendermint/tendermint/libs/log"
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -10,6 +10,7 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||||
|
"github.com/tendermint/tendermint/crypto/merkle"
|
||||||
"github.com/tendermint/tendermint/lite"
|
"github.com/tendermint/tendermint/lite"
|
||||||
certclient "github.com/tendermint/tendermint/lite/client"
|
certclient "github.com/tendermint/tendermint/lite/client"
|
||||||
nm "github.com/tendermint/tendermint/node"
|
nm "github.com/tendermint/tendermint/node"
|
||||||
@@ -143,12 +144,13 @@ func TestTxProofs(t *testing.T) {
|
|||||||
require.NotNil(err)
|
require.NotNil(err)
|
||||||
require.Contains(err.Error(), "not found")
|
require.Contains(err.Error(), "not found")
|
||||||
|
|
||||||
// Now let's check with the real tx hash.
|
// Now let's check with the real tx root hash.
|
||||||
key = types.Tx(tx).Hash()
|
key = types.Tx(tx).Hash()
|
||||||
res, err = cl.Tx(key, true)
|
res, err = cl.Tx(key, true)
|
||||||
require.NoError(err, "%#v", err)
|
require.NoError(err, "%#v", err)
|
||||||
require.NotNil(res)
|
require.NotNil(res)
|
||||||
err = res.Proof.Validate(key)
|
keyHash := merkle.SimpleHashFromByteSlices([][]byte{key})
|
||||||
|
err = res.Proof.Validate(keyHash)
|
||||||
assert.NoError(err, "%#v", err)
|
assert.NoError(err, "%#v", err)
|
||||||
|
|
||||||
commit, err := GetCertifiedCommit(br.Height, cl, cert)
|
commit, err := GetCertifiedCommit(br.Height, cl, cert)
|
||||||
|
@@ -65,6 +65,9 @@ var (
|
|||||||
|
|
||||||
// ErrMempoolIsFull means Tendermint & an application can't handle that much load
|
// ErrMempoolIsFull means Tendermint & an application can't handle that much load
|
||||||
ErrMempoolIsFull = errors.New("Mempool is full")
|
ErrMempoolIsFull = errors.New("Mempool is full")
|
||||||
|
|
||||||
|
// ErrTxTooLarge means the tx is too big to be sent in a message to other peers
|
||||||
|
ErrTxTooLarge = fmt.Errorf("Tx too large. Max size is %d", maxTxSize)
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrPreCheck is returned when tx is too big
|
// ErrPreCheck is returned when tx is too big
|
||||||
@@ -108,6 +111,10 @@ func PostCheckMaxGas(maxGas int64) PostCheckFunc {
|
|||||||
if maxGas == -1 {
|
if maxGas == -1 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if res.GasWanted < 0 {
|
||||||
|
return fmt.Errorf("gas wanted %d is negative",
|
||||||
|
res.GasWanted)
|
||||||
|
}
|
||||||
if res.GasWanted > maxGas {
|
if res.GasWanted > maxGas {
|
||||||
return fmt.Errorf("gas wanted %d is greater than max gas %d",
|
return fmt.Errorf("gas wanted %d is greater than max gas %d",
|
||||||
res.GasWanted, maxGas)
|
res.GasWanted, maxGas)
|
||||||
@@ -305,6 +312,13 @@ func (mem *Mempool) CheckTx(tx types.Tx, cb func(*abci.Response)) (err error) {
|
|||||||
return ErrMempoolIsFull
|
return ErrMempoolIsFull
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The size of the corresponding amino-encoded TxMessage
|
||||||
|
// can't be larger than the maxMsgSize, otherwise we can't
|
||||||
|
// relay it to peers.
|
||||||
|
if len(tx) > maxTxSize {
|
||||||
|
return ErrTxTooLarge
|
||||||
|
}
|
||||||
|
|
||||||
if mem.preCheck != nil {
|
if mem.preCheck != nil {
|
||||||
if err := mem.preCheck(tx); err != nil {
|
if err := mem.preCheck(tx); err != nil {
|
||||||
return ErrPreCheck{err}
|
return ErrPreCheck{err}
|
||||||
@@ -486,11 +500,15 @@ func (mem *Mempool) ReapMaxBytesMaxGas(maxBytes, maxGas int64) types.Txs {
|
|||||||
return txs
|
return txs
|
||||||
}
|
}
|
||||||
totalBytes += int64(len(memTx.tx)) + aminoOverhead
|
totalBytes += int64(len(memTx.tx)) + aminoOverhead
|
||||||
// Check total gas requirement
|
// Check total gas requirement.
|
||||||
if maxGas > -1 && totalGas+memTx.gasWanted > maxGas {
|
// If maxGas is negative, skip this check.
|
||||||
|
// Since newTotalGas < masGas, which
|
||||||
|
// must be non-negative, it follows that this won't overflow.
|
||||||
|
newTotalGas := totalGas + memTx.gasWanted
|
||||||
|
if maxGas > -1 && newTotalGas > maxGas {
|
||||||
return txs
|
return txs
|
||||||
}
|
}
|
||||||
totalGas += memTx.gasWanted
|
totalGas = newTotalGas
|
||||||
txs = append(txs, memTx.tx)
|
txs = append(txs, memTx.tx)
|
||||||
}
|
}
|
||||||
return txs
|
return txs
|
||||||
@@ -548,13 +566,18 @@ func (mem *Mempool) Update(
|
|||||||
// Remove committed transactions.
|
// Remove committed transactions.
|
||||||
txsLeft := mem.removeTxs(txs)
|
txsLeft := mem.removeTxs(txs)
|
||||||
|
|
||||||
// Recheck mempool txs if any txs were committed in the block
|
// Either recheck non-committed txs to see if they became invalid
|
||||||
if mem.config.Recheck && len(txsLeft) > 0 {
|
// or just notify there're some txs left.
|
||||||
mem.logger.Info("Recheck txs", "numtxs", len(txsLeft), "height", height)
|
if len(txsLeft) > 0 {
|
||||||
mem.recheckTxs(txsLeft)
|
if mem.config.Recheck {
|
||||||
// At this point, mem.txs are being rechecked.
|
mem.logger.Info("Recheck txs", "numtxs", len(txsLeft), "height", height)
|
||||||
// mem.recheckCursor re-scans mem.txs and possibly removes some txs.
|
mem.recheckTxs(txsLeft)
|
||||||
// Before mem.Reap(), we should wait for mem.recheckCursor to be nil.
|
// At this point, mem.txs are being rechecked.
|
||||||
|
// mem.recheckCursor re-scans mem.txs and possibly removes some txs.
|
||||||
|
// Before mem.Reap(), we should wait for mem.recheckCursor to be nil.
|
||||||
|
} else {
|
||||||
|
mem.notifyTxsAvailable()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update metrics
|
// Update metrics
|
||||||
@@ -663,7 +686,7 @@ func (cache *mapTxCache) Push(tx types.Tx) bool {
|
|||||||
// Use the tx hash in the cache
|
// Use the tx hash in the cache
|
||||||
txHash := sha256.Sum256(tx)
|
txHash := sha256.Sum256(tx)
|
||||||
if moved, exists := cache.map_[txHash]; exists {
|
if moved, exists := cache.map_[txHash]; exists {
|
||||||
cache.list.MoveToFront(moved)
|
cache.list.MoveToBack(moved)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -14,10 +14,12 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
amino "github.com/tendermint/go-amino"
|
||||||
"github.com/tendermint/tendermint/abci/example/counter"
|
"github.com/tendermint/tendermint/abci/example/counter"
|
||||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
cfg "github.com/tendermint/tendermint/config"
|
cfg "github.com/tendermint/tendermint/config"
|
||||||
|
cmn "github.com/tendermint/tendermint/libs/common"
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
"github.com/tendermint/tendermint/proxy"
|
"github.com/tendermint/tendermint/proxy"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
@@ -394,6 +396,60 @@ func TestMempoolCloseWAL(t *testing.T) {
|
|||||||
require.Equal(t, 1, len(m3), "expecting the wal match in")
|
require.Equal(t, 1, len(m3), "expecting the wal match in")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size of the amino encoded TxMessage is the length of the
|
||||||
|
// encoded byte array, plus 1 for the struct field, plus 4
|
||||||
|
// for the amino prefix.
|
||||||
|
func txMessageSize(tx types.Tx) int {
|
||||||
|
return amino.ByteSliceSize(tx) + 1 + 4
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMempoolMaxMsgSize(t *testing.T) {
|
||||||
|
app := kvstore.NewKVStoreApplication()
|
||||||
|
cc := proxy.NewLocalClientCreator(app)
|
||||||
|
mempl := newMempoolWithApp(cc)
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
len int
|
||||||
|
err bool
|
||||||
|
}{
|
||||||
|
// check small txs. no error
|
||||||
|
{10, false},
|
||||||
|
{1000, false},
|
||||||
|
{1000000, false},
|
||||||
|
|
||||||
|
// check around maxTxSize
|
||||||
|
// changes from no error to error
|
||||||
|
{maxTxSize - 2, false},
|
||||||
|
{maxTxSize - 1, false},
|
||||||
|
{maxTxSize, false},
|
||||||
|
{maxTxSize + 1, true},
|
||||||
|
{maxTxSize + 2, true},
|
||||||
|
|
||||||
|
// check around maxMsgSize. all error
|
||||||
|
{maxMsgSize - 1, true},
|
||||||
|
{maxMsgSize, true},
|
||||||
|
{maxMsgSize + 1, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, testCase := range testCases {
|
||||||
|
caseString := fmt.Sprintf("case %d, len %d", i, testCase.len)
|
||||||
|
|
||||||
|
tx := cmn.RandBytes(testCase.len)
|
||||||
|
err := mempl.CheckTx(tx, nil)
|
||||||
|
msg := &TxMessage{tx}
|
||||||
|
encoded := cdc.MustMarshalBinaryBare(msg)
|
||||||
|
require.Equal(t, len(encoded), txMessageSize(tx), caseString)
|
||||||
|
if !testCase.err {
|
||||||
|
require.True(t, len(encoded) <= maxMsgSize, caseString)
|
||||||
|
require.NoError(t, err, caseString)
|
||||||
|
} else {
|
||||||
|
require.True(t, len(encoded) > maxMsgSize, caseString)
|
||||||
|
require.Equal(t, err, ErrTxTooLarge, caseString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func checksumIt(data []byte) string {
|
func checksumIt(data []byte) string {
|
||||||
h := md5.New()
|
h := md5.New()
|
||||||
h.Write(data)
|
h.Write(data)
|
||||||
|
@@ -7,7 +7,11 @@ import (
|
|||||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||||
)
|
)
|
||||||
|
|
||||||
const MetricsSubsytem = "mempool"
|
const (
|
||||||
|
// MetricsSubsystem is a subsystem shared by all metrics exposed by this
|
||||||
|
// package.
|
||||||
|
MetricsSubsystem = "mempool"
|
||||||
|
)
|
||||||
|
|
||||||
// Metrics contains metrics exposed by this package.
|
// Metrics contains metrics exposed by this package.
|
||||||
// see MetricsProvider for descriptions.
|
// see MetricsProvider for descriptions.
|
||||||
@@ -23,33 +27,39 @@ type Metrics struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PrometheusMetrics returns Metrics build using Prometheus client library.
|
// PrometheusMetrics returns Metrics build using Prometheus client library.
|
||||||
func PrometheusMetrics(namespace string) *Metrics {
|
// Optionally, labels can be provided along with their values ("foo",
|
||||||
|
// "fooValue").
|
||||||
|
func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
|
||||||
|
labels := []string{}
|
||||||
|
for i := 0; i < len(labelsAndValues); i += 2 {
|
||||||
|
labels = append(labels, labelsAndValues[i])
|
||||||
|
}
|
||||||
return &Metrics{
|
return &Metrics{
|
||||||
Size: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
Size: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: MetricsSubsytem,
|
Subsystem: MetricsSubsystem,
|
||||||
Name: "size",
|
Name: "size",
|
||||||
Help: "Size of the mempool (number of uncommitted transactions).",
|
Help: "Size of the mempool (number of uncommitted transactions).",
|
||||||
}, []string{}),
|
}, labels).With(labelsAndValues...),
|
||||||
TxSizeBytes: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
TxSizeBytes: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: MetricsSubsytem,
|
Subsystem: MetricsSubsystem,
|
||||||
Name: "tx_size_bytes",
|
Name: "tx_size_bytes",
|
||||||
Help: "Transaction sizes in bytes.",
|
Help: "Transaction sizes in bytes.",
|
||||||
Buckets: stdprometheus.ExponentialBuckets(1, 3, 17),
|
Buckets: stdprometheus.ExponentialBuckets(1, 3, 17),
|
||||||
}, []string{}),
|
}, labels).With(labelsAndValues...),
|
||||||
FailedTxs: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
FailedTxs: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: MetricsSubsytem,
|
Subsystem: MetricsSubsystem,
|
||||||
Name: "failed_txs",
|
Name: "failed_txs",
|
||||||
Help: "Number of failed transactions.",
|
Help: "Number of failed transactions.",
|
||||||
}, []string{}),
|
}, labels).With(labelsAndValues...),
|
||||||
RecheckTimes: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
RecheckTimes: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: MetricsSubsytem,
|
Subsystem: MetricsSubsystem,
|
||||||
Name: "recheck_times",
|
Name: "recheck_times",
|
||||||
Help: "Number of times transactions are rechecked in the mempool.",
|
Help: "Number of times transactions are rechecked in the mempool.",
|
||||||
}, []string{}),
|
}, labels).With(labelsAndValues...),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,7 +6,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
amino "github.com/tendermint/go-amino"
|
amino "github.com/tendermint/go-amino"
|
||||||
abci "github.com/tendermint/tendermint/abci/types"
|
|
||||||
"github.com/tendermint/tendermint/libs/clist"
|
"github.com/tendermint/tendermint/libs/clist"
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
|
|
||||||
@@ -18,8 +17,10 @@ import (
|
|||||||
const (
|
const (
|
||||||
MempoolChannel = byte(0x30)
|
MempoolChannel = byte(0x30)
|
||||||
|
|
||||||
maxMsgSize = 1048576 // 1MB TODO make it configurable
|
maxMsgSize = 1048576 // 1MB TODO make it configurable
|
||||||
peerCatchupSleepIntervalMS = 100 // If peer is behind, sleep this amount
|
maxTxSize = maxMsgSize - 8 // account for amino overhead of TxMessage
|
||||||
|
|
||||||
|
peerCatchupSleepIntervalMS = 100 // If peer is behind, sleep this amount
|
||||||
)
|
)
|
||||||
|
|
||||||
// MempoolReactor handles mempool tx broadcasting amongst peers.
|
// MempoolReactor handles mempool tx broadcasting amongst peers.
|
||||||
@@ -98,11 +99,6 @@ func (memR *MempoolReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BroadcastTx is an alias for Mempool.CheckTx. Broadcasting itself happens in peer routines.
|
|
||||||
func (memR *MempoolReactor) BroadcastTx(tx types.Tx, cb func(*abci.Response)) error {
|
|
||||||
return memR.Mempool.CheckTx(tx, cb)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PeerState describes the state of a peer.
|
// PeerState describes the state of a peer.
|
||||||
type PeerState interface {
|
type PeerState interface {
|
||||||
GetHeight() int64
|
GetHeight() int64
|
||||||
|
96
node/node.go
96
node/node.go
@@ -7,6 +7,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
_ "net/http/pprof"
|
_ "net/http/pprof"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -86,8 +87,26 @@ func DefaultNewNode(config *cfg.Config, logger log.Logger) (*Node, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert old PrivValidator if it exists.
|
||||||
|
oldPrivVal := config.OldPrivValidatorFile()
|
||||||
|
newPrivValKey := config.PrivValidatorKeyFile()
|
||||||
|
newPrivValState := config.PrivValidatorStateFile()
|
||||||
|
if _, err := os.Stat(oldPrivVal); !os.IsNotExist(err) {
|
||||||
|
oldPV, err := privval.LoadOldFilePV(oldPrivVal)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Error reading OldPrivValidator from %v: %v\n", oldPrivVal, err)
|
||||||
|
}
|
||||||
|
logger.Info("Upgrading PrivValidator file",
|
||||||
|
"old", oldPrivVal,
|
||||||
|
"newKey", newPrivValKey,
|
||||||
|
"newState", newPrivValState,
|
||||||
|
)
|
||||||
|
oldPV.Upgrade(newPrivValKey, newPrivValState)
|
||||||
|
}
|
||||||
|
|
||||||
return NewNode(config,
|
return NewNode(config,
|
||||||
privval.LoadOrGenFilePV(config.PrivValidatorFile()),
|
privval.LoadOrGenFilePV(newPrivValKey, newPrivValState),
|
||||||
nodeKey,
|
nodeKey,
|
||||||
proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()),
|
proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()),
|
||||||
DefaultGenesisDocProviderFunc(config),
|
DefaultGenesisDocProviderFunc(config),
|
||||||
@@ -98,15 +117,17 @@ func DefaultNewNode(config *cfg.Config, logger log.Logger) (*Node, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MetricsProvider returns a consensus, p2p and mempool Metrics.
|
// MetricsProvider returns a consensus, p2p and mempool Metrics.
|
||||||
type MetricsProvider func() (*cs.Metrics, *p2p.Metrics, *mempl.Metrics, *sm.Metrics)
|
type MetricsProvider func(chainID string) (*cs.Metrics, *p2p.Metrics, *mempl.Metrics, *sm.Metrics)
|
||||||
|
|
||||||
// DefaultMetricsProvider returns Metrics build using Prometheus client library
|
// DefaultMetricsProvider returns Metrics build using Prometheus client library
|
||||||
// if Prometheus is enabled. Otherwise, it returns no-op Metrics.
|
// if Prometheus is enabled. Otherwise, it returns no-op Metrics.
|
||||||
func DefaultMetricsProvider(config *cfg.InstrumentationConfig) MetricsProvider {
|
func DefaultMetricsProvider(config *cfg.InstrumentationConfig) MetricsProvider {
|
||||||
return func() (*cs.Metrics, *p2p.Metrics, *mempl.Metrics, *sm.Metrics) {
|
return func(chainID string) (*cs.Metrics, *p2p.Metrics, *mempl.Metrics, *sm.Metrics) {
|
||||||
if config.Prometheus {
|
if config.Prometheus {
|
||||||
return cs.PrometheusMetrics(config.Namespace), p2p.PrometheusMetrics(config.Namespace),
|
return cs.PrometheusMetrics(config.Namespace, "chain_id", chainID),
|
||||||
mempl.PrometheusMetrics(config.Namespace), sm.PrometheusMetrics(config.Namespace)
|
p2p.PrometheusMetrics(config.Namespace, "chain_id", chainID),
|
||||||
|
mempl.PrometheusMetrics(config.Namespace, "chain_id", chainID),
|
||||||
|
sm.PrometheusMetrics(config.Namespace, "chain_id", chainID)
|
||||||
}
|
}
|
||||||
return cs.NopMetrics(), p2p.NopMetrics(), mempl.NopMetrics(), sm.NopMetrics()
|
return cs.NopMetrics(), p2p.NopMetrics(), mempl.NopMetrics(), sm.NopMetrics()
|
||||||
}
|
}
|
||||||
@@ -240,19 +261,22 @@ func NewNode(config *cfg.Config,
|
|||||||
fastSync := config.FastSync
|
fastSync := config.FastSync
|
||||||
if state.Validators.Size() == 1 {
|
if state.Validators.Size() == 1 {
|
||||||
addr, _ := state.Validators.GetByIndex(0)
|
addr, _ := state.Validators.GetByIndex(0)
|
||||||
if bytes.Equal(privValidator.GetAddress(), addr) {
|
privValAddr := privValidator.GetPubKey().Address()
|
||||||
|
if bytes.Equal(privValAddr, addr) {
|
||||||
fastSync = false
|
fastSync = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pubKey := privValidator.GetPubKey()
|
||||||
|
addr := pubKey.Address()
|
||||||
// Log whether this node is a validator or an observer
|
// Log whether this node is a validator or an observer
|
||||||
if state.Validators.HasAddress(privValidator.GetAddress()) {
|
if state.Validators.HasAddress(addr) {
|
||||||
consensusLogger.Info("This node is a validator", "addr", privValidator.GetAddress(), "pubKey", privValidator.GetPubKey())
|
consensusLogger.Info("This node is a validator", "addr", addr, "pubKey", pubKey)
|
||||||
} else {
|
} else {
|
||||||
consensusLogger.Info("This node is not a validator", "addr", privValidator.GetAddress(), "pubKey", privValidator.GetPubKey())
|
consensusLogger.Info("This node is not a validator", "addr", addr, "pubKey", pubKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
csMetrics, p2pMetrics, memplMetrics, smMetrics := metricsProvider()
|
csMetrics, p2pMetrics, memplMetrics, smMetrics := metricsProvider(genDoc.ChainID)
|
||||||
|
|
||||||
// Make MempoolReactor
|
// Make MempoolReactor
|
||||||
mempool := mempl.NewMempool(
|
mempool := mempl.NewMempool(
|
||||||
@@ -348,20 +372,21 @@ func NewNode(config *cfg.Config,
|
|||||||
indexerService := txindex.NewIndexerService(txIndexer, eventBus)
|
indexerService := txindex.NewIndexerService(txIndexer, eventBus)
|
||||||
indexerService.SetLogger(logger.With("module", "txindex"))
|
indexerService.SetLogger(logger.With("module", "txindex"))
|
||||||
|
|
||||||
var (
|
p2pLogger := logger.With("module", "p2p")
|
||||||
p2pLogger = logger.With("module", "p2p")
|
nodeInfo, err := makeNodeInfo(
|
||||||
nodeInfo = makeNodeInfo(
|
config,
|
||||||
config,
|
nodeKey.ID(),
|
||||||
nodeKey.ID(),
|
txIndexer,
|
||||||
txIndexer,
|
genDoc.ChainID,
|
||||||
genDoc.ChainID,
|
p2p.NewProtocolVersion(
|
||||||
p2p.NewProtocolVersion(
|
version.P2PProtocol, // global
|
||||||
version.P2PProtocol, // global
|
state.Version.Consensus.Block,
|
||||||
state.Version.Consensus.Block,
|
state.Version.Consensus.App,
|
||||||
state.Version.Consensus.App,
|
),
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Setup Transport.
|
// Setup Transport.
|
||||||
var (
|
var (
|
||||||
@@ -616,7 +641,8 @@ func (n *Node) ConfigureRPC() {
|
|||||||
rpccore.SetEvidencePool(n.evidencePool)
|
rpccore.SetEvidencePool(n.evidencePool)
|
||||||
rpccore.SetP2PPeers(n.sw)
|
rpccore.SetP2PPeers(n.sw)
|
||||||
rpccore.SetP2PTransport(n)
|
rpccore.SetP2PTransport(n)
|
||||||
rpccore.SetPubKey(n.privValidator.GetPubKey())
|
pubKey := n.privValidator.GetPubKey()
|
||||||
|
rpccore.SetPubKey(pubKey)
|
||||||
rpccore.SetGenesisDoc(n.genesisDoc)
|
rpccore.SetGenesisDoc(n.genesisDoc)
|
||||||
rpccore.SetAddrBook(n.addrBook)
|
rpccore.SetAddrBook(n.addrBook)
|
||||||
rpccore.SetProxyAppQuery(n.proxyApp.Query())
|
rpccore.SetProxyAppQuery(n.proxyApp.Query())
|
||||||
@@ -782,7 +808,7 @@ func makeNodeInfo(
|
|||||||
txIndexer txindex.TxIndexer,
|
txIndexer txindex.TxIndexer,
|
||||||
chainID string,
|
chainID string,
|
||||||
protocolVersion p2p.ProtocolVersion,
|
protocolVersion p2p.ProtocolVersion,
|
||||||
) p2p.NodeInfo {
|
) (p2p.NodeInfo, error) {
|
||||||
txIndexerStatus := "on"
|
txIndexerStatus := "on"
|
||||||
if _, ok := txIndexer.(*null.TxIndex); ok {
|
if _, ok := txIndexer.(*null.TxIndex); ok {
|
||||||
txIndexerStatus = "off"
|
txIndexerStatus = "off"
|
||||||
@@ -817,7 +843,8 @@ func makeNodeInfo(
|
|||||||
|
|
||||||
nodeInfo.ListenAddr = lAddr
|
nodeInfo.ListenAddr = lAddr
|
||||||
|
|
||||||
return nodeInfo
|
err := nodeInfo.Validate()
|
||||||
|
return nodeInfo, err
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -853,16 +880,20 @@ func createAndStartPrivValidatorSocketClient(
|
|||||||
listenAddr string,
|
listenAddr string,
|
||||||
logger log.Logger,
|
logger log.Logger,
|
||||||
) (types.PrivValidator, error) {
|
) (types.PrivValidator, error) {
|
||||||
var pvsc types.PrivValidator
|
var listener net.Listener
|
||||||
|
|
||||||
protocol, address := cmn.ProtocolAndAddress(listenAddr)
|
protocol, address := cmn.ProtocolAndAddress(listenAddr)
|
||||||
|
ln, err := net.Listen(protocol, address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
switch protocol {
|
switch protocol {
|
||||||
case "unix":
|
case "unix":
|
||||||
pvsc = privval.NewIPCVal(logger.With("module", "privval"), address)
|
listener = privval.NewUnixListener(ln)
|
||||||
case "tcp":
|
case "tcp":
|
||||||
// TODO: persist this key so external signer
|
// TODO: persist this key so external signer
|
||||||
// can actually authenticate us
|
// can actually authenticate us
|
||||||
pvsc = privval.NewTCPVal(logger.With("module", "privval"), listenAddr, ed25519.GenPrivKey())
|
listener = privval.NewTCPListener(ln, ed25519.GenPrivKey())
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf(
|
return nil, fmt.Errorf(
|
||||||
"Wrong listen address: expected either 'tcp' or 'unix' protocols, got %s",
|
"Wrong listen address: expected either 'tcp' or 'unix' protocols, got %s",
|
||||||
@@ -870,10 +901,9 @@ func createAndStartPrivValidatorSocketClient(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if pvsc, ok := pvsc.(cmn.Service); ok {
|
pvsc := privval.NewSocketVal(logger.With("module", "privval"), listener)
|
||||||
if err := pvsc.Start(); err != nil {
|
if err := pvsc.Start(); err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to start")
|
return nil, errors.Wrap(err, "failed to start private validator")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pvsc, nil
|
return pvsc, nil
|
||||||
|
@@ -15,10 +15,14 @@ import (
|
|||||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||||
cfg "github.com/tendermint/tendermint/config"
|
cfg "github.com/tendermint/tendermint/config"
|
||||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||||
|
"github.com/tendermint/tendermint/evidence"
|
||||||
cmn "github.com/tendermint/tendermint/libs/common"
|
cmn "github.com/tendermint/tendermint/libs/common"
|
||||||
|
dbm "github.com/tendermint/tendermint/libs/db"
|
||||||
"github.com/tendermint/tendermint/libs/log"
|
"github.com/tendermint/tendermint/libs/log"
|
||||||
|
mempl "github.com/tendermint/tendermint/mempool"
|
||||||
"github.com/tendermint/tendermint/p2p"
|
"github.com/tendermint/tendermint/p2p"
|
||||||
"github.com/tendermint/tendermint/privval"
|
"github.com/tendermint/tendermint/privval"
|
||||||
|
"github.com/tendermint/tendermint/proxy"
|
||||||
sm "github.com/tendermint/tendermint/state"
|
sm "github.com/tendermint/tendermint/state"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
tmtime "github.com/tendermint/tendermint/types/time"
|
tmtime "github.com/tendermint/tendermint/types/time"
|
||||||
@@ -122,25 +126,25 @@ func TestNodeSetPrivValTCP(t *testing.T) {
|
|||||||
config := cfg.ResetTestRoot("node_priv_val_tcp_test")
|
config := cfg.ResetTestRoot("node_priv_val_tcp_test")
|
||||||
config.BaseConfig.PrivValidatorListenAddr = addr
|
config.BaseConfig.PrivValidatorListenAddr = addr
|
||||||
|
|
||||||
rs := privval.NewRemoteSigner(
|
dialer := privval.DialTCPFn(addr, 100*time.Millisecond, ed25519.GenPrivKey())
|
||||||
|
pvsc := privval.NewRemoteSigner(
|
||||||
log.TestingLogger(),
|
log.TestingLogger(),
|
||||||
config.ChainID(),
|
config.ChainID(),
|
||||||
addr,
|
|
||||||
types.NewMockPV(),
|
types.NewMockPV(),
|
||||||
ed25519.GenPrivKey(),
|
dialer,
|
||||||
)
|
)
|
||||||
privval.RemoteSignerConnDeadline(5 * time.Millisecond)(rs)
|
|
||||||
go func() {
|
go func() {
|
||||||
err := rs.Start()
|
err := pvsc.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
defer rs.Stop()
|
defer pvsc.Stop()
|
||||||
|
|
||||||
n, err := DefaultNewNode(config, log.TestingLogger())
|
n, err := DefaultNewNode(config, log.TestingLogger())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.IsType(t, &privval.TCPVal{}, n.PrivValidator())
|
assert.IsType(t, &privval.SocketVal{}, n.PrivValidator())
|
||||||
}
|
}
|
||||||
|
|
||||||
// address without a protocol must result in error
|
// address without a protocol must result in error
|
||||||
@@ -161,25 +165,25 @@ func TestNodeSetPrivValIPC(t *testing.T) {
|
|||||||
config := cfg.ResetTestRoot("node_priv_val_tcp_test")
|
config := cfg.ResetTestRoot("node_priv_val_tcp_test")
|
||||||
config.BaseConfig.PrivValidatorListenAddr = "unix://" + tmpfile
|
config.BaseConfig.PrivValidatorListenAddr = "unix://" + tmpfile
|
||||||
|
|
||||||
rs := privval.NewIPCRemoteSigner(
|
dialer := privval.DialUnixFn(tmpfile)
|
||||||
|
pvsc := privval.NewRemoteSigner(
|
||||||
log.TestingLogger(),
|
log.TestingLogger(),
|
||||||
config.ChainID(),
|
config.ChainID(),
|
||||||
tmpfile,
|
|
||||||
types.NewMockPV(),
|
types.NewMockPV(),
|
||||||
|
dialer,
|
||||||
)
|
)
|
||||||
privval.IPCRemoteSignerConnDeadline(3 * time.Second)(rs)
|
|
||||||
|
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer close(done)
|
defer close(done)
|
||||||
n, err := DefaultNewNode(config, log.TestingLogger())
|
n, err := DefaultNewNode(config, log.TestingLogger())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.IsType(t, &privval.IPCVal{}, n.PrivValidator())
|
assert.IsType(t, &privval.SocketVal{}, n.PrivValidator())
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err := rs.Start()
|
err := pvsc.Start()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer rs.Stop()
|
defer pvsc.Stop()
|
||||||
|
|
||||||
<-done
|
<-done
|
||||||
}
|
}
|
||||||
@@ -192,3 +196,110 @@ func testFreeAddr(t *testing.T) string {
|
|||||||
|
|
||||||
return fmt.Sprintf("127.0.0.1:%d", ln.Addr().(*net.TCPAddr).Port)
|
return fmt.Sprintf("127.0.0.1:%d", ln.Addr().(*net.TCPAddr).Port)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create a proposal block using real and full
|
||||||
|
// mempool and evidence pool and validate it.
|
||||||
|
func TestCreateProposalBlock(t *testing.T) {
|
||||||
|
config := cfg.ResetTestRoot("node_create_proposal")
|
||||||
|
cc := proxy.NewLocalClientCreator(kvstore.NewKVStoreApplication())
|
||||||
|
proxyApp := proxy.NewAppConns(cc)
|
||||||
|
err := proxyApp.Start()
|
||||||
|
require.Nil(t, err)
|
||||||
|
defer proxyApp.Stop()
|
||||||
|
|
||||||
|
logger := log.TestingLogger()
|
||||||
|
|
||||||
|
var height int64 = 1
|
||||||
|
state, stateDB := state(1, height)
|
||||||
|
maxBytes := 16384
|
||||||
|
state.ConsensusParams.BlockSize.MaxBytes = int64(maxBytes)
|
||||||
|
proposerAddr, _ := state.Validators.GetByIndex(0)
|
||||||
|
|
||||||
|
// Make Mempool
|
||||||
|
memplMetrics := mempl.PrometheusMetrics("node_test")
|
||||||
|
mempool := mempl.NewMempool(
|
||||||
|
config.Mempool,
|
||||||
|
proxyApp.Mempool(),
|
||||||
|
state.LastBlockHeight,
|
||||||
|
mempl.WithMetrics(memplMetrics),
|
||||||
|
mempl.WithPreCheck(sm.TxPreCheck(state)),
|
||||||
|
mempl.WithPostCheck(sm.TxPostCheck(state)),
|
||||||
|
)
|
||||||
|
mempool.SetLogger(logger)
|
||||||
|
|
||||||
|
// Make EvidencePool
|
||||||
|
types.RegisterMockEvidencesGlobal()
|
||||||
|
evidence.RegisterMockEvidences()
|
||||||
|
evidenceDB := dbm.NewMemDB()
|
||||||
|
evidenceStore := evidence.NewEvidenceStore(evidenceDB)
|
||||||
|
evidencePool := evidence.NewEvidencePool(stateDB, evidenceStore)
|
||||||
|
evidencePool.SetLogger(logger)
|
||||||
|
|
||||||
|
// fill the evidence pool with more evidence
|
||||||
|
// than can fit in a block
|
||||||
|
minEvSize := 12
|
||||||
|
numEv := (maxBytes / types.MaxEvidenceBytesDenominator) / minEvSize
|
||||||
|
for i := 0; i < numEv; i++ {
|
||||||
|
ev := types.NewMockRandomGoodEvidence(1, proposerAddr, cmn.RandBytes(minEvSize))
|
||||||
|
err := evidencePool.AddEvidence(ev)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill the mempool with more txs
|
||||||
|
// than can fit in a block
|
||||||
|
txLength := 1000
|
||||||
|
for i := 0; i < maxBytes/txLength; i++ {
|
||||||
|
tx := cmn.RandBytes(txLength)
|
||||||
|
err := mempool.CheckTx(tx, nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
blockExec := sm.NewBlockExecutor(
|
||||||
|
stateDB,
|
||||||
|
logger,
|
||||||
|
proxyApp.Consensus(),
|
||||||
|
mempool,
|
||||||
|
evidencePool,
|
||||||
|
)
|
||||||
|
|
||||||
|
commit := &types.Commit{}
|
||||||
|
block, _ := blockExec.CreateProposalBlock(
|
||||||
|
height,
|
||||||
|
state, commit,
|
||||||
|
proposerAddr,
|
||||||
|
)
|
||||||
|
|
||||||
|
err = blockExec.ValidateBlock(state, block)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func state(nVals int, height int64) (sm.State, dbm.DB) {
|
||||||
|
vals := make([]types.GenesisValidator, nVals)
|
||||||
|
for i := 0; i < nVals; i++ {
|
||||||
|
secret := []byte(fmt.Sprintf("test%d", i))
|
||||||
|
pk := ed25519.GenPrivKeyFromSecret(secret)
|
||||||
|
vals[i] = types.GenesisValidator{
|
||||||
|
pk.PubKey().Address(),
|
||||||
|
pk.PubKey(),
|
||||||
|
1000,
|
||||||
|
fmt.Sprintf("test%d", i),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s, _ := sm.MakeGenesisState(&types.GenesisDoc{
|
||||||
|
ChainID: "test-chain",
|
||||||
|
Validators: vals,
|
||||||
|
AppHash: nil,
|
||||||
|
})
|
||||||
|
|
||||||
|
// save validators to db for 2 heights
|
||||||
|
stateDB := dbm.NewMemDB()
|
||||||
|
sm.SaveState(stateDB, s)
|
||||||
|
|
||||||
|
for i := 1; i < int(height); i++ {
|
||||||
|
s.LastBlockHeight++
|
||||||
|
s.LastValidators = s.Validators.Copy()
|
||||||
|
sm.SaveState(stateDB, s)
|
||||||
|
}
|
||||||
|
return s, stateDB
|
||||||
|
}
|
||||||
|
@@ -4,8 +4,8 @@ The p2p package provides an abstraction around peer-to-peer communication.
|
|||||||
|
|
||||||
Docs:
|
Docs:
|
||||||
|
|
||||||
- [Connection](https://github.com/tendermint/tendermint/blob/master/docs/spec/docs/spec/p2p/connection.md) for details on how connections and multiplexing work
|
- [Connection](https://github.com/tendermint/tendermint/blob/master/docs/spec/p2p/connection.md) for details on how connections and multiplexing work
|
||||||
- [Peer](https://github.com/tendermint/tendermint/blob/master/docs/spec/docs/spec/p2p/peer.md) for details on peer ID, handshakes, and peer exchange
|
- [Peer](https://github.com/tendermint/tendermint/blob/master/docs/spec/p2p/peer.md) for details on peer ID, handshakes, and peer exchange
|
||||||
- [Node](https://github.com/tendermint/tendermint/blob/master/docs/spec/docs/spec/p2p/node.md) for details about different types of nodes and how they should work
|
- [Node](https://github.com/tendermint/tendermint/blob/master/docs/spec/p2p/node.md) for details about different types of nodes and how they should work
|
||||||
- [Pex](https://github.com/tendermint/tendermint/blob/master/docs/spec/docs/spec/reactors/pex/pex.md) for details on peer discovery and exchange
|
- [Pex](https://github.com/tendermint/tendermint/blob/master/docs/spec/reactors/pex/pex.md) for details on peer discovery and exchange
|
||||||
- [Config](https://github.com/tendermint/tendermint/blob/master/docs/spec/docs/spec/p2p/config.md) for details on some config option
|
- [Config](https://github.com/tendermint/tendermint/blob/master/docs/spec/p2p/config.md) for details on some config option
|
||||||
|
@@ -160,6 +160,7 @@ func NewMConnectionWithConfig(conn net.Conn, chDescs []*ChannelDescriptor, onRec
|
|||||||
onReceive: onReceive,
|
onReceive: onReceive,
|
||||||
onError: onError,
|
onError: onError,
|
||||||
config: config,
|
config: config,
|
||||||
|
created: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create channels
|
// Create channels
|
||||||
|
@@ -8,9 +8,9 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
// forked to github.com/tendermint/crypto
|
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
"golang.org/x/crypto/curve25519"
|
"golang.org/x/crypto/curve25519"
|
||||||
"golang.org/x/crypto/nacl/box"
|
"golang.org/x/crypto/nacl/box"
|
||||||
@@ -28,20 +28,36 @@ const aeadSizeOverhead = 16 // overhead of poly 1305 authentication tag
|
|||||||
const aeadKeySize = chacha20poly1305.KeySize
|
const aeadKeySize = chacha20poly1305.KeySize
|
||||||
const aeadNonceSize = chacha20poly1305.NonceSize
|
const aeadNonceSize = chacha20poly1305.NonceSize
|
||||||
|
|
||||||
// SecretConnection implements net.conn.
|
// SecretConnection implements net.Conn.
|
||||||
// It is an implementation of the STS protocol.
|
// It is an implementation of the STS protocol.
|
||||||
// Note we do not (yet) assume that a remote peer's pubkey
|
// See https://github.com/tendermint/tendermint/blob/0.1/docs/sts-final.pdf for
|
||||||
// is known ahead of time, and thus we are technically
|
// details on the protocol.
|
||||||
// still vulnerable to MITM. (TODO!)
|
//
|
||||||
// See docs/sts-final.pdf for more info
|
// Consumers of the SecretConnection are responsible for authenticating
|
||||||
|
// the remote peer's pubkey against known information, like a nodeID.
|
||||||
|
// Otherwise they are vulnerable to MITM.
|
||||||
|
// (TODO(ismail): see also https://github.com/tendermint/tendermint/issues/3010)
|
||||||
type SecretConnection struct {
|
type SecretConnection struct {
|
||||||
conn io.ReadWriteCloser
|
|
||||||
recvBuffer []byte
|
// immutable
|
||||||
recvNonce *[aeadNonceSize]byte
|
|
||||||
sendNonce *[aeadNonceSize]byte
|
|
||||||
recvSecret *[aeadKeySize]byte
|
recvSecret *[aeadKeySize]byte
|
||||||
sendSecret *[aeadKeySize]byte
|
sendSecret *[aeadKeySize]byte
|
||||||
remPubKey crypto.PubKey
|
remPubKey crypto.PubKey
|
||||||
|
conn io.ReadWriteCloser
|
||||||
|
|
||||||
|
// net.Conn must be thread safe:
|
||||||
|
// https://golang.org/pkg/net/#Conn.
|
||||||
|
// Since we have internal mutable state,
|
||||||
|
// we need mtxs. But recv and send states
|
||||||
|
// are independent, so we can use two mtxs.
|
||||||
|
// All .Read are covered by recvMtx,
|
||||||
|
// all .Write are covered by sendMtx.
|
||||||
|
recvMtx sync.Mutex
|
||||||
|
recvBuffer []byte
|
||||||
|
recvNonce *[aeadNonceSize]byte
|
||||||
|
|
||||||
|
sendMtx sync.Mutex
|
||||||
|
sendNonce *[aeadNonceSize]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeSecretConnection performs handshake and returns a new authenticated
|
// MakeSecretConnection performs handshake and returns a new authenticated
|
||||||
@@ -110,9 +126,12 @@ func (sc *SecretConnection) RemotePubKey() crypto.PubKey {
|
|||||||
return sc.remPubKey
|
return sc.remPubKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writes encrypted frames of `sealedFrameSize`
|
// Writes encrypted frames of `totalFrameSize + aeadSizeOverhead`.
|
||||||
// CONTRACT: data smaller than dataMaxSize is read atomically.
|
// CONTRACT: data smaller than dataMaxSize is written atomically.
|
||||||
func (sc *SecretConnection) Write(data []byte) (n int, err error) {
|
func (sc *SecretConnection) Write(data []byte) (n int, err error) {
|
||||||
|
sc.sendMtx.Lock()
|
||||||
|
defer sc.sendMtx.Unlock()
|
||||||
|
|
||||||
for 0 < len(data) {
|
for 0 < len(data) {
|
||||||
var frame = make([]byte, totalFrameSize)
|
var frame = make([]byte, totalFrameSize)
|
||||||
var chunk []byte
|
var chunk []byte
|
||||||
@@ -131,6 +150,7 @@ func (sc *SecretConnection) Write(data []byte) (n int, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return n, errors.New("Invalid SecretConnection Key")
|
return n, errors.New("Invalid SecretConnection Key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// encrypt the frame
|
// encrypt the frame
|
||||||
var sealedFrame = make([]byte, aeadSizeOverhead+totalFrameSize)
|
var sealedFrame = make([]byte, aeadSizeOverhead+totalFrameSize)
|
||||||
aead.Seal(sealedFrame[:0], sc.sendNonce[:], frame, nil)
|
aead.Seal(sealedFrame[:0], sc.sendNonce[:], frame, nil)
|
||||||
@@ -148,23 +168,30 @@ func (sc *SecretConnection) Write(data []byte) (n int, err error) {
|
|||||||
|
|
||||||
// CONTRACT: data smaller than dataMaxSize is read atomically.
|
// CONTRACT: data smaller than dataMaxSize is read atomically.
|
||||||
func (sc *SecretConnection) Read(data []byte) (n int, err error) {
|
func (sc *SecretConnection) Read(data []byte) (n int, err error) {
|
||||||
|
sc.recvMtx.Lock()
|
||||||
|
defer sc.recvMtx.Unlock()
|
||||||
|
|
||||||
|
// read off and update the recvBuffer, if non-empty
|
||||||
if 0 < len(sc.recvBuffer) {
|
if 0 < len(sc.recvBuffer) {
|
||||||
n = copy(data, sc.recvBuffer)
|
n = copy(data, sc.recvBuffer)
|
||||||
sc.recvBuffer = sc.recvBuffer[n:]
|
sc.recvBuffer = sc.recvBuffer[n:]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read off the conn
|
||||||
|
sealedFrame := make([]byte, totalFrameSize+aeadSizeOverhead)
|
||||||
|
_, err = io.ReadFull(sc.conn, sealedFrame)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
aead, err := chacha20poly1305.New(sc.recvSecret[:])
|
aead, err := chacha20poly1305.New(sc.recvSecret[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return n, errors.New("Invalid SecretConnection Key")
|
return n, errors.New("Invalid SecretConnection Key")
|
||||||
}
|
}
|
||||||
sealedFrame := make([]byte, totalFrameSize+aeadSizeOverhead)
|
|
||||||
_, err = io.ReadFull(sc.conn, sealedFrame)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// decrypt the frame
|
// decrypt the frame.
|
||||||
|
// reads and updates the sc.recvNonce
|
||||||
var frame = make([]byte, totalFrameSize)
|
var frame = make([]byte, totalFrameSize)
|
||||||
_, err = aead.Open(frame[:0], sc.recvNonce[:], sealedFrame, nil)
|
_, err = aead.Open(frame[:0], sc.recvNonce[:], sealedFrame, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -173,12 +200,13 @@ func (sc *SecretConnection) Read(data []byte) (n int, err error) {
|
|||||||
incrNonce(sc.recvNonce)
|
incrNonce(sc.recvNonce)
|
||||||
// end decryption
|
// end decryption
|
||||||
|
|
||||||
|
// copy checkLength worth into data,
|
||||||
|
// set recvBuffer to the rest.
|
||||||
var chunkLength = binary.LittleEndian.Uint32(frame) // read the first four bytes
|
var chunkLength = binary.LittleEndian.Uint32(frame) // read the first four bytes
|
||||||
if chunkLength > dataMaxSize {
|
if chunkLength > dataMaxSize {
|
||||||
return 0, errors.New("chunkLength is greater than dataMaxSize")
|
return 0, errors.New("chunkLength is greater than dataMaxSize")
|
||||||
}
|
}
|
||||||
var chunk = frame[dataLenSize : dataLenSize+chunkLength]
|
var chunk = frame[dataLenSize : dataLenSize+chunkLength]
|
||||||
|
|
||||||
n = copy(data, chunk)
|
n = copy(data, chunk)
|
||||||
sc.recvBuffer = chunk[n:]
|
sc.recvBuffer = chunk[n:]
|
||||||
return
|
return
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user