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  }