Browse Source

Merge pull request #1554 from tendermint/jae/expose_peer_stats

Expose peer stats for dump_consensus_state
pull/1529/head
Ethan Buchman 6 years ago
committed by GitHub
parent
commit
d0229e8b1e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 281 additions and 250 deletions
  1. +6
    -6
      Gopkg.lock
  2. +3
    -3
      Gopkg.toml
  3. +114
    -113
      consensus/reactor.go
  4. +19
    -3
      p2p/node_info.go
  5. +6
    -5
      rpc/client/interface.go
  6. +1
    -1
      rpc/client/rpc_test.go
  7. +127
    -112
      rpc/core/consensus.go
  8. +5
    -7
      rpc/core/types/responses.go

+ 6
- 6
Gopkg.lock View File

@ -254,8 +254,8 @@
[[projects]] [[projects]]
name = "github.com/tendermint/go-amino" name = "github.com/tendermint/go-amino"
packages = ["."] packages = ["."]
revision = "3668c02a8feace009f80754a5e5a8541e5d7b996"
version = "0.9.8"
revision = "ed62928576cfcaf887209dc96142cd79cdfff389"
version = "0.9.9"
[[projects]] [[projects]]
name = "github.com/tendermint/go-crypto" name = "github.com/tendermint/go-crypto"
@ -285,8 +285,8 @@
"pubsub/query", "pubsub/query",
"test" "test"
] ]
revision = "d94e312673e16a11ea55d742cefb3e331228f898"
version = "v0.8.2"
revision = "cc5f287c4798ffe88c04d02df219ecb6932080fd"
version = "v0.8.3-rc0"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -301,7 +301,7 @@
"ripemd160", "ripemd160",
"salsa20/salsa" "salsa20/salsa"
] ]
revision = "b49d69b5da943f7ef3c9cf91c8777c1f78a0cc3c"
revision = "b0697eccbea9adec5b7ba8008f4c33d98d733388"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -384,6 +384,6 @@
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "94cb2543199b0f4b6e9ac0e5b6469bdb77391da1c9f79f5b9792d7af936008ff"
inputs-digest = "52a0dcbebdf8714612444914cfce59a3af8c47c4453a2d43c4ccc5ff1a91d8ea"
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

+ 3
- 3
Gopkg.toml View File

@ -79,11 +79,11 @@
[[constraint]] [[constraint]]
name = "github.com/tendermint/go-amino" name = "github.com/tendermint/go-amino"
version = "~0.9.7"
version = "0.9.9"
[[constraint]]
[[override]]
name = "github.com/tendermint/tmlibs" name = "github.com/tendermint/tmlibs"
version = "~0.8.2-rc0"
version = "~0.8.3-rc0"
[[constraint]] [[constraint]]
name = "google.golang.org/grpc" name = "google.golang.org/grpc"


+ 114
- 113
consensus/reactor.go View File

