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