Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

consensus: add Istanbul BFT #166

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ var (
utils.DevModeFlag,
utils.TestnetFlag,
utils.RinkebyFlag,
utils.OttomanFlag,
utils.VMEnableDebugFlag,
utils.NetworkIdFlag,
utils.RPCCORSDomainFlag,
Expand All @@ -118,6 +119,9 @@ var (
utils.RaftJoinExistingFlag,
utils.RaftPortFlag,
utils.EmitCheckpointsFlag,
utils.IstanbulRequestTimeoutFlag,
utils.IstanbulBlockPeriodFlag,
utils.IstanbulBlockPauseTimeFlag,
}

rpcFlags = []cli.Flag{
Expand Down
1 change: 0 additions & 1 deletion cmd/geth/misccmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ func version(ctx *cli.Context) error {
fmt.Println("Git Commit:", gitCommit)
}
fmt.Println("Architecture:", runtime.GOARCH)
fmt.Println("Protocol Versions:", eth.ProtocolVersions)
fmt.Println("Network Id:", eth.DefaultConfig.NetworkId)
fmt.Println("Go Version:", runtime.Version())
fmt.Println("Operating System:", runtime.GOOS)
Expand Down
9 changes: 9 additions & 0 deletions cmd/geth/usage.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ var AppHelpFlagGroups = []flagGroup{
utils.NetworkIdFlag,
utils.TestnetFlag,
utils.RinkebyFlag,
utils.OttomanFlag,
utils.DevModeFlag,
utils.SyncModeFlag,
utils.EthStatsURLFlag,
Expand Down Expand Up @@ -221,6 +222,14 @@ var AppHelpFlagGroups = []flagGroup{
{
Name: "MISC",
},
{
Name: "ISTANBUL",
Flags: []cli.Flag{
utils.IstanbulRequestTimeoutFlag,
utils.IstanbulBlockPeriodFlag,
utils.IstanbulBlockPauseTimeFlag,
},
},
}

// byCategory sorts an array of flagGroup by Name in the order
Expand Down
52 changes: 50 additions & 2 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ var (
}
NetworkIdFlag = cli.Uint64Flag{
Name: "networkid",
Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten, 4=Rinkeby)",
Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten, 4=Rinkeby, 5=Ottoman)",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like we might want to use another (higher) number that's less likely to cause a conflict in the future?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depends on the upstream codebase. We submit the same code to geth, so if that gets merged, we are going to grab network id 5. If we eventually get a larger number, I think we can update it here accordingly.

Value: eth.DefaultConfig.NetworkId,
}
TestnetFlag = cli.BoolFlag{
Expand All @@ -135,6 +135,10 @@ var (
Name: "rinkeby",
Usage: "Rinkeby network: pre-configured proof-of-authority test network",
}
OttomanFlag = cli.BoolFlag{
Name: "ottoman",
Usage: "Ottoman network: pre-configured istanbul bft test network",
}
DevModeFlag = cli.BoolFlag{
Name: "dev",
Usage: "Developer mode: pre-configured private network with several debugging flags",
Expand Down Expand Up @@ -516,6 +520,23 @@ var (
Name: "permissioned",
Usage: "If enabled, the node will allow only a defined list of nodes to connect",
}

// Istanbul settings
IstanbulRequestTimeoutFlag = cli.Uint64Flag{
Name: "istanbul.requesttimeout",
Usage: "Timeout for each Istanbul round in milliseconds",
Value: eth.DefaultConfig.Istanbul.RequestTimeout,
}
IstanbulBlockPeriodFlag = cli.Uint64Flag{
Name: "istanbul.blockperiod",
Usage: "Default minimum difference between two consecutive block's timestamps in seconds",
Value: eth.DefaultConfig.Istanbul.BlockPeriod,
}
IstanbulBlockPauseTimeFlag = cli.Uint64Flag{
Name: "istanbul.blockpausetime",
Usage: "Pause time when zero tx in previous block, values should be larger than istanbul.blockperiod",
Value: eth.DefaultConfig.Istanbul.BlockPauseTime,
}
)

// MakeDataDir retrieves the currently requested data directory, terminating
Expand All @@ -529,6 +550,9 @@ func MakeDataDir(ctx *cli.Context) string {
if ctx.GlobalBool(RinkebyFlag.Name) {
return filepath.Join(path, "rinkeby")
}
if ctx.GlobalBool(OttomanFlag.Name) {
return filepath.Join(path, "ottoman")
}
return path
}
Fatalf("Cannot determine default data directory, please set manually (--datadir)")
Expand Down Expand Up @@ -583,6 +607,8 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
urls = params.TestnetBootnodes
case ctx.GlobalBool(RinkebyFlag.Name):
urls = params.RinkebyBootnodes
case ctx.GlobalBool(OttomanFlag.Name):
urls = params.OttomanBootnodes
}

cfg.BootstrapNodes = make([]*discover.Node, 0, len(urls))
Expand Down Expand Up @@ -854,6 +880,8 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "testnet")
case ctx.GlobalBool(RinkebyFlag.Name):
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby")
case ctx.GlobalBool(OttomanFlag.Name):
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "ottoman")
}

