2017-10-31 15:24:11 -07:00
|
|
|
// Copyright 2017 The go-ethereum Authors
|
|
|
|
// This file is part of the go-ethereum library.
|
|
|
|
//
|
|
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Lesser General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
package core
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io"
|
|
|
|
"math/big"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
|
|
"github.com/ethereum/go-ethereum/consensus/istanbul"
|
|
|
|
"github.com/ethereum/go-ethereum/rlp"
|
|
|
|
)
|
|
|
|
|
|
|
|
// newRoundState creates a new roundState instance with the given view and validatorSet
|
|
|
|
// lockedHash and preprepare are for round change when lock exists,
|
|
|
|
// we need to keep a reference of preprepare in order to propose locked proposal when there is a lock and itself is the proposer
|
2018-01-30 18:27:08 -08:00
|
|
|
func newRoundState(view *istanbul.View, validatorSet istanbul.ValidatorSet, lockedHash common.Hash, preprepare *istanbul.Preprepare, pendingRequest *istanbul.Request, hasBadProposal func(hash common.Hash) bool) *roundState {
|
2017-10-31 15:24:11 -07:00
|
|
|
return &roundState{
|
|
|
|
round: view.Round,
|
|
|
|
sequence: view.Sequence,
|
|
|
|
Preprepare: preprepare,
|
|
|
|
Prepares: newMessageSet(validatorSet),
|
|
|
|
Commits: newMessageSet(validatorSet),
|
|
|
|
lockedHash: lockedHash,
|
|
|
|
mu: new(sync.RWMutex),
|
|
|
|
pendingRequest: pendingRequest,
|
2018-01-30 18:27:08 -08:00
|
|
|
hasBadProposal: hasBadProposal,
|
2017-10-31 15:24:11 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// roundState stores the consensus state
|
|
|
|
type roundState struct {
|
|
|
|
round *big.Int
|
|
|
|
sequence *big.Int
|
|
|
|
Preprepare *istanbul.Preprepare
|
|
|
|
Prepares *messageSet
|
|
|
|
Commits *messageSet
|
|
|
|
lockedHash common.Hash
|
|
|
|
pendingRequest *istanbul.Request
|
|
|
|
|
2018-01-30 18:27:08 -08:00
|
|
|
mu *sync.RWMutex
|
|
|
|
hasBadProposal func(hash common.Hash) bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *roundState) GetPrepareOrCommitSize() int {
|
|
|
|
s.mu.RLock()
|
|
|
|
defer s.mu.RUnlock()
|
|
|
|
|
|
|
|
result := s.Prepares.Size() + s.Commits.Size()
|
|
|
|
|
|
|
|
// find duplicate one
|
|
|
|
for _, m := range s.Prepares.Values() {
|
|
|
|
if s.Commits.Get(m.Address) != nil {
|
|
|
|
result--
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
2017-10-31 15:24:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *roundState) Subject() *istanbul.Subject {
|
|
|
|
s.mu.RLock()
|
|
|
|
defer s.mu.RUnlock()
|
|
|
|
|
|
|
|
if s.Preprepare == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return &istanbul.Subject{
|
|
|
|
View: &istanbul.View{
|
|
|
|
Round: new(big.Int).Set(s.round),
|
|
|
|
Sequence: new(big.Int).Set(s.sequence),
|
|
|
|
},
|
|
|
|
Digest: s.Preprepare.Proposal.Hash(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *roundState) SetPreprepare(preprepare *istanbul.Preprepare) {
|
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
|
|
|
s.Preprepare = preprepare
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *roundState) Proposal() istanbul.Proposal {
|
|
|
|
s.mu.RLock()
|
|
|
|
defer s.mu.RUnlock()
|
|
|
|
|
|
|
|
if s.Preprepare != nil {
|
|
|
|
return s.Preprepare.Proposal
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *roundState) SetRound(r *big.Int) {
|
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
|
|
|
s.round = new(big.Int).Set(r)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *roundState) Round() *big.Int {
|
|
|
|
s.mu.RLock()
|
|
|
|
defer s.mu.RUnlock()
|
|
|
|
|
|
|
|
return s.round
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *roundState) SetSequence(seq *big.Int) {
|
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
|
|
|
s.sequence = seq
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *roundState) Sequence() *big.Int {
|
|
|
|
s.mu.RLock()
|
|
|
|
defer s.mu.RUnlock()
|
|
|
|
|
|
|
|
return s.sequence
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *roundState) LockHash() {
|
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
|
|
|
if s.Preprepare != nil {
|
|
|
|
s.lockedHash = s.Preprepare.Proposal.Hash()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *roundState) UnlockHash() {
|
|
|
|
s.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
|
|
|
s.lockedHash = common.Hash{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *roundState) IsHashLocked() bool {
|
|
|
|
s.mu.RLock()
|
|
|
|
defer s.mu.RUnlock()
|
|
|
|
|
2018-01-30 18:27:08 -08:00
|
|
|
if common.EmptyHash(s.lockedHash) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return !s.hasBadProposal(s.GetLockedHash())
|
2017-10-31 15:24:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *roundState) GetLockedHash() common.Hash {
|
|
|
|
s.mu.RLock()
|
|
|
|
defer s.mu.RUnlock()
|
|
|
|
|
|
|
|
return s.lockedHash
|
|
|
|
}
|
|
|
|
|
|
|
|
// The DecodeRLP method should read one value from the given
|
|
|
|
// Stream. It is not forbidden to read less or more, but it might
|
|
|
|
// be confusing.
|
|
|
|
func (s *roundState) DecodeRLP(stream *rlp.Stream) error {
|
|
|
|
var ss struct {
|
|
|
|
Round *big.Int
|
|
|
|
Sequence *big.Int
|
|
|
|
Preprepare *istanbul.Preprepare
|
|
|
|
Prepares *messageSet
|
|
|
|
Commits *messageSet
|
|
|
|
lockedHash common.Hash
|
|
|
|
pendingRequest *istanbul.Request
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := stream.Decode(&ss); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
s.round = ss.Round
|
|
|
|
s.sequence = ss.Sequence
|
|
|
|
s.Preprepare = ss.Preprepare
|
|
|
|
s.Prepares = ss.Prepares
|
|
|
|
s.Commits = ss.Commits
|
|
|
|
s.lockedHash = ss.lockedHash
|
|
|
|
s.pendingRequest = ss.pendingRequest
|
|
|
|
s.mu = new(sync.RWMutex)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// EncodeRLP should write the RLP encoding of its receiver to w.
|
|
|
|
// If the implementation is a pointer method, it may also be
|
|
|
|
// called for nil pointers.
|
|
|
|
//
|
|
|
|
// Implementations should generate valid RLP. The data written is
|
|
|
|
// not verified at the moment, but a future version might. It is
|
|
|
|
// recommended to write only a single value but writing multiple
|
|
|
|
// values or no value at all is also permitted.
|
|
|
|
func (s *roundState) EncodeRLP(w io.Writer) error {
|
|
|
|
s.mu.RLock()
|
|
|
|
defer s.mu.RUnlock()
|
|
|
|
|
|
|
|
return rlp.Encode(w, []interface{}{
|
|
|
|
s.round,
|
|
|
|
s.sequence,
|
|
|
|
s.Preprepare,
|
|
|
|
s.Prepares,
|
|
|
|
s.Commits,
|
|
|
|
s.lockedHash,
|
|
|
|
s.pendingRequest,
|
|
|
|
})
|
|
|
|
}
|