diff --git a/consensus/simplebft/backlog.go b/consensus/simplebft/backlog.go index 5c8d67a36d2..53d29dea78f 100644 --- a/consensus/simplebft/backlog.go +++ b/consensus/simplebft/backlog.go @@ -17,7 +17,7 @@ limitations under the License. package simplebft func (s *SBFT) testBacklog(m *Msg, src uint64) bool { - if len(s.backLog[src]) > 0 { + if len(s.replicaState[src].backLog) > 0 { return true } @@ -66,7 +66,7 @@ func (s *SBFT) recordBacklogMsg(m *Msg, src uint64) { // to make progress, the backlog processing algorithm as lined // out below will take care of starting a state transfer, // using the hello message we received on reconnect. - s.backLog[src] = append(s.backLog[src], m) + s.replicaState[src].backLog = append(s.replicaState[src].backLog, m) } func (s *SBFT) processBacklog() { @@ -75,14 +75,17 @@ func (s *SBFT) processBacklog() { for processed { processed = false - for src := range s.backLog { - for len(s.backLog[src]) > 0 { - m, rest := s.backLog[src][0], s.backLog[src][1:] + for src := range s.replicaState { + state := &s.replicaState[src] + src := uint64(src) + + for len(state.backLog) > 0 { + m, rest := state.backLog[0], state.backLog[1:] if s.testBacklog2(m, src) { notReady++ break } - s.backLog[src] = rest + state.backLog = rest log.Debugf("processing stored message from %d: %s", src, m) diff --git a/consensus/simplebft/connection.go b/consensus/simplebft/connection.go index a6985816379..5eaacfd8fb6 100644 --- a/consensus/simplebft/connection.go +++ b/consensus/simplebft/connection.go @@ -39,4 +39,5 @@ func (s *SBFT) Connection(replica uint64) { } func (s *SBFT) handleHello(h *Batch, src uint64) { + s.replicaState[src].hello = h } diff --git a/consensus/simplebft/newview.go b/consensus/simplebft/newview.go index 857323b9b76..288fce33812 100644 --- a/consensus/simplebft/newview.go +++ b/consensus/simplebft/newview.go @@ -29,10 +29,10 @@ func (s *SBFT) maybeSendNewView() { vset := make(map[uint64]*Signed) var vcs []*ViewChange - for src, vc := range s.viewchange { - if vc.vc.View == s.seq.View { - vset[src] = vc.svc - vcs = append(vcs, vc.vc) + for src, state := range s.replicaState { + if state.viewchange != nil && state.viewchange.View == s.seq.View { + vset[uint64(src)] = state.signedViewchange + vcs = append(vcs, state.viewchange) } } @@ -73,7 +73,7 @@ func (s *SBFT) handleNewView(nv *NewView, src uint64) { return } - if onv, ok := s.newview[s.primaryIDView(nv.View)]; ok && onv.View >= nv.View { + if onv := s.replicaState[s.primaryIDView(nv.View)].newview; onv != nil && onv.View >= nv.View { log.Debugf("discarding duplicate new view for %d", nv.View) return } @@ -128,7 +128,7 @@ func (s *SBFT) handleNewView(nv *NewView, src uint64) { return } - s.newview[s.primaryIDView(nv.View)] = nv + s.replicaState[s.primaryIDView(nv.View)].newview = nv s.processNewView() } @@ -138,8 +138,8 @@ func (s *SBFT) processNewView() { return } - nv, ok := s.newview[s.primaryIDView(s.seq.View)] - if !ok || nv.View != s.seq.View { + nv := s.replicaState[s.primaryIDView(s.seq.View)].newview + if nv == nil || nv.View != s.seq.View { return } diff --git a/consensus/simplebft/simplebft.go b/consensus/simplebft/simplebft.go index b52561306cd..9b313aa741e 100644 --- a/consensus/simplebft/simplebft.go +++ b/consensus/simplebft/simplebft.go @@ -61,13 +61,10 @@ type SBFT struct { batchTimer Canceller cur reqInfo activeView bool - viewchange map[uint64]*viewChangeInfo - newview map[uint64]*NewView lastNewViewSent uint64 viewChangeTimeout time.Duration viewChangeTimer Canceller - - backLog map[uint64][]*Msg + replicaState []replicaInfo } type reqInfo struct { @@ -82,9 +79,12 @@ type reqInfo struct { checkpointDone bool } -type viewChangeInfo struct { - svc *Signed - vc *ViewChange +type replicaInfo struct { + backLog []*Msg + hello *Batch + signedViewchange *Signed + viewchange *ViewChange + newview *NewView } var log = logging.MustGetLogger("sbft") @@ -103,10 +103,8 @@ func New(id uint64, config *Config, sys System) (*SBFT, error) { config: *config, sys: sys, id: id, - viewchange: make(map[uint64]*viewChangeInfo), - newview: make(map[uint64]*NewView), viewChangeTimer: dummyCanceller{}, - backLog: make(map[uint64][]*Msg), + replicaState: make([]replicaInfo, config.N), } s.sys.SetReceiver(s) diff --git a/consensus/simplebft/simplebft_test.go b/consensus/simplebft/simplebft_test.go index b31418fc93e..5a7c6f83dd5 100644 --- a/consensus/simplebft/simplebft_test.go +++ b/consensus/simplebft/simplebft_test.go @@ -29,7 +29,7 @@ var testLog = logging.MustGetLogger("test") func init() { logging.SetLevel(logging.NOTICE, "") - logging.SetLevel(logging.NOTICE, "test") + logging.SetLevel(logging.DEBUG, "test") logging.SetLevel(logging.DEBUG, "sbft") } diff --git a/consensus/simplebft/viewchange.go b/consensus/simplebft/viewchange.go index fea01ee5658..f6be9b76631 100644 --- a/consensus/simplebft/viewchange.go +++ b/consensus/simplebft/viewchange.go @@ -22,9 +22,10 @@ func (s *SBFT) sendViewChange() { s.seq.View = s.nextView() s.cur.timeout.Cancel() s.activeView = false - for r, vs := range s.viewchange { - if vs.vc.View < s.seq.View { - delete(s.viewchange, r) + for src := range s.replicaState { + state := &s.replicaState[src] + if state.viewchange != nil && state.viewchange.View < s.seq.View { + state.viewchange = nil } } log.Noticef("sending viewchange for view %d", s.seq.View) @@ -71,21 +72,27 @@ func (s *SBFT) handleViewChange(svc *Signed, src uint64) { log.Debugf("old view change from %s for view %d, we are in view %d", src, vc.View, s.seq.View) return } - if ovc, ok := s.viewchange[src]; ok && vc.View <= ovc.vc.View { + if ovc := s.replicaState[src].viewchange; ovc != nil && vc.View <= ovc.View { log.Noticef("duplicate view change for %d from %d", vc.View, src) return } log.Infof("viewchange from %d for view %d", src, vc.View) - s.viewchange[src] = &viewChangeInfo{svc: svc, vc: vc} + s.replicaState[src].viewchange = vc + s.replicaState[src].signedViewchange = svc - if len(s.viewchange) == s.oneCorrectQuorum() { - min := vc.View - for _, vc := range s.viewchange { - if vc.vc.View < min { - min = vc.vc.View + min := vc.View + quorum := 0 + for _, state := range s.replicaState { + if state.viewchange != nil { + quorum++ + if state.viewchange.View < min { + min = state.viewchange.View } } + } + + if quorum == s.oneCorrectQuorum() { // catch up to the minimum view if s.seq.View < min { log.Notice("we are behind on view change, resending for newer view")