Browse Source

Merge branch 'develop' into 2702-proposal-pol-round-validation

pull/2704/head
Ethan Buchman 6 years ago
committed by GitHub
parent
commit
8760c5b4f9
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
93 changed files with 2713 additions and 1168 deletions
  1. +143
    -0
      CHANGELOG.md
  2. +6
    -86
      CHANGELOG_PENDING.md
  3. +9
    -17
      Gopkg.lock
  4. +1
    -5
      Gopkg.toml
  5. +3
    -1
      Makefile
  6. +1
    -1
      README.md
  7. +7
    -3
      UPGRADING.md
  8. +596
    -308
      abci/types/types.pb.go
  9. +9
    -4
      abci/types/types.proto
  10. +141
    -17
      abci/types/typespb_test.go
  11. +51
    -2
      blockchain/reactor.go
  12. +1
    -1
      config/toml.go
  13. +5
    -5
      consensus/byzantine_test.go
  14. +7
    -4
      consensus/common_test.go
  15. +0
    -1
      consensus/mempool_test.go
  16. +200
    -44
      consensus/reactor.go
  17. +7
    -3
      consensus/replay.go
  18. +1
    -1
      consensus/replay_test.go
  19. +35
    -35
      consensus/state.go
  20. +193
    -4
      consensus/state_test.go
  21. +7
    -0
      consensus/types/round_state.go
  22. +2
    -5
      consensus/types/round_state_test.go
  23. +16
    -6
      crypto/crypto.go
  24. +1
    -1
      crypto/ed25519/ed25519.go
  25. +5
    -4
      crypto/merkle/proof.go
  26. +136
    -0
      crypto/merkle/proof_test.go
  27. +6
    -6
      crypto/merkle/simple_map_test.go
  28. +25
    -8
      crypto/tmhash/hash.go
  29. +21
    -2
      crypto/tmhash/hash_test.go
  30. +4
    -4
      docs/architecture/adr-001-logging.md
  31. +122
    -0
      docs/architecture/adr-033-pubsub.md
  32. +4
    -0
      docs/architecture/adr-template.md
  33. +10
    -3
      docs/spec/abci/abci.md
  34. +9
    -9
      docs/spec/blockchain/blockchain.md
  35. +3
    -4
      docs/spec/blockchain/encoding.md
  36. +2
    -2
      docs/spec/blockchain/state.md
  37. +9
    -6
      docs/spec/reactors/consensus/consensus-reactor.md
  38. +16
    -17
      docs/spec/reactors/consensus/consensus.md
  39. +1
    -1
      evidence/pool.go
  40. +1
    -1
      evidence/pool_test.go
  41. +22
    -3
      evidence/reactor.go
  42. +78
    -0
      libs/fail/fail.go
  43. +3
    -3
      libs/log/tmfmt_logger.go
  44. +1
    -2
      p2p/key.go
  45. +1
    -1
      p2p/metrics.go
  46. +4
    -0
      p2p/pex/addrbook.go
  47. +8
    -0
      p2p/pex/errors.go
  48. +22
    -6
      p2p/pex/pex_reactor.go
  49. +8
    -8
      privval/priv_validator_test.go
  50. +2
    -2
      state/execution.go
  51. +4
    -4
      state/state_test.go
  52. +1
    -1
      state/store.go
  53. +23
    -33
      state/validation.go
  54. +7
    -4
      test/docker/Dockerfile
  55. +3
    -6
      test/p2p/README.md
  56. +0
    -39
      test/p2p/data/mach1/core/config/genesis.json
  57. +0
    -6
      test/p2p/data/mach1/core/config/node_key.json
  58. +0
    -14
      test/p2p/data/mach1/core/config/priv_validator.json
  59. +0
    -39
      test/p2p/data/mach2/core/config/genesis.json
  60. +0
    -6
      test/p2p/data/mach2/core/config/node_key.json
  61. +0
    -14
      test/p2p/data/mach2/core/config/priv_validator.json
  62. +0
    -39
      test/p2p/data/mach3/core/config/genesis.json
  63. +0
    -6
      test/p2p/data/mach3/core/config/node_key.json
  64. +0
    -14
      test/p2p/data/mach3/core/config/priv_validator.json
  65. +0
    -39
      test/p2p/data/mach4/core/config/genesis.json
  66. +0
    -6
      test/p2p/data/mach4/core/config/node_key.json
  67. +0
    -14
      test/p2p/data/mach4/core/config/priv_validator.json
  68. +1
    -1
      test/p2p/ip_plus_id.sh
  69. +4
    -2
      test/p2p/peer.sh
  70. +3
    -3
      test/p2p/pex/test_addrbook.sh
  71. +97
    -20
      types/block.go
  72. +13
    -10
      types/block_test.go
  73. +14
    -16
      types/canonical.go
  74. +4
    -0
      types/event_bus.go
  75. +2
    -0
      types/events.go
  76. +22
    -1
      types/evidence.go
  77. +1
    -1
      types/evidence_test.go
  78. +31
    -0
      types/heartbeat.go
  79. +60
    -12
      types/params.go
  80. +42
    -27
      types/params_test.go
  81. +25
    -1
      types/part_set.go
  82. +53
    -20
      types/proposal.go
  83. +9
    -7
      types/proposal_test.go
  84. +182
    -84
      types/proto3/block.pb.go
  85. +4
    -4
      types/proto3/block.proto
  86. +18
    -6
      types/protobuf.go
  87. +0
    -1
      types/protobuf_test.go
  88. +12
    -0
      types/signable.go
  89. +4
    -5
      types/signed_msg_type.go
  90. +40
    -0
      types/validation.go
  91. +4
    -4
      types/validator.go
  92. +43
    -6
      types/vote.go
  93. +22
    -7
      types/vote_test.go

+ 143
- 0
CHANGELOG.md View File

