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  }