github.com/klaytn/klaytn@v1.10.2/consensus/istanbul/core/preprepare.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2017 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from quorum/consensus/istanbul/core/preprepare.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package core 22 23 import ( 24 "time" 25 26 "github.com/klaytn/klaytn/blockchain/types" 27 "github.com/klaytn/klaytn/consensus" 28 "github.com/klaytn/klaytn/consensus/istanbul" 29 ) 30 31 func (c *core) sendPreprepare(request *istanbul.Request) { 32 logger := c.logger.NewWith("state", c.state) 33 34 header := types.SetRoundToHeader(request.Proposal.Header(), c.currentView().Round.Int64()) 35 request.Proposal = request.Proposal.WithSeal(header) 36 37 // If I'm the proposer and I have the same sequence with the proposal 38 if c.current.Sequence().Cmp(request.Proposal.Number()) == 0 && c.isProposer() { 39 curView := c.currentView() 40 preprepare, err := Encode(&istanbul.Preprepare{ 41 View: curView, 42 Proposal: request.Proposal, 43 }) 44 if err != nil { 45 logger.Error("Failed to encode", "view", curView) 46 return 47 } 48 49 c.broadcast(&message{ 50 Hash: request.Proposal.ParentHash(), 51 Code: msgPreprepare, 52 Msg: preprepare, 53 }) 54 } 55 } 56 57 func (c *core) handlePreprepare(msg *message, src istanbul.Validator) error { 58 logger := c.logger.NewWith("from", src, "state", c.state) 59 60 // Decode PRE-PREPARE 61 var preprepare *istanbul.Preprepare 62 err := msg.Decode(&preprepare) 63 if err != nil { 64 logger.Error("Failed to decode message", "code", msg.Code, "err", err) 65 return errInvalidMessage 66 } 67 68 // Ensure we have the same view with the PRE-PREPARE message 69 // If it is old message, see if we need to broadcast COMMIT 70 if err := c.checkMessage(msgPreprepare, preprepare.View); err != nil { 71 if err == errOldMessage { 72 // Get validator set for the given proposal 73 valSet := c.backend.ParentValidators(preprepare.Proposal).Copy() 74 previousProposer := c.backend.GetProposer(preprepare.Proposal.Number().Uint64() - 1) 75 valSet.CalcProposer(previousProposer, preprepare.View.Round.Uint64()) 76 // Broadcast COMMIT if it is an existing block 77 // 1. The proposer needs to be a proposer matches the given (Sequence + Round) 78 // 2. The given block must exist 79 if valSet.IsProposer(src.Address()) && c.backend.HasPropsal(preprepare.Proposal.Hash(), preprepare.Proposal.Number()) { 80 c.sendCommitForOldBlock(preprepare.View, preprepare.Proposal.Hash(), preprepare.Proposal.ParentHash()) 81 return nil 82 } 83 } 84 return err 85 } 86 87 // Check if the message comes from current proposer 88 if !c.valSet.IsProposer(src.Address()) { 89 logger.Warn("Ignore preprepare messages from non-proposer") 90 return errNotFromProposer 91 } 92 93 // Verify the proposal we received 94 if duration, err := c.backend.Verify(preprepare.Proposal); err != nil { 95 logger.Warn("Failed to verify proposal", "err", err, "duration", duration) 96 // if it's a future block, we will handle it again after the duration 97 if err == consensus.ErrFutureBlock { 98 c.stopFuturePreprepareTimer() 99 c.futurePreprepareTimer = time.AfterFunc(duration, func() { 100 c.sendEvent(backlogEvent{ 101 src: src.Address(), 102 msg: msg, 103 Hash: msg.Hash, 104 }) 105 }) 106 } else { 107 c.sendNextRoundChange("handlePreprepare. Proposal verification failure. Not ErrFutureBlock") 108 } 109 return err 110 } 111 112 // Here is about to accept the PRE-PREPARE 113 if c.state == StateAcceptRequest { 114 // Send ROUND CHANGE if the locked proposal and the received proposal are different 115 if c.current.IsHashLocked() { 116 header := types.SetRoundToHeader(c.current.Preprepare.Proposal.Header(), c.currentView().Round.Int64()) 117 c.current.Preprepare.Proposal = c.current.Preprepare.Proposal.WithSeal(header) 118 119 if preprepare.Proposal.Hash() == c.current.GetLockedHash() { 120 logger.Warn("Received preprepare message of the hash locked proposal and change state to prepared") 121 // Broadcast COMMIT and enters Prepared state directly 122 c.acceptPreprepare(preprepare) 123 c.setState(StatePrepared) 124 c.sendCommit() 125 } else { 126 // Send round change 127 c.sendNextRoundChange("handlePreprepare. HashLocked, but received hash is different from locked hash") 128 } 129 } else { 130 // Either 131 // 1. the locked proposal and the received proposal match 132 // 2. we have no locked proposal 133 c.acceptPreprepare(preprepare) 134 c.setState(StatePreprepared) 135 c.sendPrepare() 136 } 137 } 138 139 return nil 140 } 141 142 func (c *core) acceptPreprepare(preprepare *istanbul.Preprepare) { 143 c.consensusTimestamp = time.Now() 144 c.current.SetPreprepare(preprepare) 145 }