github.com/annchain/OG@v0.0.9/poc/tendermint/client.go (about)

     1  package tendermint
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/sirupsen/logrus"
     6  	"strings"
     7  	"time"
     8  )
     9  
    10  // Partner is a participant in the consensus.
    11  type Partner interface {
    12  	EventLoop()
    13  	StartNewEra(height int, round int)
    14  	SetPeers(peers []Partner)
    15  	GetIncomingMessageChannel() chan Message
    16  	GetOutgoingMessageChannel() chan Message
    17  	GetWaiterTimeoutChannel() chan *WaiterRequest
    18  	GetId() int
    19  }
    20  
    21  type PartnerBase struct {
    22  	Id                     int
    23  	IncomingMessageChannel chan Message
    24  	OutgoingMessageChannel chan Message
    25  	WaiterTimeoutChannel   chan *WaiterRequest
    26  }
    27  
    28  // HeightRound is the current progress of the consensus.
    29  // Height is the block height, round is the sub-progress if no consensus can be easily reached
    30  type HeightRound struct {
    31  	Height int
    32  	Round  int
    33  }
    34  
    35  func (h *HeightRound) String() string {
    36  	return fmt.Sprintf("[%d-%d]", h.Height, h.Round)
    37  }
    38  
    39  // IsAfter judges whether self is a higher HeightRound
    40  func (h *HeightRound) IsAfter(o HeightRound) bool {
    41  	return h.Height > o.Height ||
    42  		(h.Height == o.Height && h.Round > o.Round)
    43  }
    44  
    45  // IsAfterOrEqual judges whether self is a higher or equal HeightRound
    46  func (h *HeightRound) IsAfterOrEqual(o HeightRound) bool {
    47  	return h.Height > o.Height ||
    48  		(h.Height == o.Height && h.Round >= o.Round)
    49  }
    50  
    51  // IsAfterOrEqual judges whether self is a lower HeightRound
    52  func (h *HeightRound) IsBefore(o HeightRound) bool {
    53  	return h.Height < o.Height ||
    54  		(h.Height == o.Height && h.Round < o.Round)
    55  }
    56  
    57  // HeightRoundState is the structure for each Height/Round
    58  // Always keep this state that is higher than current in Partner.States map in order not to miss future things
    59  type HeightRoundState struct {
    60  	MessageProposal                       *MessageProposal // the proposal received in this round
    61  	LockedValue                           Proposal
    62  	LockedRound                           int
    63  	ValidValue                            Proposal
    64  	ValidRound                            int
    65  	Decision                              interface{}          // final decision of mine in this round
    66  	PreVotes                              []*MessageCommonVote // other peers' PreVotes
    67  	PreCommits                            []*MessageCommonVote // other peers' PreCommits
    68  	Sources                               map[int]bool         // for line 55, who send future round so that I may advance?
    69  	StepTypeEqualPreVoteTriggered         bool                 // for line 34, FIRST time trigger
    70  	StepTypeEqualOrLargerPreVoteTriggered bool                 // for line 36, FIRST time trigger
    71  	StepTypeEqualPreCommitTriggered       bool                 // for line 47, FIRST time trigger
    72  	Step                                  StepType             // current step in this round
    73  }
    74  
    75  func NewHeightRoundState(total int) *HeightRoundState {
    76  	return &HeightRoundState{
    77  		LockedRound: -1,
    78  		ValidRound:  -1,
    79  		PreVotes:    make([]*MessageCommonVote, total),
    80  		PreCommits:  make([]*MessageCommonVote, total),
    81  		Sources:     make(map[int]bool),
    82  	}
    83  }
    84  
    85  // DefaultPartner implements a Tendermint client according to "The latest gossip on BFT consensus"
    86  // Destroy and use a new one upon peers changing.
    87  type DefaultPartner struct {
    88  	PartnerBase
    89  	CurrentHR HeightRound   // Partner's current Height/Round
    90  	blockTime time.Duration // interval between proposal being generated
    91  	N         int           // total number of participants
    92  	F         int           // max number of Byzantines
    93  	Peers     []Partner     // All peers
    94  	quit      chan bool
    95  	waiter    *Waiter                           // waiter for some state changes
    96  	States    map[HeightRound]*HeightRoundState // for line 55, record state for every HeightRound
    97  }
    98  
    99  func (p *DefaultPartner) GetWaiterTimeoutChannel() chan *WaiterRequest {
   100  	return p.WaiterTimeoutChannel
   101  }
   102  
   103  func (p *DefaultPartner) GetIncomingMessageChannel() chan Message {
   104  	return p.IncomingMessageChannel
   105  }
   106  
   107  func (p *DefaultPartner) GetOutgoingMessageChannel() chan Message {
   108  	return p.OutgoingMessageChannel
   109  }
   110  
   111  func (p *DefaultPartner) GetId() int {
   112  	return p.Id
   113  }
   114  
   115  func (p *DefaultPartner) SetPeers(peers []Partner) {
   116  	p.Peers = peers
   117  }
   118  
   119  func NewPartner(nbParticipants int, id int, blockTime time.Duration) *DefaultPartner {
   120  	p := &DefaultPartner{
   121  		N:         nbParticipants,
   122  		F:         (nbParticipants - 1) / 3,
   123  		blockTime: blockTime,
   124  		PartnerBase: PartnerBase{
   125  			Id:                     id,
   126  			IncomingMessageChannel: make(chan Message, 10),
   127  			OutgoingMessageChannel: make(chan Message, 10),
   128  			WaiterTimeoutChannel:   make(chan *WaiterRequest, 10),
   129  		},
   130  		quit:   make(chan bool),
   131  		States: make(map[HeightRound]*HeightRoundState),
   132  	}
   133  	p.waiter = NewWaiter(p.GetWaiterTimeoutChannel())
   134  	go p.waiter.StartEventLoop()
   135  	return p
   136  }
   137  
   138  // StartNewEra is called once height or round needs to be changed.
   139  func (p *DefaultPartner) StartNewEra(height int, round int) {
   140  	hr := p.CurrentHR
   141  	if height-hr.Height > 1 {
   142  		logrus.WithField("height", height).Warn("height is much higher than current. Indicating packet loss or severe behind.")
   143  	}
   144  	hr.Height = height
   145  	hr.Round = round
   146  
   147  	logrus.WithFields(logrus.Fields{
   148  		"IM":        p.Id,
   149  		"currentHR": p.CurrentHR.String(),
   150  		"newHR":     hr.String(),
   151  	}).Debug("Starting new round")
   152  
   153  	currState, _ := p.initHeightRound(hr)
   154  	// update partner height
   155  	p.CurrentHR = hr
   156  
   157  	p.WipeOldStates()
   158  	p.changeStep(StepTypePropose)
   159  
   160  	if p.Id == p.Proposer(p.CurrentHR) {
   161  		logrus.WithField("IM", p.Id).WithField("hr", p.CurrentHR.String()).Info("I'm the proposer")
   162  		var proposal Proposal
   163  		if currState.ValidValue != nil {
   164  			proposal = currState.ValidValue
   165  		} else {
   166  			proposal = p.GetValue()
   167  		}
   168  		// broadcast
   169  		p.Broadcast(MessageTypeProposal, p.CurrentHR, proposal, currState.ValidRound)
   170  	} else {
   171  		p.WaitStepTimeout(StepTypePropose, TimeoutPropose, p.CurrentHR, p.OnTimeoutPropose)
   172  	}
   173  }
   174  
   175  func (p *DefaultPartner) EventLoop() {
   176  	go p.send()
   177  	go p.receive()
   178  }
   179  
   180  // send is just for outgoing messages. It should not change any state of local tendermint
   181  func (p *DefaultPartner) send() {
   182  	timer := time.NewTimer(time.Second * 7)
   183  	for {
   184  		timer.Reset(time.Second * 7)
   185  		select {
   186  		case <-p.quit:
   187  			break
   188  		case <-timer.C:
   189  			logrus.WithField("IM", p.Id).Warn("Blocked reading outgoing")
   190  			p.dumpAll("blocked reading outgoing")
   191  		case msg := <-p.OutgoingMessageChannel:
   192  			for _, peer := range p.Peers {
   193  				logrus.WithFields(logrus.Fields{
   194  					"IM":   p.Id,
   195  					"hr":   p.CurrentHR.String(),
   196  					"from": p.Id,
   197  					"to":   peer.GetId(),
   198  					"msg":  msg.String(),
   199  				}).Debug("Out")
   200  				go func(targetPeer Partner) {
   201  					//time.Sleep(time.Duration(300 + rand.Intn(100)) * time.Millisecond)
   202  					//ffchan.NewTimeoutSenderShort(targetPeer.GetIncomingMessageChannel(), msg, "broadcasting")
   203  					targetPeer.GetIncomingMessageChannel() <- msg
   204  				}(peer)
   205  			}
   206  		}
   207  	}
   208  }
   209  
   210  // receive prevents concurrent state updates by allowing only one channel to be read per loop
   211  // Any action which involves state updates should be in this select clause
   212  func (p *DefaultPartner) receive() {
   213  	timer := time.NewTimer(time.Second * 7)
   214  	for {
   215  		timer.Reset(time.Second * 7)
   216  		select {
   217  		case <-p.quit:
   218  			break
   219  		case v := <-p.WaiterTimeoutChannel:
   220  			context := v.Context.(*TendermintContext)
   221  			logrus.WithFields(logrus.Fields{
   222  				"step": context.StepType.String(),
   223  				"IM":   p.Id,
   224  				"hr":   context.HeightRound.String(),
   225  			}).Warn("wait step timeout")
   226  			p.dumpAll("wait step timeout")
   227  			v.TimeoutCallback(v.Context)
   228  		case <-timer.C:
   229  			logrus.WithField("IM", p.Id).Warn("Blocked reading incoming")
   230  			//p.dumpAll("blocked reading incoming")
   231  		case msg := <-p.IncomingMessageChannel:
   232  			p.handleMessage(msg)
   233  		}
   234  	}
   235  }
   236  
   237  // Proposer returns current round proposer. Now simply round robin
   238  func (p *DefaultPartner) Proposer(hr HeightRound) int {
   239  	//return 3
   240  	return (hr.Height + hr.Round) % p.N
   241  }
   242  
   243  // GetValue generates the value requiring consensus
   244  func (p *DefaultPartner) GetValue() Proposal {
   245  	logrus.WithField("blocktime", p.blockTime).Info("will return a proposal after some time")
   246  	time.Sleep(p.blockTime)
   247  	logrus.Info("proposal generated")
   248  	v := fmt.Sprintf("■■■%d %d■■■", p.CurrentHR.Height, p.CurrentHR.Round)
   249  	return StringProposal(v)
   250  }
   251  
   252  // Multicast announce messages to all partners
   253  func (p *DefaultPartner) Broadcast(messageType MessageType, hr HeightRound, content Proposal, validRound int) {
   254  	m := Message{
   255  		Type: messageType,
   256  	}
   257  	basicMessage := BasicMessage{
   258  		HeightRound: hr,
   259  		SourceId:    p.Id,
   260  	}
   261  	idv := ""
   262  	if content != nil {
   263  		idv = content.GetId()
   264  	}
   265  	switch messageType {
   266  	case MessageTypeProposal:
   267  		m.Payload = MessageProposal{
   268  			BasicMessage: basicMessage,
   269  			Value:        content,
   270  			ValidRound:   validRound,
   271  		}
   272  	case MessageTypePreVote:
   273  		m.Payload = MessageCommonVote{
   274  			BasicMessage: basicMessage,
   275  			Idv:          idv,
   276  		}
   277  	case MessageTypePreCommit:
   278  		m.Payload = MessageCommonVote{
   279  			BasicMessage: basicMessage,
   280  			Idv:          idv,
   281  		}
   282  	}
   283  	p.OutgoingMessageChannel <- m
   284  	//ffchan.NewTimeoutSenderShort(p.OutgoingMessageChannel, m, "")
   285  }
   286  
   287  // OnTimeoutPropose is the callback after staying too long on propose step
   288  func (p *DefaultPartner) OnTimeoutPropose(context WaiterContext) {
   289  	v := context.(*TendermintContext)
   290  	if v.HeightRound == p.CurrentHR && p.States[p.CurrentHR].Step == StepTypePropose {
   291  		p.Broadcast(MessageTypePreVote, p.CurrentHR, nil, 0)
   292  		p.changeStep(StepTypePreVote)
   293  	}
   294  }
   295  
   296  // OnTimeoutPreVote is the callback after staying too long on prevote step
   297  func (p *DefaultPartner) OnTimeoutPreVote(context WaiterContext) {
   298  	v := context.(*TendermintContext)
   299  	if v.HeightRound == p.CurrentHR && p.States[p.CurrentHR].Step == StepTypePreVote {
   300  		p.Broadcast(MessageTypePreCommit, p.CurrentHR, nil, 0)
   301  		p.changeStep(StepTypePreCommit)
   302  	}
   303  }
   304  
   305  // OnTimeoutPreCommit is the callback after staying too long on precommit step
   306  func (p *DefaultPartner) OnTimeoutPreCommit(context WaiterContext) {
   307  	v := context.(*TendermintContext)
   308  	if v.HeightRound == p.CurrentHR {
   309  		p.StartNewEra(v.HeightRound.Height, v.HeightRound.Round+1)
   310  	} else {
   311  		logrus.Warn(v.HeightRound, " diffff ", p.CurrentHR)
   312  	}
   313  }
   314  
   315  // WaitStepTimeout waits for a centain time if stepType stays too long
   316  func (p *DefaultPartner) WaitStepTimeout(stepType StepType, timeout time.Duration, hr HeightRound, timeoutCallback func(WaiterContext)) {
   317  	p.waiter.UpdateRequest(&WaiterRequest{
   318  		WaitTime:        timeout,
   319  		TimeoutCallback: timeoutCallback,
   320  		Context: &TendermintContext{
   321  			HeightRound: hr,
   322  			StepType:    stepType,
   323  		},
   324  	})
   325  }
   326  
   327  func (p *DefaultPartner) handleMessage(message Message) {
   328  	switch message.Type {
   329  	case MessageTypeProposal:
   330  		msg := message.Payload.(MessageProposal)
   331  		if needHandle := p.checkRound(&msg.BasicMessage); !needHandle {
   332  			// out-of-date messages, ignore
   333  			break
   334  		}
   335  		logrus.WithFields(logrus.Fields{
   336  			"IM":     p.Id,
   337  			"hr":     p.CurrentHR.String(),
   338  			"type":   message.Type.String(),
   339  			"from":   msg.SourceId,
   340  			"fromHr": msg.HeightRound.String(),
   341  			"value":  msg.Value,
   342  		}).Debug("In")
   343  		p.handleProposal(&msg)
   344  	case MessageTypePreVote:
   345  		msg := message.Payload.(MessageCommonVote)
   346  		if needHandle := p.checkRound(&msg.BasicMessage); !needHandle {
   347  			// out-of-date messages, ignore
   348  			logrus.Debug("no needHandle")
   349  			break
   350  		}
   351  		p.States[msg.HeightRound].PreVotes[msg.SourceId] = &msg
   352  		logrus.WithFields(logrus.Fields{
   353  			"IM":     p.Id,
   354  			"hr":     p.CurrentHR.String(),
   355  			"type":   message.Type.String(),
   356  			"from":   msg.SourceId,
   357  			"fromHr": msg.HeightRound.String(),
   358  		}).Debug("In")
   359  		p.handlePreVote(&msg)
   360  	case MessageTypePreCommit:
   361  		msg := message.Payload.(MessageCommonVote)
   362  		if needHandle := p.checkRound(&msg.BasicMessage); !needHandle {
   363  			// out-of-date messages, ignore
   364  			logrus.Debug("no needHandle")
   365  			break
   366  		}
   367  		p.States[msg.HeightRound].PreCommits[msg.SourceId] = &msg
   368  		logrus.WithFields(logrus.Fields{
   369  			"IM":     p.Id,
   370  			"hr":     p.CurrentHR.String(),
   371  			"type":   message.Type.String(),
   372  			"from":   msg.SourceId,
   373  			"fromHr": msg.HeightRound.String(),
   374  		}).Debug("In")
   375  		p.handlePreCommit(&msg)
   376  	}
   377  }
   378  func (p *DefaultPartner) handleProposal(proposal *MessageProposal) {
   379  	state, ok := p.States[proposal.HeightRound]
   380  	if !ok {
   381  		panic("must exists")
   382  	}
   383  	state.MessageProposal = proposal
   384  	// rule line 22
   385  	if state.Step == StepTypePropose {
   386  		if p.valid(proposal.Value) && (state.LockedRound == -1 || state.LockedValue.Equal(proposal.Value)) {
   387  			p.Broadcast(MessageTypePreVote, proposal.HeightRound, proposal.Value, 0)
   388  		} else {
   389  			p.Broadcast(MessageTypePreVote, proposal.HeightRound, nil, 0)
   390  		}
   391  		p.changeStep(StepTypePreVote)
   392  	}
   393  
   394  	// rule line 28
   395  	count := p.count(MessageTypePreVote, proposal.HeightRound.Height, proposal.ValidRound, MatchTypeByValue, proposal.Value.GetId())
   396  	if count >= 2*p.F+1 {
   397  		if state.Step == StepTypePropose && (proposal.ValidRound >= 0 && proposal.ValidRound < p.CurrentHR.Round) {
   398  			if p.valid(proposal.Value) && (state.LockedRound <= proposal.ValidRound || state.LockedValue.Equal(proposal.Value)) {
   399  				p.Broadcast(MessageTypePreVote, proposal.HeightRound, proposal.Value, 0)
   400  			} else {
   401  				p.Broadcast(MessageTypePreVote, proposal.HeightRound, nil, 0)
   402  			}
   403  			p.changeStep(StepTypePreVote)
   404  		}
   405  	}
   406  }
   407  func (p *DefaultPartner) handlePreVote(vote *MessageCommonVote) {
   408  	// rule line 34
   409  	count := p.count(MessageTypePreVote, vote.HeightRound.Height, vote.HeightRound.Round, MatchTypeAny, "")
   410  	state, ok := p.States[vote.HeightRound]
   411  	if !ok {
   412  		panic("should exists: " + vote.HeightRound.String())
   413  	}
   414  	if count >= 2*p.F+1 {
   415  		if state.Step == StepTypePreVote && !state.StepTypeEqualPreVoteTriggered {
   416  			logrus.WithField("IM", p.Id).WithField("hr", vote.HeightRound.String()).Debug("prevote counter is more than 2f+1 #1")
   417  			state.StepTypeEqualPreVoteTriggered = true
   418  			p.WaitStepTimeout(StepTypePreVote, TimeoutPreVote, vote.HeightRound, p.OnTimeoutPreVote)
   419  		}
   420  	}
   421  	// rule line 36
   422  	if state.MessageProposal != nil && count >= 2*p.F+1 {
   423  		if p.valid(state.MessageProposal.Value) && state.Step >= StepTypePreVote && !state.StepTypeEqualOrLargerPreVoteTriggered {
   424  			logrus.WithField("IM", p.Id).WithField("hr", vote.HeightRound.String()).Debug("prevote counter is more than 2f+1 #2")
   425  			state.StepTypeEqualOrLargerPreVoteTriggered = true
   426  			if state.Step == StepTypePreVote {
   427  				state.LockedValue = state.MessageProposal.Value
   428  				state.LockedRound = p.CurrentHR.Round
   429  				p.Broadcast(MessageTypePreCommit, vote.HeightRound, state.MessageProposal.Value, 0)
   430  				p.changeStep(StepTypePreCommit)
   431  			}
   432  			state.ValidValue = state.MessageProposal.Value
   433  			state.ValidRound = p.CurrentHR.Round
   434  		}
   435  	}
   436  	// rule line 44
   437  	count = p.count(MessageTypePreVote, vote.HeightRound.Height, vote.HeightRound.Round, MatchTypeNil, "")
   438  	if count >= 2*p.F+1 && state.Step == StepTypePreVote {
   439  		logrus.WithField("IM", p.Id).WithField("hr", p.CurrentHR.String()).Debug("prevote counter is more than 2f+1 #3")
   440  		p.Broadcast(MessageTypePreCommit, vote.HeightRound, nil, 0)
   441  		p.changeStep(StepTypePreCommit)
   442  	}
   443  
   444  }
   445  func (p *DefaultPartner) handlePreCommit(commit *MessageCommonVote) {
   446  	// rule line 47
   447  	count := p.count(MessageTypePreCommit, commit.HeightRound.Height, commit.HeightRound.Round, MatchTypeAny, "")
   448  	state := p.States[commit.HeightRound]
   449  	if count >= 2*p.F+1 && !state.StepTypeEqualPreCommitTriggered {
   450  		logrus.WithField("IM", p.Id).WithField("hr", commit.HeightRound.String()).Debug("precommit counter is more than 2f+1 #1")
   451  		state.StepTypeEqualPreCommitTriggered = true
   452  		p.WaitStepTimeout(StepTypePreCommit, TimeoutPreCommit, commit.HeightRound, p.OnTimeoutPreCommit)
   453  	}
   454  	// rule line 49
   455  	if state.MessageProposal != nil {
   456  		count = p.count(MessageTypePreCommit, commit.HeightRound.Height, commit.HeightRound.Round, MatchTypeByValue, state.MessageProposal.Value.GetId())
   457  		if count >= 2*p.F+1 {
   458  			if state.Decision == nil {
   459  				// output decision
   460  				state.Decision = state.MessageProposal.Value
   461  				logrus.WithFields(logrus.Fields{
   462  					"IM":    p.Id,
   463  					"hr":    p.CurrentHR.String(),
   464  					"value": state.MessageProposal.Value,
   465  				}).Info("Decision")
   466  
   467  				p.StartNewEra(p.CurrentHR.Height+1, 0)
   468  			}
   469  		}
   470  	}
   471  
   472  }
   473  
   474  // valid checks proposal validation
   475  // TODO: inject so that valid will call a function to validate the proposal
   476  func (p *DefaultPartner) valid(proposal Proposal) bool {
   477  	return true
   478  }
   479  
   480  // count votes and commits from others.
   481  func (p *DefaultPartner) count(messageType MessageType, height int, validRound int, valueIdMatchType ValueIdMatchType, valueId string) int {
   482  	counter := 0
   483  	var target []*MessageCommonVote
   484  	state, ok := p.States[HeightRound{
   485  		Height: height,
   486  		Round:  validRound,
   487  	}]
   488  	if !ok {
   489  		return 0
   490  	}
   491  	switch messageType {
   492  	case MessageTypePreVote:
   493  		target = state.PreVotes
   494  	case MessageTypePreCommit:
   495  		target = state.PreCommits
   496  	default:
   497  		target = nil
   498  	}
   499  	for _, m := range target {
   500  		if m == nil {
   501  			continue
   502  		}
   503  		if m.HeightRound.Height > height || m.HeightRound.Round > validRound {
   504  			p.dumpAll("impossible now")
   505  			panic("wrong logic: " + fmt.Sprintf("%d %d %d %d", m.HeightRound.Height, height, m.HeightRound.Round, validRound))
   506  		}
   507  		switch valueIdMatchType {
   508  		case MatchTypeByValue:
   509  			if m.Idv == valueId {
   510  				counter++
   511  			}
   512  		case MatchTypeNil:
   513  			if m.Idv == "" {
   514  				counter++
   515  			}
   516  		case MatchTypeAny:
   517  			counter++
   518  		}
   519  	}
   520  	logrus.WithField("IM", p.Id).
   521  		Debugf("Counting: [%d] %s H:%d VR:%d MT:%d", counter, messageType.String(), height, validRound, valueIdMatchType)
   522  	return counter
   523  }
   524  
   525  // checkRound will init all data structure this message needs.
   526  // It also check if the message is out of date, or advanced too much
   527  func (p *DefaultPartner) checkRound(message *BasicMessage) (needHandle bool) {
   528  	// rule line 55
   529  	// slightly changed this so that if there is f+1 newer HeightRound(instead of just round), catch up to this HeightRound
   530  	if message.HeightRound.IsAfter(p.CurrentHR) {
   531  		state, ok := p.States[message.HeightRound]
   532  		if !ok {
   533  			// create one
   534  			// TODO: verify if someone is generating garbage height
   535  			d, c := p.initHeightRound(message.HeightRound)
   536  			state = d
   537  			if c != len(p.States) {
   538  				panic("number not aligned")
   539  			}
   540  		}
   541  		state.Sources[message.SourceId] = true
   542  		logrus.WithField("IM", p.Id).Tracef("Set source: %d at %s, %+v", message.SourceId, message.HeightRound.String(), state.Sources)
   543  		logrus.WithField("IM", p.Id).Tracef("%d's %s state is %+v, after receiving message %s from %d", p.Id, p.CurrentHR.String(), p.States[p.CurrentHR].Sources, message.HeightRound.String(), message.SourceId)
   544  
   545  		if len(state.Sources) >= p.F+1 {
   546  			p.dumpAll("New era received")
   547  			p.StartNewEra(message.HeightRound.Height, message.HeightRound.Round)
   548  		}
   549  	}
   550  
   551  	return message.HeightRound.IsAfterOrEqual(p.CurrentHR)
   552  }
   553  
   554  // changeStep updates the step and then notify the waiter.
   555  func (p *DefaultPartner) changeStep(stepType StepType) {
   556  	p.States[p.CurrentHR].Step = stepType
   557  	p.waiter.UpdateContext(&TendermintContext{
   558  		HeightRound: p.CurrentHR,
   559  		StepType:    stepType,
   560  	})
   561  }
   562  
   563  // dumpVotes prints all current votes received
   564  func (p *DefaultPartner) dumpVotes(votes []*MessageCommonVote) string {
   565  	sb := strings.Builder{}
   566  	sb.WriteString("[")
   567  	for _, vote := range votes {
   568  		if vote == nil {
   569  			sb.WriteString(fmt.Sprintf("[nil Vote]"))
   570  		} else {
   571  			sb.WriteString(fmt.Sprintf("[%d hr:%s s:%s]", vote.SourceId, vote.HeightRound.String(), vote.Idv))
   572  		}
   573  
   574  		sb.WriteString(" ")
   575  	}
   576  	sb.WriteString("]")
   577  	return sb.String()
   578  }
   579  
   580  func (p *DefaultPartner) dumpAll(reason string) {
   581  	//return
   582  	state := p.States[p.CurrentHR]
   583  	logrus.WithField("IM", p.Id).WithField("hr", p.CurrentHR).WithField("reason", reason).Debug("Dumping")
   584  	logrus.WithField("IM", p.Id).WithField("hr", p.CurrentHR).WithField("votes", "prevotes").Debug(p.dumpVotes(state.PreVotes))
   585  	logrus.WithField("IM", p.Id).WithField("hr", p.CurrentHR).WithField("votes", "precommits").Debug(p.dumpVotes(state.PreCommits))
   586  	logrus.WithField("IM", p.Id).WithField("hr", p.CurrentHR).WithField("step", state.Step.String()).Debug("Step")
   587  	logrus.WithField("IM", p.Id).WithField("hr", p.CurrentHR).Debugf("%+v %d", state.Sources, len(state.Sources))
   588  }
   589  
   590  func (p *DefaultPartner) WipeOldStates() {
   591  	var toRemove []HeightRound
   592  	for hr := range p.States {
   593  		if hr.IsBefore(p.CurrentHR) {
   594  			toRemove = append(toRemove, hr)
   595  		}
   596  	}
   597  	for _, hr := range toRemove {
   598  		delete(p.States, hr)
   599  	}
   600  }
   601  
   602  func (p *DefaultPartner) initHeightRound(hr HeightRound) (*HeightRoundState, int) {
   603  	// first check if there is previous message received
   604  	if _, ok := p.States[hr]; !ok {
   605  		// init one
   606  		p.States[hr] = NewHeightRoundState(p.N)
   607  	}
   608  	return p.States[hr], len(p.States)
   609  }