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