fixes from review

This commit is contained in:
Ethan Buchman 2018-05-14 10:33:31 -04:00
parent a41f0d3891
commit e9804d76cf
8 changed files with 110 additions and 33 deletions

View File

@ -174,6 +174,26 @@ func (hvs *HeightVoteSet) getVoteSet(round int, type_ byte) *types.VoteSet {
} }
} }
// If a peer claims that it has 2/3 majority for given blockKey, call this.
// NOTE: if there are too many peers, or too much peer churn,
// this can cause memory issues.
// TODO: implement ability to remove peers too
func (hvs *HeightVoteSet) SetPeerMaj23(round int, type_ byte, peerID p2p.ID, blockID types.BlockID) error {
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
if !types.IsVoteTypeValid(type_) {
return fmt.Errorf("SetPeerMaj23: Invalid vote type %v", type_)
}
voteSet := hvs.getVoteSet(round, type_)
if voteSet == nil {
return nil // something we don't know about yet
}
return voteSet.SetPeerMaj23(types.P2PID(peerID), blockID)
}
//---------------------------------------------------------
// string and json
func (hvs *HeightVoteSet) String() string { func (hvs *HeightVoteSet) String() string {
return hvs.StringIndented("") return hvs.StringIndented("")
} }
@ -207,29 +227,20 @@ func (hvs *HeightVoteSet) StringIndented(indent string) string {
indent) indent)
} }
// `"__xx_xx____x:46/100:0.46"`
type roundVotes struct {
Round int `json:"round"`
Prevotes []string `json:"prevotes"`
PrevotesBitArray string `json:"prevotes_bit_array"`
Precommits []string `json:"precommits"`
PrecommitsBitArray string `json:"precommits_bit_array"`
}
func (hvs *HeightVoteSet) MarshalJSON() ([]byte, error) { func (hvs *HeightVoteSet) MarshalJSON() ([]byte, error) {
hvs.mtx.Lock() hvs.mtx.Lock()
defer hvs.mtx.Unlock() defer hvs.mtx.Unlock()
roundsVotes := hvs.toRoundVotes() allVotes := hvs.toAllRoundVotes()
return cdc.MarshalJSON(roundsVotes) return cdc.MarshalJSON(allVotes)
} }
func (hvs *HeightVoteSet) toRoundVotes() []roundVotes { func (hvs *HeightVoteSet) toAllRoundVotes() []roundVotes {
totalRounds := hvs.round + 1 totalRounds := hvs.round + 1
roundsVotes := make([]roundVotes, totalRounds) allVotes := make([]roundVotes, totalRounds)
// rounds 0 ~ hvs.round inclusive // rounds 0 ~ hvs.round inclusive
for round := 0; round < totalRounds; round++ { for round := 0; round < totalRounds; round++ {
roundsVotes[round] = roundVotes{ allVotes[round] = roundVotes{
Round: round, Round: round,
Prevotes: hvs.roundVoteSets[round].Prevotes.VoteStrings(), Prevotes: hvs.roundVoteSets[round].Prevotes.VoteStrings(),
PrevotesBitArray: hvs.roundVoteSets[round].Prevotes.BitArrayString(), PrevotesBitArray: hvs.roundVoteSets[round].Prevotes.BitArrayString(),
@ -238,22 +249,13 @@ func (hvs *HeightVoteSet) toRoundVotes() []roundVotes {
} }
} }
// TODO: all other peer catchup rounds // TODO: all other peer catchup rounds
return roundsVotes return allVotes
} }
// If a peer claims that it has 2/3 majority for given blockKey, call this. type roundVotes struct {
// NOTE: if there are too many peers, or too much peer churn, Round int `json:"round"`
// this can cause memory issues. Prevotes []string `json:"prevotes"`
// TODO: implement ability to remove peers too PrevotesBitArray string `json:"prevotes_bit_array"`
func (hvs *HeightVoteSet) SetPeerMaj23(round int, type_ byte, peerID p2p.ID, blockID types.BlockID) error { Precommits []string `json:"precommits"`
hvs.mtx.Lock() PrecommitsBitArray string `json:"precommits_bit_array"`
defer hvs.mtx.Unlock()
if !types.IsVoteTypeValid(type_) {
return fmt.Errorf("SetPeerMaj23: Invalid vote type %v", type_)
}
voteSet := hvs.getVoteSet(round, type_)
if voteSet == nil {
return nil // something we don't know about yet
}
return voteSet.SetPeerMaj23(types.P2PID(peerID), blockID)
} }

View File

