diff --git a/consensus/istanbul/backend.go b/consensus/istanbul/backend.go
new file mode 100644
index 0000000000..46039438e7
--- /dev/null
+++ b/consensus/istanbul/backend.go
@@ -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 .
+
+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
+}
diff --git a/consensus/istanbul/config.go b/consensus/istanbul/config.go
index e6cb762604..d2d44bf2e8 100644
--- a/consensus/istanbul/config.go
+++ b/consensus/istanbul/config.go
@@ -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,
}
diff --git a/consensus/istanbul/errors.go b/consensus/istanbul/errors.go
new file mode 100644
index 0000000000..ed5b62342f
--- /dev/null
+++ b/consensus/istanbul/errors.go
@@ -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 .
+
+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")
+)
diff --git a/consensus/istanbul/events.go b/consensus/istanbul/events.go
new file mode 100644
index 0000000000..fb6e5bd9c2
--- /dev/null
+++ b/consensus/istanbul/events.go
@@ -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 .
+
+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 {
+}
diff --git a/consensus/istanbul/types.go b/consensus/istanbul/types.go
new file mode 100644
index 0000000000..86b586a2d0
--- /dev/null
+++ b/consensus/istanbul/types.go
@@ -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 .
+
+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())
+}
diff --git a/consensus/istanbul/types_test.go b/consensus/istanbul/types_test.go
new file mode 100644
index 0000000000..cc23d486f1
--- /dev/null
+++ b/consensus/istanbul/types_test.go
@@ -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 .
+
+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)
+ }
+}
diff --git a/consensus/istanbul/utils.go b/consensus/istanbul/utils.go
new file mode 100644
index 0000000000..22137b4148
--- /dev/null
+++ b/consensus/istanbul/utils.go
@@ -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 .
+
+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
+}
diff --git a/consensus/istanbul/validator.go b/consensus/istanbul/validator.go
new file mode 100644
index 0000000000..e0d142866e
--- /dev/null
+++ b/consensus/istanbul/validator.go
@@ -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 .
+
+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