github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/consensus/dbft/core/core.go (about) 1 package core 2 3 import ( 4 "bytes" 5 "math" 6 "math/big" 7 "sync" 8 "time" 9 10 "github.com/quickchainproject/quickchain/common" 11 bft "github.com/quickchainproject/quickchain/consensus/dbft" 12 "github.com/quickchainproject/quickchain/core/types" 13 "github.com/quickchainproject/quickchain/event" 14 "github.com/quickchainproject/quickchain/log" 15 metrics "github.com/quickchainproject/quickchain/metrics" 16 "gopkg.in/karalabe/cookiejar.v2/collections/prque" 17 ) 18 19 // New creates an BFT consensus core 20 func New(backend bft.Backend, config *bft.Config) Engine { 21 c := &core{ 22 config: config, 23 address: backend.Address(), 24 state: StateAcceptRequest, 25 handlerWg: new(sync.WaitGroup), 26 logger: log.New("address", backend.Address()), 27 backend: backend, 28 backlogs: make(map[bft.Validator]*prque.Prque), 29 backlogsMu: new(sync.Mutex), 30 pendingRequests: prque.New(), 31 pendingRequestsMu: new(sync.Mutex), 32 consensusTimestamp: time.Time{}, 33 roundMeter: metrics.NewMeter(), 34 sequenceMeter: metrics.NewMeter(), 35 consensusTimer: metrics.NewTimer(), 36 } 37 metrics.Register("consensus/dbft/core/round", c.roundMeter) 38 metrics.Register("consensus/dbft/core/sequence", c.sequenceMeter) 39 metrics.Register("consensus/dbft/core/consensus", c.consensusTimer) 40 c.validateFn = c.checkValidatorSignature 41 return c 42 } 43 44 // ---------------------------------------------------------------------------- 45 46 type core struct { 47 config *bft.Config 48 address common.Address 49 state State 50 logger log.Logger 51 52 backend bft.Backend 53 events *event.TypeMuxSubscription 54 finalCommittedSub *event.TypeMuxSubscription 55 timeoutSub *event.TypeMuxSubscription 56 futurePreprepareTimer *time.Timer 57 58 valSet bft.ValidatorSet 59 waitingForRoundChange bool 60 validateFn func([]byte, []byte) (common.Address, error) 61 62 backlogs map[bft.Validator]*prque.Prque 63 backlogsMu *sync.Mutex 64 65 current *roundState 66 handlerWg *sync.WaitGroup 67 68 roundChangeSet *roundChangeSet 69 roundChangeTimer *time.Timer 70 71 pendingRequests *prque.Prque 72 pendingRequestsMu *sync.Mutex 73 74 consensusTimestamp time.Time 75 // the meter to record the round change rate 76 roundMeter metrics.Meter 77 // the meter to record the sequence update rate 78 sequenceMeter metrics.Meter 79 // the timer to record consensus duration (from accepting a preprepare to final committed stage) 80 consensusTimer metrics.Timer 81 } 82 83 func (c *core) finalizeMessage(msg *message) ([]byte, error) { 84 var err error 85 // Add sender address 86 msg.Address = c.Address() 87 88 // Add proof of consensus 89 msg.CommittedSeal = []byte{} 90 // Assign the CommittedSeal if it's a COMMIT message and proposal is not nil 91 if msg.Code == msgCommit && c.current.Proposal() != nil { 92 seal := PrepareCommittedSeal(c.current.Proposal().Hash()) 93 msg.CommittedSeal, err = c.backend.Sign(seal) 94 if err != nil { 95 return nil, err 96 } 97 } 98 99 // Sign message 100 data, err := msg.PayloadNoSig() 101 if err != nil { 102 return nil, err 103 } 104 msg.Signature, err = c.backend.Sign(data) 105 if err != nil { 106 return nil, err 107 } 108 109 // Convert to payload 110 payload, err := msg.Payload() 111 if err != nil { 112 return nil, err 113 } 114 115 return payload, nil 116 } 117 118 func (c *core) broadcast(msg *message) { 119 logger := c.logger.New("state", c.state) 120 121 payload, err := c.finalizeMessage(msg) 122 if err != nil { 123 logger.Error("Failed to finalize message", "msg", msg, "err", err) 124 return 125 } 126 127 // Broadcast payload 128 if err = c.backend.Broadcast(c.valSet, payload); err != nil { 129 logger.Error("Failed to broadcast message", "msg", msg, "err", err) 130 return 131 } 132 } 133 134 func (c *core) currentView() *bft.View { 135 return &bft.View{ 136 Sequence: new(big.Int).Set(c.current.Sequence()), 137 Round: new(big.Int).Set(c.current.Round()), 138 } 139 } 140 141 func (c *core) isProposer() bool { 142 v := c.valSet 143 if v == nil { 144 return false 145 } 146 return v.IsProposer(c.backend.Address()) 147 } 148 149 func (c *core) commit() { 150 c.setState(StateCommitted) 151 152 proposal := c.current.Proposal() 153 if proposal != nil { 154 committedSeals := make([][]byte, c.current.Commits.Size()) 155 for i, v := range c.current.Commits.Values() { 156 committedSeals[i] = make([]byte, types.BFTExtraSeal) 157 copy(committedSeals[i][:], v.CommittedSeal[:]) 158 } 159 160 if err := c.backend.Commit(proposal, committedSeals); err != nil { 161 c.current.UnlockHash() //Unlock block when insertion fails 162 c.sendNextRoundChange() 163 return 164 } 165 } 166 } 167 168 // startNewRound starts a new round. if round equals to 0, it means to starts a new sequence 169 func (c *core) startNewRound(round *big.Int) { 170 var logger log.Logger 171 if c.current == nil { 172 logger = c.logger.New("old_round", -1, "old_seq", 0) 173 } else { 174 logger = c.logger.New("old_round", c.current.Round(), "old_seq", c.current.Sequence()) 175 } 176 177 roundChange := false 178 // Try to get last proposal 179 lastProposal, lastProposer := c.backend.LastProposal() 180 if c.current == nil { 181 logger.Trace("Start to the initial round") 182 } else if lastProposal.Number().Cmp(c.current.Sequence()) >= 0 { 183 diff := new(big.Int).Sub(lastProposal.Number(), c.current.Sequence()) 184 c.sequenceMeter.Mark(new(big.Int).Add(diff, common.Big1).Int64()) 185 186 if !c.consensusTimestamp.IsZero() { 187 c.consensusTimer.UpdateSince(c.consensusTimestamp) 188 c.consensusTimestamp = time.Time{} 189 } 190 logger.Trace("Catch up latest proposal", "number", lastProposal.Number().Uint64(), "hash", lastProposal.Hash()) 191 } else if lastProposal.Number().Cmp(big.NewInt(c.current.Sequence().Int64()-1)) == 0 { 192 if round.Cmp(common.Big0) == 0 { 193 // same seq and round, don't need to start new round 194 return 195 } else if round.Cmp(c.current.Round()) < 0 { 196 logger.Warn("New round should not be smaller than current round", "seq", lastProposal.Number().Int64(), "new_round", round, "old_round", c.current.Round()) 197 return 198 } 199 roundChange = true 200 } else { 201 logger.Warn("New sequence should be larger than current sequence", "new_seq", lastProposal.Number().Int64()) 202 return 203 } 204 205 var newView *bft.View 206 if roundChange { 207 newView = &bft.View{ 208 Sequence: new(big.Int).Set(c.current.Sequence()), 209 Round: new(big.Int).Set(round), 210 } 211 } else { 212 newView = &bft.View{ 213 Sequence: new(big.Int).Add(lastProposal.Number(), common.Big1), 214 Round: new(big.Int), 215 } 216 c.valSet = c.backend.Validators(lastProposal) 217 } 218 219 // Update logger 220 logger = logger.New("old_proposer", c.valSet.GetProposer()) 221 // Clear invalid ROUND CHANGE messages 222 c.roundChangeSet = newRoundChangeSet(c.valSet) 223 // New snapshot for new round 224 c.updateRoundState(newView, c.valSet, roundChange) 225 // Calculate new proposer 226 c.valSet.CalcProposer(lastProposer, newView.Round.Uint64()) 227 c.waitingForRoundChange = false 228 c.setState(StateAcceptRequest) 229 if roundChange && c.isProposer() && c.current != nil { 230 // If it is locked, propose the old proposal 231 // If we have pending request, propose pending request 232 if c.current.IsHashLocked() { 233 r := &bft.Request{ 234 Proposal: c.current.Proposal(), //c.current.Proposal would be the locked proposal by previous proposer, see updateRoundState 235 } 236 c.sendPreprepare(r) 237 } else if c.current.pendingRequest != nil { 238 c.sendPreprepare(c.current.pendingRequest) 239 } 240 } 241 c.newRoundChangeTimer() 242 243 logger.Debug("New round", "new_round", newView.Round, "new_seq", newView.Sequence, "new_proposer", c.valSet.GetProposer(), "valSet", c.valSet.List(), "size", c.valSet.Size(), "isProposer", c.isProposer()) 244 } 245 246 func (c *core) catchUpRound(view *bft.View) { 247 logger := c.logger.New("old_round", c.current.Round(), "old_seq", c.current.Sequence(), "old_proposer", c.valSet.GetProposer()) 248 249 if view.Round.Cmp(c.current.Round()) > 0 { 250 c.roundMeter.Mark(new(big.Int).Sub(view.Round, c.current.Round()).Int64()) 251 } 252 c.waitingForRoundChange = true 253 254 // Need to keep block locked for round catching up 255 c.updateRoundState(view, c.valSet, true) 256 c.roundChangeSet.Clear(view.Round) 257 c.newRoundChangeTimer() 258 259 logger.Trace("Catch up round", "new_round", view.Round, "new_seq", view.Sequence, "new_proposer", c.valSet) 260 } 261 262 // updateRoundState updates round state by checking if locking block is necessary 263 func (c *core) updateRoundState(view *bft.View, validatorSet bft.ValidatorSet, roundChange bool) { 264 // Lock only if both roundChange is true and it is locked 265 if roundChange && c.current != nil { 266 if c.current.IsHashLocked() { 267 c.current = newRoundState(view, validatorSet, c.current.GetLockedHash(), c.current.Preprepare, c.current.pendingRequest, c.backend.HasBadProposal) 268 } else { 269 c.current = newRoundState(view, validatorSet, common.Hash{}, nil, c.current.pendingRequest, c.backend.HasBadProposal) 270 } 271 } else { 272 c.current = newRoundState(view, validatorSet, common.Hash{}, nil, nil, c.backend.HasBadProposal) 273 } 274 } 275 276 func (c *core) setState(state State) { 277 if c.state != state { 278 c.state = state 279 } 280 if state == StateAcceptRequest { 281 c.processPendingRequests() 282 } 283 c.processBacklog() 284 } 285 286 func (c *core) Address() common.Address { 287 return c.address 288 } 289 290 func (c *core) stopFuturePreprepareTimer() { 291 if c.futurePreprepareTimer != nil { 292 c.futurePreprepareTimer.Stop() 293 } 294 } 295 296 func (c *core) stopTimer() { 297 c.stopFuturePreprepareTimer() 298 if c.roundChangeTimer != nil { 299 c.roundChangeTimer.Stop() 300 } 301 } 302 303 func (c *core) newRoundChangeTimer() { 304 c.stopTimer() 305 306 // set timeout based on the round number 307 timeout := time.Duration(c.config.RequestTimeout) * time.Millisecond 308 round := c.current.Round().Uint64() 309 if round > 0 { 310 timeout += time.Duration(math.Pow(2, float64(round))) * time.Second 311 } 312 313 c.roundChangeTimer = time.AfterFunc(timeout, func() { 314 c.sendEvent(timeoutEvent{}) 315 }) 316 } 317 318 func (c *core) checkValidatorSignature(data []byte, sig []byte) (common.Address, error) { 319 return bft.CheckValidatorSignature(c.valSet, data, sig) 320 } 321 322 // PrepareCommittedSeal returns a committed seal for the given hash 323 func PrepareCommittedSeal(hash common.Hash) []byte { 324 var buf bytes.Buffer 325 buf.Write(hash.Bytes()) 326 buf.Write([]byte{byte(msgCommit)}) 327 return buf.Bytes() 328 }