github.com/bigzoro/my_simplechain@v0.0.0-20240315012955-8ad0a2a29bb9/consensus/pbft/core/core.go (about) 1 // Copyright 2020 The go-simplechain Authors 2 // This file is part of the go-simplechain library. 3 // 4 // The go-simplechain library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-simplechain library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-simplechain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package core 18 19 import ( 20 "bytes" 21 "math" 22 "math/big" 23 "sync" 24 "time" 25 26 "github.com/bigzoro/my_simplechain/common" 27 cmath "github.com/bigzoro/my_simplechain/common/math" 28 "github.com/bigzoro/my_simplechain/common/prque" 29 "github.com/bigzoro/my_simplechain/consensus/pbft" 30 "github.com/bigzoro/my_simplechain/core/types" 31 "github.com/bigzoro/my_simplechain/event" 32 "github.com/bigzoro/my_simplechain/log" 33 "github.com/bigzoro/my_simplechain/metrics" 34 ) 35 36 // New creates an Istanbul consensus core 37 func New(backend pbft.Backend, config *pbft.Config) Engine { 38 //r := metrics.NewRegistry() 39 c := &core{ 40 config: config, 41 address: backend.Address(), 42 state: StateAcceptRequest, 43 handlerWg: new(sync.WaitGroup), 44 logger: log.New("address", backend.Address()), 45 backend: backend, 46 backlogs: make(map[common.Address]*prque.Prque), 47 backlogsMu: new(sync.Mutex), 48 pendingRequests: prque.New(nil), 49 pendingRequestsMu: new(sync.Mutex), 50 } 51 52 c.roundMeter = metrics.NewRegisteredMeter("consensus/pbft/core/round", nil) 53 c.sequenceMeter = metrics.NewRegisteredMeter("consensus/pbft/core/sequence", nil) 54 c.consensusTimer = metrics.NewRegisteredTimer("consensus/pbft/core/consensus", nil) 55 56 //r.Register("consensus/pbft/core/round", c.roundMeter) 57 //r.Register("consensus/pbft/core/sequence", c.sequenceMeter) 58 //r.Register("consensus/pbft/core/consensus", c.consensusTimer) 59 60 c.validateFn = c.checkValidatorSignature 61 return c 62 } 63 64 // ---------------------------------------------------------------------------- 65 66 const ( 67 timeoutRate = 1.3 68 maxRoundTimeout = 10 69 ) 70 71 type core struct { 72 config *pbft.Config 73 address common.Address 74 state State 75 logger log.Logger 76 77 backend pbft.Backend 78 events *event.TypeMuxSubscription 79 finalCommittedSub *event.TypeMuxSubscription 80 timeoutSub *event.TypeMuxSubscription 81 futurePreprepareTimer *time.Timer 82 83 valSet pbft.ValidatorSet 84 validateFn func([]byte, []byte) (common.Address, error) 85 86 waitingForRoundChange bool 87 //roundChangeView *pbft.View 88 89 backlogs map[common.Address]*prque.Prque 90 backlogsMu *sync.Mutex 91 92 current *roundState 93 handlerWg *sync.WaitGroup 94 95 roundChangeSet *roundChangeSet 96 roundChangeTimer *time.Timer 97 98 pendingRequests *prque.Prque 99 pendingRequestsMu *sync.Mutex 100 101 // the meter to record the round change rate 102 roundMeter metrics.Meter 103 // the meter to record the sequence update rate 104 sequenceMeter metrics.Meter 105 // the timer to record consensus duration (from accepting a preprepare to final committed stage) 106 consensusTimer metrics.Timer 107 consensusTimestamp time.Time 108 109 preprepareTimer metrics.Timer 110 preprepareTimestamp time.Time 111 112 executeTimer metrics.Timer 113 executeTimestamp time.Time 114 115 prepareTimer metrics.Timer 116 prepareTimestamp time.Time 117 118 commitTimer metrics.Timer 119 commitTimestamp time.Time 120 } 121 122 func (c *core) finalizeMessage(msg *message) ([]byte, error) { 123 var err error 124 // Add sender address 125 msg.Address = c.Address() 126 127 // Add proof of consensus 128 msg.CommittedSeal = []byte{} 129 // Assign the CommittedSeal if it's a COMMIT message and proposal is not nil 130 if msg.Code == msgCommit && c.current.Conclusion() != nil { 131 seal := PrepareCommittedSeal(c.current.Conclusion().Hash()) 132 msg.CommittedSeal, err = c.backend.Sign(seal) 133 if err != nil { 134 return nil, err 135 } 136 } 137 138 // Sign message 139 data, err := msg.PayloadNoSig() 140 if err != nil { 141 return nil, err 142 } 143 msg.Signature, err = c.backend.Sign(data) 144 if err != nil { 145 return nil, err 146 } 147 148 // Convert to payload 149 payload, err := msg.Payload() 150 if err != nil { 151 return nil, err 152 } 153 154 return payload, nil 155 } 156 157 //func (c *core) broadcast(msg *message, self bool) { 158 // logger := c.logger.New("state", c.state) 159 // 160 // payload, err := c.finalizeMessage(msg) 161 // if err != nil { 162 // logger.Error("Failed to finalize message", "msg", msg, "err", err) 163 // return 164 // } 165 // 166 // // Broadcast payload 167 // if err = c.backend.Broadcast(c.valSet, msg.Address, payload); err != nil { 168 // logger.Error("Failed to broadcast message", "msg", msg, "err", err) 169 // } 170 // // Post payload 171 // if self { 172 // c.backend.Post(payload) 173 // } 174 //} 175 176 func (c *core) broadcast(msg *message, self bool) { 177 logger := c.logger.New("state", c.state) 178 ps, nodes := c.backend.GetForwardNodes(c.valSet.List()) 179 // finalize forward nodes too 180 msg.ForwardNodes = nodes 181 payload, err := c.finalizeMessage(msg) 182 if err != nil { 183 logger.Error("Failed to finalize message", "msg", msg, "err", err) 184 return 185 } 186 187 // Broadcast payload 188 if err = c.backend.BroadcastMsg(ps, msg.Hash(), payload); err != nil { 189 logger.Error("Failed to broadcast message", "msg", msg, "err", err) 190 } 191 // Post payload 192 if self { 193 c.backend.Post(payload) 194 } 195 } 196 197 func (c *core) send(msg *message, val pbft.Validators) { 198 logger := c.logger.New("state", c.state) 199 200 payload, err := c.finalizeMessage(msg) 201 if err != nil { 202 logger.Error("Failed to finalize message", "msg", msg, "err", err) 203 return 204 } 205 206 if err = c.backend.SendMsg(val, payload); err != nil { 207 logger.Error("Failed to send message", "msg", msg, "err", err) 208 return 209 } 210 } 211 212 func (c *core) forward(msg *message, src pbft.Validator) bool { 213 logger := c.logger.New("state", c.state, "from", src) 214 215 // get forwardNodes from msg 216 forwardNodes := msg.ForwardNodes 217 // no nodes need to forward, exit 218 if forwardNodes == nil { 219 return false 220 } 221 var forwardValidator pbft.Validators 222 for _, forward := range forwardNodes { 223 if i, val := c.valSet.GetByAddress(forward); i >= 0 { 224 forwardValidator = append(forwardValidator, val) 225 } else { 226 logger.Warn("invalid forward node", "address", forward) 227 } 228 } 229 230 // calc forwardPeers and remain forwardNodes 231 ps, remainNodes := c.backend.GetForwardNodes(forwardValidator) 232 // no forward peers existed in protocol, exit 233 if ps == nil { 234 return false 235 } 236 237 // create message with new forwardNodes 238 msg.ForwardNodes = remainNodes 239 payload, err := msg.Payload() 240 if err != nil { 241 logger.Error("Failed to forward message", "msg", msg, "err", err) 242 return false 243 } 244 245 if err = c.backend.BroadcastMsg(ps, msg.Hash(), payload); err != nil { 246 logger.Error("Failed to forward message", "msg", msg, "err", err) 247 return false 248 } 249 250 return true 251 } 252 253 func (c *core) currentView() *pbft.View { 254 return &pbft.View{ 255 Sequence: new(big.Int).Set(c.current.Sequence()), 256 Round: new(big.Int).Set(c.current.Round()), 257 } 258 } 259 260 func (c *core) IsProposer() bool { 261 v := c.valSet 262 if v == nil { 263 return false 264 } 265 return v.IsProposer(c.backend.Address()) 266 } 267 268 func (c *core) GetCurrentBlock() *types.Block { 269 if c.current == nil || c.current.Conclusion() == nil { 270 return nil 271 } 272 return c.current.Conclusion().(*types.Block) 273 } 274 275 func (c *core) IsCurrentProposal(proposalHash common.Hash) bool { 276 return c.current != nil && c.current.pendingRequest != nil && c.current.pendingRequest.Proposal.PendingHash() == proposalHash 277 } 278 279 func (c *core) IsHashLocked(blockHash common.Hash) bool { 280 return c.current != nil && c.current.IsHashLocked() && c.current.GetLockedHash() == blockHash 281 } 282 283 func (c *core) commit() { 284 c.setState(StateCommitted) 285 286 conclusion := c.current.Conclusion() 287 if conclusion != nil { 288 // merge commits 289 committedSeals := make([][]byte, c.current.Commits.Size()) 290 for i, v := range c.current.Commits.Values() { 291 committedSeals[i] = make([]byte, types.ByzantineExtraSeal) 292 copy(committedSeals[i][:], v.CommittedSeal[:]) 293 } 294 295 if err := c.backend.Commit(conclusion, committedSeals); err != nil { 296 c.logger.Error("Commit Failed", "conclusion", conclusion.Hash(), "num", conclusion.Number(), "err", err) 297 c.current.UnlockHash() //Unlock block when insertion fails 298 c.sendNextRoundChange() 299 } 300 } 301 302 } 303 304 // startNewRound starts a new round. if round equals to 0, it means to starts a new sequence 305 func (c *core) startNewRound(round *big.Int) { 306 var logger log.Logger 307 if c.current == nil { 308 logger = c.logger.New("old_round", -1, "old_seq", 0) 309 } else { 310 logger = c.logger.New("old_round", c.current.Round(), "old_seq", c.current.Sequence()) 311 } 312 313 roundChange := false 314 // Try to get last proposal(conclusion) 315 _, lastProposal, lastProposer := c.backend.LastProposal() 316 if c.current == nil { 317 logger.Trace("Start to the initial round") 318 319 } else if lastProposal.Number().Cmp(c.current.Sequence()) >= 0 { 320 diff := new(big.Int).Sub(lastProposal.Number(), c.current.Sequence()) 321 c.sequenceMeter.Mark(new(big.Int).Add(diff, common.Big1).Int64()) 322 323 if !c.consensusTimestamp.IsZero() { 324 c.consensusTimer.UpdateSince(c.consensusTimestamp) 325 c.consensusTimestamp = time.Time{} 326 } 327 //logger.Trace("Catch up latest proposal", "number", lastProposal.Number().Uint64(), "hash", lastProposal.Hash()) 328 logger.Trace("Catch up latest proposal", "number", lastProposal.Number().Uint64(), "hash", lastProposal.PendingHash()) 329 330 } else if lastProposal.Number().Cmp(big.NewInt(c.current.Sequence().Int64()-1)) == 0 { 331 if round.Cmp(common.Big0) == 0 { 332 // same seq and round, don't need to start new round 333 return 334 } else if round.Cmp(c.current.Round()) < 0 { 335 logger.Warn("New round should not be smaller than current round", "seq", lastProposal.Number().Int64(), "new_round", round, "old_round", c.current.Round()) 336 return 337 } 338 roundChange = true 339 340 } else { 341 logger.Warn("New sequence should be larger than current sequence", "new_seq", lastProposal.Number().Int64()) 342 return 343 } 344 345 var newView *pbft.View 346 if roundChange { 347 newView = &pbft.View{ 348 Sequence: new(big.Int).Set(c.current.Sequence()), 349 Round: new(big.Int).Set(round), 350 } 351 } else { 352 newView = &pbft.View{ 353 Sequence: new(big.Int).Add(lastProposal.Number(), common.Big1), 354 Round: new(big.Int), 355 } 356 c.valSet = c.backend.Validators(lastProposal) 357 } 358 359 // Update logger 360 logger = logger.New("old_proposer", c.valSet.GetProposer()) 361 // Clear invalid ROUND CHANGE messages 362 c.roundChangeSet = newRoundChangeSet(c.valSet) 363 // New snapshot for new round 364 c.updateRoundState(newView, c.valSet, roundChange) 365 // Calculate new proposer 366 c.valSet.CalcProposer(lastProposer, newView.Round.Uint64()) 367 c.waitingForRoundChange = false 368 c.setState(StateAcceptRequest) 369 370 log.Report("startNewRound >>", "IsProposer", c.IsProposer(), "roundChange", roundChange) 371 372 if roundChange && c.IsProposer() && c.current != nil { 373 // 1.If it is locked, propose the old proposal 374 // 2.If we have pending request, propose pending request 375 if c.current.IsHashLocked() { 376 r := &pbft.Request{ 377 Proposal: c.current.Proposal(), //c.current.Proposal would be the locked proposal by previous proposer, see updateRoundState 378 } 379 c.sendPreprepare(r) 380 } else if c.current.pendingRequest != nil { 381 c.sendPreprepare(c.current.pendingRequest) 382 } 383 } 384 // start new timeout timer 385 c.newRoundChangeTimer() 386 387 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()) 388 } 389 390 func (c *core) catchUpRound(view *pbft.View) { 391 logger := c.logger.New("old_round", c.current.Round(), "old_seq", c.current.Sequence(), "old_proposer", c.valSet.GetProposer()) 392 393 if view.Round.Cmp(c.current.Round()) > 0 { 394 c.roundMeter.Mark(new(big.Int).Sub(view.Round, c.current.Round()).Int64()) 395 } 396 397 c.waitingForRoundChange = true 398 //c.roundChangeView = view 399 400 // Need to keep block locked for round catching up 401 c.updateRoundState(view, c.valSet, true) //TODO 402 c.roundChangeSet.Clear(view.Round) 403 c.newRoundChangeTimer() 404 405 logger.Trace("Catch up round", "new_round", view.Round, "new_seq", view.Sequence, "new_proposer", c.valSet) 406 } 407 408 // updateRoundState updates round state by checking if locking block is necessary 409 func (c *core) updateRoundState(view *pbft.View, validatorSet pbft.ValidatorSet, roundChange bool) { 410 //logger := c.logger.New("view", view, "validators", validatorSet.List(), "rondChange", roundChange) 411 // Lock only if both roundChange is true and it is locked 412 if roundChange && c.current != nil { 413 if c.current.IsHashLocked() { 414 //logger.Error("[report] new round state with locked proposal", "locked", c.current.GetLockedHash()) 415 c.current = newRoundState(view, validatorSet, c.current.GetLockedHash(), c.current.Preprepare, c.current.Prepare, c.current.pendingRequest, c.backend.HasBadProposal) 416 } else { 417 //logger.Error("[report] new round state with request", "pendingRequest", c.current.pendingRequest) 418 c.current = newRoundState(view, validatorSet, common.Hash{}, nil, nil, c.current.pendingRequest, c.backend.HasBadProposal) 419 } 420 } else { 421 //logger.Error("[report] new round state") 422 c.current = newRoundState(view, validatorSet, common.Hash{}, nil, nil, nil, c.backend.HasBadProposal) 423 } 424 } 425 426 func (c *core) setState(state State) { 427 if c.state != state { 428 c.state = state 429 } 430 if state == StateAcceptRequest { 431 c.processPendingRequests() 432 } 433 c.processBacklog() 434 } 435 436 func (c *core) Address() common.Address { 437 return c.address 438 } 439 440 func (c *core) stopFuturePreprepareTimer() { 441 if c.futurePreprepareTimer != nil { 442 c.futurePreprepareTimer.Stop() 443 } 444 } 445 446 func (c *core) stopTimer() { 447 c.stopFuturePreprepareTimer() 448 if c.roundChangeTimer != nil { 449 c.roundChangeTimer.Stop() 450 } 451 } 452 453 func (c *core) newRoundChangeTimer() { 454 logger := c.logger.New("state", c.state, "sequence", c.current.Sequence(), "round", c.current.Round()) 455 c.stopTimer() 456 457 // set timeout based on the round number 458 round := cmath.Uint64Min(c.current.Round().Uint64(), maxRoundTimeout) 459 timeout := time.Duration(c.config.RequestTimeout) * time.Millisecond * time.Duration(math.Pow(timeoutRate, float64(round))) 460 461 c.roundChangeTimer = time.AfterFunc(timeout, func() { 462 logger.Warn("timeout, send view change", "timeout", timeout) 463 c.sendEvent(timeoutEvent{}) //FIXME: send timeoutEvent 464 }) 465 } 466 467 func (c *core) checkValidatorSignature(data []byte, sig []byte) (common.Address, error) { 468 return pbft.CheckValidatorSignature(c.valSet, data, sig) 469 } 470 471 func (c *core) Confirmations() int { 472 // Confirmation Formula used ceil(2N/3) 473 return int(math.Ceil(float64(2*c.valSet.Size()) / 3)) 474 } 475 476 // PrepareCommittedSeal returns a committed seal for the given hash 477 func PrepareCommittedSeal(hash common.Hash) []byte { 478 var buf bytes.Buffer 479 buf.Write(hash.Bytes()) 480 buf.Write([]byte{byte(msgCommit)}) 481 return buf.Bytes() 482 }