Browse Source

validate reactor messages (#2711)

* validate reactor messages

Refs #2683

* validate blockchain messages

Refs #2683

* validate evidence messages

Refs #2683

* todo

* check ProposalPOL and signature sizes

* add a changelog entry

* check addr is valid when we add it to the addrbook

* validate incoming netAddr (not just nil check!)

* fixes after Bucky's review

* check timestamps

* beef up block#ValidateBasic

* move some checks into bcBlockResponseMessage

* update Gopkg.lock

Fix

```
grouped write of manifest, lock and vendor: failed to export github.com/tendermint/go-amino: fatal: failed to unpack tree object 6dcc6ddc14
```

by running `dep ensure -update`

* bump year since now we check it

* generate test/p2p/data on the fly using tendermint testnet

* allow sync chains older than 1 year

* use full path when creating a testnet

* move testnet gen to test/docker/Dockerfile

* relax LastCommitRound check

Refs #2737

* fix conflicts after merge

* add small comment

* some ValidateBasic updates

* fixes

* AppHash length is not fixed
pull/2746/head
Anton Kaliaev 6 years ago
committed by Ethan Buchman
parent
commit
fb91ef7462
41 changed files with 613 additions and 336 deletions
  1. +4
    -0
      CHANGELOG_PENDING.md
  2. +51
    -2
      blockchain/reactor.go
  3. +1
    -1
      config/toml.go
  4. +0
    -2
      consensus/common_test.go
  5. +0
    -1
      consensus/mempool_test.go
  6. +151
    -10
      consensus/reactor.go
  7. +6
    -2
      consensus/replay.go
  8. +7
    -0
      consensus/types/round_state.go
  9. +1
    -0
      crypto/crypto.go
  10. +21
    -2
      evidence/reactor.go
  11. +4
    -0
      p2p/pex/addrbook.go
  12. +8
    -0
      p2p/pex/errors.go
  13. +22
    -6
      p2p/pex/pex_reactor.go
  14. +20
    -30
      state/validation.go
  15. +7
    -4
      test/docker/Dockerfile
  16. +3
    -6
      test/p2p/README.md
  17. +0
    -39
      test/p2p/data/mach1/core/config/genesis.json
  18. +0
    -6
      test/p2p/data/mach1/core/config/node_key.json
  19. +0
    -14
      test/p2p/data/mach1/core/config/priv_validator.json
  20. +0
    -39
      test/p2p/data/mach2/core/config/genesis.json
  21. +0
    -6
      test/p2p/data/mach2/core/config/node_key.json
  22. +0
    -14
      test/p2p/data/mach2/core/config/priv_validator.json
  23. +0
    -39
      test/p2p/data/mach3/core/config/genesis.json
  24. +0
    -6
      test/p2p/data/mach3/core/config/node_key.json
  25. +0
    -14
      test/p2p/data/mach3/core/config/priv_validator.json
  26. +0
    -39
      test/p2p/data/mach4/core/config/genesis.json
  27. +0
    -6
      test/p2p/data/mach4/core/config/node_key.json
  28. +0
    -14
      test/p2p/data/mach4/core/config/priv_validator.json
  29. +1
    -1
      test/p2p/ip_plus_id.sh
  30. +4
    -2
      test/p2p/peer.sh
  31. +3
    -3
      test/p2p/pex/test_addrbook.sh
  32. +96
    -19
      types/block.go
  33. +4
    -2
      types/block_test.go
  34. +21
    -0
      types/evidence.go
  35. +31
    -0
      types/heartbeat.go
  36. +25
    -1
      types/part_set.go
  37. +29
    -0
      types/proposal.go
  38. +12
    -0
      types/signable.go
  39. +4
    -5
      types/signed_msg_type.go
  40. +40
    -0
      types/validation.go
  41. +37
    -1
      types/vote.go

+ 4
- 0
CHANGELOG_PENDING.md View File

@ -92,6 +92,10 @@ Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermi
github.com/tendermint/crypto github.com/tendermint/crypto
- [tools] [\#2238](https://github.com/tendermint/tendermint/issues/2238) Binary dependencies are now locked to a specific git commit - [tools] [\#2238](https://github.com/tendermint/tendermint/issues/2238) Binary dependencies are now locked to a specific git commit
- [libs/log] [\#2706](https://github.com/tendermint/tendermint/issues/2706) Add year to log format - [libs/log] [\#2706](https://github.com/tendermint/tendermint/issues/2706) Add year to log format
- [consensus] [\#2683] validate all incoming messages
- [evidence] [\#2683] validate all incoming messages
- [blockchain] [\#2683] validate all incoming messages
- [p2p/pex] [\#2683] validate pexAddrsMessage addresses
### BUG FIXES: ### BUG FIXES:
- [autofile] [\#2428](https://github.com/tendermint/tendermint/issues/2428) Group.RotateFile need call Flush() before rename (@goolAdapter) - [autofile] [\#2428](https://github.com/tendermint/tendermint/issues/2428) Group.RotateFile need call Flush() before rename (@goolAdapter)


+ 51
- 2
blockchain/reactor.go View File

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

+ 1
- 1
config/toml.go View File

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


+ 0
- 2
consensus/common_test.go View File

@ -615,8 +615,6 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G
func randGenesisState(numValidators int, randPower bool, minPower int64) (sm.State, []types.PrivValidator) { func randGenesisState(numValidators int, randPower bool, minPower int64) (sm.State, []types.PrivValidator) {
genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower) genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower)
s0, _ := sm.MakeGenesisState(genDoc) s0, _ := sm.MakeGenesisState(genDoc)
db := dbm.NewMemDB() // remove this ?
sm.SaveState(db, s0)
return s0, privValidators return s0, privValidators
} }


+ 0
- 1
consensus/mempool_test.go View File

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


+ 151
- 10
consensus/reactor.go View File

@ -8,8 +8,7 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/tendermint/go-amino"
amino "github.com/tendermint/go-amino"
cstypes "github.com/tendermint/tendermint/consensus/types" cstypes "github.com/tendermint/tendermint/consensus/types"
cmn "github.com/tendermint/tendermint/libs/common" cmn "github.com/tendermint/tendermint/libs/common"
tmevents "github.com/tendermint/tendermint/libs/events" tmevents "github.com/tendermint/tendermint/libs/events"
@ -205,6 +204,13 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
conR.Switch.StopPeerForError(src, err) conR.Switch.StopPeerForError(src, err)
return return
} }
if err = msg.ValidateBasic(); err != nil {
conR.Logger.Error("Peer sent us invalid msg", "peer", src, "msg", msg, "err", err)
conR.Switch.StopPeerForError(src, err)
return
}
conR.Logger.Debug("Receive", "src", src, "chId", chID, "msg", msg) conR.Logger.Debug("Receive", "src", src, "chId", chID, "msg", msg)
// Get peer states // Get peer states
@ -242,8 +248,7 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
case types.PrecommitType: 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")
return
panic("Bad VoteSetBitsMessage field Type. Forgot to add a check in ValidateBasic?")
} }
src.TrySend(VoteSetBitsChannel, cdc.MustMarshalBinaryBare(&VoteSetBitsMessage{ src.TrySend(VoteSetBitsChannel, cdc.MustMarshalBinaryBare(&VoteSetBitsMessage{
Height: msg.Height, Height: msg.Height,
@ -322,8 +327,7 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
case types.PrecommitType: 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")
return
panic("Bad VoteSetBitsMessage field Type. Forgot to add a check in ValidateBasic?")
} }
ps.ApplyVoteSetBitsMessage(msg, ourVotes) ps.ApplyVoteSetBitsMessage(msg, ourVotes)
} else { } else {
@ -440,9 +444,9 @@ func (conR *ConsensusReactor) broadcastHasVoteMessage(vote *types.Vote) {
func makeRoundStepMessage(rs *cstypes.RoundState) (nrsMsg *NewRoundStepMessage) { func makeRoundStepMessage(rs *cstypes.RoundState) (nrsMsg *NewRoundStepMessage) {
nrsMsg = &NewRoundStepMessage{ nrsMsg = &NewRoundStepMessage{
Height: rs.Height,
Round: rs.Round,
Step: rs.Step,
Height: rs.Height,
Round: rs.Round,
Step: rs.Step,
SecondsSinceStartTime: int(time.Since(rs.StartTime).Seconds()), SecondsSinceStartTime: int(time.Since(rs.StartTime).Seconds()),
LastCommitRound: rs.LastCommit.Round(), LastCommitRound: rs.LastCommit.Round(),
} }
@ -1349,7 +1353,9 @@ func (ps *PeerState) StringIndented(indent string) string {
// Messages // Messages
// ConsensusMessage is a message that can be sent and received on the ConsensusReactor // ConsensusMessage is a message that can be sent and received on the ConsensusReactor
type ConsensusMessage interface{}
type ConsensusMessage interface {
ValidateBasic() error
}
func RegisterConsensusMessages(cdc *amino.Codec) { func RegisterConsensusMessages(cdc *amino.Codec) {
cdc.RegisterInterface((*ConsensusMessage)(nil), nil) cdc.RegisterInterface((*ConsensusMessage)(nil), nil)
@ -1385,6 +1391,27 @@ type NewRoundStepMessage struct {
LastCommitRound int LastCommitRound int
} }
// ValidateBasic performs basic validation.
func (m *NewRoundStepMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("Negative Height")
}
if m.Round < 0 {
return errors.New("Negative Round")
}
if !m.Step.IsValid() {
return errors.New("Invalid Step")
}
// NOTE: SecondsSinceStartTime may be negative
if (m.Height == 1 && m.LastCommitRound != -1) ||
(m.Height > 1 && m.LastCommitRound < -1) { // TODO: #2737 LastCommitRound should always be >= 0 for heights > 1
return errors.New("Invalid LastCommitRound (for 1st block: -1, for others: >= 0)")
}
return nil
}
// String returns a string representation. // String returns a string representation.
func (m *NewRoundStepMessage) String() string { func (m *NewRoundStepMessage) String() string {
return fmt.Sprintf("[NewRoundStep H:%v R:%v S:%v LCR:%v]", return fmt.Sprintf("[NewRoundStep H:%v R:%v S:%v LCR:%v]",
@ -1404,6 +1431,25 @@ type NewValidBlockMessage struct {
IsCommit bool IsCommit bool
} }
// ValidateBasic performs basic validation.
func (m *NewValidBlockMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("Negative Height")
}
if m.Round < 0 {
return errors.New("Negative Round")
}
if err := m.BlockPartsHeader.ValidateBasic(); err != nil {
return fmt.Errorf("Wrong BlockPartsHeader: %v", err)
}
if m.BlockParts.Size() != m.BlockPartsHeader.Total {
return fmt.Errorf("BlockParts bit array size %d not equal to BlockPartsHeader.Total %d",
m.BlockParts.Size(),
m.BlockPartsHeader.Total)
}
return nil
}
// String returns a string representation. // String returns a string representation.
func (m *NewValidBlockMessage) String() string { func (m *NewValidBlockMessage) String() string {
return fmt.Sprintf("[ValidBlockMessage H:%v R:%v BP:%v BA:%v IsCommit:%v]", return fmt.Sprintf("[ValidBlockMessage H:%v R:%v BP:%v BA:%v IsCommit:%v]",
@ -1417,6 +1463,11 @@ type ProposalMessage struct {
Proposal *types.Proposal Proposal *types.Proposal
} }
// ValidateBasic performs basic validation.
func (m *ProposalMessage) ValidateBasic() error {
return m.Proposal.ValidateBasic()
}
// String returns a string representation. // String returns a string representation.
func (m *ProposalMessage) String() string { func (m *ProposalMessage) String() string {
return fmt.Sprintf("[Proposal %v]", m.Proposal) return fmt.Sprintf("[Proposal %v]", m.Proposal)
@ -1431,6 +1482,20 @@ type ProposalPOLMessage struct {
ProposalPOL *cmn.BitArray ProposalPOL *cmn.BitArray
} }
// ValidateBasic performs basic validation.
func (m *ProposalPOLMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("Negative Height")
}
if m.ProposalPOLRound < 0 {
return errors.New("Negative ProposalPOLRound")
}
if m.ProposalPOL.Size() == 0 {
return errors.New("Empty ProposalPOL bit array")
}
return nil
}
// String returns a string representation. // String returns a string representation.
func (m *ProposalPOLMessage) String() string { func (m *ProposalPOLMessage) String() string {
return fmt.Sprintf("[ProposalPOL H:%v POLR:%v POL:%v]", m.Height, m.ProposalPOLRound, m.ProposalPOL) return fmt.Sprintf("[ProposalPOL H:%v POLR:%v POL:%v]", m.Height, m.ProposalPOLRound, m.ProposalPOL)
@ -1445,6 +1510,20 @@ type BlockPartMessage struct {
Part *types.Part Part *types.Part
} }
// ValidateBasic performs basic validation.
func (m *BlockPartMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("Negative Height")
}
if m.Round < 0 {
return errors.New("Negative Round")
}
if err := m.Part.ValidateBasic(); err != nil {
return fmt.Errorf("Wrong Part: %v", err)
}
return nil
}
// String returns a string representation. // String returns a string representation.
func (m *BlockPartMessage) String() string { func (m *BlockPartMessage) String() string {
return fmt.Sprintf("[BlockPart H:%v R:%v P:%v]", m.Height, m.Round, m.Part) return fmt.Sprintf("[BlockPart H:%v R:%v P:%v]", m.Height, m.Round, m.Part)
@ -1457,6 +1536,11 @@ type VoteMessage struct {
Vote *types.Vote Vote *types.Vote
} }
// ValidateBasic performs basic validation.
func (m *VoteMessage) ValidateBasic() error {
return m.Vote.ValidateBasic()
}
// String returns a string representation. // String returns a string representation.
func (m *VoteMessage) String() string { func (m *VoteMessage) String() string {
return fmt.Sprintf("[Vote %v]", m.Vote) return fmt.Sprintf("[Vote %v]", m.Vote)
@ -1472,6 +1556,23 @@ type HasVoteMessage struct {
Index int Index int
} }
// ValidateBasic performs basic validation.
func (m *HasVoteMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("Negative Height")
}
if m.Round < 0 {
return errors.New("Negative Round")
}
if !types.IsVoteTypeValid(m.Type) {
return errors.New("Invalid Type")
}
if m.Index < 0 {
return errors.New("Negative Index")
}
return nil
}
// String returns a string representation. // String returns a string representation.
func (m *HasVoteMessage) String() string { func (m *HasVoteMessage) String() string {
return fmt.Sprintf("[HasVote VI:%v V:{%v/%02d/%v}]", m.Index, m.Height, m.Round, m.Type) return fmt.Sprintf("[HasVote VI:%v V:{%v/%02d/%v}]", m.Index, m.Height, m.Round, m.Type)
@ -1487,6 +1588,23 @@ type VoteSetMaj23Message struct {
BlockID types.BlockID BlockID types.BlockID
} }
// ValidateBasic performs basic validation.
func (m *VoteSetMaj23Message) ValidateBasic() error {
if m.Height < 0 {
return errors.New("Negative Height")
}
if m.Round < 0 {
return errors.New("Negative Round")
}
if !types.IsVoteTypeValid(m.Type) {
return errors.New("Invalid Type")
}
if err := m.BlockID.ValidateBasic(); err != nil {
return fmt.Errorf("Wrong BlockID: %v", err)
}
return nil
}
// String returns a string representation. // String returns a string representation.
func (m *VoteSetMaj23Message) String() string { func (m *VoteSetMaj23Message) String() string {
return fmt.Sprintf("[VSM23 %v/%02d/%v %v]", m.Height, m.Round, m.Type, m.BlockID) return fmt.Sprintf("[VSM23 %v/%02d/%v %v]", m.Height, m.Round, m.Type, m.BlockID)
@ -1503,6 +1621,24 @@ type VoteSetBitsMessage struct {
Votes *cmn.BitArray Votes *cmn.BitArray
} }
// ValidateBasic performs basic validation.
func (m *VoteSetBitsMessage) ValidateBasic() error {
if m.Height < 0 {
return errors.New("Negative Height")
}
if m.Round < 0 {
return errors.New("Negative Round")
}
if !types.IsVoteTypeValid(m.Type) {
return errors.New("Invalid Type")
}
if err := m.BlockID.ValidateBasic(); err != nil {
return fmt.Errorf("Wrong BlockID: %v", err)
}
// NOTE: Votes.Size() can be zero if the node does not have any
return nil
}
// String returns a string representation. // String returns a string representation.
func (m *VoteSetBitsMessage) String() string { func (m *VoteSetBitsMessage) String() string {
return fmt.Sprintf("[VSB %v/%02d/%v %v %v]", m.Height, m.Round, m.Type, m.BlockID, m.Votes) return fmt.Sprintf("[VSB %v/%02d/%v %v %v]", m.Height, m.Round, m.Type, m.BlockID, m.Votes)
@ -1515,6 +1651,11 @@ type ProposalHeartbeatMessage struct {
Heartbeat *types.Heartbeat Heartbeat *types.Heartbeat
} }
// ValidateBasic performs basic validation.
func (m *ProposalHeartbeatMessage) ValidateBasic() error {
return m.Heartbeat.ValidateBasic()
}
// String returns a string representation. // String returns a string representation.
func (m *ProposalHeartbeatMessage) String() string { func (m *ProposalHeartbeatMessage) String() string {
return fmt.Sprintf("[HEARTBEAT %v]", m.Heartbeat) return fmt.Sprintf("[HEARTBEAT %v]", m.Heartbeat)


+ 6
- 2
consensus/replay.go View File

@ -264,8 +264,12 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error {
// Replay all blocks since appBlockHeight and ensure the result matches the current state. // Replay all blocks since appBlockHeight and ensure the result matches the current state.
// Returns the final AppHash or an error. // Returns the final AppHash or an error.
func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight int64, proxyApp proxy.AppConns) ([]byte, error) {
func (h *Handshaker) ReplayBlocks(
state sm.State,
appHash []byte,
appBlockHeight int64,
proxyApp proxy.AppConns,
) ([]byte, error) {
storeBlockHeight := h.store.Height() storeBlockHeight := h.store.Height()
stateBlockHeight := state.LastBlockHeight stateBlockHeight := state.LastBlockHeight
h.logger.Info("ABCI Replay Blocks", "appHeight", appBlockHeight, "storeHeight", storeBlockHeight, "stateHeight", stateBlockHeight) h.logger.Info("ABCI Replay Blocks", "appHeight", appBlockHeight, "storeHeight", storeBlockHeight, "stateHeight", stateBlockHeight)


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

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


+ 1
- 0
crypto/crypto.go View File

@ -6,6 +6,7 @@ import (
) )
const ( const (
// AddressSize is the size of a pubkey address.
AddressSize = tmhash.TruncatedSize AddressSize = tmhash.TruncatedSize
) )


+ 21
- 2
evidence/reactor.go View File

@ -74,6 +74,13 @@ func (evR *EvidenceReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
evR.Switch.StopPeerForError(src, err) evR.Switch.StopPeerForError(src, err)
return return
} }
if err = msg.ValidateBasic(); err != nil {
evR.Logger.Error("Peer sent us invalid msg", "peer", src, "msg", msg, "err", err)
evR.Switch.StopPeerForError(src, err)
return
}
evR.Logger.Debug("Receive", "src", src, "chId", chID, "msg", msg) evR.Logger.Debug("Receive", "src", src, "chId", chID, "msg", msg)
switch msg := msg.(type) { switch msg := msg.(type) {
@ -191,7 +198,9 @@ type PeerState interface {
// Messages // Messages
// EvidenceMessage is a message sent or received by the EvidenceReactor. // EvidenceMessage is a message sent or received by the EvidenceReactor.
type EvidenceMessage interface{}
type EvidenceMessage interface {
ValidateBasic() error
}
func RegisterEvidenceMessages(cdc *amino.Codec) { func RegisterEvidenceMessages(cdc *amino.Codec) {
cdc.RegisterInterface((*EvidenceMessage)(nil), nil) cdc.RegisterInterface((*EvidenceMessage)(nil), nil)
@ -209,11 +218,21 @@ func decodeMsg(bz []byte) (msg EvidenceMessage, err error) {
//------------------------------------- //-------------------------------------
// EvidenceMessage contains a list of evidence.
// EvidenceListMessage contains a list of evidence.
type EvidenceListMessage struct { type EvidenceListMessage struct {
Evidence []types.Evidence Evidence []types.Evidence
} }
// ValidateBasic performs basic validation.
func (m *EvidenceListMessage) ValidateBasic() error {
for i, ev := range m.Evidence {
if err := ev.ValidateBasic(); err != nil {
return fmt.Errorf("Invalid evidence (#%d): %v", i, err)
}
}
return nil
}
// String returns a string representation of the EvidenceListMessage. // String returns a string representation of the EvidenceListMessage.
func (m *EvidenceListMessage) String() string { func (m *EvidenceListMessage) String() string {
return fmt.Sprintf("[EvidenceListMessage %v]", m.Evidence) return fmt.Sprintf("[EvidenceListMessage %v]", m.Evidence)


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

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


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

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

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

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


+ 20
- 30
state/validation.go View File

@ -21,22 +21,19 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
// Validate basic info. // Validate basic info.
if block.Version != state.Version.Consensus { if block.Version != state.Version.Consensus {
return fmt.Errorf(
"Wrong Block.Header.Version. Expected %v, got %v",
return fmt.Errorf("Wrong Block.Header.Version. Expected %v, got %v",
state.Version.Consensus, state.Version.Consensus,
block.Version, block.Version,
) )
} }
if block.ChainID != state.ChainID { if block.ChainID != state.ChainID {
return fmt.Errorf(
"Wrong Block.Header.ChainID. Expected %v, got %v",
return fmt.Errorf("Wrong Block.Header.ChainID. Expected %v, got %v",
state.ChainID, state.ChainID,
block.ChainID, block.ChainID,
) )
} }
if block.Height != state.LastBlockHeight+1 { if block.Height != state.LastBlockHeight+1 {
return fmt.Errorf(
"Wrong Block.Header.Height. Expected %v, got %v",
return fmt.Errorf("Wrong Block.Header.Height. Expected %v, got %v",
state.LastBlockHeight+1, state.LastBlockHeight+1,
block.Height, block.Height,
) )
@ -44,16 +41,15 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
// Validate prev block info. // Validate prev block info.
if !block.LastBlockID.Equals(state.LastBlockID) { if !block.LastBlockID.Equals(state.LastBlockID) {
return fmt.Errorf(
"Wrong Block.Header.LastBlockID. Expected %v, got %v",
return fmt.Errorf("Wrong Block.Header.LastBlockID. Expected %v, got %v",
state.LastBlockID, state.LastBlockID,
block.LastBlockID, block.LastBlockID,
) )
} }
newTxs := int64(len(block.Data.Txs)) newTxs := int64(len(block.Data.Txs))
if block.TotalTxs != state.LastBlockTotalTx+newTxs { if block.TotalTxs != state.LastBlockTotalTx+newTxs {
return fmt.Errorf(
"Wrong Block.Header.TotalTxs. Expected %v, got %v",
return fmt.Errorf("Wrong Block.Header.TotalTxs. Expected %v, got %v",
state.LastBlockTotalTx+newTxs, state.LastBlockTotalTx+newTxs,
block.TotalTxs, block.TotalTxs,
) )
@ -61,46 +57,44 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
// Validate app info // Validate app info
if !bytes.Equal(block.AppHash, state.AppHash) { if !bytes.Equal(block.AppHash, state.AppHash) {
return fmt.Errorf(
"Wrong Block.Header.AppHash. Expected %X, got %v",
return fmt.Errorf("Wrong Block.Header.AppHash. Expected %X, got %v",
state.AppHash, state.AppHash,
block.AppHash, block.AppHash,
) )
} }
if !bytes.Equal(block.ConsensusHash, state.ConsensusParams.Hash()) { if !bytes.Equal(block.ConsensusHash, state.ConsensusParams.Hash()) {
return fmt.Errorf(
"Wrong Block.Header.ConsensusHash. Expected %X, got %v",
return fmt.Errorf("Wrong Block.Header.ConsensusHash. Expected %X, got %v",
state.ConsensusParams.Hash(), state.ConsensusParams.Hash(),
block.ConsensusHash, block.ConsensusHash,
) )
} }
if !bytes.Equal(block.LastResultsHash, state.LastResultsHash) { if !bytes.Equal(block.LastResultsHash, state.LastResultsHash) {
return fmt.Errorf(
"Wrong Block.Header.LastResultsHash. Expected %X, got %v",
return fmt.Errorf("Wrong Block.Header.LastResultsHash. Expected %X, got %v",
state.LastResultsHash, state.LastResultsHash,
block.LastResultsHash, block.LastResultsHash,
) )
} }
if !bytes.Equal(block.ValidatorsHash, state.Validators.Hash()) { if !bytes.Equal(block.ValidatorsHash, state.Validators.Hash()) {
return fmt.Errorf(
"Wrong Block.Header.ValidatorsHash. Expected %X, got %v",
return fmt.Errorf("Wrong Block.Header.ValidatorsHash. Expected %X, got %v",
state.Validators.Hash(), state.Validators.Hash(),
block.ValidatorsHash, block.ValidatorsHash,
) )
} }
if !bytes.Equal(block.NextValidatorsHash, state.NextValidators.Hash()) { if !bytes.Equal(block.NextValidatorsHash, state.NextValidators.Hash()) {
return fmt.Errorf("Wrong Block.Header.NextValidatorsHash. Expected %X, got %v", state.NextValidators.Hash(), block.NextValidatorsHash)
return fmt.Errorf("Wrong Block.Header.NextValidatorsHash. Expected %X, got %v",
state.NextValidators.Hash(),
block.NextValidatorsHash,
)
} }
// Validate block LastCommit. // Validate block LastCommit.
if block.Height == 1 { if block.Height == 1 {
if len(block.LastCommit.Precommits) != 0 { if len(block.LastCommit.Precommits) != 0 {
return errors.New("Block at height 1 (first block) should have no LastCommit precommits")
return errors.New("Block at height 1 can't have LastCommit precommits")
} }
} else { } else {
if len(block.LastCommit.Precommits) != state.LastValidators.Size() { if len(block.LastCommit.Precommits) != state.LastValidators.Size() {
return fmt.Errorf(
"Invalid block commit size. Expected %v, got %v",
return fmt.Errorf("Invalid block commit size. Expected %v, got %v",
state.LastValidators.Size(), state.LastValidators.Size(),
len(block.LastCommit.Precommits), len(block.LastCommit.Precommits),
) )
@ -115,8 +109,7 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
// Validate block Time // Validate block Time
if block.Height > 1 { if block.Height > 1 {
if !block.Time.After(state.LastBlockTime) { if !block.Time.After(state.LastBlockTime) {
return fmt.Errorf(
"Block time %v not greater than last block time %v",
return fmt.Errorf("Block time %v not greater than last block time %v",
block.Time, block.Time,
state.LastBlockTime, state.LastBlockTime,
) )
@ -124,8 +117,7 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
medianTime := MedianTime(block.LastCommit, state.LastValidators) medianTime := MedianTime(block.LastCommit, state.LastValidators)
if !block.Time.Equal(medianTime) { if !block.Time.Equal(medianTime) {
return fmt.Errorf(
"Invalid block time. Expected %v, got %v",
return fmt.Errorf("Invalid block time. Expected %v, got %v",
medianTime, medianTime,
block.Time, block.Time,
) )
@ -133,8 +125,7 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
} else if block.Height == 1 { } else if block.Height == 1 {
genesisTime := state.LastBlockTime genesisTime := state.LastBlockTime
if !block.Time.Equal(genesisTime) { if !block.Time.Equal(genesisTime) {
return fmt.Errorf(
"Block time %v is not equal to genesis time %v",
return fmt.Errorf("Block time %v is not equal to genesis time %v",
block.Time, block.Time,
genesisTime, genesisTime,
) )
@ -160,8 +151,7 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error {
// a legit address and a known validator. // a legit address and a known validator.
if len(block.ProposerAddress) != crypto.AddressSize || if len(block.ProposerAddress) != crypto.AddressSize ||
!state.Validators.HasAddress(block.ProposerAddress) { !state.Validators.HasAddress(block.ProposerAddress) {
return fmt.Errorf(
"Block.Header.ProposerAddress, %X, is not a validator",
return fmt.Errorf("Block.Header.ProposerAddress, %X, is not a validator",
block.ProposerAddress, block.ProposerAddress,
) )
} }


+ 7
- 4
test/docker/Dockerfile View File

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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


+ 96
- 19
types/block.go View File

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


+ 4
- 2
types/block_test.go View File

@ -80,11 +80,13 @@ func TestBlockValidateBasic(t *testing.T) {
blk.EvidenceHash = []byte("something else") blk.EvidenceHash = []byte("something else")
}, true}, }, true},
} }
for _, tc := range testCases {
for i, tc := range testCases {
t.Run(tc.testName, func(t *testing.T) { t.Run(tc.testName, func(t *testing.T) {
block := MakeBlock(h, txs, commit, evList) block := MakeBlock(h, txs, commit, evList)
block.ProposerAddress = valSet.GetProposer().Address
tc.malleateBlock(block) tc.malleateBlock(block)
assert.Equal(t, tc.expErr, block.ValidateBasic() != nil, "ValidateBasic had an unexpected result")
err = block.ValidateBasic()
assert.Equal(t, tc.expErr, err != nil, "#%d: %v", i, err)
}) })
} }
} }


+ 21
- 0
types/evidence.go View File

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


+ 31
- 0
types/heartbeat.go View File

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

+ 25
- 1
types/part_set.go View File

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


+ 29
- 0
types/proposal.go View File

@ -43,6 +43,35 @@ func NewProposal(height int64, round int, polRound int, blockID BlockID) *Propos
} }
} }
// ValidateBasic performs basic validation.
func (p *Proposal) ValidateBasic() error {
if p.Type != ProposalType {
return errors.New("Invalid Type")
}
if p.Height < 0 {
return errors.New("Negative Height")
}
if p.Round < 0 {
return errors.New("Negative Round")
}
if p.POLRound < -1 {
return errors.New("Negative POLRound (exception: -1)")
}
if err := p.BlockID.ValidateBasic(); err != nil {
return fmt.Errorf("Wrong BlockID: %v", err)
}
// NOTE: Timestamp validation is subtle and handled elsewhere.
if len(p.Signature) == 0 {
return errors.New("Signature is missing")
}
if len(p.Signature) > MaxSignatureSize {
return fmt.Errorf("Signature is too big (max: %d)", MaxSignatureSize)
}
return nil
}
// String returns a string representation of the Proposal. // String returns a string representation of the Proposal.
func (p *Proposal) String() string { func (p *Proposal) String() string {
return fmt.Sprintf("Proposal{%v/%v (%v, %v) %X @ %s}", return fmt.Sprintf("Proposal{%v/%v (%v, %v) %X @ %s}",


+ 12
- 0
types/signable.go View File

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


+ 4
- 5
types/signed_msg_type.go View File

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


+ 40
- 0
types/validation.go View File

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

+ 37
- 1
types/vote.go View File

@ -46,7 +46,8 @@ func NewConflictingVoteError(val *Validator, voteA, voteB *Vote) *ErrVoteConflic
// Address is hex bytes. // Address is hex bytes.
type Address = crypto.Address type Address = crypto.Address
// Represents a prevote, precommit, or commit vote from validators for consensus.
// Vote represents a prevote, precommit, or commit vote from validators for
// consensus.
type Vote struct { type Vote struct {
Type SignedMsgType `json:"type"` Type SignedMsgType `json:"type"`
Height int64 `json:"height"` Height int64 `json:"height"`
@ -108,3 +109,38 @@ func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error {
} }
return nil return nil
} }
// ValidateBasic performs basic validation.
func (vote *Vote) ValidateBasic() error {
if !IsVoteTypeValid(vote.Type) {
return errors.New("Invalid Type")
}
if vote.Height < 0 {
return errors.New("Negative Height")
}
if vote.Round < 0 {
return errors.New("Negative Round")
}
// NOTE: Timestamp validation is subtle and handled elsewhere.
if err := vote.BlockID.ValidateBasic(); err != nil {
return fmt.Errorf("Wrong BlockID: %v", err)
}
if len(vote.ValidatorAddress) != crypto.AddressSize {
return fmt.Errorf("Expected ValidatorAddress size to be %d bytes, got %d bytes",
crypto.AddressSize,
len(vote.ValidatorAddress),
)
}
if vote.ValidatorIndex < 0 {
return errors.New("Negative ValidatorIndex")
}
if len(vote.Signature) == 0 {
return errors.New("Signature is missing")
}
if len(vote.Signature) > MaxSignatureSize {
return fmt.Errorf("Signature is too big (max: %d)", MaxSignatureSize)
}
return nil
}

Loading…
Cancel
Save