github.com/klaytn/klaytn@v1.12.1/consensus/istanbul/core/roundstate.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/roundstate.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package core 22 23 import ( 24 "io" 25 "math/big" 26 "sync" 27 28 "github.com/klaytn/klaytn/common" 29 "github.com/klaytn/klaytn/consensus/istanbul" 30 "github.com/klaytn/klaytn/rlp" 31 ) 32 33 // newRoundState creates a new roundState instance with the given view and validatorSet 34 // lockedHash and preprepare are for round change when lock exists, 35 // we need to keep a reference of preprepare in order to propose locked proposal when there is a lock and itself is the proposer 36 func newRoundState(view *istanbul.View, validatorSet istanbul.ValidatorSet, lockedHash common.Hash, preprepare *istanbul.Preprepare, pendingRequest *istanbul.Request, hasBadProposal func(hash common.Hash) bool) *roundState { 37 return &roundState{ 38 round: view.Round, 39 sequence: view.Sequence, 40 Preprepare: preprepare, 41 Prepares: newMessageSet(validatorSet), 42 Commits: newMessageSet(validatorSet), 43 lockedHash: lockedHash, 44 mu: new(sync.RWMutex), 45 pendingRequest: pendingRequest, 46 hasBadProposal: hasBadProposal, 47 } 48 } 49 50 // roundState stores the consensus state 51 type roundState struct { 52 round *big.Int 53 sequence *big.Int 54 Preprepare *istanbul.Preprepare 55 Prepares *messageSet 56 Commits *messageSet 57 lockedHash common.Hash 58 pendingRequest *istanbul.Request 59 60 mu *sync.RWMutex 61 hasBadProposal func(hash common.Hash) bool 62 } 63 64 func (s *roundState) GetPrepareOrCommitSize() int { 65 s.mu.RLock() 66 defer s.mu.RUnlock() 67 68 result := s.Prepares.Size() + s.Commits.Size() 69 70 // find duplicate one 71 for _, m := range s.Prepares.Values() { 72 if s.Commits.Get(m.Address) != nil { 73 result-- 74 } 75 } 76 return result 77 } 78 79 func (s *roundState) Subject() *istanbul.Subject { 80 s.mu.RLock() 81 defer s.mu.RUnlock() 82 83 if s.Preprepare == nil { 84 return nil 85 } 86 87 return &istanbul.Subject{ 88 View: &istanbul.View{ 89 Round: new(big.Int).Set(s.round), 90 Sequence: new(big.Int).Set(s.sequence), 91 }, 92 Digest: s.Preprepare.Proposal.Hash(), 93 PrevHash: s.Preprepare.Proposal.ParentHash(), 94 } 95 } 96 97 func (s *roundState) SetPreprepare(preprepare *istanbul.Preprepare) { 98 s.mu.Lock() 99 defer s.mu.Unlock() 100 101 s.Preprepare = preprepare 102 } 103 104 func (s *roundState) Proposal() istanbul.Proposal { 105 s.mu.RLock() 106 defer s.mu.RUnlock() 107 108 if s.Preprepare != nil { 109 return s.Preprepare.Proposal 110 } 111 112 return nil 113 } 114 115 func (s *roundState) SetRound(r *big.Int) { 116 s.mu.Lock() 117 defer s.mu.Unlock() 118 119 s.round = new(big.Int).Set(r) 120 } 121 122 func (s *roundState) Round() *big.Int { 123 s.mu.RLock() 124 defer s.mu.RUnlock() 125 126 return s.round 127 } 128 129 func (s *roundState) SetSequence(seq *big.Int) { 130 s.mu.Lock() 131 defer s.mu.Unlock() 132 133 s.sequence = seq 134 } 135 136 func (s *roundState) Sequence() *big.Int { 137 s.mu.RLock() 138 defer s.mu.RUnlock() 139 140 return s.sequence 141 } 142 143 func (s *roundState) LockHash() { 144 s.mu.Lock() 145 defer s.mu.Unlock() 146 147 if s.Preprepare != nil { 148 s.lockedHash = s.Preprepare.Proposal.Hash() 149 } 150 } 151 152 func (s *roundState) UnlockHash() { 153 s.mu.Lock() 154 defer s.mu.Unlock() 155 156 s.lockedHash = common.Hash{} 157 } 158 159 func (s *roundState) IsHashLocked() bool { 160 s.mu.RLock() 161 defer s.mu.RUnlock() 162 163 if common.EmptyHash(s.lockedHash) { 164 return false 165 } 166 return !s.hasBadProposal(s.GetLockedHash()) 167 } 168 169 func (s *roundState) GetLockedHash() common.Hash { 170 s.mu.RLock() 171 defer s.mu.RUnlock() 172 173 return s.lockedHash 174 } 175 176 // The DecodeRLP method should read one value from the given 177 // Stream. It is not forbidden to read less or more, but it might 178 // be confusing. 179 func (s *roundState) DecodeRLP(stream *rlp.Stream) error { 180 var ss struct { 181 Round *big.Int 182 Sequence *big.Int 183 Preprepare *istanbul.Preprepare 184 Prepares *messageSet 185 Commits *messageSet 186 lockedHash common.Hash 187 pendingRequest *istanbul.Request 188 } 189 190 if err := stream.Decode(&ss); err != nil { 191 return err 192 } 193 s.round = ss.Round 194 s.sequence = ss.Sequence 195 s.Preprepare = ss.Preprepare 196 s.Prepares = ss.Prepares 197 s.Commits = ss.Commits 198 s.lockedHash = ss.lockedHash 199 s.pendingRequest = ss.pendingRequest 200 s.mu = new(sync.RWMutex) 201 202 return nil 203 } 204 205 // EncodeRLP should write the RLP encoding of its receiver to w. 206 // If the implementation is a pointer method, it may also be 207 // called for nil pointers. 208 // 209 // Implementations should generate valid RLP. The data written is 210 // not verified at the moment, but a future version might. It is 211 // recommended to write only a single value but writing multiple 212 // values or no value at all is also permitted. 213 func (s *roundState) EncodeRLP(w io.Writer) error { 214 s.mu.RLock() 215 defer s.mu.RUnlock() 216 217 return rlp.Encode(w, []interface{}{ 218 s.round, 219 s.sequence, 220 s.Preprepare, 221 s.Prepares, 222 s.Commits, 223 s.lockedHash, 224 s.pendingRequest, 225 }) 226 }