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 }