github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/consensus/istanbul/core/preprepare.go (about)

     1  // Copyright 2017 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum 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-ethereum 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-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package core
    18  
    19  import (
    20  	"time"
    21  
    22  	"github.com/ethereum/go-ethereum/common"
    23  	"github.com/ethereum/go-ethereum/consensus"
    24  	"github.com/ethereum/go-ethereum/consensus/istanbul"
    25  )
    26  
    27  func (c *core) sendPreprepare(request *istanbul.Request, roundChangeCertificate istanbul.RoundChangeCertificate) {
    28  	logger := c.newLogger("func", "sendPreprepare")
    29  
    30  	// If I'm the proposer and I have the same sequence with the proposal
    31  	if c.current.Sequence().Cmp(request.Proposal.Number()) == 0 && c.isProposer() {
    32  		curView := c.current.View()
    33  		preprepare, err := Encode(&istanbul.Preprepare{
    34  			View:                   curView,
    35  			Proposal:               request.Proposal,
    36  			RoundChangeCertificate: roundChangeCertificate,
    37  		})
    38  		if err != nil {
    39  			logger.Error("Failed to prepare message")
    40  			return
    41  		}
    42  
    43  		msg := &istanbul.Message{
    44  			Code: istanbul.MsgPreprepare,
    45  			Msg:  preprepare,
    46  		}
    47  		logger.Debug("Sending preprepare", "m", msg)
    48  		c.broadcast(msg)
    49  	}
    50  }
    51  
    52  func (c *core) handlePreprepare(msg *istanbul.Message) error {
    53  	logger := c.newLogger("func", "handlePreprepare", "tag", "handleMsg", "from", msg.Address)
    54  	logger.Trace("Got preprepare message", "m", msg)
    55  
    56  	// Decode PREPREPARE
    57  	var preprepare *istanbul.Preprepare
    58  	err := msg.Decode(&preprepare)
    59  	if err != nil {
    60  		return errFailedDecodePreprepare
    61  	}
    62  
    63  	// TODO(tim) Fix and Move checkMessage check up to here
    64  
    65  	// Verify that the proposal is for the sequence number of the view we verified.
    66  	if preprepare.View.Sequence.Cmp(preprepare.Proposal.Number()) != 0 {
    67  		logger.Warn("Received preprepare with invalid block number", "number", preprepare.Proposal.Number(), "view_seq", preprepare.View.Sequence, "view_round", preprepare.View.Round)
    68  		return errInvalidProposal
    69  	}
    70  
    71  	// Check proposer is valid for the message's view (this may be a subsequent round)
    72  	headBlock, headProposer := c.backend.GetCurrentHeadBlockAndAuthor()
    73  	if headBlock == nil {
    74  		logger.Error("Could not determine head proposer")
    75  		return errNotFromProposer
    76  	}
    77  	proposerForMsgRound := c.selectProposer(c.current.ValidatorSet(), headProposer, preprepare.View.Round.Uint64())
    78  	if proposerForMsgRound.Address() != msg.Address {
    79  		logger.Warn("Ignore preprepare message from non-proposer", "actual_proposer", proposerForMsgRound.Address())
    80  		return errNotFromProposer
    81  	}
    82  
    83  	// If round > 0, handle the ROUND CHANGE certificate. If round = 0, it should not have a ROUND CHANGE certificate
    84  	if preprepare.View.Round.Cmp(common.Big0) > 0 {
    85  		if !preprepare.HasRoundChangeCertificate() {
    86  			logger.Error("Preprepare for non-zero round did not contain a round change certificate.")
    87  			return errMissingRoundChangeCertificate
    88  		}
    89  		subject := istanbul.Subject{
    90  			View:   preprepare.View,
    91  			Digest: preprepare.Proposal.Hash(),
    92  		}
    93  		// This also moves us to the next round if the certificate is valid.
    94  		err := c.handleRoundChangeCertificate(subject, preprepare.RoundChangeCertificate)
    95  		if err != nil {
    96  			logger.Warn("Invalid round change certificate with preprepare.", "err", err)
    97  			return err
    98  		}
    99  	} else if preprepare.HasRoundChangeCertificate() {
   100  		logger.Error("Preprepare for round 0 has a round change certificate.")
   101  		return errInvalidProposal
   102  	}
   103  
   104  	// Ensure we have the same view with the PREPREPARE message.
   105  	if err := c.checkMessage(istanbul.MsgPreprepare, preprepare.View); err != nil {
   106  		if err == errOldMessage {
   107  			// Get validator set for the given proposal
   108  			valSet := c.backend.ParentBlockValidators(preprepare.Proposal)
   109  			prevBlockAuthor := c.backend.AuthorForBlock(preprepare.Proposal.Number().Uint64() - 1)
   110  			proposer := c.selectProposer(valSet, prevBlockAuthor, preprepare.View.Round.Uint64())
   111  
   112  			// We no longer broadcast a COMMIT if this is a PREPREPARE from the correct proposer for an existing block.
   113  			// However, we log a WARN for potential future debugging value.
   114  			if proposer.Address() == msg.Address && c.backend.HasBlock(preprepare.Proposal.Hash(), preprepare.Proposal.Number()) {
   115  				logger.Warn("Would have sent a commit message for an old block", "view", preprepare.View, "block_hash", preprepare.Proposal.Hash())
   116  				return nil
   117  			}
   118  		}
   119  		// Probably shouldn't errFutureMessage as we should have moved to that round in handleRoundChangeCertificate
   120  		logger.Trace("Check preprepare failed", "err", err)
   121  		return err
   122  	}
   123  
   124  	// Verify the proposal we received
   125  	if duration, err := c.verifyProposal(preprepare.Proposal); err != nil {
   126  		logger.Warn("Failed to verify proposal", "err", err, "duration", duration)
   127  		// if it's a future block, we will handle it again after the duration
   128  		if err == consensus.ErrFutureBlock {
   129  			c.stopFuturePreprepareTimer()
   130  			c.futurePreprepareTimer = time.AfterFunc(duration, func() {
   131  				c.sendEvent(backlogEvent{
   132  					msg: msg,
   133  				})
   134  			})
   135  		}
   136  		return err
   137  	}
   138  
   139  	if c.current.State() == StateAcceptRequest {
   140  		logger.Trace("Accepted preprepare", "tag", "stateTransition")
   141  		c.consensusTimestamp = time.Now()
   142  
   143  		err := c.current.TransitionToPreprepared(preprepare)
   144  		if err != nil {
   145  			return err
   146  		}
   147  
   148  		// Process Backlog Messages
   149  		c.backlog.updateState(c.current.View(), c.current.State())
   150  		c.sendPrepare()
   151  	}
   152  
   153  	return nil
   154  }