Compare commits

...

6 Commits

Author SHA1 Message Date
Anton Kaliaev
37928cb990 set next validators along with validators while replay (#2637)
Closes #2634
2018-10-14 22:28:41 -04:00
Dev Ojha
0790223518 Comment about ed25519 private key format on Sign (#2632)
Closes #2001
2018-10-13 20:01:21 -04:00
Ethan Buchman
0baa7588c2 p2p: NodeInfo is an interface; General cleanup (#2556)
* p2p: NodeInfo is an interface

* (squash) fixes from review

* (squash) more fixes from review

* p2p: remove peerConn.HandshakeTimeout

* p2p: NodeInfo is two interfaces. Remove String()

* fixes from review

* remove test code from peer.RemoteIP()

* p2p: remove peer.OriginalAddr(). See #2618

* use a mockPeer in peer_set_test.go

* p2p: fix testNodeInfo naming

* p2p: remove unused var

* remove testRandNodeInfo

* fix linter

* fix retry dialing self

* fix rpc
2018-10-12 19:25:33 -04:00
Ismail Khoffi
8888595b94 [R4R] Fixed sized and reordered fields for Vote/Proposal/Heartbeat SignBytes (#2598)
* WIP: switching to fixed offsets for SignBytes

* add version field to sign bytes and update order

* more comments on test-cases and add a tc with a chainID

* remove amino:"write_empty" tag

- it doesn't affect if default fixed size fields ((u)int64) are
written or not
- add comment about int->int64 casting

* update CHANGELOG_PENDING

* update documentation

* add back link to issue #1622 in documentation

* remove JSON tags and add (failing test-case)

* fix failing test

* update test-vectors due to added `Type` field

* change Type field from string to byte and add new type alias

- SignedMsgType replaces VoteTypePrevote, VoteTypePrecommit and adds new
ProposalType to separate votes from proposal when signed

- update test-vectors

* fix remains from rebasing

* use SignMessageType instead of byte everywhere

* fixes from review
2018-10-12 19:21:46 -04:00
Dev Ojha
1b51cf3f46 Remove unnecessary layer of indirection / unnecessary allocation of hashes (#2620) 2018-10-12 17:48:00 -04:00
Zarko Milosevic
2363d88979 consensus: Wait for proposal or timeout before prevote (#2540)
* Fix termination issues and improve tests

* Improve formatting and tests based on reviewer feedback
2018-10-12 16:13:01 -04:00
56 changed files with 1142 additions and 946 deletions

View File

@@ -24,11 +24,16 @@ BREAKING CHANGES:
* [types] \#2298 Remove `Index` and `Total` fields from `TxProof`. * [types] \#2298 Remove `Index` and `Total` fields from `TxProof`.
* [crypto/merkle & lite] \#2298 Various changes to accomodate General Merkle trees * [crypto/merkle & lite] \#2298 Various changes to accomodate General Merkle trees
* [crypto/merkle] \#2595 Remove all Hasher objects in favor of byte slices * [crypto/merkle] \#2595 Remove all Hasher objects in favor of byte slices
* [types] \#2598 `VoteTypeXxx` are now
* Blockchain Protocol * Blockchain Protocol
* [types] \#2459 `Vote`/`Proposal`/`Heartbeat` use amino encoding instead of JSON in `SignBytes`. * [types] Update SignBytes for `Vote`/`Proposal`/`Heartbeat`:
* \#2459 Use amino encoding instead of JSON in `SignBytes`.
* \#2598 Reorder fields and use fixed sized encoding.
* \#2598 Change `Type` field fromt `string` to `byte` and use new
`SignedMsgType` to enumerate.
* [types] \#2512 Remove the pubkey field from the validator hash * [types] \#2512 Remove the pubkey field from the validator hash
* [state] \#2587 require block.Time of the fist block to be genesis time * [state] \#2587 Require block.Time of the fist block to be genesis time
* P2P Protocol * P2P Protocol
@@ -37,9 +42,10 @@ FEATURES:
- [abci] \#2557 Add `Codespace` field to `Response{CheckTx, DeliverTx, Query}` - [abci] \#2557 Add `Codespace` field to `Response{CheckTx, DeliverTx, Query}`
IMPROVEMENTS: IMPROVEMENTS:
- [consensus] [\#2169](https://github.com/cosmos/cosmos-sdk/issues/2169) add additional metrics - Additional Metrics
- [p2p] [\#2169](https://github.com/cosmos/cosmos-sdk/issues/2169) add additional metrics - [consensus] [\#2169](https://github.com/cosmos/cosmos-sdk/issues/2169)
- [config] \#2232 added ValidateBasic method, which performs basic checks - [p2p] [\#2169](https://github.com/cosmos/cosmos-sdk/issues/2169)
- [config] \#2232 Added ValidateBasic method, which performs basic checks
- [crypto/ed25519] \#2558 Switch to use latest `golang.org/x/crypto` through our fork at - [crypto/ed25519] \#2558 Switch to use latest `golang.org/x/crypto` through our fork at
github.com/tendermint/crypto github.com/tendermint/crypto
- [tools] \#2238 Binary dependencies are now locked to a specific git commit - [tools] \#2238 Binary dependencies are now locked to a specific git commit
@@ -50,6 +56,8 @@ BUG FIXES:
- [node] \#2434 Make node respond to signal interrupts while sleeping for genesis time - [node] \#2434 Make node respond to signal interrupts while sleeping for genesis time
- [consensus] [\#1690](https://github.com/tendermint/tendermint/issues/1690) wait for - [consensus] [\#1690](https://github.com/tendermint/tendermint/issues/1690) wait for
timeoutPrecommit before starting next round timeoutPrecommit before starting next round
- [consensus] [\#1745](https://github.com/tendermint/tendermint/issues/1745) wait for
Proposal or timeoutProposal before entering prevote
- [evidence] \#2515 fix db iter leak (@goolAdapter) - [evidence] \#2515 fix db iter leak (@goolAdapter)
- [common/bit_array] Fixed a bug in the `Or` function - [common/bit_array] Fixed a bug in the `Or` function
- [common/bit_array] Fixed a bug in the `Sub` function (@james-ray) - [common/bit_array] Fixed a bug in the `Sub` function (@james-ray)

View File

@@ -12,23 +12,27 @@ import (
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
) )
func testNodeInfo(id p2p.ID) p2p.DefaultNodeInfo {
return p2p.DefaultNodeInfo{
ID_: id,
Moniker: "SOMENAME",
Network: "SOMENAME",
ListenAddr: "SOMEADDR",
Version: "SOMEVER",
Other: p2p.DefaultNodeInfoOther{
AminoVersion: "SOMESTRING",
P2PVersion: "OTHERSTRING",
},
}
}
func BenchmarkEncodeStatusWire(b *testing.B) { func BenchmarkEncodeStatusWire(b *testing.B) {
b.StopTimer() b.StopTimer()
cdc := amino.NewCodec() cdc := amino.NewCodec()
ctypes.RegisterAmino(cdc) ctypes.RegisterAmino(cdc)
nodeKey := p2p.NodeKey{PrivKey: ed25519.GenPrivKey()} nodeKey := p2p.NodeKey{PrivKey: ed25519.GenPrivKey()}
status := &ctypes.ResultStatus{ status := &ctypes.ResultStatus{
NodeInfo: p2p.NodeInfo{ NodeInfo: testNodeInfo(nodeKey.ID()),
ID: nodeKey.ID(),
Moniker: "SOMENAME",
Network: "SOMENAME",
ListenAddr: "SOMEADDR",
Version: "SOMEVER",
Other: p2p.NodeInfoOther{
AminoVersion: "SOMESTRING",
P2PVersion: "OTHERSTRING",
},
},
SyncInfo: ctypes.SyncInfo{ SyncInfo: ctypes.SyncInfo{
LatestBlockHash: []byte("SOMEBYTES"), LatestBlockHash: []byte("SOMEBYTES"),
LatestBlockHeight: 123, LatestBlockHeight: 123,
@@ -56,17 +60,7 @@ func BenchmarkEncodeNodeInfoWire(b *testing.B) {
cdc := amino.NewCodec() cdc := amino.NewCodec()
ctypes.RegisterAmino(cdc) ctypes.RegisterAmino(cdc)
nodeKey := p2p.NodeKey{PrivKey: ed25519.GenPrivKey()} nodeKey := p2p.NodeKey{PrivKey: ed25519.GenPrivKey()}
nodeInfo := p2p.NodeInfo{ nodeInfo := testNodeInfo(nodeKey.ID())
ID: nodeKey.ID(),
Moniker: "SOMENAME",
Network: "SOMENAME",
ListenAddr: "SOMEADDR",
Version: "SOMEVER",
Other: p2p.NodeInfoOther{
AminoVersion: "SOMESTRING",
P2PVersion: "OTHERSTRING",
},
}
b.StartTimer() b.StartTimer()
counter := 0 counter := 0
@@ -84,17 +78,7 @@ func BenchmarkEncodeNodeInfoBinary(b *testing.B) {
cdc := amino.NewCodec() cdc := amino.NewCodec()
ctypes.RegisterAmino(cdc) ctypes.RegisterAmino(cdc)
nodeKey := p2p.NodeKey{PrivKey: ed25519.GenPrivKey()} nodeKey := p2p.NodeKey{PrivKey: ed25519.GenPrivKey()}
nodeInfo := p2p.NodeInfo{ nodeInfo := testNodeInfo(nodeKey.ID())
ID: nodeKey.ID(),
Moniker: "SOMENAME",
Network: "SOMENAME",
ListenAddr: "SOMEADDR",
Version: "SOMEVER",
Other: p2p.NodeInfoOther{
AminoVersion: "SOMESTRING",
P2PVersion: "OTHERSTRING",
},
}
b.StartTimer() b.StartTimer()
counter := 0 counter := 0

View File

@@ -198,7 +198,7 @@ func (tp *bcrTestPeer) TrySend(chID byte, msgBytes []byte) bool {
} }
func (tp *bcrTestPeer) Send(chID byte, msgBytes []byte) bool { return tp.TrySend(chID, msgBytes) } func (tp *bcrTestPeer) Send(chID byte, msgBytes []byte) bool { return tp.TrySend(chID, msgBytes) }
func (tp *bcrTestPeer) NodeInfo() p2p.NodeInfo { return p2p.NodeInfo{} } func (tp *bcrTestPeer) NodeInfo() p2p.NodeInfo { return p2p.DefaultNodeInfo{} }
func (tp *bcrTestPeer) Status() p2p.ConnectionStatus { return p2p.ConnectionStatus{} } func (tp *bcrTestPeer) Status() p2p.ConnectionStatus { return p2p.ConnectionStatus{} }
func (tp *bcrTestPeer) ID() p2p.ID { return tp.id } func (tp *bcrTestPeer) ID() p2p.ID { return tp.id }
func (tp *bcrTestPeer) IsOutbound() bool { return false } func (tp *bcrTestPeer) IsOutbound() bool { return false }
@@ -206,4 +206,3 @@ func (tp *bcrTestPeer) IsPersistent() bool { return true }
func (tp *bcrTestPeer) Get(s string) interface{} { return s } func (tp *bcrTestPeer) Get(s string) interface{} { return s }
func (tp *bcrTestPeer) Set(string, interface{}) {} func (tp *bcrTestPeer) Set(string, interface{}) {}
func (tp *bcrTestPeer) RemoteIP() net.IP { return []byte{127, 0, 0, 1} } func (tp *bcrTestPeer) RemoteIP() net.IP { return []byte{127, 0, 0, 1} }
func (tp *bcrTestPeer) OriginalAddr() *p2p.NetAddress { return nil }

View File

@@ -565,7 +565,7 @@ func DefaultConsensusConfig() *ConsensusConfig {
// TestConsensusConfig returns a configuration for testing the consensus service // TestConsensusConfig returns a configuration for testing the consensus service
func TestConsensusConfig() *ConsensusConfig { func TestConsensusConfig() *ConsensusConfig {
cfg := DefaultConsensusConfig() cfg := DefaultConsensusConfig()
cfg.TimeoutPropose = 100 * time.Millisecond cfg.TimeoutPropose = 40 * time.Millisecond
cfg.TimeoutProposeDelta = 1 * time.Millisecond cfg.TimeoutProposeDelta = 1 * time.Millisecond
cfg.TimeoutPrevote = 10 * time.Millisecond cfg.TimeoutPrevote = 10 * time.Millisecond
cfg.TimeoutPrevoteDelta = 1 * time.Millisecond cfg.TimeoutPrevoteDelta = 1 * time.Millisecond

View File

@@ -226,8 +226,8 @@ func sendProposalAndParts(height int64, round int, cs *ConsensusState, peer p2p.
// votes // votes
cs.mtx.Lock() cs.mtx.Lock()
prevote, _ := cs.signVote(types.VoteTypePrevote, blockHash, parts.Header()) prevote, _ := cs.signVote(types.PrevoteType, blockHash, parts.Header())
precommit, _ := cs.signVote(types.VoteTypePrecommit, blockHash, parts.Header()) precommit, _ := cs.signVote(types.PrecommitType, blockHash, parts.Header())
cs.mtx.Unlock() cs.mtx.Unlock()
peer.Send(VoteChannel, cdc.MustMarshalBinaryBare(&VoteMessage{prevote})) peer.Send(VoteChannel, cdc.MustMarshalBinaryBare(&VoteMessage{prevote}))

View File

@@ -40,7 +40,7 @@ const (
// genesis, chain_id, priv_val // genesis, chain_id, priv_val
var config *cfg.Config // NOTE: must be reset for each _test.go file var config *cfg.Config // NOTE: must be reset for each _test.go file
var ensureTimeout = time.Second * 1 // must be in seconds because CreateEmptyBlocksInterval is var ensureTimeout = time.Millisecond * 100
func ensureDir(dir string, mode os.FileMode) { func ensureDir(dir string, mode os.FileMode) {
if err := cmn.EnsureDir(dir, mode); err != nil { if err := cmn.EnsureDir(dir, mode); err != nil {
@@ -71,7 +71,7 @@ func NewValidatorStub(privValidator types.PrivValidator, valIndex int) *validato
} }
} }
func (vs *validatorStub) signVote(voteType byte, hash []byte, header types.PartSetHeader) (*types.Vote, error) { func (vs *validatorStub) signVote(voteType types.SignedMsgType, hash []byte, header types.PartSetHeader) (*types.Vote, error) {
vote := &types.Vote{ vote := &types.Vote{
ValidatorIndex: vs.Index, ValidatorIndex: vs.Index,
ValidatorAddress: vs.PrivValidator.GetAddress(), ValidatorAddress: vs.PrivValidator.GetAddress(),
@@ -86,7 +86,7 @@ func (vs *validatorStub) signVote(voteType byte, hash []byte, header types.PartS
} }
// Sign vote for type/hash/header // Sign vote for type/hash/header
func signVote(vs *validatorStub, voteType byte, hash []byte, header types.PartSetHeader) *types.Vote { func signVote(vs *validatorStub, voteType types.SignedMsgType, hash []byte, header types.PartSetHeader) *types.Vote {
v, err := vs.signVote(voteType, hash, header) v, err := vs.signVote(voteType, hash, header)
if err != nil { if err != nil {
panic(fmt.Errorf("failed to sign vote: %v", err)) panic(fmt.Errorf("failed to sign vote: %v", err))
@@ -94,7 +94,7 @@ func signVote(vs *validatorStub, voteType byte, hash []byte, header types.PartSe
return v return v
} }
func signVotes(voteType byte, hash []byte, header types.PartSetHeader, vss ...*validatorStub) []*types.Vote { func signVotes(voteType types.SignedMsgType, hash []byte, header types.PartSetHeader, vss ...*validatorStub) []*types.Vote {
votes := make([]*types.Vote, len(vss)) votes := make([]*types.Vote, len(vss))
for i, vs := range vss { for i, vs := range vss {
votes[i] = signVote(vs, voteType, hash, header) votes[i] = signVote(vs, voteType, hash, header)
@@ -144,7 +144,7 @@ func addVotes(to *ConsensusState, votes ...*types.Vote) {
} }
} }
func signAddVotes(to *ConsensusState, voteType byte, hash []byte, header types.PartSetHeader, vss ...*validatorStub) { func signAddVotes(to *ConsensusState, voteType types.SignedMsgType, hash []byte, header types.PartSetHeader, vss ...*validatorStub) {
votes := signVotes(voteType, hash, header, vss...) votes := signVotes(voteType, hash, header, vss...)
addVotes(to, votes...) addVotes(to, votes...)
} }
@@ -317,67 +317,156 @@ func ensureNoNewEvent(ch <-chan interface{}, timeout time.Duration,
} }
} }
func ensureNoNewStep(stepCh <-chan interface{}) { func ensureNoNewEventOnChannel(ch <-chan interface{}) {
ensureNoNewEvent(stepCh, ensureTimeout, "We should be stuck waiting, "+ ensureNoNewEvent(
"not moving to the next step") ch,
ensureTimeout,
"We should be stuck waiting, not receiving new event on the channel")
}
func ensureNoNewRoundStep(stepCh <-chan interface{}) {
ensureNoNewEvent(
stepCh,
ensureTimeout,
"We should be stuck waiting, not receiving NewRoundStep event")
}
func ensureNoNewUnlock(unlockCh <-chan interface{}) {
ensureNoNewEvent(
unlockCh,
ensureTimeout,
"We should be stuck waiting, not receiving Unlock event")
} }
func ensureNoNewTimeout(stepCh <-chan interface{}, timeout int64) { func ensureNoNewTimeout(stepCh <-chan interface{}, timeout int64) {
timeoutDuration := time.Duration(timeout*5) * time.Nanosecond timeoutDuration := time.Duration(timeout*5) * time.Nanosecond
ensureNoNewEvent(stepCh, timeoutDuration, "We should be stuck waiting, "+ ensureNoNewEvent(
"not moving to the next step") stepCh,
timeoutDuration,
"We should be stuck waiting, not receiving NewTimeout event")
} }
func ensureNewEvent(ch <-chan interface{}, timeout time.Duration, errorMessage string) { func ensureNewEvent(
ch <-chan interface{},
height int64,
round int,
timeout time.Duration,
errorMessage string) {
select { select {
case <-time.After(timeout): case <-time.After(timeout):
panic(errorMessage) panic(errorMessage)
case <-ch: case ev := <-ch:
break rs, ok := ev.(types.EventDataRoundState)
if !ok {
panic(
fmt.Sprintf(
"expected a EventDataRoundState, got %v.Wrong subscription channel?",
reflect.TypeOf(rs)))
}
if rs.Height != height {
panic(fmt.Sprintf("expected height %v, got %v", height, rs.Height))
}
if rs.Round != round {
panic(fmt.Sprintf("expected round %v, got %v", round, rs.Round))
}
// TODO: We could check also for a step at this point!
} }
} }
func ensureNewStep(stepCh <-chan interface{}) { func ensureNewRoundStep(stepCh <-chan interface{}, height int64, round int) {
ensureNewEvent(stepCh, ensureTimeout, ensureNewEvent(
stepCh,
height,
round,
ensureTimeout,
"Timeout expired while waiting for NewStep event") "Timeout expired while waiting for NewStep event")
} }
func ensureNewRound(roundCh <-chan interface{}) { func ensureNewVote(voteCh <-chan interface{}, height int64, round int) {
ensureNewEvent(roundCh, ensureTimeout,
"Timeout expired while waiting for NewRound event")
}
func ensureNewTimeout(timeoutCh <-chan interface{}, timeout int64) {
timeoutDuration := time.Duration(timeout*5) * time.Nanosecond
ensureNewEvent(timeoutCh, timeoutDuration,
"Timeout expired while waiting for NewTimeout event")
}
func ensureNewProposal(proposalCh <-chan interface{}) {
ensureNewEvent(proposalCh, ensureTimeout,
"Timeout expired while waiting for NewProposal event")
}
func ensureNewBlock(blockCh <-chan interface{}) {
ensureNewEvent(blockCh, ensureTimeout,
"Timeout expired while waiting for NewBlock event")
}
func ensureNewVote(voteCh <-chan interface{}) {
ensureNewEvent(voteCh, ensureTimeout,
"Timeout expired while waiting for NewVote event")
}
func ensureNewUnlock(unlockCh <-chan interface{}) {
ensureNewEvent(unlockCh, ensureTimeout,
"Timeout expired while waiting for NewUnlock event")
}
func ensureVote(voteCh chan interface{}, height int64, round int,
voteType byte) {
select { select {
case <-time.After(ensureTimeout): case <-time.After(ensureTimeout):
break break
case v := <-voteCh:
edv, ok := v.(types.EventDataVote)
if !ok {
panic(fmt.Sprintf("expected a *types.Vote, "+
"got %v. wrong subscription channel?",
reflect.TypeOf(v)))
}
vote := edv.Vote
if vote.Height != height {
panic(fmt.Sprintf("expected height %v, got %v", height, vote.Height))
}
if vote.Round != round {
panic(fmt.Sprintf("expected round %v, got %v", round, vote.Round))
}
}
}
func ensureNewRound(roundCh <-chan interface{}, height int64, round int) {
ensureNewEvent(roundCh, height, round, ensureTimeout,
"Timeout expired while waiting for NewRound event")
}
func ensureNewTimeout(timeoutCh <-chan interface{}, height int64, round int, timeout int64) {
timeoutDuration := time.Duration(timeout*3) * time.Nanosecond
ensureNewEvent(timeoutCh, height, round, timeoutDuration,
"Timeout expired while waiting for NewTimeout event")
}
func ensureNewProposal(proposalCh <-chan interface{}, height int64, round int) {
ensureNewEvent(proposalCh, height, round, ensureTimeout,
"Timeout expired while waiting for NewProposal event")
}
func ensureNewBlock(blockCh <-chan interface{}, height int64) {
select {
case <-time.After(ensureTimeout):
panic("Timeout expired while waiting for NewBlock event")
case ev := <-blockCh:
block, ok := ev.(types.EventDataNewBlock)
if !ok {
panic(fmt.Sprintf("expected a *types.EventDataNewBlock, "+
"got %v. wrong subscription channel?",
reflect.TypeOf(block)))
}
if block.Block.Height != height {
panic(fmt.Sprintf("expected height %v, got %v", height, block.Block.Height))
}
}
}
func ensureNewBlockHeader(blockCh <-chan interface{}, height int64, blockHash cmn.HexBytes) {
select {
case <-time.After(ensureTimeout):
panic("Timeout expired while waiting for NewBlockHeader event")
case ev := <-blockCh:
blockHeader, ok := ev.(types.EventDataNewBlockHeader)
if !ok {
panic(fmt.Sprintf("expected a *types.EventDataNewBlockHeader, "+
"got %v. wrong subscription channel?",
reflect.TypeOf(blockHeader)))
}
if blockHeader.Header.Height != height {
panic(fmt.Sprintf("expected height %v, got %v", height, blockHeader.Header.Height))
}
if !bytes.Equal(blockHeader.Header.Hash(), blockHash) {
panic(fmt.Sprintf("expected header %X, got %X", blockHash, blockHeader.Header.Hash()))
}
}
}
func ensureNewUnlock(unlockCh <-chan interface{}, height int64, round int) {
ensureNewEvent(unlockCh, height, round, ensureTimeout,
"Timeout expired while waiting for NewUnlock event")
}
func ensureVote(voteCh <-chan interface{}, height int64, round int,
voteType types.SignedMsgType) {
select {
case <-time.After(ensureTimeout):
panic("Timeout expired while waiting for NewVote event")
case v := <-voteCh: case v := <-voteCh:
edv, ok := v.(types.EventDataVote) edv, ok := v.(types.EventDataVote)
if !ok { if !ok {
@@ -398,6 +487,14 @@ func ensureVote(voteCh chan interface{}, height int64, round int,
} }
} }
func ensureNewEventOnChannel(ch <-chan interface{}) {
select {
case <-time.After(ensureTimeout):
panic("Timeout expired while waiting for new activity on the channel")
case <-ch:
}
}
//------------------------------------------------------------------------------- //-------------------------------------------------------------------------------
// consensus nets // consensus nets
@@ -471,7 +568,7 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF
func getSwitchIndex(switches []*p2p.Switch, peer p2p.Peer) int { func getSwitchIndex(switches []*p2p.Switch, peer p2p.Peer) int {
for i, s := range switches { for i, s := range switches {
if peer.NodeInfo().ID == s.NodeInfo().ID { if peer.NodeInfo().ID() == s.NodeInfo().ID() {
return i return i
} }
} }

View File

@@ -28,12 +28,12 @@ func TestMempoolNoProgressUntilTxsAvailable(t *testing.T) {
newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock) newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock)
startTestRound(cs, height, round) startTestRound(cs, height, round)
ensureNewStep(newBlockCh) // first block gets committed ensureNewEventOnChannel(newBlockCh) // first block gets committed
ensureNoNewStep(newBlockCh) ensureNoNewEventOnChannel(newBlockCh)
deliverTxsRange(cs, 0, 1) deliverTxsRange(cs, 0, 1)
ensureNewStep(newBlockCh) // commit txs ensureNewEventOnChannel(newBlockCh) // commit txs
ensureNewStep(newBlockCh) // commit updated app hash ensureNewEventOnChannel(newBlockCh) // commit updated app hash
ensureNoNewStep(newBlockCh) ensureNoNewEventOnChannel(newBlockCh)
} }
func TestMempoolProgressAfterCreateEmptyBlocksInterval(t *testing.T) { func TestMempoolProgressAfterCreateEmptyBlocksInterval(t *testing.T) {
@@ -46,9 +46,9 @@ func TestMempoolProgressAfterCreateEmptyBlocksInterval(t *testing.T) {
newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock) newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock)
startTestRound(cs, height, round) startTestRound(cs, height, round)
ensureNewStep(newBlockCh) // first block gets committed ensureNewEventOnChannel(newBlockCh) // first block gets committed
ensureNoNewStep(newBlockCh) // then we dont make a block ... ensureNoNewEventOnChannel(newBlockCh) // then we dont make a block ...
ensureNewStep(newBlockCh) // until the CreateEmptyBlocksInterval has passed ensureNewEventOnChannel(newBlockCh) // until the CreateEmptyBlocksInterval has passed
} }
func TestMempoolProgressInHigherRound(t *testing.T) { func TestMempoolProgressInHigherRound(t *testing.T) {
@@ -72,13 +72,19 @@ func TestMempoolProgressInHigherRound(t *testing.T) {
} }
startTestRound(cs, height, round) startTestRound(cs, height, round)
ensureNewStep(newRoundCh) // first round at first height ensureNewRoundStep(newRoundCh, height, round) // first round at first height
ensureNewStep(newBlockCh) // first block gets committed ensureNewEventOnChannel(newBlockCh) // first block gets committed
ensureNewStep(newRoundCh) // first round at next height
height = height + 1 // moving to the next height
round = 0
ensureNewRoundStep(newRoundCh, height, round) // first round at next height
deliverTxsRange(cs, 0, 1) // we deliver txs, but dont set a proposal so we get the next round deliverTxsRange(cs, 0, 1) // we deliver txs, but dont set a proposal so we get the next round
<-timeoutCh ensureNewTimeout(timeoutCh, height, round, cs.config.TimeoutPropose.Nanoseconds())
ensureNewStep(newRoundCh) // wait for the next round
ensureNewStep(newBlockCh) // now we can commit the block round = round + 1 // moving to the next round
ensureNewRoundStep(newRoundCh, height, round) // wait for the next round
ensureNewEventOnChannel(newBlockCh) // now we can commit the block
} }
func deliverTxsRange(cs *ConsensusState, start, end int) { func deliverTxsRange(cs *ConsensusState, start, end int) {

View File

@@ -237,9 +237,9 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
// (and consequently shows which we don't have) // (and consequently shows which we don't have)
var ourVotes *cmn.BitArray var ourVotes *cmn.BitArray
switch msg.Type { switch msg.Type {
case types.VoteTypePrevote: case types.PrevoteType:
ourVotes = votes.Prevotes(msg.Round).BitArrayByBlockID(msg.BlockID) ourVotes = votes.Prevotes(msg.Round).BitArrayByBlockID(msg.BlockID)
case types.VoteTypePrecommit: case types.PrecommitType:
ourVotes = votes.Precommits(msg.Round).BitArrayByBlockID(msg.BlockID) ourVotes = votes.Precommits(msg.Round).BitArrayByBlockID(msg.BlockID)
default: default:
conR.Logger.Error("Bad VoteSetBitsMessage field Type") conR.Logger.Error("Bad VoteSetBitsMessage field Type")
@@ -317,9 +317,9 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
if height == msg.Height { if height == msg.Height {
var ourVotes *cmn.BitArray var ourVotes *cmn.BitArray
switch msg.Type { switch msg.Type {
case types.VoteTypePrevote: case types.PrevoteType:
ourVotes = votes.Prevotes(msg.Round).BitArrayByBlockID(msg.BlockID) ourVotes = votes.Prevotes(msg.Round).BitArrayByBlockID(msg.BlockID)
case types.VoteTypePrecommit: case types.PrecommitType:
ourVotes = votes.Precommits(msg.Round).BitArrayByBlockID(msg.BlockID) ourVotes = votes.Precommits(msg.Round).BitArrayByBlockID(msg.BlockID)
default: default:
conR.Logger.Error("Bad VoteSetBitsMessage field Type") conR.Logger.Error("Bad VoteSetBitsMessage field Type")
@@ -739,7 +739,7 @@ OUTER_LOOP:
peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{ peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
Height: prs.Height, Height: prs.Height,
Round: prs.Round, Round: prs.Round,
Type: types.VoteTypePrevote, Type: types.PrevoteType,
BlockID: maj23, BlockID: maj23,
})) }))
time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration) time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration)
@@ -756,7 +756,7 @@ OUTER_LOOP:
peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{ peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
Height: prs.Height, Height: prs.Height,
Round: prs.Round, Round: prs.Round,
Type: types.VoteTypePrecommit, Type: types.PrecommitType,
BlockID: maj23, BlockID: maj23,
})) }))
time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration) time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration)
@@ -773,7 +773,7 @@ OUTER_LOOP:
peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{ peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
Height: prs.Height, Height: prs.Height,
Round: prs.ProposalPOLRound, Round: prs.ProposalPOLRound,
Type: types.VoteTypePrevote, Type: types.PrevoteType,
BlockID: maj23, BlockID: maj23,
})) }))
time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration) time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration)
@@ -792,7 +792,7 @@ OUTER_LOOP:
peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{ peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
Height: prs.Height, Height: prs.Height,
Round: commit.Round(), Round: commit.Round(),
Type: types.VoteTypePrecommit, Type: types.PrecommitType,
BlockID: commit.BlockID, BlockID: commit.BlockID,
})) }))
time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration) time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration)
@@ -1022,7 +1022,7 @@ func (ps *PeerState) PickVoteToSend(votes types.VoteSetReader) (vote *types.Vote
return nil, false return nil, false
} }
height, round, type_, size := votes.Height(), votes.Round(), votes.Type(), votes.Size() height, round, type_, size := votes.Height(), votes.Round(), types.SignedMsgType(votes.Type()), votes.Size()
// Lazily set data using 'votes'. // Lazily set data using 'votes'.
if votes.IsCommit() { if votes.IsCommit() {
@@ -1041,7 +1041,7 @@ func (ps *PeerState) PickVoteToSend(votes types.VoteSetReader) (vote *types.Vote
return nil, false return nil, false
} }
func (ps *PeerState) getVoteBitArray(height int64, round int, type_ byte) *cmn.BitArray { func (ps *PeerState) getVoteBitArray(height int64, round int, type_ types.SignedMsgType) *cmn.BitArray {
if !types.IsVoteTypeValid(type_) { if !types.IsVoteTypeValid(type_) {
return nil return nil
} }
@@ -1049,25 +1049,25 @@ func (ps *PeerState) getVoteBitArray(height int64, round int, type_ byte) *cmn.B
if ps.PRS.Height == height { if ps.PRS.Height == height {
if ps.PRS.Round == round { if ps.PRS.Round == round {
switch type_ { switch type_ {
case types.VoteTypePrevote: case types.PrevoteType:
return ps.PRS.Prevotes return ps.PRS.Prevotes
case types.VoteTypePrecommit: case types.PrecommitType:
return ps.PRS.Precommits return ps.PRS.Precommits
} }
} }
if ps.PRS.CatchupCommitRound == round { if ps.PRS.CatchupCommitRound == round {
switch type_ { switch type_ {
case types.VoteTypePrevote: case types.PrevoteType:
return nil return nil
case types.VoteTypePrecommit: case types.PrecommitType:
return ps.PRS.CatchupCommit return ps.PRS.CatchupCommit
} }
} }
if ps.PRS.ProposalPOLRound == round { if ps.PRS.ProposalPOLRound == round {
switch type_ { switch type_ {
case types.VoteTypePrevote: case types.PrevoteType:
return ps.PRS.ProposalPOL return ps.PRS.ProposalPOL
case types.VoteTypePrecommit: case types.PrecommitType:
return nil return nil
} }
} }
@@ -1076,9 +1076,9 @@ func (ps *PeerState) getVoteBitArray(height int64, round int, type_ byte) *cmn.B
if ps.PRS.Height == height+1 { if ps.PRS.Height == height+1 {
if ps.PRS.LastCommitRound == round { if ps.PRS.LastCommitRound == round {
switch type_ { switch type_ {
case types.VoteTypePrevote: case types.PrevoteType:
return nil return nil
case types.VoteTypePrecommit: case types.PrecommitType:
return ps.PRS.LastCommit return ps.PRS.LastCommit
} }
} }
@@ -1187,7 +1187,7 @@ func (ps *PeerState) SetHasVote(vote *types.Vote) {
ps.setHasVote(vote.Height, vote.Round, vote.Type, vote.ValidatorIndex) ps.setHasVote(vote.Height, vote.Round, vote.Type, vote.ValidatorIndex)
} }
func (ps *PeerState) setHasVote(height int64, round int, type_ byte, index int) { func (ps *PeerState) setHasVote(height int64, round int, type_ types.SignedMsgType, index int) {
logger := ps.logger.With("peerH/R", fmt.Sprintf("%d/%d", ps.PRS.Height, ps.PRS.Round), "H/R", fmt.Sprintf("%d/%d", height, round)) logger := ps.logger.With("peerH/R", fmt.Sprintf("%d/%d", ps.PRS.Height, ps.PRS.Round), "H/R", fmt.Sprintf("%d/%d", height, round))
logger.Debug("setHasVote", "type", type_, "index", index) logger.Debug("setHasVote", "type", type_, "index", index)
@@ -1453,7 +1453,7 @@ func (m *VoteMessage) String() string {
type HasVoteMessage struct { type HasVoteMessage struct {
Height int64 Height int64
Round int Round int
Type byte Type types.SignedMsgType
Index int Index int
} }
@@ -1468,7 +1468,7 @@ func (m *HasVoteMessage) String() string {
type VoteSetMaj23Message struct { type VoteSetMaj23Message struct {
Height int64 Height int64
Round int Round int
Type byte Type types.SignedMsgType
BlockID types.BlockID BlockID types.BlockID
} }
@@ -1483,7 +1483,7 @@ func (m *VoteSetMaj23Message) String() string {
type VoteSetBitsMessage struct { type VoteSetBitsMessage struct {
Height int64 Height int64
Round int Round int
Type byte Type types.SignedMsgType
BlockID types.BlockID BlockID types.BlockID
Votes *cmn.BitArray Votes *cmn.BitArray
} }

View File

@@ -287,6 +287,7 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
return nil, err return nil, err
} }
state.Validators = types.NewValidatorSet(vals) state.Validators = types.NewValidatorSet(vals)
state.NextValidators = types.NewValidatorSet(vals)
} }
if res.ConsensusParams != nil { if res.ConsensusParams != nil {
state.ConsensusParams = types.PB2TM.ConsensusParams(res.ConsensusParams) state.ConsensusParams = types.PB2TM.ConsensusParams(res.ConsensusParams)

View File

@@ -542,7 +542,7 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
return nil, nil, err return nil, nil, err
} }
case *types.Vote: case *types.Vote:
if p.Type == types.VoteTypePrecommit { if p.Type == types.PrecommitType {
thisBlockCommit = &types.Commit{ thisBlockCommit = &types.Commit{
BlockID: p.BlockID, BlockID: p.BlockID,
Precommits: []*types.Vote{p}, Precommits: []*types.Vote{p},

View File

@@ -83,6 +83,7 @@ type ConsensusState struct {
// internal state // internal state
mtx sync.RWMutex mtx sync.RWMutex
cstypes.RoundState cstypes.RoundState
triggeredTimeoutPrecommit bool
state sm.State // State until height-1. state sm.State // State until height-1.
// state changes may be triggered by: msgs from peers, // state changes may be triggered by: msgs from peers,
@@ -459,7 +460,7 @@ func (cs *ConsensusState) reconstructLastCommit(state sm.State) {
return return
} }
seenCommit := cs.blockStore.LoadSeenCommit(state.LastBlockHeight) seenCommit := cs.blockStore.LoadSeenCommit(state.LastBlockHeight)
lastPrecommits := types.NewVoteSet(state.ChainID, state.LastBlockHeight, seenCommit.Round(), types.VoteTypePrecommit, state.LastValidators) lastPrecommits := types.NewVoteSet(state.ChainID, state.LastBlockHeight, seenCommit.Round(), types.PrecommitType, state.LastValidators)
for _, precommit := range seenCommit.Precommits { for _, precommit := range seenCommit.Precommits {
if precommit == nil { if precommit == nil {
continue continue
@@ -711,6 +712,7 @@ func (cs *ConsensusState) handleTimeout(ti timeoutInfo, rs cstypes.RoundState) {
cs.enterPrecommit(ti.Height, ti.Round) cs.enterPrecommit(ti.Height, ti.Round)
case cstypes.RoundStepPrecommitWait: case cstypes.RoundStepPrecommitWait:
cs.eventBus.PublishEventTimeoutWait(cs.RoundStateEvent()) cs.eventBus.PublishEventTimeoutWait(cs.RoundStateEvent())
cs.enterPrecommit(ti.Height, ti.Round)
cs.enterNewRound(ti.Height, ti.Round+1) cs.enterNewRound(ti.Height, ti.Round+1)
default: default:
panic(fmt.Sprintf("Invalid timeout step: %v", ti.Step)) panic(fmt.Sprintf("Invalid timeout step: %v", ti.Step))
@@ -772,6 +774,7 @@ func (cs *ConsensusState) enterNewRound(height int64, round int) {
cs.ProposalBlockParts = nil cs.ProposalBlockParts = nil
} }
cs.Votes.SetRound(round + 1) // also track next round (round+1) to allow round-skipping cs.Votes.SetRound(round + 1) // also track next round (round+1) to allow round-skipping
cs.triggeredTimeoutPrecommit = false
cs.eventBus.PublishEventNewRound(cs.RoundStateEvent()) cs.eventBus.PublishEventNewRound(cs.RoundStateEvent())
cs.metrics.Rounds.Set(float64(round)) cs.metrics.Rounds.Set(float64(round))
@@ -782,7 +785,8 @@ func (cs *ConsensusState) enterNewRound(height int64, round int) {
waitForTxs := cs.config.WaitForTxs() && round == 0 && !cs.needProofBlock(height) waitForTxs := cs.config.WaitForTxs() && round == 0 && !cs.needProofBlock(height)
if waitForTxs { if waitForTxs {
if cs.config.CreateEmptyBlocksInterval > 0 { if cs.config.CreateEmptyBlocksInterval > 0 {
cs.scheduleTimeout(cs.config.CreateEmptyBlocksInterval, height, round, cstypes.RoundStepNewRound) cs.scheduleTimeout(cs.config.CreateEmptyBlocksInterval, height, round,
cstypes.RoundStepNewRound)
} }
go cs.proposalHeartbeat(height, round) go cs.proposalHeartbeat(height, round)
} else { } else {
@@ -1013,17 +1017,18 @@ func (cs *ConsensusState) enterPrevote(height int64, round int) {
func (cs *ConsensusState) defaultDoPrevote(height int64, round int) { func (cs *ConsensusState) defaultDoPrevote(height int64, round int) {
logger := cs.Logger.With("height", height, "round", round) logger := cs.Logger.With("height", height, "round", round)
// If a block is locked, prevote that. // If a block is locked, prevote that.
if cs.LockedBlock != nil { if cs.LockedBlock != nil {
logger.Info("enterPrevote: Block was locked") logger.Info("enterPrevote: Block was locked")
cs.signAddVote(types.VoteTypePrevote, cs.LockedBlock.Hash(), cs.LockedBlockParts.Header()) cs.signAddVote(types.PrevoteType, cs.LockedBlock.Hash(), cs.LockedBlockParts.Header())
return return
} }
// If ProposalBlock is nil, prevote nil. // If ProposalBlock is nil, prevote nil.
if cs.ProposalBlock == nil { if cs.ProposalBlock == nil {
logger.Info("enterPrevote: ProposalBlock is nil") logger.Info("enterPrevote: ProposalBlock is nil")
cs.signAddVote(types.VoteTypePrevote, nil, types.PartSetHeader{}) cs.signAddVote(types.PrevoteType, nil, types.PartSetHeader{})
return return
} }
@@ -1032,7 +1037,7 @@ func (cs *ConsensusState) defaultDoPrevote(height int64, round int) {
if err != nil { if err != nil {
// ProposalBlock is invalid, prevote nil. // ProposalBlock is invalid, prevote nil.
logger.Error("enterPrevote: ProposalBlock is invalid", "err", err) logger.Error("enterPrevote: ProposalBlock is invalid", "err", err)
cs.signAddVote(types.VoteTypePrevote, nil, types.PartSetHeader{}) cs.signAddVote(types.PrevoteType, nil, types.PartSetHeader{})
return return
} }
@@ -1040,7 +1045,7 @@ func (cs *ConsensusState) defaultDoPrevote(height int64, round int) {
// NOTE: the proposal signature is validated when it is received, // NOTE: the proposal signature is validated when it is received,
// and the proposal block parts are validated as they are received (against the merkle hash in the proposal) // and the proposal block parts are validated as they are received (against the merkle hash in the proposal)
logger.Info("enterPrevote: ProposalBlock is valid") logger.Info("enterPrevote: ProposalBlock is valid")
cs.signAddVote(types.VoteTypePrevote, cs.ProposalBlock.Hash(), cs.ProposalBlockParts.Header()) cs.signAddVote(types.PrevoteType, cs.ProposalBlock.Hash(), cs.ProposalBlockParts.Header())
} }
// Enter: any +2/3 prevotes at next round. // Enter: any +2/3 prevotes at next round.
@@ -1098,7 +1103,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
} else { } else {
logger.Info("enterPrecommit: No +2/3 prevotes during enterPrecommit. Precommitting nil.") logger.Info("enterPrecommit: No +2/3 prevotes during enterPrecommit. Precommitting nil.")
} }
cs.signAddVote(types.VoteTypePrecommit, nil, types.PartSetHeader{}) cs.signAddVote(types.PrecommitType, nil, types.PartSetHeader{})
return return
} }
@@ -1122,7 +1127,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
cs.LockedBlockParts = nil cs.LockedBlockParts = nil
cs.eventBus.PublishEventUnlock(cs.RoundStateEvent()) cs.eventBus.PublishEventUnlock(cs.RoundStateEvent())
} }
cs.signAddVote(types.VoteTypePrecommit, nil, types.PartSetHeader{}) cs.signAddVote(types.PrecommitType, nil, types.PartSetHeader{})
return return
} }
@@ -1133,7 +1138,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
logger.Info("enterPrecommit: +2/3 prevoted locked block. Relocking") logger.Info("enterPrecommit: +2/3 prevoted locked block. Relocking")
cs.LockedRound = round cs.LockedRound = round
cs.eventBus.PublishEventRelock(cs.RoundStateEvent()) cs.eventBus.PublishEventRelock(cs.RoundStateEvent())
cs.signAddVote(types.VoteTypePrecommit, blockID.Hash, blockID.PartsHeader) cs.signAddVote(types.PrecommitType, blockID.Hash, blockID.PartsHeader)
return return
} }
@@ -1148,7 +1153,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
cs.LockedBlock = cs.ProposalBlock cs.LockedBlock = cs.ProposalBlock
cs.LockedBlockParts = cs.ProposalBlockParts cs.LockedBlockParts = cs.ProposalBlockParts
cs.eventBus.PublishEventLock(cs.RoundStateEvent()) cs.eventBus.PublishEventLock(cs.RoundStateEvent())
cs.signAddVote(types.VoteTypePrecommit, blockID.Hash, blockID.PartsHeader) cs.signAddVote(types.PrecommitType, blockID.Hash, blockID.PartsHeader)
return return
} }
@@ -1164,15 +1169,19 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
cs.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartsHeader) cs.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartsHeader)
} }
cs.eventBus.PublishEventUnlock(cs.RoundStateEvent()) cs.eventBus.PublishEventUnlock(cs.RoundStateEvent())
cs.signAddVote(types.VoteTypePrecommit, nil, types.PartSetHeader{}) cs.signAddVote(types.PrecommitType, nil, types.PartSetHeader{})
} }
// Enter: any +2/3 precommits for next round. // Enter: any +2/3 precommits for next round.
func (cs *ConsensusState) enterPrecommitWait(height int64, round int) { func (cs *ConsensusState) enterPrecommitWait(height int64, round int) {
logger := cs.Logger.With("height", height, "round", round) logger := cs.Logger.With("height", height, "round", round)
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrecommitWait <= cs.Step) { if cs.Height != height || round < cs.Round || (cs.Round == round && cs.triggeredTimeoutPrecommit) {
logger.Debug(fmt.Sprintf("enterPrecommitWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) logger.Debug(
fmt.Sprintf(
"enterPrecommitWait(%v/%v): Invalid args. "+
"Current state is Height/Round: %v/%v/, triggeredTimeoutPrecommit:%v",
height, round, cs.Height, cs.Round, cs.triggeredTimeoutPrecommit))
return return
} }
if !cs.Votes.Precommits(round).HasTwoThirdsAny() { if !cs.Votes.Precommits(round).HasTwoThirdsAny() {
@@ -1182,7 +1191,7 @@ func (cs *ConsensusState) enterPrecommitWait(height int64, round int) {
defer func() { defer func() {
// Done enterPrecommitWait: // Done enterPrecommitWait:
cs.updateRoundStep(round, cstypes.RoundStepPrecommitWait) cs.triggeredTimeoutPrecommit = true
cs.newStep() cs.newStep()
}() }()
@@ -1495,6 +1504,9 @@ func (cs *ConsensusState) addProposalBlockPart(msg *BlockPartMessage, peerID p2p
if cs.Step <= cstypes.RoundStepPropose && cs.isProposalComplete() { if cs.Step <= cstypes.RoundStepPropose && cs.isProposalComplete() {
// Move onto the next step // Move onto the next step
cs.enterPrevote(height, cs.Round) cs.enterPrevote(height, cs.Round)
if hasTwoThirds { // this is optimisation as this will be triggered when prevote is added
cs.enterPrecommit(height, cs.Round)
}
} else if cs.Step == cstypes.RoundStepCommit { } else if cs.Step == cstypes.RoundStepCommit {
// If we're waiting on the proposal block... // If we're waiting on the proposal block...
cs.tryFinalizeCommit(height) cs.tryFinalizeCommit(height)
@@ -1538,7 +1550,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
// A precommit for the previous height? // A precommit for the previous height?
// These come in while we wait timeoutCommit // These come in while we wait timeoutCommit
if vote.Height+1 == cs.Height { if vote.Height+1 == cs.Height {
if !(cs.Step == cstypes.RoundStepNewHeight && vote.Type == types.VoteTypePrecommit) { if !(cs.Step == cstypes.RoundStepNewHeight && vote.Type == types.PrecommitType) {
// TODO: give the reason .. // TODO: give the reason ..
// fmt.Errorf("tryAddVote: Wrong height, not a LastCommit straggler commit.") // fmt.Errorf("tryAddVote: Wrong height, not a LastCommit straggler commit.")
return added, ErrVoteHeightMismatch return added, ErrVoteHeightMismatch
@@ -1581,7 +1593,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
cs.evsw.FireEvent(types.EventVote, vote) cs.evsw.FireEvent(types.EventVote, vote)
switch vote.Type { switch vote.Type {
case types.VoteTypePrevote: case types.PrevoteType:
prevotes := cs.Votes.Prevotes(vote.Round) prevotes := cs.Votes.Prevotes(vote.Round)
cs.Logger.Info("Added to prevote", "vote", vote, "prevotes", prevotes.StringShort()) cs.Logger.Info("Added to prevote", "vote", vote, "prevotes", prevotes.StringShort())
@@ -1609,7 +1621,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
// Update Valid* if we can. // Update Valid* if we can.
// NOTE: our proposal block may be nil or not what received a polka.. // 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 // TODO: we may want to still update the ValidBlock and obtain it via gossipping
if !blockID.IsZero() && if len(blockID.Hash) != 0 &&
(cs.ValidRound < vote.Round) && (cs.ValidRound < vote.Round) &&
(vote.Round <= cs.Round) && (vote.Round <= cs.Round) &&
cs.ProposalBlock.HashesTo(blockID.Hash) { cs.ProposalBlock.HashesTo(blockID.Hash) {
@@ -1621,14 +1633,14 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
} }
} }
// If +2/3 prevotes for *anything* for this or future round: // If +2/3 prevotes for *anything* for future round:
if cs.Round <= vote.Round && prevotes.HasTwoThirdsAny() { if cs.Round < vote.Round && prevotes.HasTwoThirdsAny() {
// Round-skip over to PrevoteWait or goto Precommit. // Round-skip if there is any 2/3+ of votes ahead of us
cs.enterNewRound(height, vote.Round) // if the vote is ahead of us cs.enterNewRound(height, vote.Round)
} else if cs.Round == vote.Round && cstypes.RoundStepPrevote <= cs.Step { // current round
if prevotes.HasTwoThirdsMajority() { if prevotes.HasTwoThirdsMajority() {
cs.enterPrecommit(height, vote.Round) cs.enterPrecommit(height, vote.Round)
} else { } else if prevotes.HasTwoThirdsAny() {
cs.enterPrevote(height, vote.Round) // if the vote is ahead of us
cs.enterPrevoteWait(height, vote.Round) cs.enterPrevoteWait(height, vote.Round)
} }
} else if cs.Proposal != nil && 0 <= cs.Proposal.POLRound && cs.Proposal.POLRound == vote.Round { } else if cs.Proposal != nil && 0 <= cs.Proposal.POLRound && cs.Proposal.POLRound == vote.Round {
@@ -1638,24 +1650,28 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
} }
} }
case types.VoteTypePrecommit: case types.PrecommitType:
precommits := cs.Votes.Precommits(vote.Round) precommits := cs.Votes.Precommits(vote.Round)
cs.Logger.Info("Added to precommit", "vote", vote, "precommits", precommits.StringShort()) cs.Logger.Info("Added to precommit", "vote", vote, "precommits", precommits.StringShort())
blockID, ok := precommits.TwoThirdsMajority() blockID, ok := precommits.TwoThirdsMajority()
if ok && len(blockID.Hash) != 0 { if ok {
// Executed as TwoThirdsMajority could be from a higher round // Executed as TwoThirdsMajority could be from a higher round
cs.enterNewRound(height, vote.Round) cs.enterNewRound(height, vote.Round)
cs.enterPrecommit(height, vote.Round) cs.enterPrecommit(height, vote.Round)
if len(blockID.Hash) != 0 {
cs.enterCommit(height, vote.Round) cs.enterCommit(height, vote.Round)
if cs.config.SkipTimeoutCommit && precommits.HasAll() { if cs.config.SkipTimeoutCommit && precommits.HasAll() {
cs.enterNewRound(cs.Height, 0) cs.enterNewRound(cs.Height, 0)
} }
} else if cs.Round <= vote.Round && precommits.HasTwoThirdsAny() { } else {
cs.enterNewRound(height, vote.Round)
cs.enterPrecommit(height, vote.Round)
cs.enterPrecommitWait(height, vote.Round) cs.enterPrecommitWait(height, vote.Round)
} }
} else if cs.Round <= vote.Round && precommits.HasTwoThirdsAny() {
cs.enterNewRound(height, vote.Round)
cs.enterPrecommitWait(height, vote.Round)
}
default: default:
panic(fmt.Sprintf("Unexpected vote type %X", vote.Type)) // go-wire should prevent this. panic(fmt.Sprintf("Unexpected vote type %X", vote.Type)) // go-wire should prevent this.
} }
@@ -1663,7 +1679,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
return return
} }
func (cs *ConsensusState) signVote(type_ byte, hash []byte, header types.PartSetHeader) (*types.Vote, error) { func (cs *ConsensusState) signVote(type_ types.SignedMsgType, hash []byte, header types.PartSetHeader) (*types.Vote, error) {
addr := cs.privValidator.GetAddress() addr := cs.privValidator.GetAddress()
valIndex, _ := cs.Validators.GetByAddress(addr) valIndex, _ := cs.Validators.GetByAddress(addr)
@@ -1698,7 +1714,7 @@ func (cs *ConsensusState) voteTime() time.Time {
} }
// sign the vote and publish on internalMsgQueue // sign the vote and publish on internalMsgQueue
func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header types.PartSetHeader) *types.Vote { func (cs *ConsensusState) signAddVote(type_ types.SignedMsgType, hash []byte, header types.PartSetHeader) *types.Vote {
// if we don't have a key or we're not in the validator set, do nothing // if we don't have a key or we're not in the validator set, do nothing
if cs.privValidator == nil || !cs.Validators.HasAddress(cs.privValidator.GetAddress()) { if cs.privValidator == nil || !cs.Validators.HasAddress(cs.privValidator.GetAddress()) {
return nil return nil

File diff suppressed because it is too large Load Diff

View File

@@ -99,8 +99,8 @@ func (hvs *HeightVoteSet) addRound(round int) {
cmn.PanicSanity("addRound() for an existing round") cmn.PanicSanity("addRound() for an existing round")
} }
// log.Debug("addRound(round)", "round", round) // log.Debug("addRound(round)", "round", round)
prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, types.VoteTypePrevote, hvs.valSet) prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, types.PrevoteType, hvs.valSet)
precommits := types.NewVoteSet(hvs.chainID, hvs.height, round, types.VoteTypePrecommit, hvs.valSet) precommits := types.NewVoteSet(hvs.chainID, hvs.height, round, types.PrecommitType, hvs.valSet)
hvs.roundVoteSets[round] = RoundVoteSet{ hvs.roundVoteSets[round] = RoundVoteSet{
Prevotes: prevotes, Prevotes: prevotes,
Precommits: precommits, Precommits: precommits,
@@ -134,13 +134,13 @@ func (hvs *HeightVoteSet) AddVote(vote *types.Vote, peerID p2p.ID) (added bool,
func (hvs *HeightVoteSet) Prevotes(round int) *types.VoteSet { func (hvs *HeightVoteSet) Prevotes(round int) *types.VoteSet {
hvs.mtx.Lock() hvs.mtx.Lock()
defer hvs.mtx.Unlock() defer hvs.mtx.Unlock()
return hvs.getVoteSet(round, types.VoteTypePrevote) return hvs.getVoteSet(round, types.PrevoteType)
} }
func (hvs *HeightVoteSet) Precommits(round int) *types.VoteSet { func (hvs *HeightVoteSet) Precommits(round int) *types.VoteSet {
hvs.mtx.Lock() hvs.mtx.Lock()
defer hvs.mtx.Unlock() defer hvs.mtx.Unlock()
return hvs.getVoteSet(round, types.VoteTypePrecommit) return hvs.getVoteSet(round, types.PrecommitType)
} }
// Last round and blockID that has +2/3 prevotes for a particular block or nil. // Last round and blockID that has +2/3 prevotes for a particular block or nil.
@@ -149,7 +149,7 @@ func (hvs *HeightVoteSet) POLInfo() (polRound int, polBlockID types.BlockID) {
hvs.mtx.Lock() hvs.mtx.Lock()
defer hvs.mtx.Unlock() defer hvs.mtx.Unlock()
for r := hvs.round; r >= 0; r-- { for r := hvs.round; r >= 0; r-- {
rvs := hvs.getVoteSet(r, types.VoteTypePrevote) rvs := hvs.getVoteSet(r, types.PrevoteType)
polBlockID, ok := rvs.TwoThirdsMajority() polBlockID, ok := rvs.TwoThirdsMajority()
if ok { if ok {
return r, polBlockID return r, polBlockID
@@ -158,15 +158,15 @@ func (hvs *HeightVoteSet) POLInfo() (polRound int, polBlockID types.BlockID) {
return -1, types.BlockID{} return -1, types.BlockID{}
} }
func (hvs *HeightVoteSet) getVoteSet(round int, type_ byte) *types.VoteSet { func (hvs *HeightVoteSet) getVoteSet(round int, type_ types.SignedMsgType) *types.VoteSet {
rvs, ok := hvs.roundVoteSets[round] rvs, ok := hvs.roundVoteSets[round]
if !ok { if !ok {
return nil return nil
} }
switch type_ { switch type_ {
case types.VoteTypePrevote: case types.PrevoteType:
return rvs.Prevotes return rvs.Prevotes
case types.VoteTypePrecommit: case types.PrecommitType:
return rvs.Precommits return rvs.Precommits
default: default:
cmn.PanicSanity(fmt.Sprintf("Unexpected vote type %X", type_)) cmn.PanicSanity(fmt.Sprintf("Unexpected vote type %X", type_))
@@ -178,7 +178,7 @@ func (hvs *HeightVoteSet) getVoteSet(round int, type_ byte) *types.VoteSet {
// NOTE: if there are too many peers, or too much peer churn, // NOTE: if there are too many peers, or too much peer churn,
// this can cause memory issues. // this can cause memory issues.
// TODO: implement ability to remove peers too // TODO: implement ability to remove peers too
func (hvs *HeightVoteSet) SetPeerMaj23(round int, type_ byte, peerID p2p.ID, blockID types.BlockID) error { func (hvs *HeightVoteSet) SetPeerMaj23(round int, type_ types.SignedMsgType, peerID p2p.ID, blockID types.BlockID) error {
hvs.mtx.Lock() hvs.mtx.Lock()
defer hvs.mtx.Unlock() defer hvs.mtx.Unlock()
if !types.IsVoteTypeValid(type_) { if !types.IsVoteTypeValid(type_) {

View File

@@ -56,7 +56,7 @@ func makeVoteHR(t *testing.T, height int64, round int, privVals []types.PrivVali
Height: height, Height: height,
Round: round, Round: round,
Timestamp: tmtime.Now(), Timestamp: tmtime.Now(),
Type: types.VoteTypePrecommit, Type: types.PrecommitType,
BlockID: types.BlockID{[]byte("fakehash"), types.PartSetHeader{}}, BlockID: types.BlockID{[]byte("fakehash"), types.PartSetHeader{}},
} }
chainID := config.ChainID() chainID := config.ChainID()

View File

@@ -46,6 +46,12 @@ func (privKey PrivKeyEd25519) Bytes() []byte {
} }
// Sign produces a signature on the provided message. // Sign produces a signature on the provided message.
// This assumes the privkey is wellformed in the golang format.
// The first 32 bytes should be random,
// corresponding to the normal ed25519 private key.
// The latter 32 bytes should be the compressed public key.
// If these conditions aren't met, Sign will panic or produce an
// incorrect signature.
func (privKey PrivKeyEd25519) Sign(msg []byte) ([]byte, error) { func (privKey PrivKeyEd25519) Sign(msg []byte) ([]byte, error) {
signatureBytes := ed25519.Sign(privKey[:], msg) signatureBytes := ed25519.Sign(privKey[:], msg)
return signatureBytes[:], nil return signatureBytes[:], nil

View File

@@ -21,12 +21,16 @@ func SimpleHashFromTwoHashes(left, right []byte) []byte {
// SimpleHashFromByteSlices computes a Merkle tree where the leaves are the byte slice, // SimpleHashFromByteSlices computes a Merkle tree where the leaves are the byte slice,
// in the provided order. // in the provided order.
func SimpleHashFromByteSlices(items [][]byte) []byte { func SimpleHashFromByteSlices(items [][]byte) []byte {
hashes := make([][]byte, len(items)) switch len(items) {
for i, item := range items { case 0:
hash := tmhash.Sum(item) return nil
hashes[i] = hash case 1:
return tmhash.Sum(items[0])
default:
left := SimpleHashFromByteSlices(items[:(len(items)+1)/2])
right := SimpleHashFromByteSlices(items[(len(items)+1)/2:])
return SimpleHashFromTwoHashes(left, right)
} }
return simpleHashFromHashes(hashes)
} }
// SimpleHashFromMap computes a Merkle tree from sorted map. // SimpleHashFromMap computes a Merkle tree from sorted map.
@@ -40,20 +44,3 @@ func SimpleHashFromMap(m map[string][]byte) []byte {
} }
return sm.Hash() return sm.Hash()
} }
//----------------------------------------------------------------
// Expects hashes!
func simpleHashFromHashes(hashes [][]byte) []byte {
// Recursive impl.
switch len(hashes) {
case 0:
return nil
case 1:
return hashes[0]
default:
left := simpleHashFromHashes(hashes[:(len(hashes)+1)/2])
right := simpleHashFromHashes(hashes[(len(hashes)+1)/2:])
return SimpleHashFromTwoHashes(left, right)
}
}

View File

@@ -411,7 +411,8 @@ must be greater than 2/3 of the total voting power of the complete validator set
A vote is a signed message broadcast in the consensus for a particular block at a particular height and round. A vote is a signed message broadcast in the consensus for a particular block at a particular height and round.
When stored in the blockchain or propagated over the network, votes are encoded in Amino. When stored in the blockchain or propagated over the network, votes are encoded in Amino.
For signing, votes are represented via `CanonicalVote` and also encoded using amino (protobuf compatible) via For signing, votes are represented via `CanonicalVote` and also encoded using amino (protobuf compatible) via
`Vote.SignBytes` which includes the `ChainID`. `Vote.SignBytes` which includes the `ChainID`, and uses a different ordering of
the fields.
We define a method `Verify` that returns `true` if the signature verifies against the pubkey for the `SignBytes` We define a method `Verify` that returns `true` if the signature verifies against the pubkey for the `SignBytes`
using the given ChainID: using the given ChainID:

View File

@@ -300,20 +300,23 @@ Where the `"value"` is the base64 encoding of the raw pubkey bytes, and the
Signed messages (eg. votes, proposals) in the consensus are encoded using Amino. Signed messages (eg. votes, proposals) in the consensus are encoded using Amino.
When signing, the elements of a message are sorted alphabetically by key and prepended with When signing, the elements of a message are re-ordered so the fixed-length fields
a `chain_id` and `type` field. are first, making it easy to quickly check the version, height, round, and type.
The `ChainID` is also appended to the end.
We call this encoding the SignBytes. For instance, SignBytes for a vote is the Amino encoding of the following struct: We call this encoding the SignBytes. For instance, SignBytes for a vote is the Amino encoding of the following struct:
```go ```go
type CanonicalVote struct { type CanonicalVote struct {
ChainID string Version uint64 `binary:"fixed64"`
Type string Height int64 `binary:"fixed64"`
BlockID CanonicalBlockID Round int64 `binary:"fixed64"`
Height int64
Round int
Timestamp time.Time
VoteType byte VoteType byte
Timestamp time.Time
BlockID CanonicalBlockID
ChainID string
} }
``` ```
NOTE: see [#1622](https://github.com/tendermint/tendermint/issues/1622) for how field ordering will change The field ordering and the fixed sized encoding for the first three fields is optimized to ease parsing of SignBytes
in HSMs. It creates fixed offsets for relevant fields that need to be read in this context.
See [#1622](https://github.com/tendermint/tendermint/issues/1622) for more details.

View File

@@ -97,7 +97,7 @@ func makeVote(header *types.Header, valset *types.ValidatorSet, key crypto.PrivK
Height: header.Height, Height: header.Height,
Round: 1, Round: 1,
Timestamp: tmtime.Now(), Timestamp: tmtime.Now(),
Type: types.VoteTypePrecommit, Type: types.PrecommitType,
BlockID: types.BlockID{Hash: header.Hash()}, BlockID: types.BlockID{Hash: header.Hash()},
} }
// Sign it // Sign it

View File

@@ -761,8 +761,8 @@ func makeNodeInfo(
if _, ok := txIndexer.(*null.TxIndex); ok { if _, ok := txIndexer.(*null.TxIndex); ok {
txIndexerStatus = "off" txIndexerStatus = "off"
} }
nodeInfo := p2p.NodeInfo{ nodeInfo := p2p.DefaultNodeInfo{
ID: nodeID, ID_: nodeID,
Network: chainID, Network: chainID,
Version: version.Version, Version: version.Version,
Channels: []byte{ Channels: []byte{
@@ -772,7 +772,7 @@ func makeNodeInfo(
evidence.EvidenceChannel, evidence.EvidenceChannel,
}, },
Moniker: config.Moniker, Moniker: config.Moniker,
Other: p2p.NodeInfoOther{ Other: p2p.DefaultNodeInfoOther{
AminoVersion: amino.Version, AminoVersion: amino.Version,
P2PVersion: p2p.Version, P2PVersion: p2p.Version,
ConsensusVersion: cs.Version, ConsensusVersion: cs.Version,

View File

@@ -42,7 +42,7 @@ func (p *peer) IsPersistent() bool {
// NodeInfo always returns empty node info. // NodeInfo always returns empty node info.
func (p *peer) NodeInfo() p2p.NodeInfo { func (p *peer) NodeInfo() p2p.NodeInfo {
return p2p.NodeInfo{} return p2p.DefaultNodeInfo{}
} }
// RemoteIP always returns localhost. // RemoteIP always returns localhost.
@@ -78,8 +78,3 @@ func (p *peer) Get(key string) interface{} {
} }
return nil return nil
} }
// OriginalAddr always returns nil.
func (p *peer) OriginalAddr() *p2p.NetAddress {
return nil
}

View File

@@ -40,13 +40,12 @@ func (e ErrRejected) Error() string {
if e.isDuplicate { if e.isDuplicate {
if e.conn != nil { if e.conn != nil {
return fmt.Sprintf( return fmt.Sprintf(
"duplicate CONN<%s>: %s", "duplicate CONN<%s>",
e.conn.RemoteAddr().String(), e.conn.RemoteAddr().String(),
e.err,
) )
} }
if e.id != "" { if e.id != "" {
return fmt.Sprintf("duplicate ID<%v>: %s", e.id, e.err) return fmt.Sprintf("duplicate ID<%v>", e.id)
} }
} }

View File

@@ -56,7 +56,6 @@ func PrometheusMetrics(namespace string) *Metrics {
Name: "num_txs", Name: "num_txs",
Help: "Number of transactions submitted by each peer.", Help: "Number of transactions submitted by each peer.",
}, []string{"peer_id"}), }, []string{"peer_id"}),
} }
} }

View File

@@ -2,6 +2,7 @@ package p2p
import ( import (
"fmt" "fmt"
"reflect"
"strings" "strings"
cmn "github.com/tendermint/tendermint/libs/common" cmn "github.com/tendermint/tendermint/libs/common"
@@ -17,12 +18,32 @@ func MaxNodeInfoSize() int {
return maxNodeInfoSize return maxNodeInfoSize
} }
// NodeInfo is the basic node information exchanged // NodeInfo exposes basic info of a node
// and determines if we're compatible
type NodeInfo interface {
nodeInfoAddress
nodeInfoTransport
}
// nodeInfoAddress exposes just the core info of a node.
type nodeInfoAddress interface {
ID() ID
NetAddress() *NetAddress
}
// nodeInfoTransport is validates a nodeInfo and checks
// our compatibility with it. It's for use in the handshake.
type nodeInfoTransport interface {
ValidateBasic() error
CompatibleWith(other NodeInfo) error
}
// DefaultNodeInfo is the basic node information exchanged
// between two peers during the Tendermint P2P handshake. // between two peers during the Tendermint P2P handshake.
type NodeInfo struct { type DefaultNodeInfo struct {
// Authenticate // Authenticate
// TODO: replace with NetAddress // TODO: replace with NetAddress
ID ID `json:"id"` // authenticated identifier ID_ ID `json:"id"` // authenticated identifier
ListenAddr string `json:"listen_addr"` // accepting incoming ListenAddr string `json:"listen_addr"` // accepting incoming
// Check compatibility. // Check compatibility.
@@ -33,11 +54,11 @@ type NodeInfo struct {
// ASCIIText fields // ASCIIText fields
Moniker string `json:"moniker"` // arbitrary moniker Moniker string `json:"moniker"` // arbitrary moniker
Other NodeInfoOther `json:"other"` // other application specific data Other DefaultNodeInfoOther `json:"other"` // other application specific data
} }
// NodeInfoOther is the misc. applcation specific data // DefaultNodeInfoOther is the misc. applcation specific data
type NodeInfoOther struct { type DefaultNodeInfoOther struct {
AminoVersion string `json:"amino_version"` AminoVersion string `json:"amino_version"`
P2PVersion string `json:"p2p_version"` P2PVersion string `json:"p2p_version"`
ConsensusVersion string `json:"consensus_version"` ConsensusVersion string `json:"consensus_version"`
@@ -46,19 +67,12 @@ type NodeInfoOther struct {
RPCAddress string `json:"rpc_address"` RPCAddress string `json:"rpc_address"`
} }
func (o NodeInfoOther) String() string { // ID returns the node's peer ID.
return fmt.Sprintf( func (info DefaultNodeInfo) ID() ID {
"{amino_version: %v, p2p_version: %v, consensus_version: %v, rpc_version: %v, tx_index: %v, rpc_address: %v}", return info.ID_
o.AminoVersion,
o.P2PVersion,
o.ConsensusVersion,
o.RPCVersion,
o.TxIndex,
o.RPCAddress,
)
} }
// Validate checks the self-reported NodeInfo is safe. // ValidateBasic checks the self-reported DefaultNodeInfo is safe.
// It returns an error if there // It returns an error if there
// are too many Channels, if there are any duplicate Channels, // are too many Channels, if there are any duplicate Channels,
// if the ListenAddr is malformed, or if the ListenAddr is a host name // if the ListenAddr is malformed, or if the ListenAddr is a host name
@@ -71,7 +85,7 @@ func (o NodeInfoOther) String() string {
// International clients could then use punycode (or we could use // International clients could then use punycode (or we could use
// url-encoding), and we just need to be careful with how we handle that in our // url-encoding), and we just need to be careful with how we handle that in our
// clients. (e.g. off by default). // clients. (e.g. off by default).
func (info NodeInfo) Validate() error { func (info DefaultNodeInfo) ValidateBasic() error {
if len(info.Channels) > maxNumChannels { if len(info.Channels) > maxNumChannels {
return fmt.Errorf("info.Channels is too long (%v). Max is %v", len(info.Channels), maxNumChannels) return fmt.Errorf("info.Channels is too long (%v). Max is %v", len(info.Channels), maxNumChannels)
} }
@@ -111,14 +125,19 @@ func (info NodeInfo) Validate() error {
} }
// ensure ListenAddr is good // ensure ListenAddr is good
_, err := NewNetAddressString(IDAddressString(info.ID, info.ListenAddr)) _, err := NewNetAddressString(IDAddressString(info.ID(), info.ListenAddr))
return err return err
} }
// CompatibleWith checks if two NodeInfo are compatible with eachother. // CompatibleWith checks if two DefaultNodeInfo are compatible with eachother.
// CONTRACT: two nodes are compatible if the major version matches and network match // CONTRACT: two nodes are compatible if the major version matches and network match
// and they have at least one channel in common. // and they have at least one channel in common.
func (info NodeInfo) CompatibleWith(other NodeInfo) error { func (info DefaultNodeInfo) CompatibleWith(other_ NodeInfo) error {
other, ok := other_.(DefaultNodeInfo)
if !ok {
return fmt.Errorf("wrong NodeInfo type. Expected DefaultNodeInfo, got %v", reflect.TypeOf(other_))
}
iMajor, _, _, iErr := splitVersion(info.Version) iMajor, _, _, iErr := splitVersion(info.Version)
oMajor, _, _, oErr := splitVersion(other.Version) oMajor, _, _, oErr := splitVersion(other.Version)
@@ -164,18 +183,18 @@ OUTER_LOOP:
return nil return nil
} }
// NetAddress returns a NetAddress derived from the NodeInfo - // NetAddress returns a NetAddress derived from the DefaultNodeInfo -
// it includes the authenticated peer ID and the self-reported // it includes the authenticated peer ID and the self-reported
// ListenAddr. Note that the ListenAddr is not authenticated and // ListenAddr. Note that the ListenAddr is not authenticated and
// may not match that address actually dialed if its an outbound peer. // may not match that address actually dialed if its an outbound peer.
func (info NodeInfo) NetAddress() *NetAddress { func (info DefaultNodeInfo) NetAddress() *NetAddress {
netAddr, err := NewNetAddressString(IDAddressString(info.ID, info.ListenAddr)) netAddr, err := NewNetAddressString(IDAddressString(info.ID(), info.ListenAddr))
if err != nil { if err != nil {
switch err.(type) { switch err.(type) {
case ErrNetAddressLookup: case ErrNetAddressLookup:
// XXX If the peer provided a host name and the lookup fails here // XXX If the peer provided a host name and the lookup fails here
// we're out of luck. // we're out of luck.
// TODO: use a NetAddress in NodeInfo // TODO: use a NetAddress in DefaultNodeInfo
default: default:
panic(err) // everything should be well formed by now panic(err) // everything should be well formed by now
} }
@@ -183,11 +202,6 @@ func (info NodeInfo) NetAddress() *NetAddress {
return netAddr return netAddr
} }
func (info NodeInfo) String() string {
return fmt.Sprintf("NodeInfo{id: %v, moniker: %v, network: %v [listen %v], version: %v (%v)}",
info.ID, info.Moniker, info.Network, info.ListenAddr, info.Version, info.Other)
}
func splitVersion(version string) (string, string, string, error) { func splitVersion(version string) (string, string, string, error) {
spl := strings.Split(version, ".") spl := strings.Split(version, ".")
if len(spl) != 3 { if len(spl) != 3 {

View File

@@ -3,7 +3,6 @@ package p2p
import ( import (
"fmt" "fmt"
"net" "net"
"sync/atomic"
"time" "time"
cmn "github.com/tendermint/tendermint/libs/common" cmn "github.com/tendermint/tendermint/libs/common"
@@ -15,19 +14,18 @@ import (
const metricsTickerDuration = 10 * time.Second const metricsTickerDuration = 10 * time.Second
var testIPSuffix uint32
// Peer is an interface representing a peer connected on a reactor. // Peer is an interface representing a peer connected on a reactor.
type Peer interface { type Peer interface {
cmn.Service cmn.Service
ID() ID // peer's cryptographic ID ID() ID // peer's cryptographic ID
RemoteIP() net.IP // remote IP of the connection RemoteIP() net.IP // remote IP of the connection
IsOutbound() bool // did we dial the peer IsOutbound() bool // did we dial the peer
IsPersistent() bool // do we redial this peer when we disconnect IsPersistent() bool // do we redial this peer when we disconnect
NodeInfo() NodeInfo // peer's info NodeInfo() NodeInfo // peer's info
Status() tmconn.ConnectionStatus Status() tmconn.ConnectionStatus
OriginalAddr() *NetAddress
Send(byte, []byte) bool Send(byte, []byte) bool
TrySend(byte, []byte) bool TrySend(byte, []byte) bool
@@ -44,8 +42,9 @@ type peerConn struct {
persistent bool persistent bool
config *config.P2PConfig config *config.P2PConfig
conn net.Conn // source connection conn net.Conn // source connection
// cached RemoteIP()
ip net.IP ip net.IP
originalAddr *NetAddress // nil for inbound connections
} }
// ID only exists for SecretConnection. // ID only exists for SecretConnection.
@@ -60,14 +59,6 @@ func (pc peerConn) RemoteIP() net.IP {
return pc.ip return pc.ip
} }
// In test cases a conn could not be present at all or be an in-memory
// implementation where we want to return a fake ip.
if pc.conn == nil || pc.conn.RemoteAddr().String() == "pipe" {
pc.ip = net.IP{172, 16, 0, byte(atomic.AddUint32(&testIPSuffix, 1))}
return pc.ip
}
host, _, err := net.SplitHostPort(pc.conn.RemoteAddr().String()) host, _, err := net.SplitHostPort(pc.conn.RemoteAddr().String())
if err != nil { if err != nil {
panic(err) panic(err)
@@ -120,7 +111,7 @@ func newPeer(
p := &peer{ p := &peer{
peerConn: pc, peerConn: pc,
nodeInfo: nodeInfo, nodeInfo: nodeInfo,
channels: nodeInfo.Channels, channels: nodeInfo.(DefaultNodeInfo).Channels, // TODO
Data: cmn.NewCMap(), Data: cmn.NewCMap(),
metricsTicker: time.NewTicker(metricsTickerDuration), metricsTicker: time.NewTicker(metricsTickerDuration),
metrics: NopMetrics(), metrics: NopMetrics(),
@@ -142,6 +133,15 @@ func newPeer(
return p return p
} }
// String representation.
func (p *peer) String() string {
if p.outbound {
return fmt.Sprintf("Peer{%v %v out}", p.mconn, p.ID())
}
return fmt.Sprintf("Peer{%v %v in}", p.mconn, p.ID())
}
//--------------------------------------------------- //---------------------------------------------------
// Implements cmn.Service // Implements cmn.Service
@@ -177,7 +177,7 @@ func (p *peer) OnStop() {
// ID returns the peer's ID - the hex encoded hash of its pubkey. // ID returns the peer's ID - the hex encoded hash of its pubkey.
func (p *peer) ID() ID { func (p *peer) ID() ID {
return p.nodeInfo.ID return p.nodeInfo.ID()
} }
// IsOutbound returns true if the connection is outbound, false otherwise. // IsOutbound returns true if the connection is outbound, false otherwise.
@@ -195,15 +195,6 @@ func (p *peer) NodeInfo() NodeInfo {
return p.nodeInfo return p.nodeInfo
} }
// OriginalAddr returns the original address, which was used to connect with
// the peer. Returns nil for inbound peers.
func (p *peer) OriginalAddr() *NetAddress {
if p.peerConn.outbound {
return p.peerConn.originalAddr
}
return nil
}
// Status returns the peer's ConnectionStatus. // Status returns the peer's ConnectionStatus.
func (p *peer) Status() tmconn.ConnectionStatus { func (p *peer) Status() tmconn.ConnectionStatus {
return p.mconn.Status() return p.mconn.Status()
@@ -272,53 +263,14 @@ func (p *peer) hasChannel(chID byte) bool {
} }
//--------------------------------------------------- //---------------------------------------------------
// methods used by the Switch // methods only used for testing
// TODO: can we remove these?
// CloseConn should be called by the Switch if the peer was created but never // CloseConn closes the underlying connection
// started.
func (pc *peerConn) CloseConn() { func (pc *peerConn) CloseConn() {
pc.conn.Close() // nolint: errcheck pc.conn.Close() // nolint: errcheck
} }
// HandshakeTimeout performs the Tendermint P2P handshake between a given node
// and the peer by exchanging their NodeInfo. It sets the received nodeInfo on
// the peer.
// NOTE: blocking
func (pc *peerConn) HandshakeTimeout(
ourNodeInfo NodeInfo,
timeout time.Duration,
) (peerNodeInfo NodeInfo, err error) {
// Set deadline for handshake so we don't block forever on conn.ReadFull
if err := pc.conn.SetDeadline(time.Now().Add(timeout)); err != nil {
return peerNodeInfo, cmn.ErrorWrap(err, "Error setting deadline")
}
var trs, _ = cmn.Parallel(
func(_ int) (val interface{}, err error, abort bool) {
_, err = cdc.MarshalBinaryWriter(pc.conn, ourNodeInfo)
return
},
func(_ int) (val interface{}, err error, abort bool) {
_, err = cdc.UnmarshalBinaryReader(
pc.conn,
&peerNodeInfo,
int64(MaxNodeInfoSize()),
)
return
},
)
if err := trs.FirstError(); err != nil {
return peerNodeInfo, cmn.ErrorWrap(err, "Error during handshake")
}
// Remove deadline
if err := pc.conn.SetDeadline(time.Time{}); err != nil {
return peerNodeInfo, cmn.ErrorWrap(err, "Error removing deadline")
}
return peerNodeInfo, nil
}
// Addr returns peer's remote network address. // Addr returns peer's remote network address.
func (p *peer) Addr() net.Addr { func (p *peer) Addr() net.Addr {
return p.peerConn.conn.RemoteAddr() return p.peerConn.conn.RemoteAddr()
@@ -332,14 +284,7 @@ func (p *peer) CanSend(chID byte) bool {
return p.mconn.CanSend(chID) return p.mconn.CanSend(chID)
} }
// String representation. //---------------------------------------------------
func (p *peer) String() string {
if p.outbound {
return fmt.Sprintf("Peer{%v %v out}", p.mconn, p.ID())
}
return fmt.Sprintf("Peer{%v %v in}", p.mconn, p.ID())
}
func PeerMetrics(metrics *Metrics) PeerOption { func PeerMetrics(metrics *Metrics) PeerOption {
return func(p *peer) { return func(p *peer) {

View File

@@ -1,7 +1,6 @@
package p2p package p2p
import ( import (
"fmt"
"net" "net"
"sync" "sync"
"testing" "testing"
@@ -12,24 +11,34 @@ import (
cmn "github.com/tendermint/tendermint/libs/common" cmn "github.com/tendermint/tendermint/libs/common"
) )
// Returns an empty kvstore peer // mockPeer for testing the PeerSet
func randPeer(ip net.IP) *peer { type mockPeer struct {
cmn.BaseService
ip net.IP
id ID
}
func (mp *mockPeer) TrySend(chID byte, msgBytes []byte) bool { return true }
func (mp *mockPeer) Send(chID byte, msgBytes []byte) bool { return true }
func (mp *mockPeer) NodeInfo() NodeInfo { return DefaultNodeInfo{} }
func (mp *mockPeer) Status() ConnectionStatus { return ConnectionStatus{} }
func (mp *mockPeer) ID() ID { return mp.id }
func (mp *mockPeer) IsOutbound() bool { return false }
func (mp *mockPeer) IsPersistent() bool { return true }
func (mp *mockPeer) Get(s string) interface{} { return s }
func (mp *mockPeer) Set(string, interface{}) {}
func (mp *mockPeer) RemoteIP() net.IP { return mp.ip }
// Returns a mock peer
func newMockPeer(ip net.IP) *mockPeer {
if ip == nil { if ip == nil {
ip = net.IP{127, 0, 0, 1} ip = net.IP{127, 0, 0, 1}
} }
nodeKey := NodeKey{PrivKey: ed25519.GenPrivKey()} nodeKey := NodeKey{PrivKey: ed25519.GenPrivKey()}
p := &peer{ return &mockPeer{
nodeInfo: NodeInfo{ ip: ip,
ID: nodeKey.ID(), id: nodeKey.ID(),
ListenAddr: fmt.Sprintf("%v.%v.%v.%v:26656", cmn.RandInt()%256, cmn.RandInt()%256, cmn.RandInt()%256, cmn.RandInt()%256),
},
metrics: NopMetrics(),
} }
p.ip = ip
return p
} }
func TestPeerSetAddRemoveOne(t *testing.T) { func TestPeerSetAddRemoveOne(t *testing.T) {
@@ -39,7 +48,7 @@ func TestPeerSetAddRemoveOne(t *testing.T) {
var peerList []Peer var peerList []Peer
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
p := randPeer(net.IP{127, 0, 0, byte(i)}) p := newMockPeer(net.IP{127, 0, 0, byte(i)})
if err := peerSet.Add(p); err != nil { if err := peerSet.Add(p); err != nil {
t.Error(err) t.Error(err)
} }
@@ -83,7 +92,7 @@ func TestPeerSetAddRemoveMany(t *testing.T) {
peers := []Peer{} peers := []Peer{}
N := 100 N := 100
for i := 0; i < N; i++ { for i := 0; i < N; i++ {
peer := randPeer(net.IP{127, 0, 0, byte(i)}) peer := newMockPeer(net.IP{127, 0, 0, byte(i)})
if err := peerSet.Add(peer); err != nil { if err := peerSet.Add(peer); err != nil {
t.Errorf("Failed to add new peer") t.Errorf("Failed to add new peer")
} }
@@ -107,7 +116,7 @@ func TestPeerSetAddRemoveMany(t *testing.T) {
func TestPeerSetAddDuplicate(t *testing.T) { func TestPeerSetAddDuplicate(t *testing.T) {
t.Parallel() t.Parallel()
peerSet := NewPeerSet() peerSet := NewPeerSet()
peer := randPeer(nil) peer := newMockPeer(nil)
n := 20 n := 20
errsChan := make(chan error) errsChan := make(chan error)
@@ -149,7 +158,7 @@ func TestPeerSetGet(t *testing.T) {
var ( var (
peerSet = NewPeerSet() peerSet = NewPeerSet()
peer = randPeer(nil) peer = newMockPeer(nil)
) )
assert.Nil(t, peerSet.Get(peer.ID()), "expecting a nil lookup, before .Add") assert.Nil(t, peerSet.Get(peer.ID()), "expecting a nil lookup, before .Add")

View File

@@ -19,8 +19,6 @@ import (
tmconn "github.com/tendermint/tendermint/p2p/conn" tmconn "github.com/tendermint/tendermint/p2p/conn"
) )
const testCh = 0x01
func TestPeerBasic(t *testing.T) { func TestPeerBasic(t *testing.T) {
assert, require := assert.New(t), require.New(t) assert, require := assert.New(t), require.New(t)
@@ -81,18 +79,14 @@ func createOutboundPeerAndPerformHandshake(
if err != nil { if err != nil {
return nil, err return nil, err
} }
nodeInfo, err := pc.HandshakeTimeout(NodeInfo{ timeout := 1 * time.Second
ID: addr.ID, ourNodeInfo := testNodeInfo(addr.ID, "host_peer")
Moniker: "host_peer", peerNodeInfo, err := handshake(pc.conn, timeout, ourNodeInfo)
Network: "testing",
Version: "123.123.123",
Channels: []byte{testCh},
}, 1*time.Second)
if err != nil { if err != nil {
return nil, err return nil, err
} }
p := newPeer(pc, mConfig, nodeInfo, reactorsByCh, chDescs, func(p Peer, r interface{}) {}) p := newPeer(pc, mConfig, peerNodeInfo, reactorsByCh, chDescs, func(p Peer, r interface{}) {})
p.SetLogger(log.TestingLogger().With("peer", addr)) p.SetLogger(log.TestingLogger().With("peer", addr))
return p, nil return p, nil
} }
@@ -120,7 +114,7 @@ func testOutboundPeerConn(
return peerConn{}, cmn.ErrorWrap(err, "Error creating peer") return peerConn{}, cmn.ErrorWrap(err, "Error creating peer")
} }
pc, err := testPeerConn(conn, config, true, persistent, ourNodePrivKey, addr) pc, err := testPeerConn(conn, config, true, persistent, ourNodePrivKey)
if err != nil { if err != nil {
if cerr := conn.Close(); cerr != nil { if cerr := conn.Close(); cerr != nil {
return peerConn{}, cmn.ErrorWrap(err, cerr.Error()) return peerConn{}, cmn.ErrorWrap(err, cerr.Error())
@@ -191,14 +185,7 @@ func (rp *remotePeer) accept(l net.Listener) {
golog.Fatalf("Failed to create a peer: %+v", err) golog.Fatalf("Failed to create a peer: %+v", err)
} }
_, err = handshake(pc.conn, time.Second, NodeInfo{ _, err = handshake(pc.conn, time.Second, rp.nodeInfo(l))
ID: rp.Addr().ID,
Moniker: "remote_peer",
Network: "testing",
Version: "123.123.123",
ListenAddr: l.Addr().String(),
Channels: rp.channels,
})
if err != nil { if err != nil {
golog.Fatalf("Failed to perform handshake: %+v", err) golog.Fatalf("Failed to perform handshake: %+v", err)
} }
@@ -217,3 +204,14 @@ func (rp *remotePeer) accept(l net.Listener) {
} }
} }
} }
func (rp *remotePeer) nodeInfo(l net.Listener) NodeInfo {
return DefaultNodeInfo{
ID_: rp.Addr().ID,
Moniker: "remote_peer",
Network: "testing",
Version: "123.123.123",
ListenAddr: l.Addr().String(),
Channels: rp.channels,
}
}

View File

@@ -320,7 +320,7 @@ func TestPEXReactorDoesNotAddPrivatePeersToAddrBook(t *testing.T) {
peer := p2p.CreateRandomPeer(false) peer := p2p.CreateRandomPeer(false)
pexR, book := createReactor(&PEXReactorConfig{}) pexR, book := createReactor(&PEXReactorConfig{})
book.AddPrivateIDs([]string{string(peer.NodeInfo().ID)}) book.AddPrivateIDs([]string{string(peer.NodeInfo().ID())})
defer teardownReactor(book) defer teardownReactor(book)
// we have to send a request to receive responses // we have to send a request to receive responses
@@ -391,8 +391,8 @@ func (mp mockPeer) ID() p2p.ID { return mp.addr.ID }
func (mp mockPeer) IsOutbound() bool { return mp.outbound } func (mp mockPeer) IsOutbound() bool { return mp.outbound }
func (mp mockPeer) IsPersistent() bool { return mp.persistent } func (mp mockPeer) IsPersistent() bool { return mp.persistent }
func (mp mockPeer) NodeInfo() p2p.NodeInfo { func (mp mockPeer) NodeInfo() p2p.NodeInfo {
return p2p.NodeInfo{ return p2p.DefaultNodeInfo{
ID: mp.addr.ID, ID_: mp.addr.ID,
ListenAddr: mp.addr.DialString(), ListenAddr: mp.addr.DialString(),
} }
} }
@@ -402,7 +402,6 @@ func (mockPeer) Send(byte, []byte) bool { return false }
func (mockPeer) TrySend(byte, []byte) bool { return false } func (mockPeer) TrySend(byte, []byte) bool { return false }
func (mockPeer) Set(string, interface{}) {} func (mockPeer) Set(string, interface{}) {}
func (mockPeer) Get(string) interface{} { return nil } func (mockPeer) Get(string) interface{} { return nil }
func (mockPeer) OriginalAddr() *p2p.NetAddress { return nil }
func assertPeersWithTimeout( func assertPeersWithTimeout(
t *testing.T, t *testing.T,

View File

@@ -280,12 +280,9 @@ func (sw *Switch) StopPeerForError(peer Peer, reason interface{}) {
sw.stopAndRemovePeer(peer, reason) sw.stopAndRemovePeer(peer, reason)
if peer.IsPersistent() { if peer.IsPersistent() {
addr := peer.OriginalAddr() // TODO: use the original address dialed, not the self reported one
if addr == nil { // See #2618.
// FIXME: persistent peers can't be inbound right now. addr := peer.NodeInfo().NetAddress()
// self-reported address for inbound persistent peers
addr = peer.NodeInfo().NetAddress()
}
go sw.reconnectToPeer(addr) go sw.reconnectToPeer(addr)
} }
} }
@@ -560,9 +557,13 @@ func (sw *Switch) addOutboundPeerWithConfig(
// to avoid dialing in the future. // to avoid dialing in the future.
sw.addrBook.RemoveAddress(addr) sw.addrBook.RemoveAddress(addr)
sw.addrBook.AddOurAddress(addr) sw.addrBook.AddOurAddress(addr)
return err
} }
} }
// retry persistent peers after
// any dial error besides IsSelf()
if persistent { if persistent {
go sw.reconnectToPeer(addr) go sw.reconnectToPeer(addr)
} }

View File

@@ -143,6 +143,7 @@ func assertMsgReceivedWithTimeout(t *testing.T, msgBytes []byte, channel byte, r
} }
return return
} }
case <-time.After(timeout): case <-time.After(timeout):
t.Fatalf("Expected to have received 1 message in channel #%v, got zero", channel) t.Fatalf("Expected to have received 1 message in channel #%v, got zero", channel)
} }

View File

@@ -14,6 +14,19 @@ import (
"github.com/tendermint/tendermint/p2p/conn" "github.com/tendermint/tendermint/p2p/conn"
) )
const testCh = 0x01
//------------------------------------------------
type mockNodeInfo struct {
addr *NetAddress
}
func (ni mockNodeInfo) ID() ID { return ni.addr.ID }
func (ni mockNodeInfo) NetAddress() *NetAddress { return ni.addr }
func (ni mockNodeInfo) ValidateBasic() error { return nil }
func (ni mockNodeInfo) CompatibleWith(other NodeInfo) error { return nil }
func AddPeerToSwitch(sw *Switch, peer Peer) { func AddPeerToSwitch(sw *Switch, peer Peer) {
sw.peers.Add(peer) sw.peers.Add(peer)
} }
@@ -24,10 +37,7 @@ func CreateRandomPeer(outbound bool) *peer {
peerConn: peerConn{ peerConn: peerConn{
outbound: outbound, outbound: outbound,
}, },
nodeInfo: NodeInfo{ nodeInfo: mockNodeInfo{netAddr},
ID: netAddr.ID,
ListenAddr: netAddr.DialString(),
},
mconn: &conn.MConnection{}, mconn: &conn.MConnection{},
metrics: NopMetrics(), metrics: NopMetrics(),
} }
@@ -159,36 +169,15 @@ func MakeSwitch(
initSwitch func(int, *Switch) *Switch, initSwitch func(int, *Switch) *Switch,
opts ...SwitchOption, opts ...SwitchOption,
) *Switch { ) *Switch {
var (
nodeKey = NodeKey{ nodeKey := NodeKey{
PrivKey: ed25519.GenPrivKey(), PrivKey: ed25519.GenPrivKey(),
} }
ni = NodeInfo{ nodeInfo := testNodeInfo(nodeKey.ID(), fmt.Sprintf("node%d", i))
ID: nodeKey.ID(),
Moniker: fmt.Sprintf("switch%d", i),
Network: network,
Version: version,
ListenAddr: fmt.Sprintf("127.0.0.1:%d", cmn.RandIntn(64512)+1023),
Other: NodeInfoOther{
AminoVersion: "1.0",
P2PVersion: "1.0",
ConsensusVersion: "1.0",
RPCVersion: "1.0",
TxIndex: "off",
RPCAddress: fmt.Sprintf("127.0.0.1:%d", cmn.RandIntn(64512)+1023),
},
}
)
addr, err := NewNetAddressStringWithOptionalID( t := NewMultiplexTransport(nodeInfo, nodeKey)
IDAddressString(nodeKey.ID(), ni.ListenAddr),
)
if err != nil {
panic(err)
}
t := NewMultiplexTransport(ni, nodeKey)
addr := nodeInfo.NetAddress()
if err := t.Listen(*addr); err != nil { if err := t.Listen(*addr); err != nil {
panic(err) panic(err)
} }
@@ -198,14 +187,16 @@ func MakeSwitch(
sw.SetLogger(log.TestingLogger()) sw.SetLogger(log.TestingLogger())
sw.SetNodeKey(&nodeKey) sw.SetNodeKey(&nodeKey)
ni := nodeInfo.(DefaultNodeInfo)
for ch := range sw.reactorsByCh { for ch := range sw.reactorsByCh {
ni.Channels = append(ni.Channels, ch) ni.Channels = append(ni.Channels, ch)
} }
nodeInfo = ni
// TODO: We need to setup reactors ahead of time so the NodeInfo is properly // TODO: We need to setup reactors ahead of time so the NodeInfo is properly
// populated and we don't have to do those awkward overrides and setters. // populated and we don't have to do those awkward overrides and setters.
t.nodeInfo = ni t.nodeInfo = nodeInfo
sw.SetNodeInfo(ni) sw.SetNodeInfo(nodeInfo)
return sw return sw
} }
@@ -215,7 +206,7 @@ func testInboundPeerConn(
config *config.P2PConfig, config *config.P2PConfig,
ourNodePrivKey crypto.PrivKey, ourNodePrivKey crypto.PrivKey,
) (peerConn, error) { ) (peerConn, error) {
return testPeerConn(conn, config, false, false, ourNodePrivKey, nil) return testPeerConn(conn, config, false, false, ourNodePrivKey)
} }
func testPeerConn( func testPeerConn(
@@ -223,7 +214,6 @@ func testPeerConn(
cfg *config.P2PConfig, cfg *config.P2PConfig,
outbound, persistent bool, outbound, persistent bool,
ourNodePrivKey crypto.PrivKey, ourNodePrivKey crypto.PrivKey,
originalAddr *NetAddress,
) (pc peerConn, err error) { ) (pc peerConn, err error) {
conn := rawConn conn := rawConn
@@ -245,6 +235,23 @@ func testPeerConn(
outbound: outbound, outbound: outbound,
persistent: persistent, persistent: persistent,
conn: conn, conn: conn,
originalAddr: originalAddr,
}, nil }, nil
} }
//----------------------------------------------------------------
// rand node info
func testNodeInfo(id ID, name string) NodeInfo {
return testNodeInfoWithNetwork(id, name, "testing")
}
func testNodeInfoWithNetwork(id ID, name, network string) NodeInfo {
return DefaultNodeInfo{
ID_: id,
ListenAddr: fmt.Sprintf("127.0.0.1:%d", cmn.RandIntn(64512)+1023),
Moniker: name,
Network: network,
Version: "123.123.123",
Channels: []byte{testCh},
}
}

View File

@@ -335,7 +335,7 @@ func (mt *MultiplexTransport) upgrade(
secretConn, err = upgradeSecretConn(c, mt.handshakeTimeout, mt.nodeKey.PrivKey) secretConn, err = upgradeSecretConn(c, mt.handshakeTimeout, mt.nodeKey.PrivKey)
if err != nil { if err != nil {
return nil, NodeInfo{}, ErrRejected{ return nil, nil, ErrRejected{
conn: c, conn: c,
err: fmt.Errorf("secrect conn failed: %v", err), err: fmt.Errorf("secrect conn failed: %v", err),
isAuthFailure: true, isAuthFailure: true,
@@ -344,15 +344,15 @@ func (mt *MultiplexTransport) upgrade(
nodeInfo, err = handshake(secretConn, mt.handshakeTimeout, mt.nodeInfo) nodeInfo, err = handshake(secretConn, mt.handshakeTimeout, mt.nodeInfo)
if err != nil { if err != nil {
return nil, NodeInfo{}, ErrRejected{ return nil, nil, ErrRejected{
conn: c, conn: c,
err: fmt.Errorf("handshake failed: %v", err), err: fmt.Errorf("handshake failed: %v", err),
isAuthFailure: true, isAuthFailure: true,
} }
} }
if err := nodeInfo.Validate(); err != nil { if err := nodeInfo.ValidateBasic(); err != nil {
return nil, NodeInfo{}, ErrRejected{ return nil, nil, ErrRejected{
conn: c, conn: c,
err: err, err: err,
isNodeInfoInvalid: true, isNodeInfoInvalid: true,
@@ -360,34 +360,34 @@ func (mt *MultiplexTransport) upgrade(
} }
// Ensure connection key matches self reported key. // Ensure connection key matches self reported key.
if connID := PubKeyToID(secretConn.RemotePubKey()); connID != nodeInfo.ID { if connID := PubKeyToID(secretConn.RemotePubKey()); connID != nodeInfo.ID() {
return nil, NodeInfo{}, ErrRejected{ return nil, nil, ErrRejected{
conn: c, conn: c,
id: connID, id: connID,
err: fmt.Errorf( err: fmt.Errorf(
"conn.ID (%v) NodeInfo.ID (%v) missmatch", "conn.ID (%v) NodeInfo.ID (%v) missmatch",
connID, connID,
nodeInfo.ID, nodeInfo.ID(),
), ),
isAuthFailure: true, isAuthFailure: true,
} }
} }
// Reject self. // Reject self.
if mt.nodeInfo.ID == nodeInfo.ID { if mt.nodeInfo.ID() == nodeInfo.ID() {
return nil, NodeInfo{}, ErrRejected{ return nil, nil, ErrRejected{
addr: *NewNetAddress(nodeInfo.ID, c.RemoteAddr()), addr: *NewNetAddress(nodeInfo.ID(), c.RemoteAddr()),
conn: c, conn: c,
id: nodeInfo.ID, id: nodeInfo.ID(),
isSelf: true, isSelf: true,
} }
} }
if err := mt.nodeInfo.CompatibleWith(nodeInfo); err != nil { if err := mt.nodeInfo.CompatibleWith(nodeInfo); err != nil {
return nil, NodeInfo{}, ErrRejected{ return nil, nil, ErrRejected{
conn: c, conn: c,
err: err, err: err,
id: nodeInfo.ID, id: nodeInfo.ID(),
isIncompatible: true, isIncompatible: true,
} }
} }
@@ -430,17 +430,18 @@ func handshake(
nodeInfo NodeInfo, nodeInfo NodeInfo,
) (NodeInfo, error) { ) (NodeInfo, error) {
if err := c.SetDeadline(time.Now().Add(timeout)); err != nil { if err := c.SetDeadline(time.Now().Add(timeout)); err != nil {
return NodeInfo{}, err return nil, err
} }
var ( var (
errc = make(chan error, 2) errc = make(chan error, 2)
peerNodeInfo NodeInfo peerNodeInfo DefaultNodeInfo
ourNodeInfo = nodeInfo.(DefaultNodeInfo)
) )
go func(errc chan<- error, c net.Conn) { go func(errc chan<- error, c net.Conn) {
_, err := cdc.MarshalBinaryWriter(c, nodeInfo) _, err := cdc.MarshalBinaryWriter(c, ourNodeInfo)
errc <- err errc <- err
}(errc, c) }(errc, c)
go func(errc chan<- error, c net.Conn) { go func(errc chan<- error, c net.Conn) {
@@ -455,7 +456,7 @@ func handshake(
for i := 0; i < cap(errc); i++ { for i := 0; i < cap(errc); i++ {
err := <-errc err := <-errc
if err != nil { if err != nil {
return NodeInfo{}, err return nil, err
} }
} }

View File

@@ -11,9 +11,15 @@ import (
"github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/ed25519"
) )
var defaultNodeName = "host_peer"
func emptyNodeInfo() NodeInfo {
return DefaultNodeInfo{}
}
func TestTransportMultiplexConnFilter(t *testing.T) { func TestTransportMultiplexConnFilter(t *testing.T) {
mt := NewMultiplexTransport( mt := NewMultiplexTransport(
NodeInfo{}, emptyNodeInfo(),
NodeKey{ NodeKey{
PrivKey: ed25519.GenPrivKey(), PrivKey: ed25519.GenPrivKey(),
}, },
@@ -70,7 +76,7 @@ func TestTransportMultiplexConnFilter(t *testing.T) {
func TestTransportMultiplexConnFilterTimeout(t *testing.T) { func TestTransportMultiplexConnFilterTimeout(t *testing.T) {
mt := NewMultiplexTransport( mt := NewMultiplexTransport(
NodeInfo{}, emptyNodeInfo(),
NodeKey{ NodeKey{
PrivKey: ed25519.GenPrivKey(), PrivKey: ed25519.GenPrivKey(),
}, },
@@ -120,6 +126,7 @@ func TestTransportMultiplexConnFilterTimeout(t *testing.T) {
t.Errorf("expected ErrFilterTimeout") t.Errorf("expected ErrFilterTimeout")
} }
} }
func TestTransportMultiplexAcceptMultiple(t *testing.T) { func TestTransportMultiplexAcceptMultiple(t *testing.T) {
mt := testSetupMultiplexTransport(t) mt := testSetupMultiplexTransport(t)
@@ -134,12 +141,7 @@ func TestTransportMultiplexAcceptMultiple(t *testing.T) {
var ( var (
pv = ed25519.GenPrivKey() pv = ed25519.GenPrivKey()
dialer = NewMultiplexTransport( dialer = NewMultiplexTransport(
NodeInfo{ testNodeInfo(PubKeyToID(pv.PubKey()), defaultNodeName),
ID: PubKeyToID(pv.PubKey()),
ListenAddr: "127.0.0.1:0",
Moniker: "dialer",
Version: "1.0.0",
},
NodeKey{ NodeKey{
PrivKey: pv, PrivKey: pv,
}, },
@@ -207,12 +209,7 @@ func TestTransportMultiplexAcceptNonBlocking(t *testing.T) {
var ( var (
fastNodePV = ed25519.GenPrivKey() fastNodePV = ed25519.GenPrivKey()
fastNodeInfo = NodeInfo{ fastNodeInfo = testNodeInfo(PubKeyToID(fastNodePV.PubKey()), "fastnode")
ID: PubKeyToID(fastNodePV.PubKey()),
ListenAddr: "127.0.0.1:0",
Moniker: "fastNode",
Version: "1.0.0",
}
errc = make(chan error) errc = make(chan error)
fastc = make(chan struct{}) fastc = make(chan struct{})
slowc = make(chan struct{}) slowc = make(chan struct{})
@@ -248,11 +245,11 @@ func TestTransportMultiplexAcceptNonBlocking(t *testing.T) {
return return
} }
_, err = handshake(sc, 20*time.Millisecond, NodeInfo{ _, err = handshake(sc, 20*time.Millisecond,
ID: PubKeyToID(ed25519.GenPrivKey().PubKey()), testNodeInfo(
ListenAddr: "127.0.0.1:0", PubKeyToID(ed25519.GenPrivKey().PubKey()),
Moniker: "slow_peer", "slow_peer",
}) ))
if err != nil { if err != nil {
errc <- err errc <- err
return return
@@ -311,12 +308,7 @@ func TestTransportMultiplexValidateNodeInfo(t *testing.T) {
var ( var (
pv = ed25519.GenPrivKey() pv = ed25519.GenPrivKey()
dialer = NewMultiplexTransport( dialer = NewMultiplexTransport(
NodeInfo{ testNodeInfo(PubKeyToID(pv.PubKey()), ""), // Should not be empty
ID: PubKeyToID(pv.PubKey()),
ListenAddr: "127.0.0.1:0",
Moniker: "", // Should not be empty.
Version: "1.0.0",
},
NodeKey{ NodeKey{
PrivKey: pv, PrivKey: pv,
}, },
@@ -359,12 +351,9 @@ func TestTransportMultiplexRejectMissmatchID(t *testing.T) {
go func() { go func() {
dialer := NewMultiplexTransport( dialer := NewMultiplexTransport(
NodeInfo{ testNodeInfo(
ID: PubKeyToID(ed25519.GenPrivKey().PubKey()), PubKeyToID(ed25519.GenPrivKey().PubKey()), "dialer",
ListenAddr: "127.0.0.1:0", ),
Moniker: "dialer",
Version: "1.0.0",
},
NodeKey{ NodeKey{
PrivKey: ed25519.GenPrivKey(), PrivKey: ed25519.GenPrivKey(),
}, },
@@ -408,12 +397,7 @@ func TestTransportMultiplexRejectIncompatible(t *testing.T) {
var ( var (
pv = ed25519.GenPrivKey() pv = ed25519.GenPrivKey()
dialer = NewMultiplexTransport( dialer = NewMultiplexTransport(
NodeInfo{ testNodeInfoWithNetwork(PubKeyToID(pv.PubKey()), "dialer", "incompatible-network"),
ID: PubKeyToID(pv.PubKey()),
ListenAddr: "127.0.0.1:0",
Moniker: "dialer",
Version: "2.0.0",
},
NodeKey{ NodeKey{
PrivKey: pv, PrivKey: pv,
}, },
@@ -521,9 +505,7 @@ func TestTransportHandshake(t *testing.T) {
var ( var (
peerPV = ed25519.GenPrivKey() peerPV = ed25519.GenPrivKey()
peerNodeInfo = NodeInfo{ peerNodeInfo = testNodeInfo(PubKeyToID(peerPV.PubKey()), defaultNodeName)
ID: PubKeyToID(peerPV.PubKey()),
}
) )
go func() { go func() {
@@ -534,13 +516,13 @@ func TestTransportHandshake(t *testing.T) {
} }
go func(c net.Conn) { go func(c net.Conn) {
_, err := cdc.MarshalBinaryWriter(c, peerNodeInfo) _, err := cdc.MarshalBinaryWriter(c, peerNodeInfo.(DefaultNodeInfo))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
}(c) }(c)
go func(c net.Conn) { go func(c net.Conn) {
ni := NodeInfo{} var ni DefaultNodeInfo
_, err := cdc.UnmarshalBinaryReader( _, err := cdc.UnmarshalBinaryReader(
c, c,
@@ -558,7 +540,7 @@ func TestTransportHandshake(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
ni, err := handshake(c, 20*time.Millisecond, NodeInfo{}) ni, err := handshake(c, 20*time.Millisecond, emptyNodeInfo())
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@@ -572,12 +554,9 @@ func testSetupMultiplexTransport(t *testing.T) *MultiplexTransport {
var ( var (
pv = ed25519.GenPrivKey() pv = ed25519.GenPrivKey()
mt = NewMultiplexTransport( mt = NewMultiplexTransport(
NodeInfo{ testNodeInfo(
ID: PubKeyToID(pv.PubKey()), PubKeyToID(pv.PubKey()), "transport",
ListenAddr: "127.0.0.1:0", ),
Moniker: "transport",
Version: "1.0.0",
},
NodeKey{ NodeKey{
PrivKey: pv, PrivKey: pv,
}, },

View File

@@ -25,9 +25,9 @@ const (
func voteToStep(vote *types.Vote) int8 { func voteToStep(vote *types.Vote) int8 {
switch vote.Type { switch vote.Type {
case types.VoteTypePrevote: case types.PrevoteType:
return stepPrevote return stepPrevote
case types.VoteTypePrecommit: case types.PrecommitType:
return stepPrecommit return stepPrecommit
default: default:
cmn.PanicSanity("Unknown vote type") cmn.PanicSanity("Unknown vote type")

View File

@@ -101,7 +101,7 @@ func TestSignVote(t *testing.T) {
block1 := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{}} block1 := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{}}
block2 := types.BlockID{[]byte{3, 2, 1}, types.PartSetHeader{}} block2 := types.BlockID{[]byte{3, 2, 1}, types.PartSetHeader{}}
height, round := int64(10), 1 height, round := int64(10), 1
voteType := types.VoteTypePrevote voteType := byte(types.PrevoteType)
// sign a vote for first time // sign a vote for first time
vote := newVote(privVal.Address, 0, height, round, voteType, block1) vote := newVote(privVal.Address, 0, height, round, voteType, block1)
@@ -206,7 +206,7 @@ func TestDifferByTimestamp(t *testing.T) {
// test vote // test vote
{ {
voteType := types.VoteTypePrevote voteType := byte(types.PrevoteType)
blockID := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{}} blockID := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{}}
vote := newVote(privVal.Address, 0, height, round, voteType, blockID) vote := newVote(privVal.Address, 0, height, round, voteType, blockID)
err := privVal.SignVote("mychainid", vote) err := privVal.SignVote("mychainid", vote)
@@ -235,7 +235,7 @@ func newVote(addr types.Address, idx int, height int64, round int, typ byte, blo
ValidatorIndex: idx, ValidatorIndex: idx,
Height: height, Height: height,
Round: round, Round: round,
Type: typ, Type: types.SignedMsgType(typ),
Timestamp: tmtime.Now(), Timestamp: tmtime.Now(),
BlockID: blockID, BlockID: blockID,
} }

View File

@@ -79,7 +79,7 @@ func TestSocketPVVote(t *testing.T) {
sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV()) sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV())
ts = time.Now() ts = time.Now()
vType = types.VoteTypePrecommit vType = types.PrecommitType
want = &types.Vote{Timestamp: ts, Type: vType} want = &types.Vote{Timestamp: ts, Type: vType}
have = &types.Vote{Timestamp: ts, Type: vType} have = &types.Vote{Timestamp: ts, Type: vType}
) )
@@ -237,7 +237,7 @@ func TestRemoteSignVoteErrors(t *testing.T) {
sc, rs = testSetupSocketPair(t, chainID, types.NewErroringMockPV()) sc, rs = testSetupSocketPair(t, chainID, types.NewErroringMockPV())
ts = time.Now() ts = time.Now()
vType = types.VoteTypePrecommit vType = types.PrecommitType
vote = &types.Vote{Timestamp: ts, Type: vType} vote = &types.Vote{Timestamp: ts, Type: vType}
) )
defer sc.Stop() defer sc.Stop()

View File

@@ -2,7 +2,6 @@ package core
import ( import (
cm "github.com/tendermint/tendermint/consensus" cm "github.com/tendermint/tendermint/consensus"
"github.com/tendermint/tendermint/p2p"
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
sm "github.com/tendermint/tendermint/state" sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
@@ -201,7 +200,7 @@ func DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
} }
peerStates[i] = ctypes.PeerStateInfo{ peerStates[i] = ctypes.PeerStateInfo{
// Peer basic info. // Peer basic info.
NodeAddress: p2p.IDAddressString(peer.ID(), peer.NodeInfo().ListenAddr), NodeAddress: peer.NodeInfo().NetAddress().String(),
// Peer consensus state. // Peer consensus state.
PeerState: peerStateJSON, PeerState: peerStateJSON,
} }

View File

@@ -1,8 +1,11 @@
package core package core
import ( import (
"fmt"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/tendermint/tendermint/p2p"
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
) )
@@ -37,8 +40,12 @@ import (
func NetInfo() (*ctypes.ResultNetInfo, error) { func NetInfo() (*ctypes.ResultNetInfo, error) {
peers := []ctypes.Peer{} peers := []ctypes.Peer{}
for _, peer := range p2pPeers.Peers().List() { for _, peer := range p2pPeers.Peers().List() {
nodeInfo, ok := peer.NodeInfo().(p2p.DefaultNodeInfo)
if !ok {
return nil, fmt.Errorf("peer.NodeInfo() is not DefaultNodeInfo")
}
peers = append(peers, ctypes.Peer{ peers = append(peers, ctypes.Peer{
NodeInfo: peer.NodeInfo(), NodeInfo: nodeInfo,
IsOutbound: peer.IsOutbound(), IsOutbound: peer.IsOutbound(),
ConnectionStatus: peer.Status(), ConnectionStatus: peer.Status(),
}) })

View File

@@ -5,6 +5,7 @@ import (
"time" "time"
cmn "github.com/tendermint/tendermint/libs/common" cmn "github.com/tendermint/tendermint/libs/common"
"github.com/tendermint/tendermint/p2p"
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
sm "github.com/tendermint/tendermint/state" sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types" "github.com/tendermint/tendermint/types"
@@ -91,7 +92,7 @@ func Status() (*ctypes.ResultStatus, error) {
} }
result := &ctypes.ResultStatus{ result := &ctypes.ResultStatus{
NodeInfo: p2pTransport.NodeInfo(), NodeInfo: p2pTransport.NodeInfo().(p2p.DefaultNodeInfo),
SyncInfo: ctypes.SyncInfo{ SyncInfo: ctypes.SyncInfo{
LatestBlockHash: latestBlockHash, LatestBlockHash: latestBlockHash,
LatestAppHash: latestAppHash, LatestAppHash: latestAppHash,

View File

@@ -74,7 +74,7 @@ type ValidatorInfo struct {
// Node Status // Node Status
type ResultStatus struct { type ResultStatus struct {
NodeInfo p2p.NodeInfo `json:"node_info"` NodeInfo p2p.DefaultNodeInfo `json:"node_info"`
SyncInfo SyncInfo `json:"sync_info"` SyncInfo SyncInfo `json:"sync_info"`
ValidatorInfo ValidatorInfo `json:"validator_info"` ValidatorInfo ValidatorInfo `json:"validator_info"`
} }
@@ -107,7 +107,7 @@ type ResultDialPeers struct {
// A peer // A peer
type Peer struct { type Peer struct {
p2p.NodeInfo `json:"node_info"` NodeInfo p2p.DefaultNodeInfo `json:"node_info"`
IsOutbound bool `json:"is_outbound"` IsOutbound bool `json:"is_outbound"`
ConnectionStatus p2p.ConnectionStatus `json:"connection_status"` ConnectionStatus p2p.ConnectionStatus `json:"connection_status"`
} }

View File

@@ -15,17 +15,17 @@ func TestStatusIndexer(t *testing.T) {
status = &ResultStatus{} status = &ResultStatus{}
assert.False(t, status.TxIndexEnabled()) assert.False(t, status.TxIndexEnabled())
status.NodeInfo = p2p.NodeInfo{} status.NodeInfo = p2p.DefaultNodeInfo{}
assert.False(t, status.TxIndexEnabled()) assert.False(t, status.TxIndexEnabled())
cases := []struct { cases := []struct {
expected bool expected bool
other p2p.NodeInfoOther other p2p.DefaultNodeInfoOther
}{ }{
{false, p2p.NodeInfoOther{}}, {false, p2p.DefaultNodeInfoOther{}},
{false, p2p.NodeInfoOther{TxIndex: "aa"}}, {false, p2p.DefaultNodeInfoOther{TxIndex: "aa"}},
{false, p2p.NodeInfoOther{TxIndex: "off"}}, {false, p2p.DefaultNodeInfoOther{TxIndex: "off"}},
{true, p2p.NodeInfoOther{TxIndex: "on"}}, {true, p2p.DefaultNodeInfoOther{TxIndex: "on"}},
} }
for _, tc := range cases { for _, tc := range cases {

View File

@@ -95,7 +95,7 @@ func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, b
startTime := time.Now().UnixNano() startTime := time.Now().UnixNano()
abciResponses, err := execBlockOnProxyApp(blockExec.logger, blockExec.proxyApp, block, state.LastValidators, blockExec.db) abciResponses, err := execBlockOnProxyApp(blockExec.logger, blockExec.proxyApp, block, state.LastValidators, blockExec.db)
endTime := time.Now().UnixNano() endTime := time.Now().UnixNano()
blockExec.metrics.BlockProcessingTime.Observe(float64(endTime - startTime) / 1000000) blockExec.metrics.BlockProcessingTime.Observe(float64(endTime-startTime) / 1000000)
if err != nil { if err != nil {
return state, ErrProxyAppConn(err) return state, ErrProxyAppConn(err)
} }

View File

@@ -64,7 +64,7 @@ func TestBeginBlockValidators(t *testing.T) {
prevBlockID := types.BlockID{prevHash, prevParts} prevBlockID := types.BlockID{prevHash, prevParts}
now := tmtime.Now() now := tmtime.Now()
vote0 := &types.Vote{ValidatorIndex: 0, Timestamp: now, Type: types.VoteTypePrecommit} vote0 := &types.Vote{ValidatorIndex: 0, Timestamp: now, Type: types.PrecommitType}
vote1 := &types.Vote{ValidatorIndex: 1, Timestamp: now} vote1 := &types.Vote{ValidatorIndex: 1, Timestamp: now}
testCases := []struct { testCases := []struct {
@@ -135,7 +135,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) {
types.TM2PB.Evidence(ev2, valSet, now)}}, types.TM2PB.Evidence(ev2, valSet, now)}},
} }
vote0 := &types.Vote{ValidatorIndex: 0, Timestamp: now, Type: types.VoteTypePrecommit} vote0 := &types.Vote{ValidatorIndex: 0, Timestamp: now, Type: types.PrecommitType}
vote1 := &types.Vote{ValidatorIndex: 1, Timestamp: now} vote1 := &types.Vote{ValidatorIndex: 1, Timestamp: now}
votes := []*types.Vote{vote0, vote1} votes := []*types.Vote{vote0, vote1}
lastCommit := &types.Commit{BlockID: prevBlockID, Precommits: votes} lastCommit := &types.Commit{BlockID: prevBlockID, Precommits: votes}

View File

@@ -2,9 +2,9 @@ package state
import ( import (
"github.com/go-kit/kit/metrics" "github.com/go-kit/kit/metrics"
"github.com/go-kit/kit/metrics/discard"
"github.com/go-kit/kit/metrics/prometheus" "github.com/go-kit/kit/metrics/prometheus"
stdprometheus "github.com/prometheus/client_golang/prometheus" stdprometheus "github.com/prometheus/client_golang/prometheus"
"github.com/go-kit/kit/metrics/discard"
) )
const MetricsSubsystem = "state" const MetricsSubsystem = "state"

View File

@@ -388,7 +388,7 @@ func (commit *Commit) FirstPrecommit() *Vote {
} }
} }
return &Vote{ return &Vote{
Type: VoteTypePrecommit, Type: PrecommitType,
} }
} }
@@ -410,7 +410,7 @@ func (commit *Commit) Round() int {
// Type returns the vote type of the commit, which is always VoteTypePrecommit // Type returns the vote type of the commit, which is always VoteTypePrecommit
func (commit *Commit) Type() byte { func (commit *Commit) Type() byte {
return VoteTypePrecommit return byte(PrecommitType)
} }
// Size returns the number of votes in the commit // Size returns the number of votes in the commit
@@ -462,7 +462,7 @@ func (commit *Commit) ValidateBasic() error {
continue continue
} }
// Ensure that all votes are precommits. // Ensure that all votes are precommits.
if precommit.Type != VoteTypePrecommit { if precommit.Type != PrecommitType {
return fmt.Errorf("Invalid commit vote. Expected precommit, got %v", return fmt.Errorf("Invalid commit vote. Expected precommit, got %v",
precommit.Type) precommit.Type)
} }

View File

@@ -26,7 +26,7 @@ func TestBlockAddEvidence(t *testing.T) {
lastID := makeBlockIDRandom() lastID := makeBlockIDRandom()
h := int64(3) h := int64(3)
voteSet, valSet, vals := randVoteSet(h-1, 1, VoteTypePrecommit, 10, 1) voteSet, valSet, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1)
commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals) commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals)
require.NoError(t, err) require.NoError(t, err)
@@ -46,7 +46,7 @@ func TestBlockValidateBasic(t *testing.T) {
lastID := makeBlockIDRandom() lastID := makeBlockIDRandom()
h := int64(3) h := int64(3)
voteSet, valSet, vals := randVoteSet(h-1, 1, VoteTypePrecommit, 10, 1) voteSet, valSet, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1)
commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals) commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals)
require.NoError(t, err) require.NoError(t, err)
@@ -106,7 +106,7 @@ func TestBlockMakePartSetWithEvidence(t *testing.T) {
lastID := makeBlockIDRandom() lastID := makeBlockIDRandom()
h := int64(3) h := int64(3)
voteSet, valSet, vals := randVoteSet(h-1, 1, VoteTypePrecommit, 10, 1) voteSet, valSet, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1)
commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals) commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals)
require.NoError(t, err) require.NoError(t, err)
@@ -123,7 +123,7 @@ func TestBlockHashesTo(t *testing.T) {
lastID := makeBlockIDRandom() lastID := makeBlockIDRandom()
h := int64(3) h := int64(3)
voteSet, valSet, vals := randVoteSet(h-1, 1, VoteTypePrecommit, 10, 1) voteSet, valSet, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1)
commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals) commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals)
require.NoError(t, err) require.NoError(t, err)
@@ -190,14 +190,14 @@ func TestNilDataHashDoesntCrash(t *testing.T) {
func TestCommit(t *testing.T) { func TestCommit(t *testing.T) {
lastID := makeBlockIDRandom() lastID := makeBlockIDRandom()
h := int64(3) h := int64(3)
voteSet, _, vals := randVoteSet(h-1, 1, VoteTypePrecommit, 10, 1) voteSet, _, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1)
commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals) commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals)
require.NoError(t, err) require.NoError(t, err)
assert.NotNil(t, commit.FirstPrecommit()) assert.NotNil(t, commit.FirstPrecommit())
assert.Equal(t, h-1, commit.Height()) assert.Equal(t, h-1, commit.Height())
assert.Equal(t, 1, commit.Round()) assert.Equal(t, 1, commit.Round())
assert.Equal(t, VoteTypePrecommit, commit.Type()) assert.Equal(t, PrecommitType, SignedMsgType(commit.Type()))
if commit.Size() <= 0 { if commit.Size() <= 0 {
t.Fatalf("commit %v has a zero or negative size: %d", commit, commit.Size()) t.Fatalf("commit %v has a zero or negative size: %d", commit, commit.Size())
} }
@@ -218,7 +218,7 @@ func TestCommitValidateBasic(t *testing.T) {
{"Random Commit", func(com *Commit) {}, false}, {"Random Commit", func(com *Commit) {}, false},
{"Nil precommit", func(com *Commit) { com.Precommits[0] = nil }, false}, {"Nil precommit", func(com *Commit) { com.Precommits[0] = nil }, false},
{"Incorrect signature", func(com *Commit) { com.Precommits[0].Signature = []byte{0} }, false}, {"Incorrect signature", func(com *Commit) { com.Precommits[0].Signature = []byte{0} }, false},
{"Incorrect type", func(com *Commit) { com.Precommits[0].Type = VoteTypePrevote }, true}, {"Incorrect type", func(com *Commit) { com.Precommits[0].Type = PrevoteType }, true},
{"Incorrect height", func(com *Commit) { com.Precommits[0].Height = int64(100) }, true}, {"Incorrect height", func(com *Commit) { com.Precommits[0].Height = int64(100) }, true},
{"Incorrect round", func(com *Commit) { com.Precommits[0].Round = 100 }, true}, {"Incorrect round", func(com *Commit) { com.Precommits[0].Round = 100 }, true},
} }
@@ -268,7 +268,7 @@ func TestMaxHeaderBytes(t *testing.T) {
func randCommit() *Commit { func randCommit() *Commit {
lastID := makeBlockIDRandom() lastID := makeBlockIDRandom()
h := int64(3) h := int64(3)
voteSet, _, vals := randVoteSet(h-1, 1, VoteTypePrecommit, 10, 1) voteSet, _, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1)
commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals) commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals)
if err != nil { if err != nil {
panic(err) panic(err)

View File

@@ -13,44 +13,46 @@ import (
const TimeFormat = time.RFC3339Nano const TimeFormat = time.RFC3339Nano
type CanonicalBlockID struct { type CanonicalBlockID struct {
Hash cmn.HexBytes `json:"hash,omitempty"` Hash cmn.HexBytes
PartsHeader CanonicalPartSetHeader `json:"parts,omitempty"` PartsHeader CanonicalPartSetHeader
} }
type CanonicalPartSetHeader struct { type CanonicalPartSetHeader struct {
Hash cmn.HexBytes `json:"hash,omitempty"` Hash cmn.HexBytes
Total int `json:"total,omitempty"` Total int
} }
type CanonicalProposal struct { type CanonicalProposal struct {
ChainID string `json:"@chain_id"` Version uint64 `binary:"fixed64"`
Type string `json:"@type"` Height int64 `binary:"fixed64"`
BlockPartsHeader CanonicalPartSetHeader `json:"block_parts_header"` Round int64 `binary:"fixed64"`
Height int64 `json:"height"` Type SignedMsgType // type alias for byte
POLBlockID CanonicalBlockID `json:"pol_block_id"` POLRound int64 `binary:"fixed64"`
POLRound int `json:"pol_round"` Timestamp time.Time
Round int `json:"round"` BlockPartsHeader CanonicalPartSetHeader
Timestamp time.Time `json:"timestamp"` POLBlockID CanonicalBlockID
ChainID string
} }
type CanonicalVote struct { type CanonicalVote struct {
ChainID string `json:"@chain_id"` Version uint64 `binary:"fixed64"`
Type string `json:"@type"` Height int64 `binary:"fixed64"`
BlockID CanonicalBlockID `json:"block_id"` Round int64 `binary:"fixed64"`
Height int64 `json:"height"` Type SignedMsgType // type alias for byte
Round int `json:"round"` Timestamp time.Time
Timestamp time.Time `json:"timestamp"` BlockID CanonicalBlockID
VoteType byte `json:"type"` ChainID string
} }
type CanonicalHeartbeat struct { type CanonicalHeartbeat struct {
ChainID string `json:"@chain_id"` Version uint64 `binary:"fixed64"`
Type string `json:"@type"` Height int64 `binary:"fixed64"`
Height int64 `json:"height"` Round int `binary:"fixed64"`
Round int `json:"round"` Type byte
Sequence int `json:"sequence"` Sequence int `binary:"fixed64"`
ValidatorAddress Address `json:"validator_address"` ValidatorAddress Address
ValidatorIndex int `json:"validator_index"` ValidatorIndex int
ChainID string
} }
//----------------------------------- //-----------------------------------
@@ -72,38 +74,40 @@ func CanonicalizePartSetHeader(psh PartSetHeader) CanonicalPartSetHeader {
func CanonicalizeProposal(chainID string, proposal *Proposal) CanonicalProposal { func CanonicalizeProposal(chainID string, proposal *Proposal) CanonicalProposal {
return CanonicalProposal{ return CanonicalProposal{
ChainID: chainID, Version: 0, // TODO
Type: "proposal",
BlockPartsHeader: CanonicalizePartSetHeader(proposal.BlockPartsHeader),
Height: proposal.Height, Height: proposal.Height,
Round: int64(proposal.Round), // cast int->int64 to make amino encode it fixed64 (does not work for int)
Type: ProposalType,
POLRound: int64(proposal.POLRound),
Timestamp: proposal.Timestamp, Timestamp: proposal.Timestamp,
BlockPartsHeader: CanonicalizePartSetHeader(proposal.BlockPartsHeader),
POLBlockID: CanonicalizeBlockID(proposal.POLBlockID), POLBlockID: CanonicalizeBlockID(proposal.POLBlockID),
POLRound: proposal.POLRound, ChainID: chainID,
Round: proposal.Round,
} }
} }
func CanonicalizeVote(chainID string, vote *Vote) CanonicalVote { func CanonicalizeVote(chainID string, vote *Vote) CanonicalVote {
return CanonicalVote{ return CanonicalVote{
ChainID: chainID, Version: 0, // TODO
Type: "vote",
BlockID: CanonicalizeBlockID(vote.BlockID),
Height: vote.Height, Height: vote.Height,
Round: vote.Round, Round: int64(vote.Round), // cast int->int64 to make amino encode it fixed64 (does not work for int)
Type: vote.Type,
Timestamp: vote.Timestamp, Timestamp: vote.Timestamp,
VoteType: vote.Type, BlockID: CanonicalizeBlockID(vote.BlockID),
ChainID: chainID,
} }
} }
func CanonicalizeHeartbeat(chainID string, heartbeat *Heartbeat) CanonicalHeartbeat { func CanonicalizeHeartbeat(chainID string, heartbeat *Heartbeat) CanonicalHeartbeat {
return CanonicalHeartbeat{ return CanonicalHeartbeat{
ChainID: chainID, Version: 0, // TODO
Type: "heartbeat",
Height: heartbeat.Height, Height: heartbeat.Height,
Round: heartbeat.Round, Round: heartbeat.Round,
Type: byte(HeartbeatType),
Sequence: heartbeat.Sequence, Sequence: heartbeat.Sequence,
ValidatorAddress: heartbeat.ValidatorAddress, ValidatorAddress: heartbeat.ValidatorAddress,
ValidatorIndex: heartbeat.ValidatorIndex, ValidatorIndex: heartbeat.ValidatorIndex,
ChainID: chainID,
} }
} }

View File

@@ -22,7 +22,7 @@ func makeVote(val PrivValidator, chainID string, valIndex int, height int64, rou
ValidatorIndex: valIndex, ValidatorIndex: valIndex,
Height: height, Height: height,
Round: round, Round: round,
Type: byte(step), Type: SignedMsgType(step),
BlockID: blockID, BlockID: blockID,
} }
err := val.SignVote(chainID, v) err := val.SignVote(chainID, v)

27
types/signed_msg_type.go Normal file
View File

@@ -0,0 +1,27 @@
package types
// SignedMsgType is a type of signed message in the consensus.
type SignedMsgType byte
const (
// Votes
PrevoteType SignedMsgType = 0x01
PrecommitType SignedMsgType = 0x02
// Proposals
ProposalType SignedMsgType = 0x20
// Heartbeat
HeartbeatType SignedMsgType = 0x30
)
func IsVoteTypeValid(type_ SignedMsgType) bool {
switch type_ {
case PrevoteType:
return true
case PrecommitType:
return true
default:
return false
}
}

View File

@@ -16,7 +16,7 @@ func MakeCommit(blockID BlockID, height int64, round int,
ValidatorIndex: i, ValidatorIndex: i,
Height: height, Height: height,
Round: round, Round: round,
Type: VoteTypePrecommit, Type: PrecommitType,
BlockID: blockID, BlockID: blockID,
Timestamp: tmtime.Now(), Timestamp: tmtime.Now(),
} }

View File

@@ -282,7 +282,7 @@ func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height i
if precommit.Round != round { if precommit.Round != round {
return fmt.Errorf("Invalid commit -- wrong round: want %v got %v", round, precommit.Round) return fmt.Errorf("Invalid commit -- wrong round: want %v got %v", round, precommit.Round)
} }
if precommit.Type != VoteTypePrecommit { if precommit.Type != PrecommitType {
return fmt.Errorf("Invalid commit -- not precommit @ index %v", idx) return fmt.Errorf("Invalid commit -- not precommit @ index %v", idx)
} }
_, val := vals.GetByIndex(idx) _, val := vals.GetByIndex(idx)
@@ -361,7 +361,7 @@ func (vals *ValidatorSet) VerifyFutureCommit(newSet *ValidatorSet, chainID strin
if precommit.Round != round { if precommit.Round != round {
return cmn.NewError("Invalid commit -- wrong round: %v vs %v", round, precommit.Round) return cmn.NewError("Invalid commit -- wrong round: %v vs %v", round, precommit.Round)
} }
if precommit.Type != VoteTypePrecommit { if precommit.Type != PrecommitType {
return cmn.NewError("Invalid commit -- not precommit @ index %v", idx) return cmn.NewError("Invalid commit -- not precommit @ index %v", idx)
} }
// See if this validator is in oldVals. // See if this validator is in oldVals.

View File

@@ -385,7 +385,7 @@ func TestValidatorSetVerifyCommit(t *testing.T) {
Height: height, Height: height,
Round: 0, Round: 0,
Timestamp: tmtime.Now(), Timestamp: tmtime.Now(),
Type: VoteTypePrecommit, Type: PrecommitType,
BlockID: blockID, BlockID: blockID,
} }
sig, err := privKey.Sign(vote.SignBytes(chainID)) sig, err := privKey.Sign(vote.SignBytes(chainID))

View File

@@ -43,24 +43,6 @@ func NewConflictingVoteError(val *Validator, voteA, voteB *Vote) *ErrVoteConflic
} }
} }
// Types of votes
// TODO Make a new type "VoteType"
const (
VoteTypePrevote = byte(0x01)
VoteTypePrecommit = byte(0x02)
)
func IsVoteTypeValid(type_ byte) bool {
switch type_ {
case VoteTypePrevote:
return true
case VoteTypePrecommit:
return true
default:
return false
}
}
// Address is hex bytes. // Address is hex bytes.
type Address = crypto.Address type Address = crypto.Address
@@ -71,7 +53,7 @@ type Vote struct {
Height int64 `json:"height"` Height int64 `json:"height"`
Round int `json:"round"` Round int `json:"round"`
Timestamp time.Time `json:"timestamp"` Timestamp time.Time `json:"timestamp"`
Type byte `json:"type"` Type SignedMsgType `json:"type"`
BlockID BlockID `json:"block_id"` // zero if vote is nil. BlockID BlockID `json:"block_id"` // zero if vote is nil.
Signature []byte `json:"signature"` Signature []byte `json:"signature"`
} }
@@ -95,9 +77,9 @@ func (vote *Vote) String() string {
} }
var typeString string var typeString string
switch vote.Type { switch vote.Type {
case VoteTypePrevote: case PrevoteType:
typeString = "Prevote" typeString = "Prevote"
case VoteTypePrecommit: case PrecommitType:
typeString = "Precommit" typeString = "Precommit"
default: default:
cmn.PanicSanity("Unknown vote type") cmn.PanicSanity("Unknown vote type")

View File

@@ -55,7 +55,7 @@ type VoteSet struct {
chainID string chainID string
height int64 height int64
round int round int
type_ byte type_ SignedMsgType
valSet *ValidatorSet valSet *ValidatorSet
mtx sync.Mutex mtx sync.Mutex
@@ -68,7 +68,7 @@ type VoteSet struct {
} }
// Constructs a new VoteSet struct used to accumulate votes for given height/round. // Constructs a new VoteSet struct used to accumulate votes for given height/round.
func NewVoteSet(chainID string, height int64, round int, type_ byte, valSet *ValidatorSet) *VoteSet { func NewVoteSet(chainID string, height int64, round int, type_ SignedMsgType, valSet *ValidatorSet) *VoteSet {
if height == 0 { if height == 0 {
cmn.PanicSanity("Cannot make VoteSet for height == 0, doesn't make sense.") cmn.PanicSanity("Cannot make VoteSet for height == 0, doesn't make sense.")
} }
@@ -109,7 +109,7 @@ func (voteSet *VoteSet) Type() byte {
if voteSet == nil { if voteSet == nil {
return 0x00 return 0x00
} }
return voteSet.type_ return byte(voteSet.type_)
} }
func (voteSet *VoteSet) Size() int { func (voteSet *VoteSet) Size() int {
@@ -381,7 +381,7 @@ func (voteSet *VoteSet) IsCommit() bool {
if voteSet == nil { if voteSet == nil {
return false return false
} }
if voteSet.type_ != VoteTypePrecommit { if voteSet.type_ != PrecommitType {
return false return false
} }
voteSet.mtx.Lock() voteSet.mtx.Lock()
@@ -529,8 +529,8 @@ func (voteSet *VoteSet) sumTotalFrac() (int64, int64, float64) {
// Commit // Commit
func (voteSet *VoteSet) MakeCommit() *Commit { func (voteSet *VoteSet) MakeCommit() *Commit {
if voteSet.type_ != VoteTypePrecommit { if voteSet.type_ != PrecommitType {
cmn.PanicSanity("Cannot MakeCommit() unless VoteSet.Type is VoteTypePrecommit") cmn.PanicSanity("Cannot MakeCommit() unless VoteSet.Type is PrecommitType")
} }
voteSet.mtx.Lock() voteSet.mtx.Lock()
defer voteSet.mtx.Unlock() defer voteSet.mtx.Unlock()

View File

@@ -11,7 +11,7 @@ import (
) )
// NOTE: privValidators are in order // NOTE: privValidators are in order
func randVoteSet(height int64, round int, type_ byte, numValidators int, votingPower int64) (*VoteSet, *ValidatorSet, []PrivValidator) { func randVoteSet(height int64, round int, type_ SignedMsgType, numValidators int, votingPower int64) (*VoteSet, *ValidatorSet, []PrivValidator) {
valSet, privValidators := RandValidatorSet(numValidators, votingPower) valSet, privValidators := RandValidatorSet(numValidators, votingPower)
return NewVoteSet("test_chain_id", height, round, type_, valSet), valSet, privValidators return NewVoteSet("test_chain_id", height, round, type_, valSet), valSet, privValidators
} }
@@ -41,7 +41,7 @@ func withRound(vote *Vote, round int) *Vote {
// Convenience: Return new vote with different type // Convenience: Return new vote with different type
func withType(vote *Vote, type_ byte) *Vote { func withType(vote *Vote, type_ byte) *Vote {
vote = vote.Copy() vote = vote.Copy()
vote.Type = type_ vote.Type = SignedMsgType(type_)
return vote return vote
} }
@@ -61,7 +61,7 @@ func withBlockPartsHeader(vote *Vote, blockPartsHeader PartSetHeader) *Vote {
func TestAddVote(t *testing.T) { func TestAddVote(t *testing.T) {
height, round := int64(1), 0 height, round := int64(1), 0
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 10, 1) voteSet, _, privValidators := randVoteSet(height, round, PrevoteType, 10, 1)
val0 := privValidators[0] val0 := privValidators[0]
// t.Logf(">> %v", voteSet) // t.Logf(">> %v", voteSet)
@@ -82,7 +82,7 @@ func TestAddVote(t *testing.T) {
ValidatorIndex: 0, // since privValidators are in order ValidatorIndex: 0, // since privValidators are in order
Height: height, Height: height,
Round: round, Round: round,
Type: VoteTypePrevote, Type: PrevoteType,
Timestamp: tmtime.Now(), Timestamp: tmtime.Now(),
BlockID: BlockID{nil, PartSetHeader{}}, BlockID: BlockID{nil, PartSetHeader{}},
} }
@@ -105,14 +105,14 @@ func TestAddVote(t *testing.T) {
func Test2_3Majority(t *testing.T) { func Test2_3Majority(t *testing.T) {
height, round := int64(1), 0 height, round := int64(1), 0
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 10, 1) voteSet, _, privValidators := randVoteSet(height, round, PrevoteType, 10, 1)
voteProto := &Vote{ voteProto := &Vote{
ValidatorAddress: nil, // NOTE: must fill in ValidatorAddress: nil, // NOTE: must fill in
ValidatorIndex: -1, // NOTE: must fill in ValidatorIndex: -1, // NOTE: must fill in
Height: height, Height: height,
Round: round, Round: round,
Type: VoteTypePrevote, Type: PrevoteType,
Timestamp: tmtime.Now(), Timestamp: tmtime.Now(),
BlockID: BlockID{nil, PartSetHeader{}}, BlockID: BlockID{nil, PartSetHeader{}},
} }
@@ -158,7 +158,7 @@ func Test2_3Majority(t *testing.T) {
func Test2_3MajorityRedux(t *testing.T) { func Test2_3MajorityRedux(t *testing.T) {
height, round := int64(1), 0 height, round := int64(1), 0
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 100, 1) voteSet, _, privValidators := randVoteSet(height, round, PrevoteType, 100, 1)
blockHash := crypto.CRandBytes(32) blockHash := crypto.CRandBytes(32)
blockPartsTotal := 123 blockPartsTotal := 123
@@ -170,7 +170,7 @@ func Test2_3MajorityRedux(t *testing.T) {
Height: height, Height: height,
Round: round, Round: round,
Timestamp: tmtime.Now(), Timestamp: tmtime.Now(),
Type: VoteTypePrevote, Type: PrevoteType,
BlockID: BlockID{blockHash, blockPartsHeader}, BlockID: BlockID{blockHash, blockPartsHeader},
} }
@@ -257,7 +257,7 @@ func Test2_3MajorityRedux(t *testing.T) {
func TestBadVotes(t *testing.T) { func TestBadVotes(t *testing.T) {
height, round := int64(1), 0 height, round := int64(1), 0
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 10, 1) voteSet, _, privValidators := randVoteSet(height, round, PrevoteType, 10, 1)
voteProto := &Vote{ voteProto := &Vote{
ValidatorAddress: nil, ValidatorAddress: nil,
@@ -265,7 +265,7 @@ func TestBadVotes(t *testing.T) {
Height: height, Height: height,
Round: round, Round: round,
Timestamp: tmtime.Now(), Timestamp: tmtime.Now(),
Type: VoteTypePrevote, Type: PrevoteType,
BlockID: BlockID{nil, PartSetHeader{}}, BlockID: BlockID{nil, PartSetHeader{}},
} }
@@ -308,7 +308,7 @@ func TestBadVotes(t *testing.T) {
// val3 votes of another type. // val3 votes of another type.
{ {
vote := withValidator(voteProto, privValidators[3].GetAddress(), 3) vote := withValidator(voteProto, privValidators[3].GetAddress(), 3)
added, err := signAddVote(privValidators[3], withType(vote, VoteTypePrecommit), voteSet) added, err := signAddVote(privValidators[3], withType(vote, byte(PrecommitType)), voteSet)
if added || err == nil { if added || err == nil {
t.Errorf("Expected VoteSet.Add to fail, wrong type") t.Errorf("Expected VoteSet.Add to fail, wrong type")
} }
@@ -317,7 +317,7 @@ func TestBadVotes(t *testing.T) {
func TestConflicts(t *testing.T) { func TestConflicts(t *testing.T) {
height, round := int64(1), 0 height, round := int64(1), 0
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrevote, 4, 1) voteSet, _, privValidators := randVoteSet(height, round, PrevoteType, 4, 1)
blockHash1 := cmn.RandBytes(32) blockHash1 := cmn.RandBytes(32)
blockHash2 := cmn.RandBytes(32) blockHash2 := cmn.RandBytes(32)
@@ -327,7 +327,7 @@ func TestConflicts(t *testing.T) {
Height: height, Height: height,
Round: round, Round: round,
Timestamp: tmtime.Now(), Timestamp: tmtime.Now(),
Type: VoteTypePrevote, Type: PrevoteType,
BlockID: BlockID{nil, PartSetHeader{}}, BlockID: BlockID{nil, PartSetHeader{}},
} }
@@ -447,7 +447,7 @@ func TestConflicts(t *testing.T) {
func TestMakeCommit(t *testing.T) { func TestMakeCommit(t *testing.T) {
height, round := int64(1), 0 height, round := int64(1), 0
voteSet, _, privValidators := randVoteSet(height, round, VoteTypePrecommit, 10, 1) voteSet, _, privValidators := randVoteSet(height, round, PrecommitType, 10, 1)
blockHash, blockPartsHeader := crypto.CRandBytes(32), PartSetHeader{123, crypto.CRandBytes(32)} blockHash, blockPartsHeader := crypto.CRandBytes(32), PartSetHeader{123, crypto.CRandBytes(32)}
voteProto := &Vote{ voteProto := &Vote{
@@ -456,7 +456,7 @@ func TestMakeCommit(t *testing.T) {
Height: height, Height: height,
Round: round, Round: round,
Timestamp: tmtime.Now(), Timestamp: tmtime.Now(),
Type: VoteTypePrecommit, Type: PrecommitType,
BlockID: BlockID{blockHash, blockPartsHeader}, BlockID: BlockID{blockHash, blockPartsHeader},
} }

View File

@@ -13,11 +13,11 @@ import (
) )
func examplePrevote() *Vote { func examplePrevote() *Vote {
return exampleVote(VoteTypePrevote) return exampleVote(byte(PrevoteType))
} }
func examplePrecommit() *Vote { func examplePrecommit() *Vote {
return exampleVote(VoteTypePrecommit) return exampleVote(byte(PrecommitType))
} }
func exampleVote(t byte) *Vote { func exampleVote(t byte) *Vote {
@@ -32,7 +32,7 @@ func exampleVote(t byte) *Vote {
Height: 12345, Height: 12345,
Round: 2, Round: 2,
Timestamp: stamp, Timestamp: stamp,
Type: t, Type: SignedMsgType(t),
BlockID: BlockID{ BlockID: BlockID{
Hash: tmhash.Sum([]byte("blockID_hash")), Hash: tmhash.Sum([]byte("blockID_hash")),
PartsHeader: PartSetHeader{ PartsHeader: PartSetHeader{
@@ -53,6 +53,98 @@ func TestVoteSignable(t *testing.T) {
require.Equal(t, expected, signBytes, "Got unexpected sign bytes for Vote.") require.Equal(t, expected, signBytes, "Got unexpected sign bytes for Vote.")
} }
func TestVoteSignableTestVectors(t *testing.T) {
voteWithVersion := CanonicalizeVote("", &Vote{Height: 1, Round: 1})
voteWithVersion.Version = 123
tests := []struct {
canonicalVote CanonicalVote
want []byte
}{
{
CanonicalizeVote("", &Vote{}),
// NOTE: Height and Round are skipped here. This case needs to be considered while parsing.
[]byte{0xb, 0x2a, 0x9, 0x9, 0x0, 0x9, 0x6e, 0x88, 0xf1, 0xff, 0xff, 0xff},
},
// with proper (fixed size) height and round (PreCommit):
{
CanonicalizeVote("", &Vote{Height: 1, Round: 1, Type: PrecommitType}),
[]byte{
0x1f, // total length
0x11, // (field_number << 3) | wire_type (version is missing)
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height
0x19, // (field_number << 3) | wire_type
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round
0x20, // (field_number << 3) | wire_type
0x2, // PrecommitType
0x2a, // (field_number << 3) | wire_type
// remaining fields (timestamp):
0x9, 0x9, 0x0, 0x9, 0x6e, 0x88, 0xf1, 0xff, 0xff, 0xff},
},
// with proper (fixed size) height and round (PreVote):
{
CanonicalizeVote("", &Vote{Height: 1, Round: 1, Type: PrevoteType}),
[]byte{
0x1f, // total length
0x11, // (field_number << 3) | wire_type (version is missing)
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height
0x19, // (field_number << 3) | wire_type
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round
0x20, // (field_number << 3) | wire_type
0x1, // PrevoteType
0x2a, // (field_number << 3) | wire_type
// remaining fields (timestamp):
0x9, 0x9, 0x0, 0x9, 0x6e, 0x88, 0xf1, 0xff, 0xff, 0xff},
},
// containing version (empty type)
{
voteWithVersion,
[]byte{
0x26, // total length
0x9, // (field_number << 3) | wire_type
0x7b, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // version (123)
0x11, // (field_number << 3) | wire_type
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height
0x19, // (field_number << 3) | wire_type
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round
// remaining fields (timestamp):
0x2a,
0x9, 0x9, 0x0, 0x9, 0x6e, 0x88, 0xf1, 0xff, 0xff, 0xff},
},
// containing non-empty chain_id:
{
CanonicalizeVote("test_chain_id", &Vote{Height: 1, Round: 1}),
[]byte{
0x2c, // total length
0x11, // (field_number << 3) | wire_type
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height
0x19, // (field_number << 3) | wire_type
0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // round
// remaining fields:
0x2a, // (field_number << 3) | wire_type
0x9, 0x9, 0x0, 0x9, 0x6e, 0x88, 0xf1, 0xff, 0xff, 0xff, // timestamp
0x3a, // (field_number << 3) | wire_type
0xd, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64}, // chainID
},
}
for i, tc := range tests {
got, err := cdc.MarshalBinary(tc.canonicalVote)
require.NoError(t, err)
require.Equal(t, tc.want, got, "test case #%v: got unexpected sign bytes for Vote.", i)
}
}
func TestVoteProposalNotEq(t *testing.T) {
cv := CanonicalizeVote("", &Vote{Height: 1, Round: 1})
p := CanonicalizeProposal("", &Proposal{Height: 1, Round: 1})
vb, err := cdc.MarshalBinary(cv)
require.NoError(t, err)
pb, err := cdc.MarshalBinary(p)
require.NoError(t, err)
require.NotEqual(t, vb, pb)
}
func TestVoteVerifySignature(t *testing.T) { func TestVoteVerifySignature(t *testing.T) {
privVal := NewMockPV() privVal := NewMockPV()
pubkey := privVal.GetPubKey() pubkey := privVal.GetPubKey()
@@ -85,12 +177,12 @@ func TestVoteVerifySignature(t *testing.T) {
func TestIsVoteTypeValid(t *testing.T) { func TestIsVoteTypeValid(t *testing.T) {
tc := []struct { tc := []struct {
name string name string
in byte in SignedMsgType
out bool out bool
}{ }{
{"Prevote", VoteTypePrevote, true}, {"Prevote", PrevoteType, true},
{"Precommit", VoteTypePrecommit, true}, {"Precommit", PrecommitType, true},
{"InvalidType", byte(3), false}, {"InvalidType", SignedMsgType(0x3), false},
} }
for _, tt := range tc { for _, tt := range tc {
@@ -128,7 +220,7 @@ func TestMaxVoteBytes(t *testing.T) {
Height: math.MaxInt64, Height: math.MaxInt64,
Round: math.MaxInt64, Round: math.MaxInt64,
Timestamp: tmtime.Now(), Timestamp: tmtime.Now(),
Type: VoteTypePrevote, Type: PrevoteType,
BlockID: BlockID{ BlockID: BlockID{
Hash: tmhash.Sum([]byte("blockID_hash")), Hash: tmhash.Sum([]byte("blockID_hash")),
PartsHeader: PartSetHeader{ PartsHeader: PartSetHeader{