Compare commits

...

28 Commits

Author SHA1 Message Date
Ismail Khoffi
79210e658d first stab on checked ints 2018-10-26 18:55:19 +02:00
zhangzheng
bbf15b3d09 tm-monitor: update health after we added / removed node (#2694)
Refs #2693
2018-10-25 12:27:32 +02:00
Ismail Khoffi
6643c5dd11 Catch up with amino 0.13.0 (#2690)
* catch up with amino changes in
https://github.com/tendermint/go-amino/pull/222

* WIP: update to amino v0.13.0

* update to fixed amino release
2018-10-24 21:34:01 -04:00
Jun Kimura
9795e12ef2 fix RecoverAndLogHandler not to call multiple writeheader (#2688) 2018-10-24 10:07:33 +02:00
Ethan Buchman
be929acd6a Update to Amino v0.13.0-rc0 (#2687)
* types: test tm2pm on fully populated header

* upgrade for amino v0.13.0-rc0

* fix lint

* comment out final test
2018-10-23 13:21:47 -04:00
Ethan Buchman
fe1d59ab7b Set protocol versions in NodeInfo from state (#2686)
* use types.NewValidator

* node: set p2p.ProtocolVersion from state, not globals
2018-10-22 17:55:49 -04:00
Ethan Buchman
f94eb42ebe Version bump; Update Upgrading.md; linkify Changelog (#2679)
* version bump

* update UPGRADING.md

* add missing pr numbers to changelog pending

* linkify changelog
2018-10-19 20:27:00 -04:00
Ethan Buchman
9d62bd0ad3 crypto: use stdlib crypto/rand. ref #2099 (#2669)
* crypto: use stdlib crypto/rand. ref #2099

* comment
2018-10-19 14:29:45 -04:00
Ethan Buchman
30519e8361 types: first field in Canonical structs is Type (#2675)
* types: first field in Canonical structs is Type

* fix spec
2018-10-19 14:23:14 -04:00
Ethan Buchman
7c6519adbd Bucky/changelog (#2673)
* update changelog, add authors script

* update changelog

* update changelog
2018-10-19 13:49:04 -04:00
Ethan Buchman
f536089f0b types: dont use SimpleHashFromMap for header. closes #1841 (#2670)
* types: dont use SimpleHashFromMap for header. closes #1841

* changelog and spec

* comments
2018-10-19 11:39:27 -04:00
Ethan Buchman
746d137f86 p2p: Restore OriginalAddr (#2668)
* p2p: bring back OriginalAddr

* p2p: set OriginalAddr

* update changelog
2018-10-18 18:26:32 -04:00
Ethan Buchman
e798766a27 types: remove Version from CanonicalXxx (#2666) 2018-10-18 18:02:20 -04:00
Ethan Buchman
c3384e88e5 adr-016: update int64->uint64; add version to ConsensusParams (#2667) 2018-10-18 17:32:53 -04:00
Ethan Buchman
ed4ce5ff6c ADR-016: Update ABCI Info method for versions (#2662)
* abci: update RequestInfo for versions

* abci: update ResponseInfo for versions

* compile fix

* fix test

* software_version -> version

* comment fix

* update spec

* add test

* comments and fix test
2018-10-18 16:51:17 -04:00
Anton Kaliaev
055d7adffb add tm-abci python ABCI server (fork of py-abci) (#2658)
It utilises async IO -> greater performance.
2018-10-18 20:03:45 +04:00
Ethan Buchman
14c1baeb24 ADR-016: Add protocol Version to NodeInfo (#2654)
* p2p: add protocol Version to NodeInfo

* update node pkg. remove extraneous version files

* update changelog and docs

* fix test

* p2p: Version -> ProtocolVersion; more ValidateBasic and tests
2018-10-18 10:29:59 -04:00
Ethan Buchman
455d34134c ADR-016: Add versions to Block and State (#2644)
* types: add Version to Header

* abci: add Version to Header

* state: add Version to State

* node: check software and state protocol versions match

* update changelog

* docs/spec: update for versions

* state: more tests

* remove TODOs

* remove empty test
2018-10-17 15:30:53 -04:00
Jack Zampolin
6a07f415e9 Change error output format for better SDK and Voyager UX (#2648)
* Change error output format

* Update tests

* 🤦

* apply suggestion
2018-10-17 14:26:14 -04:00
Anton Kaliaev
d20693fb16 install scripts: update go version, remove upgrade cmd (#2650)
* [install scripts] update go version, remove upgrade

- upgrading OS is out of scope of installation scripts

* add missing log statement

Refs https://github.com/tendermint/tendermint/pull/2642#discussion_r225786794
2018-10-17 12:47:56 -04:00
Hendrik Hofstadt
f60713bca8 privval: Add IPCPV and fix SocketPV (#2568)
Ref: #2563

I added IPC as an unencrypted alternative to SocketPV.

Besides I fixed the following aspects of SocketPV:

Added locking since we are operating on a single socket
The connection deadline is extended every time a successful packet exchange happens; otherwise the connection would always die permanently x seconds after the connection was established.
Added a ping/heartbeat mechanism to keep the connection alive; native TCP keepalives do not work in this use-case


* Extend the SecureConn socket to extend its deadline
* Add locking & ping/heartbeat packets to SocketPV
* Implement IPC PV and abstract socket signing
* Refactored IPC and SocketPV
* Implement @melekes comments
* Fixes to rebase
2018-10-17 10:26:14 +02:00
Uzair1995
ed107d0e84 [scripts/install_tendermint_ubuntu] change /root to local user (#2647) 2018-10-17 10:12:31 +04:00
Peng Zhong
80562669bf add Google Analytics for documentation pages (#2645)
We need Google Analytics to start measuring how many developers are viewing our documentation. That way we can gauge how successful an/or useful various pages are. VuePress supports GA and all we have to provide is the tracking code.

This PR also renames the static docs site to "Tendermint Documentation", which is a better representation of the contents than only "Tendermint Core".
2018-10-16 10:10:52 +04:00
Anton Kaliaev
55362ed766 [pubsub] document design shortcomings (#2641)
Refs https://github.com/tendermint/tendermint/issues/1811#issuecomment-427825250
2018-10-16 10:09:24 +04:00
Dev Ojha
124d0db1e0 Make txs and evidencelist use merkle.SimpleHashFromBytes to create hash (#2635)
This is a performance regression, but will also spare the types directory
from knowing about RFC 6962, which is a more correct abstraction. For txs
this performance hit will be fixed soon with #2603. For evidence, the
performance impact is negligible due to it being capped at a small number.
2018-10-15 16:42:47 -04:00
Joon
4ab7dcf3ac [R4R] Unmerklize ConsensusParams.Hash() (#2609)
* Hash() uses tmhash instead of merkle.SimpleHashFromMap

* marshal whole struct

* update comments

* update docs
2018-10-15 16:31:27 -04:00
Joon
26462025bc standardize header.Hash() (#2610) 2018-10-15 16:28:49 -04:00
Zarko Milosevic
287b25a059 Align with spec (#2642) 2018-10-15 16:05:13 -04:00
105 changed files with 3633 additions and 1802 deletions

View File

@@ -1,69 +1,107 @@
# Pending
## v0.26.0
*October 19, 2018*
Special thanks to external contributors on this release:
@goolAdapter, @bradyjoestar
@bradyjoestar, @connorwstein, @goolAdapter, @HaoyangLiu,
@james-ray, @overbool, @phymbert, @Slamper, @Uzair1995
This release is primarily about adding Version fields to various data structures,
optimizing consensus messages for signing and verification in
restricted environments (like HSMs and the Ethereum Virtual Machine), and
aligning the consensus code with the [specification](https://arxiv.org/abs/1807.04938).
It also includes our first take at a generalized merkle proof system.
See the [UPGRADING.md](UPGRADING.md#v0.26.0) for details on upgrading to the new
version.
Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint).
BREAKING CHANGES:
* CLI/RPC/Config
* [config] \#2232 timeouts as time.Duration, not ints
* [config] \#2505 Remove Mempool.RecheckEmpty (it was effectively useless anyways)
* [config] `mempool.wal` is disabled by default
* [rpc] \#2298 `/abci_query` takes `prove` argument instead of `trusted` and switches the default
* [config] [\#2232](https://github.com/tendermint/tendermint/issues/2232) timeouts as time.Duration, not ints
* [config] [\#2505](https://github.com/tendermint/tendermint/issues/2505) Remove Mempool.RecheckEmpty (it was effectively useless anyways)
* [config] [\#2490](https://github.com/tendermint/tendermint/issues/2490) `mempool.wal` is disabled by default
* [privval] [\#2459](https://github.com/tendermint/tendermint/issues/2459) Split `SocketPVMsg`s implementations into Request and Response, where the Response may contain a error message (returned by the remote signer)
* [state] [\#2644](https://github.com/tendermint/tendermint/issues/2644) Add Version field to State, breaking the format of State as
encoded on disk.
* [rpc] [\#2298](https://github.com/tendermint/tendermint/issues/2298) `/abci_query` takes `prove` argument instead of `trusted` and switches the default
behaviour to `prove=false`
* [privval] \#2459 Split `SocketPVMsg`s implementations into Request and Response, where the Response may contain a error message (returned by the remote signer)
* [rpc] [\#2654](https://github.com/tendermint/tendermint/issues/2654) Remove all `node_info.other.*_version` fields in `/status` and
`/net_info`
* Apps
* [abci] \#2298 ResponseQuery.Proof is now a structured merkle.Proof, not just
* [abci] [\#2298](https://github.com/tendermint/tendermint/issues/2298) ResponseQuery.Proof is now a structured merkle.Proof, not just
arbitrary bytes
* [abci] [\#2644](https://github.com/tendermint/tendermint/issues/2644) Add Version to Header and shift all fields by one
* [abci] [\#2662](https://github.com/tendermint/tendermint/issues/2662) Bump the field numbers for some `ResponseInfo` fields to make room for
`AppVersion`
* Go API
* [node] Remove node.RunForever
* [config] \#2232 timeouts as time.Duration, not ints
* [rpc/client] \#2298 `ABCIQueryOptions.Trusted` -> `ABCIQueryOptions.Prove`
* [types] \#2298 Remove `Index` and `Total` fields from `TxProof`.
* [crypto/merkle & lite] \#2298 Various changes to accomodate General Merkle trees
* [crypto/merkle] \#2595 Remove all Hasher objects in favor of byte slices
* [types] \#2598 `VoteTypeXxx` are now
* [config] [\#2232](https://github.com/tendermint/tendermint/issues/2232) timeouts as time.Duration, not ints
* [crypto/merkle & lite] [\#2298](https://github.com/tendermint/tendermint/issues/2298) Various changes to accomodate General Merkle trees
* [crypto/merkle] [\#2595](https://github.com/tendermint/tendermint/issues/2595) Remove all Hasher objects in favor of byte slices
* [crypto/merkle] [\#2635](https://github.com/tendermint/tendermint/issues/2635) merkle.SimpleHashFromTwoHashes is no longer exported
* [node] [\#2479](https://github.com/tendermint/tendermint/issues/2479) Remove node.RunForever
* [rpc/client] [\#2298](https://github.com/tendermint/tendermint/issues/2298) `ABCIQueryOptions.Trusted` -> `ABCIQueryOptions.Prove`
* [types] [\#2298](https://github.com/tendermint/tendermint/issues/2298) Remove `Index` and `Total` fields from `TxProof`.
* [types] [\#2598](https://github.com/tendermint/tendermint/issues/2598) `VoteTypeXxx` are now of type `SignedMsgType byte` and named `XxxType`, eg. `PrevoteType`,
`PrecommitType`.
* Blockchain Protocol
* [types] Update SignBytes for `Vote`/`Proposal`/`Heartbeat`:
* \#2459 Use amino encoding instead of JSON in `SignBytes`.
* \#2598 Reorder fields and use fixed sized encoding.
* \#2598 Change `Type` field fromt `string` to `byte` and use new
* [\#2459](https://github.com/tendermint/tendermint/issues/2459) Use amino encoding instead of JSON in `SignBytes`.
* [\#2598](https://github.com/tendermint/tendermint/issues/2598) Reorder fields and use fixed sized encoding.
* [\#2598](https://github.com/tendermint/tendermint/issues/2598) Change `Type` field fromt `string` to `byte` and use new
`SignedMsgType` to enumerate.
* [types] \#2512 Remove the pubkey field from the validator hash
* [state] \#2587 Require block.Time of the fist block to be genesis time
* [types] [\#2512](https://github.com/tendermint/tendermint/issues/2512) Remove the pubkey field from the validator hash
* [types] [\#2644](https://github.com/tendermint/tendermint/issues/2644) Add Version struct to Header
* [types] [\#2609](https://github.com/tendermint/tendermint/issues/2609) ConsensusParams.Hash() is the hash of the amino encoded
struct instead of the Merkle tree of the fields
* [state] [\#2587](https://github.com/tendermint/tendermint/issues/2587) Require block.Time of the fist block to be genesis time
* [state] [\#2644](https://github.com/tendermint/tendermint/issues/2644) Require block.Version to match state.Version
* [types] [\#2670](https://github.com/tendermint/tendermint/issues/2670) Header.Hash() builds Merkle tree out of fields in the same
order they appear in the header, instead of sorting by field name
* P2P Protocol
* [p2p] [\#2654](https://github.com/tendermint/tendermint/issues/2654) Add `ProtocolVersion` struct with protocol versions to top of
DefaultNodeInfo and require `ProtocolVersion.Block` to match during peer handshake
FEATURES:
- [crypto/merkle] \#2298 General Merkle Proof scheme for chaining various types of Merkle trees together
- [abci] \#2557 Add `Codespace` field to `Response{CheckTx, DeliverTx, Query}`
- [abci] [\#2557](https://github.com/tendermint/tendermint/issues/2557) Add `Codespace` field to `Response{CheckTx, DeliverTx, Query}`
- [abci] [\#2662](https://github.com/tendermint/tendermint/issues/2662) Add `BlockVersion` and `P2PVersion` to `RequestInfo`
- [crypto/merkle] [\#2298](https://github.com/tendermint/tendermint/issues/2298) General Merkle Proof scheme for chaining various types of Merkle trees together
IMPROVEMENTS:
- Additional Metrics
- [consensus] [\#2169](https://github.com/cosmos/cosmos-sdk/issues/2169)
- [p2p] [\#2169](https://github.com/cosmos/cosmos-sdk/issues/2169)
- [config] \#2232 Added ValidateBasic method, which performs basic checks
- [crypto/ed25519] \#2558 Switch to use latest `golang.org/x/crypto` through our fork at
- [config] [\#2232](https://github.com/tendermint/tendermint/issues/2232) Added ValidateBasic method, which performs basic checks
- [crypto/ed25519] [\#2558](https://github.com/tendermint/tendermint/issues/2558) Switch to use latest `golang.org/x/crypto` through our fork at
github.com/tendermint/crypto
- [tools] \#2238 Binary dependencies are now locked to a specific git commit
- [crypto] \#2099 make crypto random use chacha, and have forward secrecy of generated randomness
- [tools] [\#2238](https://github.com/tendermint/tendermint/issues/2238) Binary dependencies are now locked to a specific git commit
BUG FIXES:
- [autofile] \#2428 Group.RotateFile need call Flush() before rename (@goolAdapter)
- [node] \#2434 Make node respond to signal interrupts while sleeping for genesis time
- [consensus] [\#1690](https://github.com/tendermint/tendermint/issues/1690) wait for
timeoutPrecommit before starting next round
- [consensus] [\#1745](https://github.com/tendermint/tendermint/issues/1745) wait for
Proposal or timeoutProposal before entering prevote
- [evidence] \#2515 fix db iter leak (@goolAdapter)
- [common/bit_array] Fixed a bug in the `Or` function
- [common/bit_array] Fixed a bug in the `Sub` function (@james-ray)
- [common] \#2534 Make bit array's PickRandom choose uniformly from true bits
- [consensus] \#1637 Limit the amount of evidence that can be included in a
- [autofile] [\#2428](https://github.com/tendermint/tendermint/issues/2428) Group.RotateFile need call Flush() before rename (@goolAdapter)
- [common] [\#2533](https://github.com/tendermint/tendermint/issues/2533) Fixed a bug in the `BitArray.Or` method
- [common] [\#2506](https://github.com/tendermint/tendermint/issues/2506) Fixed a bug in the `BitArray.Sub` method (@james-ray)
- [common] [\#2534](https://github.com/tendermint/tendermint/issues/2534) Fix `BitArray.PickRandom` to choose uniformly from true bits
- [consensus] [\#1690](https://github.com/tendermint/tendermint/issues/1690) Wait for
timeoutPrecommit before starting next round
- [consensus] [\#1745](https://github.com/tendermint/tendermint/issues/1745) Wait for
Proposal or timeoutProposal before entering prevote
- [consensus] [\#2642](https://github.com/tendermint/tendermint/issues/2642) Only propose ValidBlock, not LockedBlock
- [consensus] [\#2642](https://github.com/tendermint/tendermint/issues/2642) Initialized ValidRound and LockedRound to -1
- [consensus] [\#1637](https://github.com/tendermint/tendermint/issues/1637) Limit the amount of evidence that can be included in a
block
- [p2p] \#2555 fix p2p switch FlushThrottle value (@goolAdapter)
- [libs/event] \#2518 fix event concurrency flaw (@goolAdapter)
- [state] \#2616 Pass nil to NewValidatorSet() when genesis file's Validators field is nil
- [evidence] [\#2515](https://github.com/tendermint/tendermint/issues/2515) Fix db iter leak (@goolAdapter)
- [libs/event] [\#2518](https://github.com/tendermint/tendermint/issues/2518) Fix event concurrency flaw (@goolAdapter)
- [node] [\#2434](https://github.com/tendermint/tendermint/issues/2434) Make node respond to signal interrupts while sleeping for genesis time
- [state] [\#2616](https://github.com/tendermint/tendermint/issues/2616) Pass nil to NewValidatorSet() when genesis file's Validators field is nil
- [p2p] [\#2555](https://github.com/tendermint/tendermint/issues/2555) Fix p2p switch FlushThrottle value (@goolAdapter)
- [p2p] [\#2668](https://github.com/tendermint/tendermint/issues/2668) Reconnect to originally dialed address (not self-reported
address) for persistent peers

72
Gopkg.lock generated
View File

@@ -11,11 +11,11 @@
[[projects]]
branch = "master"
digest = "1:2c00f064ba355903866cbfbf3f7f4c0fe64af6638cc7d1b8bdcf3181bc67f1d8"
digest = "1:c0decf632843204d2b8781de7b26e7038584e2dcccc7e2f401e88ae85b1df2b7"
name = "github.com/btcsuite/btcd"
packages = ["btcec"]
pruneopts = "UT"
revision = "f5e261fc9ec3437697fb31d8b38453c293204b29"
revision = "67e573d211ace594f1366b4ce9d39726c4b19bd0"
[[projects]]
digest = "1:1d8e1cb71c33a9470bbbae09bfec09db43c6bf358dfcae13cd8807c4e2a9a2bf"
@@ -28,12 +28,12 @@
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
[[projects]]
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
name = "github.com/davecgh/go-spew"
packages = ["spew"]
pruneopts = "UT"
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
version = "v1.1.0"
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.1"
[[projects]]
digest = "1:c7644c73a3d23741fdba8a99b1464e021a224b7e205be497271a8003a15ca41b"
@@ -83,12 +83,12 @@
version = "v0.3.0"
[[projects]]
digest = "1:c4a2528ccbcabf90f9f3c464a5fc9e302d592861bbfd0b7135a7de8a943d0406"
digest = "1:586ea76dbd0374d6fb649a91d70d652b7fe0ccffb8910a77468e7702e7901f3d"
name = "github.com/go-stack/stack"
packages = ["."]
pruneopts = "UT"
revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc"
version = "v1.7.0"
revision = "2fee6af1a9795aafbe0253a0cfbdf668e1fb8a9a"
version = "v1.8.0"
[[projects]]
digest = "1:35621fe20f140f05a0c4ef662c26c0ab4ee50bca78aa30fe87d33120bd28165e"
@@ -136,8 +136,7 @@
version = "v1.2.0"
[[projects]]
branch = "master"
digest = "1:12247a2e99a060cc692f6680e5272c8adf0b8f572e6bce0d7095e624c958a240"
digest = "1:ea40c24cdbacd054a6ae9de03e62c5f252479b96c716375aace5c120d68647c8"
name = "github.com/hashicorp/hcl"
packages = [
".",
@@ -151,7 +150,8 @@
"json/token",
]
pruneopts = "UT"
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241"
version = "v1.0.0"
[[projects]]
digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be"
@@ -193,12 +193,12 @@
version = "v1.0.1"
[[projects]]
branch = "master"
digest = "1:5ab79470a1d0fb19b041a624415612f8236b3c06070161a910562f2b2d064355"
digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318"
name = "github.com/mitchellh/mapstructure"
packages = ["."]
pruneopts = "UT"
revision = "f15292f7a699fcc1a38a80977f80a046874ba8ac"
revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe"
version = "v1.1.2"
[[projects]]
digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e"
@@ -244,7 +244,7 @@
[[projects]]
branch = "master"
digest = "1:63b68062b8968092eb86bedc4e68894bd096ea6b24920faca8b9dcf451f54bb5"
digest = "1:db712fde5d12d6cdbdf14b777f0c230f4ff5ab0be8e35b239fc319953ed577a4"
name = "github.com/prometheus/common"
packages = [
"expfmt",
@@ -252,11 +252,11 @@
"model",
]
pruneopts = "UT"
revision = "c7de2306084e37d54b8be01f3541a8464345e9a5"
revision = "7e9e6cabbd393fc208072eedef99188d0ce788b6"
[[projects]]
branch = "master"
digest = "1:8c49953a1414305f2ff5465147ee576dd705487c35b15918fcd4efdc0cb7a290"
digest = "1:ef74914912f99c79434d9c09658274678bc85080ebe3ab32bec3940ebce5e1fc"
name = "github.com/prometheus/procfs"
packages = [
".",
@@ -265,7 +265,7 @@
"xfs",
]
pruneopts = "UT"
revision = "05ee40e3a273f7245e8777337fc7b46e533a9a92"
revision = "185b4288413d2a0dd0806f78c90dde719829e5ae"
[[projects]]
digest = "1:c4556a44e350b50a490544d9b06e9fba9c286c21d6c0e47f54f3a9214597298c"
@@ -275,15 +275,15 @@
revision = "e2704e165165ec55d062f5919b4b29494e9fa790"
[[projects]]
digest = "1:bd1ae00087d17c5a748660b8e89e1043e1e5479d0fea743352cda2f8dd8c4f84"
digest = "1:6a4a11ba764a56d2758899ec6f3848d24698d48442ebce85ee7a3f63284526cd"
name = "github.com/spf13/afero"
packages = [
".",
"mem",
]
pruneopts = "UT"
revision = "787d034dfe70e44075ccc060d346146ef53270ad"
version = "v1.1.1"
revision = "d40851caa0d747393da1ffb28f7f9d8b4eeffebd"
version = "v1.1.2"
[[projects]]
digest = "1:516e71bed754268937f57d4ecb190e01958452336fa73dbac880894164e91c1f"
@@ -302,20 +302,20 @@
version = "v0.0.1"
[[projects]]
branch = "master"
digest = "1:080e5f630945ad754f4b920e60b4d3095ba0237ebf88dc462eb28002932e3805"
digest = "1:68ea4e23713989dc20b1bded5d9da2c5f9be14ff9885beef481848edd18c26cb"
name = "github.com/spf13/jwalterweatherman"
packages = ["."]
pruneopts = "UT"
revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394"
revision = "4a4406e478ca629068e7768fc33f3f044173c0a6"
version = "v1.0.0"
[[projects]]
digest = "1:9424f440bba8f7508b69414634aef3b2b3a877e522d8a4624692412805407bb7"
digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2"
name = "github.com/spf13/pflag"
packages = ["."]
pruneopts = "UT"
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
version = "v1.0.1"
revision = "298182f68c66c05229eb03ac171abe6e309ee79a"
version = "v1.0.3"
[[projects]]
digest = "1:f8e1a678a2571e265f4bf91a3e5e32aa6b1474a55cb0ea849750cc177b664d96"
@@ -338,7 +338,7 @@
[[projects]]
branch = "master"
digest = "1:b3cfb8d82b1601a846417c3f31c03a7961862cb2c98dcf0959c473843e6d9a2b"
digest = "1:59483b8e8183f10ab21a85ba1f4cbb4a2335d48891801f79ed7b9499f44d383c"
name = "github.com/syndtr/goleveldb"
packages = [
"leveldb",
@@ -355,7 +355,7 @@
"leveldb/util",
]
pruneopts = "UT"
revision = "c4c61651e9e37fa117f53c5a906d3b63090d8445"
revision = "6b91fda63f2e36186f1c9d0e48578defb69c5d43"
[[projects]]
digest = "1:605b6546f3f43745695298ec2d342d3e952b6d91cdf9f349bea9315f677d759f"
@@ -365,12 +365,12 @@
revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df"
[[projects]]
digest = "1:e0a2a4be1e20c305badc2b0a7a9ab7fef6da500763bec23ab81df3b5f9eec9ee"
digest = "1:5f52e817b6c9d52ddba70dece0ea31134d82a52c05bce98fbc739ab2a832df28"
name = "github.com/tendermint/go-amino"
packages = ["."]
pruneopts = "UT"
revision = "a8328986c1608950fa5d3d1c0472cccc4f8fc02c"
version = "v0.12.0-rc0"
revision = "cb07448b240918aa8d8df4505153549b86b77134"
version = "v0.13.0"
[[projects]]
digest = "1:72b71e3a29775e5752ed7a8012052a3dee165e27ec18cedddae5288058f09acf"
@@ -415,14 +415,14 @@
[[projects]]
branch = "master"
digest = "1:bb0fe59917bdd5b89f49b9a8b26e5f465e325d9223b3a8e32254314bdf51e0f1"
digest = "1:d1da39c9bac61327dbef1d8ef9f210425e99fd2924b6fb5f0bc587a193353637"
name = "golang.org/x/sys"
packages = [
"cpu",
"unix",
]
pruneopts = "UT"
revision = "3dc4335d56c789b04b0ba99b7a37249d9b614314"
revision = "8a28ead16f52c8aaeffbf79239b251dfdf6c4f96"
[[projects]]
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
@@ -449,11 +449,11 @@
[[projects]]
branch = "master"
digest = "1:077c1c599507b3b3e9156d17d36e1e61928ee9b53a5b420f10f28ebd4a0b275c"
digest = "1:56b0bca90b7e5d1facf5fbdacba23e4e0ce069d25381b8e2f70ef1e7ebfb9c1a"
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
pruneopts = "UT"
revision = "daca94659cb50e9f37c1b834680f2e46358f10b0"
revision = "94acd270e44e65579b9ee3cdab25034d33fed608"
[[projects]]
digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74"

View File

@@ -58,7 +58,7 @@
[[constraint]]
name = "github.com/tendermint/go-amino"
version = "v0.12.0-rc0"
version = "v0.13.0"
[[constraint]]
name = "google.golang.org/grpc"

View File

@@ -3,6 +3,82 @@
This guide provides steps to be followed when you upgrade your applications to
a newer version of Tendermint Core.
## v0.26.0
New 0.26.0 release contains a lot of changes to core data types. It is not
compatible to the old versions and there is no straight forward way to update
old data to be compatible with the new version.
To reset the state do:
```
$ tendermint unsafe_reset_all
```
Here we summarize some other notable changes to be mindful of.
### Config Changes
All timeouts must be changed from integers to strings with their duration, for
instance `flush_throttle_timeout = 100` would be changed to
`flush_throttle_timeout = "100ms"` and `timeout_propose = 3000` would be changed
to `timeout_propose = "3s"`.
### RPC Changes
The default behaviour of `/abci_query` has been changed to not return a proof,
and the name of the parameter that controls this has been changed from `trusted`
to `prove`. To get proofs with your queries, ensure you set `prove=true`.
Various version fields like `amino_version`, `p2p_version`, `consensus_version`,
and `rpc_version` have been removed from the `node_info.other` and are
consolidated under the tendermint semantic version (ie. `node_info.version`) and
the new `block` and `p2p` protocol versions under `node_info.protocol_version`..
### ABCI Changes
Field numbers were bumped in the `Header` and `ResponseInfo` messages to make
room for new `version` fields. It should be straight forward to recompile the
protobuf file for these changes.
#### Proofs
The `ResponseQuery.Proof` field is now structured as a `[]ProofOp` to support
generalized Merkle tree constructions where the leaves of one Merkle tree are
the root of another. If you don't need this functionaluty, and you used to
return `<proof bytes>` here, you should instead return a single `ProofOp` with
just the `Data` field set:
```
[]ProofOp{
ProofOp{
Data: <proof bytes>,
}
}
```
For more information, see:
- [ADR-026](https://github.com/tendermint/tendermint/blob/30519e8361c19f4bf320ef4d26288ebc621ad725/docs/architecture/adr-026-general-merkle-proof.md)
- [Relevant ABCI
documentation](https://github.com/tendermint/tendermint/blob/30519e8361c19f4bf320ef4d26288ebc621ad725/docs/spec/abci/apps.md#query-proofs)
- [Description of
keys](https://github.com/tendermint/tendermint/blob/30519e8361c19f4bf320ef4d26288ebc621ad725/crypto/merkle/proof_key_path.go#L14)
### Go API Changes
#### crypto.merkle
The `merkle.Hasher` interface was removed. Functions which used to take `Hasher`
now simply take `[]byte`. This means that any objects being Merklized should be
serialized before they are passed in.
#### node
The `node.RunForever` function was removed. Signal handling and running forever
should instead be explicitly configured by the caller. See how we do it
[here](https://github.com/tendermint/tendermint/blob/30519e8361c19f4bf320ef4d26288ebc621ad725/cmd/tendermint/commands/run_node.go#L60).
## v0.25.0
This release has minimal impact.

View File

@@ -10,11 +10,14 @@ import (
"github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/version"
)
var (
stateKey = []byte("stateKey")
kvPairPrefixKey = []byte("kvPairKey:")
ProtocolVersion version.Protocol = 0x1
)
type State struct {
@@ -65,7 +68,11 @@ func NewKVStoreApplication() *KVStoreApplication {
}
func (app *KVStoreApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
return types.ResponseInfo{Data: fmt.Sprintf("{\"size\":%v}", app.state.Size)}
return types.ResponseInfo{
Data: fmt.Sprintf("{\"size\":%v}", app.state.Size),
Version: version.ABCIVersion,
AppVersion: ProtocolVersion.Uint64(),
}
}
// tx is either "key=value" or just arbitrary bytes

File diff suppressed because it is too large Load Diff

View File

@@ -49,6 +49,8 @@ message RequestFlush {
message RequestInfo {
string version = 1;
uint64 block_version = 2;
uint64 p2p_version = 3;
}
// nondeterministic
@@ -129,9 +131,12 @@ message ResponseFlush {
message ResponseInfo {
string data = 1;
string version = 2;
int64 last_block_height = 3;
bytes last_block_app_hash = 4;
uint64 app_version = 3;
int64 last_block_height = 4;
bytes last_block_app_hash = 5;
}
// nondeterministic
@@ -231,31 +236,38 @@ message LastCommitInfo {
message Header {
// basic block info
string chain_id = 1 [(gogoproto.customname)="ChainID"];
int64 height = 2;
google.protobuf.Timestamp time = 3 [(gogoproto.nullable)=false, (gogoproto.stdtime)=true];
int64 num_txs = 4;
int64 total_txs = 5;
Version version = 1 [(gogoproto.nullable)=false];
string chain_id = 2 [(gogoproto.customname)="ChainID"];
int64 height = 3;
google.protobuf.Timestamp time = 4 [(gogoproto.nullable)=false, (gogoproto.stdtime)=true];
int64 num_txs = 5;
int64 total_txs = 6;
// prev block info
BlockID last_block_id = 6 [(gogoproto.nullable)=false];
BlockID last_block_id = 7 [(gogoproto.nullable)=false];
// hashes of block data
bytes last_commit_hash = 7; // commit from validators from the last block
bytes data_hash = 8; // transactions
bytes last_commit_hash = 8; // commit from validators from the last block
bytes data_hash = 9; // transactions
// hashes from the app output from the prev block
bytes validators_hash = 9; // validators for the current block
bytes next_validators_hash = 10; // validators for the next block
bytes consensus_hash = 11; // consensus params for current block
bytes app_hash = 12; // state after txs from the previous block
bytes last_results_hash = 13;// root hash of all results from the txs from the previous block
bytes validators_hash = 10; // validators for the current block
bytes next_validators_hash = 11; // validators for the next block
bytes consensus_hash = 12; // consensus params for current block
bytes app_hash = 13; // state after txs from the previous block
bytes last_results_hash = 14;// root hash of all results from the txs from the previous block
// consensus info
bytes evidence_hash = 14; // evidence included in the block
bytes proposer_address = 15; // original proposer of the block
bytes evidence_hash = 15; // evidence included in the block
bytes proposer_address = 16; // original proposer of the block
}
message Version {
uint64 Block = 1;
uint64 App = 2;
}
message BlockID {
bytes hash = 1;
PartSetHeader parts_header = 2 [(gogoproto.nullable)=false];

View File

@@ -1703,6 +1703,62 @@ func TestHeaderMarshalTo(t *testing.T) {
}
}
func TestVersionProto(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedVersion(popr, false)
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &Version{}
if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
littlefuzz := make([]byte, len(dAtA))
copy(littlefuzz, dAtA)
for i := range dAtA {
dAtA[i] = byte(popr.Intn(256))
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)
}
if len(littlefuzz) > 0 {
fuzzamount := 100
for i := 0; i < fuzzamount; i++ {
littlefuzz[popr.Intn(len(littlefuzz))] = byte(popr.Intn(256))
littlefuzz = append(littlefuzz, byte(popr.Intn(256)))
}
// shouldn't panic
_ = github_com_gogo_protobuf_proto.Unmarshal(littlefuzz, msg)
}
}
func TestVersionMarshalTo(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedVersion(popr, false)
size := p.Size()
dAtA := make([]byte, size)
for i := range dAtA {
dAtA[i] = byte(popr.Intn(256))
}
_, err := p.MarshalTo(dAtA)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &Version{}
if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
for i := range dAtA {
dAtA[i] = byte(popr.Intn(256))
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)
}
}
func TestBlockIDProto(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
@@ -2635,6 +2691,24 @@ func TestHeaderJSON(t *testing.T) {
t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p)
}
}
func TestVersionJSON(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedVersion(popr, true)
marshaler := github_com_gogo_protobuf_jsonpb.Marshaler{}
jsondata, err := marshaler.MarshalToString(p)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &Version{}
err = github_com_gogo_protobuf_jsonpb.UnmarshalString(jsondata, msg)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p)
}
}
func TestBlockIDJSON(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
@@ -3601,6 +3675,34 @@ func TestHeaderProtoCompactText(t *testing.T) {
}
}
func TestVersionProtoText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedVersion(popr, true)
dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p)
msg := &Version{}
if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)
}
}
func TestVersionProtoCompactText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedVersion(popr, true)
dAtA := github_com_gogo_protobuf_proto.CompactTextString(p)
msg := &Version{}
if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
if !p.Equal(msg) {
t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p)
}
}
func TestBlockIDProtoText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
@@ -4457,6 +4559,28 @@ func TestHeaderSize(t *testing.T) {
}
}
func TestVersionSize(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedVersion(popr, true)
size2 := github_com_gogo_protobuf_proto.Size(p)
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
size := p.Size()
if len(dAtA) != size {
t.Errorf("seed = %d, size %v != marshalled size %v", seed, size, len(dAtA))
}
if size2 != size {
t.Errorf("seed = %d, size %v != before marshal proto.Size %v", seed, size, size2)
}
size3 := github_com_gogo_protobuf_proto.Size(p)
if size3 != size {
t.Errorf("seed = %d, size %v != after marshal proto.Size %v", seed, size, size3)
}
}
func TestBlockIDSize(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))

View File

@@ -14,14 +14,15 @@ import (
func testNodeInfo(id p2p.ID) p2p.DefaultNodeInfo {
return p2p.DefaultNodeInfo{
ID_: id,
Moniker: "SOMENAME",
Network: "SOMENAME",
ListenAddr: "SOMEADDR",
Version: "SOMEVER",
ProtocolVersion: p2p.ProtocolVersion{1, 2, 3},
ID_: id,
Moniker: "SOMENAME",
Network: "SOMENAME",
ListenAddr: "SOMEADDR",
Version: "SOMEVER",
Other: p2p.DefaultNodeInfoOther{
AminoVersion: "SOMESTRING",
P2PVersion: "OTHERSTRING",
TxIndex: "on",
RPCAddress: "0.0.0.0:26657",
},
}
}

View File

@@ -206,3 +206,4 @@ func (tp *bcrTestPeer) IsPersistent() bool { return true }
func (tp *bcrTestPeer) Get(s string) interface{} { return s }
func (tp *bcrTestPeer) Set(string, interface{}) {}
func (tp *bcrTestPeer) RemoteIP() net.IP { return []byte{127, 0, 0, 1} }
func (tp *bcrTestPeer) OriginalAddr() *p2p.NetAddress { return nil }

View File

@@ -63,7 +63,7 @@ func (bs *BlockStore) LoadBlock(height int64) *types.Block {
part := bs.LoadBlockPart(height, i)
buf = append(buf, part.Bytes...)
}
err := cdc.UnmarshalBinary(buf, block)
err := cdc.UnmarshalBinaryLengthPrefixed(buf, block)
if err != nil {
// NOTE: The existence of meta should imply the existence of the
// block. So, make sure meta is only saved after blocks are saved.

View File

@@ -487,6 +487,14 @@ func ensureVote(voteCh <-chan interface{}, height int64, round int,
}
}
func ensurePrecommit(voteCh <-chan interface{}, height int64, round int) {
ensureVote(voteCh, height, round, types.PrecommitType)
}
func ensurePrevote(voteCh <-chan interface{}, height int64, round int) {
ensureVote(voteCh, height, round, types.PrevoteType)
}
func ensureNewEventOnChannel(ch <-chan interface{}) {
select {
case <-time.After(ensureTimeout):

View File

@@ -11,6 +11,7 @@ import (
"time"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/version"
//auto "github.com/tendermint/tendermint/libs/autofile"
cmn "github.com/tendermint/tendermint/libs/common"
dbm "github.com/tendermint/tendermint/libs/db"
@@ -19,7 +20,6 @@ import (
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tendermint/version"
)
var crc32c = crc32.MakeTable(crc32.Castagnoli)
@@ -227,7 +227,7 @@ func (h *Handshaker) NBlocks() int {
func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error {
// Handshake is done via ABCI Info on the query conn.
res, err := proxyApp.Query().InfoSync(abci.RequestInfo{Version: version.Version})
res, err := proxyApp.Query().InfoSync(proxy.RequestInfo)
if err != nil {
return fmt.Errorf("Error calling Info: %v", err)
}
@@ -238,9 +238,15 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error {
}
appHash := res.LastBlockAppHash
h.logger.Info("ABCI Handshake", "appHeight", blockHeight, "appHash", fmt.Sprintf("%X", appHash))
h.logger.Info("ABCI Handshake App Info",
"height", blockHeight,
"hash", fmt.Sprintf("%X", appHash),
"software-version", res.Version,
"protocol-version", res.AppVersion,
)
// TODO: check app version.
// Set AppVersion on the state.
h.initialState.Version.Consensus.App = version.Protocol(res.AppVersion)
// Replay blocks up to the latest in the blockstore.
_, err = h.ReplayBlocks(h.initialState, appHash, blockHeight, proxyApp)

View File

@@ -20,6 +20,7 @@ import (
crypto "github.com/tendermint/tendermint/crypto"
auto "github.com/tendermint/tendermint/libs/autofile"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/version"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/libs/log"
@@ -337,7 +338,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
t.Fatalf(err.Error())
}
stateDB, state, store := stateAndStore(config, privVal.GetPubKey())
stateDB, state, store := stateAndStore(config, privVal.GetPubKey(), kvstore.ProtocolVersion)
store.chain = chain
store.commits = commits
@@ -352,7 +353,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
// run nBlocks against a new client to build up the app state.
// use a throwaway tendermint state
proxyApp := proxy.NewAppConns(clientCreator2)
stateDB, state, _ := stateAndStore(config, privVal.GetPubKey())
stateDB, state, _ := stateAndStore(config, privVal.GetPubKey(), kvstore.ProtocolVersion)
buildAppStateFromChain(proxyApp, stateDB, state, chain, nBlocks, mode)
}
@@ -442,7 +443,7 @@ func buildAppStateFromChain(proxyApp proxy.AppConns, stateDB dbm.DB,
func buildTMStateFromChain(config *cfg.Config, stateDB dbm.DB, state sm.State, chain []*types.Block, mode uint) sm.State {
// run the whole chain against this client to build up the tendermint state
clientCreator := proxy.NewLocalClientCreator(kvstore.NewPersistentKVStoreApplication(path.Join(config.DBDir(), "1")))
proxyApp := proxy.NewAppConns(clientCreator) // sm.NewHandshaker(config, state, store, ReplayLastBlock))
proxyApp := proxy.NewAppConns(clientCreator)
if err := proxyApp.Start(); err != nil {
panic(err)
}
@@ -519,7 +520,7 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
// if its not the first one, we have a full block
if thisBlockParts != nil {
var block = new(types.Block)
_, err = cdc.UnmarshalBinaryReader(thisBlockParts.GetReader(), block, 0)
_, err = cdc.UnmarshalBinaryLengthPrefixedReader(thisBlockParts.GetReader(), block, 0)
if err != nil {
panic(err)
}
@@ -552,7 +553,7 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
}
// grab the last block too
var block = new(types.Block)
_, err = cdc.UnmarshalBinaryReader(thisBlockParts.GetReader(), block, 0)
_, err = cdc.UnmarshalBinaryLengthPrefixedReader(thisBlockParts.GetReader(), block, 0)
if err != nil {
panic(err)
}
@@ -588,9 +589,10 @@ func readPieceFromWAL(msg *TimedWALMessage) interface{} {
}
// fresh state and mock store
func stateAndStore(config *cfg.Config, pubKey crypto.PubKey) (dbm.DB, sm.State, *mockBlockStore) {
func stateAndStore(config *cfg.Config, pubKey crypto.PubKey, appVersion version.Protocol) (dbm.DB, sm.State, *mockBlockStore) {
stateDB := dbm.NewMemDB()
state, _ := sm.MakeGenesisStateFromFile(config.GenesisFile())
state.Version.Consensus.App = appVersion
store := NewMockBlockStore(config, state.ConsensusParams)
return stateDB, state, store
}
@@ -639,7 +641,7 @@ func TestInitChainUpdateValidators(t *testing.T) {
config := ResetConfig("proxy_test_")
privVal := privval.LoadFilePV(config.PrivValidatorFile())
stateDB, state, store := stateAndStore(config, privVal.GetPubKey())
stateDB, state, store := stateAndStore(config, privVal.GetPubKey(), 0x0)
oldValAddr := state.Validators.Validators[0].Address

View File

@@ -532,10 +532,10 @@ func (cs *ConsensusState) updateToState(state sm.State) {
cs.Proposal = nil
cs.ProposalBlock = nil
cs.ProposalBlockParts = nil
cs.LockedRound = 0
cs.LockedRound = -1
cs.LockedBlock = nil
cs.LockedBlockParts = nil
cs.ValidRound = 0
cs.ValidRound = -1
cs.ValidBlock = nil
cs.ValidBlockParts = nil
cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators)
@@ -889,10 +889,7 @@ func (cs *ConsensusState) defaultDecideProposal(height int64, round int) {
var blockParts *types.PartSet
// Decide on block
if cs.LockedBlock != nil {
// If we're locked onto a block, just choose that.
block, blockParts = cs.LockedBlock, cs.LockedBlockParts
} else if cs.ValidBlock != nil {
if cs.ValidBlock != nil {
// If there is valid block, choose that.
block, blockParts = cs.ValidBlock, cs.ValidBlockParts
} else {
@@ -983,7 +980,6 @@ func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts
// Enter: `timeoutPropose` after entering Propose.
// Enter: proposal block and POL is ready.
// Enter: any +2/3 prevotes for future round.
// Prevote for LockedBlock if we're locked, or ProposalBlock if valid.
// Otherwise vote nil.
func (cs *ConsensusState) enterPrevote(height int64, round int) {
@@ -1072,8 +1068,8 @@ func (cs *ConsensusState) enterPrevoteWait(height int64, round int) {
}
// Enter: `timeoutPrevote` after any +2/3 prevotes.
// Enter: `timeoutPrecommit` after any +2/3 precommits.
// Enter: +2/3 precomits for block or nil.
// Enter: any +2/3 precommits for next round.
// Lock & precommit the ProposalBlock if we have enough prevotes for it (a POL in this round)
// else, unlock an existing lock and precommit nil if +2/3 of prevotes were nil,
// else, precommit nil otherwise.
@@ -1122,7 +1118,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
logger.Info("enterPrecommit: +2/3 prevoted for nil.")
} else {
logger.Info("enterPrecommit: +2/3 prevoted for nil. Unlocking")
cs.LockedRound = 0
cs.LockedRound = -1
cs.LockedBlock = nil
cs.LockedBlockParts = nil
cs.eventBus.PublishEventUnlock(cs.RoundStateEvent())
@@ -1161,7 +1157,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
// Fetch that block, unlock, and precommit nil.
// The +2/3 prevotes for this round is the POL for our unlock.
// TODO: In the future save the POL prevotes for justification.
cs.LockedRound = 0
cs.LockedRound = -1
cs.LockedBlock = nil
cs.LockedBlockParts = nil
if !cs.ProposalBlockParts.HasHeader(blockID.PartsHeader) {
@@ -1472,7 +1468,7 @@ func (cs *ConsensusState) addProposalBlockPart(msg *BlockPartMessage, peerID p2p
}
if added && cs.ProposalBlockParts.IsComplete() {
// Added and completed!
_, err = cdc.UnmarshalBinaryReader(
_, err = cdc.UnmarshalBinaryLengthPrefixedReader(
cs.ProposalBlockParts.GetReader(),
&cs.ProposalBlock,
int64(cs.state.ConsensusParams.BlockSize.MaxBytes),
@@ -1612,7 +1608,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
!cs.LockedBlock.HashesTo(blockID.Hash) {
cs.Logger.Info("Unlocking because of POL.", "lockedRound", cs.LockedRound, "POLRound", vote.Round)
cs.LockedRound = 0
cs.LockedRound = -1
cs.LockedBlock = nil
cs.LockedBlockParts = nil
cs.eventBus.PublishEventUnlock(cs.RoundStateEvent())

View File

@@ -214,16 +214,16 @@ func TestStateBadProposal(t *testing.T) {
ensureNewProposal(proposalCh, height, round)
// wait for prevote
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
validatePrevote(t, cs1, round, vss[0], nil)
// add bad prevote from vs2 and wait for it
signAddVotes(cs1, types.PrevoteType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2)
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
// wait for precommit
ensureVote(voteCh, height, round, types.PrecommitType)
validatePrecommit(t, cs1, round, 0, vss[0], nil, nil)
ensurePrecommit(voteCh, height, round)
validatePrecommit(t, cs1, round, -1, vss[0], nil, nil)
signAddVotes(cs1, types.PrecommitType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2)
}
@@ -255,10 +255,10 @@ func TestStateFullRound1(t *testing.T) {
ensureNewProposal(propCh, height, round)
propBlockHash := cs.GetRoundState().ProposalBlock.Hash()
ensureVote(voteCh, height, round, types.PrevoteType) // wait for prevote
ensurePrevote(voteCh, height, round) // wait for prevote
validatePrevote(t, cs, round, vss[0], propBlockHash)
ensureVote(voteCh, height, round, types.PrecommitType) // wait for precommit
ensurePrecommit(voteCh, height, round) // wait for precommit
// we're going to roll right into new height
ensureNewRound(newRoundCh, height+1, 0)
@@ -276,11 +276,11 @@ func TestStateFullRoundNil(t *testing.T) {
cs.enterPrevote(height, round)
cs.startRoutines(4)
ensureVote(voteCh, height, round, types.PrevoteType) // prevote
ensureVote(voteCh, height, round, types.PrecommitType) // precommit
ensurePrevote(voteCh, height, round) // prevote
ensurePrecommit(voteCh, height, round) // precommit
// should prevote and precommit nil
validatePrevoteAndPrecommit(t, cs, round, 0, vss[0], nil, nil)
validatePrevoteAndPrecommit(t, cs, round, -1, vss[0], nil, nil)
}
// run through propose, prevote, precommit commit with two validators
@@ -296,7 +296,7 @@ func TestStateFullRound2(t *testing.T) {
// start round and wait for propose and prevote
startTestRound(cs1, height, round)
ensureVote(voteCh, height, round, types.PrevoteType) // prevote
ensurePrevote(voteCh, height, round) // prevote
// we should be stuck in limbo waiting for more prevotes
rs := cs1.GetRoundState()
@@ -304,9 +304,9 @@ func TestStateFullRound2(t *testing.T) {
// prevote arrives from vs2:
signAddVotes(cs1, types.PrevoteType, propBlockHash, propPartsHeader, vs2)
ensureVote(voteCh, height, round, types.PrevoteType) // prevote
ensurePrevote(voteCh, height, round) // prevote
ensureVote(voteCh, height, round, types.PrecommitType) //precommit
ensurePrecommit(voteCh, height, round) //precommit
// the proposed block should now be locked and our precommit added
validatePrecommit(t, cs1, 0, 0, vss[0], propBlockHash, propBlockHash)
@@ -314,7 +314,7 @@ func TestStateFullRound2(t *testing.T) {
// precommit arrives from vs2:
signAddVotes(cs1, types.PrecommitType, propBlockHash, propPartsHeader, vs2)
ensureVote(voteCh, height, round, types.PrecommitType)
ensurePrecommit(voteCh, height, round)
// wait to finish commit, propose in next height
ensureNewBlock(newBlockCh, height)
@@ -353,14 +353,14 @@ func TestStateLockNoPOL(t *testing.T) {
theBlockHash := roundState.ProposalBlock.Hash()
thePartSetHeader := roundState.ProposalBlockParts.Header()
ensureVote(voteCh, height, round, types.PrevoteType) // prevote
ensurePrevote(voteCh, height, round) // prevote
// we should now be stuck in limbo forever, waiting for more prevotes
// prevote arrives from vs2:
signAddVotes(cs1, types.PrevoteType, theBlockHash, thePartSetHeader, vs2)
ensureVote(voteCh, height, round, types.PrevoteType) // prevote
ensurePrevote(voteCh, height, round) // prevote
ensureVote(voteCh, height, round, types.PrecommitType) // precommit
ensurePrecommit(voteCh, height, round) // precommit
// the proposed block should now be locked and our precommit added
validatePrecommit(t, cs1, round, round, vss[0], theBlockHash, theBlockHash)
@@ -370,7 +370,7 @@ func TestStateLockNoPOL(t *testing.T) {
copy(hash, theBlockHash)
hash[0] = byte((hash[0] + 1) % 255)
signAddVotes(cs1, types.PrecommitType, hash, thePartSetHeader, vs2)
ensureVote(voteCh, height, round, types.PrecommitType) // precommit
ensurePrecommit(voteCh, height, round) // precommit
// (note we're entering precommit for a second time this round)
// but with invalid args. then we enterPrecommitWait, and the timeout to new round
@@ -397,26 +397,26 @@ func TestStateLockNoPOL(t *testing.T) {
}
// wait to finish prevote
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
// we should have prevoted our locked block
validatePrevote(t, cs1, round, vss[0], rs.LockedBlock.Hash())
// add a conflicting prevote from the other validator
signAddVotes(cs1, types.PrevoteType, hash, rs.LockedBlock.MakePartSet(partSize).Header(), vs2)
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
// now we're going to enter prevote again, but with invalid args
// and then prevote wait, which should timeout. then wait for precommit
ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrevote.Nanoseconds())
ensureVote(voteCh, height, round, types.PrecommitType) // precommit
ensurePrecommit(voteCh, height, round) // precommit
// the proposed block should still be locked and our precommit added
// we should precommit nil and be locked on the proposal
validatePrecommit(t, cs1, round, 0, vss[0], nil, theBlockHash)
// add conflicting precommit from vs2
signAddVotes(cs1, types.PrecommitType, hash, rs.LockedBlock.MakePartSet(partSize).Header(), vs2)
ensureVote(voteCh, height, round, types.PrecommitType)
ensurePrecommit(voteCh, height, round)
// (note we're entering precommit for a second time this round, but with invalid args
// then we enterPrecommitWait and timeout into NewRound
@@ -439,19 +439,19 @@ func TestStateLockNoPOL(t *testing.T) {
panic(fmt.Sprintf("Expected proposal block to be locked block. Got %v, Expected %v", rs.ProposalBlock, rs.LockedBlock))
}
ensureVote(voteCh, height, round, types.PrevoteType) // prevote
ensurePrevote(voteCh, height, round) // prevote
validatePrevote(t, cs1, round, vss[0], rs.LockedBlock.Hash())
signAddVotes(cs1, types.PrevoteType, hash, rs.ProposalBlock.MakePartSet(partSize).Header(), vs2)
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrevote.Nanoseconds())
ensureVote(voteCh, height, round, types.PrecommitType) // precommit
ensurePrecommit(voteCh, height, round) // precommit
validatePrecommit(t, cs1, round, 0, vss[0], nil, theBlockHash) // precommit nil but be locked on proposal
signAddVotes(cs1, types.PrecommitType, hash, rs.ProposalBlock.MakePartSet(partSize).Header(), vs2) // NOTE: conflicting precommits at same height
ensureVote(voteCh, height, round, types.PrecommitType)
ensurePrecommit(voteCh, height, round)
ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrecommit.Nanoseconds())
@@ -478,20 +478,20 @@ func TestStateLockNoPOL(t *testing.T) {
}
ensureNewProposal(proposalCh, height, round)
ensureVote(voteCh, height, round, types.PrevoteType) // prevote
ensurePrevote(voteCh, height, round) // prevote
// prevote for locked block (not proposal)
validatePrevote(t, cs1, 3, vss[0], cs1.LockedBlock.Hash())
// prevote for proposed block
signAddVotes(cs1, types.PrevoteType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2)
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrevote.Nanoseconds())
ensureVote(voteCh, height, round, types.PrecommitType)
ensurePrecommit(voteCh, height, round)
validatePrecommit(t, cs1, round, 0, vss[0], nil, theBlockHash) // precommit nil but locked on proposal
signAddVotes(cs1, types.PrecommitType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2) // NOTE: conflicting precommits at same height
ensureVote(voteCh, height, round, types.PrecommitType)
ensurePrecommit(voteCh, height, round)
}
// 4 vals, one precommits, other 3 polka at next round, so we unlock and precomit the polka
@@ -525,11 +525,11 @@ func TestStateLockPOLRelock(t *testing.T) {
theBlockHash := rs.ProposalBlock.Hash()
theBlockParts := rs.ProposalBlockParts.Header()
ensureVote(voteCh, height, round, types.PrevoteType) // prevote
ensurePrevote(voteCh, height, round) // prevote
signAddVotes(cs1, types.PrevoteType, theBlockHash, theBlockParts, vs2, vs3, vs4)
ensureVote(voteCh, height, round, types.PrecommitType) // our precommit
ensurePrecommit(voteCh, height, round) // our precommit
// the proposed block should now be locked and our precommit added
validatePrecommit(t, cs1, round, round, vss[0], theBlockHash, theBlockHash)
@@ -567,13 +567,13 @@ func TestStateLockPOLRelock(t *testing.T) {
ensureNewProposal(proposalCh, height, round)
// go to prevote, prevote for locked block (not proposal), move on
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
validatePrevote(t, cs1, round, vss[0], theBlockHash)
// now lets add prevotes from everyone else for the new block
signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4)
ensureVote(voteCh, height, round, types.PrecommitType)
ensurePrecommit(voteCh, height, round)
// we should have unlocked and locked on the new block
validatePrecommit(t, cs1, round, round, vss[0], propBlockHash, propBlockHash)
@@ -614,12 +614,12 @@ func TestStateLockPOLUnlock(t *testing.T) {
theBlockHash := rs.ProposalBlock.Hash()
theBlockParts := rs.ProposalBlockParts.Header()
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
validatePrevote(t, cs1, round, vss[0], theBlockHash)
signAddVotes(cs1, types.PrevoteType, theBlockHash, theBlockParts, vs2, vs3, vs4)
ensureVote(voteCh, height, round, types.PrecommitType)
ensurePrecommit(voteCh, height, round)
// the proposed block should now be locked and our precommit added
validatePrecommit(t, cs1, round, round, vss[0], theBlockHash, theBlockHash)
@@ -656,18 +656,18 @@ func TestStateLockPOLUnlock(t *testing.T) {
ensureNewProposal(proposalCh, height, round)
// go to prevote, prevote for locked block (not proposal)
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
validatePrevote(t, cs1, round, vss[0], lockedBlockHash)
// now lets add prevotes from everyone else for nil (a polka!)
signAddVotes(cs1, types.PrevoteType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
// the polka makes us unlock and precommit nil
ensureNewUnlock(unlockCh, height, round)
ensureVote(voteCh, height, round, types.PrecommitType)
ensurePrecommit(voteCh, height, round)
// we should have unlocked and committed nil
// NOTE: since we don't relock on nil, the lock round is 0
validatePrecommit(t, cs1, round, 0, vss[0], nil, nil)
// NOTE: since we don't relock on nil, the lock round is -1
validatePrecommit(t, cs1, round, -1, vss[0], nil, nil)
signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3)
ensureNewRound(newRoundCh, height, round+1)
@@ -698,7 +698,7 @@ func TestStateLockPOLSafety1(t *testing.T) {
rs := cs1.GetRoundState()
propBlock := rs.ProposalBlock
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
validatePrevote(t, cs1, round, vss[0], propBlock.Hash())
// the others sign a polka but we don't see it
@@ -710,7 +710,7 @@ func TestStateLockPOLSafety1(t *testing.T) {
signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
// cs1 precommit nil
ensureVote(voteCh, height, round, types.PrecommitType)
ensurePrecommit(voteCh, height, round)
ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrecommit.Nanoseconds())
t.Log("### ONTO ROUND 1")
@@ -743,13 +743,13 @@ func TestStateLockPOLSafety1(t *testing.T) {
t.Logf("new prop hash %v", fmt.Sprintf("%X", propBlockHash))
// go to prevote, prevote for proposal block
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
validatePrevote(t, cs1, round, vss[0], propBlockHash)
// now we see the others prevote for it, so we should lock on it
signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4)
ensureVote(voteCh, height, round, types.PrecommitType)
ensurePrecommit(voteCh, height, round)
// we should have precommitted
validatePrecommit(t, cs1, round, round, vss[0], propBlockHash, propBlockHash)
@@ -771,7 +771,7 @@ func TestStateLockPOLSafety1(t *testing.T) {
ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.TimeoutPropose.Nanoseconds())
// finish prevote
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
// we should prevote what we're locked on
validatePrevote(t, cs1, round, vss[0], propBlockHash)
@@ -834,12 +834,12 @@ func TestStateLockPOLSafety2(t *testing.T) {
}
ensureNewProposal(proposalCh, height, round)
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
validatePrevote(t, cs1, round, vss[0], propBlockHash1)
signAddVotes(cs1, types.PrevoteType, propBlockHash1, propBlockParts1.Header(), vs2, vs3, vs4)
ensureVote(voteCh, height, round, types.PrecommitType)
ensurePrecommit(voteCh, height, round)
// the proposed block should now be locked and our precommit added
validatePrecommit(t, cs1, round, round, vss[0], propBlockHash1, propBlockHash1)
@@ -873,11 +873,99 @@ func TestStateLockPOLSafety2(t *testing.T) {
ensureNewProposal(proposalCh, height, round)
ensureNoNewUnlock(unlockCh)
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
validatePrevote(t, cs1, round, vss[0], propBlockHash1)
}
// 4 vals.
// polka P0 at R0 for B0. We lock B0 on P0 at R0. P0 unlocks value at R1.
// What we want:
// P0 proposes B0 at R3.
func TestProposeValidBlock(t *testing.T) {
cs1, vss := randConsensusState(4)
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
height, round := cs1.Height, cs1.Round
partSize := types.BlockPartSizeBytes
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
unlockCh := subscribe(cs1.eventBus, types.EventQueryUnlock)
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
// start round and wait for propose and prevote
startTestRound(cs1, cs1.Height, round)
ensureNewRound(newRoundCh, height, round)
ensureNewProposal(proposalCh, height, round)
rs := cs1.GetRoundState()
propBlock := rs.ProposalBlock
propBlockHash := propBlock.Hash()
ensurePrevote(voteCh, height, round)
validatePrevote(t, cs1, round, vss[0], propBlockHash)
// the others sign a polka but we don't see it
signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlock.MakePartSet(partSize).Header(), vs2, vs3, vs4)
ensurePrecommit(voteCh, height, round)
// we should have precommitted
validatePrecommit(t, cs1, round, round, vss[0], propBlockHash, propBlockHash)
signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrecommit.Nanoseconds())
incrementRound(vs2, vs3, vs4)
round = round + 1 // moving to the next round
ensureNewRound(newRoundCh, height, round)
t.Log("### ONTO ROUND 2")
// timeout of propose
ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.TimeoutPropose.Nanoseconds())
ensurePrevote(voteCh, height, round)
validatePrevote(t, cs1, round, vss[0], propBlockHash)
signAddVotes(cs1, types.PrevoteType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
ensureNewUnlock(unlockCh, height, round)
ensurePrecommit(voteCh, height, round)
// we should have precommitted
validatePrecommit(t, cs1, round, -1, vss[0], nil, nil)
incrementRound(vs2, vs3, vs4)
incrementRound(vs2, vs3, vs4)
signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
round = round + 2 // moving to the next round
ensureNewRound(newRoundCh, height, round)
t.Log("### ONTO ROUND 3")
ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrecommit.Nanoseconds())
round = round + 1 // moving to the next round
ensureNewRound(newRoundCh, height, round)
t.Log("### ONTO ROUND 4")
ensureNewProposal(proposalCh, height, round)
rs = cs1.GetRoundState()
assert.True(t, bytes.Equal(rs.ProposalBlock.Hash(), propBlockHash))
assert.True(t, bytes.Equal(rs.ProposalBlock.Hash(), rs.ValidBlock.Hash()))
}
// 4 vals, 3 Nil Precommits at P0
// What we want:
// P0 waits for timeoutPrecommit before starting next round
@@ -915,7 +1003,7 @@ func TestWaitingTimeoutProposeOnNewRound(t *testing.T) {
startTestRound(cs1, height, round)
ensureNewRound(newRoundCh, height, round)
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
incrementRound(vss[1:]...)
signAddVotes(cs1, types.PrevoteType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
@@ -928,7 +1016,7 @@ func TestWaitingTimeoutProposeOnNewRound(t *testing.T) {
ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPropose.Nanoseconds())
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
validatePrevote(t, cs1, round, vss[0], nil)
}
@@ -948,7 +1036,7 @@ func TestRoundSkipOnNilPolkaFromHigherRound(t *testing.T) {
startTestRound(cs1, height, round)
ensureNewRound(newRoundCh, height, round)
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
incrementRound(vss[1:]...)
signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
@@ -956,8 +1044,8 @@ func TestRoundSkipOnNilPolkaFromHigherRound(t *testing.T) {
round = round + 1 // moving to the next round
ensureNewRound(newRoundCh, height, round)
ensureVote(voteCh, height, round, types.PrecommitType)
validatePrecommit(t, cs1, round, 0, vss[0], nil, nil)
ensurePrecommit(voteCh, height, round)
validatePrecommit(t, cs1, round, -1, vss[0], nil, nil)
ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrecommit.Nanoseconds())
@@ -986,7 +1074,7 @@ func TestWaitTimeoutProposeOnNilPolkaForTheCurrentRound(t *testing.T) {
ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.TimeoutPropose.Nanoseconds())
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
validatePrevote(t, cs1, round, vss[0], nil)
}
@@ -1096,11 +1184,11 @@ func TestStateHalt1(t *testing.T) {
propBlock := rs.ProposalBlock
propBlockParts := propBlock.MakePartSet(partSize)
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
signAddVotes(cs1, types.PrevoteType, propBlock.Hash(), propBlockParts.Header(), vs2, vs3, vs4)
ensureVote(voteCh, height, round, types.PrecommitType)
ensurePrecommit(voteCh, height, round)
// the proposed block should now be locked and our precommit added
validatePrecommit(t, cs1, round, round, vss[0], propBlock.Hash(), propBlock.Hash())
@@ -1127,7 +1215,7 @@ func TestStateHalt1(t *testing.T) {
*/
// go to prevote, prevote for locked block
ensureVote(voteCh, height, round, types.PrevoteType)
ensurePrevote(voteCh, height, round)
validatePrevote(t, cs1, round, vss[0], rs.LockedBlock.Hash())
// now we receive the precommit from the previous round

View File

@@ -1,11 +0,0 @@
package consensus
import "fmt"
// kind of arbitrary
var Spec = "1" // async
var Major = "0" //
var Minor = "2" // replay refactor
var Revision = "2" // validation -> commit
var Version = fmt.Sprintf("v%s/%s.%s.%s", Spec, Major, Minor, Revision)

View File

@@ -38,7 +38,8 @@ func WALGenerateNBlocks(wr io.Writer, numBlocks int) (err error) {
/////////////////////////////////////////////////////////////////////////////
// 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.
privValidatorFile := config.PrivValidatorFile()
privValidator := privval.LoadOrGenFilePV(privValidatorFile)
genDoc, err := types.GenesisDocFromFile(config.GenesisFile())
@@ -51,6 +52,7 @@ func WALGenerateNBlocks(wr io.Writer, numBlocks int) (err error) {
if err != nil {
return errors.Wrap(err, "failed to make genesis state")
}
state.Version.Consensus.App = kvstore.ProtocolVersion
blockStore := bc.NewBlockStore(blockStoreDB)
proxyApp := proxy.NewAppConns(proxy.NewLocalClientCreator(app))
proxyApp.SetLogger(logger.With("module", "proxy"))

View File

@@ -42,7 +42,7 @@ func SimpleValueOpDecoder(pop ProofOp) (ProofOperator, error) {
return nil, cmn.NewError("unexpected ProofOp.Type; got %v, want %v", pop.Type, ProofOpSimpleValue)
}
var op SimpleValueOp // a bit strange as we'll discard this, but it works.
err := cdc.UnmarshalBinary(pop.Data, &op)
err := cdc.UnmarshalBinaryLengthPrefixed(pop.Data, &op)
if err != nil {
return nil, cmn.ErrorWrap(err, "decoding ProofOp.Data into SimpleValueOp")
}
@@ -50,7 +50,7 @@ func SimpleValueOpDecoder(pop ProofOp) (ProofOperator, error) {
}
func (op SimpleValueOp) ProofOp() ProofOp {
bz := cdc.MustMarshalBinary(op)
bz := cdc.MustMarshalBinaryLengthPrefixed(op)
return ProofOp{
Type: ProofOpSimpleValue,
Key: op.key,

View File

@@ -134,13 +134,13 @@ func computeHashFromAunts(index int, total int, leafHash []byte, innerHashes [][
if leftHash == nil {
return nil
}
return SimpleHashFromTwoHashes(leftHash, innerHashes[len(innerHashes)-1])
return simpleHashFromTwoHashes(leftHash, innerHashes[len(innerHashes)-1])
}
rightHash := computeHashFromAunts(index-numLeft, total-numLeft, leafHash, innerHashes[:len(innerHashes)-1])
if rightHash == nil {
return nil
}
return SimpleHashFromTwoHashes(innerHashes[len(innerHashes)-1], rightHash)
return simpleHashFromTwoHashes(innerHashes[len(innerHashes)-1], rightHash)
}
}
@@ -187,7 +187,7 @@ func trailsFromByteSlices(items [][]byte) (trails []*SimpleProofNode, root *Simp
default:
lefts, leftRoot := trailsFromByteSlices(items[:(len(items)+1)/2])
rights, rightRoot := trailsFromByteSlices(items[(len(items)+1)/2:])
rootHash := SimpleHashFromTwoHashes(leftRoot.Hash, rightRoot.Hash)
rootHash := simpleHashFromTwoHashes(leftRoot.Hash, rightRoot.Hash)
root := &SimpleProofNode{rootHash, nil, nil, nil}
leftRoot.Parent = root
leftRoot.Right = rightRoot

View File

@@ -4,8 +4,8 @@ import (
"github.com/tendermint/tendermint/crypto/tmhash"
)
// SimpleHashFromTwoHashes is the basic operation of the Merkle tree: Hash(left | right).
func SimpleHashFromTwoHashes(left, right []byte) []byte {
// 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 {
@@ -29,7 +29,7 @@ func SimpleHashFromByteSlices(items [][]byte) []byte {
default:
left := SimpleHashFromByteSlices(items[:(len(items)+1)/2])
right := SimpleHashFromByteSlices(items[(len(items)+1)/2:])
return SimpleHashFromTwoHashes(left, right)
return simpleHashFromTwoHashes(left, right)
}
}

View File

@@ -9,10 +9,11 @@ import (
"sync"
"golang.org/x/crypto/chacha20poly1305"
. "github.com/tendermint/tendermint/libs/common"
)
// NOTE: This is ignored for now until we have time
// to properly review the MixEntropy function - https://github.com/tendermint/tendermint/issues/2099.
//
// The randomness here is derived from xoring a chacha20 keystream with
// output from crypto/rand's OS Entropy Reader. (Due to fears of the OS'
// entropy being backdoored)
@@ -23,9 +24,13 @@ var gRandInfo *randInfo
func init() {
gRandInfo = &randInfo{}
gRandInfo.MixEntropy(randBytes(32)) // Init
// TODO: uncomment after reviewing MixEntropy -
// https://github.com/tendermint/tendermint/issues/2099
// gRandInfo.MixEntropy(randBytes(32)) // Init
}
// WARNING: This function needs review - https://github.com/tendermint/tendermint/issues/2099.
// Mix additional bytes of randomness, e.g. from hardware, user-input, etc.
// It is OK to call it multiple times. It does not diminish security.
func MixEntropy(seedBytes []byte) {
@@ -37,20 +42,28 @@ func randBytes(numBytes int) []byte {
b := make([]byte, numBytes)
_, err := crand.Read(b)
if err != nil {
PanicCrisis(err)
panic(err)
}
return b
}
// This only uses the OS's randomness
func CRandBytes(numBytes int) []byte {
return randBytes(numBytes)
}
/* TODO: uncomment after reviewing MixEntropy - https://github.com/tendermint/tendermint/issues/2099
// This uses the OS and the Seed(s).
func CRandBytes(numBytes int) []byte {
b := make([]byte, numBytes)
_, err := gRandInfo.Read(b)
if err != nil {
PanicCrisis(err)
}
return b
return randBytes(numBytes)
b := make([]byte, numBytes)
_, err := gRandInfo.Read(b)
if err != nil {
panic(err)
}
return b
}
*/
// CRandHex returns a hex encoded string that's floor(numDigits/2) * 2 long.
//
@@ -60,10 +73,17 @@ func CRandHex(numDigits int) string {
return hex.EncodeToString(CRandBytes(numDigits / 2))
}
// Returns a crand.Reader.
func CReader() io.Reader {
return crand.Reader
}
/* TODO: uncomment after reviewing MixEntropy - https://github.com/tendermint/tendermint/issues/2099
// Returns a crand.Reader mixed with user-supplied entropy
func CReader() io.Reader {
return gRandInfo
}
*/
//--------------------------------------------------------------------------------
@@ -75,7 +95,7 @@ type randInfo struct {
}
// You can call this as many times as you'd like.
// XXX TODO review
// XXX/TODO: review - https://github.com/tendermint/tendermint/issues/2099
func (ri *randInfo) MixEntropy(seedBytes []byte) {
ri.mtx.Lock()
defer ri.mtx.Unlock()

View File

@@ -1,6 +1,7 @@
module.exports = {
title: "Tendermint Core",
description: "Documentation for Tendermint Core",
title: "Tendermint Documentation",
description: "Documentation for Tendermint Core.",
ga: "UA-51029217-1",
dest: "./dist/docs",
base: "/docs/",
markdown: {

View File

@@ -163,6 +163,12 @@
"language": "Python",
"author": "Dave Bryson"
},
{
"name": "tm-abci",
"url": "https://github.com/SoftblocksCo/tm-abci",
"language": "Python",
"author": "Softblocks"
},
{
"name": "Spearmint",
"url": "https://github.com/dennismckinnon/spearmint",

View File

@@ -96,7 +96,7 @@ Each component of the software is independently versioned in a modular way and i
## Proposal
Each of BlockVersion, AppVersion, P2PVersion, is a monotonically increasing int64.
Each of BlockVersion, AppVersion, P2PVersion, is a monotonically increasing uint64.
To use these versions, we need to update the block Header, the p2p NodeInfo, and the ABCI.
@@ -106,8 +106,8 @@ Block Header should include a `Version` struct as its first field like:
```
type Version struct {
Block int64
App int64
Block uint64
App uint64
}
```
@@ -130,9 +130,9 @@ NodeInfo should include a Version struct as its first field like:
```
type Version struct {
P2P int64
Block int64
App int64
P2P uint64
Block uint64
App uint64
Other []string
}
@@ -168,9 +168,9 @@ RequestInfo should add support for protocol versions like:
```
message RequestInfo {
string software_version
int64 block_version
int64 p2p_version
string version
uint64 block_version
uint64 p2p_version
}
```
@@ -180,39 +180,46 @@ Similarly, ResponseInfo should return the versions:
message ResponseInfo {
string data
string software_version
int64 app_version
string version
uint64 app_version
int64 last_block_height
bytes last_block_app_hash
}
```
The existing `version` fields should be called `software_version` but we leave
them for now to reduce the number of breaking changes.
#### EndBlock
Updating the version could be done either with new fields or by using the
existing `tags`. Since we're trying to communicate information that will be
included in Tendermint block Headers, it should be native to the ABCI, and not
something embedded through some scheme in the tags.
something embedded through some scheme in the tags. Thus, version updates should
be communicated through EndBlock.
ResponseEndBlock will include a new field `version_updates`:
EndBlock already contains `ConsensusParams`. We can add version information to
the ConsensusParams as well:
```
message ResponseEndBlock {
repeated Validator validator_updates
ConsensusParams consensus_param_updates
repeated common.KVPair tags
message ConsensusParams {
VersionUpdate version_update
BlockSize block_size
EvidenceParams evidence_params
VersionParams version
}
message VersionUpdate {
int64 app_version
message VersionParams {
uint64 block_version
uint64 app_version
}
```
Tendermint will use the information in VersionUpdate for the next block it
proposes.
For now, the `block_version` will be ignored, as we do not allow block version
to be updated live. If the `app_version` is set, it signals that the app's
protocol version has changed, and the new `app_version` will be included in the
`Block.Header.Version.App` for the next block.
### BlockVersion

View File

@@ -134,10 +134,13 @@ Commit are included in the header of the next block.
### Info
- **Request**:
- `Version (string)`: The Tendermint version
- `Version (string)`: The Tendermint software semantic version
- `BlockVersion (uint64)`: The Tendermint Block Protocol version
- `P2PVersion (uint64)`: The Tendermint P2P Protocol version
- **Response**:
- `Data (string)`: Some arbitrary information
- `Version (Version)`: Version information
- `Version (string)`: The application software semantic version
- `AppVersion (uint64)`: The application protocol version
- `LastBlockHeight (int64)`: Latest block for which the app has
called Commit
- `LastBlockAppHash ([]byte)`: Latest result of Commit
@@ -145,6 +148,7 @@ Commit are included in the header of the next block.
- Return information about the application state.
- Used to sync Tendermint with the application during a handshake
that happens on startup.
- The returned `AppVersion` will be included in the Header of every block.
- Tendermint expects `LastBlockAppHash` and `LastBlockHeight` to
be updated during `Commit`, ensuring that `Commit` is never
called twice for the same block height.
@@ -338,6 +342,7 @@ Commit are included in the header of the next block.
### Header
- **Fields**:
- `Version (Version)`: Version of the blockchain and the application
- `ChainID (string)`: ID of the blockchain
- `Height (int64)`: Height of the block in the chain
- `Time (google.protobuf.Timestamp)`: Time of the block. It is the proposer's
@@ -363,6 +368,15 @@ Commit are included in the header of the next block.
- Provides the proposer of the current block, for use in proposer-based
reward mechanisms.
### Version
- **Fields**:
- `Block (uint64)`: Protocol version of the blockchain data structures.
- `App (uint64)`: Protocol version of the application.
- **Usage**:
- Block version should be static in the life of a blockchain.
- App version may be updated over time by the application.
### Validator
- **Fields**:

View File

@@ -8,6 +8,7 @@ The Tendermint blockchains consists of a short list of basic data types:
- `Block`
- `Header`
- `Version`
- `BlockID`
- `Time`
- `Data` (for transactions)
@@ -38,6 +39,7 @@ the data in the current block, the previous block, and the results returned by t
```go
type Header struct {
// basic block info
Version Version
ChainID string
Height int64
Time Time
@@ -65,6 +67,19 @@ type Header struct {
Further details on each of these fields is described below.
## Version
The `Version` contains the protocol version for the blockchain and the
application as two `uint64` values:
```go
type Version struct {
Block uint64
App uint64
}
```
## BlockID
The `BlockID` contains two distinct Merkle roots of the block.
@@ -200,6 +215,15 @@ See [here](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockc
A Header is valid if its corresponding fields are valid.
### Version
```
block.Version.Block == state.Version.Block
block.Version.App == state.Version.App
```
The block version must match the state version.
### ChainID
```
@@ -320,10 +344,10 @@ next validator sets Merkle root.
### ConsensusParamsHash
```go
block.ConsensusParamsHash == SimpleMerkleRoot(state.ConsensusParams)
block.ConsensusParamsHash == tmhash(amino(state.ConsensusParams))
```
Simple Merkle root of the consensus parameters.
Hash of the amino-encoded consensus parameters.
### AppHash

View File

@@ -216,7 +216,7 @@ prefix) before being concatenated together and hashed.
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
field in the struct sorted by the hash of the field name.
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.
### Simple Merkle Proof
@@ -301,16 +301,15 @@ Where the `"value"` is the base64 encoding of the raw pubkey bytes, and the
Signed messages (eg. votes, proposals) in the consensus are encoded using Amino.
When signing, the elements of a message are re-ordered so the fixed-length fields
are first, making it easy to quickly check the version, height, round, and type.
are first, making it easy to quickly check the type, height, and round.
The `ChainID` is also appended to the end.
We call this encoding the SignBytes. For instance, SignBytes for a vote is the Amino encoding of the following struct:
```go
type CanonicalVote struct {
Version uint64 `binary:"fixed64"`
Type byte
Height int64 `binary:"fixed64"`
Round int64 `binary:"fixed64"`
VoteType byte
Timestamp time.Time
BlockID CanonicalBlockID
ChainID string

View File

@@ -15,6 +15,7 @@ validation.
```go
type State struct {
Version Version
LastResults []Result
AppHash []byte

View File

@@ -75,22 +75,25 @@ The Tendermint Version Handshake allows the peers to exchange their NodeInfo:
```golang
type NodeInfo struct {
Version p2p.Version
ID p2p.ID
ListenAddr string
Network string
Version string
SoftwareVersion string
Channels []int8
Moniker string
Other NodeInfoOther
}
type Version struct {
P2P uint64
Block uint64
App uint64
}
type NodeInfoOther struct {
AminoVersion string
P2PVersion string
ConsensusVersion string
RPCVersion string
TxIndex string
RPCAddress string
}
@@ -99,8 +102,7 @@ type NodeInfoOther struct {
The connection is disconnected if:
- `peer.NodeInfo.ID` is not equal `peerConn.ID`
- `peer.NodeInfo.Version` is not formatted as `X.X.X` where X are integers known as Major, Minor, and Revision
- `peer.NodeInfo.Version` Major is not the same as ours
- `peer.NodeInfo.Version.Block` does not match ours
- `peer.NodeInfo.Network` is not the same as ours
- `peer.Channels` does not intersect with our known Channels.
- `peer.NodeInfo.ListenAddr` is malformed or is a DNS host that cannot be

View File

@@ -146,7 +146,7 @@ func (err *cmnError) Format(s fmt.State, verb rune) {
s.Write([]byte("--= /Error =--\n"))
} else {
// Write msg.
s.Write([]byte(fmt.Sprintf("Error{%v}", err.data))) // TODO tick-esc?
s.Write([]byte(fmt.Sprintf("%v", err.data)))
}
}
}

View File

@@ -24,7 +24,7 @@ func TestErrorPanic(t *testing.T) {
var err = capturePanic()
assert.Equal(t, pnk{"something"}, err.Data())
assert.Equal(t, "Error{{something}}", fmt.Sprintf("%v", err))
assert.Equal(t, "{something}", fmt.Sprintf("%v", err))
assert.Contains(t, fmt.Sprintf("%#v", err), "This is the message in ErrorWrap(r, message).")
assert.Contains(t, fmt.Sprintf("%#v", err), "Stack Trace:\n 0")
}
@@ -34,7 +34,7 @@ func TestErrorWrapSomething(t *testing.T) {
var err = ErrorWrap("something", "formatter%v%v", 0, 1)
assert.Equal(t, "something", err.Data())
assert.Equal(t, "Error{something}", fmt.Sprintf("%v", err))
assert.Equal(t, "something", fmt.Sprintf("%v", err))
assert.Regexp(t, `formatter01\n`, fmt.Sprintf("%#v", err))
assert.Contains(t, fmt.Sprintf("%#v", err), "Stack Trace:\n 0")
}
@@ -46,7 +46,7 @@ func TestErrorWrapNothing(t *testing.T) {
assert.Equal(t,
FmtError{"formatter%v%v", []interface{}{0, 1}},
err.Data())
assert.Equal(t, "Error{formatter01}", fmt.Sprintf("%v", err))
assert.Equal(t, "formatter01", fmt.Sprintf("%v", err))
assert.Contains(t, fmt.Sprintf("%#v", err), `Data: common.FmtError{format:"formatter%v%v", args:[]interface {}{0, 1}}`)
assert.Contains(t, fmt.Sprintf("%#v", err), "Stack Trace:\n 0")
}
@@ -58,7 +58,7 @@ func TestErrorNewError(t *testing.T) {
assert.Equal(t,
FmtError{"formatter%v%v", []interface{}{0, 1}},
err.Data())
assert.Equal(t, "Error{formatter01}", fmt.Sprintf("%v", err))
assert.Equal(t, "formatter01", fmt.Sprintf("%v", err))
assert.Contains(t, fmt.Sprintf("%#v", err), `Data: common.FmtError{format:"formatter%v%v", args:[]interface {}{0, 1}}`)
assert.NotContains(t, fmt.Sprintf("%#v", err), "Stack Trace")
}
@@ -70,7 +70,7 @@ func TestErrorNewErrorWithStacktrace(t *testing.T) {
assert.Equal(t,
FmtError{"formatter%v%v", []interface{}{0, 1}},
err.Data())
assert.Equal(t, "Error{formatter01}", fmt.Sprintf("%v", err))
assert.Equal(t, "formatter01", fmt.Sprintf("%v", err))
assert.Contains(t, fmt.Sprintf("%#v", err), `Data: common.FmtError{format:"formatter%v%v", args:[]interface {}{0, 1}}`)
assert.Contains(t, fmt.Sprintf("%#v", err), "Stack Trace:\n 0")
}
@@ -85,7 +85,7 @@ func TestErrorNewErrorWithTrace(t *testing.T) {
assert.Equal(t,
FmtError{"formatter%v%v", []interface{}{0, 1}},
err.Data())
assert.Equal(t, "Error{formatter01}", fmt.Sprintf("%v", err))
assert.Equal(t, "formatter01", fmt.Sprintf("%v", err))
assert.Contains(t, fmt.Sprintf("%#v", err), `Data: common.FmtError{format:"formatter%v%v", args:[]interface {}{0, 1}}`)
dump := fmt.Sprintf("%#v", err)
assert.NotContains(t, dump, "Stack Trace")

View File

@@ -9,6 +9,39 @@
// When some message is published, we match it with all queries. If there is a
// match, this message will be pushed to all clients, subscribed to that query.
// See query subpackage for our implementation.
//
// Due to the blocking send implementation, a single subscriber can freeze an
// entire server by not reading messages before it unsubscribes. To avoid such
// scenario, subscribers must either:
//
// a) make sure they continue to read from the out channel until
// Unsubscribe(All) is called
//
// s.Subscribe(ctx, sub, qry, out)
// go func() {
// for msg := range out {
// // handle msg
// // will exit automatically when out is closed by Unsubscribe(All)
// }
// }()
// s.UnsubscribeAll(ctx, sub)
//
// b) drain the out channel before calling Unsubscribe(All)
//
// s.Subscribe(ctx, sub, qry, out)
// defer func() {
// for range out {
// // drain out to make sure we don't block
// }
// s.UnsubscribeAll(ctx, sub)
// }()
// for msg := range out {
// // handle msg
// if err != nil {
// return err
// }
// }
//
package pubsub
import (

View File

@@ -56,7 +56,7 @@ func (dbp *DBProvider) SaveFullCommit(fc FullCommit) error {
// We might be overwriting what we already have, but
// it makes the logic easier for now.
vsKey := validatorSetKey(fc.ChainID(), fc.Height())
vsBz, err := dbp.cdc.MarshalBinary(fc.Validators)
vsBz, err := dbp.cdc.MarshalBinaryLengthPrefixed(fc.Validators)
if err != nil {
return err
}
@@ -64,7 +64,7 @@ func (dbp *DBProvider) SaveFullCommit(fc FullCommit) error {
// Save the fc.NextValidators.
nvsKey := validatorSetKey(fc.ChainID(), fc.Height()+1)
nvsBz, err := dbp.cdc.MarshalBinary(fc.NextValidators)
nvsBz, err := dbp.cdc.MarshalBinaryLengthPrefixed(fc.NextValidators)
if err != nil {
return err
}
@@ -72,7 +72,7 @@ func (dbp *DBProvider) SaveFullCommit(fc FullCommit) error {
// Save the fc.SignedHeader
shKey := signedHeaderKey(fc.ChainID(), fc.Height())
shBz, err := dbp.cdc.MarshalBinary(fc.SignedHeader)
shBz, err := dbp.cdc.MarshalBinaryLengthPrefixed(fc.SignedHeader)
if err != nil {
return err
}
@@ -121,7 +121,7 @@ func (dbp *DBProvider) LatestFullCommit(chainID string, minHeight, maxHeight int
// Found the latest full commit signed header.
shBz := itr.Value()
sh := types.SignedHeader{}
err := dbp.cdc.UnmarshalBinary(shBz, &sh)
err := dbp.cdc.UnmarshalBinaryLengthPrefixed(shBz, &sh)
if err != nil {
return FullCommit{}, err
} else {
@@ -150,7 +150,7 @@ func (dbp *DBProvider) getValidatorSet(chainID string, height int64) (valset *ty
err = lerr.ErrUnknownValidators(chainID, height)
return
}
err = dbp.cdc.UnmarshalBinary(vsBz, &valset)
err = dbp.cdc.UnmarshalBinaryLengthPrefixed(vsBz, &valset)
if err != nil {
return
}

View File

@@ -32,7 +32,6 @@ import (
rpccore "github.com/tendermint/tendermint/rpc/core"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
grpccore "github.com/tendermint/tendermint/rpc/grpc"
"github.com/tendermint/tendermint/rpc/lib"
"github.com/tendermint/tendermint/rpc/lib/server"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/state/txindex"
@@ -196,8 +195,8 @@ func NewNode(config *cfg.Config,
return nil, fmt.Errorf("Error starting proxy app connections: %v", err)
}
// Create the handshaker, which calls RequestInfo and replays any blocks
// as necessary to sync tendermint with the app.
// Create the handshaker, which calls RequestInfo, sets the AppVersion on the state,
// and replays any blocks as necessary to sync tendermint with the app.
consensusLogger := logger.With("module", "consensus")
handshaker := cs.NewHandshaker(stateDB, state, blockStore, genDoc)
handshaker.SetLogger(consensusLogger)
@@ -205,9 +204,21 @@ func NewNode(config *cfg.Config,
return nil, fmt.Errorf("Error during handshake: %v", err)
}
// reload the state (it may have been updated by the handshake)
// Reload the state. It will have the Version.Consensus.App set by the
// Handshake, and may have other modifications as well (ie. depending on
// what happened during block replay).
state = sm.LoadState(stateDB)
// Ensure the state's block version matches that of the software.
if state.Version.Consensus.Block != version.BlockProtocol {
return nil, fmt.Errorf(
"Block version of the software does not match that of the state.\n"+
"Got version.BlockProtocol=%v, state.Version.Consensus.Block=%v",
version.BlockProtocol,
state.Version.Consensus.Block,
)
}
// If an address is provided, listen on the socket for a
// connection from an external signing process.
if config.PrivValidatorListenAddr != "" {
@@ -215,7 +226,7 @@ func NewNode(config *cfg.Config,
// TODO: persist this key so external signer
// can actually authenticate us
privKey = ed25519.GenPrivKey()
pvsc = privval.NewSocketPV(
pvsc = privval.NewTCPVal(
logger.With("module", "privval"),
config.PrivValidatorListenAddr,
privKey,
@@ -351,7 +362,17 @@ func NewNode(config *cfg.Config,
var (
p2pLogger = logger.With("module", "p2p")
nodeInfo = makeNodeInfo(config, nodeKey.ID(), txIndexer, genDoc.ChainID)
nodeInfo = makeNodeInfo(
config,
nodeKey.ID(),
txIndexer,
genDoc.ChainID,
p2p.NewProtocolVersion(
version.P2PProtocol, // global
state.Version.Consensus.Block,
state.Version.Consensus.App,
),
)
)
// Setup Transport.
@@ -579,7 +600,7 @@ func (n *Node) OnStop() {
}
}
if pvsc, ok := n.privValidator.(*privval.SocketPV); ok {
if pvsc, ok := n.privValidator.(*privval.TCPVal); ok {
if err := pvsc.Stop(); err != nil {
n.Logger.Error("Error stopping priv validator socket client", "err", err)
}
@@ -756,15 +777,17 @@ func makeNodeInfo(
nodeID p2p.ID,
txIndexer txindex.TxIndexer,
chainID string,
protocolVersion p2p.ProtocolVersion,
) p2p.NodeInfo {
txIndexerStatus := "on"
if _, ok := txIndexer.(*null.TxIndex); ok {
txIndexerStatus = "off"
}
nodeInfo := p2p.DefaultNodeInfo{
ID_: nodeID,
Network: chainID,
Version: version.Version,
ProtocolVersion: protocolVersion,
ID_: nodeID,
Network: chainID,
Version: version.TMCoreSemVer,
Channels: []byte{
bc.BlockchainChannel,
cs.StateChannel, cs.DataChannel, cs.VoteChannel, cs.VoteSetBitsChannel,
@@ -773,12 +796,8 @@ func makeNodeInfo(
},
Moniker: config.Moniker,
Other: p2p.DefaultNodeInfoOther{
AminoVersion: amino.Version,
P2PVersion: p2p.Version,
ConsensusVersion: cs.Version,
RPCVersion: fmt.Sprintf("%v/%v", rpc.Version, rpccore.Version),
TxIndex: txIndexerStatus,
RPCAddress: config.RPC.ListenAddress,
TxIndex: txIndexerStatus,
RPCAddress: config.RPC.ListenAddress,
},
}

View File

@@ -10,7 +10,11 @@ import (
"github.com/stretchr/testify/assert"
"github.com/tendermint/tendermint/abci/example/kvstore"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/p2p"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/version"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/types"
@@ -91,3 +95,21 @@ func TestNodeDelayedStop(t *testing.T) {
startTime := tmtime.Now()
assert.Equal(t, true, startTime.After(n.GenesisDoc().GenesisTime))
}
func TestNodeSetAppVersion(t *testing.T) {
config := cfg.ResetTestRoot("node_app_version_test")
// create & start node
n, err := DefaultNewNode(config, log.TestingLogger())
assert.NoError(t, err, "expected no err on DefaultNewNode")
// default config uses the kvstore app
var appVersion version.Protocol = kvstore.ProtocolVersion
// check version is set in state
state := sm.LoadState(n.stateDB)
assert.Equal(t, state.Version.Consensus.App, appVersion)
// check version is set in node info
assert.Equal(t, n.nodeInfo.(p2p.DefaultNodeInfo).ProtocolVersion.App, appVersion)
}

View File

@@ -337,7 +337,7 @@ FOR_LOOP:
}
case <-c.pingTimer.Chan():
c.Logger.Debug("Send Ping")
_n, err = cdc.MarshalBinaryWriter(c.bufConnWriter, PacketPing{})
_n, err = cdc.MarshalBinaryLengthPrefixedWriter(c.bufConnWriter, PacketPing{})
if err != nil {
break SELECTION
}
@@ -359,7 +359,7 @@ FOR_LOOP:
}
case <-c.pong:
c.Logger.Debug("Send Pong")
_n, err = cdc.MarshalBinaryWriter(c.bufConnWriter, PacketPong{})
_n, err = cdc.MarshalBinaryLengthPrefixedWriter(c.bufConnWriter, PacketPong{})
if err != nil {
break SELECTION
}
@@ -477,7 +477,7 @@ FOR_LOOP:
var packet Packet
var _n int64
var err error
_n, err = cdc.UnmarshalBinaryReader(c.bufConnReader, &packet, int64(c._maxPacketMsgSize))
_n, err = cdc.UnmarshalBinaryLengthPrefixedReader(c.bufConnReader, &packet, int64(c._maxPacketMsgSize))
c.recvMonitor.Update(int(_n))
if err != nil {
if c.IsRunning() {
@@ -553,7 +553,7 @@ func (c *MConnection) stopPongTimer() {
// maxPacketMsgSize returns a maximum size of PacketMsg, including the overhead
// of amino encoding.
func (c *MConnection) maxPacketMsgSize() int {
return len(cdc.MustMarshalBinary(PacketMsg{
return len(cdc.MustMarshalBinaryLengthPrefixed(PacketMsg{
ChannelID: 0x01,
EOF: 1,
Bytes: make([]byte, c.config.MaxPacketMsgPayloadSize),
@@ -723,7 +723,7 @@ func (ch *Channel) nextPacketMsg() PacketMsg {
// Not goroutine-safe
func (ch *Channel) writePacketMsgTo(w io.Writer) (n int64, err error) {
var packet = ch.nextPacketMsg()
n, err = cdc.MarshalBinaryWriter(w, packet)
n, err = cdc.MarshalBinaryLengthPrefixedWriter(w, packet)
atomic.AddInt64(&ch.recentlySent, n)
return
}

View File

@@ -140,7 +140,7 @@ func TestMConnectionPongTimeoutResultsInError(t *testing.T) {
go func() {
// read ping
var pkt PacketPing
_, err = cdc.UnmarshalBinaryReader(server, &pkt, maxPingPongPacketSize)
_, err = cdc.UnmarshalBinaryLengthPrefixedReader(server, &pkt, maxPingPongPacketSize)
assert.Nil(t, err)
serverGotPing <- struct{}{}
}()
@@ -176,22 +176,22 @@ func TestMConnectionMultiplePongsInTheBeginning(t *testing.T) {
defer mconn.Stop()
// sending 3 pongs in a row (abuse)
_, err = server.Write(cdc.MustMarshalBinary(PacketPong{}))
_, err = server.Write(cdc.MustMarshalBinaryLengthPrefixed(PacketPong{}))
require.Nil(t, err)
_, err = server.Write(cdc.MustMarshalBinary(PacketPong{}))
_, err = server.Write(cdc.MustMarshalBinaryLengthPrefixed(PacketPong{}))
require.Nil(t, err)
_, err = server.Write(cdc.MustMarshalBinary(PacketPong{}))
_, err = server.Write(cdc.MustMarshalBinaryLengthPrefixed(PacketPong{}))
require.Nil(t, err)
serverGotPing := make(chan struct{})
go func() {
// read ping (one byte)
var packet, err = Packet(nil), error(nil)
_, err = cdc.UnmarshalBinaryReader(server, &packet, maxPingPongPacketSize)
_, err = cdc.UnmarshalBinaryLengthPrefixedReader(server, &packet, maxPingPongPacketSize)
require.Nil(t, err)
serverGotPing <- struct{}{}
// respond with pong
_, err = server.Write(cdc.MustMarshalBinary(PacketPong{}))
_, err = server.Write(cdc.MustMarshalBinaryLengthPrefixed(PacketPong{}))
require.Nil(t, err)
}()
<-serverGotPing
@@ -227,18 +227,18 @@ func TestMConnectionMultiplePings(t *testing.T) {
// sending 3 pings in a row (abuse)
// see https://github.com/tendermint/tendermint/issues/1190
_, err = server.Write(cdc.MustMarshalBinary(PacketPing{}))
_, err = server.Write(cdc.MustMarshalBinaryLengthPrefixed(PacketPing{}))
require.Nil(t, err)
var pkt PacketPong
_, err = cdc.UnmarshalBinaryReader(server, &pkt, maxPingPongPacketSize)
_, err = cdc.UnmarshalBinaryLengthPrefixedReader(server, &pkt, maxPingPongPacketSize)
require.Nil(t, err)
_, err = server.Write(cdc.MustMarshalBinary(PacketPing{}))
_, err = server.Write(cdc.MustMarshalBinaryLengthPrefixed(PacketPing{}))
require.Nil(t, err)
_, err = cdc.UnmarshalBinaryReader(server, &pkt, maxPingPongPacketSize)
_, err = cdc.UnmarshalBinaryLengthPrefixedReader(server, &pkt, maxPingPongPacketSize)
require.Nil(t, err)
_, err = server.Write(cdc.MustMarshalBinary(PacketPing{}))
_, err = server.Write(cdc.MustMarshalBinaryLengthPrefixed(PacketPing{}))
require.Nil(t, err)
_, err = cdc.UnmarshalBinaryReader(server, &pkt, maxPingPongPacketSize)
_, err = cdc.UnmarshalBinaryLengthPrefixedReader(server, &pkt, maxPingPongPacketSize)
require.Nil(t, err)
assert.True(t, mconn.IsRunning())
@@ -270,20 +270,20 @@ func TestMConnectionPingPongs(t *testing.T) {
go func() {
// read ping
var pkt PacketPing
_, err = cdc.UnmarshalBinaryReader(server, &pkt, maxPingPongPacketSize)
_, err = cdc.UnmarshalBinaryLengthPrefixedReader(server, &pkt, maxPingPongPacketSize)
require.Nil(t, err)
serverGotPing <- struct{}{}
// respond with pong
_, err = server.Write(cdc.MustMarshalBinary(PacketPong{}))
_, err = server.Write(cdc.MustMarshalBinaryLengthPrefixed(PacketPong{}))
require.Nil(t, err)
time.Sleep(mconn.config.PingInterval)
// read ping
_, err = cdc.UnmarshalBinaryReader(server, &pkt, maxPingPongPacketSize)
_, err = cdc.UnmarshalBinaryLengthPrefixedReader(server, &pkt, maxPingPongPacketSize)
require.Nil(t, err)
// respond with pong
_, err = server.Write(cdc.MustMarshalBinary(PacketPong{}))
_, err = server.Write(cdc.MustMarshalBinaryLengthPrefixed(PacketPong{}))
require.Nil(t, err)
}()
<-serverGotPing
@@ -380,7 +380,7 @@ func TestMConnectionReadErrorBadEncoding(t *testing.T) {
client := mconnClient.conn
// send badly encoded msgPacket
bz := cdc.MustMarshalBinary(PacketMsg{})
bz := cdc.MustMarshalBinaryLengthPrefixed(PacketMsg{})
bz[4] += 0x01 // Invalid prefix bytes.
// Write it.
@@ -428,7 +428,7 @@ func TestMConnectionReadErrorLongMessage(t *testing.T) {
EOF: 1,
Bytes: make([]byte, mconnClient.config.MaxPacketMsgPayloadSize),
}
_, err = cdc.MarshalBinaryWriter(buf, packet)
_, err = cdc.MarshalBinaryLengthPrefixedWriter(buf, packet)
assert.Nil(t, err)
_, err = client.Write(buf.Bytes())
assert.Nil(t, err)
@@ -441,7 +441,7 @@ func TestMConnectionReadErrorLongMessage(t *testing.T) {
EOF: 1,
Bytes: make([]byte, mconnClient.config.MaxPacketMsgPayloadSize+100),
}
_, err = cdc.MarshalBinaryWriter(buf, packet)
_, err = cdc.MarshalBinaryLengthPrefixedWriter(buf, packet)
assert.Nil(t, err)
_, err = client.Write(buf.Bytes())
assert.NotNil(t, err)

View File

@@ -211,7 +211,7 @@ func shareEphPubKey(conn io.ReadWriteCloser, locEphPub *[32]byte) (remEphPub *[3
// Send our pubkey and receive theirs in tandem.
var trs, _ = cmn.Parallel(
func(_ int) (val interface{}, err error, abort bool) {
var _, err1 = cdc.MarshalBinaryWriter(conn, locEphPub)
var _, err1 = cdc.MarshalBinaryLengthPrefixedWriter(conn, locEphPub)
if err1 != nil {
return nil, err1, true // abort
}
@@ -219,7 +219,7 @@ func shareEphPubKey(conn io.ReadWriteCloser, locEphPub *[32]byte) (remEphPub *[3
},
func(_ int) (val interface{}, err error, abort bool) {
var _remEphPub [32]byte
var _, err2 = cdc.UnmarshalBinaryReader(conn, &_remEphPub, 1024*1024) // TODO
var _, err2 = cdc.UnmarshalBinaryLengthPrefixedReader(conn, &_remEphPub, 1024*1024) // TODO
if err2 != nil {
return nil, err2, true // abort
}
@@ -305,7 +305,7 @@ func shareAuthSignature(sc *SecretConnection, pubKey crypto.PubKey, signature []
// Send our info and receive theirs in tandem.
var trs, _ = cmn.Parallel(
func(_ int) (val interface{}, err error, abort bool) {
var _, err1 = cdc.MarshalBinaryWriter(sc, authSigMessage{pubKey, signature})
var _, err1 = cdc.MarshalBinaryLengthPrefixedWriter(sc, authSigMessage{pubKey, signature})
if err1 != nil {
return nil, err1, true // abort
}
@@ -313,7 +313,7 @@ func shareAuthSignature(sc *SecretConnection, pubKey crypto.PubKey, signature []
},
func(_ int) (val interface{}, err error, abort bool) {
var _recvMsg authSigMessage
var _, err2 = cdc.UnmarshalBinaryReader(sc, &_recvMsg, 1024*1024) // TODO
var _, err2 = cdc.UnmarshalBinaryLengthPrefixedReader(sc, &_recvMsg, 1024*1024) // TODO
if err2 != nil {
return nil, err2, true // abort
}

View File

@@ -78,3 +78,8 @@ func (p *peer) Get(key string) interface{} {
}
return nil
}
// OriginalAddr always returns nil.
func (p *peer) OriginalAddr() *p2p.NetAddress {
return nil
}

View File

@@ -3,9 +3,9 @@ package p2p
import (
"fmt"
"reflect"
"strings"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/version"
)
const (
@@ -18,8 +18,10 @@ func MaxNodeInfoSize() int {
return maxNodeInfoSize
}
//-------------------------------------------------------------
// NodeInfo exposes basic info of a node
// and determines if we're compatible
// and determines if we're compatible.
type NodeInfo interface {
nodeInfoAddress
nodeInfoTransport
@@ -31,16 +33,49 @@ type nodeInfoAddress interface {
NetAddress() *NetAddress
}
// nodeInfoTransport is validates a nodeInfo and checks
// nodeInfoTransport validates a nodeInfo and checks
// our compatibility with it. It's for use in the handshake.
type nodeInfoTransport interface {
ValidateBasic() error
CompatibleWith(other NodeInfo) error
}
//-------------------------------------------------------------
// ProtocolVersion contains the protocol versions for the software.
type ProtocolVersion struct {
P2P version.Protocol `json:"p2p"`
Block version.Protocol `json:"block"`
App version.Protocol `json:"app"`
}
// defaultProtocolVersion populates the Block and P2P versions using
// the global values, but not the App.
var defaultProtocolVersion = NewProtocolVersion(
version.P2PProtocol,
version.BlockProtocol,
0,
)
// NewProtocolVersion returns a fully populated ProtocolVersion.
func NewProtocolVersion(p2p, block, app version.Protocol) ProtocolVersion {
return ProtocolVersion{
P2P: p2p,
Block: block,
App: app,
}
}
//-------------------------------------------------------------
// Assert DefaultNodeInfo satisfies NodeInfo
var _ NodeInfo = DefaultNodeInfo{}
// DefaultNodeInfo is the basic node information exchanged
// between two peers during the Tendermint P2P handshake.
type DefaultNodeInfo struct {
ProtocolVersion ProtocolVersion `json:"protocol_version"`
// Authenticate
// TODO: replace with NetAddress
ID_ ID `json:"id"` // authenticated identifier
@@ -59,12 +94,8 @@ type DefaultNodeInfo struct {
// DefaultNodeInfoOther is the misc. applcation specific data
type DefaultNodeInfoOther struct {
AminoVersion string `json:"amino_version"`
P2PVersion string `json:"p2p_version"`
ConsensusVersion string `json:"consensus_version"`
RPCVersion string `json:"rpc_version"`
TxIndex string `json:"tx_index"`
RPCAddress string `json:"rpc_address"`
TxIndex string `json:"tx_index"`
RPCAddress string `json:"rpc_address"`
}
// ID returns the node's peer ID.
@@ -86,35 +117,28 @@ func (info DefaultNodeInfo) ID() ID {
// url-encoding), and we just need to be careful with how we handle that in our
// clients. (e.g. off by default).
func (info DefaultNodeInfo) ValidateBasic() error {
// ID is already validated.
// Validate ListenAddr.
_, err := NewNetAddressString(IDAddressString(info.ID(), info.ListenAddr))
if err != nil {
return err
}
// Network is validated in CompatibleWith.
// Validate Version
if len(info.Version) > 0 &&
(!cmn.IsASCIIText(info.Version) || cmn.ASCIITrim(info.Version) == "") {
return fmt.Errorf("info.Version must be valid ASCII text without tabs, but got %v", info.Version)
}
// Validate Channels - ensure max and check for duplicates.
if len(info.Channels) > maxNumChannels {
return fmt.Errorf("info.Channels is too long (%v). Max is %v", len(info.Channels), maxNumChannels)
}
// Sanitize ASCII text fields.
if !cmn.IsASCIIText(info.Moniker) || cmn.ASCIITrim(info.Moniker) == "" {
return fmt.Errorf("info.Moniker must be valid non-empty ASCII text without tabs, but got %v", info.Moniker)
}
// Sanitize versions
// XXX: Should we be more strict about version and address formats?
other := info.Other
versions := []string{
other.AminoVersion,
other.P2PVersion,
other.ConsensusVersion,
other.RPCVersion}
for i, v := range versions {
if cmn.ASCIITrim(v) != "" && !cmn.IsASCIIText(v) {
return fmt.Errorf("info.Other[%d]=%v must be valid non-empty ASCII text without tabs", i, v)
}
}
if cmn.ASCIITrim(other.TxIndex) != "" && (other.TxIndex != "on" && other.TxIndex != "off") {
return fmt.Errorf("info.Other.TxIndex should be either 'on' or 'off', got '%v'", other.TxIndex)
}
if cmn.ASCIITrim(other.RPCAddress) != "" && !cmn.IsASCIIText(other.RPCAddress) {
return fmt.Errorf("info.Other.RPCAddress=%v must be valid non-empty ASCII text without tabs", other.RPCAddress)
}
channels := make(map[byte]struct{})
for _, ch := range info.Channels {
_, ok := channels[ch]
@@ -124,13 +148,30 @@ func (info DefaultNodeInfo) ValidateBasic() error {
channels[ch] = struct{}{}
}
// ensure ListenAddr is good
_, err := NewNetAddressString(IDAddressString(info.ID(), info.ListenAddr))
return err
// Validate Moniker.
if !cmn.IsASCIIText(info.Moniker) || cmn.ASCIITrim(info.Moniker) == "" {
return fmt.Errorf("info.Moniker must be valid non-empty ASCII text without tabs, but got %v", info.Moniker)
}
// Validate Other.
other := info.Other
txIndex := other.TxIndex
switch txIndex {
case "", "on", "off":
default:
return fmt.Errorf("info.Other.TxIndex should be either 'on', 'off', or empty string, got '%v'", txIndex)
}
// XXX: Should we be more strict about address formats?
rpcAddr := other.RPCAddress
if len(rpcAddr) > 0 && (!cmn.IsASCIIText(rpcAddr) || cmn.ASCIITrim(rpcAddr) == "") {
return fmt.Errorf("info.Other.RPCAddress=%v must be valid ASCII text without tabs", rpcAddr)
}
return nil
}
// CompatibleWith checks if two DefaultNodeInfo are compatible with eachother.
// CONTRACT: two nodes are compatible if the major version matches and network match
// CONTRACT: two nodes are compatible if the Block version and network match
// and they have at least one channel in common.
func (info DefaultNodeInfo) CompatibleWith(other_ NodeInfo) error {
other, ok := other_.(DefaultNodeInfo)
@@ -138,22 +179,9 @@ func (info DefaultNodeInfo) CompatibleWith(other_ NodeInfo) error {
return fmt.Errorf("wrong NodeInfo type. Expected DefaultNodeInfo, got %v", reflect.TypeOf(other_))
}
iMajor, _, _, iErr := splitVersion(info.Version)
oMajor, _, _, oErr := splitVersion(other.Version)
// if our own version number is not formatted right, we messed up
if iErr != nil {
return iErr
}
// version number must be formatted correctly ("x.x.x")
if oErr != nil {
return oErr
}
// major version must match
if iMajor != oMajor {
return fmt.Errorf("Peer is on a different major version. Got %v, expected %v", oMajor, iMajor)
if info.ProtocolVersion.Block != other.ProtocolVersion.Block {
return fmt.Errorf("Peer is on a different Block version. Got %v, expected %v",
other.ProtocolVersion.Block, info.ProtocolVersion.Block)
}
// nodes must be on the same network
@@ -201,11 +229,3 @@ func (info DefaultNodeInfo) NetAddress() *NetAddress {
}
return netAddr
}
func splitVersion(version string) (string, string, string, error) {
spl := strings.Split(version, ".")
if len(spl) != 3 {
return "", "", "", fmt.Errorf("Invalid version format %v", version)
}
return spl[0], spl[1], spl[2], nil
}

123
p2p/node_info_test.go Normal file
View File

@@ -0,0 +1,123 @@
package p2p
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tendermint/tendermint/crypto/ed25519"
)
func TestNodeInfoValidate(t *testing.T) {
// empty fails
ni := DefaultNodeInfo{}
assert.Error(t, ni.ValidateBasic())
channels := make([]byte, maxNumChannels)
for i := 0; i < maxNumChannels; i++ {
channels[i] = byte(i)
}
dupChannels := make([]byte, 5)
copy(dupChannels[:], channels[:5])
dupChannels = append(dupChannels, testCh)
nonAscii := "¢§µ"
emptyTab := fmt.Sprintf("\t")
emptySpace := fmt.Sprintf(" ")
testCases := []struct {
testName string
malleateNodeInfo func(*DefaultNodeInfo)
expectErr bool
}{
{"Too Many Channels", func(ni *DefaultNodeInfo) { ni.Channels = append(channels, byte(maxNumChannels)) }, true},
{"Duplicate Channel", func(ni *DefaultNodeInfo) { ni.Channels = dupChannels }, true},
{"Good Channels", func(ni *DefaultNodeInfo) { ni.Channels = ni.Channels[:5] }, false},
{"Invalid NetAddress", func(ni *DefaultNodeInfo) { ni.ListenAddr = "not-an-address" }, true},
{"Good NetAddress", func(ni *DefaultNodeInfo) { ni.ListenAddr = "0.0.0.0:26656" }, false},
{"Non-ASCII Version", func(ni *DefaultNodeInfo) { ni.Version = nonAscii }, true},
{"Empty tab Version", func(ni *DefaultNodeInfo) { ni.Version = emptyTab }, true},
{"Empty space Version", func(ni *DefaultNodeInfo) { ni.Version = emptySpace }, true},
{"Empty Version", func(ni *DefaultNodeInfo) { ni.Version = "" }, false},
{"Non-ASCII Moniker", func(ni *DefaultNodeInfo) { ni.Moniker = nonAscii }, true},
{"Empty tab Moniker", func(ni *DefaultNodeInfo) { ni.Moniker = emptyTab }, true},
{"Empty space Moniker", func(ni *DefaultNodeInfo) { ni.Moniker = emptySpace }, true},
{"Empty Moniker", func(ni *DefaultNodeInfo) { ni.Moniker = "" }, true},
{"Good Moniker", func(ni *DefaultNodeInfo) { ni.Moniker = "hey its me" }, false},
{"Non-ASCII TxIndex", func(ni *DefaultNodeInfo) { ni.Other.TxIndex = nonAscii }, true},
{"Empty tab TxIndex", func(ni *DefaultNodeInfo) { ni.Other.TxIndex = emptyTab }, true},
{"Empty space TxIndex", func(ni *DefaultNodeInfo) { ni.Other.TxIndex = emptySpace }, true},
{"Empty TxIndex", func(ni *DefaultNodeInfo) { ni.Other.TxIndex = "" }, false},
{"Off TxIndex", func(ni *DefaultNodeInfo) { ni.Other.TxIndex = "off" }, false},
{"Non-ASCII RPCAddress", func(ni *DefaultNodeInfo) { ni.Other.RPCAddress = nonAscii }, true},
{"Empty tab RPCAddress", func(ni *DefaultNodeInfo) { ni.Other.RPCAddress = emptyTab }, true},
{"Empty space RPCAddress", func(ni *DefaultNodeInfo) { ni.Other.RPCAddress = emptySpace }, true},
{"Empty RPCAddress", func(ni *DefaultNodeInfo) { ni.Other.RPCAddress = "" }, false},
{"Good RPCAddress", func(ni *DefaultNodeInfo) { ni.Other.RPCAddress = "0.0.0.0:26657" }, false},
}
nodeKey := NodeKey{PrivKey: ed25519.GenPrivKey()}
name := "testing"
// test case passes
ni = testNodeInfo(nodeKey.ID(), name).(DefaultNodeInfo)
ni.Channels = channels
assert.NoError(t, ni.ValidateBasic())
for _, tc := range testCases {
ni := testNodeInfo(nodeKey.ID(), name).(DefaultNodeInfo)
ni.Channels = channels
tc.malleateNodeInfo(&ni)
err := ni.ValidateBasic()
if tc.expectErr {
assert.Error(t, err, tc.testName)
} else {
assert.NoError(t, err, tc.testName)
}
}
}
func TestNodeInfoCompatible(t *testing.T) {
nodeKey1 := NodeKey{PrivKey: ed25519.GenPrivKey()}
nodeKey2 := NodeKey{PrivKey: ed25519.GenPrivKey()}
name := "testing"
var newTestChannel byte = 0x2
// test NodeInfo is compatible
ni1 := testNodeInfo(nodeKey1.ID(), name).(DefaultNodeInfo)
ni2 := testNodeInfo(nodeKey2.ID(), name).(DefaultNodeInfo)
assert.NoError(t, ni1.CompatibleWith(ni2))
// add another channel; still compatible
ni2.Channels = []byte{newTestChannel, testCh}
assert.NoError(t, ni1.CompatibleWith(ni2))
// wrong NodeInfo type is not compatible
_, netAddr := CreateRoutableAddr()
ni3 := mockNodeInfo{netAddr}
assert.Error(t, ni1.CompatibleWith(ni3))
testCases := []struct {
testName string
malleateNodeInfo func(*DefaultNodeInfo)
}{
{"Wrong block version", func(ni *DefaultNodeInfo) { ni.ProtocolVersion.Block += 1 }},
{"Wrong network", func(ni *DefaultNodeInfo) { ni.Network += "-wrong" }},
{"No common channels", func(ni *DefaultNodeInfo) { ni.Channels = []byte{newTestChannel} }},
}
for _, tc := range testCases {
ni := testNodeInfo(nodeKey2.ID(), name).(DefaultNodeInfo)
tc.malleateNodeInfo(&ni)
assert.Error(t, ni1.CompatibleWith(ni))
}
}

View File

@@ -26,6 +26,7 @@ type Peer interface {
NodeInfo() NodeInfo // peer's info
Status() tmconn.ConnectionStatus
OriginalAddr() *NetAddress
Send(byte, []byte) bool
TrySend(byte, []byte) bool
@@ -43,10 +44,28 @@ type peerConn struct {
config *config.P2PConfig
conn net.Conn // source connection
originalAddr *NetAddress // nil for inbound connections
// cached RemoteIP()
ip net.IP
}
func newPeerConn(
outbound, persistent bool,
config *config.P2PConfig,
conn net.Conn,
originalAddr *NetAddress,
) peerConn {
return peerConn{
outbound: outbound,
persistent: persistent,
config: config,
conn: conn,
originalAddr: originalAddr,
}
}
// ID only exists for SecretConnection.
// NOTE: Will panic if conn is not *SecretConnection.
func (pc peerConn) ID() ID {
@@ -195,6 +214,15 @@ func (p *peer) NodeInfo() NodeInfo {
return p.nodeInfo
}
// OriginalAddr returns the original address, which was used to connect with
// the peer. Returns nil for inbound peers.
func (p *peer) OriginalAddr() *NetAddress {
if p.peerConn.outbound {
return p.peerConn.originalAddr
}
return nil
}
// Status returns the peer's ConnectionStatus.
func (p *peer) Status() tmconn.ConnectionStatus {
return p.mconn.Status()

View File

@@ -28,6 +28,7 @@ func (mp *mockPeer) IsPersistent() bool { return true }
func (mp *mockPeer) Get(s string) interface{} { return s }
func (mp *mockPeer) Set(string, interface{}) {}
func (mp *mockPeer) RemoteIP() net.IP { return mp.ip }
func (mp *mockPeer) OriginalAddr() *NetAddress { return nil }
// Returns a mock peer
func newMockPeer(ip net.IP) *mockPeer {

View File

@@ -114,7 +114,7 @@ func testOutboundPeerConn(
return peerConn{}, cmn.ErrorWrap(err, "Error creating peer")
}
pc, err := testPeerConn(conn, config, true, persistent, ourNodePrivKey)
pc, err := testPeerConn(conn, config, true, persistent, ourNodePrivKey, addr)
if err != nil {
if cerr := conn.Close(); cerr != nil {
return peerConn{}, cmn.ErrorWrap(err, cerr.Error())
@@ -207,11 +207,12 @@ func (rp *remotePeer) accept(l net.Listener) {
func (rp *remotePeer) nodeInfo(l net.Listener) NodeInfo {
return DefaultNodeInfo{
ID_: rp.Addr().ID,
Moniker: "remote_peer",
Network: "testing",
Version: "123.123.123",
ListenAddr: l.Addr().String(),
Channels: rp.channels,
ProtocolVersion: defaultProtocolVersion,
ID_: rp.Addr().ID,
ListenAddr: l.Addr().String(),
Network: "testing",
Version: "1.2.3-rc0-deadbeef",
Channels: rp.channels,
Moniker: "remote_peer",
}
}

View File

@@ -402,6 +402,7 @@ func (mockPeer) Send(byte, []byte) bool { return false }
func (mockPeer) TrySend(byte, []byte) bool { return false }
func (mockPeer) Set(string, interface{}) {}
func (mockPeer) Get(string) interface{} { return nil }
func (mockPeer) OriginalAddr() *p2p.NetAddress { return nil }
func assertPeersWithTimeout(
t *testing.T,

View File

@@ -280,9 +280,12 @@ func (sw *Switch) StopPeerForError(peer Peer, reason interface{}) {
sw.stopAndRemovePeer(peer, reason)
if peer.IsPersistent() {
// TODO: use the original address dialed, not the self reported one
// See #2618.
addr := peer.NodeInfo().NetAddress()
addr := peer.OriginalAddr()
if addr == nil {
// FIXME: persistent peers can't be inbound right now.
// self-reported address for inbound persistent peers
addr = peer.NodeInfo().NetAddress()
}
go sw.reconnectToPeer(addr)
}
}

View File

@@ -206,7 +206,7 @@ func testInboundPeerConn(
config *config.P2PConfig,
ourNodePrivKey crypto.PrivKey,
) (peerConn, error) {
return testPeerConn(conn, config, false, false, ourNodePrivKey)
return testPeerConn(conn, config, false, false, ourNodePrivKey, nil)
}
func testPeerConn(
@@ -214,6 +214,7 @@ func testPeerConn(
cfg *config.P2PConfig,
outbound, persistent bool,
ourNodePrivKey crypto.PrivKey,
originalAddr *NetAddress,
) (pc peerConn, err error) {
conn := rawConn
@@ -231,10 +232,11 @@ func testPeerConn(
// Only the information we already have
return peerConn{
config: cfg,
outbound: outbound,
persistent: persistent,
conn: conn,
config: cfg,
outbound: outbound,
persistent: persistent,
conn: conn,
originalAddr: originalAddr,
}, nil
}
@@ -247,11 +249,16 @@ func testNodeInfo(id ID, name string) NodeInfo {
func testNodeInfoWithNetwork(id ID, name, network string) NodeInfo {
return DefaultNodeInfo{
ID_: id,
ListenAddr: fmt.Sprintf("127.0.0.1:%d", cmn.RandIntn(64512)+1023),
Moniker: name,
Network: network,
Version: "123.123.123",
Channels: []byte{testCh},
ProtocolVersion: defaultProtocolVersion,
ID_: id,
ListenAddr: fmt.Sprintf("127.0.0.1:%d", cmn.RandIntn(64512)+1023),
Network: network,
Version: "1.2.3-rc0-deadbeef",
Channels: []byte{testCh},
Moniker: name,
Other: DefaultNodeInfoOther{
TxIndex: "on",
RPCAddress: fmt.Sprintf("127.0.0.1:%d", cmn.RandIntn(64512)+1023),
},
}
}

View File

@@ -171,7 +171,7 @@ func (mt *MultiplexTransport) Accept(cfg peerConfig) (Peer, error) {
cfg.outbound = false
return mt.wrapPeer(a.conn, a.nodeInfo, cfg), nil
return mt.wrapPeer(a.conn, a.nodeInfo, cfg, nil), nil
case <-mt.closec:
return nil, &ErrTransportClosed{}
}
@@ -199,7 +199,7 @@ func (mt *MultiplexTransport) Dial(
cfg.outbound = true
p := mt.wrapPeer(secretConn, nodeInfo, cfg)
p := mt.wrapPeer(secretConn, nodeInfo, cfg, &addr)
return p, nil
}
@@ -399,14 +399,19 @@ func (mt *MultiplexTransport) wrapPeer(
c net.Conn,
ni NodeInfo,
cfg peerConfig,
dialedAddr *NetAddress,
) Peer {
peerConn := newPeerConn(
cfg.outbound,
cfg.persistent,
&mt.p2pConfig,
c,
dialedAddr,
)
p := newPeer(
peerConn{
conn: c,
config: &mt.p2pConfig,
outbound: cfg.outbound,
persistent: cfg.persistent,
},
peerConn,
mt.mConfig,
ni,
cfg.reactorsByCh,
@@ -441,11 +446,11 @@ func handshake(
)
go func(errc chan<- error, c net.Conn) {
_, err := cdc.MarshalBinaryWriter(c, ourNodeInfo)
_, err := cdc.MarshalBinaryLengthPrefixedWriter(c, ourNodeInfo)
errc <- err
}(errc, c)
go func(errc chan<- error, c net.Conn) {
_, err := cdc.UnmarshalBinaryReader(
_, err := cdc.UnmarshalBinaryLengthPrefixedReader(
c,
&peerNodeInfo,
int64(MaxNodeInfoSize()),

View File

@@ -516,7 +516,7 @@ func TestTransportHandshake(t *testing.T) {
}
go func(c net.Conn) {
_, err := cdc.MarshalBinaryWriter(c, peerNodeInfo.(DefaultNodeInfo))
_, err := cdc.MarshalBinaryLengthPrefixedWriter(c, peerNodeInfo.(DefaultNodeInfo))
if err != nil {
t.Error(err)
}
@@ -524,7 +524,7 @@ func TestTransportHandshake(t *testing.T) {
go func(c net.Conn) {
var ni DefaultNodeInfo
_, err := cdc.UnmarshalBinaryReader(
_, err := cdc.UnmarshalBinaryLengthPrefixedReader(
c,
&ni,
int64(MaxNodeInfoSize()),

View File

@@ -1,3 +0,0 @@
package p2p
const Version = "0.5.0"

120
privval/ipc.go Normal file
View File

@@ -0,0 +1,120 @@
package privval
import (
"net"
"time"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/types"
)
// IPCValOption sets an optional parameter on the SocketPV.
type IPCValOption func(*IPCVal)
// IPCValConnTimeout sets the read and write timeout for connections
// from external signing processes.
func IPCValConnTimeout(timeout time.Duration) IPCValOption {
return func(sc *IPCVal) { sc.connTimeout = timeout }
}
// IPCValHeartbeat sets the period on which to check the liveness of the
// connected Signer connections.
func IPCValHeartbeat(period time.Duration) IPCValOption {
return func(sc *IPCVal) { sc.connHeartbeat = period }
}
// IPCVal implements PrivValidator, it uses a unix socket to request signatures
// from an external process.
type IPCVal struct {
cmn.BaseService
*RemoteSignerClient
addr string
connTimeout time.Duration
connHeartbeat time.Duration
conn net.Conn
cancelPing chan struct{}
pingTicker *time.Ticker
}
// Check that IPCVal implements PrivValidator.
var _ types.PrivValidator = (*IPCVal)(nil)
// NewIPCVal returns an instance of IPCVal.
func NewIPCVal(
logger log.Logger,
socketAddr string,
) *IPCVal {
sc := &IPCVal{
addr: socketAddr,
connTimeout: connTimeout,
connHeartbeat: connHeartbeat,
}
sc.BaseService = *cmn.NewBaseService(logger, "IPCVal", sc)
return sc
}
// OnStart implements cmn.Service.
func (sc *IPCVal) OnStart() error {
err := sc.connect()
if err != nil {
sc.Logger.Error("OnStart", "err", err)
return err
}
sc.RemoteSignerClient = NewRemoteSignerClient(sc.conn)
// Start a routine to keep the connection alive
sc.cancelPing = make(chan struct{}, 1)
sc.pingTicker = time.NewTicker(sc.connHeartbeat)
go func() {
for {
select {
case <-sc.pingTicker.C:
err := sc.Ping()
if err != nil {
sc.Logger.Error("Ping", "err", err)
}
case <-sc.cancelPing:
sc.pingTicker.Stop()
return
}
}
}()
return nil
}
// OnStop implements cmn.Service.
func (sc *IPCVal) OnStop() {
if sc.cancelPing != nil {
close(sc.cancelPing)
}
if sc.conn != nil {
if err := sc.conn.Close(); err != nil {
sc.Logger.Error("OnStop", "err", err)
}
}
}
func (sc *IPCVal) connect() error {
la, err := net.ResolveUnixAddr("unix", sc.addr)
if err != nil {
return err
}
conn, err := net.DialUnix("unix", nil, la)
if err != nil {
return err
}
sc.conn = newTimeoutConn(conn, sc.connTimeout)
return nil
}

131
privval/ipc_server.go Normal file
View File

@@ -0,0 +1,131 @@
package privval
import (
"io"
"net"
"time"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/types"
)
// IPCRemoteSignerOption sets an optional parameter on the IPCRemoteSigner.
type IPCRemoteSignerOption func(*IPCRemoteSigner)
// IPCRemoteSignerConnDeadline sets the read and write deadline for connections
// from external signing processes.
func IPCRemoteSignerConnDeadline(deadline time.Duration) IPCRemoteSignerOption {
return func(ss *IPCRemoteSigner) { ss.connDeadline = deadline }
}
// IPCRemoteSignerConnRetries sets the amount of attempted retries to connect.
func IPCRemoteSignerConnRetries(retries int) IPCRemoteSignerOption {
return func(ss *IPCRemoteSigner) { ss.connRetries = retries }
}
// IPCRemoteSigner is a RPC implementation of PrivValidator that listens on a unix socket.
type IPCRemoteSigner struct {
cmn.BaseService
addr string
chainID string
connDeadline time.Duration
connRetries int
privVal types.PrivValidator
listener *net.UnixListener
}
// NewIPCRemoteSigner returns an instance of IPCRemoteSigner.
func NewIPCRemoteSigner(
logger log.Logger,
chainID, socketAddr string,
privVal types.PrivValidator,
) *IPCRemoteSigner {
rs := &IPCRemoteSigner{
addr: socketAddr,
chainID: chainID,
connDeadline: time.Second * defaultConnDeadlineSeconds,
connRetries: defaultDialRetries,
privVal: privVal,
}
rs.BaseService = *cmn.NewBaseService(logger, "IPCRemoteSigner", rs)
return rs
}
// OnStart implements cmn.Service.
func (rs *IPCRemoteSigner) OnStart() error {
err := rs.listen()
if err != nil {
err = cmn.ErrorWrap(err, "listen")
rs.Logger.Error("OnStart", "err", err)
return err
}
go func() {
for {
conn, err := rs.listener.AcceptUnix()
if err != nil {
return
}
go rs.handleConnection(conn)
}
}()
return nil
}
// OnStop implements cmn.Service.
func (rs *IPCRemoteSigner) OnStop() {
if rs.listener != nil {
if err := rs.listener.Close(); err != nil {
rs.Logger.Error("OnStop", "err", cmn.ErrorWrap(err, "closing listener failed"))
}
}
}
func (rs *IPCRemoteSigner) listen() error {
la, err := net.ResolveUnixAddr("unix", rs.addr)
if err != nil {
return err
}
rs.listener, err = net.ListenUnix("unix", la)
return err
}
func (rs *IPCRemoteSigner) handleConnection(conn net.Conn) {
for {
if !rs.IsRunning() {
return // Ignore error from listener closing.
}
// Reset the connection deadline
conn.SetDeadline(time.Now().Add(rs.connDeadline))
req, err := readMsg(conn)
if err != nil {
if err != io.EOF {
rs.Logger.Error("handleConnection", "err", err)
}
return
}
res, err := handleRequest(req, rs.chainID, rs.privVal)
if err != nil {
// only log the error; we'll reply with an error in res
rs.Logger.Error("handleConnection", "err", err)
}
err = writeMsg(conn, res)
if err != nil {
rs.Logger.Error("handleConnection", "err", err)
return
}
}
}

147
privval/ipc_test.go Normal file
View File

@@ -0,0 +1,147 @@
package privval
import (
"io/ioutil"
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/types"
)
func TestIPCPVVote(t *testing.T) {
var (
chainID = cmn.RandStr(12)
sc, rs = testSetupIPCSocketPair(t, chainID, types.NewMockPV())
ts = time.Now()
vType = types.PrecommitType
want = &types.Vote{Timestamp: ts, Type: vType}
have = &types.Vote{Timestamp: ts, Type: vType}
)
defer sc.Stop()
defer rs.Stop()
require.NoError(t, rs.privVal.SignVote(chainID, want))
require.NoError(t, sc.SignVote(chainID, have))
assert.Equal(t, want.Signature, have.Signature)
}
func TestIPCPVVoteResetDeadline(t *testing.T) {
var (
chainID = cmn.RandStr(12)
sc, rs = testSetupIPCSocketPair(t, chainID, types.NewMockPV())
ts = time.Now()
vType = types.PrecommitType
want = &types.Vote{Timestamp: ts, Type: vType}
have = &types.Vote{Timestamp: ts, Type: vType}
)
defer sc.Stop()
defer rs.Stop()
time.Sleep(3 * time.Millisecond)
require.NoError(t, rs.privVal.SignVote(chainID, want))
require.NoError(t, sc.SignVote(chainID, have))
assert.Equal(t, want.Signature, have.Signature)
// This would exceed the deadline if it was not extended by the previous message
time.Sleep(3 * time.Millisecond)
require.NoError(t, rs.privVal.SignVote(chainID, want))
require.NoError(t, sc.SignVote(chainID, have))
assert.Equal(t, want.Signature, have.Signature)
}
func TestIPCPVVoteKeepalive(t *testing.T) {
var (
chainID = cmn.RandStr(12)
sc, rs = testSetupIPCSocketPair(t, chainID, types.NewMockPV())
ts = time.Now()
vType = types.PrecommitType
want = &types.Vote{Timestamp: ts, Type: vType}
have = &types.Vote{Timestamp: ts, Type: vType}
)
defer sc.Stop()
defer rs.Stop()
time.Sleep(10 * time.Millisecond)
require.NoError(t, rs.privVal.SignVote(chainID, want))
require.NoError(t, sc.SignVote(chainID, have))
assert.Equal(t, want.Signature, have.Signature)
}
func testSetupIPCSocketPair(
t *testing.T,
chainID string,
privValidator types.PrivValidator,
) (*IPCVal, *IPCRemoteSigner) {
addr, err := testUnixAddr()
require.NoError(t, err)
var (
logger = log.TestingLogger()
privVal = privValidator
readyc = make(chan struct{})
rs = NewIPCRemoteSigner(
logger,
chainID,
addr,
privVal,
)
sc = NewIPCVal(
logger,
addr,
)
)
IPCValConnTimeout(5 * time.Millisecond)(sc)
IPCValHeartbeat(time.Millisecond)(sc)
IPCRemoteSignerConnDeadline(time.Millisecond * 5)(rs)
testStartIPCRemoteSigner(t, readyc, rs)
<-readyc
require.NoError(t, sc.Start())
assert.True(t, sc.IsRunning())
return sc, rs
}
func testStartIPCRemoteSigner(t *testing.T, readyc chan struct{}, rs *IPCRemoteSigner) {
go func(rs *IPCRemoteSigner) {
require.NoError(t, rs.Start())
assert.True(t, rs.IsRunning())
readyc <- struct{}{}
}(rs)
}
func testUnixAddr() (string, error) {
f, err := ioutil.TempFile("/tmp", "nettest")
if err != nil {
return "", err
}
addr := f.Name()
err = f.Close()
if err != nil {
return "", err
}
err = os.Remove(addr)
if err != nil {
return "", err
}
return addr, nil
}

View File

@@ -314,10 +314,10 @@ func (pv *FilePV) String() string {
// returns true if the only difference in the votes is their timestamp.
func checkVotesOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) {
var lastVote, newVote types.CanonicalVote
if err := cdc.UnmarshalBinary(lastSignBytes, &lastVote); err != nil {
if err := cdc.UnmarshalBinaryLengthPrefixed(lastSignBytes, &lastVote); err != nil {
panic(fmt.Sprintf("LastSignBytes cannot be unmarshalled into vote: %v", err))
}
if err := cdc.UnmarshalBinary(newSignBytes, &newVote); err != nil {
if err := cdc.UnmarshalBinaryLengthPrefixed(newSignBytes, &newVote); err != nil {
panic(fmt.Sprintf("signBytes cannot be unmarshalled into vote: %v", err))
}
@@ -337,10 +337,10 @@ func checkVotesOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.T
// returns true if the only difference in the proposals is their timestamp
func checkProposalsOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (time.Time, bool) {
var lastProposal, newProposal types.CanonicalProposal
if err := cdc.UnmarshalBinary(lastSignBytes, &lastProposal); err != nil {
if err := cdc.UnmarshalBinaryLengthPrefixed(lastSignBytes, &lastProposal); err != nil {
panic(fmt.Sprintf("LastSignBytes cannot be unmarshalled into proposal: %v", err))
}
if err := cdc.UnmarshalBinary(newSignBytes, &newProposal); err != nil {
if err := cdc.UnmarshalBinaryLengthPrefixed(newSignBytes, &newProposal); err != nil {
panic(fmt.Sprintf("signBytes cannot be unmarshalled into proposal: %v", err))
}
@@ -349,8 +349,8 @@ func checkProposalsOnlyDifferByTimestamp(lastSignBytes, newSignBytes []byte) (ti
now := tmtime.Now()
lastProposal.Timestamp = now
newProposal.Timestamp = now
lastProposalBytes, _ := cdc.MarshalBinary(lastProposal)
newProposalBytes, _ := cdc.MarshalBinary(newProposal)
lastProposalBytes, _ := cdc.MarshalBinaryLengthPrefixed(lastProposal)
newProposalBytes, _ := cdc.MarshalBinaryLengthPrefixed(newProposal)
return lastTime, bytes.Equal(newProposalBytes, lastProposalBytes)
}

303
privval/remote_signer.go Normal file
View File

@@ -0,0 +1,303 @@
package privval
import (
"fmt"
"io"
"net"
"sync"
"github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/crypto"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/types"
)
// RemoteSignerClient implements PrivValidator, it uses a socket to request signatures
// from an external process.
type RemoteSignerClient struct {
conn net.Conn
lock sync.Mutex
}
// Check that RemoteSignerClient implements PrivValidator.
var _ types.PrivValidator = (*RemoteSignerClient)(nil)
// NewRemoteSignerClient returns an instance of RemoteSignerClient.
func NewRemoteSignerClient(
conn net.Conn,
) *RemoteSignerClient {
sc := &RemoteSignerClient{
conn: conn,
}
return sc
}
// GetAddress implements PrivValidator.
func (sc *RemoteSignerClient) GetAddress() types.Address {
pubKey, err := sc.getPubKey()
if err != nil {
panic(err)
}
return pubKey.Address()
}
// GetPubKey implements PrivValidator.
func (sc *RemoteSignerClient) GetPubKey() crypto.PubKey {
pubKey, err := sc.getPubKey()
if err != nil {
panic(err)
}
return pubKey
}
func (sc *RemoteSignerClient) getPubKey() (crypto.PubKey, error) {
sc.lock.Lock()
defer sc.lock.Unlock()
err := writeMsg(sc.conn, &PubKeyMsg{})
if err != nil {
return nil, err
}
res, err := readMsg(sc.conn)
if err != nil {
return nil, err
}
return res.(*PubKeyMsg).PubKey, nil
}
// SignVote implements PrivValidator.
func (sc *RemoteSignerClient) SignVote(chainID string, vote *types.Vote) error {
sc.lock.Lock()
defer sc.lock.Unlock()
err := writeMsg(sc.conn, &SignVoteRequest{Vote: vote})
if err != nil {
return err
}
res, err := readMsg(sc.conn)
if err != nil {
return err
}
resp, ok := res.(*SignedVoteResponse)
if !ok {
return ErrUnexpectedResponse
}
if resp.Error != nil {
return resp.Error
}
*vote = *resp.Vote
return nil
}
// SignProposal implements PrivValidator.
func (sc *RemoteSignerClient) SignProposal(
chainID string,
proposal *types.Proposal,
) error {
sc.lock.Lock()
defer sc.lock.Unlock()
err := writeMsg(sc.conn, &SignProposalRequest{Proposal: proposal})
if err != nil {
return err
}
res, err := readMsg(sc.conn)
if err != nil {
return err
}
resp, ok := res.(*SignedProposalResponse)
if !ok {
return ErrUnexpectedResponse
}
if resp.Error != nil {
return resp.Error
}
*proposal = *resp.Proposal
return nil
}
// SignHeartbeat implements PrivValidator.
func (sc *RemoteSignerClient) SignHeartbeat(
chainID string,
heartbeat *types.Heartbeat,
) error {
sc.lock.Lock()
defer sc.lock.Unlock()
err := writeMsg(sc.conn, &SignHeartbeatRequest{Heartbeat: heartbeat})
if err != nil {
return err
}
res, err := readMsg(sc.conn)
if err != nil {
return err
}
resp, ok := res.(*SignedHeartbeatResponse)
if !ok {
return ErrUnexpectedResponse
}
if resp.Error != nil {
return resp.Error
}
*heartbeat = *resp.Heartbeat
return nil
}
// Ping is used to check connection health.
func (sc *RemoteSignerClient) Ping() error {
sc.lock.Lock()
defer sc.lock.Unlock()
err := writeMsg(sc.conn, &PingRequest{})
if err != nil {
return err
}
res, err := readMsg(sc.conn)
if err != nil {
return err
}
_, ok := res.(*PingResponse)
if !ok {
return ErrUnexpectedResponse
}
return nil
}
// RemoteSignerMsg is sent between RemoteSigner and the RemoteSigner client.
type RemoteSignerMsg interface{}
func RegisterRemoteSignerMsg(cdc *amino.Codec) {
cdc.RegisterInterface((*RemoteSignerMsg)(nil), nil)
cdc.RegisterConcrete(&PubKeyMsg{}, "tendermint/remotesigner/PubKeyMsg", nil)
cdc.RegisterConcrete(&SignVoteRequest{}, "tendermint/remotesigner/SignVoteRequest", nil)
cdc.RegisterConcrete(&SignedVoteResponse{}, "tendermint/remotesigner/SignedVoteResponse", nil)
cdc.RegisterConcrete(&SignProposalRequest{}, "tendermint/remotesigner/SignProposalRequest", nil)
cdc.RegisterConcrete(&SignedProposalResponse{}, "tendermint/remotesigner/SignedProposalResponse", nil)
cdc.RegisterConcrete(&SignHeartbeatRequest{}, "tendermint/remotesigner/SignHeartbeatRequest", nil)
cdc.RegisterConcrete(&SignedHeartbeatResponse{}, "tendermint/remotesigner/SignedHeartbeatResponse", nil)
cdc.RegisterConcrete(&PingRequest{}, "tendermint/remotesigner/PingRequest", nil)
cdc.RegisterConcrete(&PingResponse{}, "tendermint/remotesigner/PingResponse", nil)
}
// PubKeyMsg is a PrivValidatorSocket message containing the public key.
type PubKeyMsg struct {
PubKey crypto.PubKey
}
// SignVoteRequest is a PrivValidatorSocket message containing a vote.
type SignVoteRequest struct {
Vote *types.Vote
}
// SignedVoteResponse is a PrivValidatorSocket message containing a signed vote along with a potenial error message.
type SignedVoteResponse struct {
Vote *types.Vote
Error *RemoteSignerError
}
// SignProposalRequest is a PrivValidatorSocket message containing a Proposal.
type SignProposalRequest struct {
Proposal *types.Proposal
}
type SignedProposalResponse struct {
Proposal *types.Proposal
Error *RemoteSignerError
}
// SignHeartbeatRequest is a PrivValidatorSocket message containing a Heartbeat.
type SignHeartbeatRequest struct {
Heartbeat *types.Heartbeat
}
type SignedHeartbeatResponse struct {
Heartbeat *types.Heartbeat
Error *RemoteSignerError
}
// PingRequest is a PrivValidatorSocket message to keep the connection alive.
type PingRequest struct {
}
type PingResponse struct {
}
// RemoteSignerError allows (remote) validators to include meaningful error descriptions in their reply.
type RemoteSignerError struct {
// TODO(ismail): create an enum of known errors
Code int
Description string
}
func (e *RemoteSignerError) Error() string {
return fmt.Sprintf("RemoteSigner returned error #%d: %s", e.Code, e.Description)
}
func readMsg(r io.Reader) (msg RemoteSignerMsg, err error) {
const maxRemoteSignerMsgSize = 1024 * 10
_, err = cdc.UnmarshalBinaryLengthPrefixedReader(r, &msg, maxRemoteSignerMsgSize)
if _, ok := err.(timeoutError); ok {
err = cmn.ErrorWrap(ErrConnTimeout, err.Error())
}
return
}
func writeMsg(w io.Writer, msg interface{}) (err error) {
_, err = cdc.MarshalBinaryLengthPrefixedWriter(w, msg)
if _, ok := err.(timeoutError); ok {
err = cmn.ErrorWrap(ErrConnTimeout, err.Error())
}
return
}
func handleRequest(req RemoteSignerMsg, chainID string, privVal types.PrivValidator) (RemoteSignerMsg, error) {
var res RemoteSignerMsg
var err error
switch r := req.(type) {
case *PubKeyMsg:
var p crypto.PubKey
p = privVal.GetPubKey()
res = &PubKeyMsg{p}
case *SignVoteRequest:
err = privVal.SignVote(chainID, r.Vote)
if err != nil {
res = &SignedVoteResponse{nil, &RemoteSignerError{0, err.Error()}}
} else {
res = &SignedVoteResponse{r.Vote, nil}
}
case *SignProposalRequest:
err = privVal.SignProposal(chainID, r.Proposal)
if err != nil {
res = &SignedProposalResponse{nil, &RemoteSignerError{0, err.Error()}}
} else {
res = &SignedProposalResponse{r.Proposal, nil}
}
case *SignHeartbeatRequest:
err = privVal.SignHeartbeat(chainID, r.Heartbeat)
if err != nil {
res = &SignedHeartbeatResponse{nil, &RemoteSignerError{0, err.Error()}}
} else {
res = &SignedHeartbeatResponse{r.Heartbeat, nil}
}
case *PingRequest:
res = &PingResponse{}
default:
err = fmt.Errorf("unknown msg: %v", r)
}
return res, err
}

View File

@@ -1,605 +0,0 @@
package privval
import (
"errors"
"fmt"
"io"
"net"
"time"
"github.com/tendermint/go-amino"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/log"
p2pconn "github.com/tendermint/tendermint/p2p/conn"
"github.com/tendermint/tendermint/types"
)
const (
defaultAcceptDeadlineSeconds = 30 // tendermint waits this long for remote val to connect
defaultConnDeadlineSeconds = 3 // must be set before each read
defaultConnHeartBeatSeconds = 30 // tcp keep-alive period
defaultConnWaitSeconds = 60 // XXX: is this redundant with the accept deadline?
defaultDialRetries = 10 // try to connect to tendermint this many times
)
// Socket errors.
var (
ErrDialRetryMax = errors.New("dialed maximum retries")
ErrConnWaitTimeout = errors.New("waited for remote signer for too long")
ErrConnTimeout = errors.New("remote signer timed out")
ErrUnexpectedResponse = errors.New("received unexpected response")
)
// SocketPVOption sets an optional parameter on the SocketPV.
type SocketPVOption func(*SocketPV)
// SocketPVAcceptDeadline sets the deadline for the SocketPV listener.
// A zero time value disables the deadline.
func SocketPVAcceptDeadline(deadline time.Duration) SocketPVOption {
return func(sc *SocketPV) { sc.acceptDeadline = deadline }
}
// SocketPVConnDeadline sets the read and write deadline for connections
// from external signing processes.
func SocketPVConnDeadline(deadline time.Duration) SocketPVOption {
return func(sc *SocketPV) { sc.connDeadline = deadline }
}
// SocketPVHeartbeat sets the period on which to check the liveness of the
// connected Signer connections.
func SocketPVHeartbeat(period time.Duration) SocketPVOption {
return func(sc *SocketPV) { sc.connHeartbeat = period }
}
// SocketPVConnWait sets the timeout duration before connection of external
// signing processes are considered to be unsuccessful.
func SocketPVConnWait(timeout time.Duration) SocketPVOption {
return func(sc *SocketPV) { sc.connWaitTimeout = timeout }
}
// SocketPV implements PrivValidator, it uses a socket to request signatures
// from an external process.
type SocketPV struct {
cmn.BaseService
addr string
acceptDeadline time.Duration
connDeadline time.Duration
connHeartbeat time.Duration
connWaitTimeout time.Duration
privKey ed25519.PrivKeyEd25519
conn net.Conn
listener net.Listener
}
// Check that SocketPV implements PrivValidator.
var _ types.PrivValidator = (*SocketPV)(nil)
// NewSocketPV returns an instance of SocketPV.
func NewSocketPV(
logger log.Logger,
socketAddr string,
privKey ed25519.PrivKeyEd25519,
) *SocketPV {
sc := &SocketPV{
addr: socketAddr,
acceptDeadline: time.Second * defaultAcceptDeadlineSeconds,
connDeadline: time.Second * defaultConnDeadlineSeconds,
connHeartbeat: time.Second * defaultConnHeartBeatSeconds,
connWaitTimeout: time.Second * defaultConnWaitSeconds,
privKey: privKey,
}
sc.BaseService = *cmn.NewBaseService(logger, "SocketPV", sc)
return sc
}
// GetAddress implements PrivValidator.
func (sc *SocketPV) GetAddress() types.Address {
addr, err := sc.getAddress()
if err != nil {
panic(err)
}
return addr
}
// Address is an alias for PubKey().Address().
func (sc *SocketPV) getAddress() (cmn.HexBytes, error) {
p, err := sc.getPubKey()
if err != nil {
return nil, err
}
return p.Address(), nil
}
// GetPubKey implements PrivValidator.
func (sc *SocketPV) GetPubKey() crypto.PubKey {
pubKey, err := sc.getPubKey()
if err != nil {
panic(err)
}
return pubKey
}
func (sc *SocketPV) getPubKey() (crypto.PubKey, error) {
err := writeMsg(sc.conn, &PubKeyMsg{})
if err != nil {
return nil, err
}
res, err := readMsg(sc.conn)
if err != nil {
return nil, err
}
return res.(*PubKeyMsg).PubKey, nil
}
// SignVote implements PrivValidator.
func (sc *SocketPV) SignVote(chainID string, vote *types.Vote) error {
err := writeMsg(sc.conn, &SignVoteRequest{Vote: vote})
if err != nil {
return err
}
res, err := readMsg(sc.conn)
if err != nil {
return err
}
resp, ok := res.(*SignedVoteResponse)
if !ok {
return ErrUnexpectedResponse
}
if resp.Error != nil {
return fmt.Errorf("remote error occurred: code: %v, description: %s",
resp.Error.Code,
resp.Error.Description)
}
*vote = *resp.Vote
return nil
}
// SignProposal implements PrivValidator.
func (sc *SocketPV) SignProposal(
chainID string,
proposal *types.Proposal,
) error {
err := writeMsg(sc.conn, &SignProposalRequest{Proposal: proposal})
if err != nil {
return err
}
res, err := readMsg(sc.conn)
if err != nil {
return err
}
resp, ok := res.(*SignedProposalResponse)
if !ok {
return ErrUnexpectedResponse
}
if resp.Error != nil {
return fmt.Errorf("remote error occurred: code: %v, description: %s",
resp.Error.Code,
resp.Error.Description)
}
*proposal = *resp.Proposal
return nil
}
// SignHeartbeat implements PrivValidator.
func (sc *SocketPV) SignHeartbeat(
chainID string,
heartbeat *types.Heartbeat,
) error {
err := writeMsg(sc.conn, &SignHeartbeatRequest{Heartbeat: heartbeat})
if err != nil {
return err
}
res, err := readMsg(sc.conn)
if err != nil {
return err
}
resp, ok := res.(*SignedHeartbeatResponse)
if !ok {
return ErrUnexpectedResponse
}
if resp.Error != nil {
return fmt.Errorf("remote error occurred: code: %v, description: %s",
resp.Error.Code,
resp.Error.Description)
}
*heartbeat = *resp.Heartbeat
return nil
}
// OnStart implements cmn.Service.
func (sc *SocketPV) OnStart() error {
if err := sc.listen(); err != nil {
err = cmn.ErrorWrap(err, "failed to listen")
sc.Logger.Error(
"OnStart",
"err", err,
)
return err
}
conn, err := sc.waitConnection()
if err != nil {
err = cmn.ErrorWrap(err, "failed to accept connection")
sc.Logger.Error(
"OnStart",
"err", err,
)
return err
}
sc.conn = conn
return nil
}
// OnStop implements cmn.Service.
func (sc *SocketPV) OnStop() {
if sc.conn != nil {
if err := sc.conn.Close(); err != nil {
err = cmn.ErrorWrap(err, "failed to close connection")
sc.Logger.Error(
"OnStop",
"err", err,
)
}
}
if sc.listener != nil {
if err := sc.listener.Close(); err != nil {
err = cmn.ErrorWrap(err, "failed to close listener")
sc.Logger.Error(
"OnStop",
"err", err,
)
}
}
}
func (sc *SocketPV) acceptConnection() (net.Conn, error) {
conn, err := sc.listener.Accept()
if err != nil {
if !sc.IsRunning() {
return nil, nil // Ignore error from listener closing.
}
return nil, err
}
conn, err = p2pconn.MakeSecretConnection(conn, sc.privKey)
if err != nil {
return nil, err
}
return conn, nil
}
func (sc *SocketPV) listen() error {
ln, err := net.Listen(cmn.ProtocolAndAddress(sc.addr))
if err != nil {
return err
}
sc.listener = newTCPTimeoutListener(
ln,
sc.acceptDeadline,
sc.connDeadline,
sc.connHeartbeat,
)
return nil
}
// waitConnection uses the configured wait timeout to error if no external
// process connects in the time period.
func (sc *SocketPV) waitConnection() (net.Conn, error) {
var (
connc = make(chan net.Conn, 1)
errc = make(chan error, 1)
)
go func(connc chan<- net.Conn, errc chan<- error) {
conn, err := sc.acceptConnection()
if err != nil {
errc <- err
return
}
connc <- conn
}(connc, errc)
select {
case conn := <-connc:
return conn, nil
case err := <-errc:
if _, ok := err.(timeoutError); ok {
return nil, cmn.ErrorWrap(ErrConnWaitTimeout, err.Error())
}
return nil, err
case <-time.After(sc.connWaitTimeout):
return nil, ErrConnWaitTimeout
}
}
//---------------------------------------------------------
// RemoteSignerOption sets an optional parameter on the RemoteSigner.
type RemoteSignerOption func(*RemoteSigner)
// RemoteSignerConnDeadline sets the read and write deadline for connections
// from external signing processes.
func RemoteSignerConnDeadline(deadline time.Duration) RemoteSignerOption {
return func(ss *RemoteSigner) { ss.connDeadline = deadline }
}
// RemoteSignerConnRetries sets the amount of attempted retries to connect.
func RemoteSignerConnRetries(retries int) RemoteSignerOption {
return func(ss *RemoteSigner) { ss.connRetries = retries }
}
// RemoteSigner implements PrivValidator by dialing to a socket.
type RemoteSigner struct {
cmn.BaseService
addr string
chainID string
connDeadline time.Duration
connRetries int
privKey ed25519.PrivKeyEd25519
privVal types.PrivValidator
conn net.Conn
}
// NewRemoteSigner returns an instance of RemoteSigner.
func NewRemoteSigner(
logger log.Logger,
chainID, socketAddr string,
privVal types.PrivValidator,
privKey ed25519.PrivKeyEd25519,
) *RemoteSigner {
rs := &RemoteSigner{
addr: socketAddr,
chainID: chainID,
connDeadline: time.Second * defaultConnDeadlineSeconds,
connRetries: defaultDialRetries,
privKey: privKey,
privVal: privVal,
}
rs.BaseService = *cmn.NewBaseService(logger, "RemoteSigner", rs)
return rs
}
// OnStart implements cmn.Service.
func (rs *RemoteSigner) OnStart() error {
conn, err := rs.connect()
if err != nil {
err = cmn.ErrorWrap(err, "connect")
rs.Logger.Error("OnStart", "err", err)
return err
}
go rs.handleConnection(conn)
return nil
}
// OnStop implements cmn.Service.
func (rs *RemoteSigner) OnStop() {
if rs.conn == nil {
return
}
if err := rs.conn.Close(); err != nil {
rs.Logger.Error("OnStop", "err", cmn.ErrorWrap(err, "closing listener failed"))
}
}
func (rs *RemoteSigner) connect() (net.Conn, error) {
for retries := rs.connRetries; retries > 0; retries-- {
// Don't sleep if it is the first retry.
if retries != rs.connRetries {
time.Sleep(rs.connDeadline)
}
conn, err := cmn.Connect(rs.addr)
if err != nil {
err = cmn.ErrorWrap(err, "connection failed")
rs.Logger.Error(
"connect",
"addr", rs.addr,
"err", err,
)
continue
}
if err := conn.SetDeadline(time.Now().Add(time.Second * defaultConnDeadlineSeconds)); err != nil {
err = cmn.ErrorWrap(err, "setting connection timeout failed")
rs.Logger.Error(
"connect",
"err", err,
)
continue
}
conn, err = p2pconn.MakeSecretConnection(conn, rs.privKey)
if err != nil {
err = cmn.ErrorWrap(err, "encrypting connection failed")
rs.Logger.Error(
"connect",
"err", err,
)
continue
}
return conn, nil
}
return nil, ErrDialRetryMax
}
func (rs *RemoteSigner) handleConnection(conn net.Conn) {
for {
if !rs.IsRunning() {
return // Ignore error from listener closing.
}
req, err := readMsg(conn)
if err != nil {
if err != io.EOF {
rs.Logger.Error("handleConnection", "err", err)
}
return
}
var res SocketPVMsg
switch r := req.(type) {
case *PubKeyMsg:
var p crypto.PubKey
p = rs.privVal.GetPubKey()
res = &PubKeyMsg{p}
case *SignVoteRequest:
err = rs.privVal.SignVote(rs.chainID, r.Vote)
if err != nil {
res = &SignedVoteResponse{nil, &RemoteSignerError{0, err.Error()}}
} else {
res = &SignedVoteResponse{r.Vote, nil}
}
case *SignProposalRequest:
err = rs.privVal.SignProposal(rs.chainID, r.Proposal)
if err != nil {
res = &SignedProposalResponse{nil, &RemoteSignerError{0, err.Error()}}
} else {
res = &SignedProposalResponse{r.Proposal, nil}
}
case *SignHeartbeatRequest:
err = rs.privVal.SignHeartbeat(rs.chainID, r.Heartbeat)
if err != nil {
res = &SignedHeartbeatResponse{nil, &RemoteSignerError{0, err.Error()}}
} else {
res = &SignedHeartbeatResponse{r.Heartbeat, nil}
}
default:
err = fmt.Errorf("unknown msg: %v", r)
}
if err != nil {
// only log the error; we'll reply with an error in res
rs.Logger.Error("handleConnection", "err", err)
}
err = writeMsg(conn, res)
if err != nil {
rs.Logger.Error("handleConnection", "err", err)
return
}
}
}
//---------------------------------------------------------
// SocketPVMsg is sent between RemoteSigner and SocketPV.
type SocketPVMsg interface{}
func RegisterSocketPVMsg(cdc *amino.Codec) {
cdc.RegisterInterface((*SocketPVMsg)(nil), nil)
cdc.RegisterConcrete(&PubKeyMsg{}, "tendermint/socketpv/PubKeyMsg", nil)
cdc.RegisterConcrete(&SignVoteRequest{}, "tendermint/socketpv/SignVoteRequest", nil)
cdc.RegisterConcrete(&SignedVoteResponse{}, "tendermint/socketpv/SignedVoteResponse", nil)
cdc.RegisterConcrete(&SignProposalRequest{}, "tendermint/socketpv/SignProposalRequest", nil)
cdc.RegisterConcrete(&SignedProposalResponse{}, "tendermint/socketpv/SignedProposalResponse", nil)
cdc.RegisterConcrete(&SignHeartbeatRequest{}, "tendermint/socketpv/SignHeartbeatRequest", nil)
cdc.RegisterConcrete(&SignedHeartbeatResponse{}, "tendermint/socketpv/SignedHeartbeatResponse", nil)
}
// PubKeyMsg is a PrivValidatorSocket message containing the public key.
type PubKeyMsg struct {
PubKey crypto.PubKey
}
// SignVoteRequest is a PrivValidatorSocket message containing a vote.
type SignVoteRequest struct {
Vote *types.Vote
}
// SignedVoteResponse is a PrivValidatorSocket message containing a signed vote along with a potenial error message.
type SignedVoteResponse struct {
Vote *types.Vote
Error *RemoteSignerError
}
// SignProposalRequest is a PrivValidatorSocket message containing a Proposal.
type SignProposalRequest struct {
Proposal *types.Proposal
}
type SignedProposalResponse struct {
Proposal *types.Proposal
Error *RemoteSignerError
}
// SignHeartbeatRequest is a PrivValidatorSocket message containing a Heartbeat.
type SignHeartbeatRequest struct {
Heartbeat *types.Heartbeat
}
type SignedHeartbeatResponse struct {
Heartbeat *types.Heartbeat
Error *RemoteSignerError
}
// RemoteSignerError allows (remote) validators to include meaningful error descriptions in their reply.
type RemoteSignerError struct {
// TODO(ismail): create an enum of known errors
Code int
Description string
}
func readMsg(r io.Reader) (msg SocketPVMsg, err error) {
const maxSocketPVMsgSize = 1024 * 10
// set deadline before trying to read
conn := r.(net.Conn)
if err := conn.SetDeadline(time.Now().Add(time.Second * defaultConnDeadlineSeconds)); err != nil {
err = cmn.ErrorWrap(err, "setting connection timeout failed in readMsg")
return msg, err
}
_, err = cdc.UnmarshalBinaryReader(r, &msg, maxSocketPVMsgSize)
if _, ok := err.(timeoutError); ok {
err = cmn.ErrorWrap(ErrConnTimeout, err.Error())
}
return
}
func writeMsg(w io.Writer, msg interface{}) (err error) {
_, err = cdc.MarshalBinaryWriter(w, msg)
if _, ok := err.(timeoutError); ok {
err = cmn.ErrorWrap(ErrConnTimeout, err.Error())
}
return
}

214
privval/tcp.go Normal file
View File

@@ -0,0 +1,214 @@
package privval
import (
"errors"
"net"
"time"
"github.com/tendermint/tendermint/crypto/ed25519"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/log"
p2pconn "github.com/tendermint/tendermint/p2p/conn"
"github.com/tendermint/tendermint/types"
)
const (
defaultAcceptDeadlineSeconds = 3
defaultConnDeadlineSeconds = 3
defaultConnHeartBeatSeconds = 2
defaultDialRetries = 10
)
// Socket errors.
var (
ErrDialRetryMax = errors.New("dialed maximum retries")
ErrConnTimeout = errors.New("remote signer timed out")
ErrUnexpectedResponse = errors.New("received unexpected response")
)
var (
acceptDeadline = time.Second * defaultAcceptDeadlineSeconds
connTimeout = time.Second * defaultConnDeadlineSeconds
connHeartbeat = time.Second * defaultConnHeartBeatSeconds
)
// TCPValOption sets an optional parameter on the SocketPV.
type TCPValOption func(*TCPVal)
// TCPValAcceptDeadline sets the deadline for the TCPVal listener.
// A zero time value disables the deadline.
func TCPValAcceptDeadline(deadline time.Duration) TCPValOption {
return func(sc *TCPVal) { sc.acceptDeadline = deadline }
}
// TCPValConnTimeout sets the read and write timeout for connections
// from external signing processes.
func TCPValConnTimeout(timeout time.Duration) TCPValOption {
return func(sc *TCPVal) { sc.connTimeout = timeout }
}
// TCPValHeartbeat sets the period on which to check the liveness of the
// connected Signer connections.
func TCPValHeartbeat(period time.Duration) TCPValOption {
return func(sc *TCPVal) { sc.connHeartbeat = period }
}
// TCPVal implements PrivValidator, it uses a socket to request signatures
// from an external process.
type TCPVal struct {
cmn.BaseService
*RemoteSignerClient
addr string
acceptDeadline time.Duration
connTimeout time.Duration
connHeartbeat time.Duration
privKey ed25519.PrivKeyEd25519
conn net.Conn
listener net.Listener
cancelPing chan struct{}
pingTicker *time.Ticker
}
// Check that TCPVal implements PrivValidator.
var _ types.PrivValidator = (*TCPVal)(nil)
// NewTCPVal returns an instance of TCPVal.
func NewTCPVal(
logger log.Logger,
socketAddr string,
privKey ed25519.PrivKeyEd25519,
) *TCPVal {
sc := &TCPVal{
addr: socketAddr,
acceptDeadline: acceptDeadline,
connTimeout: connTimeout,
connHeartbeat: connHeartbeat,
privKey: privKey,
}
sc.BaseService = *cmn.NewBaseService(logger, "TCPVal", sc)
return sc
}
// OnStart implements cmn.Service.
func (sc *TCPVal) OnStart() error {
if err := sc.listen(); err != nil {
sc.Logger.Error("OnStart", "err", err)
return err
}
conn, err := sc.waitConnection()
if err != nil {
sc.Logger.Error("OnStart", "err", err)
return err
}
sc.conn = conn
sc.RemoteSignerClient = NewRemoteSignerClient(sc.conn)
// Start a routine to keep the connection alive
sc.cancelPing = make(chan struct{}, 1)
sc.pingTicker = time.NewTicker(sc.connHeartbeat)
go func() {
for {
select {
case <-sc.pingTicker.C:
err := sc.Ping()
if err != nil {
sc.Logger.Error(
"Ping",
"err", err,
)
}
case <-sc.cancelPing:
sc.pingTicker.Stop()
return
}
}
}()
return nil
}
// OnStop implements cmn.Service.
func (sc *TCPVal) OnStop() {
if sc.cancelPing != nil {
close(sc.cancelPing)
}
if sc.conn != nil {
if err := sc.conn.Close(); err != nil {
sc.Logger.Error("OnStop", "err", err)
}
}
if sc.listener != nil {
if err := sc.listener.Close(); err != nil {
sc.Logger.Error("OnStop", "err", err)
}
}
}
func (sc *TCPVal) acceptConnection() (net.Conn, error) {
conn, err := sc.listener.Accept()
if err != nil {
if !sc.IsRunning() {
return nil, nil // Ignore error from listener closing.
}
return nil, err
}
conn, err = p2pconn.MakeSecretConnection(conn, sc.privKey)
if err != nil {
return nil, err
}
return conn, nil
}
func (sc *TCPVal) listen() error {
ln, err := net.Listen(cmn.ProtocolAndAddress(sc.addr))
if err != nil {
return err
}
sc.listener = newTCPTimeoutListener(
ln,
sc.acceptDeadline,
sc.connTimeout,
sc.connHeartbeat,
)
return nil
}
// waitConnection uses the configured wait timeout to error if no external
// process connects in the time period.
func (sc *TCPVal) waitConnection() (net.Conn, error) {
var (
connc = make(chan net.Conn, 1)
errc = make(chan error, 1)
)
go func(connc chan<- net.Conn, errc chan<- error) {
conn, err := sc.acceptConnection()
if err != nil {
errc <- err
return
}
connc <- conn
}(connc, errc)
select {
case conn := <-connc:
return conn, nil
case err := <-errc:
return nil, err
}
}

160
privval/tcp_server.go Normal file
View File

@@ -0,0 +1,160 @@
package privval
import (
"io"
"net"
"time"
"github.com/tendermint/tendermint/crypto/ed25519"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/log"
p2pconn "github.com/tendermint/tendermint/p2p/conn"
"github.com/tendermint/tendermint/types"
)
// RemoteSignerOption sets an optional parameter on the RemoteSigner.
type RemoteSignerOption func(*RemoteSigner)
// RemoteSignerConnDeadline sets the read and write deadline for connections
// from external signing processes.
func RemoteSignerConnDeadline(deadline time.Duration) RemoteSignerOption {
return func(ss *RemoteSigner) { ss.connDeadline = deadline }
}
// RemoteSignerConnRetries sets the amount of attempted retries to connect.
func RemoteSignerConnRetries(retries int) RemoteSignerOption {
return func(ss *RemoteSigner) { ss.connRetries = retries }
}
// RemoteSigner implements PrivValidator by dialing to a socket.
type RemoteSigner struct {
cmn.BaseService
addr string
chainID string
connDeadline time.Duration
connRetries int
privKey ed25519.PrivKeyEd25519
privVal types.PrivValidator
conn net.Conn
}
// NewRemoteSigner returns an instance of RemoteSigner.
func NewRemoteSigner(
logger log.Logger,
chainID, socketAddr string,
privVal types.PrivValidator,
privKey ed25519.PrivKeyEd25519,
) *RemoteSigner {
rs := &RemoteSigner{
addr: socketAddr,
chainID: chainID,
connDeadline: time.Second * defaultConnDeadlineSeconds,
connRetries: defaultDialRetries,
privKey: privKey,
privVal: privVal,
}
rs.BaseService = *cmn.NewBaseService(logger, "RemoteSigner", rs)
return rs
}
// OnStart implements cmn.Service.
func (rs *RemoteSigner) OnStart() error {
conn, err := rs.connect()
if err != nil {
rs.Logger.Error("OnStart", "err", err)
return err
}
go rs.handleConnection(conn)
return nil
}
// OnStop implements cmn.Service.
func (rs *RemoteSigner) OnStop() {
if rs.conn == nil {
return
}
if err := rs.conn.Close(); err != nil {
rs.Logger.Error("OnStop", "err", cmn.ErrorWrap(err, "closing listener failed"))
}
}
func (rs *RemoteSigner) connect() (net.Conn, error) {
for retries := rs.connRetries; retries > 0; retries-- {
// Don't sleep if it is the first retry.
if retries != rs.connRetries {
time.Sleep(rs.connDeadline)
}
conn, err := cmn.Connect(rs.addr)
if err != nil {
rs.Logger.Error(
"connect",
"addr", rs.addr,
"err", err,
)
continue
}
if err := conn.SetDeadline(time.Now().Add(connTimeout)); err != nil {
rs.Logger.Error(
"connect",
"err", err,
)
continue
}
conn, err = p2pconn.MakeSecretConnection(conn, rs.privKey)
if err != nil {
rs.Logger.Error(
"connect",
"err", err,
)
continue
}
return conn, nil
}
return nil, ErrDialRetryMax
}
func (rs *RemoteSigner) handleConnection(conn net.Conn) {
for {
if !rs.IsRunning() {
return // Ignore error from listener closing.
}
// Reset the connection deadline
conn.SetDeadline(time.Now().Add(rs.connDeadline))
req, err := readMsg(conn)
if err != nil {
if err != io.EOF {
rs.Logger.Error("handleConnection", "err", err)
}
return
}
res, err := handleRequest(req, rs.chainID, rs.privVal)
if err != nil {
// only log the error; we'll reply with an error in res
rs.Logger.Error("handleConnection", "err", err)
}
err = writeMsg(conn, res)
if err != nil {
rs.Logger.Error("handleConnection", "err", err)
return
}
}
}

View File

@@ -24,6 +24,13 @@ type tcpTimeoutListener struct {
period time.Duration
}
// timeoutConn wraps a net.Conn to standardise protocol timeouts / deadline resets.
type timeoutConn struct {
net.Conn
connDeadline time.Duration
}
// newTCPTimeoutListener returns an instance of tcpTimeoutListener.
func newTCPTimeoutListener(
ln net.Listener,
@@ -38,6 +45,16 @@ func newTCPTimeoutListener(
}
}
// newTimeoutConn returns an instance of newTCPTimeoutConn.
func newTimeoutConn(
conn net.Conn,
connDeadline time.Duration) *timeoutConn {
return &timeoutConn{
conn,
connDeadline,
}
}
// Accept implements net.Listener.
func (ln tcpTimeoutListener) Accept() (net.Conn, error) {
err := ln.SetDeadline(time.Now().Add(ln.acceptDeadline))
@@ -50,17 +67,24 @@ func (ln tcpTimeoutListener) Accept() (net.Conn, error) {
return nil, err
}
if err := tc.SetDeadline(time.Now().Add(ln.connDeadline)); err != nil {
return nil, err
}
// Wrap the conn in our timeout wrapper
conn := newTimeoutConn(tc, ln.connDeadline)
if err := tc.SetKeepAlive(true); err != nil {
return nil, err
}
if err := tc.SetKeepAlivePeriod(ln.period); err != nil {
return nil, err
}
return tc, nil
return conn, nil
}
// Read implements net.Listener.
func (c timeoutConn) Read(b []byte) (n int, err error) {
// Reset deadline
c.Conn.SetReadDeadline(time.Now().Add(c.connDeadline))
return c.Conn.Read(b)
}
// Write implements net.Listener.
func (c timeoutConn) Write(b []byte) (n int, err error) {
// Reset deadline
c.Conn.SetWriteDeadline(time.Now().Add(c.connDeadline))
return c.Conn.Write(b)
}

View File

@@ -44,13 +44,14 @@ func TestTCPTimeoutListenerConnDeadline(t *testing.T) {
time.Sleep(2 * time.Millisecond)
_, err = c.Write([]byte("foo"))
msg := make([]byte, 200)
_, err = c.Read(msg)
opErr, ok := err.(*net.OpError)
if !ok {
t.Fatalf("have %v, want *net.OpError", err)
}
if have, want := opErr.Op, "write"; have != want {
if have, want := opErr.Op, "read"; have != want {
t.Errorf("have %v, want %v", have, want)
}
}(ln)

View File

@@ -27,8 +27,7 @@ func TestSocketPVAddress(t *testing.T) {
serverAddr := rs.privVal.GetAddress()
clientAddr, err := sc.getAddress()
require.NoError(t, err)
clientAddr := sc.GetAddress()
assert.Equal(t, serverAddr, clientAddr)
@@ -91,6 +90,53 @@ func TestSocketPVVote(t *testing.T) {
assert.Equal(t, want.Signature, have.Signature)
}
func TestSocketPVVoteResetDeadline(t *testing.T) {
var (
chainID = cmn.RandStr(12)
sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV())
ts = time.Now()
vType = types.PrecommitType
want = &types.Vote{Timestamp: ts, Type: vType}
have = &types.Vote{Timestamp: ts, Type: vType}
)
defer sc.Stop()
defer rs.Stop()
time.Sleep(3 * time.Millisecond)
require.NoError(t, rs.privVal.SignVote(chainID, want))
require.NoError(t, sc.SignVote(chainID, have))
assert.Equal(t, want.Signature, have.Signature)
// This would exceed the deadline if it was not extended by the previous message
time.Sleep(3 * time.Millisecond)
require.NoError(t, rs.privVal.SignVote(chainID, want))
require.NoError(t, sc.SignVote(chainID, have))
assert.Equal(t, want.Signature, have.Signature)
}
func TestSocketPVVoteKeepalive(t *testing.T) {
var (
chainID = cmn.RandStr(12)
sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV())
ts = time.Now()
vType = types.PrecommitType
want = &types.Vote{Timestamp: ts, Type: vType}
have = &types.Vote{Timestamp: ts, Type: vType}
)
defer sc.Stop()
defer rs.Stop()
time.Sleep(10 * time.Millisecond)
require.NoError(t, rs.privVal.SignVote(chainID, want))
require.NoError(t, sc.SignVote(chainID, have))
assert.Equal(t, want.Signature, have.Signature)
}
func TestSocketPVHeartbeat(t *testing.T) {
var (
chainID = cmn.RandStr(12)
@@ -107,36 +153,20 @@ func TestSocketPVHeartbeat(t *testing.T) {
assert.Equal(t, want.Signature, have.Signature)
}
func TestSocketPVAcceptDeadline(t *testing.T) {
var (
sc = NewSocketPV(
log.TestingLogger(),
"127.0.0.1:0",
ed25519.GenPrivKey(),
)
)
defer sc.Stop()
SocketPVAcceptDeadline(time.Millisecond)(sc)
assert.Equal(t, sc.Start().(cmn.Error).Data(), ErrConnWaitTimeout)
}
func TestSocketPVDeadline(t *testing.T) {
var (
addr = testFreeAddr(t)
listenc = make(chan struct{})
sc = NewSocketPV(
sc = NewTCPVal(
log.TestingLogger(),
addr,
ed25519.GenPrivKey(),
)
)
SocketPVConnDeadline(100 * time.Millisecond)(sc)
SocketPVConnWait(500 * time.Millisecond)(sc)
TCPValConnTimeout(100 * time.Millisecond)(sc)
go func(sc *SocketPV) {
go func(sc *TCPVal) {
defer close(listenc)
require.NoError(t, sc.Start())
@@ -161,26 +191,10 @@ func TestSocketPVDeadline(t *testing.T) {
<-listenc
// Sleep to guarantee deadline has been hit.
time.Sleep(20 * time.Microsecond)
_, err := sc.getPubKey()
assert.Equal(t, err.(cmn.Error).Data(), ErrConnTimeout)
}
func TestSocketPVWait(t *testing.T) {
sc := NewSocketPV(
log.TestingLogger(),
"127.0.0.1:0",
ed25519.GenPrivKey(),
)
defer sc.Stop()
SocketPVConnWait(time.Millisecond)(sc)
assert.Equal(t, sc.Start().(cmn.Error).Data(), ErrConnWaitTimeout)
}
func TestRemoteSignerRetry(t *testing.T) {
var (
attemptc = make(chan int)
@@ -221,7 +235,7 @@ func TestRemoteSignerRetry(t *testing.T) {
RemoteSignerConnDeadline(time.Millisecond)(rs)
RemoteSignerConnRetries(retries)(rs)
assert.Equal(t, rs.Start().(cmn.Error).Data(), ErrDialRetryMax)
assert.Equal(t, rs.Start(), ErrDialRetryMax)
select {
case attempts := <-attemptc:
@@ -328,7 +342,7 @@ func TestErrUnexpectedResponse(t *testing.T) {
types.NewMockPV(),
ed25519.GenPrivKey(),
)
sc = NewSocketPV(
sc = NewTCPVal(
logger,
addr,
ed25519.GenPrivKey(),
@@ -383,7 +397,7 @@ func testSetupSocketPair(
t *testing.T,
chainID string,
privValidator types.PrivValidator,
) (*SocketPV, *RemoteSigner) {
) (*TCPVal, *RemoteSigner) {
var (
addr = testFreeAddr(t)
logger = log.TestingLogger()
@@ -396,18 +410,20 @@ func testSetupSocketPair(
privVal,
ed25519.GenPrivKey(),
)
sc = NewSocketPV(
sc = NewTCPVal(
logger,
addr,
ed25519.GenPrivKey(),
)
)
testStartSocketPV(t, readyc, sc)
RemoteSignerConnDeadline(time.Millisecond)(rs)
TCPValConnTimeout(5 * time.Millisecond)(sc)
TCPValHeartbeat(2 * time.Millisecond)(sc)
RemoteSignerConnDeadline(5 * time.Millisecond)(rs)
RemoteSignerConnRetries(1e6)(rs)
testStartSocketPV(t, readyc, sc)
require.NoError(t, rs.Start())
assert.True(t, rs.IsRunning())
@@ -416,7 +432,7 @@ func testSetupSocketPair(
return sc, rs
}
func testReadWriteResponse(t *testing.T, resp SocketPVMsg, rsConn net.Conn) {
func testReadWriteResponse(t *testing.T, resp RemoteSignerMsg, rsConn net.Conn) {
_, err := readMsg(rsConn)
require.NoError(t, err)
@@ -424,8 +440,8 @@ func testReadWriteResponse(t *testing.T, resp SocketPVMsg, rsConn net.Conn) {
require.NoError(t, err)
}
func testStartSocketPV(t *testing.T, readyc chan struct{}, sc *SocketPV) {
go func(sc *SocketPV) {
func testStartSocketPV(t *testing.T, readyc chan struct{}, sc *TCPVal) {
go func(sc *TCPVal) {
require.NoError(t, sc.Start())
assert.True(t, sc.IsRunning())

View File

@@ -9,5 +9,5 @@ var cdc = amino.NewCodec()
func init() {
cryptoAmino.RegisterAmino(cdc)
RegisterSocketPVMsg(cdc)
RegisterRemoteSignerMsg(cdc)
}

View File

@@ -143,7 +143,7 @@ func TestInfo(t *testing.T) {
proxy := NewAppConnTest(cli)
t.Log("Connected")
resInfo, err := proxy.InfoSync(types.RequestInfo{Version: ""})
resInfo, err := proxy.InfoSync(RequestInfo)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}

15
proxy/version.go Normal file
View File

@@ -0,0 +1,15 @@
package proxy
import (
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/version"
)
// RequestInfo contains all the information for sending
// the abci.RequestInfo message during handshake with the app.
// It contains only compile-time version information.
var RequestInfo = abci.RequestInfo{
Version: version.Version,
BlockVersion: version.BlockProtocol.Uint64(),
P2PVersion: version.P2PProtocol.Uint64(),
}

View File

@@ -3,10 +3,10 @@ package mock
import (
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/proxy"
"github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tendermint/version"
)
// ABCIApp will send all abci related request to the named app,
@@ -23,7 +23,7 @@ var (
)
func (a ABCIApp) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
return &ctypes.ResultABCIInfo{a.App.Info(abci.RequestInfo{Version: version.Version})}, nil
return &ctypes.ResultABCIInfo{a.App.Info(proxy.RequestInfo)}, nil
}
func (a ABCIApp) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQuery, error) {

View File

@@ -3,8 +3,8 @@ package core
import (
abci "github.com/tendermint/tendermint/abci/types"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/proxy"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/version"
)
// Query the application for some information.
@@ -87,7 +87,7 @@ func ABCIQuery(path string, data cmn.HexBytes, height int64, prove bool) (*ctype
// }
// ```
func ABCIInfo() (*ctypes.ResultABCIInfo, error) {
resInfo, err := proxyAppQuery.InfoSync(abci.RequestInfo{Version: version.Version})
resInfo, err := proxyAppQuery.InfoSync(proxy.RequestInfo)
if err != nil {
return nil, err
}

View File

@@ -31,6 +31,11 @@ import (
// "id": "",
// "result": {
// "node_info": {
// "protocol_version": {
// "p2p": "4",
// "block": "7",
// "app": "0"
// },
// "id": "53729852020041b956e86685e24394e0bee4373f",
// "listen_addr": "10.0.2.15:26656",
// "network": "test-chain-Y1OHx6",
@@ -38,10 +43,6 @@ import (
// "channels": "4020212223303800",
// "moniker": "ubuntu-xenial",
// "other": {
// "amino_version": "0.12.0",
// "p2p_version": "0.5.0",
// "consensus_version": "v1/0.2.2",
// "rpc_version": "0.7.0/3",
// "tx_index": "on",
// "rpc_addr": "tcp://0.0.0.0:26657"
// }

View File

@@ -1,5 +0,0 @@
package core
// a single integer is sufficient here
const Version = "3" // rpc routes for profiling, setting config

View File

@@ -173,8 +173,7 @@ func RecoverAndLogHandler(handler http.Handler, logger log.Logger) http.Handler
"Panic in RPC HTTP handler", "err", e, "stack",
string(debug.Stack()),
)
rww.WriteHeader(http.StatusInternalServerError)
WriteRPCResponseHTTP(rww, types.RPCInternalError("", e.(error)))
WriteRPCResponseHTTPError(rww, http.StatusInternalServerError, types.RPCInternalError("", e.(error)))
}
}

View File

@@ -1,7 +0,0 @@
package rpc
const Maj = "0"
const Min = "7"
const Fix = "0"
const Version = Maj + "." + Min + "." + Fix

16
scripts/authors.sh Executable file
View File

@@ -0,0 +1,16 @@
#! /bin/bash
# Usage:
# `./authors.sh`
# Print a list of all authors who have committed to develop since master.
#
# `./authors.sh <email address>`
# Lookup the email address on Github and print the associated username
author=$1
if [[ "$author" == "" ]]; then
git log master..develop | grep Author | sort | uniq
else
curl -s "https://api.github.com/search/users?q=$author+in%3Aemail&type=Users&utf8=%E2%9C%93" | jq .items[0].login
fi

View File

@@ -17,16 +17,15 @@ set BRANCH=master
sudo pkg update
sudo pkg upgrade -y
sudo pkg install -y gmake
sudo pkg install -y git
# get and unpack golang
curl -O https://storage.googleapis.com/golang/go1.10.freebsd-amd64.tar.gz
tar -xvf go1.10.freebsd-amd64.tar.gz
curl -O https://storage.googleapis.com/golang/go1.11.freebsd-amd64.tar.gz
tar -xvf go1.11.freebsd-amd64.tar.gz
# move go binary and add to path
mv go /usr/local
# move go folder and add go binary to path
sudo mv go /usr/local
set path=($path /usr/local/go/bin)
@@ -41,7 +40,7 @@ source ~/.tcshrc
# get the code and move into repo
set REPO=github.com/tendermint/tendermint
go get $REPO
cd $GOPATH/src/$REPO
cd "$GOPATH/src/$REPO"
# build & install master
git checkout $BRANCH

View File

@@ -14,27 +14,25 @@ REPO=github.com/tendermint/tendermint
BRANCH=master
sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt-get install -y make
# get and unpack golang
curl -O https://storage.googleapis.com/golang/go1.10.linux-amd64.tar.gz
tar -xvf go1.10.linux-amd64.tar.gz
curl -O https://storage.googleapis.com/golang/go1.11.linux-amd64.tar.gz
tar -xvf go1.11.linux-amd64.tar.gz
# move go binary and add to path
mv go /usr/local
# move go folder and add go binary to path
sudo mv go /usr/local
echo "export PATH=\$PATH:/usr/local/go/bin" >> ~/.profile
# create the goApps directory, set GOPATH, and put it on PATH
mkdir goApps
echo "export GOPATH=/root/goApps" >> ~/.profile
echo "export GOPATH=$HOME/goApps" >> ~/.profile
echo "export PATH=\$PATH:\$GOPATH/bin" >> ~/.profile
source ~/.profile
# get the code and move into repo
go get $REPO
cd $GOPATH/src/$REPO
cd "$GOPATH/src/$REPO"
# build & install
git checkout $BRANCH

View File

@@ -398,9 +398,13 @@ func updateState(
lastHeightParamsChanged = header.Height + 1
}
// TODO: allow app to upgrade version
nextVersion := state.Version
// NOTE: the AppHash has not been populated.
// It will be filled on state.Save.
return State{
Version: nextVersion,
ChainID: state.ChainID,
LastBlockHeight: header.Height,
LastBlockTotalTx: state.LastBlockTotalTx + header.NumTxs,

View File

@@ -8,6 +8,7 @@ import (
"github.com/tendermint/tendermint/types"
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/tendermint/tendermint/version"
)
// database keys
@@ -17,6 +18,29 @@ var (
//-----------------------------------------------------------------------------
// Version is for versioning the State.
// It holds the Block and App version needed for making blocks,
// and the software version to support upgrades to the format of
// the State as stored on disk.
type Version struct {
Consensus version.Consensus
Software string
}
// initStateVersion sets the Consensus.Block and Software versions,
// but leaves the Consensus.App version blank.
// The Consensus.App version will be set during the Handshake, once
// we hear from the app what protocol version it is running.
var initStateVersion = Version{
Consensus: version.Consensus{
Block: version.BlockProtocol,
App: 0,
},
Software: version.TMCoreSemVer,
}
//-----------------------------------------------------------------------------
// State is a short description of the latest committed block of the Tendermint consensus.
// It keeps all information necessary to validate new blocks,
// including the last validator set and the consensus params.
@@ -25,6 +49,8 @@ var (
// Instead, use state.Copy() or state.NextState(...).
// NOTE: not goroutine-safe.
type State struct {
Version Version
// immutable
ChainID string
@@ -59,6 +85,7 @@ type State struct {
// Copy makes a copy of the State for mutating.
func (state State) Copy() State {
return State{
Version: state.Version,
ChainID: state.ChainID,
LastBlockHeight: state.LastBlockHeight,
@@ -101,7 +128,7 @@ func (state State) IsEmpty() bool {
// MakeBlock builds a block from the current state with the given txs, commit,
// and evidence. Note it also takes a proposerAddress because the state does not
// track rounds, and hence doesn't know the correct proposer. TODO: alleviate this!
// track rounds, and hence does not know the correct proposer. TODO: fix this!
func (state State) MakeBlock(
height int64,
txs []types.Tx,
@@ -113,28 +140,22 @@ func (state State) MakeBlock(
// Build base block with block data.
block := types.MakeBlock(height, txs, commit, evidence)
// Fill rest of header with state data.
block.ChainID = state.ChainID
// Set time
// Set time.
var timestamp time.Time
if height == 1 {
block.Time = state.LastBlockTime // genesis time
timestamp = state.LastBlockTime // genesis time
} else {
block.Time = MedianTime(commit, state.LastValidators)
timestamp = MedianTime(commit, state.LastValidators)
}
block.LastBlockID = state.LastBlockID
block.TotalTxs = state.LastBlockTotalTx + block.NumTxs
block.ValidatorsHash = state.Validators.Hash()
block.NextValidatorsHash = state.NextValidators.Hash()
block.ConsensusHash = state.ConsensusParams.Hash()
block.AppHash = state.AppHash
block.LastResultsHash = state.LastResultsHash
// NOTE: we can't use the state.Validators because we don't
// IncrementAccum for rounds there.
block.ProposerAddress = proposerAddress
// Fill rest of header with state data.
block.Header.Populate(
state.Version.Consensus, state.ChainID,
timestamp, state.LastBlockID, state.LastBlockTotalTx+block.NumTxs,
state.Validators.Hash(), state.NextValidators.Hash(),
state.ConsensusParams.Hash(), state.AppHash, state.LastResultsHash,
proposerAddress,
)
return block, block.MakePartSet(types.BlockPartSizeBytes)
}
@@ -194,7 +215,6 @@ func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) {
return State{}, fmt.Errorf("Error in genesis file: %v", err)
}
// Make validators slice
var validatorSet, nextValidatorSet *types.ValidatorSet
if genDoc.Validators == nil {
validatorSet = types.NewValidatorSet(nil)
@@ -202,22 +222,14 @@ func MakeGenesisState(genDoc *types.GenesisDoc) (State, error) {
} else {
validators := make([]*types.Validator, len(genDoc.Validators))
for i, val := range genDoc.Validators {
pubKey := val.PubKey
address := pubKey.Address()
// Make validator
validators[i] = &types.Validator{
Address: address,
PubKey: pubKey,
VotingPower: val.Power,
}
validators[i] = types.NewValidator(val.PubKey, val.Power)
}
validatorSet = types.NewValidatorSet(validators)
nextValidatorSet = types.NewValidatorSet(validators).CopyIncrementAccum(1)
}
return State{
Version: initStateVersion,
ChainID: genDoc.ChainID,
LastBlockHeight: 0,

View File

@@ -319,9 +319,11 @@ func TestStateMakeBlock(t *testing.T) {
defer tearDown(t)
proposerAddress := state.Validators.GetProposer().Address
stateVersion := state.Version.Consensus
block := makeBlock(state, 2)
// test we set proposer address
// test we set some fields
assert.Equal(t, stateVersion, block.Version)
assert.Equal(t, proposerAddress, block.ProposerAddress)
}

View File

@@ -20,6 +20,13 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
}
// Validate basic info.
if block.Version != state.Version.Consensus {
return fmt.Errorf(
"Wrong Block.Header.Version. Expected %v, got %v",
state.Version.Consensus,
block.Version,
)
}
if block.ChainID != state.ChainID {
return fmt.Errorf(
"Wrong Block.Header.ChainID. Expected %v, got %v",

View File

@@ -5,6 +5,7 @@ import (
"time"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/libs/log"
@@ -26,13 +27,20 @@ func TestValidateBlockHeader(t *testing.T) {
err := blockExec.ValidateBlock(state, block)
require.NoError(t, err)
// some bad values
wrongHash := tmhash.Sum([]byte("this hash is wrong"))
wrongVersion1 := state.Version.Consensus
wrongVersion1.Block += 1
wrongVersion2 := state.Version.Consensus
wrongVersion2.App += 1
// Manipulation of any header field causes failure.
testCases := []struct {
name string
malleateBlock func(block *types.Block)
}{
{"Version wrong1", func(block *types.Block) { block.Version = wrongVersion1 }},
{"Version wrong2", func(block *types.Block) { block.Version = wrongVersion2 }},
{"ChainID wrong", func(block *types.Block) { block.ChainID = "not-the-real-one" }},
{"Height wrong", func(block *types.Block) { block.Height += 10 }},
{"Time wrong", func(block *types.Block) { block.Time = block.Time.Add(-time.Second * 3600 * 24) }},

View File

@@ -140,14 +140,22 @@ func (n *Network) NodeIsOnline(name string) {
// NewNode is called when the new node is added to the monitor.
func (n *Network) NewNode(name string) {
n.mu.Lock()
defer n.mu.Unlock()
n.NumNodesMonitored++
n.NumNodesMonitoredOnline++
n.updateHealth()
}
// NodeDeleted is called when the node is deleted from under the monitor.
func (n *Network) NodeDeleted(name string) {
n.mu.Lock()
defer n.mu.Unlock()
n.NumNodesMonitored--
n.NumNodesMonitoredOnline--
n.updateHealth()
}
func (n *Network) updateHealth() {

View File

@@ -10,11 +10,12 @@ import (
"github.com/tendermint/tendermint/crypto/merkle"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/version"
)
const (
// MaxHeaderBytes is a maximum header size (including amino overhead).
MaxHeaderBytes int64 = 511
MaxHeaderBytes int64 = 537
// MaxAminoOverheadForBlock - maximum amino overhead to encode a block (up to
// MaxBlockSizeBytes in size) not including it's parts except Data.
@@ -27,7 +28,6 @@ const (
)
// Block defines the atomic unit of a Tendermint blockchain.
// TODO: add Version byte
type Block struct {
mtx sync.Mutex
Header `json:"header"`
@@ -149,7 +149,7 @@ func (b *Block) MakePartSet(partSize int) *PartSet {
// We prefix the byte length, so that unmarshaling
// can easily happen via a reader.
bz, err := cdc.MarshalBinary(b)
bz, err := cdc.MarshalBinaryLengthPrefixed(b)
if err != nil {
panic(err)
}
@@ -257,17 +257,19 @@ func MaxDataBytesUnknownEvidence(maxBytes int64, valsCount int) int64 {
//-----------------------------------------------------------------------------
// Header defines the structure of a Tendermint block header
// TODO: limit header size
// NOTE: changes to the Header should be duplicated in the abci Header
// and in /docs/spec/blockchain/blockchain.md
// Header defines the structure of a Tendermint block header.
// NOTE: changes to the Header should be duplicated in:
// - header.Hash()
// - abci.Header
// - /docs/spec/blockchain/blockchain.md
type Header struct {
// basic block info
ChainID string `json:"chain_id"`
Height int64 `json:"height"`
Time time.Time `json:"time"`
NumTxs int64 `json:"num_txs"`
TotalTxs int64 `json:"total_txs"`
Version version.Consensus `json:"version"`
ChainID string `json:"chain_id"`
Height int64 `json:"height"`
Time time.Time `json:"time"`
NumTxs int64 `json:"num_txs"`
TotalTxs int64 `json:"total_txs"`
// prev block info
LastBlockID BlockID `json:"last_block_id"`
@@ -288,7 +290,31 @@ type Header struct {
ProposerAddress Address `json:"proposer_address"` // original proposer of the block
}
// Populate the Header with state-derived data.
// Call this after MakeBlock to complete the Header.
func (h *Header) Populate(
version version.Consensus, chainID string,
timestamp time.Time, lastBlockID BlockID, totalTxs int64,
valHash, nextValHash []byte,
consensusHash, appHash, lastResultsHash []byte,
proposerAddress Address,
) {
h.Version = version
h.ChainID = chainID
h.Time = timestamp
h.LastBlockID = lastBlockID
h.TotalTxs = totalTxs
h.ValidatorsHash = valHash
h.NextValidatorsHash = nextValHash
h.ConsensusHash = consensusHash
h.AppHash = appHash
h.LastResultsHash = lastResultsHash
h.ProposerAddress = proposerAddress
}
// Hash returns the hash of the header.
// It computes a Merkle tree from the header fields
// ordered as they appear in the Header.
// Returns nil if ValidatorHash is missing,
// since a Header is not valid unless there is
// a ValidatorsHash (corresponding to the validator set).
@@ -296,22 +322,23 @@ func (h *Header) Hash() cmn.HexBytes {
if h == nil || len(h.ValidatorsHash) == 0 {
return nil
}
return merkle.SimpleHashFromMap(map[string][]byte{
"ChainID": cdcEncode(h.ChainID),
"Height": cdcEncode(h.Height),
"Time": cdcEncode(h.Time),
"NumTxs": cdcEncode(h.NumTxs),
"TotalTxs": cdcEncode(h.TotalTxs),
"LastBlockID": cdcEncode(h.LastBlockID),
"LastCommit": cdcEncode(h.LastCommitHash),
"Data": cdcEncode(h.DataHash),
"Validators": cdcEncode(h.ValidatorsHash),
"NextValidators": cdcEncode(h.NextValidatorsHash),
"App": cdcEncode(h.AppHash),
"Consensus": cdcEncode(h.ConsensusHash),
"Results": cdcEncode(h.LastResultsHash),
"Evidence": cdcEncode(h.EvidenceHash),
"Proposer": cdcEncode(h.ProposerAddress),
return merkle.SimpleHashFromByteSlices([][]byte{
cdcEncode(h.Version),
cdcEncode(h.ChainID),
cdcEncode(h.Height),
cdcEncode(h.Time),
cdcEncode(h.NumTxs),
cdcEncode(h.TotalTxs),
cdcEncode(h.LastBlockID),
cdcEncode(h.LastCommitHash),
cdcEncode(h.DataHash),
cdcEncode(h.ValidatorsHash),
cdcEncode(h.NextValidatorsHash),
cdcEncode(h.ConsensusHash),
cdcEncode(h.AppHash),
cdcEncode(h.LastResultsHash),
cdcEncode(h.EvidenceHash),
cdcEncode(h.ProposerAddress),
})
}
@@ -321,6 +348,7 @@ func (h *Header) StringIndented(indent string) string {
return "nil-Header"
}
return fmt.Sprintf(`Header{
%s Version: %v
%s ChainID: %v
%s Height: %v
%s Time: %v
@@ -337,6 +365,7 @@ func (h *Header) StringIndented(indent string) string {
%s Evidence: %v
%s Proposer: %v
%s}#%v`,
indent, h.Version,
indent, h.ChainID,
indent, h.Height,
indent, h.Time,
@@ -538,6 +567,7 @@ func (sh SignedHeader) ValidateBasic(chainID string) error {
if sh.Commit == nil {
return errors.New("SignedHeader missing commit (precommit votes).")
}
// Check ChainID.
if sh.ChainID != chainID {
return fmt.Errorf("Header belongs to another chain '%s' not '%s'",
@@ -576,7 +606,6 @@ func (sh SignedHeader) StringIndented(indent string) string {
indent, sh.Header.StringIndented(indent+" "),
indent, sh.Commit.StringIndented(indent+" "),
indent)
return ""
}
//-----------------------------------------------------------------------------
@@ -660,7 +689,6 @@ func (data *EvidenceData) StringIndented(indent string) string {
%s}#%v`,
indent, strings.Join(evStrings, "\n"+indent+" "),
indent, data.hash)
return ""
}
//--------------------------------------------------------------------------------

View File

@@ -12,6 +12,7 @@ import (
"github.com/tendermint/tendermint/crypto/tmhash"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/version"
)
func TestMain(m *testing.M) {
@@ -241,10 +242,15 @@ func TestMaxHeaderBytes(t *testing.T) {
maxChainID += "𠜎"
}
// time is varint encoded so need to pick the max.
// year int, month Month, day, hour, min, sec, nsec int, loc *Location
timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC)
h := Header{
Version: version.Consensus{math.MaxInt64, math.MaxInt64},
ChainID: maxChainID,
Height: math.MaxInt64,
Time: time.Now().UTC(),
Time: timestamp,
NumTxs: math.MaxInt64,
TotalTxs: math.MaxInt64,
LastBlockID: makeBlockID(make([]byte, tmhash.Size), math.MaxInt64, make([]byte, tmhash.Size)),
@@ -259,7 +265,7 @@ func TestMaxHeaderBytes(t *testing.T) {
ProposerAddress: tmhash.Sum([]byte("proposer_address")),
}
bz, err := cdc.MarshalBinary(h)
bz, err := cdc.MarshalBinaryLengthPrefixed(h)
require.NoError(t, err)
assert.EqualValues(t, MaxHeaderBytes, len(bz))
@@ -286,9 +292,9 @@ func TestBlockMaxDataBytes(t *testing.T) {
}{
0: {-10, 1, 0, true, 0},
1: {10, 1, 0, true, 0},
2: {721, 1, 0, true, 0},
3: {722, 1, 0, false, 0},
4: {723, 1, 0, false, 1},
2: {750, 1, 0, true, 0},
3: {751, 1, 0, false, 0},
4: {752, 1, 0, false, 1},
}
for i, tc := range testCases {
@@ -314,9 +320,9 @@ func TestBlockMaxDataBytesUnknownEvidence(t *testing.T) {
}{
0: {-10, 1, true, 0},
1: {10, 1, true, 0},
2: {801, 1, true, 0},
3: {802, 1, false, 0},
4: {803, 1, false, 1},
2: {833, 1, true, 0},
3: {834, 1, false, 0},
4: {835, 1, false, 1},
}
for i, tc := range testCases {

View File

@@ -23,10 +23,9 @@ type CanonicalPartSetHeader struct {
}
type CanonicalProposal struct {
Version uint64 `binary:"fixed64"`
Type SignedMsgType // type alias for byte
Height int64 `binary:"fixed64"`
Round int64 `binary:"fixed64"`
Type SignedMsgType // type alias for byte
POLRound int64 `binary:"fixed64"`
Timestamp time.Time
BlockPartsHeader CanonicalPartSetHeader
@@ -35,21 +34,19 @@ type CanonicalProposal struct {
}
type CanonicalVote struct {
Version uint64 `binary:"fixed64"`
Type SignedMsgType // type alias for byte
Height int64 `binary:"fixed64"`
Round int64 `binary:"fixed64"`
Type SignedMsgType // type alias for byte
Timestamp time.Time
BlockID CanonicalBlockID
ChainID string
}
type CanonicalHeartbeat struct {
Version uint64 `binary:"fixed64"`
Height int64 `binary:"fixed64"`
Round int `binary:"fixed64"`
Type byte
Sequence int `binary:"fixed64"`
Height int64 `binary:"fixed64"`
Round int `binary:"fixed64"`
Sequence int `binary:"fixed64"`
ValidatorAddress Address
ValidatorIndex int
ChainID string
@@ -74,10 +71,9 @@ func CanonicalizePartSetHeader(psh PartSetHeader) CanonicalPartSetHeader {
func CanonicalizeProposal(chainID string, proposal *Proposal) CanonicalProposal {
return CanonicalProposal{
Version: 0, // TODO
Type: ProposalType,
Height: proposal.Height,
Round: int64(proposal.Round), // cast int->int64 to make amino encode it fixed64 (does not work for int)
Type: ProposalType,
POLRound: int64(proposal.POLRound),
Timestamp: proposal.Timestamp,
BlockPartsHeader: CanonicalizePartSetHeader(proposal.BlockPartsHeader),
@@ -88,10 +84,9 @@ func CanonicalizeProposal(chainID string, proposal *Proposal) CanonicalProposal
func CanonicalizeVote(chainID string, vote *Vote) CanonicalVote {
return CanonicalVote{
Version: 0, // TODO
Type: vote.Type,
Height: vote.Height,
Round: int64(vote.Round), // cast int->int64 to make amino encode it fixed64 (does not work for int)
Type: vote.Type,
Timestamp: vote.Timestamp,
BlockID: CanonicalizeBlockID(vote.BlockID),
ChainID: chainID,
@@ -100,10 +95,9 @@ func CanonicalizeVote(chainID string, vote *Vote) CanonicalVote {
func CanonicalizeHeartbeat(chainID string, heartbeat *Heartbeat) CanonicalHeartbeat {
return CanonicalHeartbeat{
Version: 0, // TODO
Type: byte(HeartbeatType),
Height: heartbeat.Height,
Round: heartbeat.Round,
Type: byte(HeartbeatType),
Sequence: heartbeat.Sequence,
ValidatorAddress: heartbeat.ValidatorAddress,
ValidatorIndex: heartbeat.ValidatorIndex,

33
types/checked_ints.go Normal file
View File

@@ -0,0 +1,33 @@
package types
import (
"errors"
"math"
)
var ErrOverflowInt = errors.New("integer overflow")
type CheckedInt32 int32
type CheckedUint32 uint32
type CheckedInt64 int64
type CheckedUint64 int64
func (i32 CheckedInt32) CheckedAdd(otherI32 CheckedInt32) (CheckedInt32, error) {
if otherI32 > 0 && (i32 > math.MaxInt32-otherI32) {
return 0, ErrOverflowInt
} else if otherI32 < 0 && (i32 < math.MinInt32-otherI32) {
return 0, ErrOverflowInt
}
return i32 + otherI32, nil
}
func (i32 CheckedInt32) CheckedSub(otherI32 CheckedInt32) (CheckedInt32, error) {
if otherI32 > 0 && (i32 < math.MinInt32+otherI32) {
return 0, ErrOverflowInt
} else if otherI32 < 0 && (i32 > math.MaxInt32+otherI32) {
return 0, ErrOverflowInt
}
return i32 - otherI32, nil
}

View File

@@ -0,0 +1,61 @@
package types
import (
"math"
"testing"
"github.com/stretchr/testify/assert"
)
func TestCheckedInt32_CheckedAdd(t *testing.T) {
tcs := []struct {
val1 int32
val2 int32
sum int32
wantErr bool
}{
0: {math.MaxInt32, 1, 0, true},
1: {math.MinInt32, 1, math.MinInt32 + 1, false},
2: {math.MaxInt32, math.MaxInt32, 0, true},
3: {0, math.MaxInt32, math.MaxInt32, false},
4: {0, 1, 1, false},
5: {1, 1, 2, false},
}
for i, tc := range tcs {
v1 := CheckedInt32(tc.val1)
v2 := CheckedInt32(tc.val2)
sum, err := v1.CheckedAdd(v2)
if tc.wantErr {
assert.Error(t, err, "Should fail: %v", i)
assert.Zero(t, sum, "Got invalid sum for case %v", i)
} else {
assert.EqualValues(t, tc.sum, sum)
}
}
}
func TestCheckedInt32_CheckedSub(t *testing.T) {
tcs := []struct {
val1 int32
val2 int32
sum int32
wantErr bool
}{
0: {math.MaxInt32, math.MaxInt32, 0, false},
1: {math.MinInt32, 1, 0, true},
2: {math.MinInt32 + 1, 1, math.MinInt32, false},
3: {1, 1, 0, false},
4: {1, 2, -1, false},
}
for i, tc := range tcs {
v1 := CheckedInt32(tc.val1)
v2 := CheckedInt32(tc.val2)
sum, err := v1.CheckedSub(v2)
if tc.wantErr {
assert.Error(t, err, "Should fail: %v", i)
assert.Zero(t, sum, "Got invalid sum for case %v", i)
} else {
assert.EqualValues(t, tc.sum, sum, "failed: %v", i)
}
}
}

View File

@@ -14,7 +14,7 @@ import (
const (
// MaxEvidenceBytes is a maximum size of any evidence (including amino overhead).
MaxEvidenceBytes int64 = 440
MaxEvidenceBytes int64 = 444
)
// ErrEvidenceInvalid wraps a piece of evidence and the error denoting how or why it is invalid.
@@ -55,6 +55,7 @@ func (err *ErrEvidenceOverflow) Error() string {
type Evidence interface {
Height() int64 // height of the equivocation
Address() []byte // address of the equivocating validator
Bytes() []byte // bytes which compromise the evidence
Hash() []byte // hash of the evidence
Verify(chainID string, pubKey crypto.PubKey) error // verify the evidence
Equal(Evidence) bool // check equality of evidence
@@ -88,6 +89,8 @@ type DuplicateVoteEvidence struct {
VoteB *Vote
}
var _ Evidence = &DuplicateVoteEvidence{}
// String returns a string representation of the evidence.
func (dve *DuplicateVoteEvidence) String() string {
return fmt.Sprintf("VoteA: %v; VoteB: %v", dve.VoteA, dve.VoteB)
@@ -104,6 +107,11 @@ func (dve *DuplicateVoteEvidence) Address() []byte {
return dve.PubKey.Address()
}
// Hash returns the hash of the evidence.
func (dve *DuplicateVoteEvidence) Bytes() []byte {
return cdcEncode(dve)
}
// Hash returns the hash of the evidence.
func (dve *DuplicateVoteEvidence) Hash() []byte {
return tmhash.Sum(cdcEncode(dve))
@@ -172,6 +180,8 @@ type MockGoodEvidence struct {
Address_ []byte
}
var _ Evidence = &MockGoodEvidence{}
// UNSTABLE
func NewMockGoodEvidence(height int64, idx int, address []byte) MockGoodEvidence {
return MockGoodEvidence{height, address}
@@ -182,6 +192,9 @@ func (e MockGoodEvidence) Address() []byte { return e.Address_ }
func (e MockGoodEvidence) Hash() []byte {
return []byte(fmt.Sprintf("%d-%x", e.Height_, e.Address_))
}
func (e MockGoodEvidence) Bytes() []byte {
return []byte(fmt.Sprintf("%d-%x", e.Height_, e.Address_))
}
func (e MockGoodEvidence) Verify(chainID string, pubKey crypto.PubKey) error { return nil }
func (e MockGoodEvidence) Equal(ev Evidence) bool {
e2 := ev.(MockGoodEvidence)
@@ -216,18 +229,14 @@ type EvidenceList []Evidence
// Hash returns the simple merkle root hash of the EvidenceList.
func (evl EvidenceList) Hash() []byte {
// Recursive impl.
// Copied from crypto/merkle to avoid allocations
switch len(evl) {
case 0:
return nil
case 1:
return evl[0].Hash()
default:
left := EvidenceList(evl[:(len(evl)+1)/2]).Hash()
right := EvidenceList(evl[(len(evl)+1)/2:]).Hash()
return merkle.SimpleHashFromTwoHashes(left, right)
// These allocations are required because Evidence is not of type Bytes, and
// golang slices can't be typed cast. This shouldn't be a performance problem since
// the Evidence size is capped.
evidenceBzs := make([][]byte, len(evl))
for i := 0; i < len(evl); i++ {
evidenceBzs[i] = evl[i].Bytes()
}
return merkle.SimpleHashFromByteSlices(evidenceBzs)
}
func (evl EvidenceList) String() string {

View File

@@ -105,7 +105,7 @@ func TestMaxEvidenceBytes(t *testing.T) {
VoteB: makeVote(val, chainID, math.MaxInt64, math.MaxInt64, math.MaxInt64, math.MaxInt64, blockID2),
}
bz, err := cdc.MarshalBinary(ev)
bz, err := cdc.MarshalBinaryLengthPrefixed(ev)
require.NoError(t, err)
assert.EqualValues(t, MaxEvidenceBytes, len(bz))

View File

@@ -23,7 +23,7 @@ type Heartbeat struct {
// SignBytes returns the Heartbeat bytes for signing.
// It panics if the Heartbeat is nil.
func (heartbeat *Heartbeat) SignBytes(chainID string) []byte {
bz, err := cdc.MarshalBinary(CanonicalizeHeartbeat(chainID, heartbeat))
bz, err := cdc.MarshalBinaryLengthPrefixed(CanonicalizeHeartbeat(chainID, heartbeat))
if err != nil {
panic(err)
}

View File

@@ -39,7 +39,7 @@ func TestHeartbeatWriteSignBytes(t *testing.T) {
{
testHeartbeat := &Heartbeat{ValidatorIndex: 1, Height: 10, Round: 1}
signBytes := testHeartbeat.SignBytes(chainID)
expected, err := cdc.MarshalBinary(CanonicalizeHeartbeat(chainID, testHeartbeat))
expected, err := cdc.MarshalBinaryLengthPrefixed(CanonicalizeHeartbeat(chainID, testHeartbeat))
require.NoError(t, err)
require.Equal(t, expected, signBytes, "Got unexpected sign bytes for Heartbeat")
}
@@ -47,7 +47,7 @@ func TestHeartbeatWriteSignBytes(t *testing.T) {
{
testHeartbeat := &Heartbeat{}
signBytes := testHeartbeat.SignBytes(chainID)
expected, err := cdc.MarshalBinary(CanonicalizeHeartbeat(chainID, testHeartbeat))
expected, err := cdc.MarshalBinaryLengthPrefixed(CanonicalizeHeartbeat(chainID, testHeartbeat))
require.NoError(t, err)
require.Equal(t, expected, signBytes, "Got unexpected sign bytes for Heartbeat")
}

View File

@@ -2,7 +2,7 @@ package types
import (
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/merkle"
"github.com/tendermint/tendermint/crypto/tmhash"
cmn "github.com/tendermint/tendermint/libs/common"
)
@@ -80,13 +80,18 @@ func (params *ConsensusParams) Validate() error {
return nil
}
// Hash returns a merkle hash of the parameters to store in the block header
// Hash returns a hash of the parameters to store in the block header
// No Merkle tree here, only three values are hashed here
// thus benefit from saving space < drawbacks from proofs' overhead
// Revisit this function if new fields are added to ConsensusParams
func (params *ConsensusParams) Hash() []byte {
return merkle.SimpleHashFromMap(map[string][]byte{
"block_size_max_bytes": cdcEncode(params.BlockSize.MaxBytes),
"block_size_max_gas": cdcEncode(params.BlockSize.MaxGas),
"evidence_params_max_age": cdcEncode(params.EvidenceParams.MaxAge),
})
hasher := tmhash.New()
bz := cdcEncode(params)
if bz == nil {
panic("cannot fail to encode ConsensusParams")
}
hasher.Write(bz)
return hasher.Sum(nil)
}
// Update returns a copy of the params with updates from the non-zero fields of p2.

View File

@@ -53,6 +53,11 @@ func TestConsensusParamsHash(t *testing.T) {
makeParams(4, 2, 3),
makeParams(1, 4, 3),
makeParams(1, 2, 4),
makeParams(2, 5, 7),
makeParams(1, 7, 6),
makeParams(9, 5, 4),
makeParams(7, 8, 9),
makeParams(4, 6, 5),
}
hashes := make([][]byte, len(params))

View File

@@ -52,7 +52,7 @@ func (p *Proposal) String() string {
// SignBytes returns the Proposal bytes for signing
func (p *Proposal) SignBytes(chainID string) []byte {
bz, err := cdc.MarshalBinary(CanonicalizeProposal(chainID, p))
bz, err := cdc.MarshalBinaryLengthPrefixed(CanonicalizeProposal(chainID, p))
if err != nil {
panic(err)
}

View File

@@ -27,7 +27,7 @@ func TestProposalSignable(t *testing.T) {
chainID := "test_chain_id"
signBytes := testProposal.SignBytes(chainID)
expected, err := cdc.MarshalBinary(CanonicalizeProposal(chainID, testProposal))
expected, err := cdc.MarshalBinaryLengthPrefixed(CanonicalizeProposal(chainID, testProposal))
require.NoError(t, err)
require.Equal(t, expected, signBytes, "Got unexpected sign bytes for Proposal")
}
@@ -57,9 +57,9 @@ func TestProposalVerifySignature(t *testing.T) {
// serialize, deserialize and verify again....
newProp := new(Proposal)
bs, err := cdc.MarshalBinary(prop)
bs, err := cdc.MarshalBinaryLengthPrefixed(prop)
require.NoError(t, err)
err = cdc.UnmarshalBinary(bs, &newProp)
err = cdc.UnmarshalBinaryLengthPrefixed(bs, &newProp)
require.NoError(t, err)
// verify the transmitted proposal

View File

@@ -1,7 +1,19 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: types/proto3/block.proto
// source: block.proto
//nolint
/*
Package proto3 is a generated protocol buffer package.
It is generated from these files:
block.proto
It has these top-level messages:
PartSetHeader
BlockID
Header
Version
Timestamp
*/
package proto3
import proto "github.com/golang/protobuf/proto"
@@ -20,36 +32,14 @@ var _ = math.Inf
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type PartSetHeader struct {
Total int32 `protobuf:"zigzag32,1,opt,name=Total,proto3" json:"Total,omitempty"`
Hash []byte `protobuf:"bytes,2,opt,name=Hash,proto3" json:"Hash,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
Total int32 `protobuf:"zigzag32,1,opt,name=Total" json:"Total,omitempty"`
Hash []byte `protobuf:"bytes,2,opt,name=Hash,proto3" json:"Hash,omitempty"`
}
func (m *PartSetHeader) Reset() { *m = PartSetHeader{} }
func (m *PartSetHeader) String() string { return proto.CompactTextString(m) }
func (*PartSetHeader) ProtoMessage() {}
func (*PartSetHeader) Descriptor() ([]byte, []int) {
return fileDescriptor_block_c8c1dcbe91697ccd, []int{0}
}
func (m *PartSetHeader) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PartSetHeader.Unmarshal(m, b)
}
func (m *PartSetHeader) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_PartSetHeader.Marshal(b, m, deterministic)
}
func (dst *PartSetHeader) XXX_Merge(src proto.Message) {
xxx_messageInfo_PartSetHeader.Merge(dst, src)
}
func (m *PartSetHeader) XXX_Size() int {
return xxx_messageInfo_PartSetHeader.Size(m)
}
func (m *PartSetHeader) XXX_DiscardUnknown() {
xxx_messageInfo_PartSetHeader.DiscardUnknown(m)
}
var xxx_messageInfo_PartSetHeader proto.InternalMessageInfo
func (m *PartSetHeader) Reset() { *m = PartSetHeader{} }
func (m *PartSetHeader) String() string { return proto.CompactTextString(m) }
func (*PartSetHeader) ProtoMessage() {}
func (*PartSetHeader) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func (m *PartSetHeader) GetTotal() int32 {
if m != nil {
@@ -66,36 +56,14 @@ func (m *PartSetHeader) GetHash() []byte {
}
type BlockID struct {
Hash []byte `protobuf:"bytes,1,opt,name=Hash,proto3" json:"Hash,omitempty"`
PartsHeader *PartSetHeader `protobuf:"bytes,2,opt,name=PartsHeader,proto3" json:"PartsHeader,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
Hash []byte `protobuf:"bytes,1,opt,name=Hash,proto3" json:"Hash,omitempty"`
PartsHeader *PartSetHeader `protobuf:"bytes,2,opt,name=PartsHeader" json:"PartsHeader,omitempty"`
}
func (m *BlockID) Reset() { *m = BlockID{} }
func (m *BlockID) String() string { return proto.CompactTextString(m) }
func (*BlockID) ProtoMessage() {}
func (*BlockID) Descriptor() ([]byte, []int) {
return fileDescriptor_block_c8c1dcbe91697ccd, []int{1}
}
func (m *BlockID) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_BlockID.Unmarshal(m, b)
}
func (m *BlockID) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_BlockID.Marshal(b, m, deterministic)
}
func (dst *BlockID) XXX_Merge(src proto.Message) {
xxx_messageInfo_BlockID.Merge(dst, src)
}
func (m *BlockID) XXX_Size() int {
return xxx_messageInfo_BlockID.Size(m)
}
func (m *BlockID) XXX_DiscardUnknown() {
xxx_messageInfo_BlockID.DiscardUnknown(m)
}
var xxx_messageInfo_BlockID proto.InternalMessageInfo
func (m *BlockID) Reset() { *m = BlockID{} }
func (m *BlockID) String() string { return proto.CompactTextString(m) }
func (*BlockID) ProtoMessage() {}
func (*BlockID) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
func (m *BlockID) GetHash() []byte {
if m != nil {
@@ -113,52 +81,39 @@ func (m *BlockID) GetPartsHeader() *PartSetHeader {
type Header struct {
// basic block info
ChainID string `protobuf:"bytes,1,opt,name=ChainID,proto3" json:"ChainID,omitempty"`
Height int64 `protobuf:"zigzag64,2,opt,name=Height,proto3" json:"Height,omitempty"`
Time *Timestamp `protobuf:"bytes,3,opt,name=Time,proto3" json:"Time,omitempty"`
NumTxs int64 `protobuf:"zigzag64,4,opt,name=NumTxs,proto3" json:"NumTxs,omitempty"`
TotalTxs int64 `protobuf:"zigzag64,5,opt,name=TotalTxs,proto3" json:"TotalTxs,omitempty"`
Version *Version `protobuf:"bytes,1,opt,name=Version" json:"Version,omitempty"`
ChainID string `protobuf:"bytes,2,opt,name=ChainID" json:"ChainID,omitempty"`
Height int64 `protobuf:"zigzag64,3,opt,name=Height" json:"Height,omitempty"`
Time *Timestamp `protobuf:"bytes,4,opt,name=Time" json:"Time,omitempty"`
NumTxs int64 `protobuf:"zigzag64,5,opt,name=NumTxs" json:"NumTxs,omitempty"`
TotalTxs int64 `protobuf:"zigzag64,6,opt,name=TotalTxs" json:"TotalTxs,omitempty"`
// prev block info
LastBlockID *BlockID `protobuf:"bytes,6,opt,name=LastBlockID,proto3" json:"LastBlockID,omitempty"`
LastBlockID *BlockID `protobuf:"bytes,7,opt,name=LastBlockID" json:"LastBlockID,omitempty"`
// hashes of block data
LastCommitHash []byte `protobuf:"bytes,7,opt,name=LastCommitHash,proto3" json:"LastCommitHash,omitempty"`
DataHash []byte `protobuf:"bytes,8,opt,name=DataHash,proto3" json:"DataHash,omitempty"`
LastCommitHash []byte `protobuf:"bytes,8,opt,name=LastCommitHash,proto3" json:"LastCommitHash,omitempty"`
DataHash []byte `protobuf:"bytes,9,opt,name=DataHash,proto3" json:"DataHash,omitempty"`
// hashes from the app output from the prev block
ValidatorsHash []byte `protobuf:"bytes,9,opt,name=ValidatorsHash,proto3" json:"ValidatorsHash,omitempty"`
ConsensusHash []byte `protobuf:"bytes,10,opt,name=ConsensusHash,proto3" json:"ConsensusHash,omitempty"`
AppHash []byte `protobuf:"bytes,11,opt,name=AppHash,proto3" json:"AppHash,omitempty"`
LastResultsHash []byte `protobuf:"bytes,12,opt,name=LastResultsHash,proto3" json:"LastResultsHash,omitempty"`
ValidatorsHash []byte `protobuf:"bytes,10,opt,name=ValidatorsHash,proto3" json:"ValidatorsHash,omitempty"`
NextValidatorsHash []byte `protobuf:"bytes,11,opt,name=NextValidatorsHash,proto3" json:"NextValidatorsHash,omitempty"`
ConsensusHash []byte `protobuf:"bytes,12,opt,name=ConsensusHash,proto3" json:"ConsensusHash,omitempty"`
AppHash []byte `protobuf:"bytes,13,opt,name=AppHash,proto3" json:"AppHash,omitempty"`
LastResultsHash []byte `protobuf:"bytes,14,opt,name=LastResultsHash,proto3" json:"LastResultsHash,omitempty"`
// consensus info
EvidenceHash []byte `protobuf:"bytes,13,opt,name=EvidenceHash,proto3" json:"EvidenceHash,omitempty"`
ProposerAddress []byte `protobuf:"bytes,14,opt,name=ProposerAddress,proto3" json:"ProposerAddress,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
EvidenceHash []byte `protobuf:"bytes,15,opt,name=EvidenceHash,proto3" json:"EvidenceHash,omitempty"`
ProposerAddress []byte `protobuf:"bytes,16,opt,name=ProposerAddress,proto3" json:"ProposerAddress,omitempty"`
}
func (m *Header) Reset() { *m = Header{} }
func (m *Header) String() string { return proto.CompactTextString(m) }
func (*Header) ProtoMessage() {}
func (*Header) Descriptor() ([]byte, []int) {
return fileDescriptor_block_c8c1dcbe91697ccd, []int{2}
}
func (m *Header) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Header.Unmarshal(m, b)
}
func (m *Header) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Header.Marshal(b, m, deterministic)
}
func (dst *Header) XXX_Merge(src proto.Message) {
xxx_messageInfo_Header.Merge(dst, src)
}
func (m *Header) XXX_Size() int {
return xxx_messageInfo_Header.Size(m)
}
func (m *Header) XXX_DiscardUnknown() {
xxx_messageInfo_Header.DiscardUnknown(m)
}
func (m *Header) Reset() { *m = Header{} }
func (m *Header) String() string { return proto.CompactTextString(m) }
func (*Header) ProtoMessage() {}
func (*Header) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
var xxx_messageInfo_Header proto.InternalMessageInfo
func (m *Header) GetVersion() *Version {
if m != nil {
return m.Version
}
return nil
}
func (m *Header) GetChainID() string {
if m != nil {
@@ -223,6 +178,13 @@ func (m *Header) GetValidatorsHash() []byte {
return nil
}
func (m *Header) GetNextValidatorsHash() []byte {
if m != nil {
return m.NextValidatorsHash
}
return nil
}
func (m *Header) GetConsensusHash() []byte {
if m != nil {
return m.ConsensusHash
@@ -258,41 +220,44 @@ func (m *Header) GetProposerAddress() []byte {
return nil
}
// Timestamp wraps how amino encodes time. Note that this is different from the protobuf well-known type
// protobuf/timestamp.proto in the sense that there seconds and nanos are varint encoded. See:
type Version struct {
Block uint64 `protobuf:"varint,1,opt,name=Block" json:"Block,omitempty"`
App uint64 `protobuf:"varint,2,opt,name=App" json:"App,omitempty"`
}
func (m *Version) Reset() { *m = Version{} }
func (m *Version) String() string { return proto.CompactTextString(m) }
func (*Version) ProtoMessage() {}
func (*Version) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
func (m *Version) GetBlock() uint64 {
if m != nil {
return m.Block
}
return 0
}
func (m *Version) GetApp() uint64 {
if m != nil {
return m.App
}
return 0
}
// Timestamp wraps how amino encodes time.
// This is the protobuf well-known type protobuf/timestamp.proto
// See:
// https://github.com/google/protobuf/blob/d2980062c859649523d5fd51d6b55ab310e47482/src/google/protobuf/timestamp.proto#L123-L135
// Also nanos do not get skipped if they are zero in amino.
// NOTE/XXX: nanos do not get skipped if they are zero in amino.
type Timestamp struct {
Seconds int64 `protobuf:"fixed64,1,opt,name=seconds,proto3" json:"seconds,omitempty"`
Nanos int32 `protobuf:"fixed32,2,opt,name=nanos,proto3" json:"nanos,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
Seconds int64 `protobuf:"varint,1,opt,name=seconds" json:"seconds,omitempty"`
Nanos int32 `protobuf:"varint,2,opt,name=nanos" json:"nanos,omitempty"`
}
func (m *Timestamp) Reset() { *m = Timestamp{} }
func (m *Timestamp) String() string { return proto.CompactTextString(m) }
func (*Timestamp) ProtoMessage() {}
func (*Timestamp) Descriptor() ([]byte, []int) {
return fileDescriptor_block_c8c1dcbe91697ccd, []int{3}
}
func (m *Timestamp) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Timestamp.Unmarshal(m, b)
}
func (m *Timestamp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Timestamp.Marshal(b, m, deterministic)
}
func (dst *Timestamp) XXX_Merge(src proto.Message) {
xxx_messageInfo_Timestamp.Merge(dst, src)
}
func (m *Timestamp) XXX_Size() int {
return xxx_messageInfo_Timestamp.Size(m)
}
func (m *Timestamp) XXX_DiscardUnknown() {
xxx_messageInfo_Timestamp.DiscardUnknown(m)
}
var xxx_messageInfo_Timestamp proto.InternalMessageInfo
func (m *Timestamp) Reset() { *m = Timestamp{} }
func (m *Timestamp) String() string { return proto.CompactTextString(m) }
func (*Timestamp) ProtoMessage() {}
func (*Timestamp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
func (m *Timestamp) GetSeconds() int64 {
if m != nil {
@@ -312,36 +277,40 @@ func init() {
proto.RegisterType((*PartSetHeader)(nil), "proto3.PartSetHeader")
proto.RegisterType((*BlockID)(nil), "proto3.BlockID")
proto.RegisterType((*Header)(nil), "proto3.Header")
proto.RegisterType((*Version)(nil), "proto3.Version")
proto.RegisterType((*Timestamp)(nil), "proto3.Timestamp")
}
func init() { proto.RegisterFile("types/proto3/block.proto", fileDescriptor_block_c8c1dcbe91697ccd) }
func init() { proto.RegisterFile("block.proto", fileDescriptor0) }
var fileDescriptor_block_c8c1dcbe91697ccd = []byte{
// 395 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x5c, 0x52, 0x4b, 0x8b, 0xdb, 0x30,
0x10, 0xc6, 0xcd, 0x7b, 0x9c, 0x47, 0x23, 0xda, 0x22, 0x7a, 0x0a, 0xa6, 0x2d, 0x39, 0x25, 0xb4,
0x39, 0x94, 0xd2, 0x53, 0x9a, 0x14, 0x12, 0x28, 0x25, 0x68, 0x43, 0xee, 0x4a, 0x2c, 0x36, 0x66,
0x6d, 0xcb, 0x78, 0x94, 0x65, 0xf7, 0x3f, 0xef, 0x8f, 0x58, 0x34, 0xb2, 0xbd, 0x71, 0x6e, 0xfe,
0x1e, 0xfa, 0x3e, 0x79, 0x46, 0xc0, 0xcd, 0x73, 0xa6, 0x70, 0x9e, 0xe5, 0xda, 0xe8, 0xc5, 0xfc,
0x18, 0xeb, 0xd3, 0xc3, 0x8c, 0x00, 0x6b, 0x3b, 0x2e, 0xf8, 0x05, 0x83, 0x9d, 0xcc, 0xcd, 0x9d,
0x32, 0x1b, 0x25, 0x43, 0x95, 0xb3, 0x0f, 0xd0, 0xda, 0x6b, 0x23, 0x63, 0xee, 0x4d, 0xbc, 0xe9,
0x58, 0x38, 0xc0, 0x18, 0x34, 0x37, 0x12, 0xcf, 0xfc, 0xdd, 0xc4, 0x9b, 0xf6, 0x05, 0x7d, 0x07,
0x07, 0xe8, 0xfc, 0xb1, 0x89, 0xdb, 0x75, 0x25, 0x7b, 0x6f, 0x32, 0xfb, 0x09, 0xbe, 0x4d, 0x46,
0x97, 0x4b, 0x27, 0xfd, 0x1f, 0x1f, 0x5d, 0xfd, 0x62, 0x56, 0x2b, 0x15, 0xd7, 0xce, 0xe0, 0xa5,
0x01, 0xed, 0xe2, 0x32, 0x1c, 0x3a, 0xab, 0xb3, 0x8c, 0xd2, 0xed, 0x9a, 0xa2, 0x7b, 0xa2, 0x84,
0xec, 0x93, 0xf5, 0x44, 0xf7, 0x67, 0x43, 0xc1, 0x4c, 0x14, 0x88, 0x7d, 0x85, 0xe6, 0x3e, 0x4a,
0x14, 0x6f, 0x50, 0xdd, 0xb8, 0xac, 0xb3, 0x1c, 0x1a, 0x99, 0x64, 0x82, 0x64, 0x7b, 0xfc, 0xff,
0x25, 0xd9, 0x3f, 0x21, 0x6f, 0xba, 0xe3, 0x0e, 0xb1, 0xcf, 0xd0, 0xa5, 0x1f, 0xb6, 0x4a, 0x8b,
0x94, 0x0a, 0xb3, 0xef, 0xe0, 0xff, 0x93, 0x68, 0x8a, 0x7f, 0xe6, 0x6d, 0x6a, 0x18, 0x95, 0x0d,
0x05, 0x2d, 0xae, 0x3d, 0xec, 0x1b, 0x0c, 0x2d, 0x5c, 0xe9, 0x24, 0x89, 0x0c, 0x4d, 0xa8, 0x43,
0x13, 0xba, 0x61, 0x6d, 0xed, 0x5a, 0x1a, 0x49, 0x8e, 0x2e, 0x39, 0x2a, 0x6c, 0x33, 0x0e, 0x32,
0x8e, 0x42, 0x69, 0x74, 0x8e, 0xe4, 0xe8, 0xb9, 0x8c, 0x3a, 0xcb, 0xbe, 0xc0, 0x60, 0xa5, 0x53,
0x54, 0x29, 0x5e, 0x9c, 0x0d, 0xc8, 0x56, 0x27, 0xed, 0x44, 0x97, 0x59, 0x46, 0xba, 0x4f, 0x7a,
0x09, 0xd9, 0x14, 0x46, 0xf6, 0x56, 0x42, 0xe1, 0x25, 0x36, 0x2e, 0xa1, 0x4f, 0x8e, 0x5b, 0x9a,
0x05, 0xd0, 0xff, 0xfb, 0x18, 0x85, 0x2a, 0x3d, 0x29, 0xb2, 0x0d, 0xc8, 0x56, 0xe3, 0x6c, 0xda,
0x2e, 0xd7, 0x99, 0x46, 0x95, 0x2f, 0xc3, 0x30, 0x57, 0x88, 0x7c, 0xe8, 0xd2, 0x6e, 0xe8, 0xe0,
0x37, 0xf4, 0xaa, 0xed, 0xd8, 0xeb, 0xa1, 0x3a, 0xe9, 0x34, 0x44, 0x5a, 0xf8, 0x7b, 0x51, 0x42,
0xfb, 0x2e, 0x53, 0x99, 0x6a, 0xa4, 0x7d, 0x8f, 0x84, 0x03, 0xc7, 0xe2, 0x19, 0xbf, 0x06, 0x00,
0x00, 0xff, 0xff, 0xde, 0x29, 0x34, 0x75, 0xe9, 0x02, 0x00, 0x00,
var fileDescriptor0 = []byte{
// 443 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x53, 0xcd, 0x6a, 0xdb, 0x40,
0x10, 0x46, 0xb5, 0x6c, 0xc7, 0x23, 0x3b, 0x4e, 0x86, 0xb6, 0x88, 0x9e, 0x8c, 0x68, 0x8b, 0x7b,
0x31, 0x24, 0x39, 0x94, 0xd2, 0x93, 0x6b, 0x17, 0x12, 0x28, 0x21, 0x6c, 0x8d, 0xef, 0x1b, 0x6b,
0xa9, 0x45, 0x2d, 0xad, 0xd0, 0xac, 0x4b, 0xde, 0xb0, 0xaf, 0x55, 0x66, 0x56, 0x52, 0x23, 0x93,
0x93, 0xf7, 0xfb, 0x99, 0x6f, 0x76, 0xc7, 0x23, 0x88, 0x1e, 0x0f, 0x76, 0xf7, 0x7b, 0x51, 0x56,
0xd6, 0x59, 0x1c, 0xc8, 0xcf, 0x4d, 0xf2, 0x05, 0x26, 0x0f, 0xba, 0x72, 0x3f, 0x8d, 0xbb, 0x35,
0x3a, 0x35, 0x15, 0xbe, 0x86, 0xfe, 0xc6, 0x3a, 0x7d, 0x88, 0x83, 0x59, 0x30, 0xbf, 0x54, 0x1e,
0x20, 0x42, 0x78, 0xab, 0x69, 0x1f, 0xbf, 0x9a, 0x05, 0xf3, 0xb1, 0x92, 0x73, 0xb2, 0x85, 0xe1,
0x37, 0x4e, 0xbc, 0x5b, 0xb7, 0x72, 0xf0, 0x5f, 0xc6, 0xcf, 0x10, 0x71, 0x32, 0xf9, 0x5c, 0xa9,
0x8c, 0xae, 0xdf, 0xf8, 0xf6, 0x37, 0x8b, 0x4e, 0x53, 0xf5, 0xdc, 0x99, 0xfc, 0x0d, 0x61, 0x50,
0x5f, 0xe6, 0x13, 0x0c, 0xb7, 0xa6, 0xa2, 0xcc, 0x16, 0x12, 0x1d, 0x5d, 0x4f, 0x9b, 0xfa, 0x9a,
0x56, 0x8d, 0x8e, 0x31, 0x0c, 0x57, 0x7b, 0x9d, 0x15, 0x77, 0x6b, 0x69, 0x35, 0x52, 0x0d, 0xc4,
0xb7, 0x1c, 0x97, 0xfd, 0xda, 0xbb, 0xb8, 0x37, 0x0b, 0xe6, 0xa8, 0x6a, 0x84, 0x1f, 0x20, 0xdc,
0x64, 0xb9, 0x89, 0x43, 0x49, 0xbe, 0x6c, 0x92, 0x99, 0x23, 0xa7, 0xf3, 0x52, 0x89, 0xcc, 0xe5,
0xf7, 0xc7, 0x7c, 0xf3, 0x44, 0x71, 0xdf, 0x97, 0x7b, 0x84, 0xef, 0xe0, 0x4c, 0x66, 0xc3, 0xca,
0x40, 0x94, 0x16, 0xe3, 0x15, 0x44, 0x3f, 0x34, 0xb9, 0x7a, 0x3c, 0xf1, 0xb0, 0x7b, 0xf7, 0x9a,
0x56, 0xcf, 0x3d, 0xf8, 0x11, 0xce, 0x19, 0xae, 0x6c, 0x9e, 0x67, 0x4e, 0x86, 0x79, 0x26, 0xc3,
0x3c, 0x61, 0xb9, 0xed, 0x5a, 0x3b, 0x2d, 0x8e, 0x91, 0x38, 0x5a, 0xcc, 0x19, 0x5b, 0x7d, 0xc8,
0x52, 0xed, 0x6c, 0x45, 0xe2, 0x00, 0x9f, 0xd1, 0x65, 0x71, 0x01, 0x78, 0x6f, 0x9e, 0xdc, 0x89,
0x37, 0x12, 0xef, 0x0b, 0x0a, 0xbe, 0x87, 0xc9, 0xca, 0x16, 0x64, 0x0a, 0x3a, 0x7a, 0xeb, 0x58,
0xac, 0x5d, 0x92, 0xff, 0x81, 0x65, 0x59, 0x8a, 0x3e, 0x11, 0xbd, 0x81, 0x38, 0x87, 0x29, 0xbf,
0x42, 0x19, 0x3a, 0x1e, 0x9c, 0x4f, 0x38, 0x17, 0xc7, 0x29, 0x8d, 0x09, 0x8c, 0xbf, 0xff, 0xc9,
0x52, 0x53, 0xec, 0x8c, 0xd8, 0xa6, 0x62, 0xeb, 0x70, 0x9c, 0xf6, 0x50, 0xd9, 0xd2, 0x92, 0xa9,
0x96, 0x69, 0x5a, 0x19, 0xa2, 0xf8, 0xc2, 0xa7, 0x9d, 0xd0, 0xc9, 0x55, 0xbb, 0x3e, 0xbc, 0xd6,
0x32, 0x69, 0xd9, 0xa3, 0x50, 0x79, 0x80, 0x17, 0xd0, 0x5b, 0x96, 0xa5, 0x2c, 0x4c, 0xa8, 0xf8,
0x98, 0x7c, 0x85, 0x51, 0xbb, 0x00, 0xfc, 0x22, 0x32, 0x3b, 0x5b, 0xa4, 0x24, 0x65, 0x3d, 0xd5,
0x40, 0x8e, 0x2b, 0x74, 0x61, 0x49, 0x4a, 0xfb, 0xca, 0x83, 0xc7, 0xfa, 0xa3, 0xfa, 0x17, 0x00,
0x00, 0xff, 0xff, 0x8f, 0x82, 0xc0, 0x0c, 0x6a, 0x03, 0x00, 0x00,
}

View File

@@ -15,36 +15,43 @@ message BlockID {
message Header {
// basic block info
string ChainID = 1;
sint64 Height = 2;
Timestamp Time = 3;
sint64 NumTxs = 4;
sint64 TotalTxs = 5;
Version Version = 1;
string ChainID = 2;
sint64 Height = 3;
Timestamp Time = 4;
sint64 NumTxs = 5;
sint64 TotalTxs = 6;
// prev block info
BlockID LastBlockID = 6;
BlockID LastBlockID = 7;
// hashes of block data
bytes LastCommitHash = 7; // commit from validators from the last block
bytes DataHash = 8; // transactions
bytes LastCommitHash = 8; // commit from validators from the last block
bytes DataHash = 9; // transactions
// hashes from the app output from the prev block
bytes ValidatorsHash = 9; // validators for the current block
bytes NextValidatorsHash = 10; // validators for the next block
bytes ConsensusHash = 11; // consensus params for current block
bytes AppHash = 12; // state after txs from the previous block
bytes LastResultsHash = 13; // root hash of all results from the txs from the previous block
bytes ValidatorsHash = 10; // validators for the current block
bytes NextValidatorsHash = 11; // validators for the next block
bytes ConsensusHash = 12; // consensus params for current block
bytes AppHash = 13; // state after txs from the previous block
bytes LastResultsHash = 14; // root hash of all results from the txs from the previous block
// consensus info
bytes EvidenceHash = 14; // evidence included in the block
bytes ProposerAddress = 15; // original proposer of the block
bytes EvidenceHash = 15; // evidence included in the block
bytes ProposerAddress = 16; // original proposer of the block
}
// Timestamp wraps how amino encodes time. Note that this is different from the protobuf well-known type
// protobuf/timestamp.proto in the sense that there seconds and nanos are varint encoded. See:
// https://github.com/google/protobuf/blob/d2980062c859649523d5fd51d6b55ab310e47482/src/google/protobuf/timestamp.proto#L123-L135
// Also nanos do not get skipped if they are zero in amino.
message Timestamp {
sfixed64 seconds = 1;
sfixed32 nanos = 2;
message Version {
uint64 Block = 1;
uint64 App = 2;
}
// Timestamp wraps how amino encodes time.
// This is the protobuf well-known type protobuf/timestamp.proto
// See:
// https://github.com/google/protobuf/blob/d2980062c859649523d5fd51d6b55ab310e47482/src/google/protobuf/timestamp.proto#L123-L135
// NOTE/XXX: nanos do not get skipped if they are zero in amino.
message Timestamp {
int64 seconds = 1;
int32 nanos = 2;
}

View File

@@ -34,6 +34,10 @@ type tm2pb struct{}
func (tm2pb) Header(header *Header) abci.Header {
return abci.Header{
Version: abci.Version{
Block: header.Version.Block.Uint64(),
App: header.Version.App.Uint64(),
},
ChainID: header.ChainID,
Height: header.Height,
Time: header.Time,
@@ -45,10 +49,11 @@ func (tm2pb) Header(header *Header) abci.Header {
LastCommitHash: header.LastCommitHash,
DataHash: header.DataHash,
ValidatorsHash: header.ValidatorsHash,
ConsensusHash: header.ConsensusHash,
AppHash: header.AppHash,
LastResultsHash: header.LastResultsHash,
ValidatorsHash: header.ValidatorsHash,
NextValidatorsHash: header.NextValidatorsHash,
ConsensusHash: header.ConsensusHash,
AppHash: header.AppHash,
LastResultsHash: header.LastResultsHash,
EvidenceHash: header.EvidenceHash,
ProposerAddress: header.ProposerAddress,

View File

@@ -4,12 +4,16 @@ import (
"testing"
"time"
"github.com/golang/protobuf/proto"
"github.com/stretchr/testify/assert"
"github.com/tendermint/go-amino"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/secp256k1"
tmtime "github.com/tendermint/tendermint/types/time"
"github.com/tendermint/tendermint/version"
)
func TestABCIPubKey(t *testing.T) {
@@ -30,17 +34,9 @@ func TestABCIValidators(t *testing.T) {
pkEd := ed25519.GenPrivKey().PubKey()
// correct validator
tmValExpected := &Validator{
Address: pkEd.Address(),
PubKey: pkEd,
VotingPower: 10,
}
tmValExpected := NewValidator(pkEd, 10)
tmVal := &Validator{
Address: pkEd.Address(),
PubKey: pkEd,
VotingPower: 10,
}
tmVal := NewValidator(pkEd, 10)
abciVal := TM2PB.ValidatorUpdate(tmVal)
tmVals, err := PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{abciVal})
@@ -75,17 +71,71 @@ func TestABCIConsensusParams(t *testing.T) {
assert.Equal(t, *cp, cp2)
}
func TestABCIHeader(t *testing.T) {
header := &Header{
Height: int64(3),
Time: tmtime.Now(),
NumTxs: int64(10),
ProposerAddress: []byte("cloak"),
func newHeader(
height, numTxs int64,
commitHash, dataHash, evidenceHash []byte,
) *Header {
return &Header{
Height: height,
NumTxs: numTxs,
LastCommitHash: commitHash,
DataHash: dataHash,
EvidenceHash: evidenceHash,
}
abciHeader := TM2PB.Header(header)
}
func TestABCIHeader(t *testing.T) {
// build a full header
var height int64 = 5
var numTxs int64 = 3
header := newHeader(
height, numTxs,
[]byte("lastCommitHash"), []byte("dataHash"), []byte("evidenceHash"),
)
protocolVersion := version.Consensus{7, 8}
timestamp := time.Now()
lastBlockID := BlockID{
Hash: []byte("hash"),
PartsHeader: PartSetHeader{
Total: 10,
Hash: []byte("hash"),
},
}
var totalTxs int64 = 100
header.Populate(
protocolVersion, "chainID",
timestamp, lastBlockID, totalTxs,
[]byte("valHash"), []byte("nextValHash"),
[]byte("consHash"), []byte("appHash"), []byte("lastResultsHash"),
[]byte("proposerAddress"),
)
cdc := amino.NewCodec()
headerBz := cdc.MustMarshalBinaryBare(header)
pbHeader := TM2PB.Header(header)
pbHeaderBz, err := proto.Marshal(&pbHeader)
assert.NoError(t, err)
// assert some fields match
assert.EqualValues(t, protocolVersion.Block, pbHeader.Version.Block)
assert.EqualValues(t, protocolVersion.App, pbHeader.Version.App)
assert.EqualValues(t, "chainID", pbHeader.ChainID)
assert.EqualValues(t, height, pbHeader.Height)
assert.EqualValues(t, timestamp, pbHeader.Time)
assert.EqualValues(t, numTxs, pbHeader.NumTxs)
assert.EqualValues(t, totalTxs, pbHeader.TotalTxs)
assert.EqualValues(t, lastBlockID.Hash, pbHeader.LastBlockId.Hash)
assert.EqualValues(t, []byte("lastCommitHash"), pbHeader.LastCommitHash)
assert.Equal(t, []byte("proposerAddress"), pbHeader.ProposerAddress)
// assert the encodings match
// NOTE: they don't yet because Amino encodes
// int64 as zig-zag and we're using non-zigzag in the protobuf.
// See https://github.com/tendermint/tendermint/issues/2682
_, _ = headerBz, pbHeaderBz
// assert.EqualValues(t, headerBz, pbHeaderBz)
assert.Equal(t, int64(3), abciHeader.Height)
assert.Equal(t, []byte("cloak"), abciHeader.ProposerAddress)
}
func TestABCIEvidence(t *testing.T) {
@@ -127,11 +177,7 @@ func TestABCIValidatorFromPubKeyAndPower(t *testing.T) {
func TestABCIValidatorWithoutPubKey(t *testing.T) {
pkEd := ed25519.GenPrivKey().PubKey()
abciVal := TM2PB.Validator(&Validator{
Address: pkEd.Address(),
PubKey: pkEd,
VotingPower: 10,
})
abciVal := TM2PB.Validator(NewValidator(pkEd, 10))
// pubkey must be nil
tmValExpected := abci.Validator{

View File

@@ -48,7 +48,7 @@ func NewResultFromResponse(response *abci.ResponseDeliverTx) ABCIResult {
// Bytes serializes the ABCIResponse using wire
func (a ABCIResults) Bytes() []byte {
bz, err := cdc.MarshalBinary(a)
bz, err := cdc.MarshalBinaryLengthPrefixed(a)
if err != nil {
panic(err)
}

View File

@@ -43,7 +43,7 @@ func TestABCIResults(t *testing.T) {
}
}
func TestABCIBytes(t *testing.T) {
func TestABCIResultsBytes(t *testing.T) {
results := NewResults([]*abci.ResponseDeliverTx{
{Code: 0, Data: []byte{}},
{Code: 0, Data: []byte("one")},

View File

@@ -31,18 +31,13 @@ type Txs []Tx
// Hash returns the simple Merkle root hash of the transactions.
func (txs Txs) Hash() []byte {
// Recursive impl.
// Copied from tendermint/crypto/merkle to avoid allocations
switch len(txs) {
case 0:
return nil
case 1:
return txs[0].Hash()
default:
left := Txs(txs[:(len(txs)+1)/2]).Hash()
right := Txs(txs[(len(txs)+1)/2:]).Hash()
return merkle.SimpleHashFromTwoHashes(left, right)
// These allocations will be removed once Txs is switched to [][]byte,
// ref #2603. This is because golang does not allow type casting slices without unsafe
txBzs := make([][]byte, len(txs))
for i := 0; i < len(txs); i++ {
txBzs[i] = txs[i]
}
return merkle.SimpleHashFromByteSlices(txBzs)
}
// Index returns the index of this transaction in the list, or -1 if not found

Some files were not shown because too many files have changed in this diff Show More