mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 17:33:47 +00:00
consensus/istanbul: common Istanbul interfaces and types
This commit is contained in:
parent
94ec62c544
commit
d9b440a0de
8 changed files with 493 additions and 4 deletions
73
consensus/istanbul/backend.go
Normal file
73
consensus/istanbul/backend.go
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
// 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 istanbul
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
)
|
||||
|
||||
// Backend provides application specific functions for Istanbul core
|
||||
type Backend interface {
|
||||
// Address returns the owner's address
|
||||
Address() common.Address
|
||||
|
||||
// Validators returns the validator set
|
||||
Validators(proposal Proposal) ValidatorSet
|
||||
|
||||
// EventMux returns the event mux in backend
|
||||
EventMux() *event.TypeMux
|
||||
|
||||
// Broadcast sends a message to all validators (include self)
|
||||
Broadcast(valSet ValidatorSet, payload []byte) error
|
||||
|
||||
// Gossip sends a message to all validators (exclude self)
|
||||
Gossip(valSet ValidatorSet, payload []byte) error
|
||||
|
||||
// Commit delivers an approved proposal to backend.
|
||||
// The delivered proposal will be put into blockchain.
|
||||
Commit(proposal Proposal, seals [][]byte) error
|
||||
|
||||
// Verify verifies the proposal. If a consensus.ErrFutureBlock error is returned,
|
||||
// the time difference of the proposal and current time is also returned.
|
||||
Verify(Proposal) (time.Duration, error)
|
||||
|
||||
// Sign signs input data with the backend's private key
|
||||
Sign([]byte) ([]byte, error)
|
||||
|
||||
// CheckSignature verifies the signature by checking if it's signed by
|
||||
// the given validator
|
||||
CheckSignature(data []byte, addr common.Address, sig []byte) error
|
||||
|
||||
// LastProposal retrieves latest committed proposal and the address of proposer
|
||||
LastProposal() (Proposal, common.Address)
|
||||
|
||||
// HasPropsal checks if the combination of the given hash and height matches any existing blocks
|
||||
HasPropsal(hash common.Hash, number *big.Int) bool
|
||||
|
||||
// GetProposer returns the proposer of the given block height
|
||||
GetProposer(number uint64) common.Address
|
||||
|
||||
// ParentValidators returns the validator set of the given proposal's parent block
|
||||
ParentValidators(proposal Proposal) ValidatorSet
|
||||
|
||||
// HasBadBlock returns whether the block with the hash is a bad block
|
||||
HasBadProposal(hash common.Hash) bool
|
||||
}
|
||||
|
|
@ -24,17 +24,15 @@ const (
|
|||
)
|
||||
|
||||
type Config struct {
|
||||
RequestTimeout uint64 `toml:",omitempty"` // The timeout for each Istanbul round in milliseconds. This timeout should be larger than BlockPauseTime.
|
||||
RequestTimeout uint64 `toml:",omitempty"` // The timeout for each Istanbul round in milliseconds.
|
||||
BlockPeriod uint64 `toml:",omitempty"` // Default minimum difference between two consecutive block's timestamps in second
|
||||
BlockPauseTime uint64 `toml:",omitempty"` // Pause time when zero tx in previous block, values should be larger than istanbul_block_period
|
||||
ProposerPolicy ProposerPolicy `toml:",omitempty"` // The policy for proposer, the detail is not determined
|
||||
ProposerPolicy ProposerPolicy `toml:",omitempty"` // The policy for proposer selection
|
||||
Epoch uint64 `toml:",omitempty"` // The number of blocks after which to checkpoint and reset the pending votes
|
||||
}
|
||||
|
||||
var DefaultConfig = &Config{
|
||||
RequestTimeout: 10000,
|
||||
BlockPeriod: 1,
|
||||
BlockPauseTime: 2,
|
||||
ProposerPolicy: RoundRobin,
|
||||
Epoch: 30000,
|
||||
}
|
||||
|
|
|
|||
29
consensus/istanbul/errors.go
Normal file
29
consensus/istanbul/errors.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// 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 istanbul
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
// ErrUnauthorizedAddress is returned when given address cannot be found in
|
||||
// current validator set.
|
||||
ErrUnauthorizedAddress = errors.New("unauthorized address")
|
||||
// ErrStoppedEngine is returned if the engine is stopped
|
||||
ErrStoppedEngine = errors.New("stopped engine")
|
||||
// ErrStartedEngine is returned if the engine is already started
|
||||
ErrStartedEngine = errors.New("started engine")
|
||||
)
|
||||
31
consensus/istanbul/events.go
Normal file
31
consensus/istanbul/events.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
// 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 istanbul
|
||||
|
||||
// RequestEvent is posted to propose a proposal
|
||||
type RequestEvent struct {
|
||||
Proposal Proposal
|
||||
}
|
||||
|
||||
// MessageEvent is posted for Istanbul engine communication
|
||||
type MessageEvent struct {
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
// FinalCommittedEvent is posted when a proposal is committed
|
||||
type FinalCommittedEvent struct {
|
||||
}
|
||||
147
consensus/istanbul/types.go
Normal file
147
consensus/istanbul/types.go
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
// 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 istanbul
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// Proposal supports retrieving height and serialized block to be used during Istanbul consensus.
|
||||
type Proposal interface {
|
||||
// Number retrieves the sequence number of this proposal.
|
||||
Number() *big.Int
|
||||
|
||||
// Hash retrieves the hash of this proposal.
|
||||
Hash() common.Hash
|
||||
|
||||
EncodeRLP(w io.Writer) error
|
||||
|
||||
DecodeRLP(s *rlp.Stream) error
|
||||
|
||||
String() string
|
||||
}
|
||||
|
||||
type Request struct {
|
||||
Proposal Proposal
|
||||
}
|
||||
|
||||
// View includes a round number and a sequence number.
|
||||
// Sequence is the block number we'd like to commit.
|
||||
// Each round has a number and is composed by 3 steps: preprepare, prepare and commit.
|
||||
//
|
||||
// If the given block is not accepted by validators, a round change will occur
|
||||
// and the validators start a new round with round+1.
|
||||
type View struct {
|
||||
Round *big.Int
|
||||
Sequence *big.Int
|
||||
}
|
||||
|
||||
// EncodeRLP serializes b into the Ethereum RLP format.
|
||||
func (v *View) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, []interface{}{v.Round, v.Sequence})
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder, and load the consensus fields from a RLP stream.
|
||||
func (v *View) DecodeRLP(s *rlp.Stream) error {
|
||||
var view struct {
|
||||
Round *big.Int
|
||||
Sequence *big.Int
|
||||
}
|
||||
|
||||
if err := s.Decode(&view); err != nil {
|
||||
return err
|
||||
}
|
||||
v.Round, v.Sequence = view.Round, view.Sequence
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *View) String() string {
|
||||
return fmt.Sprintf("{Round: %d, Sequence: %d}", v.Round.Uint64(), v.Sequence.Uint64())
|
||||
}
|
||||
|
||||
// Cmp compares v and y and returns:
|
||||
// -1 if v < y
|
||||
// 0 if v == y
|
||||
// +1 if v > y
|
||||
func (v *View) Cmp(y *View) int {
|
||||
if v.Sequence.Cmp(y.Sequence) != 0 {
|
||||
return v.Sequence.Cmp(y.Sequence)
|
||||
}
|
||||
if v.Round.Cmp(y.Round) != 0 {
|
||||
return v.Round.Cmp(y.Round)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type Preprepare struct {
|
||||
View *View
|
||||
Proposal Proposal
|
||||
}
|
||||
|
||||
// EncodeRLP serializes b into the Ethereum RLP format.
|
||||
func (b *Preprepare) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, []interface{}{b.View, b.Proposal})
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder, and load the consensus fields from a RLP stream.
|
||||
func (b *Preprepare) DecodeRLP(s *rlp.Stream) error {
|
||||
var preprepare struct {
|
||||
View *View
|
||||
Proposal *types.Block
|
||||
}
|
||||
|
||||
if err := s.Decode(&preprepare); err != nil {
|
||||
return err
|
||||
}
|
||||
b.View, b.Proposal = preprepare.View, preprepare.Proposal
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Subject struct {
|
||||
View *View
|
||||
Digest common.Hash
|
||||
}
|
||||
|
||||
// EncodeRLP serializes b into the Ethereum RLP format.
|
||||
func (b *Subject) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, []interface{}{b.View, b.Digest})
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder, and load the consensus fields from a RLP stream.
|
||||
func (b *Subject) DecodeRLP(s *rlp.Stream) error {
|
||||
var subject struct {
|
||||
View *View
|
||||
Digest common.Hash
|
||||
}
|
||||
|
||||
if err := s.Decode(&subject); err != nil {
|
||||
return err
|
||||
}
|
||||
b.View, b.Digest = subject.View, subject.Digest
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Subject) String() string {
|
||||
return fmt.Sprintf("{View: %v, Digest: %v}", b.View, b.Digest.String())
|
||||
}
|
||||
71
consensus/istanbul/types_test.go
Normal file
71
consensus/istanbul/types_test.go
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// 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 istanbul
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestViewCompare(t *testing.T) {
|
||||
// test equality
|
||||
srvView := &View{
|
||||
Sequence: big.NewInt(2),
|
||||
Round: big.NewInt(1),
|
||||
}
|
||||
tarView := &View{
|
||||
Sequence: big.NewInt(2),
|
||||
Round: big.NewInt(1),
|
||||
}
|
||||
if r := srvView.Cmp(tarView); r != 0 {
|
||||
t.Errorf("source(%v) should be equal to target(%v): have %v, want %v", srvView, tarView, r, 0)
|
||||
}
|
||||
|
||||
// test larger Sequence
|
||||
tarView = &View{
|
||||
Sequence: big.NewInt(1),
|
||||
Round: big.NewInt(1),
|
||||
}
|
||||
if r := srvView.Cmp(tarView); r != 1 {
|
||||
t.Errorf("source(%v) should be larger than target(%v): have %v, want %v", srvView, tarView, r, 1)
|
||||
}
|
||||
|
||||
// test larger Round
|
||||
tarView = &View{
|
||||
Sequence: big.NewInt(2),
|
||||
Round: big.NewInt(0),
|
||||
}
|
||||
if r := srvView.Cmp(tarView); r != 1 {
|
||||
t.Errorf("source(%v) should be larger than target(%v): have %v, want %v", srvView, tarView, r, 1)
|
||||
}
|
||||
|
||||
// test smaller Sequence
|
||||
tarView = &View{
|
||||
Sequence: big.NewInt(3),
|
||||
Round: big.NewInt(1),
|
||||
}
|
||||
if r := srvView.Cmp(tarView); r != -1 {
|
||||
t.Errorf("source(%v) should be smaller than target(%v): have %v, want %v", srvView, tarView, r, -1)
|
||||
}
|
||||
tarView = &View{
|
||||
Sequence: big.NewInt(2),
|
||||
Round: big.NewInt(2),
|
||||
}
|
||||
if r := srvView.Cmp(tarView); r != -1 {
|
||||
t.Errorf("source(%v) should be smaller than target(%v): have %v, want %v", srvView, tarView, r, -1)
|
||||
}
|
||||
}
|
||||
60
consensus/istanbul/utils.go
Normal file
60
consensus/istanbul/utils.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// 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 istanbul
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
func RLPHash(v interface{}) (h common.Hash) {
|
||||
hw := sha3.NewKeccak256()
|
||||
rlp.Encode(hw, v)
|
||||
hw.Sum(h[:0])
|
||||
return h
|
||||
}
|
||||
|
||||
// GetSignatureAddress gets the signer address from the signature
|
||||
func GetSignatureAddress(data []byte, sig []byte) (common.Address, error) {
|
||||
// 1. Keccak data
|
||||
hashData := crypto.Keccak256(data)
|
||||
// 2. Recover public key
|
||||
pubkey, err := crypto.SigToPub(hashData, sig)
|
||||
if err != nil {
|
||||
return common.Address{}, err
|
||||
}
|
||||
return crypto.PubkeyToAddress(*pubkey), nil
|
||||
}
|
||||
|
||||
func CheckValidatorSignature(valSet ValidatorSet, data []byte, sig []byte) (common.Address, error) {
|
||||
// 1. Get signature address
|
||||
signer, err := GetSignatureAddress(data, sig)
|
||||
if err != nil {
|
||||
log.Error("Failed to get signer address", "err", err)
|
||||
return common.Address{}, err
|
||||
}
|
||||
|
||||
// 2. Check validator
|
||||
if _, val := valSet.GetByAddress(signer); val != nil {
|
||||
return val.Address(), nil
|
||||
}
|
||||
|
||||
return common.Address{}, ErrUnauthorizedAddress
|
||||
}
|
||||
80
consensus/istanbul/validator.go
Normal file
80
consensus/istanbul/validator.go
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
// 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 istanbul
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
type Validator interface {
|
||||
// Address returns address
|
||||
Address() common.Address
|
||||
|
||||
// String representation of Validator
|
||||
String() string
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type Validators []Validator
|
||||
|
||||
func (slice Validators) Len() int {
|
||||
return len(slice)
|
||||
}
|
||||
|
||||
func (slice Validators) Less(i, j int) bool {
|
||||
return strings.Compare(slice[i].String(), slice[j].String()) < 0
|
||||
}
|
||||
|
||||
func (slice Validators) Swap(i, j int) {
|
||||
slice[i], slice[j] = slice[j], slice[i]
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type ValidatorSet interface {
|
||||
// Calculate the proposer
|
||||
CalcProposer(lastProposer common.Address, round uint64)
|
||||
// Return the validator size
|
||||
Size() int
|
||||
// Return the validator array
|
||||
List() []Validator
|
||||
// Get validator by index
|
||||
GetByIndex(i uint64) Validator
|
||||
// Get validator by given address
|
||||
GetByAddress(addr common.Address) (int, Validator)
|
||||
// Get current proposer
|
||||
GetProposer() Validator
|
||||
// Check whether the validator with given address is a proposer
|
||||
IsProposer(address common.Address) bool
|
||||
// Add validator
|
||||
AddValidator(address common.Address) bool
|
||||
// Remove validator
|
||||
RemoveValidator(address common.Address) bool
|
||||
// Copy validator set
|
||||
Copy() ValidatorSet
|
||||
// Get the maximum number of faulty nodes
|
||||
F() int
|
||||
// Get proposer policy
|
||||
Policy() ProposerPolicy
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
type ProposalSelector func(ValidatorSet, common.Address, uint64) Validator
|
||||
Loading…
Reference in a new issue