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  }