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 }