if ctx.GlobalIsSet(KeyStoreDirFlag.Name) {
Expand Down Expand Up @@ -930,6 +958,18 @@ func setEthash(ctx *cli.Context, cfg *eth.Config) {
}
}

func setIstanbul(ctx *cli.Context, cfg *eth.Config) {
if ctx.GlobalIsSet(IstanbulRequestTimeoutFlag.Name) {
cfg.Istanbul.RequestTimeout = ctx.GlobalUint64(IstanbulRequestTimeoutFlag.Name)
}
if ctx.GlobalIsSet(IstanbulBlockPeriodFlag.Name) {
cfg.Istanbul.BlockPeriod = ctx.GlobalUint64(IstanbulBlockPeriodFlag.Name)
}
if ctx.GlobalIsSet(IstanbulBlockPauseTimeFlag.Name) {
cfg.Istanbul.BlockPauseTime = ctx.GlobalUint64(IstanbulBlockPauseTimeFlag.Name)
}
}

func checkExclusive(ctx *cli.Context, flags ...cli.Flag) {
set := make([]string, 0, 1)
for _, flag := range flags {
Expand All @@ -955,14 +995,15 @@ func SetShhConfig(ctx *cli.Context, stack *node.Node, cfg *whisper.Config) {
// SetEthConfig applies eth-related command line flags to the config.
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
// Avoid conflicting network flags
checkExclusive(ctx, DevModeFlag, TestnetFlag, RinkebyFlag)
checkExclusive(ctx, DevModeFlag, TestnetFlag, RinkebyFlag, OttomanFlag)
checkExclusive(ctx, FastSyncFlag, LightModeFlag, SyncModeFlag)

ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
setEtherbase(ctx, ks, cfg)
setGPO(ctx, &cfg.GPO)
setTxPool(ctx, &cfg.TxPool)
setEthash(ctx, cfg)
setIstanbul(ctx, cfg)

switch {
case ctx.GlobalIsSet(SyncModeFlag.Name):
Expand Down Expand Up @@ -1020,6 +1061,11 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
cfg.NetworkId = 4
}
cfg.Genesis = core.DefaultRinkebyGenesisBlock()
case ctx.GlobalBool(OttomanFlag.Name):
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
cfg.NetworkId = 5
}
cfg.Genesis = core.DefaultOttomanGenesisBlock()
case ctx.GlobalBool(DevModeFlag.Name):
cfg.Genesis = core.DevGenesisBlock()
if !ctx.GlobalIsSet(GasPriceFlag.Name) {
Expand Down Expand Up @@ -1116,6 +1162,8 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis {
genesis = core.DefaultTestnetGenesisBlock()
case ctx.GlobalBool(RinkebyFlag.Name):
genesis = core.DefaultRinkebyGenesisBlock()
case ctx.GlobalBool(OttomanFlag.Name):
genesis = core.DefaultOttomanGenesisBlock()
case ctx.GlobalBool(DevModeFlag.Name):
genesis = core.DevGenesisBlock()
}
Expand Down
5 changes: 5 additions & 0 deletions consensus/clique/clique.go
Original file line number Diff line number Diff line change
Expand Up @@ -654,3 +654,8 @@ func (c *Clique) APIs(chain consensus.ChainReader) []rpc.API {
Public: false,
}}
}

// Protocol implements consensus.Engine.Protocol
func (c *Clique) Protocol() consensus.Protocol {
return consensus.EthProtocol
}
27 changes: 27 additions & 0 deletions consensus/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
)
Expand Down Expand Up @@ -90,6 +91,21 @@ type Engine interface {

// APIs returns the RPC APIs this consensus engine provides.
APIs(chain ChainReader) []rpc.API

// Protocol returns the protocol for this consensus
Protocol() Protocol
}

// Handler should be implemented is the consensus needs to handle and send peer's message

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo? Should "is" be "if"?

type Handler interface {
// NewChainHead handles a new head block comes
NewChainHead() error

// HandleMsg handles a message from peer
HandleMsg(address common.Address, data p2p.Msg) (bool, error)

// SetBroadcaster sets the broadcaster to send message to peers
SetBroadcaster(Broadcaster)
}

// PoW is a consensus engine based on proof-of-work.
Expand All @@ -99,3 +115,14 @@ type PoW interface {
// Hashrate returns the current mining hashrate of a PoW consensus engine.
Hashrate() float64
}

// Istanbul is a consensus engine to avoid byzantine failure
type Istanbul interface {
Engine

// Start starts the engine
Start(chain ChainReader, currentBlock func() *types.Block, hasBadBlock func(hash common.Hash) bool) error

// Stop stops the engine
Stop() error
}
5 changes: 5 additions & 0 deletions consensus/ethash/ethash.go
Original file line number Diff line number Diff line change
Expand Up @@ -597,3 +597,8 @@ func (ethash *Ethash) APIs(chain consensus.ChainReader) []rpc.API {
func SeedHash(block uint64) []byte {
return seedHash(block)
}

// Protocol implements consensus.Engine.Protocol
func (ethash *Ethash) Protocol() consensus.Protocol {
return consensus.EthProtocol
}
73 changes: 73 additions & 0 deletions consensus/istanbul/backend.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading