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  }