@ -210,7 +210,7 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
return return
} }
// Peer claims to have a maj23 for some BlockID at H,R,S, // Peer claims to have a maj23 for some BlockID at H,R,S,
err := votes.SetPeerMaj23(msg.Round, msg.Type, ps.Peer.ID(), msg.BlockID)
err := votes.SetPeerMaj23(msg.Round, msg.Type, ps.peer.ID(), msg.BlockID)
if err != nil { if err != nil {
conR.Switch.StopPeerForError(src, err) conR.Switch.StopPeerForError(src, err)
return return
@ -840,41 +840,42 @@ var (
// PeerState contains the known state of a peer, including its connection and // PeerState contains the known state of a peer, including its connection and
// threadsafe access to its PeerRoundState. // threadsafe access to its PeerRoundState.
// NOTE: THIS GETS DUMPED WITH rpc/core/consensus.go.
// Be mindful of what you Expose.
type PeerState struct { type PeerState struct {
Peer p2p.Peer
peer p2p.Peer
logger log.Logger logger log.Logger
mtx sync.Mutex
cstypes.PeerRoundState
stats *peerStateStats
mtx sync.Mutex `json:"-"` // NOTE: Modify below using setters, never directly.
PRS cstypes.PeerRoundState `json:"round_state"` // Exposed.
Stats *peerStateStats `json:"stats"` // Exposed.
} }
// peerStateStats holds internal statistics for a peer. // peerStateStats holds internal statistics for a peer.
type peerStateStats struct { type peerStateStats struct {
lastVoteHeight int64
votes int
lastBlockPartHeight int64
blockParts int
LastVoteHeight int64 `json:"last_vote_height"`
Votes int `json:"votes"`
LastBlockPartHeight int64 `json:"last_block_part_height"`
BlockParts int `json:"block_parts"`
} }
func (pss peerStateStats) String() string { func (pss peerStateStats) String() string {
return fmt.Sprintf("peerStateStats{votes: %d, blockParts: %d}", pss.votes, pss.blockParts)
return fmt.Sprintf("peerStateStats{lvh: %d, votes: %d, lbph: %d, blockParts: %d}",
pss.LastVoteHeight, pss.Votes, pss.LastBlockPartHeight, pss.BlockParts)
} }
// NewPeerState returns a new PeerState for the given Peer // NewPeerState returns a new PeerState for the given Peer
func NewPeerState(peer p2p.Peer) *PeerState { func NewPeerState(peer p2p.Peer) *PeerState {
return &PeerState{ return &PeerState{
Peer: peer,
peer: peer,
logger: log.NewNopLogger(), logger: log.NewNopLogger(),
PeerRoundState: cstypes.PeerRoundState{
PRS: cstypes.PeerRoundState{
Round: -1, Round: -1,
ProposalPOLRound: -1, ProposalPOLRound: -1,
LastCommitRound: -1, LastCommitRound: -1,
CatchupCommitRound: -1, CatchupCommitRound: -1,
}, },
stats: &peerStateStats{},
Stats: &peerStateStats{},
} }
} }
@ -891,16 +892,16 @@ func (ps *PeerState) GetRoundState() *cstypes.PeerRoundState {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
prs := ps.PeerRoundState // copy
prs := ps.PRS // copy
return &prs return &prs
} }
// GetRoundStateJSON returns a json of PeerRoundState, marshalled using go-amino.
func (ps *PeerState) GetRoundStateJSON() ([]byte, error) {
// ToJSON returns a json of PeerState, marshalled using go-amino.
func (ps *PeerState) ToJSON() ([]byte, error) {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
return cdc.MarshalJSON(ps.PeerRoundState)
return cdc.MarshalJSON(ps)
} }
// GetHeight returns an atomic snapshot of the PeerRoundState's height // GetHeight returns an atomic snapshot of the PeerRoundState's height
@ -908,7 +909,7 @@ func (ps *PeerState) GetRoundStateJSON() ([]byte, error) {
func (ps *PeerState) GetHeight() int64 { func (ps *PeerState) GetHeight() int64 {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
return ps.PeerRoundState.Height
return ps.PRS.Height
} }
// SetHasProposal sets the given proposal as known for the peer. // SetHasProposal sets the given proposal as known for the peer.
@ -916,18 +917,18 @@ func (ps *PeerState) SetHasProposal(proposal *types.Proposal) {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
if ps.Height != proposal.Height || ps.Round != proposal.Round {
if ps.PRS.Height != proposal.Height || ps.PRS.Round != proposal.Round {
return return
} }
if ps.Proposal {
if ps.PRS.Proposal {
return return
} }
ps.Proposal = true
ps.ProposalBlockPartsHeader = proposal.BlockPartsHeader
ps.ProposalBlockParts = cmn.NewBitArray(proposal.BlockPartsHeader.Total)
ps.ProposalPOLRound = proposal.POLRound
ps.ProposalPOL = nil // Nil until ProposalPOLMessage received.
ps.PRS.Proposal = true
ps.PRS.ProposalBlockPartsHeader = proposal.BlockPartsHeader
ps.PRS.ProposalBlockParts = cmn.NewBitArray(proposal.BlockPartsHeader.Total)
ps.PRS.ProposalPOLRound = proposal.POLRound
ps.PRS.ProposalPOL = nil // Nil until ProposalPOLMessage received.
} }
// InitProposalBlockParts initializes the peer's proposal block parts header and bit array. // InitProposalBlockParts initializes the peer's proposal block parts header and bit array.
@ -935,12 +936,12 @@ func (ps *PeerState) InitProposalBlockParts(partsHeader types.PartSetHeader) {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
if ps.ProposalBlockParts != nil {
if ps.PRS.ProposalBlockParts != nil {
return return
} }
ps.ProposalBlockPartsHeader = partsHeader
ps.ProposalBlockParts = cmn.NewBitArray(partsHeader.Total)
ps.PRS.ProposalBlockPartsHeader = partsHeader
ps.PRS.ProposalBlockParts = cmn.NewBitArray(partsHeader.Total)
} }
// SetHasProposalBlockPart sets the given block part index as known for the peer. // SetHasProposalBlockPart sets the given block part index as known for the peer.
@ -948,11 +949,11 @@ func (ps *PeerState) SetHasProposalBlockPart(height int64, round int, index int)
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
if ps.Height != height || ps.Round != round {
if ps.PRS.Height != height || ps.PRS.Round != round {
return return
} }
ps.ProposalBlockParts.SetIndex(index, true)
ps.PRS.ProposalBlockParts.SetIndex(index, true)
} }
// PickSendVote picks a vote and sends it to the peer. // PickSendVote picks a vote and sends it to the peer.
@ -961,7 +962,7 @@ func (ps *PeerState) PickSendVote(votes types.VoteSetReader) bool {
if vote, ok := ps.PickVoteToSend(votes); ok { if vote, ok := ps.PickVoteToSend(votes); ok {
msg := &VoteMessage{vote} msg := &VoteMessage{vote}
ps.logger.Debug("Sending vote message", "ps", ps, "vote", vote) ps.logger.Debug("Sending vote message", "ps", ps, "vote", vote)
return ps.Peer.Send(VoteChannel, cdc.MustMarshalBinaryBare(msg))
return ps.peer.Send(VoteChannel, cdc.MustMarshalBinaryBare(msg))
} }
return false return false
} }
@ -1001,40 +1002,40 @@ func (ps *PeerState) getVoteBitArray(height int64, round int, type_ byte) *cmn.B
return nil return nil
} }
if ps.Height == height {
if ps.Round == round {
if ps.PRS.Height == height {
if ps.PRS.Round == round {
switch type_ { switch type_ {
case types.VoteTypePrevote: case types.VoteTypePrevote:
return ps.Prevotes
return ps.PRS.Prevotes
case types.VoteTypePrecommit: case types.VoteTypePrecommit:
return ps.Precommits
return ps.PRS.Precommits
} }
} }
if ps.CatchupCommitRound == round {
if ps.PRS.CatchupCommitRound == round {
switch type_ { switch type_ {
case types.VoteTypePrevote: case types.VoteTypePrevote:
return nil return nil
case types.VoteTypePrecommit: case types.VoteTypePrecommit:
return ps.CatchupCommit
return ps.PRS.CatchupCommit
} }
} }
if ps.ProposalPOLRound == round {
if ps.PRS.ProposalPOLRound == round {
switch type_ { switch type_ {
case types.VoteTypePrevote: case types.VoteTypePrevote:
return ps.ProposalPOL
return ps.PRS.ProposalPOL
case types.VoteTypePrecommit: case types.VoteTypePrecommit:
return nil return nil
} }
} }
return nil return nil
} }
if ps.Height == height+1 {
if ps.LastCommitRound == round {
if ps.PRS.Height == height+1 {
if ps.PRS.LastCommitRound == round {
switch type_ { switch type_ {
case types.VoteTypePrevote: case types.VoteTypePrevote:
return nil return nil
case types.VoteTypePrecommit: case types.VoteTypePrecommit:
return ps.LastCommit
return ps.PRS.LastCommit
} }
} }
return nil return nil
@ -1044,7 +1045,7 @@ func (ps *PeerState) getVoteBitArray(height int64, round int, type_ byte) *cmn.B
// 'round': A round for which we have a +2/3 commit. // 'round': A round for which we have a +2/3 commit.
func (ps *PeerState) ensureCatchupCommitRound(height int64, round int, numValidators int) { func (ps *PeerState) ensureCatchupCommitRound(height int64, round int, numValidators int) {
if ps.Height != height {
if ps.PRS.Height != height {
return return
} }
/* /*
@ -1054,14 +1055,14 @@ func (ps *PeerState) ensureCatchupCommitRound(height int64, round int, numValida
cmn.PanicSanity(cmn.Fmt("Conflicting CatchupCommitRound. Height: %v, Orig: %v, New: %v", height, ps.CatchupCommitRound, round)) cmn.PanicSanity(cmn.Fmt("Conflicting CatchupCommitRound. Height: %v, Orig: %v, New: %v", height, ps.CatchupCommitRound, round))
} }
*/ */
if ps.CatchupCommitRound == round {
if ps.PRS.CatchupCommitRound == round {
return // Nothing to do! return // Nothing to do!
} }
ps.CatchupCommitRound = round
if round == ps.Round {
ps.CatchupCommit = ps.Precommits
ps.PRS.CatchupCommitRound = round
if round == ps.PRS.Round {
ps.PRS.CatchupCommit = ps.PRS.Precommits
} else { } else {
ps.CatchupCommit = cmn.NewBitArray(numValidators)
ps.PRS.CatchupCommit = cmn.NewBitArray(numValidators)
} }
} }
@ -1076,22 +1077,22 @@ func (ps *PeerState) EnsureVoteBitArrays(height int64, numValidators int) {
} }
func (ps *PeerState) ensureVoteBitArrays(height int64, numValidators int) { func (ps *PeerState) ensureVoteBitArrays(height int64, numValidators int) {
if ps.Height == height {
if ps.Prevotes == nil {
ps.Prevotes = cmn.NewBitArray(numValidators)
if ps.PRS.Height == height {
if ps.PRS.Prevotes == nil {
ps.PRS.Prevotes = cmn.NewBitArray(numValidators)
} }
if ps.Precommits == nil {
ps.Precommits = cmn.NewBitArray(numValidators)
if ps.PRS.Precommits == nil {
ps.PRS.Precommits = cmn.NewBitArray(numValidators)
} }
if ps.CatchupCommit == nil {
ps.CatchupCommit = cmn.NewBitArray(numValidators)
if ps.PRS.CatchupCommit == nil {
ps.PRS.CatchupCommit = cmn.NewBitArray(numValidators)
} }
if ps.ProposalPOL == nil {
ps.ProposalPOL = cmn.NewBitArray(numValidators)
if ps.PRS.ProposalPOL == nil {
ps.PRS.ProposalPOL = cmn.NewBitArray(numValidators)
} }
} else if ps.Height == height+1 {
if ps.LastCommit == nil {
ps.LastCommit = cmn.NewBitArray(numValidators)
} else if ps.PRS.Height == height+1 {
if ps.PRS.LastCommit == nil {
ps.PRS.LastCommit = cmn.NewBitArray(numValidators)
} }
} }
} }
@ -1103,12 +1104,12 @@ func (ps *PeerState) RecordVote(vote *types.Vote) int {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
if ps.stats.lastVoteHeight >= vote.Height {
return ps.stats.votes
if ps.Stats.LastVoteHeight >= vote.Height {
return ps.Stats.Votes
} }
ps.stats.lastVoteHeight = vote.Height
ps.stats.votes++
return ps.stats.votes
ps.Stats.LastVoteHeight = vote.Height
ps.Stats.Votes++
return ps.Stats.Votes
} }
// VotesSent returns the number of blocks for which peer has been sending us // VotesSent returns the number of blocks for which peer has been sending us
@ -1117,7 +1118,7 @@ func (ps *PeerState) VotesSent() int {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
return ps.stats.votes
return ps.Stats.Votes
} }
// RecordBlockPart updates internal statistics for this peer by recording the // RecordBlockPart updates internal statistics for this peer by recording the
@ -1128,13 +1129,13 @@ func (ps *PeerState) RecordBlockPart(bp *BlockPartMessage) int {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
if ps.stats.lastBlockPartHeight >= bp.Height {
return ps.stats.blockParts
if ps.Stats.LastBlockPartHeight >= bp.Height {
return ps.Stats.BlockParts
} }
ps.stats.lastBlockPartHeight = bp.Height
ps.stats.blockParts++
return ps.stats.blockParts
ps.Stats.LastBlockPartHeight = bp.Height
ps.Stats.BlockParts++
return ps.Stats.BlockParts
} }
// BlockPartsSent returns the number of blocks for which peer has been sending // BlockPartsSent returns the number of blocks for which peer has been sending
@ -1143,7 +1144,7 @@ func (ps *PeerState) BlockPartsSent() int {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
return ps.stats.blockParts
return ps.Stats.BlockParts
} }
// SetHasVote sets the given vote as known by the peer // SetHasVote sets the given vote as known by the peer
@ -1155,7 +1156,7 @@ func (ps *PeerState) SetHasVote(vote *types.Vote) {
} }
func (ps *PeerState) setHasVote(height int64, round int, type_ byte, index int) { func (ps *PeerState) setHasVote(height int64, round int, type_ byte, index int) {
logger := ps.logger.With("peerH/R", cmn.Fmt("%d/%d", ps.Height, ps.Round), "H/R", cmn.Fmt("%d/%d", height, round))
logger := ps.logger.With("peerH/R", cmn.Fmt("%d/%d", ps.PRS.Height, ps.PRS.Round), "H/R", cmn.Fmt("%d/%d", height, round))
logger.Debug("setHasVote", "type", type_, "index", index) logger.Debug("setHasVote", "type", type_, "index", index)
// NOTE: some may be nil BitArrays -> no side effects. // NOTE: some may be nil BitArrays -> no side effects.
@ -1171,51 +1172,51 @@ func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage) {
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
// Ignore duplicates or decreases // Ignore duplicates or decreases
if CompareHRS(msg.Height, msg.Round, msg.Step, ps.Height, ps.Round, ps.Step) <= 0 {
if CompareHRS(msg.Height, msg.Round, msg.Step, ps.PRS.Height, ps.PRS.Round, ps.PRS.Step) <= 0 {
return return
} }
// Just remember these values. // Just remember these values.
psHeight := ps.Height
psRound := ps.Round
//psStep := ps.Step
psCatchupCommitRound := ps.CatchupCommitRound
psCatchupCommit := ps.CatchupCommit
psHeight := ps.PRS.Height
psRound := ps.PRS.Round
//psStep := ps.PRS.Step
psCatchupCommitRound := ps.PRS.CatchupCommitRound
psCatchupCommit := ps.PRS.CatchupCommit
startTime := time.Now().Add(-1 * time.Duration(msg.SecondsSinceStartTime) * time.Second) startTime := time.Now().Add(-1 * time.Duration(msg.SecondsSinceStartTime) * time.Second)
ps.Height = msg.Height
ps.Round = msg.Round
ps.Step = msg.Step
ps.StartTime = startTime
ps.PRS.Height = msg.Height
ps.PRS.Round = msg.Round
ps.PRS.Step = msg.Step
ps.PRS.StartTime = startTime
if psHeight != msg.Height || psRound != msg.Round { if psHeight != msg.Height || psRound != msg.Round {
ps.Proposal = false
ps.ProposalBlockPartsHeader = types.PartSetHeader{}
ps.ProposalBlockParts = nil
ps.ProposalPOLRound = -1
ps.ProposalPOL = nil
ps.PRS.Proposal = false
ps.PRS.ProposalBlockPartsHeader = types.PartSetHeader{}
ps.PRS.ProposalBlockParts = nil
ps.PRS.ProposalPOLRound = -1
ps.PRS.ProposalPOL = nil
// We'll update the BitArray capacity later. // We'll update the BitArray capacity later.
ps.Prevotes = nil
ps.Precommits = nil
ps.PRS.Prevotes = nil
ps.PRS.Precommits = nil
} }
if psHeight == msg.Height && psRound != msg.Round && msg.Round == psCatchupCommitRound { if psHeight == msg.Height && psRound != msg.Round && msg.Round == psCatchupCommitRound {
// Peer caught up to CatchupCommitRound. // Peer caught up to CatchupCommitRound.
// Preserve psCatchupCommit! // Preserve psCatchupCommit!
// NOTE: We prefer to use prs.Precommits if // NOTE: We prefer to use prs.Precommits if
// pr.Round matches pr.CatchupCommitRound. // pr.Round matches pr.CatchupCommitRound.
ps.Precommits = psCatchupCommit
ps.PRS.Precommits = psCatchupCommit
} }
if psHeight != msg.Height { if psHeight != msg.Height {
// Shift Precommits to LastCommit. // Shift Precommits to LastCommit.
if psHeight+1 == msg.Height && psRound == msg.LastCommitRound { if psHeight+1 == msg.Height && psRound == msg.LastCommitRound {
ps.LastCommitRound = msg.LastCommitRound
ps.LastCommit = ps.Precommits
ps.PRS.LastCommitRound = msg.LastCommitRound
ps.PRS.LastCommit = ps.PRS.Precommits
} else { } else {
ps.LastCommitRound = msg.LastCommitRound
ps.LastCommit = nil
ps.PRS.LastCommitRound = msg.LastCommitRound
ps.PRS.LastCommit = nil
} }
// We'll update the BitArray capacity later. // We'll update the BitArray capacity later.
ps.CatchupCommitRound = -1
ps.CatchupCommit = nil
ps.PRS.CatchupCommitRound = -1
ps.PRS.CatchupCommit = nil
} }
} }
@ -1224,12 +1225,12 @@ func (ps *PeerState) ApplyCommitStepMessage(msg *CommitStepMessage) {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
if ps.Height != msg.Height {
if ps.PRS.Height != msg.Height {
return return
} }
ps.ProposalBlockPartsHeader = msg.BlockPartsHeader
ps.ProposalBlockParts = msg.BlockParts
ps.PRS.ProposalBlockPartsHeader = msg.BlockPartsHeader
ps.PRS.ProposalBlockParts = msg.BlockParts
} }
// ApplyProposalPOLMessage updates the peer state for the new proposal POL. // ApplyProposalPOLMessage updates the peer state for the new proposal POL.
@ -1237,16 +1238,16 @@ func (ps *PeerState) ApplyProposalPOLMessage(msg *ProposalPOLMessage) {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
if ps.Height != msg.Height {
if ps.PRS.Height != msg.Height {
return return
} }
if ps.ProposalPOLRound != msg.ProposalPOLRound {
if ps.PRS.ProposalPOLRound != msg.ProposalPOLRound {
return return
} }
// TODO: Merge onto existing ps.ProposalPOL?
// TODO: Merge onto existing ps.PRS.ProposalPOL?
// We might have sent some prevotes in the meantime. // We might have sent some prevotes in the meantime.
ps.ProposalPOL = msg.ProposalPOL
ps.PRS.ProposalPOL = msg.ProposalPOL
} }
// ApplyHasVoteMessage updates the peer state for the new vote. // ApplyHasVoteMessage updates the peer state for the new vote.
@ -1254,7 +1255,7 @@ func (ps *PeerState) ApplyHasVoteMessage(msg *HasVoteMessage) {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
if ps.Height != msg.Height {
if ps.PRS.Height != msg.Height {
return return
} }
@ -1292,13 +1293,13 @@ func (ps *PeerState) StringIndented(indent string) string {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
return fmt.Sprintf(`PeerState{ return fmt.Sprintf(`PeerState{
%s Key %v
%s PRS %v
%s Stats %v
%s Key %v
%s RoundState %v
%s Stats %v
%s}`, %s}`,
indent, ps.Peer.ID(),
indent, ps.PeerRoundState.StringIndented(indent+" "),
indent, ps.stats,
indent, ps.peer.ID(),
indent, ps.PRS.StringIndented(indent+" "),
indent, ps.Stats,
indent) indent)
} }


