You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1244 lines
36 KiB

10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. package consensus
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "reflect"
  7. "sync"
  8. "time"
  9. . "github.com/tendermint/go-common"
  10. "github.com/tendermint/go-p2p"
  11. "github.com/tendermint/go-wire"
  12. sm "github.com/tendermint/tendermint/state"
  13. "github.com/tendermint/tendermint/types"
  14. )
  15. const (
  16. StateChannel = byte(0x20)
  17. DataChannel = byte(0x21)
  18. VoteChannel = byte(0x22)
  19. VoteSetBitsChannel = byte(0x23)
  20. peerGossipSleepDuration = 100 * time.Millisecond // Time to sleep if there's nothing to send.
  21. peerQueryMaj23SleepDuration = 2 * time.Second // Time to sleep after each VoteSetMaj23Message sent
  22. maxConsensusMessageSize = 1048576 // 1MB; NOTE: keep in sync with types.PartSet sizes.
  23. )
  24. //-----------------------------------------------------------------------------
  25. type ConsensusReactor struct {
  26. p2p.BaseReactor // BaseService + p2p.Switch
  27. conS *ConsensusState
  28. fastSync bool
  29. evsw types.EventSwitch
  30. }
  31. func NewConsensusReactor(consensusState *ConsensusState, fastSync bool) *ConsensusReactor {
  32. conR := &ConsensusReactor{
  33. conS: consensusState,
  34. fastSync: fastSync,
  35. }
  36. conR.BaseReactor = *p2p.NewBaseReactor(log, "ConsensusReactor", conR)
  37. return conR
  38. }
  39. func (conR *ConsensusReactor) OnStart() error {
  40. log.Notice("ConsensusReactor ", "fastSync", conR.fastSync)
  41. conR.BaseReactor.OnStart()
  42. // callbacks for broadcasting new steps and votes to peers
  43. // upon their respective events (ie. uses evsw)
  44. conR.registerEventCallbacks()
  45. if !conR.fastSync {
  46. _, err := conR.conS.Start()
  47. if err != nil {
  48. return err
  49. }
  50. }
  51. return nil
  52. }
  53. func (conR *ConsensusReactor) OnStop() {
  54. conR.BaseReactor.OnStop()
  55. conR.conS.Stop()
  56. }
  57. // Switch from the fast_sync to the consensus:
  58. // reset the state, turn off fast_sync, start the consensus-state-machine
  59. func (conR *ConsensusReactor) SwitchToConsensus(state *sm.State) {
  60. log.Notice("SwitchToConsensus")
  61. conR.conS.reconstructLastCommit(state)
  62. // NOTE: The line below causes broadcastNewRoundStepRoutine() to
  63. // broadcast a NewRoundStepMessage.
  64. conR.conS.updateToState(state)
  65. conR.fastSync = false
  66. conR.conS.Start()
  67. }
  68. // Implements Reactor
  69. func (conR *ConsensusReactor) GetChannels() []*p2p.ChannelDescriptor {
  70. // TODO optimize
  71. return []*p2p.ChannelDescriptor{
  72. &p2p.ChannelDescriptor{
  73. ID: StateChannel,
  74. Priority: 5,
  75. SendQueueCapacity: 100,
  76. },
  77. &p2p.ChannelDescriptor{
  78. ID: DataChannel, // maybe split between gossiping current block and catchup stuff
  79. Priority: 10, // once we gossip the whole block there's nothing left to send until next height or round
  80. SendQueueCapacity: 100,
  81. RecvBufferCapacity: 50 * 4096,
  82. },
  83. &p2p.ChannelDescriptor{
  84. ID: VoteChannel,
  85. Priority: 5,
  86. SendQueueCapacity: 100,
  87. RecvBufferCapacity: 100 * 100,
  88. },
  89. &p2p.ChannelDescriptor{
  90. ID: VoteSetBitsChannel,
  91. Priority: 1,
  92. SendQueueCapacity: 2,
  93. RecvBufferCapacity: 1024,
  94. },
  95. }
  96. }
  97. // Implements Reactor
  98. func (conR *ConsensusReactor) AddPeer(peer *p2p.Peer) {
  99. if !conR.IsRunning() {
  100. return
  101. }
  102. // Create peerState for peer
  103. peerState := NewPeerState(peer)
  104. peer.Data.Set(types.PeerStateKey, peerState)
  105. // Begin routines for this peer.
  106. go conR.gossipDataRoutine(peer, peerState)
  107. go conR.gossipVotesRoutine(peer, peerState)
  108. go conR.queryMaj23Routine(peer, peerState)
  109. // Send our state to peer.
  110. // If we're fast_syncing, broadcast a RoundStepMessage later upon SwitchToConsensus().
  111. if !conR.fastSync {
  112. conR.sendNewRoundStepMessage(peer)
  113. }
  114. }
  115. // Implements Reactor
  116. func (conR *ConsensusReactor) RemovePeer(peer *p2p.Peer, reason interface{}) {
  117. if !conR.IsRunning() {
  118. return
  119. }
  120. // TODO
  121. //peer.Data.Get(PeerStateKey).(*PeerState).Disconnect()
  122. }
  123. // Implements Reactor
  124. // NOTE: We process these messages even when we're fast_syncing.
  125. // Messages affect either a peer state or the consensus state.
  126. // Peer state updates can happen in parallel, but processing of
  127. // proposals, block parts, and votes are ordered by the receiveRoutine
  128. // NOTE: blocks on consensus state for proposals, block parts, and votes
  129. func (conR *ConsensusReactor) Receive(chID byte, src *p2p.Peer, msgBytes []byte) {
  130. if !conR.IsRunning() {
  131. log.Debug("Receive", "src", src, "chId", chID, "bytes", msgBytes)
  132. return
  133. }
  134. _, msg, err := DecodeMessage(msgBytes)
  135. if err != nil {
  136. log.Warn("Error decoding message", "src", src, "chId", chID, "msg", msg, "error", err, "bytes", msgBytes)
  137. // TODO punish peer?
  138. return
  139. }
  140. log.Debug("Receive", "src", src, "chId", chID, "msg", msg)
  141. // Get peer states
  142. ps := src.Data.Get(types.PeerStateKey).(*PeerState)
  143. switch chID {
  144. case StateChannel:
  145. switch msg := msg.(type) {
  146. case *NewRoundStepMessage:
  147. ps.ApplyNewRoundStepMessage(msg)
  148. case *CommitStepMessage:
  149. ps.ApplyCommitStepMessage(msg)
  150. case *HasVoteMessage:
  151. ps.ApplyHasVoteMessage(msg)
  152. case *VoteSetMaj23Message:
  153. cs := conR.conS
  154. cs.mtx.Lock()
  155. height, votes := cs.Height, cs.Votes
  156. cs.mtx.Unlock()
  157. if height != msg.Height {
  158. return
  159. }
  160. // Peer claims to have a maj23 for some BlockID at H,R,S,
  161. votes.SetPeerMaj23(msg.Round, msg.Type, ps.Peer.Key, msg.BlockID)
  162. // Respond with a VoteSetBitsMessage showing which votes we have.
  163. // (and consequently shows which we don't have)
  164. var ourVotes *BitArray
  165. switch msg.Type {
  166. case types.VoteTypePrevote:
  167. ourVotes = votes.Prevotes(msg.Round).BitArrayByBlockID(msg.BlockID)
  168. case types.VoteTypePrecommit:
  169. ourVotes = votes.Precommits(msg.Round).BitArrayByBlockID(msg.BlockID)
  170. default:
  171. log.Warn("Bad VoteSetBitsMessage field Type")
  172. return
  173. }
  174. src.TrySend(VoteSetBitsChannel, struct{ ConsensusMessage }{&VoteSetBitsMessage{
  175. Height: msg.Height,
  176. Round: msg.Round,
  177. Type: msg.Type,
  178. BlockID: msg.BlockID,
  179. Votes: ourVotes,
  180. }})
  181. default:
  182. log.Warn(Fmt("Unknown message type %v", reflect.TypeOf(msg)))
  183. }
  184. case DataChannel:
  185. if conR.fastSync {
  186. log.Warn("Ignoring message received during fastSync", "msg", msg)
  187. return
  188. }
  189. switch msg := msg.(type) {
  190. case *ProposalMessage:
  191. ps.SetHasProposal(msg.Proposal)
  192. conR.conS.peerMsgQueue <- msgInfo{msg, src.Key}
  193. case *ProposalPOLMessage:
  194. ps.ApplyProposalPOLMessage(msg)
  195. case *BlockPartMessage:
  196. ps.SetHasProposalBlockPart(msg.Height, msg.Round, msg.Part.Index)
  197. conR.conS.peerMsgQueue <- msgInfo{msg, src.Key}
  198. default:
  199. log.Warn(Fmt("Unknown message type %v", reflect.TypeOf(msg)))
  200. }
  201. case VoteChannel:
  202. if conR.fastSync {
  203. log.Warn("Ignoring message received during fastSync", "msg", msg)
  204. return
  205. }
  206. switch msg := msg.(type) {
  207. case *VoteMessage:
  208. cs := conR.conS
  209. cs.mtx.Lock()
  210. height, valSize, lastCommitSize := cs.Height, cs.Validators.Size(), cs.LastCommit.Size()
  211. cs.mtx.Unlock()
  212. ps.EnsureVoteBitArrays(height, valSize)
  213. ps.EnsureVoteBitArrays(height-1, lastCommitSize)
  214. ps.SetHasVote(msg.Vote)
  215. conR.conS.peerMsgQueue <- msgInfo{msg, src.Key}
  216. default:
  217. // don't punish (leave room for soft upgrades)
  218. log.Warn(Fmt("Unknown message type %v", reflect.TypeOf(msg)))
  219. }
  220. case VoteSetBitsChannel:
  221. if conR.fastSync {
  222. log.Warn("Ignoring message received during fastSync", "msg", msg)
  223. return
  224. }
  225. switch msg := msg.(type) {
  226. case *VoteSetBitsMessage:
  227. cs := conR.conS
  228. cs.mtx.Lock()
  229. height, votes := cs.Height, cs.Votes
  230. cs.mtx.Unlock()
  231. if height == msg.Height {
  232. var ourVotes *BitArray
  233. switch msg.Type {
  234. case types.VoteTypePrevote:
  235. ourVotes = votes.Prevotes(msg.Round).BitArrayByBlockID(msg.BlockID)
  236. case types.VoteTypePrecommit:
  237. ourVotes = votes.Precommits(msg.Round).BitArrayByBlockID(msg.BlockID)
  238. default:
  239. log.Warn("Bad VoteSetBitsMessage field Type")
  240. return
  241. }
  242. ps.ApplyVoteSetBitsMessage(msg, ourVotes)
  243. } else {
  244. ps.ApplyVoteSetBitsMessage(msg, nil)
  245. }
  246. default:
  247. // don't punish (leave room for soft upgrades)
  248. log.Warn(Fmt("Unknown message type %v", reflect.TypeOf(msg)))
  249. }
  250. default:
  251. log.Warn(Fmt("Unknown chId %X", chID))
  252. }
  253. if err != nil {
  254. log.Warn("Error in Receive()", "error", err)
  255. }
  256. }
  257. // Sets our private validator account for signing votes.
  258. func (conR *ConsensusReactor) SetPrivValidator(priv PrivValidator) {
  259. conR.conS.SetPrivValidator(priv)
  260. }
  261. // implements events.Eventable
  262. func (conR *ConsensusReactor) SetEventSwitch(evsw types.EventSwitch) {
  263. conR.evsw = evsw
  264. conR.conS.SetEventSwitch(evsw)
  265. }
  266. //--------------------------------------
  267. // Listens for new steps and votes,
  268. // broadcasting the result to peers
  269. func (conR *ConsensusReactor) registerEventCallbacks() {
  270. types.AddListenerForEvent(conR.evsw, "conR", types.EventStringNewRoundStep(), func(data types.TMEventData) {
  271. rs := data.(types.EventDataRoundState).RoundState.(*RoundState)
  272. conR.broadcastNewRoundStep(rs)
  273. })
  274. types.AddListenerForEvent(conR.evsw, "conR", types.EventStringVote(), func(data types.TMEventData) {
  275. edv := data.(types.EventDataVote)
  276. conR.broadcastHasVoteMessage(edv.Vote)
  277. })
  278. }
  279. func (conR *ConsensusReactor) broadcastNewRoundStep(rs *RoundState) {
  280. nrsMsg, csMsg := makeRoundStepMessages(rs)
  281. if nrsMsg != nil {
  282. conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{nrsMsg})
  283. }
  284. if csMsg != nil {
  285. conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{csMsg})
  286. }
  287. }
  288. // Broadcasts HasVoteMessage to peers that care.
  289. func (conR *ConsensusReactor) broadcastHasVoteMessage(vote *types.Vote) {
  290. msg := &HasVoteMessage{
  291. Height: vote.Height,
  292. Round: vote.Round,
  293. Type: vote.Type,
  294. Index: vote.ValidatorIndex,
  295. }
  296. conR.Switch.Broadcast(StateChannel, struct{ ConsensusMessage }{msg})
  297. /*
  298. // TODO: Make this broadcast more selective.
  299. for _, peer := range conR.Switch.Peers().List() {
  300. ps := peer.Data.Get(PeerStateKey).(*PeerState)
  301. prs := ps.GetRoundState()
  302. if prs.Height == vote.Height {
  303. // TODO: Also filter on round?
  304. peer.TrySend(StateChannel, struct{ ConsensusMessage }{msg})
  305. } else {
  306. // Height doesn't match
  307. // TODO: check a field, maybe CatchupCommitRound?
  308. // TODO: But that requires changing the struct field comment.
  309. }
  310. }
  311. */
  312. }
  313. func makeRoundStepMessages(rs *RoundState) (nrsMsg *NewRoundStepMessage, csMsg *CommitStepMessage) {
  314. nrsMsg = &NewRoundStepMessage{
  315. Height: rs.Height,
  316. Round: rs.Round,
  317. Step: rs.Step,
  318. SecondsSinceStartTime: int(time.Now().Sub(rs.StartTime).Seconds()),
  319. LastCommitRound: rs.LastCommit.Round(),
  320. }
  321. if rs.Step == RoundStepCommit {
  322. csMsg = &CommitStepMessage{
  323. Height: rs.Height,
  324. BlockPartsHeader: rs.ProposalBlockParts.Header(),
  325. BlockParts: rs.ProposalBlockParts.BitArray(),
  326. }
  327. }
  328. return
  329. }
  330. func (conR *ConsensusReactor) sendNewRoundStepMessage(peer *p2p.Peer) {
  331. rs := conR.conS.GetRoundState()
  332. nrsMsg, csMsg := makeRoundStepMessages(rs)
  333. if nrsMsg != nil {
  334. peer.Send(StateChannel, struct{ ConsensusMessage }{nrsMsg})
  335. }
  336. if csMsg != nil {
  337. peer.Send(StateChannel, struct{ ConsensusMessage }{csMsg})
  338. }
  339. }
  340. func (conR *ConsensusReactor) gossipDataRoutine(peer *p2p.Peer, ps *PeerState) {
  341. log := log.New("peer", peer)
  342. OUTER_LOOP:
  343. for {
  344. // Manage disconnects from self or peer.
  345. if !peer.IsRunning() || !conR.IsRunning() {
  346. log.Notice(Fmt("Stopping gossipDataRoutine for %v.", peer))
  347. return
  348. }
  349. rs := conR.conS.GetRoundState()
  350. prs := ps.GetRoundState()
  351. // Send proposal Block parts?
  352. if rs.ProposalBlockParts.HasHeader(prs.ProposalBlockPartsHeader) {
  353. //log.Info("ProposalBlockParts matched", "blockParts", prs.ProposalBlockParts)
  354. if index, ok := rs.ProposalBlockParts.BitArray().Sub(prs.ProposalBlockParts.Copy()).PickRandom(); ok {
  355. part := rs.ProposalBlockParts.GetPart(index)
  356. msg := &BlockPartMessage{
  357. Height: rs.Height, // This tells peer that this part applies to us.
  358. Round: rs.Round, // This tells peer that this part applies to us.
  359. Part: part,
  360. }
  361. peer.Send(DataChannel, struct{ ConsensusMessage }{msg})
  362. ps.SetHasProposalBlockPart(prs.Height, prs.Round, index)
  363. continue OUTER_LOOP
  364. }
  365. }
  366. // If the peer is on a previous height, help catch up.
  367. if (0 < prs.Height) && (prs.Height < rs.Height) {
  368. //log.Info("Data catchup", "height", rs.Height, "peerHeight", prs.Height, "peerProposalBlockParts", prs.ProposalBlockParts)
  369. if index, ok := prs.ProposalBlockParts.Not().PickRandom(); ok {
  370. // Ensure that the peer's PartSetHeader is correct
  371. blockMeta := conR.conS.blockStore.LoadBlockMeta(prs.Height)
  372. if blockMeta == nil {
  373. log.Warn("Failed to load block meta", "peer height", prs.Height, "our height", rs.Height, "blockstore height", conR.conS.blockStore.Height(), "pv", conR.conS.privValidator)
  374. time.Sleep(peerGossipSleepDuration)
  375. continue OUTER_LOOP
  376. } else if !blockMeta.PartsHeader.Equals(prs.ProposalBlockPartsHeader) {
  377. log.Info("Peer ProposalBlockPartsHeader mismatch, sleeping",
  378. "peerHeight", prs.Height, "blockPartsHeader", blockMeta.PartsHeader, "peerBlockPartsHeader", prs.ProposalBlockPartsHeader)
  379. time.Sleep(peerGossipSleepDuration)
  380. continue OUTER_LOOP
  381. }
  382. // Load the part
  383. part := conR.conS.blockStore.LoadBlockPart(prs.Height, index)
  384. if part == nil {
  385. log.Warn("Could not load part", "index", index,
  386. "peerHeight", prs.Height, "blockPartsHeader", blockMeta.PartsHeader, "peerBlockPartsHeader", prs.ProposalBlockPartsHeader)
  387. time.Sleep(peerGossipSleepDuration)
  388. continue OUTER_LOOP
  389. }
  390. // Send the part
  391. msg := &BlockPartMessage{
  392. Height: prs.Height, // Not our height, so it doesn't matter.
  393. Round: prs.Round, // Not our height, so it doesn't matter.
  394. Part: part,
  395. }
  396. peer.Send(DataChannel, struct{ ConsensusMessage }{msg})
  397. ps.SetHasProposalBlockPart(prs.Height, prs.Round, index)
  398. continue OUTER_LOOP
  399. } else {
  400. //log.Info("No parts to send in catch-up, sleeping")
  401. time.Sleep(peerGossipSleepDuration)
  402. continue OUTER_LOOP
  403. }
  404. }
  405. // If height and round don't match, sleep.
  406. if (rs.Height != prs.Height) || (rs.Round != prs.Round) {
  407. //log.Info("Peer Height|Round mismatch, sleeping", "peerHeight", prs.Height, "peerRound", prs.Round, "peer", peer)
  408. time.Sleep(peerGossipSleepDuration)
  409. continue OUTER_LOOP
  410. }
  411. // By here, height and round match.
  412. // Proposal block parts were already matched and sent if any were wanted.
  413. // (These can match on hash so the round doesn't matter)
  414. // Now consider sending other things, like the Proposal itself.
  415. // Send Proposal && ProposalPOL BitArray?
  416. if rs.Proposal != nil && !prs.Proposal {
  417. // Proposal: share the proposal metadata with peer.
  418. {
  419. msg := &ProposalMessage{Proposal: rs.Proposal}
  420. peer.Send(DataChannel, struct{ ConsensusMessage }{msg})
  421. ps.SetHasProposal(rs.Proposal)
  422. }
  423. // ProposalPOL: lets peer know which POL votes we have so far.
  424. // Peer must receive ProposalMessage first.
  425. // rs.Proposal was validated, so rs.Proposal.POLRound <= rs.Round,
  426. // so we definitely have rs.Votes.Prevotes(rs.Proposal.POLRound).
  427. if 0 <= rs.Proposal.POLRound {
  428. msg := &ProposalPOLMessage{
  429. Height: rs.Height,
  430. ProposalPOLRound: rs.Proposal.POLRound,
  431. ProposalPOL: rs.Votes.Prevotes(rs.Proposal.POLRound).BitArray(),
  432. }
  433. peer.Send(DataChannel, struct{ ConsensusMessage }{msg})
  434. }
  435. continue OUTER_LOOP
  436. }
  437. // Nothing to do. Sleep.
  438. time.Sleep(peerGossipSleepDuration)
  439. continue OUTER_LOOP
  440. }
  441. }
  442. func (conR *ConsensusReactor) gossipVotesRoutine(peer *p2p.Peer, ps *PeerState) {
  443. log := log.New("peer", peer)
  444. // Simple hack to throttle logs upon sleep.
  445. var sleeping = 0
  446. OUTER_LOOP:
  447. for {
  448. // Manage disconnects from self or peer.
  449. if !peer.IsRunning() || !conR.IsRunning() {
  450. log.Notice(Fmt("Stopping gossipVotesRoutine for %v.", peer))
  451. return
  452. }
  453. rs := conR.conS.GetRoundState()
  454. prs := ps.GetRoundState()
  455. switch sleeping {
  456. case 1: // First sleep
  457. sleeping = 2
  458. case 2: // No more sleep
  459. sleeping = 0
  460. }
  461. //log.Debug("gossipVotesRoutine", "rsHeight", rs.Height, "rsRound", rs.Round,
  462. // "prsHeight", prs.Height, "prsRound", prs.Round, "prsStep", prs.Step)
  463. // If height matches, then send LastCommit, Prevotes, Precommits.
  464. if rs.Height == prs.Height {
  465. // If there are lastCommits to send...
  466. if prs.Step == RoundStepNewHeight {
  467. if ps.PickSendVote(rs.LastCommit) {
  468. log.Debug("Picked rs.LastCommit to send")
  469. continue OUTER_LOOP
  470. }
  471. }
  472. // If there are prevotes to send...
  473. if prs.Step <= RoundStepPrevote && prs.Round != -1 && prs.Round <= rs.Round {
  474. if ps.PickSendVote(rs.Votes.Prevotes(prs.Round)) {
  475. log.Debug("Picked rs.Prevotes(prs.Round) to send")
  476. continue OUTER_LOOP
  477. }
  478. }
  479. // If there are precommits to send...
  480. if prs.Step <= RoundStepPrecommit && prs.Round != -1 && prs.Round <= rs.Round {
  481. if ps.PickSendVote(rs.Votes.Precommits(prs.Round)) {
  482. log.Debug("Picked rs.Precommits(prs.Round) to send")
  483. continue OUTER_LOOP
  484. }
  485. }
  486. // If there are POLPrevotes to send...
  487. if prs.ProposalPOLRound != -1 {
  488. if polPrevotes := rs.Votes.Prevotes(prs.ProposalPOLRound); polPrevotes != nil {
  489. if ps.PickSendVote(polPrevotes) {
  490. log.Debug("Picked rs.Prevotes(prs.ProposalPOLRound) to send")
  491. continue OUTER_LOOP
  492. }
  493. }
  494. }
  495. }
  496. // Special catchup logic.
  497. // If peer is lagging by height 1, send LastCommit.
  498. if prs.Height != 0 && rs.Height == prs.Height+1 {
  499. if ps.PickSendVote(rs.LastCommit) {
  500. log.Debug("Picked rs.LastCommit to send")
  501. continue OUTER_LOOP
  502. }
  503. }
  504. // Catchup logic
  505. // If peer is lagging by more than 1, send Commit.
  506. if prs.Height != 0 && rs.Height >= prs.Height+2 {
  507. // Load the block commit for prs.Height,
  508. // which contains precommit signatures for prs.Height.
  509. commit := conR.conS.blockStore.LoadBlockCommit(prs.Height)
  510. log.Info("Loaded BlockCommit for catch-up", "height", prs.Height, "commit", commit)
  511. if ps.PickSendVote(commit) {
  512. log.Debug("Picked Catchup commit to send")
  513. continue OUTER_LOOP
  514. }
  515. }
  516. if sleeping == 0 {
  517. // We sent nothing. Sleep...
  518. sleeping = 1
  519. log.Debug("No votes to send, sleeping", "peer", peer,
  520. "localPV", rs.Votes.Prevotes(rs.Round).BitArray(), "peerPV", prs.Prevotes,
  521. "localPC", rs.Votes.Precommits(rs.Round).BitArray(), "peerPC", prs.Precommits)
  522. } else if sleeping == 2 {
  523. // Continued sleep...
  524. sleeping = 1
  525. }
  526. time.Sleep(peerGossipSleepDuration)
  527. continue OUTER_LOOP
  528. }
  529. }
  530. func (conR *ConsensusReactor) queryMaj23Routine(peer *p2p.Peer, ps *PeerState) {
  531. log := log.New("peer", peer)
  532. OUTER_LOOP:
  533. for {
  534. // Manage disconnects from self or peer.
  535. if !peer.IsRunning() || !conR.IsRunning() {
  536. log.Notice(Fmt("Stopping queryMaj23Routine for %v.", peer))
  537. return
  538. }
  539. // Maybe send Height/Round/Prevotes
  540. {
  541. rs := conR.conS.GetRoundState()
  542. prs := ps.GetRoundState()
  543. if rs.Height == prs.Height {
  544. if maj23, ok := rs.Votes.Prevotes(prs.Round).TwoThirdsMajority(); ok {
  545. peer.TrySend(StateChannel, struct{ ConsensusMessage }{&VoteSetMaj23Message{
  546. Height: prs.Height,
  547. Round: prs.Round,
  548. Type: types.VoteTypePrevote,
  549. BlockID: maj23,
  550. }})
  551. time.Sleep(peerQueryMaj23SleepDuration)
  552. }
  553. }
  554. }
  555. // Maybe send Height/Round/Precommits
  556. {
  557. rs := conR.conS.GetRoundState()
  558. prs := ps.GetRoundState()
  559. if rs.Height == prs.Height {
  560. if maj23, ok := rs.Votes.Precommits(prs.Round).TwoThirdsMajority(); ok {
  561. peer.TrySend(StateChannel, struct{ ConsensusMessage }{&VoteSetMaj23Message{
  562. Height: prs.Height,
  563. Round: prs.Round,
  564. Type: types.VoteTypePrecommit,
  565. BlockID: maj23,
  566. }})
  567. time.Sleep(peerQueryMaj23SleepDuration)
  568. }
  569. }
  570. }
  571. // Maybe send Height/Round/ProposalPOL
  572. {
  573. rs := conR.conS.GetRoundState()
  574. prs := ps.GetRoundState()
  575. if rs.Height == prs.Height && prs.ProposalPOLRound >= 0 {
  576. if maj23, ok := rs.Votes.Prevotes(prs.ProposalPOLRound).TwoThirdsMajority(); ok {
  577. peer.TrySend(StateChannel, struct{ ConsensusMessage }{&VoteSetMaj23Message{
  578. Height: prs.Height,
  579. Round: prs.ProposalPOLRound,
  580. Type: types.VoteTypePrevote,
  581. BlockID: maj23,
  582. }})
  583. time.Sleep(peerQueryMaj23SleepDuration)
  584. }
  585. }
  586. }
  587. // Little point sending LastCommitRound/LastCommit,
  588. // These are fleeting and non-blocking.
  589. // Maybe send Height/CatchupCommitRound/CatchupCommit.
  590. {
  591. prs := ps.GetRoundState()
  592. if prs.CatchupCommitRound != -1 && 0 < prs.Height && prs.Height <= conR.conS.blockStore.Height() {
  593. var commit *types.Commit
  594. if prs.Height == conR.conS.blockStore.Height() {
  595. commit = conR.conS.blockStore.LoadSeenCommit(prs.Height)
  596. } else {
  597. commit = conR.conS.blockStore.LoadBlockCommit(prs.Height)
  598. }
  599. peer.TrySend(StateChannel, struct{ ConsensusMessage }{&VoteSetMaj23Message{
  600. Height: prs.Height,
  601. Round: commit.Round(),
  602. Type: types.VoteTypePrecommit,
  603. BlockID: commit.BlockID,
  604. }})
  605. time.Sleep(peerQueryMaj23SleepDuration)
  606. }
  607. }
  608. time.Sleep(peerQueryMaj23SleepDuration)
  609. continue OUTER_LOOP
  610. }
  611. }
  612. func (conR *ConsensusReactor) String() string {
  613. return conR.StringIndented("")
  614. }
  615. func (conR *ConsensusReactor) StringIndented(indent string) string {
  616. s := "ConsensusReactor{\n"
  617. s += indent + " " + conR.conS.StringIndented(indent+" ") + "\n"
  618. for _, peer := range conR.Switch.Peers().List() {
  619. ps := peer.Data.Get(types.PeerStateKey).(*PeerState)
  620. s += indent + " " + ps.StringIndented(indent+" ") + "\n"
  621. }
  622. s += indent + "}"
  623. return s
  624. }
  625. //-----------------------------------------------------------------------------
  626. // Read only when returned by PeerState.GetRoundState().
  627. type PeerRoundState struct {
  628. Height int // Height peer is at
  629. Round int // Round peer is at, -1 if unknown.
  630. Step RoundStepType // Step peer is at
  631. StartTime time.Time // Estimated start of round 0 at this height
  632. Proposal bool // True if peer has proposal for this round
  633. ProposalBlockPartsHeader types.PartSetHeader //
  634. ProposalBlockParts *BitArray //
  635. ProposalPOLRound int // Proposal's POL round. -1 if none.
  636. ProposalPOL *BitArray // nil until ProposalPOLMessage received.
  637. Prevotes *BitArray // All votes peer has for this round
  638. Precommits *BitArray // All precommits peer has for this round
  639. LastCommitRound int // Round of commit for last height. -1 if none.
  640. LastCommit *BitArray // All commit precommits of commit for last height.
  641. CatchupCommitRound int // Round that we have commit for. Not necessarily unique. -1 if none.
  642. CatchupCommit *BitArray // All commit precommits peer has for this height & CatchupCommitRound
  643. }
  644. func (prs PeerRoundState) String() string {
  645. return prs.StringIndented("")
  646. }
  647. func (prs PeerRoundState) StringIndented(indent string) string {
  648. return fmt.Sprintf(`PeerRoundState{
  649. %s %v/%v/%v @%v
  650. %s Proposal %v -> %v
  651. %s POL %v (round %v)
  652. %s Prevotes %v
  653. %s Precommits %v
  654. %s LastCommit %v (round %v)
  655. %s Catchup %v (round %v)
  656. %s}`,
  657. indent, prs.Height, prs.Round, prs.Step, prs.StartTime,
  658. indent, prs.ProposalBlockPartsHeader, prs.ProposalBlockParts,
  659. indent, prs.ProposalPOL, prs.ProposalPOLRound,
  660. indent, prs.Prevotes,
  661. indent, prs.Precommits,
  662. indent, prs.LastCommit, prs.LastCommitRound,
  663. indent, prs.CatchupCommit, prs.CatchupCommitRound,
  664. indent)
  665. }
  666. //-----------------------------------------------------------------------------
  667. var (
  668. ErrPeerStateHeightRegression = errors.New("Error peer state height regression")
  669. ErrPeerStateInvalidStartTime = errors.New("Error peer state invalid startTime")
  670. )
  671. type PeerState struct {
  672. Peer *p2p.Peer
  673. mtx sync.Mutex
  674. PeerRoundState
  675. }
  676. func NewPeerState(peer *p2p.Peer) *PeerState {
  677. return &PeerState{
  678. Peer: peer,
  679. PeerRoundState: PeerRoundState{
  680. Round: -1,
  681. ProposalPOLRound: -1,
  682. LastCommitRound: -1,
  683. CatchupCommitRound: -1,
  684. },
  685. }
  686. }
  687. // Returns an atomic snapshot of the PeerRoundState.
  688. // There's no point in mutating it since it won't change PeerState.
  689. func (ps *PeerState) GetRoundState() *PeerRoundState {
  690. ps.mtx.Lock()
  691. defer ps.mtx.Unlock()
  692. prs := ps.PeerRoundState // copy
  693. return &prs
  694. }
  695. // Returns an atomic snapshot of the PeerRoundState's height
  696. // used by the mempool to ensure peers are caught up before broadcasting new txs
  697. func (ps *PeerState) GetHeight() int {
  698. ps.mtx.Lock()
  699. defer ps.mtx.Unlock()
  700. return ps.PeerRoundState.Height
  701. }
  702. func (ps *PeerState) SetHasProposal(proposal *types.Proposal) {
  703. ps.mtx.Lock()
  704. defer ps.mtx.Unlock()
  705. if ps.Height != proposal.Height || ps.Round != proposal.Round {
  706. return
  707. }
  708. if ps.Proposal {
  709. return
  710. }
  711. ps.Proposal = true
  712. ps.ProposalBlockPartsHeader = proposal.BlockPartsHeader
  713. ps.ProposalBlockParts = NewBitArray(proposal.BlockPartsHeader.Total)
  714. ps.ProposalPOLRound = proposal.POLRound
  715. ps.ProposalPOL = nil // Nil until ProposalPOLMessage received.
  716. }
  717. func (ps *PeerState) SetHasProposalBlockPart(height int, round int, index int) {
  718. ps.mtx.Lock()
  719. defer ps.mtx.Unlock()
  720. if ps.Height != height || ps.Round != round {
  721. return
  722. }
  723. ps.ProposalBlockParts.SetIndex(index, true)
  724. }
  725. // Convenience function to send vote to peer.
  726. // Returns true if vote was sent.
  727. func (ps *PeerState) PickSendVote(votes types.VoteSetReader) (ok bool) {
  728. if vote, ok := ps.PickVoteToSend(votes); ok {
  729. msg := &VoteMessage{vote}
  730. ps.Peer.Send(VoteChannel, struct{ ConsensusMessage }{msg})
  731. return true
  732. }
  733. return false
  734. }
  735. // votes: Must be the correct Size() for the Height().
  736. func (ps *PeerState) PickVoteToSend(votes types.VoteSetReader) (vote *types.Vote, ok bool) {
  737. ps.mtx.Lock()
  738. defer ps.mtx.Unlock()
  739. if votes.Size() == 0 {
  740. return nil, false
  741. }
  742. height, round, type_, size := votes.Height(), votes.Round(), votes.Type(), votes.Size()
  743. // Lazily set data using 'votes'.
  744. if votes.IsCommit() {
  745. ps.ensureCatchupCommitRound(height, round, size)
  746. }
  747. ps.ensureVoteBitArrays(height, size)
  748. psVotes := ps.getVoteBitArray(height, round, type_)
  749. if psVotes == nil {
  750. return nil, false // Not something worth sending
  751. }
  752. if index, ok := votes.BitArray().Sub(psVotes).PickRandom(); ok {
  753. ps.setHasVote(height, round, type_, index)
  754. return votes.GetByIndex(index), true
  755. }
  756. return nil, false
  757. }
  758. func (ps *PeerState) getVoteBitArray(height, round int, type_ byte) *BitArray {
  759. if !types.IsVoteTypeValid(type_) {
  760. PanicSanity("Invalid vote type")
  761. }
  762. if ps.Height == height {
  763. if ps.Round == round {
  764. switch type_ {
  765. case types.VoteTypePrevote:
  766. return ps.Prevotes
  767. case types.VoteTypePrecommit:
  768. return ps.Precommits
  769. }
  770. }
  771. if ps.CatchupCommitRound == round {
  772. switch type_ {
  773. case types.VoteTypePrevote:
  774. return nil
  775. case types.VoteTypePrecommit:
  776. return ps.CatchupCommit
  777. }
  778. }
  779. if ps.ProposalPOLRound == round {
  780. switch type_ {
  781. case types.VoteTypePrevote:
  782. return ps.ProposalPOL
  783. case types.VoteTypePrecommit:
  784. return nil
  785. }
  786. }
  787. return nil
  788. }
  789. if ps.Height == height+1 {
  790. if ps.LastCommitRound == round {
  791. switch type_ {
  792. case types.VoteTypePrevote:
  793. return nil
  794. case types.VoteTypePrecommit:
  795. return ps.LastCommit
  796. }
  797. }
  798. return nil
  799. }
  800. return nil
  801. }
  802. // 'round': A round for which we have a +2/3 commit.
  803. func (ps *PeerState) ensureCatchupCommitRound(height, round int, numValidators int) {
  804. if ps.Height != height {
  805. return
  806. }
  807. /*
  808. NOTE: This is wrong, 'round' could change.
  809. e.g. if orig round is not the same as block LastCommit round.
  810. if ps.CatchupCommitRound != -1 && ps.CatchupCommitRound != round {
  811. PanicSanity(Fmt("Conflicting CatchupCommitRound. Height: %v, Orig: %v, New: %v", height, ps.CatchupCommitRound, round))
  812. }
  813. */
  814. if ps.CatchupCommitRound == round {
  815. return // Nothing to do!
  816. }
  817. ps.CatchupCommitRound = round
  818. if round == ps.Round {
  819. ps.CatchupCommit = ps.Precommits
  820. } else {
  821. ps.CatchupCommit = NewBitArray(numValidators)
  822. }
  823. }
  824. // NOTE: It's important to make sure that numValidators actually matches
  825. // what the node sees as the number of validators for height.
  826. func (ps *PeerState) EnsureVoteBitArrays(height int, numValidators int) {
  827. ps.mtx.Lock()
  828. defer ps.mtx.Unlock()
  829. ps.ensureVoteBitArrays(height, numValidators)
  830. }
  831. func (ps *PeerState) ensureVoteBitArrays(height int, numValidators int) {
  832. if ps.Height == height {
  833. if ps.Prevotes == nil {
  834. ps.Prevotes = NewBitArray(numValidators)
  835. }
  836. if ps.Precommits == nil {
  837. ps.Precommits = NewBitArray(numValidators)
  838. }
  839. if ps.CatchupCommit == nil {
  840. ps.CatchupCommit = NewBitArray(numValidators)
  841. }
  842. if ps.ProposalPOL == nil {
  843. ps.ProposalPOL = NewBitArray(numValidators)
  844. }
  845. } else if ps.Height == height+1 {
  846. if ps.LastCommit == nil {
  847. ps.LastCommit = NewBitArray(numValidators)
  848. }
  849. }
  850. }
  851. func (ps *PeerState) SetHasVote(vote *types.Vote) {
  852. ps.mtx.Lock()
  853. defer ps.mtx.Unlock()
  854. ps.setHasVote(vote.Height, vote.Round, vote.Type, vote.ValidatorIndex)
  855. }
  856. func (ps *PeerState) setHasVote(height int, round int, type_ byte, index int) {
  857. log := log.New("peer", ps.Peer, "peerRound", ps.Round, "height", height, "round", round)
  858. log.Info("setHasVote(LastCommit)", "lastCommit", ps.LastCommit, "index", index)
  859. // NOTE: some may be nil BitArrays -> no side effects.
  860. ps.getVoteBitArray(height, round, type_).SetIndex(index, true)
  861. }
  862. func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage) {
  863. ps.mtx.Lock()
  864. defer ps.mtx.Unlock()
  865. // Ignore duplicates or decreases
  866. if CompareHRS(msg.Height, msg.Round, msg.Step, ps.Height, ps.Round, ps.Step) <= 0 {
  867. return
  868. }
  869. // Just remember these values.
  870. psHeight := ps.Height
  871. psRound := ps.Round
  872. //psStep := ps.Step
  873. psCatchupCommitRound := ps.CatchupCommitRound
  874. psCatchupCommit := ps.CatchupCommit
  875. startTime := time.Now().Add(-1 * time.Duration(msg.SecondsSinceStartTime) * time.Second)
  876. ps.Height = msg.Height
  877. ps.Round = msg.Round
  878. ps.Step = msg.Step
  879. ps.StartTime = startTime
  880. if psHeight != msg.Height || psRound != msg.Round {
  881. ps.Proposal = false
  882. ps.ProposalBlockPartsHeader = types.PartSetHeader{}
  883. ps.ProposalBlockParts = nil
  884. ps.ProposalPOLRound = -1
  885. ps.ProposalPOL = nil
  886. // We'll update the BitArray capacity later.
  887. ps.Prevotes = nil
  888. ps.Precommits = nil
  889. }
  890. if psHeight == msg.Height && psRound != msg.Round && msg.Round == psCatchupCommitRound {
  891. // Peer caught up to CatchupCommitRound.
  892. // Preserve psCatchupCommit!
  893. // NOTE: We prefer to use prs.Precommits if
  894. // pr.Round matches pr.CatchupCommitRound.
  895. ps.Precommits = psCatchupCommit
  896. }
  897. if psHeight != msg.Height {
  898. // Shift Precommits to LastCommit.
  899. if psHeight+1 == msg.Height && psRound == msg.LastCommitRound {
  900. ps.LastCommitRound = msg.LastCommitRound
  901. ps.LastCommit = ps.Precommits
  902. } else {
  903. ps.LastCommitRound = msg.LastCommitRound
  904. ps.LastCommit = nil
  905. }
  906. // We'll update the BitArray capacity later.
  907. ps.CatchupCommitRound = -1
  908. ps.CatchupCommit = nil
  909. }
  910. }
  911. func (ps *PeerState) ApplyCommitStepMessage(msg *CommitStepMessage) {
  912. ps.mtx.Lock()
  913. defer ps.mtx.Unlock()
  914. if ps.Height != msg.Height {
  915. return
  916. }
  917. ps.ProposalBlockPartsHeader = msg.BlockPartsHeader
  918. ps.ProposalBlockParts = msg.BlockParts
  919. }
  920. func (ps *PeerState) ApplyProposalPOLMessage(msg *ProposalPOLMessage) {
  921. ps.mtx.Lock()
  922. defer ps.mtx.Unlock()
  923. if ps.Height != msg.Height {
  924. return
  925. }
  926. if ps.ProposalPOLRound != msg.ProposalPOLRound {
  927. return
  928. }
  929. // TODO: Merge onto existing ps.ProposalPOL?
  930. // We might have sent some prevotes in the meantime.
  931. ps.ProposalPOL = msg.ProposalPOL
  932. }
  933. func (ps *PeerState) ApplyHasVoteMessage(msg *HasVoteMessage) {
  934. ps.mtx.Lock()
  935. defer ps.mtx.Unlock()
  936. if ps.Height != msg.Height {
  937. return
  938. }
  939. ps.setHasVote(msg.Height, msg.Round, msg.Type, msg.Index)
  940. }
  941. // The peer has responded with a bitarray of votes that it has
  942. // of the corresponding BlockID.
  943. // ourVotes: BitArray of votes we have for msg.BlockID
  944. // NOTE: if ourVotes is nil (e.g. msg.Height < rs.Height),
  945. // we conservatively overwrite ps's votes w/ msg.Votes.
  946. func (ps *PeerState) ApplyVoteSetBitsMessage(msg *VoteSetBitsMessage, ourVotes *BitArray) {
  947. ps.mtx.Lock()
  948. defer ps.mtx.Unlock()
  949. votes := ps.getVoteBitArray(msg.Height, msg.Round, msg.Type)
  950. if votes != nil {
  951. if ourVotes == nil {
  952. votes.Update(msg.Votes)
  953. } else {
  954. otherVotes := votes.Sub(ourVotes)
  955. hasVotes := otherVotes.Or(msg.Votes)
  956. votes.Update(hasVotes)
  957. }
  958. }
  959. }
  960. func (ps *PeerState) String() string {
  961. return ps.StringIndented("")
  962. }
  963. func (ps *PeerState) StringIndented(indent string) string {
  964. return fmt.Sprintf(`PeerState{
  965. %s Key %v
  966. %s PRS %v
  967. %s}`,
  968. indent, ps.Peer.Key,
  969. indent, ps.PeerRoundState.StringIndented(indent+" "),
  970. indent)
  971. }
  972. //-----------------------------------------------------------------------------
  973. // Messages
  974. const (
  975. msgTypeNewRoundStep = byte(0x01)
  976. msgTypeCommitStep = byte(0x02)
  977. msgTypeProposal = byte(0x11)
  978. msgTypeProposalPOL = byte(0x12)
  979. msgTypeBlockPart = byte(0x13) // both block & POL
  980. msgTypeVote = byte(0x14)
  981. msgTypeHasVote = byte(0x15)
  982. msgTypeVoteSetMaj23 = byte(0x16)
  983. msgTypeVoteSetBits = byte(0x17)
  984. )
  985. type ConsensusMessage interface{}
  986. var _ = wire.RegisterInterface(
  987. struct{ ConsensusMessage }{},
  988. wire.ConcreteType{&NewRoundStepMessage{}, msgTypeNewRoundStep},
  989. wire.ConcreteType{&CommitStepMessage{}, msgTypeCommitStep},
  990. wire.ConcreteType{&ProposalMessage{}, msgTypeProposal},
  991. wire.ConcreteType{&ProposalPOLMessage{}, msgTypeProposalPOL},
  992. wire.ConcreteType{&BlockPartMessage{}, msgTypeBlockPart},
  993. wire.ConcreteType{&VoteMessage{}, msgTypeVote},
  994. wire.ConcreteType{&HasVoteMessage{}, msgTypeHasVote},
  995. wire.ConcreteType{&VoteSetMaj23Message{}, msgTypeVoteSetMaj23},
  996. wire.ConcreteType{&VoteSetBitsMessage{}, msgTypeVoteSetBits},
  997. )
  998. // TODO: check for unnecessary extra bytes at the end.
  999. func DecodeMessage(bz []byte) (msgType byte, msg ConsensusMessage, err error) {
  1000. msgType = bz[0]
  1001. n := new(int)
  1002. r := bytes.NewReader(bz)
  1003. msg = wire.ReadBinary(struct{ ConsensusMessage }{}, r, maxConsensusMessageSize, n, &err).(struct{ ConsensusMessage }).ConsensusMessage
  1004. return
  1005. }
  1006. //-------------------------------------
  1007. // For every height/round/step transition
  1008. type NewRoundStepMessage struct {
  1009. Height int
  1010. Round int
  1011. Step RoundStepType
  1012. SecondsSinceStartTime int
  1013. LastCommitRound int
  1014. }
  1015. func (m *NewRoundStepMessage) String() string {
  1016. return fmt.Sprintf("[NewRoundStep H:%v R:%v S:%v LCR:%v]",
  1017. m.Height, m.Round, m.Step, m.LastCommitRound)
  1018. }
  1019. //-------------------------------------
  1020. type CommitStepMessage struct {
  1021. Height int
  1022. BlockPartsHeader types.PartSetHeader
  1023. BlockParts *BitArray
  1024. }
  1025. func (m *CommitStepMessage) String() string {
  1026. return fmt.Sprintf("[CommitStep H:%v BP:%v BA:%v]", m.Height, m.BlockPartsHeader, m.BlockParts)
  1027. }
  1028. //-------------------------------------
  1029. type ProposalMessage struct {
  1030. Proposal *types.Proposal
  1031. }
  1032. func (m *ProposalMessage) String() string {
  1033. return fmt.Sprintf("[Proposal %v]", m.Proposal)
  1034. }
  1035. //-------------------------------------
  1036. type ProposalPOLMessage struct {
  1037. Height int
  1038. ProposalPOLRound int
  1039. ProposalPOL *BitArray
  1040. }
  1041. func (m *ProposalPOLMessage) String() string {
  1042. return fmt.Sprintf("[ProposalPOL H:%v POLR:%v POL:%v]", m.Height, m.ProposalPOLRound, m.ProposalPOL)
  1043. }
  1044. //-------------------------------------
  1045. type BlockPartMessage struct {
  1046. Height int
  1047. Round int
  1048. Part *types.Part
  1049. }
  1050. func (m *BlockPartMessage) String() string {
  1051. return fmt.Sprintf("[BlockPart H:%v R:%v P:%v]", m.Height, m.Round, m.Part)
  1052. }
  1053. //-------------------------------------
  1054. type VoteMessage struct {
  1055. Vote *types.Vote
  1056. }
  1057. func (m *VoteMessage) String() string {
  1058. return fmt.Sprintf("[Vote %v]", m.Vote)
  1059. }
  1060. //-------------------------------------
  1061. type HasVoteMessage struct {
  1062. Height int
  1063. Round int
  1064. Type byte
  1065. Index int
  1066. }
  1067. func (m *HasVoteMessage) String() string {
  1068. return fmt.Sprintf("[HasVote VI:%v V:{%v/%02d/%v} VI:%v]", m.Index, m.Height, m.Round, m.Type, m.Index)
  1069. }
  1070. //-------------------------------------
  1071. type VoteSetMaj23Message struct {
  1072. Height int
  1073. Round int
  1074. Type byte
  1075. BlockID types.BlockID
  1076. }
  1077. func (m *VoteSetMaj23Message) String() string {
  1078. return fmt.Sprintf("[VSM23 %v/%02d/%v %v]", m.Height, m.Round, m.Type, m.BlockID)
  1079. }
  1080. //-------------------------------------
  1081. type VoteSetBitsMessage struct {
  1082. Height int
  1083. Round int
  1084. Type byte
  1085. BlockID types.BlockID
  1086. Votes *BitArray
  1087. }
  1088. func (m *VoteSetBitsMessage) String() string {
  1089. return fmt.Sprintf("[VSB %v/%02d/%v %v %v]", m.Height, m.Round, m.Type, m.BlockID, m.Votes)
  1090. }