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 }