+ 19
- 3
p2p/node_info.go View File

@ -2,9 +2,8 @@ package p2p
import ( import (
"fmt" "fmt"
"strings"
cmn "github.com/tendermint/tmlibs/common" cmn "github.com/tendermint/tmlibs/common"
"strings"
) )
const ( const (
@ -31,7 +30,7 @@ type NodeInfo struct {
Version string `json:"version"` // major.minor.revision Version string `json:"version"` // major.minor.revision
Channels cmn.HexBytes `json:"channels"` // channels this node knows about Channels cmn.HexBytes `json:"channels"` // channels this node knows about
// Sanitize
// ASCIIText fields
Moniker string `json:"moniker"` // arbitrary moniker Moniker string `json:"moniker"` // arbitrary moniker
Other []string `json:"other"` // other application specific data Other []string `json:"other"` // other application specific data
} }
@ -42,11 +41,28 @@ type NodeInfo struct {
// 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
// that can not be resolved to some IP. // that can not be resolved to some IP.
// TODO: constraints for Moniker/Other? Or is that for the UI ? // TODO: constraints for Moniker/Other? Or is that for the UI ?
// JAE: It needs to be done on the client, but to prevent ambiguous
// unicode characters, maybe it's worth sanitizing it here.
// In the future we might want to validate these, once we have a
// name-resolution system up.
// 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
// clients. (e.g. off by default).
func (info NodeInfo) Validate() error { func (info NodeInfo) Validate() 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)
} }
// Sanitize ASCII text fields.
if !cmn.IsASCIIText(info.Moniker) || cmn.ASCIITrim(info.Moniker) == "" {
return fmt.Errorf("info.Moniker must be valid non-empty ASCII text without tabs, but got %v.", info.Moniker)
}
for i, s := range info.Other {
if !cmn.IsASCIIText(s) || cmn.ASCIITrim(s) == "" {
return fmt.Errorf("info.Other[%v] must be valid non-empty ASCII text without tabs, but got %v.", i, s)
}
}
channels := make(map[byte]struct{}) channels := make(map[byte]struct{})
for _, ch := range info.Channels { for _, ch := range info.Channels {
_, ok := channels[ch] _, ok := channels[ch]


+ 6
- 5
rpc/client/interface.go View File

@ -1,5 +1,7 @@
package client
/* /*
package client provides a general purpose interface (Client) for connecting
The client package provides a general purpose interface (Client) for connecting
to a tendermint node, as well as higher-level functionality. to a tendermint node, as well as higher-level functionality.
The main implementation for production code is client.HTTP, which The main implementation for production code is client.HTTP, which
@ -17,7 +19,6 @@ for maximum flexibility and testability, and two implementations,
this package also provides helper functions that work on any Client this package also provides helper functions that work on any Client
implementation. implementation.
*/ */
package client
import ( import (
ctypes "github.com/tendermint/tendermint/rpc/core/types" ctypes "github.com/tendermint/tendermint/rpc/core/types"
@ -29,13 +30,13 @@ import (
// affects the ABCI app. In many cases this will be all we want, // affects the ABCI app. In many cases this will be all we want,
// so we can accept an interface which is easier to mock // so we can accept an interface which is easier to mock
type ABCIClient interface { type ABCIClient interface {
// reading from abci app
// Reading from abci app
ABCIInfo() (*ctypes.ResultABCIInfo, error) ABCIInfo() (*ctypes.ResultABCIInfo, error)
ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQuery, error) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQuery, error)
ABCIQueryWithOptions(path string, data cmn.HexBytes, ABCIQueryWithOptions(path string, data cmn.HexBytes,
opts ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) opts ABCIQueryOptions) (*ctypes.ResultABCIQuery, error)
// writing to abci app
// Writing to abci app
BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error)
BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error)
BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error)
@ -59,7 +60,7 @@ type HistoryClient interface {
} }
type StatusClient interface { type StatusClient interface {
// general chain info
// General chain info
Status() (*ctypes.ResultStatus, error) Status() (*ctypes.ResultStatus, error)
} }


+ 1
- 1
rpc/client/rpc_test.go View File

@ -74,7 +74,7 @@ func TestDumpConsensusState(t *testing.T) {
cons, err := nc.DumpConsensusState() cons, err := nc.DumpConsensusState()
require.Nil(t, err, "%d: %+v", i, err) require.Nil(t, err, "%d: %+v", i, err)
assert.NotEmpty(t, cons.RoundState) assert.NotEmpty(t, cons.RoundState)
assert.Empty(t, cons.PeerRoundStates)
assert.Empty(t, cons.Peers)
} }
} }