@ -79,6 +79,7 @@ type RoundState struct {
LastValidators *types.ValidatorSet `json:"last_validators"` LastValidators *types.ValidatorSet `json:"last_validators"`
} }
// Compressed version of the RoundState for use in RPC
type RoundStateSimple struct { type RoundStateSimple struct {
HeightRoundStep string `json:"height/round/step"` HeightRoundStep string `json:"height/round/step"`
StartTime time.Time `json:"start_time"` StartTime time.Time `json:"start_time"`
@ -88,8 +89,12 @@ type RoundStateSimple struct {
Votes json.RawMessage `json:"height_vote_set"` Votes json.RawMessage `json:"height_vote_set"`
} }
// Compress the RoundState to RoundStateSimple
func (rs *RoundState) RoundStateSimple() RoundStateSimple { func (rs *RoundState) RoundStateSimple() RoundStateSimple {
votesJSON, _ := rs.Votes.MarshalJSON() // TODO err votesJSON, err := rs.Votes.MarshalJSON()
if err != nil {
panic(err)
}
return RoundStateSimple{ return RoundStateSimple{
HeightRoundStep: fmt.Sprintf("%d/%d/%d", rs.Height, rs.Round, rs.Step), HeightRoundStep: fmt.Sprintf("%d/%d/%d", rs.Height, rs.Round, rs.Step),
StartTime: rs.StartTime, StartTime: rs.StartTime,

View File

@ -126,6 +126,15 @@ func (c *HTTP) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
return result, nil return result, nil
} }
func (c *HTTP) ConsensusState() (*ctypes.ResultConsensusState, error) {
result := new(ctypes.ResultConsensusState)
_, err := c.rpc.Call("consensus_state", map[string]interface{}{}, result)
if err != nil {
return nil, errors.Wrap(err, "ConsensusState")
}
return result, nil
}
func (c *HTTP) Health() (*ctypes.ResultHealth, error) { func (c *HTTP) Health() (*ctypes.ResultHealth, error) {
result := new(ctypes.ResultHealth) result := new(ctypes.ResultHealth)
_, err := c.rpc.Call("health", map[string]interface{}{}, result) _, err := c.rpc.Call("health", map[string]interface{}{}, result)

View File

@ -84,6 +84,7 @@ type Client interface {
type NetworkClient interface { type NetworkClient interface {
NetInfo() (*ctypes.ResultNetInfo, error) NetInfo() (*ctypes.ResultNetInfo, error)
DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error)
ConsensusState() (*ctypes.ResultConsensusState, error)
Health() (*ctypes.ResultHealth, error) Health() (*ctypes.ResultHealth, error)
} }

View File

@ -84,6 +84,10 @@ func (Local) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
return core.DumpConsensusState() return core.DumpConsensusState()
} }
func (Local) ConsensusState() (*ctypes.ResultConsensusState, error) {
return core.ConsensusState()
}
func (Local) Health() (*ctypes.ResultHealth, error) { func (Local) Health() (*ctypes.ResultHealth, error) {
return core.Health() return core.Health()
} }

View File

@ -78,6 +78,17 @@ func TestDumpConsensusState(t *testing.T) {
} }
} }
func TestConsensusState(t *testing.T) {
for i, c := range GetClients() {
// FIXME: fix server so it doesn't panic on invalid input
nc, ok := c.(client.NetworkClient)
require.True(t, ok, "%d", i)
cons, err := nc.ConsensusState()
require.Nil(t, err, "%d: %+v", i, err)
assert.NotEmpty(t, cons.RoundState)
}
}
func TestHealth(t *testing.T) { func TestHealth(t *testing.T) {
for i, c := range GetClients() { for i, c := range GetClients() {
nc, ok := c.(client.NetworkClient) nc, ok := c.(client.NetworkClient)

View File

@ -212,7 +212,48 @@ func DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
return &ctypes.ResultDumpConsensusState{roundState, peerStates}, nil return &ctypes.ResultDumpConsensusState{roundState, peerStates}, nil
} }
// ConsensusState returns a concise summary of the consensus state.
// UNSTABLE // UNSTABLE
//
// ```shell
// curl 'localhost:46657/consensus_state'
// ```
//
// ```go
// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket")
// state, err := client.ConsensusState()
// ```
//
// The above command returns JSON structured like this:
//
// ```json
//{
// "jsonrpc": "2.0",
// "id": "",
// "result": {
// "round_state": {
// "height/round/step": "9336/0/1",
// "start_time": "2018-05-14T10:25:45.72595357-04:00",
// "proposal_block_hash": "",
// "locked_block_hash": "",
// "valid_block_hash": "",
// "height_vote_set": [
// {
// "round": 0,
// "prevotes": [
// "nil-Vote"
// ],
// "prevotes_bit_array": "BA{1:_} 0/10 = 0.00",
// "precommits": [
// "nil-Vote"
// ],
// "precommits_bit_array": "BA{1:_} 0/10 = 0.00"
// }
// ]
// }
// }
//}
//```
func ConsensusState() (*ctypes.ResultConsensusState, error) { func ConsensusState() (*ctypes.ResultConsensusState, error) {
// Get self round state. // Get self round state.
bz, err := consensusState.GetRoundStateSimpleJSON() bz, err := consensusState.GetRoundStateSimpleJSON()

View File

@ -418,6 +418,9 @@ func (voteSet *VoteSet) TwoThirdsMajority() (blockID BlockID, ok bool) {
return BlockID{}, false return BlockID{}, false
} }
//--------------------------------------------------------------------------------
// Strings and JSON
func (voteSet *VoteSet) String() string { func (voteSet *VoteSet) String() string {
if voteSet == nil { if voteSet == nil {
return "nil-VoteSet" return "nil-VoteSet"
@ -470,8 +473,9 @@ type VoteSetJSON struct {
PeerMaj23s map[P2PID]BlockID `json:"peer_maj_23s"` PeerMaj23s map[P2PID]BlockID `json:"peer_maj_23s"`
} }
// Return the bit-array of votes including the fraction of power // Return the bit-array of votes including
// that has voted eg. "__x_xxx_:6/20 = 0.3". // the fraction of power that has voted like:
// "BA{29:xx__x__x_x___x__x_______xxx__} 856/1304 = 0.66"
func (voteSet *VoteSet) BitArrayString() string { func (voteSet *VoteSet) BitArrayString() string {
voteSet.mtx.Lock() voteSet.mtx.Lock()
defer voteSet.mtx.Unlock() defer voteSet.mtx.Unlock()