github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/consensus/istanbul/qbft/core/commit.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  	"github.com/kisexp/xdchain/common/hexutil"
    21  	qbfttypes "github.com/kisexp/xdchain/consensus/istanbul/qbft/types"
    22  	"github.com/kisexp/xdchain/core/types"
    23  	"github.com/kisexp/xdchain/rlp"
    24  )
    25  
    26  // broadcastCommit is called when receiving quorum of PREPARE message
    27  
    28  // It
    29  // - creates a COMMIT message from current proposal
    30  // - broadcast COMMIT message to other validators
    31  func (c *core) broadcastCommit() {
    32  	var err error
    33  
    34  	logger := c.currentLogger(true, nil)
    35  
    36  	sub := c.current.Subject()
    37  
    38  	var header *types.Header
    39  	if block, ok := c.current.Proposal().(*types.Block); ok {
    40  		header = block.Header()
    41  	}
    42  	// Create Commit Seal
    43  	commitSeal, err := c.backend.SignWithoutHashing(PrepareCommittedSeal(header, uint32(c.currentView().Round.Uint64())))
    44  	if err != nil {
    45  		logger.Error("QBFT: failed to create COMMIT seal", "sub", sub, "err", err)
    46  		return
    47  	}
    48  
    49  	commit := qbfttypes.NewCommit(sub.View.Sequence, sub.View.Round, sub.Digest, commitSeal)
    50  	commit.SetSource(c.Address())
    51  
    52  	// Sign Message
    53  	encodedPayload, err := commit.EncodePayloadForSigning()
    54  	if err != nil {
    55  		withMsg(logger, commit).Error("QBFT: failed to encode payload of COMMIT message", "err", err)
    56  		return
    57  	}
    58  
    59  	signature, err := c.backend.Sign(encodedPayload)
    60  	if err != nil {
    61  		withMsg(logger, commit).Error("QBFT: failed to sign COMMIT message", "err", err)
    62  		return
    63  	}
    64  	commit.SetSignature(signature)
    65  
    66  	// RLP-encode message
    67  	payload, err := rlp.EncodeToBytes(&commit)
    68  	if err != nil {
    69  		withMsg(logger, commit).Error("QBFT: failed to encode COMMIT message", "err", err)
    70  		return
    71  	}
    72  
    73  	withMsg(logger, commit).Info("QBFT: broadcast COMMIT message", "payload", hexutil.Encode(payload))
    74  
    75  	// Broadcast RLP-encoded message
    76  	if err = c.backend.Broadcast(c.valSet, commit.Code(), payload); err != nil {
    77  		withMsg(logger, commit).Error("QBFT: failed to broadcast COMMIT message", "err", err)
    78  		return
    79  	}
    80  }
    81  
    82  // handleCommitMsg is called when receiving a COMMIT message from another validator
    83  
    84  // It
    85  // - validates COMMIT message digest matches the current block proposal
    86  // - accumulates valid COMMIT messages until reaching quorum
    87  // - when quorum of COMMIT messages is reached then update state and commits
    88  func (c *core) handleCommitMsg(commit *qbfttypes.Commit) error {
    89  	logger := c.currentLogger(true, commit)
    90  
    91  	logger.Info("QBFT: handle COMMIT message", "commits.count", c.current.QBFTCommits.Size(), "quorum", c.QuorumSize())
    92  
    93  	// Check digest
    94  	if commit.Digest != c.current.Proposal().Hash() {
    95  		logger.Error("QBFT: invalid COMMIT message digest", "digest", commit.Digest, "proposal", c.current.Proposal().Hash().String())
    96  		return errInvalidMessage
    97  	}
    98  
    99  	// Add to received msgs
   100  	if err := c.current.QBFTCommits.Add(commit); err != nil {
   101  		c.logger.Error("QBFT: failed to save COMMIT message", "err", err)
   102  		return err
   103  	}
   104  
   105  	logger = logger.New("commits.count", c.current.QBFTCommits.Size(), "quorum", c.QuorumSize())
   106  
   107  	// If we reached thresho
   108  	if c.current.QBFTCommits.Size() >= c.QuorumSize() {
   109  		logger.Info("QBFT: received quorum of COMMIT messages")
   110  		c.commitQBFT()
   111  	} else {
   112  		logger.Debug("QBFT: accepted new COMMIT messages")
   113  	}
   114  
   115  	return nil
   116  }
   117  
   118  // commitQBFT is called once quorum of commits is reached
   119  // - computes committedSeals from each received commit messages
   120  // - then commits block proposal to database with committed seals
   121  // - broadcast round change
   122  func (c *core) commitQBFT() {
   123  	c.setState(StateCommitted)
   124  
   125  	proposal := c.current.Proposal()
   126  	if proposal != nil {
   127  		// Compute committed seals
   128  		committedSeals := make([][]byte, c.current.QBFTCommits.Size())
   129  		for i, msg := range c.current.QBFTCommits.Values() {
   130  			committedSeals[i] = make([]byte, types.IstanbulExtraSeal)
   131  			commitMsg := msg.(*qbfttypes.Commit)
   132  			copy(committedSeals[i][:], commitMsg.CommitSeal[:])
   133  		}
   134  
   135  		// Commit proposal to database
   136  		if err := c.backend.Commit(proposal, committedSeals, c.currentView().Round); err != nil {
   137  			c.currentLogger(true, nil).Error("QBFT: error committing proposal", "err", err)
   138  			c.broadcastNextRoundChange()
   139  			return
   140  		}
   141  	}
   142  }