+ 127
- 112
rpc/core/consensus.go View File

@ -20,7 +20,7 @@ import (
// state, err := client.Validators() // state, err := client.Validators()
// ``` // ```
// //
// > The above command returns JSON structured like this:
// The above command returns JSON structured like this:
// //
// ```json // ```json
// { // {
@ -58,6 +58,7 @@ func Validators(heightPtr *int64) (*ctypes.ResultValidators, error) {
} }
// DumpConsensusState dumps consensus state. // DumpConsensusState dumps consensus state.
// UNSTABLE
// //
// ```shell // ```shell
// curl 'localhost:46657/dump_consensus_state' // curl 'localhost:46657/dump_consensus_state'
@ -68,131 +69,145 @@ func Validators(heightPtr *int64) (*ctypes.ResultValidators, error) {
// state, err := client.DumpConsensusState() // state, err := client.DumpConsensusState()
// ``` // ```
// //
// > The above command returns JSON structured like this:
// The above command returns JSON structured like this:
// //
// ```json // ```json
//{
// "jsonrpc": "2.0",
// "id": "",
// "result": {
// "round_state": {
// "height": 138,
// "round": 0,
// "step": 1,
// "start_time": "2018-04-27T23:16:34.472087096-04:00",
// "commit_time": "2018-04-27T23:16:33.472087096-04:00",
// "validators": {
// "validators": [
// {
// "address": "5875562FF0FFDECC895C20E32FC14988952E99E7",
// "pub_key": {
// "type": "AC26791624DE60",
// "value": "PpDJRUrLG2RgFqYYjawfn/AcAgacSXpLFrmfYYQnuzE="
// },
// "voting_power": 10,
// "accum": 0
// }
// ],
// "proposer": {
// "address": "5875562FF0FFDECC895C20E32FC14988952E99E7",
// "pub_key": {
// "type": "AC26791624DE60",
// "value": "PpDJRUrLG2RgFqYYjawfn/AcAgacSXpLFrmfYYQnuzE="
// },
// "voting_power": 10,
// "accum": 0
// }
// },
// "proposal": null,
// "proposal_block": null,
// "proposal_block_parts": null,
// "locked_round": 0,
// "locked_block": null,
// "locked_block_parts": null,
// "valid_round": 0,
// "valid_block": null,
// "valid_block_parts": null,
// "votes": [
// {
// "round": 0,
// "prevotes": "_",
// "precommits": "_"
// }
// ],
// "commit_round": -1,
// "last_commit": {
// "votes": [
// "Vote{0:5875562FF0FF 137/00/2(Precommit) 5701C93659EA /ED3588D7AF29.../ @ 2018-04-28T03:16:33.469Z}"
// ],
// "votes_bit_array": "x",
// "peer_maj_23s": {}
// },
// "last_validators": {
// "validators": [
// {
// "address": "5875562FF0FFDECC895C20E32FC14988952E99E7",
// "pub_key": {
// "type": "AC26791624DE60",
// "value": "PpDJRUrLG2RgFqYYjawfn/AcAgacSXpLFrmfYYQnuzE="
// },
// "voting_power": 10,
// "accum": 0
// }
// ],
// "proposer": {
// "address": "5875562FF0FFDECC895C20E32FC14988952E99E7",
// "pub_key": {
// "type": "AC26791624DE60",
// "value": "PpDJRUrLG2RgFqYYjawfn/AcAgacSXpLFrmfYYQnuzE="
// },
// "voting_power": 10,
// "accum": 0
// }
// }
// },
// "peer_round_states": {
// "d4bf26bfa5e390b94d98106ab858abf64db26d48": {
// "Height": 136,
// "Round": 0,
// "Step": 1,
// "StartTime": "2018-04-27T23:16:33.841163812-04:00",
// "Proposal": false,
// "ProposalBlockPartsHeader": {
// "total": 1,
// "hash": "E27F2D13298F7CB14090EE60CD9AB214D2F5161F"
// },
// "ProposalBlockParts": "x",
// "ProposalPOLRound": -1,
// "ProposalPOL": "_",
// "Prevotes": "_",
// "Precommits": "x",
// "LastCommitRound": 0,
// "LastCommit": null,
// "CatchupCommitRound": 0,
// "CatchupCommit": "_"
// }
// }
// }
//}
// {
// "jsonrpc": "2.0",
// "id": "",
// "result": {
// "round_state": {
// "height": 7185,
// "round": 0,
// "step": 1,
// "start_time": "2018-05-12T13:57:28.440293621-07:00",
// "commit_time": "2018-05-12T13:57:27.440293621-07:00",
// "validators": {
// "validators": [
// {
// "address": "B5B3D40BE53982AD294EF99FF5A34C0C3E5A3244",
// "pub_key": {
// "type": "AC26791624DE60",
// "value": "SBctdhRBcXtBgdI/8a/alTsUhGXqGs9k5ylV1u5iKHg="
// },
// "voting_power": 10,
// "accum": 0
// }
// ],
// "proposer": {
// "address": "B5B3D40BE53982AD294EF99FF5A34C0C3E5A3244",
// "pub_key": {
// "type": "AC26791624DE60",
// "value": "SBctdhRBcXtBgdI/8a/alTsUhGXqGs9k5ylV1u5iKHg="
// },
// "voting_power": 10,
// "accum": 0
// }
// },
// "proposal": null,
// "proposal_block": null,
// "proposal_block_parts": null,
// "locked_round": 0,
// "locked_block": null,
// "locked_block_parts": null,
// "valid_round": 0,
// "valid_block": null,
// "valid_block_parts": null,
// "votes": [
// {
// "round": 0,
// "prevotes": "_",
// "precommits": "_"
// }
// ],
// "commit_round": -1,
// "last_commit": {
// "votes": [
// "Vote{0:B5B3D40BE539 7184/00/2(Precommit) 14F946FA7EF0 /702B1B1A602A.../ @ 2018-05-12T20:57:27.342Z}"
// ],
// "votes_bit_array": "x",
// "peer_maj_23s": {}
// },
// "last_validators": {
// "validators": [
// {
// "address": "B5B3D40BE53982AD294EF99FF5A34C0C3E5A3244",
// "pub_key": {
// "type": "AC26791624DE60",
// "value": "SBctdhRBcXtBgdI/8a/alTsUhGXqGs9k5ylV1u5iKHg="
// },
// "voting_power": 10,
// "accum": 0
// }
// ],
// "proposer": {
// "address": "B5B3D40BE53982AD294EF99FF5A34C0C3E5A3244",
// "pub_key": {
// "type": "AC26791624DE60",
// "value": "SBctdhRBcXtBgdI/8a/alTsUhGXqGs9k5ylV1u5iKHg="
// },
// "voting_power": 10,
// "accum": 0
// }
// }
// },
// "peers": [
// {
// "node_address": "30ad1854af22506383c3f0e57fb3c7f90984c5e8@172.16.63.221:46656",
// "peer_state": {
// "round_state": {
// "height": 7185,
// "round": 0,
// "step": 1,
// "start_time": "2018-05-12T13:57:27.438039872-07:00",
// "proposal": false,
// "proposal_block_parts_header": {
// "total": 0,
// "hash": ""
// },
// "proposal_block_parts": null,
// "proposal_pol_round": -1,
// "proposal_pol": "_",
// "prevotes": "_",
// "precommits": "_",
// "last_commit_round": 0,
// "last_commit": "x",
// "catchup_commit_round": -1,
// "catchup_commit": "_"
// },
// "stats": {
// "last_vote_height": 7184,
// "votes": 255,
// "last_block_part_height": 7184,
// "block_parts": 255
// }
// }
// }
// ]
// }
// }
// ``` // ```
// UNSTABLE
func DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { func DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
// Get Peer consensus states.
peers := p2pSwitch.Peers().List() peers := p2pSwitch.Peers().List()
peerRoundStates := make([]ctypes.PeerRoundState, len(peers))
peerStates := make([]ctypes.PeerStateInfo, len(peers))
for i, peer := range peers { for i, peer := range peers {
peerState := peer.Get(types.PeerStateKey).(*cm.PeerState) peerState := peer.Get(types.PeerStateKey).(*cm.PeerState)
peerRoundState, err := peerState.GetRoundStateJSON()
peerStateJSON, err := peerState.ToJSON()
if err != nil { if err != nil {
return nil, err return nil, err
} }
peerRoundStates[i] = ctypes.PeerRoundState{
NodeAddress: p2p.IDAddressString(peer.ID(), peer.NodeInfo().ListenAddr),
PeerRoundState: peerRoundState,
peerStates[i] = ctypes.PeerStateInfo{
// Peer basic info.
NodeAddress: p2p.IDAddressString(peer.ID(), peer.NodeInfo().ListenAddr),
// Peer consensus state.
PeerState: peerStateJSON,
} }
} }
// Get self round state.
roundState, err := consensusState.GetRoundStateJSON() roundState, err := consensusState.GetRoundStateJSON()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &ctypes.ResultDumpConsensusState{roundState, peerRoundStates}, nil
return &ctypes.ResultDumpConsensusState{roundState, peerStates}, nil
} }

+ 5
- 7
rpc/core/types/responses.go View File

@ -130,15 +130,13 @@ type ResultValidators struct {
// Info about the consensus state. // Info about the consensus state.
// Unstable // Unstable
type ResultDumpConsensusState struct { type ResultDumpConsensusState struct {
RoundState json.RawMessage `json:"round_state"`
PeerRoundStates []PeerRoundState `json:"peer_round_states"`
RoundState json.RawMessage `json:"round_state"`
Peers []PeerStateInfo `json:"peers"`
} }
// Raw JSON for the PeerRoundState
// Unstable
type PeerRoundState struct {
NodeAddress string `json:"node_address"`
PeerRoundState json.RawMessage `json:"peer_round_state"`
type PeerStateInfo struct {
NodeAddress string `json:"node_address"`
PeerState json.RawMessage `json:"peer_state"`
} }
// CheckTx result // CheckTx result


Loading…
Cancel
Save