@ -1,5 +1,148 @@
# Changelog
## v0.26.0
*November 2, 2018*
Special thanks to external contributors on this release:
@bradyjoestar, @connorwstein, @goolAdapter, @HaoyangLiu,
@james-ray, @overbool, @phymbert, @Slamper, @Uzair1995, @yutianwu.
Special thanks to @Slamper for a series of bug reports in our [bug bounty
program](https://hackerone.com/tendermint) which are fixed in this release.
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, and
changes the length of hashes used for hashing data structures from 20 to 32
bytes.
See the [UPGRADING.md](UPGRADING.md#v0.26.0) for details on upgrading to the new
version.
Please note that we are still making breaking changes to the protocols.
While the new Version fields should help us to keep the software backwards compatible
even while upgrading the protocols, we cannot guarantee that new releases will
be compatible with old chains just yet. We expect there will be another breaking
release or two before the Cosmos Hub launch, but we will otherwise be paying
increasing attention to backwards compatibility. Thanks for bearing with us!
### BREAKING CHANGES:
* CLI/RPC/Config
* [config] [\#2232](https://github.com/tendermint/tendermint/issues/2232) Timeouts are now strings like "3s" and "100ms", 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`
* [rpc] [\#2654](https://github.com/tendermint/tendermint/issues/2654) Remove all `node_info.other.*_version` fields in `/status` and
`/net_info`
* [rpc] [\#2636](https://github.com/tendermint/tendermint/issues/2636) Remove
`_params` suffix from fields in `consensus_params`.
* Apps
* [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`
* [abci] [\#2636](https://github.com/tendermint/tendermint/issues/2636) Updates to ConsensusParams
* Remove `Params` suffix from field names
* Add `Params` suffix to message types
* Add new field and type, `Validator ValidatorParams`, to control what types of validator keys are allowed.
* Go API
* [config] [\#2232](https://github.com/tendermint/tendermint/issues/2232) Timeouts are 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`.
* [types] [\#2636](https://github.com/tendermint/tendermint/issues/2636) Rename fields in ConsensusParams to remove `Params` suffixes
* [types] [\#2735](https://github.com/tendermint/tendermint/issues/2735) Simplify Proposal message to align with spec
* Blockchain Protocol
* [crypto/tmhash] [\#2732](https://github.com/tendermint/tendermint/issues/2732) TMHASH is now full 32-byte SHA256
* All hashes in the block header and Merkle trees are now 32-bytes
* PubKey Addresses are still only 20-bytes
* [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] Update SignBytes for `Vote`/`Proposal`/`Heartbeat`:
* [\#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 from `string` to `byte` and use new
`SignedMsgType` to enumerate.
* [types] [\#2730](https://github.com/tendermint/tendermint/issues/2730) Use
same order for fields in `Vote` as in the SignBytes
* [types] [\#2732](https://github.com/tendermint/tendermint/issues/2732) Remove the address 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
* [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
* [types] [\#2682](https://github.com/tendermint/tendermint/issues/2682) Use proto3 `varint` encoding for ints that are usually unsigned (instead of zigzag encoding).
* [types] [\#2636](https://github.com/tendermint/tendermint/issues/2636) Add Validator field to ConsensusParams
(Used to control which pubkey types validators can use, by abci type).
* P2P Protocol
* [consensus] [\#2652](https://github.com/tendermint/tendermint/issues/2652)
Replace `CommitStepMessage` with `NewValidBlockMessage`
* [consensus] [\#2735](https://github.com/tendermint/tendermint/issues/2735) Simplify `Proposal` message to align with spec
* [consensus] [\#2730](https://github.com/tendermint/tendermint/issues/2730)
Add `Type` field to `Proposal` and use same order of fields as in the
SignBytes for both `Proposal` and `Vote`
* [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:
- [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](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
- [libs/log] [\#2707](https://github.com/tendermint/tendermint/issues/2707) Add year to log format (@yutianwu)
- [tools] [\#2238](https://github.com/tendermint/tendermint/issues/2238) Binary dependencies are now locked to a specific git commit
### BUG FIXES:
- [\#2711](https://github.com/tendermint/tendermint/issues/2711) Validate all incoming reactor messages. Fixes various bugs due to negative ints.
- [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
- [consensus] [\#2652](https://github.com/tendermint/tendermint/issues/2652) Ensure valid block property with faulty proposer
- [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
## v0.25.0
*September 22, 2018*


+ 6
- 86
CHANGELOG_PENDING.md View File

@ -1,107 +1,27 @@
# Pending
## v0.26.0
## v0.26.1
*October 19, 2018*
*TBA*
Special thanks to external contributors on this release:
@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:
### BREAKING CHANGES:
* CLI/RPC/Config
* [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`
* [rpc] [\#2654](https://github.com/tendermint/tendermint/issues/2654) Remove all `node_info.other.*_version` fields in `/status` and
`/net_info`
* Apps
* [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
* [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](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](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:
- [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](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](https://github.com/tendermint/tendermint/issues/2238) Binary dependencies are now locked to a specific git commit
### FEATURES:
BUG FIXES:
- [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
- [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
### IMPROVEMENTS:
### BUG FIXES:

+ 9
- 17
Gopkg.lock View File

@ -35,13 +35,6 @@
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.1"
[[projects]]
digest = "1:c7644c73a3d23741fdba8a99b1464e021a224b7e205be497271a8003a15ca41b"
name = "github.com/ebuchman/fail-test"
packages = ["."]
pruneopts = "UT"
revision = "95f809107225be108efcf10a3509e4ea6ceef3c4"
[[projects]]
digest = "1:544229a3ca0fb2dd5ebc2896d3d2ff7ce096d9751635301e44e37e761349ee70"
name = "github.com/fortytw2/leaktest"
@ -286,12 +279,12 @@
version = "v1.1.2"
[[projects]]
digest = "1:516e71bed754268937f57d4ecb190e01958452336fa73dbac880894164e91c1f"
digest = "1:08d65904057412fc0270fc4812a1c90c594186819243160dc779a402d4b6d0bc"
name = "github.com/spf13/cast"
packages = ["."]
pruneopts = "UT"
revision = "8965335b8c7107321228e3e3702cab9832751bac"
version = "v1.2.0"
revision = "8c9545af88b134710ab1cd196795e7f2388358d7"
version = "v1.3.0"
[[projects]]
digest = "1:7ffc0983035bc7e297da3688d9fe19d60a420e9c38bef23f845c53788ed6a05e"
@ -365,12 +358,12 @@
revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df"
[[projects]]
digest = "1:5f52e817b6c9d52ddba70dece0ea31134d82a52c05bce98fbc739ab2a832df28"
digest = "1:10b3a599325740c84a7c81f3f3cb2e1fdb70b3ea01b7fa28495567a2519df431"
name = "github.com/tendermint/go-amino"
packages = ["."]
pruneopts = "UT"
revision = "cb07448b240918aa8d8df4505153549b86b77134"
version = "v0.13.0"
revision = "6dcc6ddc143e116455c94b25c1004c99e0d0ca12"
version = "v0.14.0"
[[projects]]
digest = "1:72b71e3a29775e5752ed7a8012052a3dee165e27ec18cedddae5288058f09acf"
@ -415,14 +408,14 @@
[[projects]]
branch = "master"
digest = "1:d1da39c9bac61327dbef1d8ef9f210425e99fd2924b6fb5f0bc587a193353637"
digest = "1:6f86e2f2e2217cd4d74dec6786163cf80e4d2b99adb341ecc60a45113b844dca"
name = "golang.org/x/sys"
packages = [
"cpu",
"unix",
]
pruneopts = "UT"
revision = "8a28ead16f52c8aaeffbf79239b251dfdf6c4f96"
revision = "7e31e0c00fa05cb5fbf4347b585621d6709e19a4"
[[projects]]
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
@ -453,7 +446,7 @@
name = "google.golang.org/genproto"
packages = ["googleapis/rpc/status"]
pruneopts = "UT"
revision = "94acd270e44e65579b9ee3cdab25034d33fed608"
revision = "b69ba1387ce2108ac9bc8e8e5e5a46e7d5c72313"
[[projects]]
digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74"
@ -503,7 +496,6 @@
input-imports = [
"github.com/btcsuite/btcutil/base58",
"github.com/btcsuite/btcutil/bech32",
"github.com/ebuchman/fail-test",
"github.com/fortytw2/leaktest",
"github.com/go-kit/kit/log",
"github.com/go-kit/kit/log/level",


+ 1
- 5
Gopkg.toml View File

@ -58,7 +58,7 @@
[[constraint]]
name = "github.com/tendermint/go-amino"
version = "v0.13.0"
version = "v0.14.0"
[[constraint]]
name = "google.golang.org/grpc"
@ -81,10 +81,6 @@
name = "github.com/jmhodges/levigo"
revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9"
[[constraint]]
name = "github.com/ebuchman/fail-test"
revision = "95f809107225be108efcf10a3509e4ea6ceef3c4"
# last revision used by go-crypto
[[constraint]]
name = "github.com/btcsuite/btcutil"


+ 3
- 1
Makefile View File

@ -36,7 +36,7 @@ install:
########################################
### Protobuf
protoc_all: protoc_libs protoc_merkle protoc_abci protoc_grpc
protoc_all: protoc_libs protoc_merkle protoc_abci protoc_grpc protoc_proto3types
%.pb.go: %.proto
## If you get the following error,
@ -52,6 +52,8 @@ protoc_all: protoc_libs protoc_merkle protoc_abci protoc_grpc
# see protobuf section above
protoc_abci: abci/types/types.pb.go
protoc_proto3types: types/proto3/block.pb.go
build_abci:
@go build -i ./abci/cmd/...


+ 1
- 1
README.md View File

@ -36,7 +36,7 @@ We are also still making breaking changes to the protocol and the APIs.
Thus, we tag the releases as *alpha software*.
In any case, if you intend to run Tendermint in production,
please [contact us](https://riot.im/app/#/room/#tendermint:matrix.org) :)
please [contact us](mailto:partners@tendermint.com) and [join the chat](https://riot.im/app/#/room/#tendermint:matrix.org).
## Security


+ 7
- 3
UPGRADING.md View File

@ -5,7 +5,7 @@ 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
New 0.26.0 release contains a lot of changes to core data types and protocols. 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.
@ -33,7 +33,7 @@ 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`..
the new `block` and `p2p` protocol versions under `node_info.protocol_version`.
### ABCI Changes
@ -45,7 +45,7 @@ protobuf file for these changes.
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
the root of another. If you don't need this functionality, and you used to
return `<proof bytes>` here, you should instead return a single `ProofOp` with
just the `Data` field set:
@ -79,6 +79,10 @@ 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).
### Other
All hashes, except for public key addresses, are now 32-bytes.
## v0.25.0
This release has minimal impact.


+ 596
- 308
abci/types/types.pb.go
File diff suppressed because it is too large
View File


+ 9
- 4
abci/types/types.proto View File

@ -74,7 +74,6 @@ message RequestQuery {
bool prove = 4;
}
// NOTE: validators here have empty pubkeys.
message RequestBeginBlock {
bytes hash = 1;
Header header = 2 [(gogoproto.nullable)=false];
@ -208,12 +207,13 @@ message ResponseCommit {
// ConsensusParams contains all consensus-relevant parameters
// that can be adjusted by the abci app
message ConsensusParams {
BlockSize block_size = 1;
EvidenceParams evidence_params = 2;
BlockSizeParams block_size = 1;
EvidenceParams evidence = 2;
ValidatorParams validator = 3;
}
// BlockSize contains limits on the block size.
message BlockSize {
message BlockSizeParams {
// Note: must be greater than 0
int64 max_bytes = 1;
// Note: must be greater or equal to -1
@ -226,6 +226,11 @@ message EvidenceParams {
int64 max_age = 1;
}
// ValidatorParams contains limits on validators.
message ValidatorParams {
repeated string pub_key_types = 1;
}
message LastCommitInfo {
int32 round = 1;
repeated VoteInfo votes = 2 [(gogoproto.nullable)=false];


+ 141
- 17
abci/types/typespb_test.go View File

@ -1479,15 +1479,15 @@ func TestConsensusParamsMarshalTo(t *testing.T) {
}
}
func TestBlockSizeProto(t *testing.T) {
func TestBlockSizeParamsProto(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedBlockSize(popr, false)
p := NewPopulatedBlockSizeParams(popr, false)
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &BlockSize{}
msg := &BlockSizeParams{}
if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
@ -1510,10 +1510,10 @@ func TestBlockSizeProto(t *testing.T) {
}
}
func TestBlockSizeMarshalTo(t *testing.T) {
func TestBlockSizeParamsMarshalTo(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedBlockSize(popr, false)
p := NewPopulatedBlockSizeParams(popr, false)
size := p.Size()
dAtA := make([]byte, size)
for i := range dAtA {
@ -1523,7 +1523,7 @@ func TestBlockSizeMarshalTo(t *testing.T) {
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &BlockSize{}
msg := &BlockSizeParams{}
if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
@ -1591,6 +1591,62 @@ func TestEvidenceParamsMarshalTo(t *testing.T) {
}
}
func TestValidatorParamsProto(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedValidatorParams(popr, false)
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
msg := &ValidatorParams{}
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 TestValidatorParamsMarshalTo(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedValidatorParams(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 := &ValidatorParams{}
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 TestLastCommitInfoProto(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
@ -2619,16 +2675,16 @@ func TestConsensusParamsJSON(t *testing.T) {
t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p)
}
}
func TestBlockSizeJSON(t *testing.T) {
func TestBlockSizeParamsJSON(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedBlockSize(popr, true)
p := NewPopulatedBlockSizeParams(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 := &BlockSize{}
msg := &BlockSizeParams{}
err = github_com_gogo_protobuf_jsonpb.UnmarshalString(jsondata, msg)
if err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
@ -2655,6 +2711,24 @@ func TestEvidenceParamsJSON(t *testing.T) {
t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p)
}
}
func TestValidatorParamsJSON(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedValidatorParams(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 := &ValidatorParams{}
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 TestLastCommitInfoJSON(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
@ -3563,12 +3637,12 @@ func TestConsensusParamsProtoCompactText(t *testing.T) {
}
}
func TestBlockSizeProtoText(t *testing.T) {
func TestBlockSizeParamsProtoText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedBlockSize(popr, true)
p := NewPopulatedBlockSizeParams(popr, true)
dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p)
msg := &BlockSize{}
msg := &BlockSizeParams{}
if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
@ -3577,12 +3651,12 @@ func TestBlockSizeProtoText(t *testing.T) {
}
}
func TestBlockSizeProtoCompactText(t *testing.T) {
func TestBlockSizeParamsProtoCompactText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedBlockSize(popr, true)
p := NewPopulatedBlockSizeParams(popr, true)
dAtA := github_com_gogo_protobuf_proto.CompactTextString(p)
msg := &BlockSize{}
msg := &BlockSizeParams{}
if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil {
t.Fatalf("seed = %d, err = %v", seed, err)
}
@ -3619,6 +3693,34 @@ func TestEvidenceParamsProtoCompactText(t *testing.T) {
}
}
func TestValidatorParamsProtoText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedValidatorParams(popr, true)
dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p)
msg := &ValidatorParams{}
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 TestValidatorParamsProtoCompactText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedValidatorParams(popr, true)
dAtA := github_com_gogo_protobuf_proto.CompactTextString(p)
msg := &ValidatorParams{}
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 TestLastCommitInfoProtoText(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
@ -4471,10 +4573,10 @@ func TestConsensusParamsSize(t *testing.T) {
}
}
func TestBlockSizeSize(t *testing.T) {
func TestBlockSizeParamsSize(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedBlockSize(popr, true)
p := NewPopulatedBlockSizeParams(popr, true)
size2 := github_com_gogo_protobuf_proto.Size(p)
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
if err != nil {
@ -4515,6 +4617,28 @@ func TestEvidenceParamsSize(t *testing.T) {
}
}
func TestValidatorParamsSize(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))
p := NewPopulatedValidatorParams(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 TestLastCommitInfoSize(t *testing.T) {
seed := time.Now().UnixNano()
popr := math_rand.New(math_rand.NewSource(seed))


+ 51
- 2
blockchain/reactor.go View File

@ -1,6 +1,7 @@
package blockchain
import (
"errors"
"fmt"
"reflect"
"time"
@ -180,6 +181,12 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
return
}
if err = msg.ValidateBasic(); err != nil {
bcR.Logger.Error("Peer sent us invalid msg", "peer", src, "msg", msg, "err", err)
bcR.Switch.StopPeerForError(src, err)
return
}
bcR.Logger.Debug("Receive", "src", src, "chID", chID, "msg", msg)
switch msg := msg.(type) {
@ -188,7 +195,6 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
// Unfortunately not queued since the queue is full.
}
case *bcBlockResponseMessage:
// Got a block.
bcR.pool.AddBlock(src.ID(), msg.Block, len(msgBytes))
case *bcStatusRequestMessage:
// Send peer our state.
@ -352,7 +358,9 @@ func (bcR *BlockchainReactor) BroadcastStatusRequest() error {
// Messages
// BlockchainMessage is a generic message for this reactor.
type BlockchainMessage interface{}
type BlockchainMessage interface {
ValidateBasic() error
}
func RegisterBlockchainMessages(cdc *amino.Codec) {
cdc.RegisterInterface((*BlockchainMessage)(nil), nil)
@ -377,6 +385,14 @@ type bcBlockRequestMessage struct {
Height int64
}
// ValidateBasic performs basic validation.
func (m *bcBlockRequestMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("Negative Height")
}
return nil
}
func (m *bcBlockRequestMessage) String() string {
return fmt.Sprintf("[bcBlockRequestMessage %v]", m.Height)
}
@ -385,6 +401,14 @@ type bcNoBlockResponseMessage struct {
Height int64
}
// ValidateBasic performs basic validation.
func (m *bcNoBlockResponseMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("Negative Height")
}
return nil
}
func (brm *bcNoBlockResponseMessage) String() string {
return fmt.Sprintf("[bcNoBlockResponseMessage %d]", brm.Height)
}
@ -395,6 +419,15 @@ type bcBlockResponseMessage struct {
Block *types.Block
}
// ValidateBasic performs basic validation.
func (m *bcBlockResponseMessage) ValidateBasic() error {
if err := m.Block.ValidateBasic(); err != nil {
return err
}
return nil
}
func (m *bcBlockResponseMessage) String() string {
return fmt.Sprintf("[bcBlockResponseMessage %v]", m.Block.Height)
}
@ -405,6 +438,14 @@ type bcStatusRequestMessage struct {
Height int64
}
// ValidateBasic performs basic validation.
func (m *bcStatusRequestMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("Negative Height")
}
return nil
}
func (m *bcStatusRequestMessage) String() string {
return fmt.Sprintf("[bcStatusRequestMessage %v]", m.Height)
}
@ -415,6 +456,14 @@ type bcStatusResponseMessage struct {
Height int64
}
// ValidateBasic performs basic validation.
func (m *bcStatusResponseMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("Negative Height")
}
return nil
}
func (m *bcStatusResponseMessage) String() string {
return fmt.Sprintf("[bcStatusResponseMessage %v]", m.Height)
}

+ 1
- 1
config/toml.go View File

@ -342,7 +342,7 @@ func ResetTestRoot(testName string) *Config {
}
var testGenesis = `{
"genesis_time": "2017-10-10T08:20:13.695936996Z",
"genesis_time": "2018-10-10T08:20:13.695936996Z",
"chain_id": "tendermint_test",
"validators": [
{


+ 5
- 5
consensus/byzantine_test.go View File

@ -179,16 +179,16 @@ func byzantineDecideProposalFunc(t *testing.T, height int64, round int, cs *Cons
// Create a new proposal block from state/txs from the mempool.
block1, blockParts1 := cs.createProposalBlock()
polRound, polBlockID := cs.Votes.POLInfo()
proposal1 := types.NewProposal(height, round, blockParts1.Header(), polRound, polBlockID)
polRound, propBlockID := cs.ValidRound, types.BlockID{block1.Hash(), blockParts1.Header()}
proposal1 := types.NewProposal(height, round, polRound, propBlockID)
if err := cs.privValidator.SignProposal(cs.state.ChainID, proposal1); err != nil {
t.Error(err)
}
// Create a new proposal block from state/txs from the mempool.
block2, blockParts2 := cs.createProposalBlock()
polRound, polBlockID = cs.Votes.POLInfo()
proposal2 := types.NewProposal(height, round, blockParts2.Header(), polRound, polBlockID)
polRound, propBlockID = cs.ValidRound, types.BlockID{block2.Hash(), blockParts2.Header()}
proposal2 := types.NewProposal(height, round, polRound, propBlockID)
if err := cs.privValidator.SignProposal(cs.state.ChainID, proposal2); err != nil {
t.Error(err)
}
@ -263,7 +263,7 @@ func (br *ByzantineReactor) AddPeer(peer p2p.Peer) {
// Send our state to peer.
// If we're fast_syncing, broadcast a RoundStepMessage later upon SwitchToConsensus().
if !br.reactor.fastSync {
br.reactor.sendNewRoundStepMessages(peer)
br.reactor.sendNewRoundStepMessage(peer)
}
}
func (br *ByzantineReactor) RemovePeer(peer p2p.Peer, reason interface{}) {


+ 7
- 4
consensus/common_test.go View File

@ -130,8 +130,8 @@ func decideProposal(cs1 *ConsensusState, vs *validatorStub, height int64, round
}
// Make proposal
polRound, polBlockID := cs1.Votes.POLInfo()
proposal = types.NewProposal(height, round, blockParts.Header(), polRound, polBlockID)
polRound, propBlockID := cs1.ValidRound, types.BlockID{block.Hash(), blockParts.Header()}
proposal = types.NewProposal(height, round, polRound, propBlockID)
if err := vs.SignProposal(cs1.state.ChainID, proposal); err != nil {
panic(err)
}
@ -420,6 +420,11 @@ func ensureNewProposal(proposalCh <-chan interface{}, height int64, round int) {
"Timeout expired while waiting for NewProposal event")
}
func ensureNewValidBlock(validBlockCh <-chan interface{}, height int64, round int) {
ensureNewEvent(validBlockCh, height, round, ensureTimeout,
"Timeout expired while waiting for NewValidBlock event")
}
func ensureNewBlock(blockCh <-chan interface{}, height int64) {
select {
case <-time.After(ensureTimeout):
@ -610,8 +615,6 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G
func randGenesisState(numValidators int, randPower bool, minPower int64) (sm.State, []types.PrivValidator) {
genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower)
s0, _ := sm.MakeGenesisState(genDoc)
db := dbm.NewMemDB() // remove this ?
sm.SaveState(db, s0)
return s0, privValidators
}


+ 0
- 1
consensus/mempool_test.go View File

@ -10,7 +10,6 @@ import (
"github.com/tendermint/tendermint/abci/example/code"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/types"
)


+ 200
- 44
consensus/reactor.go View File

@ -8,8 +8,7 @@ import (
"github.com/pkg/errors"
"github.com/tendermint/go-amino"
amino "github.com/tendermint/go-amino"
cstypes "github.com/tendermint/tendermint/consensus/types"
cmn "github.com/tendermint/tendermint/libs/common"
tmevents "github.com/tendermint/tendermint/libs/events"
@ -174,7 +173,7 @@ func (conR *ConsensusReactor) AddPeer(peer p2p.Peer) {
// Send our state to peer.
// If we're fast_syncing, broadcast a RoundStepMessage later upon SwitchToConsensus().
if !conR.FastSync() {
conR.sendNewRoundStepMessages(peer)
conR.sendNewRoundStepMessage(peer)
}
}
@ -205,6 +204,13 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
conR.Switch.StopPeerForError(src, err)
return
}
if err = msg.ValidateBasic(); err != nil {
conR.Logger.Error("Peer sent us invalid msg", "peer", src, "msg", msg, "err", err)
conR.Switch.StopPeerForError(src, err)
return
}
conR.Logger.Debug("Receive", "src", src, "chId", chID, "msg", msg)
// Get peer states
@ -215,8 +221,8 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
switch msg := msg.(type) {
case *NewRoundStepMessage:
ps.ApplyNewRoundStepMessage(msg)
case *CommitStepMessage:
ps.ApplyCommitStepMessage(msg)
case *NewValidBlockMessage:
ps.ApplyNewValidBlockMessage(msg)
case *HasVoteMessage:
ps.ApplyHasVoteMessage(msg)
case *VoteSetMaj23Message:
@ -242,8 +248,7 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
case types.PrecommitType:
ourVotes = votes.Precommits(msg.Round).BitArrayByBlockID(msg.BlockID)
default:
conR.Logger.Error("Bad VoteSetBitsMessage field Type")
return
panic("Bad VoteSetBitsMessage field Type. Forgot to add a check in ValidateBasic?")
}
src.TrySend(VoteSetBitsChannel, cdc.MustMarshalBinaryBare(&VoteSetBitsMessage{
Height: msg.Height,
@ -322,8 +327,7 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
case types.PrecommitType:
ourVotes = votes.Precommits(msg.Round).BitArrayByBlockID(msg.BlockID)
default:
conR.Logger.Error("Bad VoteSetBitsMessage field Type")
return
panic("Bad VoteSetBitsMessage field Type. Forgot to add a check in ValidateBasic?")
}
ps.ApplyVoteSetBitsMessage(msg, ourVotes)
} else {
@ -365,7 +369,12 @@ func (conR *ConsensusReactor) subscribeToBroadcastEvents() {
const subscriber = "consensus-reactor"
conR.conS.evsw.AddListenerForEvent(subscriber, types.EventNewRoundStep,
func(data tmevents.EventData) {
conR.broadcastNewRoundStepMessages(data.(*cstypes.RoundState))
conR.broadcastNewRoundStepMessage(data.(*cstypes.RoundState))
})
conR.conS.evsw.AddListenerForEvent(subscriber, types.EventValidBlock,
func(data tmevents.EventData) {
conR.broadcastNewValidBlockMessage(data.(*cstypes.RoundState))
})
conR.conS.evsw.AddListenerForEvent(subscriber, types.EventVote,
@ -391,14 +400,20 @@ func (conR *ConsensusReactor) broadcastProposalHeartbeatMessage(hb *types.Heartb
conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(msg))
}
func (conR *ConsensusReactor) broadcastNewRoundStepMessages(rs *cstypes.RoundState) {
nrsMsg, csMsg := makeRoundStepMessages(rs)
if nrsMsg != nil {
conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(nrsMsg))
}
if csMsg != nil {
conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(csMsg))
func (conR *ConsensusReactor) broadcastNewRoundStepMessage(rs *cstypes.RoundState) {
nrsMsg := makeRoundStepMessage(rs)
conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(nrsMsg))
}
func (conR *ConsensusReactor) broadcastNewValidBlockMessage(rs *cstypes.RoundState) {
csMsg := &NewValidBlockMessage{
Height: rs.Height,
Round: rs.Round,
BlockPartsHeader: rs.ProposalBlockParts.Header(),
BlockParts: rs.ProposalBlockParts.BitArray(),
IsCommit: rs.Step == cstypes.RoundStepCommit,
}
conR.Switch.Broadcast(StateChannel, cdc.MustMarshalBinaryBare(csMsg))
}
// Broadcasts HasVoteMessage to peers that care.
@ -427,7 +442,7 @@ func (conR *ConsensusReactor) broadcastHasVoteMessage(vote *types.Vote) {
*/
}
func makeRoundStepMessages(rs *cstypes.RoundState) (nrsMsg *NewRoundStepMessage, csMsg *CommitStepMessage) {
func makeRoundStepMessage(rs *cstypes.RoundState) (nrsMsg *NewRoundStepMessage) {
nrsMsg = &NewRoundStepMessage{
Height: rs.Height,
Round: rs.Round,
@ -435,25 +450,13 @@ func makeRoundStepMessages(rs *cstypes.RoundState) (nrsMsg *NewRoundStepMessage,
SecondsSinceStartTime: int(time.Since(rs.StartTime).Seconds()),
LastCommitRound: rs.LastCommit.Round(),
}
if rs.Step == cstypes.RoundStepCommit {
csMsg = &CommitStepMessage{
Height: rs.Height,
BlockPartsHeader: rs.ProposalBlockParts.Header(),
BlockParts: rs.ProposalBlockParts.BitArray(),
}
}
return
}
func (conR *ConsensusReactor) sendNewRoundStepMessages(peer p2p.Peer) {
func (conR *ConsensusReactor) sendNewRoundStepMessage(peer p2p.Peer) {
rs := conR.conS.GetRoundState()
nrsMsg, csMsg := makeRoundStepMessages(rs)
if nrsMsg != nil {
peer.Send(StateChannel, cdc.MustMarshalBinaryBare(nrsMsg))
}
if csMsg != nil {
peer.Send(StateChannel, cdc.MustMarshalBinaryBare(csMsg))
}
nrsMsg := makeRoundStepMessage(rs)
peer.Send(StateChannel, cdc.MustMarshalBinaryBare(nrsMsg))
}
func (conR *ConsensusReactor) gossipDataRoutine(peer p2p.Peer, ps *PeerState) {
@ -524,6 +527,7 @@ OUTER_LOOP:
msg := &ProposalMessage{Proposal: rs.Proposal}
logger.Debug("Sending proposal", "height", prs.Height, "round", prs.Round)
if peer.Send(DataChannel, cdc.MustMarshalBinaryBare(msg)) {
// NOTE[ZM]: A peer might have received different proposal msg so this Proposal msg will be rejected!
ps.SetHasProposal(rs.Proposal)
}
}
@ -964,13 +968,20 @@ func (ps *PeerState) SetHasProposal(proposal *types.Proposal) {
if ps.PRS.Height != proposal.Height || ps.PRS.Round != proposal.Round {
return
}
if ps.PRS.Proposal {
return
}
ps.PRS.Proposal = true
ps.PRS.ProposalBlockPartsHeader = proposal.BlockPartsHeader
ps.PRS.ProposalBlockParts = cmn.NewBitArray(proposal.BlockPartsHeader.Total)
// ps.PRS.ProposalBlockParts is set due to NewValidBlockMessage
if ps.PRS.ProposalBlockParts != nil {
return
}
ps.PRS.ProposalBlockPartsHeader = proposal.BlockID.PartsHeader
ps.PRS.ProposalBlockParts = cmn.NewBitArray(proposal.BlockID.PartsHeader.Total)
ps.PRS.ProposalPOLRound = proposal.POLRound
ps.PRS.ProposalPOL = nil // Nil until ProposalPOLMessage received.
}
@ -1211,7 +1222,6 @@ func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage) {
// Just remember these values.
psHeight := ps.PRS.Height
psRound := ps.PRS.Round
//psStep := ps.PRS.Step
psCatchupCommitRound := ps.PRS.CatchupCommitRound
psCatchupCommit := ps.PRS.CatchupCommit
@ -1252,8 +1262,8 @@ func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage) {
}
}
// ApplyCommitStepMessage updates the peer state for the new commit.
func (ps *PeerState) ApplyCommitStepMessage(msg *CommitStepMessage) {
// ApplyNewValidBlockMessage updates the peer state for the new valid block.
func (ps *PeerState) ApplyNewValidBlockMessage(msg *NewValidBlockMessage) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
@ -1261,6 +1271,10 @@ func (ps *PeerState) ApplyCommitStepMessage(msg *CommitStepMessage) {
return
}
if ps.PRS.Round != msg.Round && !msg.IsCommit {
return
}
ps.PRS.ProposalBlockPartsHeader = msg.BlockPartsHeader
ps.PRS.ProposalBlockParts = msg.BlockParts
}
@ -1339,12 +1353,14 @@ func (ps *PeerState) StringIndented(indent string) string {
// Messages
// ConsensusMessage is a message that can be sent and received on the ConsensusReactor
type ConsensusMessage interface{}
type ConsensusMessage interface {
ValidateBasic() error
}
func RegisterConsensusMessages(cdc *amino.Codec) {
cdc.RegisterInterface((*ConsensusMessage)(nil), nil)
cdc.RegisterConcrete(&NewRoundStepMessage{}, "tendermint/NewRoundStepMessage", nil)
cdc.RegisterConcrete(&CommitStepMessage{}, "tendermint/CommitStep", nil)
cdc.RegisterConcrete(&NewValidBlockMessage{}, "tendermint/NewValidBlockMessage", nil)
cdc.RegisterConcrete(&ProposalMessage{}, "tendermint/Proposal", nil)
cdc.RegisterConcrete(&ProposalPOLMessage{}, "tendermint/ProposalPOL", nil)
cdc.RegisterConcrete(&BlockPartMessage{}, "tendermint/BlockPart", nil)
@ -1375,6 +1391,27 @@ type NewRoundStepMessage struct {
LastCommitRound int
}
// ValidateBasic performs basic validation.
func (m *NewRoundStepMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("Negative Height")
}
if m.Round < 0 {
return errors.New("Negative Round")
}
if !m.Step.IsValid() {
return errors.New("Invalid Step")
}
// NOTE: SecondsSinceStartTime may be negative
if (m.Height == 1 && m.LastCommitRound != -1) ||
(m.Height > 1 && m.LastCommitRound < -1) { // TODO: #2737 LastCommitRound should always be >= 0 for heights > 1
return errors.New("Invalid LastCommitRound (for 1st block: -1, for others: >= 0)")
}
return nil
}
// String returns a string representation.
func (m *NewRoundStepMessage) String() string {
return fmt.Sprintf("[NewRoundStep H:%v R:%v S:%v LCR:%v]",
@ -1383,16 +1420,40 @@ func (m *NewRoundStepMessage) String() string {
//-------------------------------------
// CommitStepMessage is sent when a block is committed.
type CommitStepMessage struct {
// NewValidBlockMessage is sent when a validator observes a valid block B in some round r,
//i.e., there is a Proposal for block B and 2/3+ prevotes for the block B in the round r.
// In case the block is also committed, then IsCommit flag is set to true.
type NewValidBlockMessage struct {
Height int64
Round int
BlockPartsHeader types.PartSetHeader
BlockParts *cmn.BitArray
IsCommit bool
}
// ValidateBasic performs basic validation.
func (m *NewValidBlockMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("Negative Height")
}
if m.Round < 0 {
return errors.New("Negative Round")
}
if err := m.BlockPartsHeader.ValidateBasic(); err != nil {
return fmt.Errorf("Wrong BlockPartsHeader: %v", err)
}
if m.BlockParts.Size() != m.BlockPartsHeader.Total {
return fmt.Errorf("BlockParts bit array size %d not equal to BlockPartsHeader.Total %d",
m.BlockParts.Size(),
m.BlockPartsHeader.Total)
}
return nil
}
// String returns a string representation.
func (m *CommitStepMessage) String() string {
return fmt.Sprintf("[CommitStep H:%v BP:%v BA:%v]", m.Height, m.BlockPartsHeader, m.BlockParts)
func (m *NewValidBlockMessage) String() string {
return fmt.Sprintf("[ValidBlockMessage H:%v R:%v BP:%v BA:%v IsCommit:%v]",
m.Height, m.Round, m.BlockPartsHeader, m.BlockParts, m.IsCommit)
}
//-------------------------------------
@ -1402,6 +1463,11 @@ type ProposalMessage struct {
Proposal *types.Proposal
}
// ValidateBasic performs basic validation.
func (m *ProposalMessage) ValidateBasic() error {
return m.Proposal.ValidateBasic()
}
// String returns a string representation.
func (m *ProposalMessage) String() string {
return fmt.Sprintf("[Proposal %v]", m.Proposal)
@ -1416,6 +1482,20 @@ type ProposalPOLMessage struct {
ProposalPOL *cmn.BitArray
}
// ValidateBasic performs basic validation.
func (m *ProposalPOLMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("Negative Height")
}
if m.ProposalPOLRound < 0 {
return errors.New("Negative ProposalPOLRound")
}
if m.ProposalPOL.Size() == 0 {
return errors.New("Empty ProposalPOL bit array")
}
return nil
}
// String returns a string representation.
func (m *ProposalPOLMessage) String() string {
return fmt.Sprintf("[ProposalPOL H:%v POLR:%v POL:%v]", m.Height, m.ProposalPOLRound, m.ProposalPOL)
@ -1430,6 +1510,20 @@ type BlockPartMessage struct {
Part *types.Part
}
// ValidateBasic performs basic validation.
func (m *BlockPartMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("Negative Height")
}
if m.Round < 0 {
return errors.New("Negative Round")
}
if err := m.Part.ValidateBasic(); err != nil {
return fmt.Errorf("Wrong Part: %v", err)
}
return nil
}
// String returns a string representation.
func (m *BlockPartMessage) String() string {
return fmt.Sprintf("[BlockPart H:%v R:%v P:%v]", m.Height, m.Round, m.Part)
@ -1442,6 +1536,11 @@ type VoteMessage struct {
Vote *types.Vote
}
// ValidateBasic performs basic validation.
func (m *VoteMessage) ValidateBasic() error {
return m.Vote.ValidateBasic()
}
// String returns a string representation.
func (m *VoteMessage) String() string {
return fmt.Sprintf("[Vote %v]", m.Vote)
@ -1457,6 +1556,23 @@ type HasVoteMessage struct {
Index int
}
// ValidateBasic performs basic validation.
func (m *HasVoteMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("Negative Height")
}
if m.Round < 0 {
return errors.New("Negative Round")
}
if !types.IsVoteTypeValid(m.Type) {
return errors.New("Invalid Type")
}
if m.Index < 0 {
return errors.New("Negative Index")
}
return nil
}
// String returns a string representation.
func (m *HasVoteMessage) String() string {
return fmt.Sprintf("[HasVote VI:%v V:{%v/%02d/%v}]", m.Index, m.Height, m.Round, m.Type)
@ -1472,6 +1588,23 @@ type VoteSetMaj23Message struct {
BlockID types.BlockID
}
// ValidateBasic performs basic validation.
func (m *VoteSetMaj23Message) ValidateBasic() error {
if m.Height < 0 {
return errors.New("Negative Height")
}
if m.Round < 0 {
return errors.New("Negative Round")
}
if !types.IsVoteTypeValid(m.Type) {
return errors.New("Invalid Type")
}
if err := m.BlockID.ValidateBasic(); err != nil {
return fmt.Errorf("Wrong BlockID: %v", err)
}
return nil
}
// String returns a string representation.
func (m *VoteSetMaj23Message) String() string {
return fmt.Sprintf("[VSM23 %v/%02d/%v %v]", m.Height, m.Round, m.Type, m.BlockID)
@ -1488,6 +1621,24 @@ type VoteSetBitsMessage struct {
Votes *cmn.BitArray
}
// ValidateBasic performs basic validation.
func (m *VoteSetBitsMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("Negative Height")
}
if m.Round < 0 {
return errors.New("Negative Round")
}
if !types.IsVoteTypeValid(m.Type) {
return errors.New("Invalid Type")
}
if err := m.BlockID.ValidateBasic(); err != nil {
return fmt.Errorf("Wrong BlockID: %v", err)
}
// NOTE: Votes.Size() can be zero if the node does not have any
return nil
}
// String returns a string representation.
func (m *VoteSetBitsMessage) String() string {
return fmt.Sprintf("[VSB %v/%02d/%v %v %v]", m.Height, m.Round, m.Type, m.BlockID, m.Votes)
@ -1500,6 +1651,11 @@ type ProposalHeartbeatMessage struct {
Heartbeat *types.Heartbeat
}
// ValidateBasic performs basic validation.
func (m *ProposalHeartbeatMessage) ValidateBasic() error {
return m.Heartbeat.ValidateBasic()
}
// String returns a string representation.
func (m *ProposalHeartbeatMessage) String() string {
return fmt.Sprintf("[HEARTBEAT %v]", m.Heartbeat)


+ 7
- 3
consensus/replay.go View File

@ -73,7 +73,7 @@ func (cs *ConsensusState) readReplayMessage(msg *TimedWALMessage, newStepCh chan
case *ProposalMessage:
p := msg.Proposal
cs.Logger.Info("Replay: Proposal", "height", p.Height, "round", p.Round, "header",
p.BlockPartsHeader, "pol", p.POLRound, "peer", peerID)
p.BlockID.PartsHeader, "pol", p.POLRound, "peer", peerID)
case *BlockPartMessage:
cs.Logger.Info("Replay: BlockPart", "height", msg.Height, "round", msg.Round, "peer", peerID)
case *VoteMessage:
@ -264,8 +264,12 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error {
// Replay all blocks since appBlockHeight and ensure the result matches the current state.
// Returns the final AppHash or an error.
func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight int64, proxyApp proxy.AppConns) ([]byte, error) {
func (h *Handshaker) ReplayBlocks(
state sm.State,
appHash []byte,
appBlockHeight int64,
proxyApp proxy.AppConns,
) ([]byte, error) {
storeBlockHeight := h.store.Height()
stateBlockHeight := state.LastBlockHeight
h.logger.Info("ABCI Replay Blocks", "appHeight", appBlockHeight, "storeHeight", storeBlockHeight, "stateHeight", stateBlockHeight)


+ 1
- 1
consensus/replay_test.go View File

@ -575,7 +575,7 @@ func readPieceFromWAL(msg *TimedWALMessage) interface{} {
case msgInfo:
switch msg := m.Msg.(type) {
case *ProposalMessage:
return &msg.Proposal.BlockPartsHeader
return &msg.Proposal.BlockID.PartsHeader
case *BlockPartMessage:
return msg.Part
case *VoteMessage:


+ 35
- 35
consensus/state.go View File

@ -9,8 +9,8 @@ import (
"sync"
"time"
fail "github.com/ebuchman/fail-test"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/libs/fail"
"github.com/tendermint/tendermint/libs/log"
tmtime "github.com/tendermint/tendermint/types/time"
@ -901,15 +901,9 @@ func (cs *ConsensusState) defaultDecideProposal(height int64, round int) {
}
// Make proposal
polRound, polBlockID := cs.Votes.POLInfo()
proposal := types.NewProposal(height, round, blockParts.Header(), polRound, polBlockID)
propBlockId := types.BlockID{block.Hash(), blockParts.Header()}
proposal := types.NewProposal(height, round, cs.ValidRound, propBlockId)
if err := cs.privValidator.SignProposal(cs.state.ChainID, proposal); err == nil {
// Set fields
/* fields set by setProposal and addBlockPart
cs.Proposal = proposal
cs.ProposalBlock = block
cs.ProposalBlockParts = blockParts
*/
// send proposal and block parts on internal msg queue
cs.sendInternalMessage(msgInfo{&ProposalMessage{proposal}, ""})
@ -994,14 +988,6 @@ func (cs *ConsensusState) enterPrevote(height int64, round int) {
cs.newStep()
}()
// fire event for how we got here
if cs.isProposalComplete() {
cs.eventBus.PublishEventCompleteProposal(cs.RoundStateEvent())
} else {
// we received +2/3 prevotes for a future round
// TODO: catchup event?
}
cs.Logger.Info(fmt.Sprintf("enterPrevote(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
// Sign and broadcast vote as necessary
@ -1240,6 +1226,8 @@ func (cs *ConsensusState) enterCommit(height int64, commitRound int) {
// Set up ProposalBlockParts and keep waiting.
cs.ProposalBlock = nil
cs.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartsHeader)
cs.eventBus.PublishEventValidBlock(cs.RoundStateEvent())
cs.evsw.FireEvent(types.EventValidBlock, &cs.RoundState)
} else {
// We just need to keep waiting.
}
@ -1420,12 +1408,7 @@ func (cs *ConsensusState) defaultSetProposal(proposal *types.Proposal) error {
return nil
}
// We don't care about the proposal if we're already in cstypes.RoundStepCommit.
if cstypes.RoundStepCommit <= cs.Step {
return nil
}
// Verify POLRound, which must be -1 or between 0 inclusive and proposal.Round exclusive.
// Verify POLRound, which must be -1 or in range [0, proposal.Round).
if proposal.POLRound < -1 ||
(proposal.POLRound >= 0 && proposal.POLRound >= proposal.Round) {
return ErrInvalidProposalPOLRound
@ -1437,7 +1420,12 @@ func (cs *ConsensusState) defaultSetProposal(proposal *types.Proposal) error {
}
cs.Proposal = proposal
cs.ProposalBlockParts = types.NewPartSetFromHeader(proposal.BlockPartsHeader)
// We don't update cs.ProposalBlockParts if it is already set.
// This happens if we're already in cstypes.RoundStepCommit or if there is a valid block in the current round.
// TODO: We can check if Proposal is for a different block as this is a sign of misbehavior!
if cs.ProposalBlockParts == nil {
cs.ProposalBlockParts = types.NewPartSetFromHeader(proposal.BlockID.PartsHeader)
}
cs.Logger.Info("Received proposal", "proposal", proposal)
return nil
}
@ -1478,6 +1466,7 @@ func (cs *ConsensusState) addProposalBlockPart(msg *BlockPartMessage, peerID p2p
}
// NOTE: it's possible to receive complete proposal blocks for future rounds without having the proposal
cs.Logger.Info("Received complete proposal block", "height", cs.ProposalBlock.Height, "hash", cs.ProposalBlock.Hash())
cs.eventBus.PublishEventCompleteProposal(cs.RoundStateEvent())
// Update Valid* if we can.
prevotes := cs.Votes.Prevotes(cs.Round)
@ -1616,16 +1605,26 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
// Update Valid* if we can.
// NOTE: our proposal block may be nil or not what received a polka..
// TODO: we may want to still update the ValidBlock and obtain it via gossipping
if len(blockID.Hash) != 0 &&
(cs.ValidRound < vote.Round) &&
(vote.Round <= cs.Round) &&
cs.ProposalBlock.HashesTo(blockID.Hash) {
cs.Logger.Info("Updating ValidBlock because of POL.", "validRound", cs.ValidRound, "POLRound", vote.Round)
cs.ValidRound = vote.Round
cs.ValidBlock = cs.ProposalBlock
cs.ValidBlockParts = cs.ProposalBlockParts
if len(blockID.Hash) != 0 && (cs.ValidRound < vote.Round) && (vote.Round == cs.Round) {
if cs.ProposalBlock.HashesTo(blockID.Hash) {
cs.Logger.Info(
"Updating ValidBlock because of POL.", "validRound", cs.ValidRound, "POLRound", vote.Round)
cs.ValidRound = vote.Round
cs.ValidBlock = cs.ProposalBlock
cs.ValidBlockParts = cs.ProposalBlockParts
} else {
cs.Logger.Info(
"Valid block we don't know about. Set ProposalBlock=nil",
"proposal", cs.ProposalBlock.Hash(), "blockId", blockID.Hash)
// We're getting the wrong block.
cs.ProposalBlock = nil
}
if !cs.ProposalBlockParts.HasHeader(blockID.PartsHeader) {
cs.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartsHeader)
}
cs.evsw.FireEvent(types.EventValidBlock, &cs.RoundState)
cs.eventBus.PublishEventValidBlock(cs.RoundStateEvent())
}
}
@ -1634,7 +1633,8 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
// Round-skip if there is any 2/3+ of votes ahead of us
cs.enterNewRound(height, vote.Round)
} else if cs.Round == vote.Round && cstypes.RoundStepPrevote <= cs.Step { // current round
if prevotes.HasTwoThirdsMajority() {
blockID, ok := prevotes.TwoThirdsMajority()
if ok && (cs.isProposalComplete() || len(blockID.Hash) == 0) {
cs.enterPrecommit(height, vote.Round)
} else if prevotes.HasTwoThirdsAny() {
cs.enterPrevoteWait(height, vote.Round)


+ 193
- 4
consensus/state_test.go View File

@ -197,7 +197,9 @@ func TestStateBadProposal(t *testing.T) {
stateHash[0] = byte((stateHash[0] + 1) % 255)
propBlock.AppHash = stateHash
propBlockParts := propBlock.MakePartSet(partSize)
proposal := types.NewProposal(vs2.Height, round, propBlockParts.Header(), -1, types.BlockID{})
proposal := types.NewProposal(
vs2.Height, round, -1,
types.BlockID{propBlock.Hash(), propBlockParts.Header()})
if err := vs2.SignProposal(config.ChainID(), proposal); err != nil {
t.Fatal("failed to sign bad proposal", err)
}
@ -811,6 +813,7 @@ func TestStateLockPOLSafety2(t *testing.T) {
_, propBlock0 := decideProposal(cs1, vss[0], height, round)
propBlockHash0 := propBlock0.Hash()
propBlockParts0 := propBlock0.MakePartSet(partSize)
propBlockID0 := types.BlockID{propBlockHash0, propBlockParts0.Header()}
// the others sign a polka but we don't see it
prevotes := signVotes(types.PrevoteType, propBlockHash0, propBlockParts0.Header(), vs2, vs3, vs4)
@ -819,7 +822,6 @@ func TestStateLockPOLSafety2(t *testing.T) {
prop1, propBlock1 := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
propBlockHash1 := propBlock1.Hash()
propBlockParts1 := propBlock1.MakePartSet(partSize)
propBlockID1 := types.BlockID{propBlockHash1, propBlockParts1.Header()}
incrementRound(vs2, vs3, vs4)
@ -854,7 +856,7 @@ func TestStateLockPOLSafety2(t *testing.T) {
round = round + 1 // moving to the next round
// in round 2 we see the polkad block from round 0
newProp := types.NewProposal(height, round, propBlockParts0.Header(), 0, propBlockID1)
newProp := types.NewProposal(height, round, 0, propBlockID0)
if err := vs3.SignProposal(config.ChainID(), newProp); err != nil {
t.Fatal(err)
}
@ -909,7 +911,7 @@ func TestProposeValidBlock(t *testing.T) {
ensurePrevote(voteCh, height, round)
validatePrevote(t, cs1, round, vss[0], propBlockHash)
// the others sign a polka but we don't see it
// the others sign a polka
signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlock.MakePartSet(partSize).Header(), vs2, vs3, vs4)
ensurePrecommit(voteCh, height, round)
@ -964,6 +966,119 @@ func TestProposeValidBlock(t *testing.T) {
rs = cs1.GetRoundState()
assert.True(t, bytes.Equal(rs.ProposalBlock.Hash(), propBlockHash))
assert.True(t, bytes.Equal(rs.ProposalBlock.Hash(), rs.ValidBlock.Hash()))
assert.True(t, rs.Proposal.POLRound == rs.ValidRound)
assert.True(t, bytes.Equal(rs.Proposal.BlockID.Hash, rs.ValidBlock.Hash()))
}
// What we want:
// P0 miss to lock B but set valid block to B after receiving delayed prevote.
func TestSetValidBlockOnDelayedPrevote(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)
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock)
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()
propBlockParts := propBlock.MakePartSet(partSize)
ensurePrevote(voteCh, height, round)
validatePrevote(t, cs1, round, vss[0], propBlockHash)
// vs2 send prevote for propBlock
signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlockParts.Header(), vs2)
// vs3 send prevote nil
signAddVotes(cs1, types.PrevoteType, nil, types.PartSetHeader{}, vs3)
ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrevote.Nanoseconds())
ensurePrecommit(voteCh, height, round)
// we should have precommitted
validatePrecommit(t, cs1, round, -1, vss[0], nil, nil)
rs = cs1.GetRoundState()
assert.True(t, rs.ValidBlock == nil)
assert.True(t, rs.ValidBlockParts == nil)
assert.True(t, rs.ValidRound == -1)
// vs2 send (delayed) prevote for propBlock
signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlockParts.Header(), vs4)
ensureNewValidBlock(validBlockCh, height, round)
rs = cs1.GetRoundState()
assert.True(t, bytes.Equal(rs.ValidBlock.Hash(), propBlockHash))
assert.True(t, rs.ValidBlockParts.Header().Equals(propBlockParts.Header()))
assert.True(t, rs.ValidRound == round)
}
// What we want:
// P0 miss to lock B as Proposal Block is missing, but set valid block to B after
// receiving delayed Block Proposal.
func TestSetValidBlockOnDelayedProposal(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
timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait)
timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose)
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock)
voteCh := subscribeToVoter(cs1, cs1.privValidator.GetAddress())
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
round = round + 1 // move to round in which P0 is not proposer
incrementRound(vs2, vs3, vs4)
startTestRound(cs1, cs1.Height, round)
ensureNewRound(newRoundCh, height, round)
ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.TimeoutPropose.Nanoseconds())
ensurePrevote(voteCh, height, round)
validatePrevote(t, cs1, round, vss[0], nil)
prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round+1)
propBlockHash := propBlock.Hash()
propBlockParts := propBlock.MakePartSet(partSize)
// vs2, vs3 and vs4 send prevote for propBlock
signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4)
ensureNewValidBlock(validBlockCh, height, round)
ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrevote.Nanoseconds())
ensurePrecommit(voteCh, height, round)
validatePrecommit(t, cs1, round, -1, vss[0], nil, nil)
if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil {
t.Fatal(err)
}
ensureNewProposal(proposalCh, height, round)
rs := cs1.GetRoundState()
assert.True(t, bytes.Equal(rs.ValidBlock.Hash(), propBlockHash))
assert.True(t, rs.ValidBlockParts.Header().Equals(propBlockParts.Header()))
assert.True(t, rs.ValidRound == round)
}
// 4 vals, 3 Nil Precommits at P0
@ -1078,6 +1193,80 @@ func TestWaitTimeoutProposeOnNilPolkaForTheCurrentRound(t *testing.T) {
validatePrevote(t, cs1, round, vss[0], nil)
}
// What we want:
// P0 emit NewValidBlock event upon receiving 2/3+ Precommit for B but hasn't received block B yet
func TestEmitNewValidBlockEventOnCommitWithoutBlock(t *testing.T) {
cs1, vss := randConsensusState(4)
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
height, round := cs1.Height, 1
incrementRound(vs2, vs3, vs4)
partSize := types.BlockPartSizeBytes
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock)
_, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round)
propBlockHash := propBlock.Hash()
propBlockParts := propBlock.MakePartSet(partSize)
// start round in which PO is not proposer
startTestRound(cs1, height, round)
ensureNewRound(newRoundCh, height, round)
// vs2, vs3 and vs4 send precommit for propBlock
signAddVotes(cs1, types.PrecommitType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4)
ensureNewValidBlock(validBlockCh, height, round)
rs := cs1.GetRoundState()
assert.True(t, rs.Step == cstypes.RoundStepCommit)
assert.True(t, rs.ProposalBlock == nil)
assert.True(t, rs.ProposalBlockParts.Header().Equals(propBlockParts.Header()))
}
// What we want:
// P0 receives 2/3+ Precommit for B for round 0, while being in round 1. It emits NewValidBlock event.
// After receiving block, it executes block and moves to the next height.
func TestCommitFromPreviousRound(t *testing.T) {
cs1, vss := randConsensusState(4)
vs2, vs3, vs4 := vss[1], vss[2], vss[3]
height, round := cs1.Height, 1
partSize := types.BlockPartSizeBytes
newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound)
validBlockCh := subscribe(cs1.eventBus, types.EventQueryValidBlock)
proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal)
prop, propBlock := decideProposal(cs1, vs2, vs2.Height, vs2.Round)
propBlockHash := propBlock.Hash()
propBlockParts := propBlock.MakePartSet(partSize)
// start round in which PO is not proposer
startTestRound(cs1, height, round)
ensureNewRound(newRoundCh, height, round)
// vs2, vs3 and vs4 send precommit for propBlock for the previous round
signAddVotes(cs1, types.PrecommitType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4)
ensureNewValidBlock(validBlockCh, height, round)
rs := cs1.GetRoundState()
assert.True(t, rs.Step == cstypes.RoundStepCommit)
assert.True(t, rs.CommitRound == vs2.Round)
assert.True(t, rs.ProposalBlock == nil)
assert.True(t, rs.ProposalBlockParts.Header().Equals(propBlockParts.Header()))
if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil {
t.Fatal(err)
}
ensureNewProposal(proposalCh, height, round)
ensureNewRound(newRoundCh, height+1, 0)
}
//------------------------------------------------------------------------------------------
// SlashingSuite
// TODO: Slashing


+ 7
- 0
consensus/types/round_state.go View File

@ -26,8 +26,15 @@ const (
RoundStepPrecommitWait = RoundStepType(0x07) // Did receive any +2/3 precommits, start timeout
RoundStepCommit = RoundStepType(0x08) // Entered commit state machine
// NOTE: RoundStepNewHeight acts as RoundStepCommitWait.
// NOTE: Update IsValid method if you change this!
)
// IsValid returns true if the step is valid, false if unknown/undefined.
func (rs RoundStepType) IsValid() bool {
return uint8(rs) >= 0x01 && uint8(rs) <= 0x08
}
// String returns a string
func (rs RoundStepType) String() string {
switch rs {


+ 2
- 5
consensus/types/round_state_test.go View File

@ -63,11 +63,8 @@ func BenchmarkRoundStateDeepCopy(b *testing.B) {
// Random Proposal
proposal := &types.Proposal{
Timestamp: tmtime.Now(),
BlockPartsHeader: types.PartSetHeader{
Hash: cmn.RandBytes(20),
},
POLBlockID: blockID,
Signature: sig,
BlockID: blockID,
Signature: sig,
}
// Random HeightVoteSet
// TODO: hvs :=


+ 16
- 6
crypto/crypto.go View File

@ -1,21 +1,24 @@
package crypto
import (
"github.com/tendermint/tendermint/crypto/tmhash"
cmn "github.com/tendermint/tendermint/libs/common"
)
type PrivKey interface {
Bytes() []byte
Sign(msg []byte) ([]byte, error)
PubKey() PubKey
Equals(PrivKey) bool
}
const (
// AddressSize is the size of a pubkey address.
AddressSize = tmhash.TruncatedSize
)
// An address is a []byte, but hex-encoded even in JSON.
// []byte leaves us the option to change the address length.
// Use an alias so Unmarshal methods (with ptr receivers) are available too.
type Address = cmn.HexBytes
func AddressHash(bz []byte) Address {
return Address(tmhash.SumTruncated(bz))
}
type PubKey interface {
Address() Address
Bytes() []byte
@ -23,6 +26,13 @@ type PubKey interface {
Equals(PubKey) bool
}
type PrivKey interface {
Bytes() []byte
Sign(msg []byte) ([]byte, error)
PubKey() PubKey
Equals(PrivKey) bool
}
type Symmetric interface {
Keygen() []byte
Encrypt(plaintext []byte, secret []byte) (ciphertext []byte)


+ 1
- 1
crypto/ed25519/ed25519.go View File

@ -136,7 +136,7 @@ type PubKeyEd25519 [PubKeyEd25519Size]byte
// Address is the SHA256-20 of the raw pubkey bytes.
func (pubKey PubKeyEd25519) Address() crypto.Address {
return crypto.Address(tmhash.Sum(pubKey[:]))
return crypto.Address(tmhash.SumTruncated(pubKey[:]))
}
// Bytes marshals the PubKey using amino encoding.


+ 5
- 4
crypto/merkle/proof.go View File

@ -43,10 +43,11 @@ func (poz ProofOperators) Verify(root []byte, keypath string, args [][]byte) (er
for i, op := range poz {
key := op.GetKey()
if len(key) != 0 {
if !bytes.Equal(keys[0], key) {
return cmn.NewError("Key mismatch on operation #%d: expected %+v but %+v", i, []byte(keys[0]), []byte(key))
lastKey := keys[len(keys)-1]
if !bytes.Equal(lastKey, key) {
return cmn.NewError("Key mismatch on operation #%d: expected %+v but got %+v", i, string(lastKey), string(key))
}
keys = keys[1:]
keys = keys[:len(keys)-1]
}
args, err = op.Run(args)
if err != nil {
@ -54,7 +55,7 @@ func (poz ProofOperators) Verify(root []byte, keypath string, args [][]byte) (er
}
}
if !bytes.Equal(root, args[0]) {
return cmn.NewError("Calculated root hash is invalid: expected %+v but %+v", root, args[0])
return cmn.NewError("Calculated root hash is invalid: expected %+v but got %+v", root, args[0])
}
if len(keys) != 0 {
return cmn.NewError("Keypath not consumed all")


+ 136
- 0
crypto/merkle/proof_test.go View File

@ -0,0 +1,136 @@
package merkle
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/tendermint/go-amino"
cmn "github.com/tendermint/tendermint/libs/common"
)
const ProofOpDomino = "test:domino"
// Expects given input, produces given output.
// Like the game dominos.
type DominoOp struct {
key string // unexported, may be empty
Input string
Output string
}
func NewDominoOp(key, input, output string) DominoOp {
return DominoOp{
key: key,
Input: input,
Output: output,
}
}
func DominoOpDecoder(pop ProofOp) (ProofOperator, error) {
if pop.Type != ProofOpDomino {
panic("unexpected proof op type")
}
var op DominoOp // a bit strange as we'll discard this, but it works.
err := amino.UnmarshalBinaryLengthPrefixed(pop.Data, &op)
if err != nil {
return nil, cmn.ErrorWrap(err, "decoding ProofOp.Data into SimpleValueOp")
}
return NewDominoOp(string(pop.Key), op.Input, op.Output), nil
}
func (dop DominoOp) ProofOp() ProofOp {
bz := amino.MustMarshalBinaryLengthPrefixed(dop)
return ProofOp{
Type: ProofOpDomino,
Key: []byte(dop.key),
Data: bz,
}
}
func (dop DominoOp) Run(input [][]byte) (output [][]byte, err error) {
if len(input) != 1 {
return nil, cmn.NewError("Expected input of length 1")
}
if string(input[0]) != dop.Input {
return nil, cmn.NewError("Expected input %v, got %v",
dop.Input, string(input[0]))
}
return [][]byte{[]byte(dop.Output)}, nil
}
func (dop DominoOp) GetKey() []byte {
return []byte(dop.key)
}
//----------------------------------------
func TestProofOperators(t *testing.T) {
var err error
// ProofRuntime setup
// TODO test this somehow.
// prt := NewProofRuntime()
// prt.RegisterOpDecoder(ProofOpDomino, DominoOpDecoder)
// ProofOperators setup
op1 := NewDominoOp("KEY1", "INPUT1", "INPUT2")
op2 := NewDominoOp("KEY2", "INPUT2", "INPUT3")
op3 := NewDominoOp("", "INPUT3", "INPUT4")
op4 := NewDominoOp("KEY4", "INPUT4", "OUTPUT4")
// Good
popz := ProofOperators([]ProofOperator{op1, op2, op3, op4})
err = popz.Verify(bz("OUTPUT4"), "/KEY4/KEY2/KEY1", [][]byte{bz("INPUT1")})
assert.Nil(t, err)
err = popz.VerifyValue(bz("OUTPUT4"), "/KEY4/KEY2/KEY1", bz("INPUT1"))
assert.Nil(t, err)
// BAD INPUT
err = popz.Verify(bz("OUTPUT4"), "/KEY4/KEY2/KEY1", [][]byte{bz("INPUT1_WRONG")})
assert.NotNil(t, err)
err = popz.VerifyValue(bz("OUTPUT4"), "/KEY4/KEY2/KEY1", bz("INPUT1_WRONG"))
assert.NotNil(t, err)
// BAD KEY 1
err = popz.Verify(bz("OUTPUT4"), "/KEY3/KEY2/KEY1", [][]byte{bz("INPUT1")})
assert.NotNil(t, err)
// BAD KEY 2
err = popz.Verify(bz("OUTPUT4"), "KEY4/KEY2/KEY1", [][]byte{bz("INPUT1")})
assert.NotNil(t, err)
// BAD KEY 3
err = popz.Verify(bz("OUTPUT4"), "/KEY4/KEY2/KEY1/", [][]byte{bz("INPUT1")})
assert.NotNil(t, err)
// BAD KEY 4
err = popz.Verify(bz("OUTPUT4"), "//KEY4/KEY2/KEY1", [][]byte{bz("INPUT1")})
assert.NotNil(t, err)
// BAD OUTPUT 1
err = popz.Verify(bz("OUTPUT4_WRONG"), "/KEY4/KEY2/KEY1", [][]byte{bz("INPUT1")})
assert.NotNil(t, err)
// BAD OUTPUT 2
err = popz.Verify(bz(""), "/KEY4/KEY2/KEY1", [][]byte{bz("INPUT1")})
assert.NotNil(t, err)
// BAD POPZ 1
popz = []ProofOperator{op1, op2, op4}
err = popz.Verify(bz("OUTPUT4"), "/KEY4/KEY2/KEY1", [][]byte{bz("INPUT1")})
assert.NotNil(t, err)
// BAD POPZ 2
popz = []ProofOperator{op4, op3, op2, op1}
err = popz.Verify(bz("OUTPUT4"), "/KEY4/KEY2/KEY1", [][]byte{bz("INPUT1")})
assert.NotNil(t, err)
// BAD POPZ 3
popz = []ProofOperator{}
err = popz.Verify(bz("OUTPUT4"), "/KEY4/KEY2/KEY1", [][]byte{bz("INPUT1")})
assert.NotNil(t, err)
}
func bz(s string) []byte {
return []byte(s)
}

+ 6
- 6
crypto/merkle/simple_map_test.go View File

@ -13,14 +13,14 @@ func TestSimpleMap(t *testing.T) {
values []string // each string gets converted to []byte in test
want string
}{
{[]string{"key1"}, []string{"value1"}, "fa9bc106ffd932d919bee935ceb6cf2b3dd72d8f"},
{[]string{"key1"}, []string{"value2"}, "e00e7dcfe54e9fafef5111e813a587f01ba9c3e8"},
{[]string{"key1"}, []string{"value1"}, "321d150de16dceb51c72981b432b115045383259b1a550adf8dc80f927508967"},
{[]string{"key1"}, []string{"value2"}, "2a9e4baf321eac99f6eecc3406603c14bc5e85bb7b80483cbfc75b3382d24a2f"},
// swap order with 2 keys
{[]string{"key1", "key2"}, []string{"value1", "value2"}, "eff12d1c703a1022ab509287c0f196130123d786"},
{[]string{"key2", "key1"}, []string{"value2", "value1"}, "eff12d1c703a1022ab509287c0f196130123d786"},
{[]string{"key1", "key2"}, []string{"value1", "value2"}, "c4d8913ab543ba26aa970646d4c99a150fd641298e3367cf68ca45fb45a49881"},
{[]string{"key2", "key1"}, []string{"value2", "value1"}, "c4d8913ab543ba26aa970646d4c99a150fd641298e3367cf68ca45fb45a49881"},
// swap order with 3 keys
{[]string{"key1", "key2", "key3"}, []string{"value1", "value2", "value3"}, "b2c62a277c08dbd2ad73ca53cd1d6bfdf5830d26"},
{[]string{"key1", "key3", "key2"}, []string{"value1", "value3", "value2"}, "b2c62a277c08dbd2ad73ca53cd1d6bfdf5830d26"},
{[]string{"key1", "key2", "key3"}, []string{"value1", "value2", "value3"}, "b23cef00eda5af4548a213a43793f2752d8d9013b3f2b64bc0523a4791196268"},
{[]string{"key1", "key3", "key2"}, []string{"value1", "value3", "value2"}, "b23cef00eda5af4548a213a43793f2752d8d9013b3f2b64bc0523a4791196268"},
}
for i, tc := range tests {
db := newSimpleMap()


+ 25
- 8
crypto/tmhash/hash.go View File

@ -6,10 +6,27 @@ import (
)
const (
Size = 20
Size = sha256.Size
BlockSize = sha256.BlockSize
)
// New returns a new hash.Hash.
func New() hash.Hash {
return sha256.New()
}
// Sum returns the SHA256 of the bz.
func Sum(bz []byte) []byte {
h := sha256.Sum256(bz)
return h[:]
}
//-------------------------------------------------------------
const (
TruncatedSize = 20
)
type sha256trunc struct {
sha256 hash.Hash
}
@ -19,7 +36,7 @@ func (h sha256trunc) Write(p []byte) (n int, err error) {
}
func (h sha256trunc) Sum(b []byte) []byte {
shasum := h.sha256.Sum(b)
return shasum[:Size]
return shasum[:TruncatedSize]
}
func (h sha256trunc) Reset() {
@ -27,22 +44,22 @@ func (h sha256trunc) Reset() {
}
func (h sha256trunc) Size() int {
return Size
return TruncatedSize
}
func (h sha256trunc) BlockSize() int {
return h.sha256.BlockSize()
}
// New returns a new hash.Hash.
func New() hash.Hash {
// NewTruncated returns a new hash.Hash.
func NewTruncated() hash.Hash {
return sha256trunc{
sha256: sha256.New(),
}
}
// Sum returns the first 20 bytes of SHA256 of the bz.
func Sum(bz []byte) []byte {
// SumTruncated returns the first 20 bytes of SHA256 of the bz.
func SumTruncated(bz []byte) []byte {
hash := sha256.Sum256(bz)
return hash[:Size]
return hash[:TruncatedSize]
}

+ 21
- 2
crypto/tmhash/hash_test.go View File

@ -14,10 +14,29 @@ func TestHash(t *testing.T) {
hasher.Write(testVector)
bz := hasher.Sum(nil)
bz2 := tmhash.Sum(testVector)
hasher = sha256.New()
hasher.Write(testVector)
bz3 := hasher.Sum(nil)
assert.Equal(t, bz, bz2)
assert.Equal(t, bz, bz3)
}
func TestHashTruncated(t *testing.T) {
testVector := []byte("abc")
hasher := tmhash.NewTruncated()
hasher.Write(testVector)
bz := hasher.Sum(nil)
bz2 := tmhash.SumTruncated(testVector)
hasher = sha256.New()
hasher.Write(testVector)
bz2 := hasher.Sum(nil)
bz2 = bz2[:20]
bz3 := hasher.Sum(nil)
bz3 = bz3[:tmhash.TruncatedSize]
assert.Equal(t, bz, bz2)
assert.Equal(t, bz, bz3)
}

+ 4
- 4
docs/architecture/adr-001-logging.md View File

@ -52,13 +52,13 @@ On top of this interface, we will need to implement a stdout logger, which will
Many people say that they like the current output, so let's stick with it.
```
NOTE[04-25|14:45:08] ABCI Replay Blocks module=consensus appHeight=0 storeHeight=0 stateHeight=0
NOTE[2017-04-25|14:45:08] ABCI Replay Blocks module=consensus appHeight=0 storeHeight=0 stateHeight=0
```
Couple of minor changes:
```
I[04-25|14:45:08.322] ABCI Replay Blocks module=consensus appHeight=0 storeHeight=0 stateHeight=0
I[2017-04-25|14:45:08.322] ABCI Replay Blocks module=consensus appHeight=0 storeHeight=0 stateHeight=0
```
Notice the level is encoded using only one char plus milliseconds.
@ -155,14 +155,14 @@ Important keyvals should go first. Example:
```
correct
I[04-25|14:45:08.322] ABCI Replay Blocks module=consensus instance=1 appHeight=0 storeHeight=0 stateHeight=0
I[2017-04-25|14:45:08.322] ABCI Replay Blocks module=consensus instance=1 appHeight=0 storeHeight=0 stateHeight=0
```
not
```
wrong
I[04-25|14:45:08.322] ABCI Replay Blocks module=consensus appHeight=0 storeHeight=0 stateHeight=0 instance=1
I[2017-04-25|14:45:08.322] ABCI Replay Blocks module=consensus appHeight=0 storeHeight=0 stateHeight=0 instance=1
```
for that in most cases you'll need to add `instance` field to a logger upon creating, not when u log a particular message:


+ 122
- 0
docs/architecture/adr-033-pubsub.md View File

@ -0,0 +1,122 @@
# ADR 033: pubsub 2.0
Author: Anton Kaliaev (@melekes)
## Changelog
02-10-2018: Initial draft
## Context
Since the initial version of the pubsub, there's been a number of issues
raised: #951, #1879, #1880. Some of them are high-level issues questioning the
core design choices made. Others are minor and mostly about the interface of
`Subscribe()` / `Publish()` functions.
### Sync vs Async
Now, when publishing a message to subscribers, we can do it in a goroutine:
_using channels for data transmission_
```go
for each subscriber {
out := subscriber.outc
go func() {
out <- msg
}
}
```
_by invoking callback functions_
```go
for each subscriber {
go subscriber.callbackFn()
}
```
This gives us greater performance and allows us to avoid "slow client problem"
(when other subscribers have to wait for a slow subscriber). A pool of
goroutines can be used to avoid uncontrolled memory growth.
In certain cases, this is what you want. But in our case, because we need
strict ordering of events (if event A was published before B, the guaranteed
delivery order will be A -> B), we can't use goroutines.
There is also a question whenever we should have a non-blocking send:
```go
for each subscriber {
out := subscriber.outc
select {
case out <- msg:
default:
log("subscriber %v buffer is full, skipping...")
}
}
```
This fixes the "slow client problem", but there is no way for a slow client to
know if it had missed a message. On the other hand, if we're going to stick
with blocking send, **devs must always ensure subscriber's handling code does not
block**. As you can see, there is an implicit choice between ordering guarantees
and using goroutines.
The interim option is to run goroutines pool for a single message, wait for all
goroutines to finish. This will solve "slow client problem", but we'd still
have to wait `max(goroutine_X_time)` before we can publish the next message.
My opinion: not worth doing.
### Channels vs Callbacks
Yet another question is whether we should use channels for message transmission or
call subscriber-defined callback functions. Callback functions give subscribers
more flexibility - you can use mutexes in there, channels, spawn goroutines,
anything you really want. But they also carry local scope, which can result in
memory leaks and/or memory usage increase.
Go channels are de-facto standard for carrying data between goroutines.
**Question: Is it worth switching to callback functions?**
### Why `Subscribe()` accepts an `out` channel?
Because in our tests, we create buffered channels (cap: 1). Alternatively, we
can make capacity an argument.
## Decision
Change Subscribe() function to return out channel:
```go
// outCap can be used to set capacity of out channel (unbuffered by default).
Subscribe(ctx context.Context, clientID string, query Query, outCap... int) (out <-chan interface{}, err error) {
```
It's more idiomatic since we're closing it during Unsubscribe/UnsubscribeAll calls.
Also, we should make tags available to subscribers:
```go
type MsgAndTags struct {
Msg interface{}
Tags TagMap
}
// outCap can be used to set capacity of out channel (unbuffered by default).
Subscribe(ctx context.Context, clientID string, query Query, outCap... int) (out <-chan MsgAndTags, err error) {
```
## Status
In review
## Consequences
### Positive
- more idiomatic interface
- subscribers know what tags msg was published with
### Negative
### Neutral

+ 4
- 0
docs/architecture/adr-template.md View File

@ -1,5 +1,9 @@
# ADR 000: Template for an ADR
Author:
## Changelog
## Context
## Decision


+ 10
- 3
docs/spec/abci/abci.md View File

@ -441,11 +441,12 @@ Commit are included in the header of the next block.
### ConsensusParams
- **Fields**:
- `BlockSize (BlockSize)`: Parameters limiting the size of a block.
- `EvidenceParams (EvidenceParams)`: Parameters limiting the validity of
- `BlockSize (BlockSizeParams)`: Parameters limiting the size of a block.
- `Evidence (EvidenceParams)`: Parameters limiting the validity of
evidence of byzantine behaviour.
- `Validator (ValidatorParams)`: Parameters limitng the types of pubkeys validators can use.
### BlockSize
### BlockSizeParams
- **Fields**:
- `MaxBytes (int64)`: Max size of a block, in bytes.
@ -463,6 +464,12 @@ Commit are included in the header of the next block.
similar mechanism for handling Nothing-At-Stake attacks.
- NOTE: this should change to time (instead of blocks)!
### ValidatorParams
- **Fields**:
- `PubKeyTypes ([]string)`: List of accepted pubkey types. Uses same
naming as `PubKey.Type`.
### Proof
- **Fields**:


+ 9
- 9
docs/spec/blockchain/blockchain.md View File

@ -146,14 +146,14 @@ The vote includes information about the validator signing it.
```go
type Vote struct {
ValidatorAddress []byte
ValidatorIndex int
Height int64
Round int
Timestamp Time
Type int8
BlockID BlockID
Signature []byte
Type SignedMsgType // byte
Height int64
Round int
Timestamp time.Time
BlockID BlockID
ValidatorAddress Address
ValidatorIndex int
Signature []byte
}
```
@ -344,7 +344,7 @@ next validator sets Merkle root.
### ConsensusParamsHash
```go
block.ConsensusParamsHash == tmhash(amino(state.ConsensusParams))
block.ConsensusParamsHash == TMHASH(amino(state.ConsensusParams))
```
Hash of the amino-encoded consensus parameters.


+ 3
- 4
docs/spec/blockchain/encoding.md View File

@ -176,13 +176,12 @@ greater, for example:
h0 h1 h3 h4 h0 h1 h2 h3 h4 h5
```
Tendermint always uses the `TMHASH` hash function, which is the first 20-bytes
of the SHA256:
Tendermint always uses the `TMHASH` hash function, which is equivalent to
SHA256:
```
func TMHASH(bz []byte) []byte {
shasum := SHA256(bz)
return shasum[:20]
return SHA256(bz)
}
```


+ 2
- 2
docs/spec/blockchain/state.md View File

@ -56,8 +56,8 @@ type Validator struct {
}
```
When hashing the Validator struct, the pubkey is not hashed,
because the address is already the hash of the pubkey.
When hashing the Validator struct, the address is not included,
because it is redundant with the pubkey.
The `state.Validators`, `state.LastValidators`, and `state.NextValidators`, must always by sorted by validator address,
so that there is a canonical order for computing the SimpleMerkleRoot.


+ 9
- 6
docs/spec/reactors/consensus/consensus-reactor.md View File

@ -129,13 +129,16 @@ handleMessage(msg):
Reset prs.CatchupCommitRound and prs.CatchupCommit
```
### CommitStepMessage handler
### NewValidBlockMessage handler
```
handleMessage(msg):
if prs.Height == msg.Height then
prs.ProposalBlockPartsHeader = msg.BlockPartsHeader
prs.ProposalBlockParts = msg.BlockParts
if prs.Height != msg.Height then return
if prs.Round != msg.Round && !msg.IsCommit then return
prs.ProposalBlockPartsHeader = msg.BlockPartsHeader
prs.ProposalBlockParts = msg.BlockParts
```
### HasVoteMessage handler
@ -161,8 +164,8 @@ handleMessage(msg):
handleMessage(msg):
if prs.Height != msg.Height || prs.Round != msg.Round || prs.Proposal then return
prs.Proposal = true
prs.ProposalBlockPartsHeader = msg.BlockPartsHeader
prs.ProposalBlockParts = empty set
if prs.ProposalBlockParts == empty set then // otherwise it is set in NewValidBlockMessage handler
prs.ProposalBlockPartsHeader = msg.BlockPartsHeader
prs.ProposalPOLRound = msg.POLRound
prs.ProposalPOL = nil
Send msg through internal peerMsgQueue to ConsensusState service


+ 16
- 17
docs/spec/reactors/consensus/consensus.md View File

@ -26,7 +26,7 @@ only to a subset of processes called peers. By the gossiping protocol, a validat
all needed information (`ProposalMessage`, `VoteMessage` and `BlockPartMessage`) so they can
reach agreement on some block, and also obtain the content of the chosen block (block parts). As
part of the gossiping protocol, processes also send auxiliary messages that inform peers about the
executed steps of the core consensus algorithm (`NewRoundStepMessage` and `CommitStepMessage`), and
executed steps of the core consensus algorithm (`NewRoundStepMessage` and `NewValidBlockMessage`), and
also messages that inform peers what votes the process has seen (`HasVoteMessage`,
`VoteSetMaj23Message` and `VoteSetBitsMessage`). These messages are then used in the gossiping
protocol to determine what messages a process should send to its peers.
@ -47,25 +47,21 @@ type ProposalMessage struct {
### Proposal
Proposal contains height and round for which this proposal is made, BlockID as a unique identifier
of proposed block, timestamp, and two fields (POLRound and POLBlockID) that are needed for
termination of the consensus. The message is signed by the validator private key.
of proposed block, timestamp, and POLRound (a so-called Proof-of-Lock (POL) round) that is needed for
termination of the consensus. If POLRound >= 0, then BlockID corresponds to the block that
is locked in POLRound. The message is signed by the validator private key.
```go
type Proposal struct {
Height int64
Round int
Timestamp Time
BlockID BlockID
POLRound int
POLBlockID BlockID
BlockID BlockID
Timestamp Time
Signature Signature
}
```
NOTE: In the current version of the Tendermint, the consensus value in proposal is represented with
PartSetHeader, and with BlockID in vote message. It should be aligned as suggested in this spec as
BlockID contains PartSetHeader.
## VoteMessage
VoteMessage is sent to vote for some block (or to inform others that a process does not vote in the
@ -136,23 +132,26 @@ type NewRoundStepMessage struct {
}
```
## CommitStepMessage
## NewValidBlockMessage
CommitStepMessage is sent when an agreement on some block is reached. It contains height for which
agreement is reached, block parts header that describes the decided block and is used to obtain all
NewValidBlockMessage is sent when a validator observes a valid block B in some round r,
i.e., there is a Proposal for block B and 2/3+ prevotes for the block B in the round r.
It contains height and round in which valid block is observed, block parts header that describes
the valid block and is used to obtain all
block parts, and a bit array of the block parts a process currently has, so its peers can know what
parts it is missing so they can send them.
In case the block is also committed, then IsCommit flag is set to true.
```go
type CommitStepMessage struct {
type NewValidBlockMessage struct {
Height int64
BlockID BlockID
Round int
BlockPartsHeader PartSetHeader
BlockParts BitArray
IsCommit bool
}
```
TODO: We use BlockID instead of BlockPartsHeader (in current implementation) for symmetry.
## ProposalPOLMessage
ProposalPOLMessage is sent when a previous block is re-proposed.


+ 1
- 1
evidence/pool.go View File

@ -127,7 +127,7 @@ func (evpool *EvidencePool) MarkEvidenceAsCommitted(height int64, evidence []typ
}
// remove committed evidence from the clist
maxAge := evpool.State().ConsensusParams.EvidenceParams.MaxAge
maxAge := evpool.State().ConsensusParams.Evidence.MaxAge
evpool.removeEvidence(height, maxAge, blockEvidenceMap)
}


+ 1
- 1
evidence/pool_test.go View File

@ -38,7 +38,7 @@ func initializeValidatorState(valAddr []byte, height int64) dbm.DB {
NextValidators: valSet.CopyIncrementAccum(1),
LastHeightValidatorsChanged: 1,
ConsensusParams: types.ConsensusParams{
EvidenceParams: types.EvidenceParams{
Evidence: types.EvidenceParams{
MaxAge: 1000000,
},
},


+ 22
- 3
evidence/reactor.go View File

@ -74,6 +74,13 @@ func (evR *EvidenceReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
evR.Switch.StopPeerForError(src, err)
return
}
if err = msg.ValidateBasic(); err != nil {
evR.Logger.Error("Peer sent us invalid msg", "peer", src, "msg", msg, "err", err)
evR.Switch.StopPeerForError(src, err)
return
}
evR.Logger.Debug("Receive", "src", src, "chId", chID, "msg", msg)
switch msg := msg.(type) {
@ -164,7 +171,7 @@ func (evR EvidenceReactor) checkSendEvidenceMessage(peer p2p.Peer, ev types.Evid
// NOTE: We only send evidence to peers where
// peerHeight - maxAge < evidenceHeight < peerHeight
maxAge := evR.evpool.State().ConsensusParams.EvidenceParams.MaxAge
maxAge := evR.evpool.State().ConsensusParams.Evidence.MaxAge
peerHeight := peerState.GetHeight()
if peerHeight < evHeight {
// peer is behind. sleep while he catches up
@ -191,7 +198,9 @@ type PeerState interface {
// Messages
// EvidenceMessage is a message sent or received by the EvidenceReactor.
type EvidenceMessage interface{}
type EvidenceMessage interface {
ValidateBasic() error
}
func RegisterEvidenceMessages(cdc *amino.Codec) {
cdc.RegisterInterface((*EvidenceMessage)(nil), nil)
@ -209,11 +218,21 @@ func decodeMsg(bz []byte) (msg EvidenceMessage, err error) {
//-------------------------------------
// EvidenceMessage contains a list of evidence.
// EvidenceListMessage contains a list of evidence.
type EvidenceListMessage struct {
Evidence []types.Evidence
}
// ValidateBasic performs basic validation.
func (m *EvidenceListMessage) ValidateBasic() error {
for i, ev := range m.Evidence {
if err := ev.ValidateBasic(); err != nil {
return fmt.Errorf("Invalid evidence (#%d): %v", i, err)
}
}
return nil
}
// String returns a string representation of the EvidenceListMessage.
func (m *EvidenceListMessage) String() string {
return fmt.Sprintf("[EvidenceListMessage %v]", m.Evidence)


+ 78
- 0
libs/fail/fail.go View File

@ -0,0 +1,78 @@
package fail
import (
"fmt"
"math/rand"
"os"
"strconv"
)
var callIndexToFail int
func init() {
callIndexToFailS := os.Getenv("FAIL_TEST_INDEX")
if callIndexToFailS == "" {
callIndexToFail = -1
} else {
var err error
callIndexToFail, err = strconv.Atoi(callIndexToFailS)
if err != nil {
callIndexToFail = -1
}
}
}
// Fail when FAIL_TEST_INDEX == callIndex
var (
callIndex int //indexes Fail calls
callRandIndex int // indexes a run of FailRand calls
callRandIndexToFail = -1 // the callRandIndex to fail on in FailRand
)
func Fail() {
if callIndexToFail < 0 {
return
}
if callIndex == callIndexToFail {
Exit()
}
callIndex += 1
}
// FailRand should be called n successive times.
// It will fail on a random one of those calls
// n must be greater than 0
func FailRand(n int) {
if callIndexToFail < 0 {
return
}
if callRandIndexToFail < 0 {
// first call in the loop, pick a random index to fail at
callRandIndexToFail = rand.Intn(n)
callRandIndex = 0
}
if callIndex == callIndexToFail {
if callRandIndex == callRandIndexToFail {
Exit()
}
}
callRandIndex += 1
if callRandIndex == n {
callIndex += 1
}
}
func Exit() {
fmt.Printf("*** fail-test %d ***\n", callIndex)
proc, _ := os.FindProcess(os.Getpid())
proc.Signal(os.Interrupt)
// panic(fmt.Sprintf("*** fail-test %d ***", callIndex))
}

+ 3
- 3
libs/log/tmfmt_logger.go View File

@ -84,13 +84,13 @@ func (l tmfmtLogger) Log(keyvals ...interface{}) error {
// Form a custom Tendermint line
//
// Example:
// D[05-02|11:06:44.322] Stopping AddrBook (ignoring: already stopped)
// D[2016-05-02|11:06:44.322] Stopping AddrBook (ignoring: already stopped)
//
// Description:
// D - first character of the level, uppercase (ASCII only)
// [05-02|11:06:44.322] - our time format (see https://golang.org/src/time/format.go)
// [2016-05-02|11:06:44.322] - our time format (see https://golang.org/src/time/format.go)
// Stopping ... - message
enc.buf.WriteString(fmt.Sprintf("%c[%s] %-44s ", lvl[0]-32, time.Now().Format("01-02|15:04:05.000"), msg))
enc.buf.WriteString(fmt.Sprintf("%c[%s] %-44s ", lvl[0]-32, time.Now().Format("2016-01-02|15:04:05.000"), msg))
if module != unknown {
enc.buf.WriteString("module=" + module + " ")


+ 1
- 2
p2p/key.go View File

@ -8,7 +8,6 @@ import (
crypto "github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/tmhash"
cmn "github.com/tendermint/tendermint/libs/common"
)
@ -17,7 +16,7 @@ type ID string
// IDByteLength is the length of a crypto.Address. Currently only 20.
// TODO: support other length addresses ?
const IDByteLength = tmhash.Size
const IDByteLength = crypto.AddressSize
//------------------------------------------------------------------------------
// Persistent peer ID


+ 1
- 1
p2p/metrics.go View File

@ -62,7 +62,7 @@ func PrometheusMetrics(namespace string) *Metrics {
// NopMetrics returns no-op Metrics.
func NopMetrics() *Metrics {
return &Metrics{
Peers: discard.NewGauge(),
Peers: discard.NewGauge(),
PeerReceiveBytesTotal: discard.NewCounter(),
PeerSendBytesTotal: discard.NewCounter(),
PeerPendingSendBytes: discard.NewGauge(),


+ 4
- 0
p2p/pex/addrbook.go View File

@ -648,6 +648,10 @@ func (a *addrBook) addAddress(addr, src *p2p.NetAddress) error {
return ErrAddrBookNonRoutable{addr}
}
if !addr.Valid() {
return ErrAddrBookInvalidAddr{addr}
}
// TODO: we should track ourAddrs by ID and by IP:PORT and refuse both.
if _, ok := a.ourAddrs[addr.String()]; ok {
return ErrAddrBookSelf{addr}


+ 8
- 0
p2p/pex/errors.go View File

@ -46,3 +46,11 @@ type ErrAddrBookNilAddr struct {
func (err ErrAddrBookNilAddr) Error() string {
return fmt.Sprintf("Cannot add a nil address. Got (addr, src) = (%v, %v)", err.Addr, err.Src)
}
type ErrAddrBookInvalidAddr struct {
Addr *p2p.NetAddress
}
func (err ErrAddrBookInvalidAddr) Error() string {
return fmt.Sprintf("Cannot add invalid address %v", err.Addr)
}

+ 22
- 6
p2p/pex/pex_reactor.go View File

@ -288,21 +288,37 @@ func (r *PEXReactor) RequestAddrs(p Peer) {
func (r *PEXReactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error {
id := string(src.ID())
if !r.requestsSent.Has(id) {
return cmn.NewError("Received unsolicited pexAddrsMessage")
return errors.New("Unsolicited pexAddrsMessage")
}
r.requestsSent.Delete(id)
srcAddr := src.NodeInfo().NetAddress()
for _, netAddr := range addrs {
// NOTE: GetSelection methods should never return nil addrs
// Validate netAddr. Disconnect from a peer if it sends us invalid data.
if netAddr == nil {
return cmn.NewError("received nil addr")
return errors.New("nil address in pexAddrsMessage")
}
// TODO: extract validating logic from NewNetAddressStringWithOptionalID
// and put it in netAddr#Valid (#2722)
na, err := p2p.NewNetAddressString(netAddr.String())
if err != nil {
return fmt.Errorf("%s address in pexAddrsMessage is invalid: %v",
netAddr.String(),
err,
)
}
err := r.book.AddAddress(netAddr, srcAddr)
r.logErrAddrBook(err)
// NOTE: we check netAddr validity and routability in book#AddAddress.
err = r.book.AddAddress(na, srcAddr)
if err != nil {
r.logErrAddrBook(err)
// XXX: should we be strict about incoming data and disconnect from a
// peer here too?
continue
}
// If this address came from a seed node, try to connect to it without waiting.
// If this address came from a seed node, try to connect to it without
// waiting.
for _, seedAddr := range r.seedAddrs {
if seedAddr.Equals(srcAddr) {
r.ensurePeers()


+ 8
- 8
privval/priv_validator_test.go View File

@ -140,8 +140,8 @@ func TestSignProposal(t *testing.T) {
require.Nil(t, err)
privVal := GenFilePV(tempFile.Name())
block1 := types.PartSetHeader{5, []byte{1, 2, 3}}
block2 := types.PartSetHeader{10, []byte{3, 2, 1}}
block1 := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{5, []byte{1, 2, 3}}}
block2 := types.BlockID{[]byte{3, 2, 1}, types.PartSetHeader{10, []byte{3, 2, 1}}}
height, round := int64(10), 1
// sign a proposal for first time
@ -179,7 +179,7 @@ func TestDifferByTimestamp(t *testing.T) {
require.Nil(t, err)
privVal := GenFilePV(tempFile.Name())
block1 := types.PartSetHeader{5, []byte{1, 2, 3}}
block1 := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{5, []byte{1, 2, 3}}}
height, round := int64(10), 1
chainID := "mychainid"
@ -241,11 +241,11 @@ func newVote(addr types.Address, idx int, height int64, round int, typ byte, blo
}
}
func newProposal(height int64, round int, partsHeader types.PartSetHeader) *types.Proposal {
func newProposal(height int64, round int, blockID types.BlockID) *types.Proposal {
return &types.Proposal{
Height: height,
Round: round,
BlockPartsHeader: partsHeader,
Timestamp: tmtime.Now(),
Height: height,
Round: round,
BlockID: blockID,
Timestamp: tmtime.Now(),
}
}

+ 2
- 2
state/execution.go View File

@ -4,9 +4,9 @@ import (
"fmt"
"time"
"github.com/ebuchman/fail-test"
abci "github.com/tendermint/tendermint/abci/types"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/fail"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/mempool"
"github.com/tendermint/tendermint/proxy"
@ -186,7 +186,7 @@ func (blockExec *BlockExecutor) Commit(
state.Validators.Size(),
),
),
mempool.PostCheckMaxGas(state.ConsensusParams.MaxGas),
mempool.PostCheckMaxGas(state.ConsensusParams.BlockSize.MaxGas),
)
return res.Data, err


+ 4
- 4
state/state_test.go View File

@ -390,11 +390,11 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) {
func makeParams(blockBytes, blockGas, evidenceAge int64) types.ConsensusParams {
return types.ConsensusParams{
BlockSize: types.BlockSize{
BlockSize: types.BlockSizeParams{
MaxBytes: blockBytes,
MaxGas: blockGas,
},
EvidenceParams: types.EvidenceParams{
Evidence: types.EvidenceParams{
MaxAge: evidenceAge,
},
}
@ -416,7 +416,7 @@ func TestApplyUpdates(t *testing.T) {
1: {initParams, abci.ConsensusParams{}, initParams},
2: {initParams,
abci.ConsensusParams{
BlockSize: &abci.BlockSize{
BlockSize: &abci.BlockSizeParams{
MaxBytes: 44,
MaxGas: 55,
},
@ -424,7 +424,7 @@ func TestApplyUpdates(t *testing.T) {
makeParams(44, 55, 3)},
3: {initParams,
abci.ConsensusParams{
EvidenceParams: &abci.EvidenceParams{
Evidence: &abci.EvidenceParams{
MaxAge: 66,
},
},


+ 1
- 1
state/store.go View File

@ -251,7 +251,7 @@ func LoadConsensusParams(db dbm.DB, height int64) (types.ConsensusParams, error)
return empty, ErrNoConsensusParamsForHeight{height}
}
if paramsInfo.ConsensusParams == empty {
if paramsInfo.ConsensusParams.Equals(&empty) {
paramsInfo2 := loadConsensusParamsInfo(db, paramsInfo.LastHeightChanged)
if paramsInfo2 == nil {
panic(


+ 23
- 33
state/validation.go View File

@ -5,7 +5,7 @@ import (
"errors"
"fmt"
"github.com/tendermint/tendermint/crypto/tmhash"
"github.com/tendermint/tendermint/crypto"
dbm "github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/types"
)
@ -21,22 +21,19 @@ 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",
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",
return fmt.Errorf("Wrong Block.Header.ChainID. Expected %v, got %v",
state.ChainID,
block.ChainID,
)
}
if block.Height != state.LastBlockHeight+1 {
return fmt.Errorf(
"Wrong Block.Header.Height. Expected %v, got %v",
return fmt.Errorf("Wrong Block.Header.Height. Expected %v, got %v",
state.LastBlockHeight+1,
block.Height,
)
@ -44,16 +41,15 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
// Validate prev block info.
if !block.LastBlockID.Equals(state.LastBlockID) {
return fmt.Errorf(
"Wrong Block.Header.LastBlockID. Expected %v, got %v",
return fmt.Errorf("Wrong Block.Header.LastBlockID. Expected %v, got %v",
state.LastBlockID,
block.LastBlockID,
)
}
newTxs := int64(len(block.Data.Txs))
if block.TotalTxs != state.LastBlockTotalTx+newTxs {
return fmt.Errorf(
"Wrong Block.Header.TotalTxs. Expected %v, got %v",
return fmt.Errorf("Wrong Block.Header.TotalTxs. Expected %v, got %v",
state.LastBlockTotalTx+newTxs,
block.TotalTxs,
)
@ -61,46 +57,44 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
// Validate app info
if !bytes.Equal(block.AppHash, state.AppHash) {
return fmt.Errorf(
"Wrong Block.Header.AppHash. Expected %X, got %v",
return fmt.Errorf("Wrong Block.Header.AppHash. Expected %X, got %v",
state.AppHash,
block.AppHash,
)
}
if !bytes.Equal(block.ConsensusHash, state.ConsensusParams.Hash()) {
return fmt.Errorf(
"Wrong Block.Header.ConsensusHash. Expected %X, got %v",
return fmt.Errorf("Wrong Block.Header.ConsensusHash. Expected %X, got %v",
state.ConsensusParams.Hash(),
block.ConsensusHash,
)
}
if !bytes.Equal(block.LastResultsHash, state.LastResultsHash) {
return fmt.Errorf(
"Wrong Block.Header.LastResultsHash. Expected %X, got %v",
return fmt.Errorf("Wrong Block.Header.LastResultsHash. Expected %X, got %v",
state.LastResultsHash,
block.LastResultsHash,
)
}
if !bytes.Equal(block.ValidatorsHash, state.Validators.Hash()) {
return fmt.Errorf(
"Wrong Block.Header.ValidatorsHash. Expected %X, got %v",
return fmt.Errorf("Wrong Block.Header.ValidatorsHash. Expected %X, got %v",
state.Validators.Hash(),
block.ValidatorsHash,
)
}
if !bytes.Equal(block.NextValidatorsHash, state.NextValidators.Hash()) {
return fmt.Errorf("Wrong Block.Header.NextValidatorsHash. Expected %X, got %v", state.NextValidators.Hash(), block.NextValidatorsHash)
return fmt.Errorf("Wrong Block.Header.NextValidatorsHash. Expected %X, got %v",
state.NextValidators.Hash(),
block.NextValidatorsHash,
)
}
// Validate block LastCommit.
if block.Height == 1 {
if len(block.LastCommit.Precommits) != 0 {
return errors.New("Block at height 1 (first block) should have no LastCommit precommits")
return errors.New("Block at height 1 can't have LastCommit precommits")
}
} else {
if len(block.LastCommit.Precommits) != state.LastValidators.Size() {
return fmt.Errorf(
"Invalid block commit size. Expected %v, got %v",
return fmt.Errorf("Invalid block commit size. Expected %v, got %v",
state.LastValidators.Size(),
len(block.LastCommit.Precommits),
)
@ -115,8 +109,7 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
// Validate block Time
if block.Height > 1 {
if !block.Time.After(state.LastBlockTime) {
return fmt.Errorf(
"Block time %v not greater than last block time %v",
return fmt.Errorf("Block time %v not greater than last block time %v",
block.Time,
state.LastBlockTime,
)
@ -124,8 +117,7 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
medianTime := MedianTime(block.LastCommit, state.LastValidators)
if !block.Time.Equal(medianTime) {
return fmt.Errorf(
"Invalid block time. Expected %v, got %v",
return fmt.Errorf("Invalid block time. Expected %v, got %v",
medianTime,
block.Time,
)
@ -133,8 +125,7 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
} else if block.Height == 1 {
genesisTime := state.LastBlockTime
if !block.Time.Equal(genesisTime) {
return fmt.Errorf(
"Block time %v is not equal to genesis time %v",
return fmt.Errorf("Block time %v is not equal to genesis time %v",
block.Time,
genesisTime,
)
@ -158,10 +149,9 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
// NOTE: We can't actually verify it's the right proposer because we dont
// know what round the block was first proposed. So just check that it's
// a legit address and a known validator.
if len(block.ProposerAddress) != tmhash.Size ||
if len(block.ProposerAddress) != crypto.AddressSize ||
!state.Validators.HasAddress(block.ProposerAddress) {
return fmt.Errorf(
"Block.Header.ProposerAddress, %X, is not a validator",
return fmt.Errorf("Block.Header.ProposerAddress, %X, is not a validator",
block.ProposerAddress,
)
}
@ -178,7 +168,7 @@ func VerifyEvidence(stateDB dbm.DB, state State, evidence types.Evidence) error
height := state.LastBlockHeight
evidenceAge := height - evidence.Height()
maxAge := state.ConsensusParams.EvidenceParams.MaxAge
maxAge := state.ConsensusParams.Evidence.MaxAge
if evidenceAge > maxAge {
return fmt.Errorf("Evidence from height %d is too old. Min height is %d",
evidence.Height(), height-maxAge)


+ 7
- 4
test/docker/Dockerfile View File

@ -14,6 +14,7 @@ ENV GOBIN $GOPATH/bin
WORKDIR $REPO
# Copy in the code
# TODO: rewrite to only copy Makefile & other files?
COPY . $REPO
# Install the vendored dependencies
@ -21,16 +22,18 @@ COPY . $REPO
RUN make get_tools
RUN make get_vendor_deps
# Now copy in the code
# NOTE: this will overwrite whatever is in vendor/
COPY . $REPO
# install ABCI CLI
RUN make install_abci
# install Tendermint
RUN make install
RUN tendermint testnet --node-dir-prefix="mach" --v=4 --populate-persistent-peers=false --o=$REPO/test/p2p/data
# Now copy in the code
# NOTE: this will overwrite whatever is in vendor/
COPY . $REPO
# expose the volume for debugging
VOLUME $REPO


+ 3
- 6
test/p2p/README.md View File

@ -19,7 +19,7 @@ docker network create --driver bridge --subnet 172.57.0.0/16 my_testnet
```
This gives us a new network with IP addresses in the rage `172.57.0.0 - 172.57.255.255`.
Peers on the network can have any IP address in this range.
Peers on the network can have any IP address in this range.
For our four node network, let's pick `172.57.0.101 - 172.57.0.104`.
Since we use Tendermint's default listening port of 26656, our list of seed nodes will look like:
@ -37,7 +37,7 @@ for i in $(seq 1 4); do
--ip="172.57.0.$((100 + $i))" \
--name local_testnet_$i \
--entrypoint tendermint \
-e TMHOME=/go/src/github.com/tendermint/tendermint/test/p2p/data/mach$i/core \
-e TMHOME=/go/src/github.com/tendermint/tendermint/test/p2p/data/mach$((i-1)) \
tendermint_tester node --p2p.persistent_peers 172.57.0.101:26656,172.57.0.102:26656,172.57.0.103:26656,172.57.0.104:26656 --proxy_app=kvstore
done
```
@ -47,8 +47,5 @@ If you now run `docker ps`, you'll see your containers!
We can confirm they are making blocks by checking the `/status` message using `curl` and `jq` to pretty print the output json:
```
curl 172.57.0.101:26657/status | jq .
curl 172.57.0.101:26657/status | jq .
```

+ 0
- 39
test/p2p/data/mach1/core/config/genesis.json View File

@ -1,39 +0,0 @@
{
"genesis_time": "2016-06-24T20:01:19.322Z",
"chain_id": "chain-9ujDWI",
"validators": [
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "vokz3/FgDAJuNHGPF4Wkzeq5DDVpizlOOLaUeukd4RY="
},
"power": "1",
"name": "mach1"
},
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "bcU0RlMjEmWH0qKpO1nWibcXBzsd6WiiWm7xPVlTGK0="
},
"power": "1",
"name": "mach2"
},
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "rmesaX0TWqC0YB6lfqqz/r9Lqk8inEWlmMKYWxL80aE="
},
"power": "1",
"name": "mach3"
},
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "nryPWM7UtG3NWrirpZHdJTzXy1A3Jz/aMrwLZGHE79k="
},
"power": "1",
"name": "mach4"
}
],
"app_hash": ""
}

+ 0
- 6
test/p2p/data/mach1/core/config/node_key.json View File

@ -1,6 +0,0 @@
{
"priv_key": {
"type": "tendermint/PrivKeyEd25519",
"value": "BpYtFp8xSrudBa5aBLRuSPD72PGDAUm0dJORDL3Kd5YJbluUzRefVFrjwoHZv1yeDj2P9xkEi2L3hJCUz/qFkQ=="
}
}

+ 0
- 14
test/p2p/data/mach1/core/config/priv_validator.json View File

@ -1,14 +0,0 @@
{
"address": "AE47BBD4B3ACD80BFE17F6E0C66C5B8492A81AE4",
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "vokz3/FgDAJuNHGPF4Wkzeq5DDVpizlOOLaUeukd4RY="
},
"last_height": "0",
"last_round": "0",
"last_step": 0,
"priv_key": {
"type": "tendermint/PrivKeyEd25519",
"value": "VHqgfHqM4WxcsqQMbCbRWwoylgQQqfHqblC2NvGrOJq+iTPf8WAMAm40cY8XhaTN6rkMNWmLOU44tpR66R3hFg=="
}
}

+ 0
- 39
test/p2p/data/mach2/core/config/genesis.json View File

@ -1,39 +0,0 @@
{
"genesis_time": "2016-06-24T20:01:19.322Z",
"chain_id": "chain-9ujDWI",
"validators": [
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "vokz3/FgDAJuNHGPF4Wkzeq5DDVpizlOOLaUeukd4RY="
},
"power": "1",
"name": "mach1"
},
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "bcU0RlMjEmWH0qKpO1nWibcXBzsd6WiiWm7xPVlTGK0="
},
"power": "1",
"name": "mach2"
},
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "rmesaX0TWqC0YB6lfqqz/r9Lqk8inEWlmMKYWxL80aE="
},
"power": "1",
"name": "mach3"
},
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "nryPWM7UtG3NWrirpZHdJTzXy1A3Jz/aMrwLZGHE79k="
},
"power": "1",
"name": "mach4"
}
],
"app_hash": ""
}

+ 0
- 6
test/p2p/data/mach2/core/config/node_key.json View File

@ -1,6 +0,0 @@
{
"priv_key": {
"type": "tendermint/PrivKeyEd25519",
"value": "uM6LDVE4wQIIUmq9rc6RxzX8zEGG4G4Jcuw15klzQopF68YfJM4bkbPSavurEcJ4nvBMusKBg2GcARFrZqnFKA=="
}
}

+ 0
- 14
test/p2p/data/mach2/core/config/priv_validator.json View File

@ -1,14 +0,0 @@
{
"address": "5D61EE46CCE91F579086522D7FD8CEC3F854E946",
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "bcU0RlMjEmWH0qKpO1nWibcXBzsd6WiiWm7xPVlTGK0="
},
"last_height": "0",
"last_round": "0",
"last_step": 0,
"priv_key": {
"type": "tendermint/PrivKeyEd25519",
"value": "0EeInmBQL8MSnQq38zSxg47Z7R7Nmcu5a3GtWr9agUNtxTRGUyMSZYfSoqk7WdaJtxcHOx3paKJabvE9WVMYrQ=="
}
}

+ 0
- 39
test/p2p/data/mach3/core/config/genesis.json View File

@ -1,39 +0,0 @@
{
"genesis_time": "2016-06-24T20:01:19.322Z",
"chain_id": "chain-9ujDWI",
"validators": [
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "vokz3/FgDAJuNHGPF4Wkzeq5DDVpizlOOLaUeukd4RY="
},
"power": "1",
"name": "mach1"
},
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "bcU0RlMjEmWH0qKpO1nWibcXBzsd6WiiWm7xPVlTGK0="
},
"power": "1",
"name": "mach2"
},
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "rmesaX0TWqC0YB6lfqqz/r9Lqk8inEWlmMKYWxL80aE="
},
"power": "1",
"name": "mach3"
},
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "nryPWM7UtG3NWrirpZHdJTzXy1A3Jz/aMrwLZGHE79k="
},
"power": "1",
"name": "mach4"
}
],
"app_hash": ""
}

+ 0
- 6
test/p2p/data/mach3/core/config/node_key.json View File

@ -1,6 +0,0 @@
{
"priv_key": {
"type": "tendermint/PrivKeyEd25519",
"value": "kT3orG0YkipT9rAZbvAjtGk/7Pu1ZeCE8LSUF2jz2uiSs1rdlUVi/gccRlvCRLKvrtSicOyEkmk0FHPOGS3mgg=="
}
}

+ 0
- 14
test/p2p/data/mach3/core/config/priv_validator.json View File

@ -1,14 +0,0 @@
{
"address": "705F9DA2CC7D7AF5F4519455ED99622E40E439A1",
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "rmesaX0TWqC0YB6lfqqz/r9Lqk8inEWlmMKYWxL80aE="
},
"last_height": "0",
"last_round": "0",
"last_step": 0,
"priv_key": {
"type": "tendermint/PrivKeyEd25519",
"value": "waTkfzSfxfVW9Kmie6d2uUQkwxK6ps9u5EuGc0jXw/KuZ6xpfRNaoLRgHqV+qrP+v0uqTyKcRaWYwphbEvzRoQ=="
}
}

+ 0
- 39
test/p2p/data/mach4/core/config/genesis.json View File

@ -1,39 +0,0 @@
{
"genesis_time": "2016-06-24T20:01:19.322Z",
"chain_id": "chain-9ujDWI",
"validators": [
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "vokz3/FgDAJuNHGPF4Wkzeq5DDVpizlOOLaUeukd4RY="
},
"power": "1",
"name": "mach1"
},
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "bcU0RlMjEmWH0qKpO1nWibcXBzsd6WiiWm7xPVlTGK0="
},
"power": "1",
"name": "mach2"
},
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "rmesaX0TWqC0YB6lfqqz/r9Lqk8inEWlmMKYWxL80aE="
},
"power": "1",
"name": "mach3"
},
{
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "nryPWM7UtG3NWrirpZHdJTzXy1A3Jz/aMrwLZGHE79k="
},
"power": "1",
"name": "mach4"
}
],
"app_hash": ""
}

+ 0
- 6
test/p2p/data/mach4/core/config/node_key.json View File

@ -1,6 +0,0 @@
{
"priv_key": {
"type": "tendermint/PrivKeyEd25519",
"value": "QIIm8/QEEawiJi3Zozv+J9b+1CufCEkGs3lxGMlRy4L4FVIXCoXJTwYIrotZtwoMqLYEqQV1hbKKJmFA3GFelw=="
}
}

+ 0
- 14
test/p2p/data/mach4/core/config/priv_validator.json View File

@ -1,14 +0,0 @@
{
"address": "D1054266EC9EEA511ED9A76DEFD520BBE1B5E850",
"pub_key": {
"type": "tendermint/PubKeyEd25519",
"value": "nryPWM7UtG3NWrirpZHdJTzXy1A3Jz/aMrwLZGHE79k="
},
"last_height": "0",
"last_round": "0",
"last_step": 0,
"priv_key": {
"type": "tendermint/PrivKeyEd25519",
"value": "xMw+0o8CDC29qYvNvwjDztNwRw508l6TjV0pXo49KwyevI9YztS0bc1auKulkd0lPNfLUDcnP9oyvAtkYcTv2Q=="
}
}

+ 1
- 1
test/p2p/ip_plus_id.sh View File

@ -3,5 +3,5 @@ set -eu
ID=$1
DOCKER_IMAGE=$2
NODEID="$(docker run --rm -e TMHOME=/go/src/github.com/tendermint/tendermint/test/p2p/data/mach$ID/core $DOCKER_IMAGE tendermint show_node_id)"
NODEID="$(docker run --rm -e TMHOME=/go/src/github.com/tendermint/tendermint/test/p2p/data/mach$((ID-1)) $DOCKER_IMAGE tendermint show_node_id)"
echo "$NODEID@172.57.0.$((100+$ID))"

+ 4
- 2
test/p2p/peer.sh View File

@ -15,13 +15,15 @@ echo "starting tendermint peer ID=$ID"
# NOTE: $NODE_FLAGS should be unescaped (no quotes). otherwise it will be
# treated as one flag.
# test/p2p/data/mach$((ID-1)) data is generated in test/docker/Dockerfile using
# the tendermint testnet command.
if [[ "$ID" == "x" ]]; then # Set "x" to "1" to print to console.
docker run \
--net="$NETWORK_NAME" \
--ip=$(test/p2p/ip.sh "$ID") \
--name "local_testnet_$ID" \
--entrypoint tendermint \
-e TMHOME="/go/src/github.com/tendermint/tendermint/test/p2p/data/mach$ID/core" \
-e TMHOME="/go/src/github.com/tendermint/tendermint/test/p2p/data/mach$((ID-1))" \
-e GOMAXPROCS=1 \
--log-driver=syslog \
--log-opt syslog-address=udp://127.0.0.1:5514 \
@ -34,7 +36,7 @@ else
--ip=$(test/p2p/ip.sh "$ID") \
--name "local_testnet_$ID" \
--entrypoint tendermint \
-e TMHOME="/go/src/github.com/tendermint/tendermint/test/p2p/data/mach$ID/core" \
-e TMHOME="/go/src/github.com/tendermint/tendermint/test/p2p/data/mach$((ID-1))" \
-e GOMAXPROCS=1 \
--log-driver=syslog \
--log-opt syslog-address=udp://127.0.0.1:5514 \


+ 3
- 3
test/p2p/pex/test_addrbook.sh View File

@ -18,7 +18,7 @@ echo "1. restart peer $ID"
docker stop "local_testnet_$ID"
echo "stopped local_testnet_$ID"
# preserve addrbook.json
docker cp "local_testnet_$ID:/go/src/github.com/tendermint/tendermint/test/p2p/data/mach1/core/config/addrbook.json" "/tmp/addrbook.json"
docker cp "local_testnet_$ID:/go/src/github.com/tendermint/tendermint/test/p2p/data/mach0/config/addrbook.json" "/tmp/addrbook.json"
set +e #CIRCLE
docker rm -vf "local_testnet_$ID"
set -e
@ -32,11 +32,11 @@ bash test/p2p/client.sh "$DOCKER_IMAGE" "$NETWORK_NAME" "$CLIENT_NAME" "test/p2p
# Now we know that the node is up.
docker cp "/tmp/addrbook.json" "local_testnet_$ID:/go/src/github.com/tendermint/tendermint/test/p2p/data/mach1/core/config/addrbook.json"
docker cp "/tmp/addrbook.json" "local_testnet_$ID:/go/src/github.com/tendermint/tendermint/test/p2p/data/mach0/config/addrbook.json"
echo "with the following addrbook:"
cat /tmp/addrbook.json
# exec doesn't work on circle
# docker exec "local_testnet_$ID" cat "/go/src/github.com/tendermint/tendermint/test/p2p/data/mach1/core/config/addrbook.json"
# docker exec "local_testnet_$ID" cat "/go/src/github.com/tendermint/tendermint/test/p2p/data/mach0/config/addrbook.json"
echo ""
echo "----------------------------------------------------------------------"


+ 97
- 20
types/block.go View File

@ -2,12 +2,14 @@ package types
import (
"bytes"
"errors"
"fmt"
"strings"
"sync"
"time"
"github.com/pkg/errors"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/merkle"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/version"
@ -15,7 +17,7 @@ import (
const (
// MaxHeaderBytes is a maximum header size (including amino overhead).
MaxHeaderBytes int64 = 537
MaxHeaderBytes int64 = 653
// MaxAminoOverheadForBlock - maximum amino overhead to encode a block (up to
// MaxBlockSizeBytes in size) not including it's parts except Data.
@ -57,54 +59,117 @@ func MakeBlock(height int64, txs []Tx, lastCommit *Commit, evidence []Evidence)
// ValidateBasic performs basic validation that doesn't involve state data.
// It checks the internal consistency of the block.
// Further validation is done using state#ValidateBlock.
func (b *Block) ValidateBasic() error {
if b == nil {
return errors.New("Nil blocks are invalid")
return errors.New("nil block")
}
b.mtx.Lock()
defer b.mtx.Unlock()
if len(b.ChainID) > MaxChainIDLen {
return fmt.Errorf("ChainID is too long. Max is %d, got %d", MaxChainIDLen, len(b.ChainID))
}
if b.Height < 0 {
return fmt.Errorf(
"Negative Block.Header.Height: %v",
b.Height,
)
return errors.New("Negative Header.Height")
} else if b.Height == 0 {
return errors.New("Zero Header.Height")
}
// NOTE: Timestamp validation is subtle and handled elsewhere.
newTxs := int64(len(b.Data.Txs))
if b.NumTxs != newTxs {
return fmt.Errorf(
"Wrong Block.Header.NumTxs. Expected %v, got %v",
return fmt.Errorf("Wrong Header.NumTxs. Expected %v, got %v",
newTxs,
b.NumTxs,
)
}
// TODO: fix tests so we can do this
/*if b.TotalTxs < b.NumTxs {
return fmt.Errorf("Header.TotalTxs (%d) is less than Header.NumTxs (%d)", b.TotalTxs, b.NumTxs)
}*/
if b.TotalTxs < 0 {
return errors.New("Negative Header.TotalTxs")
}
if err := b.LastBlockID.ValidateBasic(); err != nil {
return fmt.Errorf("Wrong Header.LastBlockID: %v", err)
}
// Validate the last commit and its hash.
if b.Header.Height > 1 {
if b.LastCommit == nil {
return errors.New("nil LastCommit")
}
if err := b.LastCommit.ValidateBasic(); err != nil {
return fmt.Errorf("Wrong LastCommit")
}
}
if err := ValidateHash(b.LastCommitHash); err != nil {
return fmt.Errorf("Wrong Header.LastCommitHash: %v", err)
}
if !bytes.Equal(b.LastCommitHash, b.LastCommit.Hash()) {
return fmt.Errorf(
"Wrong Block.Header.LastCommitHash. Expected %v, got %v",
b.LastCommitHash,
return fmt.Errorf("Wrong Header.LastCommitHash. Expected %v, got %v",
b.LastCommit.Hash(),
b.LastCommitHash,
)
}
if b.Header.Height != 1 {
if err := b.LastCommit.ValidateBasic(); err != nil {
return err
}
// Validate the hash of the transactions.
// NOTE: b.Data.Txs may be nil, but b.Data.Hash()
// still works fine
if err := ValidateHash(b.DataHash); err != nil {
return fmt.Errorf("Wrong Header.DataHash: %v", err)
}
if !bytes.Equal(b.DataHash, b.Data.Hash()) {
return fmt.Errorf(
"Wrong Block.Header.DataHash. Expected %v, got %v",
b.DataHash,
"Wrong Header.DataHash. Expected %v, got %v",
b.Data.Hash(),
b.DataHash,
)
}
// Basic validation of hashes related to application data.
// Will validate fully against state in state#ValidateBlock.
if err := ValidateHash(b.ValidatorsHash); err != nil {
return fmt.Errorf("Wrong Header.ValidatorsHash: %v", err)
}
if err := ValidateHash(b.NextValidatorsHash); err != nil {
return fmt.Errorf("Wrong Header.NextValidatorsHash: %v", err)
}
if err := ValidateHash(b.ConsensusHash); err != nil {
return fmt.Errorf("Wrong Header.ConsensusHash: %v", err)
}
// NOTE: AppHash is arbitrary length
if err := ValidateHash(b.LastResultsHash); err != nil {
return fmt.Errorf("Wrong Header.LastResultsHash: %v", err)
}
// Validate evidence and its hash.
if err := ValidateHash(b.EvidenceHash); err != nil {
return fmt.Errorf("Wrong Header.EvidenceHash: %v", err)
}
// NOTE: b.Evidence.Evidence may be nil, but we're just looping.
for i, ev := range b.Evidence.Evidence {
if err := ev.ValidateBasic(); err != nil {
return fmt.Errorf("Invalid evidence (#%d): %v", i, err)
}
}
if !bytes.Equal(b.EvidenceHash, b.Evidence.Hash()) {
return fmt.Errorf(
"Wrong Block.Header.EvidenceHash. Expected %v, got %v",
return fmt.Errorf("Wrong Header.EvidenceHash. Expected %v, got %v",
b.EvidenceHash,
b.Evidence.Hash(),
)
}
if len(b.ProposerAddress) != crypto.AddressSize {
return fmt.Errorf("Expected len(Header.ProposerAddress) to be %d, got %d",
crypto.AddressSize, len(b.ProposerAddress))
}
return nil
}
@ -719,6 +784,18 @@ func (blockID BlockID) Key() string {
return string(blockID.Hash) + string(bz)
}
// ValidateBasic performs basic validation.
func (blockID BlockID) ValidateBasic() error {
// Hash can be empty in case of POLBlockID in Proposal.
if err := ValidateHash(blockID.Hash); err != nil {
return fmt.Errorf("Wrong Hash")
}
if err := blockID.PartsHeader.ValidateBasic(); err != nil {
return fmt.Errorf("Wrong PartsHeader: %v", err)
}
return nil
}
// String returns a human readable string representation of the BlockID
func (blockID BlockID) String() string {
return fmt.Sprintf(`%v:%v`, blockID.Hash, blockID.PartsHeader)


+ 13
- 10
types/block_test.go View File

@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/tmhash"
cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/version"
@ -79,11 +80,13 @@ func TestBlockValidateBasic(t *testing.T) {
blk.EvidenceHash = []byte("something else")
}, true},
}
for _, tc := range testCases {
for i, tc := range testCases {
t.Run(tc.testName, func(t *testing.T) {
block := MakeBlock(h, txs, commit, evList)
block.ProposerAddress = valSet.GetProposer().Address
tc.malleateBlock(block)
assert.Equal(t, tc.expErr, block.ValidateBasic() != nil, "ValidateBasic had an unexpected result")
err = block.ValidateBasic()
assert.Equal(t, tc.expErr, err != nil, "#%d: %v", i, err)
})
}
}
@ -116,7 +119,7 @@ func TestBlockMakePartSetWithEvidence(t *testing.T) {
partSet := MakeBlock(h, []Tx{Tx("Hello World")}, commit, evList).MakePartSet(1024)
assert.NotNil(t, partSet)
assert.Equal(t, 2, partSet.Total())
assert.Equal(t, 3, partSet.Total())
}
func TestBlockHashesTo(t *testing.T) {
@ -262,7 +265,7 @@ func TestMaxHeaderBytes(t *testing.T) {
AppHash: tmhash.Sum([]byte("app_hash")),
LastResultsHash: tmhash.Sum([]byte("last_results_hash")),
EvidenceHash: tmhash.Sum([]byte("evidence_hash")),
ProposerAddress: tmhash.Sum([]byte("proposer_address")),
ProposerAddress: crypto.AddressHash([]byte("proposer_address")),
}
bz, err := cdc.MarshalBinaryLengthPrefixed(h)
@ -292,9 +295,9 @@ func TestBlockMaxDataBytes(t *testing.T) {
}{
0: {-10, 1, 0, true, 0},
1: {10, 1, 0, true, 0},
2: {750, 1, 0, true, 0},
3: {751, 1, 0, false, 0},
4: {752, 1, 0, false, 1},
2: {886, 1, 0, true, 0},
3: {887, 1, 0, false, 0},
4: {888, 1, 0, false, 1},
}
for i, tc := range testCases {
@ -320,9 +323,9 @@ func TestBlockMaxDataBytesUnknownEvidence(t *testing.T) {
}{
0: {-10, 1, true, 0},
1: {10, 1, true, 0},
2: {833, 1, true, 0},
3: {834, 1, false, 0},
4: {835, 1, false, 1},
2: {984, 1, true, 0},
3: {985, 1, false, 0},
4: {986, 1, false, 1},
}
for i, tc := range testCases {


+ 14
- 16
types/canonical.go View File

@ -23,14 +23,13 @@ type CanonicalPartSetHeader struct {
}
type CanonicalProposal struct {
Type SignedMsgType // type alias for byte
Height int64 `binary:"fixed64"`
Round int64 `binary:"fixed64"`
POLRound int64 `binary:"fixed64"`
Timestamp time.Time
BlockPartsHeader CanonicalPartSetHeader
POLBlockID CanonicalBlockID
ChainID string
Type SignedMsgType // type alias for byte
Height int64 `binary:"fixed64"`
Round int64 `binary:"fixed64"`
POLRound int64 `binary:"fixed64"`
BlockID CanonicalBlockID
Timestamp time.Time
ChainID string
}
type CanonicalVote struct {
@ -71,14 +70,13 @@ func CanonicalizePartSetHeader(psh PartSetHeader) CanonicalPartSetHeader {
func CanonicalizeProposal(chainID string, proposal *Proposal) CanonicalProposal {
return CanonicalProposal{
Type: ProposalType,
Height: proposal.Height,
Round: int64(proposal.Round), // cast int->int64 to make amino encode it fixed64 (does not work for int)
POLRound: int64(proposal.POLRound),
Timestamp: proposal.Timestamp,
BlockPartsHeader: CanonicalizePartSetHeader(proposal.BlockPartsHeader),
POLBlockID: CanonicalizeBlockID(proposal.POLBlockID),
ChainID: chainID,
Type: ProposalType,
Height: proposal.Height,
Round: int64(proposal.Round), // cast int->int64 to make amino encode it fixed64 (does not work for int)
POLRound: int64(proposal.POLRound),
BlockID: CanonicalizeBlockID(proposal.BlockID),
Timestamp: proposal.Timestamp,
ChainID: chainID,
}
}


+ 4
- 0
types/event_bus.go View File

@ -83,6 +83,10 @@ func (b *EventBus) PublishEventVote(data EventDataVote) error {
return b.Publish(EventVote, data)
}
func (b *EventBus) PublishEventValidBlock(data EventDataRoundState) error {
return b.Publish(EventValidBlock, data)
}
// PublishEventTx publishes tx event with tags from Result. Note it will add
// predefined tags (EventTypeKey, TxHashKey). Existing tags with the same names
// will be overwritten.


+ 2
- 0
types/events.go View File

@ -23,6 +23,7 @@ const (
EventTimeoutWait = "TimeoutWait"
EventTx = "Tx"
EventUnlock = "Unlock"
EventValidBlock = "ValidBlock"
EventValidatorSetUpdates = "ValidatorSetUpdates"
EventVote = "Vote"
)
@ -119,6 +120,7 @@ var (
EventQueryTx = QueryForEvent(EventTx)
EventQueryUnlock = QueryForEvent(EventUnlock)
EventQueryValidatorSetUpdates = QueryForEvent(EventValidatorSetUpdates)
EventQueryValidBlock = QueryForEvent(EventValidBlock)
EventQueryVote = QueryForEvent(EventVote)
)


+ 22
- 1
types/evidence.go View File

@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"github.com/pkg/errors"
"github.com/tendermint/tendermint/crypto/tmhash"
amino "github.com/tendermint/go-amino"
@ -14,7 +15,7 @@ import (
const (
// MaxEvidenceBytes is a maximum size of any evidence (including amino overhead).
MaxEvidenceBytes int64 = 444
MaxEvidenceBytes int64 = 484
)
// ErrEvidenceInvalid wraps a piece of evidence and the error denoting how or why it is invalid.
@ -60,6 +61,7 @@ type Evidence interface {
Verify(chainID string, pubKey crypto.PubKey) error // verify the evidence
Equal(Evidence) bool // check equality of evidence
ValidateBasic() error
String() string
}
@ -172,6 +174,23 @@ func (dve *DuplicateVoteEvidence) Equal(ev Evidence) bool {
return bytes.Equal(dveHash, evHash)
}
// ValidateBasic performs basic validation.
func (dve *DuplicateVoteEvidence) ValidateBasic() error {
if len(dve.PubKey.Bytes()) == 0 {
return errors.New("Empty PubKey")
}
if dve.VoteA == nil || dve.VoteB == nil {
return fmt.Errorf("One or both of the votes are empty %v, %v", dve.VoteA, dve.VoteB)
}
if err := dve.VoteA.ValidateBasic(); err != nil {
return fmt.Errorf("Invalid VoteA: %v", err)
}
if err := dve.VoteB.ValidateBasic(); err != nil {
return fmt.Errorf("Invalid VoteB: %v", err)
}
return nil
}
//-----------------------------------------------------------------
// UNSTABLE
@ -201,6 +220,7 @@ func (e MockGoodEvidence) Equal(ev Evidence) bool {
return e.Height_ == e2.Height_ &&
bytes.Equal(e.Address_, e2.Address_)
}
func (e MockGoodEvidence) ValidateBasic() error { return nil }
func (e MockGoodEvidence) String() string {
return fmt.Sprintf("GoodEvidence: %d/%s", e.Height_, e.Address_)
}
@ -218,6 +238,7 @@ func (e MockBadEvidence) Equal(ev Evidence) bool {
return e.Height_ == e2.Height_ &&
bytes.Equal(e.Address_, e2.Address_)
}
func (e MockBadEvidence) ValidateBasic() error { return nil }
func (e MockBadEvidence) String() string {
return fmt.Sprintf("BadEvidence: %d/%s", e.Height_, e.Address_)
}


+ 1
- 1
types/evidence_test.go View File

@ -61,7 +61,7 @@ func TestEvidence(t *testing.T) {
{vote1, makeVote(val, chainID, 0, 10, 3, 1, blockID2), false}, // wrong round
{vote1, makeVote(val, chainID, 0, 10, 2, 2, blockID2), false}, // wrong step
{vote1, makeVote(val2, chainID, 0, 10, 2, 1, blockID), false}, // wrong validator
{vote1, badVote, false}, // signed by wrong key
{vote1, badVote, false}, // signed by wrong key
}
pubKey := val.GetPubKey()


+ 31
- 0
types/heartbeat.go View File

@ -3,6 +3,8 @@ package types
import (
"fmt"
"github.com/pkg/errors"
"github.com/tendermint/tendermint/crypto"
cmn "github.com/tendermint/tendermint/libs/common"
)
@ -50,3 +52,32 @@ func (heartbeat *Heartbeat) String() string {
heartbeat.Height, heartbeat.Round, heartbeat.Sequence,
fmt.Sprintf("/%X.../", cmn.Fingerprint(heartbeat.Signature[:])))
}
// ValidateBasic performs basic validation.
func (heartbeat *Heartbeat) ValidateBasic() error {
if len(heartbeat.ValidatorAddress) != crypto.AddressSize {
return fmt.Errorf("Expected ValidatorAddress size to be %d bytes, got %d bytes",
crypto.AddressSize,
len(heartbeat.ValidatorAddress),
)
}
if heartbeat.ValidatorIndex < 0 {
return errors.New("Negative ValidatorIndex")
}
if heartbeat.Height < 0 {
return errors.New("Negative Height")
}
if heartbeat.Round < 0 {
return errors.New("Negative Round")
}
if heartbeat.Sequence < 0 {
return errors.New("Negative Sequence")
}
if len(heartbeat.Signature) == 0 {
return errors.New("Signature is missing")
}
if len(heartbeat.Signature) > MaxSignatureSize {
return fmt.Errorf("Signature is too big (max: %d)", MaxSignatureSize)
}
return nil
}

+ 60
- 12
types/params.go View File

@ -17,12 +17,13 @@ const (
// ConsensusParams contains consensus critical parameters that determine the
// validity of blocks.
type ConsensusParams struct {
BlockSize `json:"block_size_params"`
EvidenceParams `json:"evidence_params"`
BlockSize BlockSizeParams `json:"block_size"`
Evidence EvidenceParams `json:"evidence"`
Validator ValidatorParams `json:"validator"`
}
// BlockSize contain limits on the block size.
type BlockSize struct {
// BlockSizeParams define limits on the block size.
type BlockSizeParams struct {
MaxBytes int64 `json:"max_bytes"`
MaxGas int64 `json:"max_gas"`
}
@ -32,17 +33,24 @@ type EvidenceParams struct {
MaxAge int64 `json:"max_age"` // only accept new evidence more recent than this
}
// ValidatorParams restrict the public key types validators can use.
// NOTE: uses ABCI pubkey naming, not Amino routes.
type ValidatorParams struct {
PubKeyTypes []string `json:"pub_key_types"`
}
// DefaultConsensusParams returns a default ConsensusParams.
func DefaultConsensusParams() *ConsensusParams {
return &ConsensusParams{
DefaultBlockSize(),
DefaultBlockSizeParams(),
DefaultEvidenceParams(),
DefaultValidatorParams(),
}
}
// DefaultBlockSize returns a default BlockSize.
func DefaultBlockSize() BlockSize {
return BlockSize{
// DefaultBlockSizeParams returns a default BlockSizeParams.
func DefaultBlockSizeParams() BlockSizeParams {
return BlockSizeParams{
MaxBytes: 22020096, // 21MB
MaxGas: -1,
}
@ -55,6 +63,12 @@ func DefaultEvidenceParams() EvidenceParams {
}
}
// DefaultValidatorParams returns a default ValidatorParams, which allows
// only ed25519 pubkeys.
func DefaultValidatorParams() ValidatorParams {
return ValidatorParams{[]string{ABCIPubKeyTypeEd25519}}
}
// Validate validates the ConsensusParams to ensure all values are within their
// allowed limits, and returns an error if they are not.
func (params *ConsensusParams) Validate() error {
@ -72,9 +86,22 @@ func (params *ConsensusParams) Validate() error {
params.BlockSize.MaxGas)
}
if params.EvidenceParams.MaxAge <= 0 {
if params.Evidence.MaxAge <= 0 {
return cmn.NewError("EvidenceParams.MaxAge must be greater than 0. Got %d",
params.EvidenceParams.MaxAge)
params.Evidence.MaxAge)
}
if len(params.Validator.PubKeyTypes) == 0 {
return cmn.NewError("len(Validator.PubKeyTypes) must be greater than 0")
}
// Check if keyType is a known ABCIPubKeyType
for i := 0; i < len(params.Validator.PubKeyTypes); i++ {
keyType := params.Validator.PubKeyTypes[i]
if _, ok := ABCIPubKeyTypesToAminoRoutes[keyType]; !ok {
return cmn.NewError("params.Validator.PubKeyTypes[%d], %s, is an unknown pubkey type",
i, keyType)
}
}
return nil
@ -94,6 +121,24 @@ func (params *ConsensusParams) Hash() []byte {
return hasher.Sum(nil)
}
func (params *ConsensusParams) Equals(params2 *ConsensusParams) bool {
return params.BlockSize == params2.BlockSize &&
params.Evidence == params2.Evidence &&
stringSliceEqual(params.Validator.PubKeyTypes, params2.Validator.PubKeyTypes)
}
func stringSliceEqual(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i := 0; i < len(a); i++ {
if a[i] != b[i] {
return false
}
}
return true
}
// Update returns a copy of the params with updates from the non-zero fields of p2.
// NOTE: note: must not modify the original
func (params ConsensusParams) Update(params2 *abci.ConsensusParams) ConsensusParams {
@ -108,8 +153,11 @@ func (params ConsensusParams) Update(params2 *abci.ConsensusParams) ConsensusPar
res.BlockSize.MaxBytes = params2.BlockSize.MaxBytes
res.BlockSize.MaxGas = params2.BlockSize.MaxGas
}
if params2.EvidenceParams != nil {
res.EvidenceParams.MaxAge = params2.EvidenceParams.MaxAge
if params2.Evidence != nil {
res.Evidence.MaxAge = params2.Evidence.MaxAge
}
if params2.Validator != nil {
res.Validator.PubKeyTypes = params2.Validator.PubKeyTypes
}
return res
}

+ 42
- 27
types/params_test.go View File

@ -9,23 +9,32 @@ import (
abci "github.com/tendermint/tendermint/abci/types"
)
var (
valEd25519 = []string{ABCIPubKeyTypeEd25519}
valSecp256k1 = []string{ABCIPubKeyTypeSecp256k1}
)
func TestConsensusParamsValidation(t *testing.T) {
testCases := []struct {
params ConsensusParams
valid bool
}{
// test block size
0: {makeParams(1, 0, 1), true},
1: {makeParams(0, 0, 1), false},
2: {makeParams(47*1024*1024, 0, 1), true},
3: {makeParams(10, 0, 1), true},
4: {makeParams(100*1024*1024, 0, 1), true},
5: {makeParams(101*1024*1024, 0, 1), false},
6: {makeParams(1024*1024*1024, 0, 1), false},
7: {makeParams(1024*1024*1024, 0, -1), false},
0: {makeParams(1, 0, 1, valEd25519), true},
1: {makeParams(0, 0, 1, valEd25519), false},
2: {makeParams(47*1024*1024, 0, 1, valEd25519), true},
3: {makeParams(10, 0, 1, valEd25519), true},
4: {makeParams(100*1024*1024, 0, 1, valEd25519), true},
5: {makeParams(101*1024*1024, 0, 1, valEd25519), false},
6: {makeParams(1024*1024*1024, 0, 1, valEd25519), false},
7: {makeParams(1024*1024*1024, 0, -1, valEd25519), false},
// test evidence age
8: {makeParams(1, 0, 0), false},
9: {makeParams(1, 0, -1), false},
8: {makeParams(1, 0, 0, valEd25519), false},
9: {makeParams(1, 0, -1, valEd25519), false},
// test no pubkey type provided
10: {makeParams(1, 0, 1, []string{}), false},
// test invalid pubkey type provided
11: {makeParams(1, 0, 1, []string{"potatoes make good pubkeys"}), false},
}
for i, tc := range testCases {
if tc.valid {
@ -36,28 +45,31 @@ func TestConsensusParamsValidation(t *testing.T) {
}
}
func makeParams(blockBytes, blockGas, evidenceAge int64) ConsensusParams {
func makeParams(blockBytes, blockGas, evidenceAge int64, pubkeyTypes []string) ConsensusParams {
return ConsensusParams{
BlockSize: BlockSize{
BlockSize: BlockSizeParams{
MaxBytes: blockBytes,
MaxGas: blockGas,
},
EvidenceParams: EvidenceParams{
Evidence: EvidenceParams{
MaxAge: evidenceAge,
},
Validator: ValidatorParams{
PubKeyTypes: pubkeyTypes,
},
}
}
func TestConsensusParamsHash(t *testing.T) {
params := []ConsensusParams{
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),
makeParams(4, 2, 3, valEd25519),
makeParams(1, 4, 3, valEd25519),
makeParams(1, 2, 4, valEd25519),
makeParams(2, 5, 7, valEd25519),
makeParams(1, 7, 6, valEd25519),
makeParams(9, 5, 4, valEd25519),
makeParams(7, 8, 9, valEd25519),
makeParams(4, 6, 5, valEd25519),
}
hashes := make([][]byte, len(params))
@ -83,23 +95,26 @@ func TestConsensusParamsUpdate(t *testing.T) {
}{
// empty updates
{
makeParams(1, 2, 3),
makeParams(1, 2, 3, valEd25519),
&abci.ConsensusParams{},
makeParams(1, 2, 3),
makeParams(1, 2, 3, valEd25519),
},
// fine updates
{
makeParams(1, 2, 3),
makeParams(1, 2, 3, valEd25519),
&abci.ConsensusParams{
BlockSize: &abci.BlockSize{
BlockSize: &abci.BlockSizeParams{
MaxBytes: 100,
MaxGas: 200,
},
EvidenceParams: &abci.EvidenceParams{
Evidence: &abci.EvidenceParams{
MaxAge: 300,
},
Validator: &abci.ValidatorParams{
PubKeyTypes: valSecp256k1,
},
},
makeParams(100, 200, 300),
makeParams(100, 200, 300, valSecp256k1),
},
}
for _, tc := range testCases {


+ 25
- 1
types/part_set.go View File

@ -2,11 +2,12 @@ package types
import (
"bytes"
"errors"
"fmt"
"io"
"sync"
"github.com/pkg/errors"
"github.com/tendermint/tendermint/crypto/merkle"
"github.com/tendermint/tendermint/crypto/tmhash"
cmn "github.com/tendermint/tendermint/libs/common"
@ -36,6 +37,17 @@ func (part *Part) Hash() []byte {
return part.hash
}
// ValidateBasic performs basic validation.
func (part *Part) ValidateBasic() error {
if part.Index < 0 {
return errors.New("Negative Index")
}
if len(part.Bytes) > BlockPartSizeBytes {
return fmt.Errorf("Too big (max: %d)", BlockPartSizeBytes)
}
return nil
}
func (part *Part) String() string {
return part.StringIndented("")
}
@ -70,6 +82,18 @@ func (psh PartSetHeader) Equals(other PartSetHeader) bool {
return psh.Total == other.Total && bytes.Equal(psh.Hash, other.Hash)
}
// ValidateBasic performs basic validation.
func (psh PartSetHeader) ValidateBasic() error {
if psh.Total < 0 {
return errors.New("Negative Total")
}
// Hash can be empty in case of POLBlockID.PartsHeader in Proposal.
if err := ValidateHash(psh.Hash); err != nil {
return errors.Wrap(err, "Wrong Hash")
}
return nil
}
//-------------------------------------
type PartSet struct {


+ 53
- 20
types/proposal.go View File

@ -15,39 +15,72 @@ var (
)
// Proposal defines a block proposal for the consensus.
// It refers to the block only by its PartSetHeader.
// It refers to the block by BlockID field.
// It must be signed by the correct proposer for the given Height/Round
// to be considered valid. It may depend on votes from a previous round,
// a so-called Proof-of-Lock (POL) round, as noted in the POLRound and POLBlockID.
// a so-called Proof-of-Lock (POL) round, as noted in the POLRound.
// If POLRound >= 0, then BlockID corresponds to the block that is locked in POLRound.
type Proposal struct {
Height int64 `json:"height"`
Round int `json:"round"`
Timestamp time.Time `json:"timestamp"`
BlockPartsHeader PartSetHeader `json:"block_parts_header"`
POLRound int `json:"pol_round"` // -1 if null.
POLBlockID BlockID `json:"pol_block_id"` // zero if null.
Signature []byte `json:"signature"`
Type SignedMsgType
Height int64 `json:"height"`
Round int `json:"round"`
POLRound int `json:"pol_round"` // -1 if null.
BlockID BlockID `json:"block_id"`
Timestamp time.Time `json:"timestamp"`
Signature []byte `json:"signature"`
}
// NewProposal returns a new Proposal.
// If there is no POLRound, polRound should be -1.
func NewProposal(height int64, round int, blockPartsHeader PartSetHeader, polRound int, polBlockID BlockID) *Proposal {
func NewProposal(height int64, round int, polRound int, blockID BlockID) *Proposal {
return &Proposal{
Height: height,
Round: round,
Timestamp: tmtime.Now(),
BlockPartsHeader: blockPartsHeader,
POLRound: polRound,
POLBlockID: polBlockID,
Type: ProposalType,
Height: height,
Round: round,
BlockID: blockID,
POLRound: polRound,
Timestamp: tmtime.Now(),
}
}
// ValidateBasic performs basic validation.
func (p *Proposal) ValidateBasic() error {
if p.Type != ProposalType {
return errors.New("Invalid Type")
}
if p.Height < 0 {
return errors.New("Negative Height")
}
if p.Round < 0 {
return errors.New("Negative Round")
}
if p.POLRound < -1 {
return errors.New("Negative POLRound (exception: -1)")
}
if err := p.BlockID.ValidateBasic(); err != nil {
return fmt.Errorf("Wrong BlockID: %v", err)
}
// NOTE: Timestamp validation is subtle and handled elsewhere.
if len(p.Signature) == 0 {
return errors.New("Signature is missing")
}
if len(p.Signature) > MaxSignatureSize {
return fmt.Errorf("Signature is too big (max: %d)", MaxSignatureSize)
}
return nil
}
// String returns a string representation of the Proposal.
func (p *Proposal) String() string {
return fmt.Sprintf("Proposal{%v/%v %v (%v,%v) %X @ %s}",
p.Height, p.Round, p.BlockPartsHeader, p.POLRound,
p.POLBlockID,
cmn.Fingerprint(p.Signature), CanonicalTime(p.Timestamp))
return fmt.Sprintf("Proposal{%v/%v (%v, %v) %X @ %s}",
p.Height,
p.Round,
p.BlockID,
p.POLRound,
cmn.Fingerprint(p.Signature),
CanonicalTime(p.Timestamp))
}
// SignBytes returns the Proposal bytes for signing


+ 9
- 7
types/proposal_test.go View File

@ -15,11 +15,11 @@ func init() {
panic(err)
}
testProposal = &Proposal{
Height: 12345,
Round: 23456,
BlockPartsHeader: PartSetHeader{111, []byte("blockparts")},
POLRound: -1,
Timestamp: stamp,
Height: 12345,
Round: 23456,
BlockID: BlockID{[]byte{1, 2, 3}, PartSetHeader{111, []byte("blockparts")}},
POLRound: -1,
Timestamp: stamp,
}
}
@ -34,7 +34,7 @@ func TestProposalSignable(t *testing.T) {
func TestProposalString(t *testing.T) {
str := testProposal.String()
expected := `Proposal{12345/23456 111:626C6F636B70 (-1,:0:000000000000) 000000000000 @ 2018-02-11T07:09:22.765Z}`
expected := `Proposal{12345/23456 (010203:111:626C6F636B70, -1) 000000000000 @ 2018-02-11T07:09:22.765Z}`
if str != expected {
t.Errorf("Got unexpected string for Proposal. Expected:\n%v\nGot:\n%v", expected, str)
}
@ -44,7 +44,9 @@ func TestProposalVerifySignature(t *testing.T) {
privVal := NewMockPV()
pubKey := privVal.GetPubKey()
prop := NewProposal(4, 2, PartSetHeader{777, []byte("proper")}, 2, BlockID{})
prop := NewProposal(
4, 2, 2,
BlockID{[]byte{1, 2, 3}, PartSetHeader{777, []byte("proper")}})
signBytes := prop.SignBytes("test_chain_id")
// sign it


+ 182
- 84
types/proto3/block.pb.go View File

@ -1,22 +1,9 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: block.proto
/*
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
*/
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: types/proto3/block.proto
package proto3
import proto "github.com/golang/protobuf/proto"
import proto "github.com/gogo/protobuf/proto"
import fmt "fmt"
import math "math"
@ -29,17 +16,39 @@ var _ = math.Inf
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
type PartSetHeader struct {
Total int32 `protobuf:"zigzag32,1,opt,name=Total" json:"Total,omitempty"`
Hash []byte `protobuf:"bytes,2,opt,name=Hash,proto3" json:"Hash,omitempty"`
Total int32 `protobuf:"varint,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:"-"`
}
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) Reset() { *m = PartSetHeader{} }
func (m *PartSetHeader) String() string { return proto.CompactTextString(m) }
func (*PartSetHeader) ProtoMessage() {}
func (*PartSetHeader) Descriptor() ([]byte, []int) {
return fileDescriptor_block_57c41dfc0fc285b3, []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) GetTotal() int32 {
if m != nil {
@ -56,14 +65,36 @@ 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" json:"PartsHeader,omitempty"`
Hash []byte `protobuf:"bytes,1,opt,name=Hash,proto3" json:"Hash,omitempty"`
PartsHeader *PartSetHeader `protobuf:"bytes,2,opt,name=PartsHeader" json:"PartsHeader,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
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_57c41dfc0fc285b3, []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)
}
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} }
var xxx_messageInfo_BlockID proto.InternalMessageInfo
func (m *BlockID) GetHash() []byte {
if m != nil {
@ -82,11 +113,11 @@ func (m *BlockID) GetPartsHeader() *PartSetHeader {
type Header struct {
// basic block info
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"`
ChainID string `protobuf:"bytes,2,opt,name=ChainID,proto3" json:"ChainID,omitempty"`
Height int64 `protobuf:"varint,3,opt,name=Height,proto3" 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"`
NumTxs int64 `protobuf:"varint,5,opt,name=NumTxs,proto3" json:"NumTxs,omitempty"`
TotalTxs int64 `protobuf:"varint,6,opt,name=TotalTxs,proto3" json:"TotalTxs,omitempty"`
// prev block info
LastBlockID *BlockID `protobuf:"bytes,7,opt,name=LastBlockID" json:"LastBlockID,omitempty"`
// hashes of block data
@ -99,14 +130,36 @@ type Header struct {
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,15,opt,name=EvidenceHash,proto3" json:"EvidenceHash,omitempty"`
ProposerAddress []byte `protobuf:"bytes,16,opt,name=ProposerAddress,proto3" json:"ProposerAddress,omitempty"`
EvidenceHash []byte `protobuf:"bytes,15,opt,name=EvidenceHash,proto3" json:"EvidenceHash,omitempty"`
ProposerAddress []byte `protobuf:"bytes,16,opt,name=ProposerAddress,proto3" json:"ProposerAddress,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
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_57c41dfc0fc285b3, []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 {
@ -221,14 +274,36 @@ func (m *Header) GetProposerAddress() []byte {
}
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"`
Block uint64 `protobuf:"varint,1,opt,name=Block,proto3" json:"Block,omitempty"`
App uint64 `protobuf:"varint,2,opt,name=App,proto3" json:"App,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Version) Reset() { *m = Version{} }
func (m *Version) String() string { return proto.CompactTextString(m) }
func (*Version) ProtoMessage() {}
func (*Version) Descriptor() ([]byte, []int) {
return fileDescriptor_block_57c41dfc0fc285b3, []int{3}
}
func (m *Version) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Version.Unmarshal(m, b)
}
func (m *Version) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Version.Marshal(b, m, deterministic)
}
func (dst *Version) XXX_Merge(src proto.Message) {
xxx_messageInfo_Version.Merge(dst, src)
}
func (m *Version) XXX_Size() int {
return xxx_messageInfo_Version.Size(m)
}
func (m *Version) XXX_DiscardUnknown() {
xxx_messageInfo_Version.DiscardUnknown(m)
}
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} }
var xxx_messageInfo_Version proto.InternalMessageInfo
func (m *Version) GetBlock() uint64 {
if m != nil {
@ -250,14 +325,36 @@ func (m *Version) GetApp() uint64 {
// 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.
type Timestamp struct {
Seconds int64 `protobuf:"varint,1,opt,name=seconds" json:"seconds,omitempty"`
Nanos int32 `protobuf:"varint,2,opt,name=nanos" json:"nanos,omitempty"`
Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"`
Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
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_57c41dfc0fc285b3, []int{4}
}
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)
}
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} }
var xxx_messageInfo_Timestamp proto.InternalMessageInfo
func (m *Timestamp) GetSeconds() int64 {
if m != nil {
@ -281,36 +378,37 @@ func init() {
proto.RegisterType((*Timestamp)(nil), "proto3.Timestamp")
}
func init() { proto.RegisterFile("block.proto", fileDescriptor0) }
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,
func init() { proto.RegisterFile("types/proto3/block.proto", fileDescriptor_block_57c41dfc0fc285b3) }
var fileDescriptor_block_57c41dfc0fc285b3 = []byte{
// 451 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x53, 0x5f, 0x6f, 0xd3, 0x30,
0x10, 0x57, 0x68, 0xda, 0xae, 0x97, 0x76, 0x1d, 0x27, 0x40, 0x16, 0x4f, 0x55, 0x04, 0xa8, 0xbc,
0x74, 0xda, 0xf6, 0x80, 0x10, 0x4f, 0xa5, 0x45, 0xda, 0x24, 0x34, 0x4d, 0xa6, 0xea, 0xbb, 0xd7,
0x58, 0x34, 0xa2, 0x89, 0xa3, 0x9c, 0x8b, 0xc6, 0x27, 0xe4, 0x6b, 0x21, 0x9f, 0x93, 0xd0, 0x44,
0x7b, 0xf3, 0xef, 0xcf, 0xfd, 0xce, 0xbe, 0x5c, 0x40, 0xd8, 0x3f, 0x85, 0xa6, 0xcb, 0xa2, 0x34,
0xd6, 0xdc, 0x5c, 0x3e, 0x1e, 0xcc, 0xee, 0xd7, 0x82, 0x01, 0x0e, 0x3c, 0x17, 0x7f, 0x86, 0xc9,
0x83, 0x2a, 0xed, 0x0f, 0x6d, 0x6f, 0xb5, 0x4a, 0x74, 0x89, 0xaf, 0xa0, 0xbf, 0x31, 0x56, 0x1d,
0x44, 0x30, 0x0b, 0xe6, 0x7d, 0xe9, 0x01, 0x22, 0x84, 0xb7, 0x8a, 0xf6, 0xe2, 0xc5, 0x2c, 0x98,
0x8f, 0x25, 0x9f, 0xe3, 0x2d, 0x0c, 0xbf, 0xba, 0xc4, 0xbb, 0x75, 0x23, 0x07, 0xff, 0x65, 0xfc,
0x04, 0x91, 0x4b, 0x26, 0x9f, 0xcb, 0x95, 0xd1, 0xf5, 0x6b, 0xdf, 0xfe, 0x66, 0xd1, 0x6a, 0x2a,
0x4f, 0x9d, 0xf1, 0xdf, 0x10, 0x06, 0xd5, 0x65, 0x3e, 0xc2, 0x70, 0xab, 0x4b, 0x4a, 0x4d, 0xce,
0xd1, 0xd1, 0xf5, 0xb4, 0xae, 0xaf, 0x68, 0x59, 0xeb, 0x28, 0x60, 0xb8, 0xda, 0xab, 0x34, 0xbf,
0x5b, 0x73, 0xab, 0x91, 0xac, 0x21, 0xbe, 0x71, 0x71, 0xe9, 0xcf, 0xbd, 0x15, 0xbd, 0x59, 0x30,
0xef, 0xc9, 0x0a, 0xe1, 0x7b, 0x08, 0x37, 0x69, 0xa6, 0x45, 0xc8, 0xc9, 0x2f, 0xeb, 0x64, 0xc7,
0x91, 0x55, 0x59, 0x21, 0x59, 0x76, 0xe5, 0xf7, 0xc7, 0x6c, 0xf3, 0x44, 0xa2, 0xef, 0xcb, 0x3d,
0xc2, 0xb7, 0x70, 0xc6, 0xb3, 0x71, 0xca, 0x80, 0x95, 0x06, 0xe3, 0x15, 0x44, 0xdf, 0x15, 0xd9,
0x6a, 0x3c, 0x62, 0xd8, 0xbe, 0x7b, 0x45, 0xcb, 0x53, 0x0f, 0x7e, 0x80, 0x73, 0x07, 0x57, 0x26,
0xcb, 0x52, 0xcb, 0xc3, 0x3c, 0xe3, 0x61, 0x76, 0x58, 0xd7, 0x76, 0xad, 0xac, 0x62, 0xc7, 0x88,
0x1d, 0x0d, 0x76, 0x19, 0x5b, 0x75, 0x48, 0x13, 0x65, 0x4d, 0x49, 0xec, 0x00, 0x9f, 0xd1, 0x66,
0x71, 0x01, 0x78, 0xaf, 0x9f, 0x6c, 0xc7, 0x1b, 0xb1, 0xf7, 0x19, 0x05, 0xdf, 0xc1, 0x64, 0x65,
0x72, 0xd2, 0x39, 0x1d, 0xbd, 0x75, 0xcc, 0xd6, 0x36, 0xe9, 0xbe, 0xc0, 0xb2, 0x28, 0x58, 0x9f,
0xb0, 0x5e, 0x43, 0x9c, 0xc3, 0xd4, 0xbd, 0x42, 0x6a, 0x3a, 0x1e, 0xac, 0x4f, 0x38, 0x67, 0x47,
0x97, 0xc6, 0x18, 0xc6, 0xdf, 0x7e, 0xa7, 0x89, 0xce, 0x77, 0x9a, 0x6d, 0x53, 0xb6, 0xb5, 0x38,
0x97, 0xf6, 0x50, 0x9a, 0xc2, 0x90, 0x2e, 0x97, 0x49, 0x52, 0x6a, 0x22, 0x71, 0xe1, 0xd3, 0x3a,
0x74, 0x7c, 0xd5, 0xac, 0x8f, 0x5b, 0x6b, 0x9e, 0x34, 0xef, 0x51, 0x28, 0x3d, 0xc0, 0x0b, 0xe8,
0x2d, 0x8b, 0x82, 0x17, 0x26, 0x94, 0xee, 0x18, 0x7f, 0x81, 0x51, 0xb3, 0x00, 0xee, 0x45, 0xa4,
0x77, 0x26, 0x4f, 0x88, 0xcb, 0x7a, 0xb2, 0x86, 0x2e, 0x2e, 0x57, 0xb9, 0x21, 0x2e, 0xed, 0x4b,
0x0f, 0x1e, 0xab, 0x9f, 0xea, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x4f, 0x84, 0xb5, 0xf8, 0x77,
0x03, 0x00, 0x00,
}

+ 4
- 4
types/proto3/block.proto View File

@ -4,7 +4,7 @@ package proto3;
message PartSetHeader {
sint32 Total = 1;
int32 Total = 1;
bytes Hash = 2;
}
@ -17,10 +17,10 @@ message Header {
// basic block info
Version Version = 1;
string ChainID = 2;
sint64 Height = 3;
int64 Height = 3;
Timestamp Time = 4;
sint64 NumTxs = 5;
sint64 TotalTxs = 6;
int64 NumTxs = 5;
int64 TotalTxs = 6;
// prev block info
BlockID LastBlockID = 7;


+ 18
- 6
types/protobuf.go View File

@ -24,6 +24,12 @@ const (
ABCIPubKeyTypeSecp256k1 = "secp256k1"
)
// TODO: Make non-global by allowing for registration of more pubkey types
var ABCIPubKeyTypesToAminoRoutes = map[string]string{
ABCIPubKeyTypeEd25519: ed25519.PubKeyAminoRoute,
ABCIPubKeyTypeSecp256k1: secp256k1.PubKeyAminoRoute,
}
//-------------------------------------------------------
// TM2PB is used for converting Tendermint ABCI to protobuf ABCI.
@ -119,12 +125,15 @@ func (tm2pb) ValidatorUpdates(vals *ValidatorSet) []abci.ValidatorUpdate {
func (tm2pb) ConsensusParams(params *ConsensusParams) *abci.ConsensusParams {
return &abci.ConsensusParams{
BlockSize: &abci.BlockSize{
BlockSize: &abci.BlockSizeParams{
MaxBytes: params.BlockSize.MaxBytes,
MaxGas: params.BlockSize.MaxGas,
},
EvidenceParams: &abci.EvidenceParams{
MaxAge: params.EvidenceParams.MaxAge,
Evidence: &abci.EvidenceParams{
MaxAge: params.Evidence.MaxAge,
},
Validator: &abci.ValidatorParams{
PubKeyTypes: params.Validator.PubKeyTypes,
},
}
}
@ -215,12 +224,15 @@ func (pb2tm) ValidatorUpdates(vals []abci.ValidatorUpdate) ([]*Validator, error)
func (pb2tm) ConsensusParams(csp *abci.ConsensusParams) ConsensusParams {
return ConsensusParams{
BlockSize: BlockSize{
BlockSize: BlockSizeParams{
MaxBytes: csp.BlockSize.MaxBytes,
MaxGas: csp.BlockSize.MaxGas,
},
EvidenceParams: EvidenceParams{
MaxAge: csp.EvidenceParams.MaxAge,
Evidence: EvidenceParams{
MaxAge: csp.Evidence.MaxAge,
},
Validator: ValidatorParams{
PubKeyTypes: csp.Validator.PubKeyTypes,
},
}
}

+ 0
- 1
types/protobuf_test.go View File

@ -64,7 +64,6 @@ func TestABCIValidators(t *testing.T) {
func TestABCIConsensusParams(t *testing.T) {
cp := DefaultConsensusParams()
cp.EvidenceParams.MaxAge = 0 // TODO add this to ABCI
abciCP := TM2PB.ConsensusParams(cp)
cp2 := PB2TM.ConsensusParams(abciCP)


+ 12
- 0
types/signable.go View File

@ -1,5 +1,17 @@
package types
import (
"github.com/tendermint/tendermint/crypto/ed25519"
cmn "github.com/tendermint/tendermint/libs/common"
)
var (
// MaxSignatureSize is a maximum allowed signature size for the Heartbeat,
// Proposal and Vote.
// XXX: secp256k1 does not have Size nor MaxSize defined.
MaxSignatureSize = cmn.MaxInt(ed25519.SignatureSize, 64)
)
// Signable is an interface for all signable things.
// It typically removes signatures before serializing.
// SignBytes returns the bytes to be signed


+ 4
- 5
types/signed_msg_type.go View File

@ -15,11 +15,10 @@ const (
HeartbeatType SignedMsgType = 0x30
)
func IsVoteTypeValid(type_ SignedMsgType) bool {
switch type_ {
case PrevoteType:
return true
case PrecommitType:
// IsVoteTypeValid returns true if t is a valid vote type.
func IsVoteTypeValid(t SignedMsgType) bool {
switch t {
case PrevoteType, PrecommitType:
return true
default:
return false


+ 40
- 0
types/validation.go View File

@ -0,0 +1,40 @@
package types
import (
"fmt"
"time"
"github.com/tendermint/tendermint/crypto/tmhash"
tmtime "github.com/tendermint/tendermint/types/time"
)
// ValidateTime does a basic time validation ensuring time does not drift too
// much: +/- one year.
// TODO: reduce this to eg 1 day
// NOTE: DO NOT USE in ValidateBasic methods in this package. This function
// can only be used for real time validation, like on proposals and votes
// in the consensus. If consensus is stuck, and rounds increase for more than a day,
// having only a 1-day band here could break things...
// Can't use for validating blocks because we may be syncing years worth of history.
func ValidateTime(t time.Time) error {
var (
now = tmtime.Now()
oneYear = 8766 * time.Hour
)
if t.Before(now.Add(-oneYear)) || t.After(now.Add(oneYear)) {
return fmt.Errorf("Time drifted too much. Expected: -1 < %v < 1 year", now)
}
return nil
}
// ValidateHash returns an error if the hash is not empty, but its
// size != tmhash.Size.
func ValidateHash(h []byte) error {
if len(h) > 0 && len(h) != tmhash.Size {
return fmt.Errorf("Expected size to be %d bytes, got %d bytes",
tmhash.Size,
len(h),
)
}
return nil
}

+ 4
- 4
types/validator.go View File

@ -77,15 +77,15 @@ func (v *Validator) Hash() []byte {
}
// Bytes computes the unique encoding of a validator with a given voting power.
// These are the bytes that gets hashed in consensus. It excludes pubkey
// as its redundant with the address. This also excludes accum which changes
// These are the bytes that gets hashed in consensus. It excludes address
// as its redundant with the pubkey. This also excludes accum which changes
// every round.
func (v *Validator) Bytes() []byte {
return cdcEncode((struct {
Address Address
PubKey crypto.PubKey
VotingPower int64
}{
v.Address,
v.PubKey,
v.VotingPower,
}))
}


+ 43
- 6
types/vote.go View File

@ -12,7 +12,7 @@ import (
const (
// MaxVoteBytes is a maximum vote size (including amino overhead).
MaxVoteBytes int64 = 203
MaxVoteBytes int64 = 223
)
var (
@ -46,15 +46,16 @@ func NewConflictingVoteError(val *Validator, voteA, voteB *Vote) *ErrVoteConflic
// Address is hex bytes.
type Address = crypto.Address
// Represents a prevote, precommit, or commit vote from validators for consensus.
// Vote represents a prevote, precommit, or commit vote from validators for
// consensus.
type Vote struct {
ValidatorAddress Address `json:"validator_address"`
ValidatorIndex int `json:"validator_index"`
Type SignedMsgType `json:"type"`
Height int64 `json:"height"`
Round int `json:"round"`
Timestamp time.Time `json:"timestamp"`
Type SignedMsgType `json:"type"`
BlockID BlockID `json:"block_id"` // zero if vote is nil.
ValidatorAddress Address `json:"validator_address"`
ValidatorIndex int `json:"validator_index"`
Signature []byte `json:"signature"`
}
@ -94,7 +95,8 @@ func (vote *Vote) String() string {
typeString,
cmn.Fingerprint(vote.BlockID.Hash),
cmn.Fingerprint(vote.Signature),
CanonicalTime(vote.Timestamp))
CanonicalTime(vote.Timestamp),
)
}
func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error {
@ -107,3 +109,38 @@ func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error {
}
return nil
}
// ValidateBasic performs basic validation.
func (vote *Vote) ValidateBasic() error {
if !IsVoteTypeValid(vote.Type) {
return errors.New("Invalid Type")
}
if vote.Height < 0 {
return errors.New("Negative Height")
}
if vote.Round < 0 {
return errors.New("Negative Round")
}
// NOTE: Timestamp validation is subtle and handled elsewhere.
if err := vote.BlockID.ValidateBasic(); err != nil {
return fmt.Errorf("Wrong BlockID: %v", err)
}
if len(vote.ValidatorAddress) != crypto.AddressSize {
return fmt.Errorf("Expected ValidatorAddress size to be %d bytes, got %d bytes",
crypto.AddressSize,
len(vote.ValidatorAddress),
)
}
if vote.ValidatorIndex < 0 {
return errors.New("Negative ValidatorIndex")
}
if len(vote.Signature) == 0 {
return errors.New("Signature is missing")
}
if len(vote.Signature) > MaxSignatureSize {
return fmt.Errorf("Signature is too big (max: %d)", MaxSignatureSize)
}
return nil
}

+ 22
- 7
types/vote_test.go View File

@ -7,6 +7,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/tmhash"
)
@ -26,12 +27,10 @@ func exampleVote(t byte) *Vote {
}
return &Vote{
ValidatorAddress: tmhash.Sum([]byte("validator_address")),
ValidatorIndex: 56789,
Height: 12345,
Round: 2,
Timestamp: stamp,
Type: SignedMsgType(t),
Type: SignedMsgType(t),
Height: 12345,
Round: 2,
Timestamp: stamp,
BlockID: BlockID{
Hash: tmhash.Sum([]byte("blockID_hash")),
PartsHeader: PartSetHeader{
@ -39,6 +38,8 @@ func exampleVote(t byte) *Vote {
Hash: tmhash.Sum([]byte("blockID_part_set_header_hash")),
},
},
ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
ValidatorIndex: 56789,
}
}
@ -211,7 +212,7 @@ func TestMaxVoteBytes(t *testing.T) {
timestamp := time.Date(math.MaxInt64, 0, 0, 0, 0, 0, math.MaxInt64, time.UTC)
vote := &Vote{
ValidatorAddress: tmhash.Sum([]byte("validator_address")),
ValidatorAddress: crypto.AddressHash([]byte("validator_address")),
ValidatorIndex: math.MaxInt64,
Height: math.MaxInt64,
Round: math.MaxInt64,
@ -235,3 +236,17 @@ func TestMaxVoteBytes(t *testing.T) {
assert.EqualValues(t, MaxVoteBytes, len(bz))
}
func TestVoteString(t *testing.T) {
str := examplePrecommit().String()
expected := `Vote{56789:6AF1F4111082 12345/02/2(Precommit) 8B01023386C3 000000000000 @ 2017-12-25T03:00:01.234Z}`
if str != expected {
t.Errorf("Got unexpected string for Vote. Expected:\n%v\nGot:\n%v", expected, str)
}
str2 := examplePrevote().String()
expected = `Vote{56789:6AF1F4111082 12345/02/1(Prevote) 8B01023386C3 000000000000 @ 2017-12-25T03:00:01.234Z}`
if str2 != expected {
t.Errorf("Got unexpected string for Vote. Expected:\n%v\nGot:\n%v", expected, str2)
}
}

Loading…
Cancel
Save