github.com/klaytn/klaytn@v1.12.1/consensus/istanbul/core/prepare.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/prepare.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package core
    22  
    23  import (
    24  	"github.com/klaytn/klaytn/consensus/istanbul"
    25  )
    26  
    27  func (c *core) sendPrepare() {
    28  	logger := c.logger.NewWith("state", c.state)
    29  
    30  	sub := c.current.Subject()
    31  	prevHash := c.current.Proposal().ParentHash()
    32  
    33  	// Do not send message if the owner of the core is not a member of the committee for the `sub.View`
    34  	if !c.valSet.CheckInSubList(prevHash, sub.View, c.Address()) {
    35  		return
    36  	}
    37  
    38  	encodedSubject, err := Encode(sub)
    39  	if err != nil {
    40  		logger.Error("Failed to encode", "subject", sub)
    41  		return
    42  	}
    43  
    44  	c.broadcast(&message{
    45  		Hash: prevHash,
    46  		Code: msgPrepare,
    47  		Msg:  encodedSubject,
    48  	})
    49  }
    50  
    51  func (c *core) handlePrepare(msg *message, src istanbul.Validator) error {
    52  	// Decode PREPARE message
    53  	var prepare *istanbul.Subject
    54  	err := msg.Decode(&prepare)
    55  	if err != nil {
    56  		logger.Error("Failed to decode message", "code", msg.Code, "err", err)
    57  		return errInvalidMessage
    58  	}
    59  
    60  	// logger.Error("call receive prepare","num",prepare.View.Sequence)
    61  	if err := c.checkMessage(msgPrepare, prepare.View); err != nil {
    62  		return err
    63  	}
    64  
    65  	// If it is locked, it can only process on the locked block.
    66  	// Passing verifyPrepare and checkMessage implies it is processing on the locked block since it was verified in the Preprepared state.
    67  	if err := c.verifyPrepare(prepare, src); err != nil {
    68  		return err
    69  	}
    70  
    71  	if !c.valSet.CheckInSubList(msg.Hash, prepare.View, src.Address()) {
    72  		logger.Warn("received an istanbul prepare message from non-committee",
    73  			"currentSequence", c.current.sequence.Uint64(), "sender", src.Address().String(), "msgView", prepare.View.String())
    74  		return errNotFromCommittee
    75  	}
    76  
    77  	c.acceptPrepare(msg, src)
    78  
    79  	// Change to Prepared state if we've received enough PREPARE/COMMIT messages or it is locked
    80  	// and we are in earlier state before Prepared state.
    81  	// Both of PREPARE and COMMIT messages are counted since the nodes which is hashlocked in
    82  	// the previous round skip sending PREPARE messages.
    83  	if c.state.Cmp(StatePrepared) < 0 {
    84  		if c.current.IsHashLocked() && prepare.Digest == c.current.GetLockedHash() {
    85  			logger.Warn("received prepare of the hash locked proposal and change state to prepared", "msgType", msgPrepare)
    86  			c.setState(StatePrepared)
    87  			c.sendCommit()
    88  		} else if c.current.GetPrepareOrCommitSize() >= RequiredMessageCount(c.valSet) {
    89  			logger.Info("received a quorum of the messages and change state to prepared", "msgType", msgPrepare, "prepareMsgNum", c.current.Prepares.Size(), "commitMsgNum", c.current.Commits.Size(), "valSet", c.valSet.Size())
    90  			c.current.LockHash()
    91  			c.setState(StatePrepared)
    92  			c.sendCommit()
    93  		}
    94  	}
    95  
    96  	return nil
    97  }
    98  
    99  // verifyPrepare verifies if the received PREPARE message is equivalent to our subject
   100  func (c *core) verifyPrepare(prepare *istanbul.Subject, src istanbul.Validator) error {
   101  	logger := c.logger.NewWith("from", src, "state", c.state)
   102  
   103  	sub := c.current.Subject()
   104  	if !prepare.Equal(sub) {
   105  		logger.Warn("Inconsistent subjects between PREPARE and proposal", "expected", sub, "got", prepare)
   106  		return errInconsistentSubject
   107  	}
   108  
   109  	return nil
   110  }
   111  
   112  func (c *core) acceptPrepare(msg *message, src istanbul.Validator) error {
   113  	logger := c.logger.NewWith("from", src, "state", c.state)
   114  
   115  	// Add the PREPARE message to current round state
   116  	if err := c.current.Prepares.Add(msg); err != nil {
   117  		logger.Error("Failed to add PREPARE message to round state", "msg", msg, "err", err)
   118  		return err
   119  	}
   120  
   121  	return nil
   122  }