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