github.com/klaytn/klaytn@v1.12.1/consensus/istanbul/core/commit.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/commit.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package core
    22  
    23  import (
    24  	"github.com/klaytn/klaytn/common"
    25  	"github.com/klaytn/klaytn/consensus/istanbul"
    26  )
    27  
    28  func (c *core) sendCommit() {
    29  	logger := c.logger.NewWith("state", c.state)
    30  	if c.current.Preprepare == nil {
    31  		logger.Error("Failed to get parentHash from roundState in sendCommit")
    32  		return
    33  	}
    34  
    35  	sub := c.current.Subject()
    36  	prevHash := c.current.Proposal().ParentHash()
    37  
    38  	// Do not send message if the owner of the core is not a member of the committee for the `sub.View`
    39  	if !c.valSet.CheckInSubList(prevHash, sub.View, c.Address()) {
    40  		return
    41  	}
    42  
    43  	// TODO-Klaytn-Istanbul: generalize broadcastCommit for all istanbul message types
    44  	c.broadcastCommit(sub)
    45  }
    46  
    47  func (c *core) sendCommitForOldBlock(view *istanbul.View, digest common.Hash, prevHash common.Hash) {
    48  	sub := &istanbul.Subject{
    49  		View:     view,
    50  		Digest:   digest,
    51  		PrevHash: prevHash,
    52  	}
    53  	c.broadcastCommit(sub)
    54  }
    55  
    56  func (c *core) broadcastCommit(sub *istanbul.Subject) {
    57  	logger := c.logger.NewWith("state", c.state)
    58  
    59  	encodedSubject, err := Encode(sub)
    60  	if err != nil {
    61  		logger.Error("Failed to encode", "subject", sub)
    62  		return
    63  	}
    64  
    65  	c.broadcast(&message{
    66  		Hash: sub.PrevHash,
    67  		Code: msgCommit,
    68  		Msg:  encodedSubject,
    69  	})
    70  }
    71  
    72  func (c *core) handleCommit(msg *message, src istanbul.Validator) error {
    73  	// Decode COMMIT message
    74  	var commit *istanbul.Subject
    75  	err := msg.Decode(&commit)
    76  	if err != nil {
    77  		logger.Error("Failed to decode message", "code", msg.Code, "err", err)
    78  		return errInvalidMessage
    79  	}
    80  
    81  	if vrank != nil {
    82  		vrank.AddCommit(commit, src)
    83  	}
    84  
    85  	// logger.Error("receive handle commit","num", commit.View.Sequence)
    86  	if err := c.checkMessage(msgCommit, commit.View); err != nil {
    87  		// logger.Error("### istanbul/commit.go checkMessage","num",commit.View.Sequence,"err",err)
    88  		return err
    89  	}
    90  
    91  	if err := c.verifyCommit(commit, src); err != nil {
    92  		return err
    93  	}
    94  
    95  	if !c.valSet.CheckInSubList(msg.Hash, commit.View, src.Address()) {
    96  		logger.Warn("received an istanbul commit message from non-committee",
    97  			"currentSequence", c.current.sequence.Uint64(), "sender", src.Address().String(), "msgView", commit.View.String())
    98  		return errNotFromCommittee
    99  	}
   100  
   101  	c.acceptCommit(msg, src)
   102  
   103  	// Change to Prepared state if we've received enough PREPARE/COMMIT messages or it is locked
   104  	// and we are in earlier state before Prepared state.
   105  	// Both of PREPARE and COMMIT messages are counted since the nodes which is hashlocked in
   106  	// the previous round skip sending PREPARE messages.
   107  	if c.state.Cmp(StatePrepared) < 0 {
   108  		if c.current.IsHashLocked() && commit.Digest == c.current.GetLockedHash() {
   109  			logger.Warn("received commit of the hash locked proposal and change state to prepared", "msgType", msgCommit)
   110  			c.setState(StatePrepared)
   111  			c.sendCommit()
   112  		} else if c.current.GetPrepareOrCommitSize() >= RequiredMessageCount(c.valSet) {
   113  			logger.Info("received a quorum of the messages and change state to prepared", "msgType", msgCommit, "valSet", c.valSet.Size())
   114  			c.current.LockHash()
   115  			c.setState(StatePrepared)
   116  			c.sendCommit()
   117  		}
   118  	}
   119  
   120  	// Commit the proposal once we have enough COMMIT messages and we are not in the Committed state.
   121  	//
   122  	// If we already have a proposal, we may have chance to speed up the consensus process
   123  	// by committing the proposal without PREPARE messages.
   124  	//logger.Error("### consensus check","len(commits)",c.current.Commits.Size(),"f(2/3)",2*c.valSet.F(),"state",c.state.Cmp(StateCommitted))
   125  	if c.state.Cmp(StateCommitted) < 0 && c.current.Commits.Size() >= RequiredMessageCount(c.valSet) {
   126  		// Still need to call LockHash here since state can skip Prepared state and jump directly to the Committed state.
   127  		c.current.LockHash()
   128  		c.commit()
   129  	}
   130  
   131  	return nil
   132  }
   133  
   134  // verifyCommit verifies if the received COMMIT message is equivalent to our subject
   135  func (c *core) verifyCommit(commit *istanbul.Subject, src istanbul.Validator) error {
   136  	logger := c.logger.NewWith("from", src, "state", c.state)
   137  
   138  	sub := c.current.Subject()
   139  	if !commit.Equal(sub) {
   140  		logger.Warn("Inconsistent subjects between commit and proposal", "expected", sub, "got", commit)
   141  		return errInconsistentSubject
   142  	}
   143  
   144  	return nil
   145  }
   146  
   147  func (c *core) acceptCommit(msg *message, src istanbul.Validator) error {
   148  	logger := c.logger.NewWith("from", src, "state", c.state)
   149  
   150  	// Add the COMMIT message to current round state
   151  	if err := c.current.Commits.Add(msg); err != nil {
   152  		logger.Error("Failed to record commit message", "msg", msg, "err", err)
   153  		return err
   154  	}
   155  
   156  	return nil
   157  }