cmd, eth: support switching client modes of operation

pull/1889/head
Péter Szilágyi 9 years ago
parent 10ed107ba2
commit 92f9a3e5fa
  1. 1
      cmd/geth/main.go
  2. 24
      cmd/utils/flags.go
  3. 6
      eth/backend.go
  4. 38
      eth/handler.go
  5. 44
      eth/handler_test.go
  6. 19
      eth/helper_test.go
  7. 17
      eth/protocol.go
  8. 6
      eth/protocol_test.go

@ -304,6 +304,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
utils.DataDirFlag, utils.DataDirFlag,
utils.BlockchainVersionFlag, utils.BlockchainVersionFlag,
utils.OlympicFlag, utils.OlympicFlag,
utils.EthModeFlag,
utils.EthVersionFlag, utils.EthVersionFlag,
utils.CacheFlag, utils.CacheFlag,
utils.JSpathFlag, utils.JSpathFlag,

@ -28,6 +28,7 @@ import (
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv" "strconv"
"strings"
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/ethereum/ethash" "github.com/ethereum/ethash"
@ -148,9 +149,14 @@ var (
Name: "olympic", Name: "olympic",
Usage: "Use olympic style protocol", Usage: "Use olympic style protocol",
} }
EthModeFlag = cli.StringFlag{
Name: "mode",
Value: "archive",
Usage: "Client mode of operation (archive, full, light)",
}
EthVersionFlag = cli.IntFlag{ EthVersionFlag = cli.IntFlag{
Name: "eth", Name: "eth",
Value: 62, Value: 63,
Usage: "Highest eth protocol to advertise (temporary, dev option)", Usage: "Highest eth protocol to advertise (temporary, dev option)",
} }
@ -425,12 +431,25 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
if err != nil { if err != nil {
glog.V(logger.Error).Infoln("WARNING: No etherbase set and no accounts found as default") glog.V(logger.Error).Infoln("WARNING: No etherbase set and no accounts found as default")
} }
// Resolve the mode of opeation from the string flag
var clientMode eth.Mode
switch strings.ToLower(ctx.GlobalString(EthModeFlag.Name)) {
case "archive":
clientMode = eth.ArchiveMode
case "full":
clientMode = eth.FullMode
case "light":
clientMode = eth.LightMode
default:
glog.Fatalf("Unknown node type requested: %s", ctx.GlobalString(EthModeFlag.Name))
}
// Assemble the entire eth configuration and return
cfg := &eth.Config{ cfg := &eth.Config{
Name: common.MakeName(clientID, version), Name: common.MakeName(clientID, version),
DataDir: MustDataDir(ctx), DataDir: MustDataDir(ctx),
GenesisNonce: ctx.GlobalInt(GenesisNonceFlag.Name), GenesisNonce: ctx.GlobalInt(GenesisNonceFlag.Name),
GenesisFile: ctx.GlobalString(GenesisFileFlag.Name), GenesisFile: ctx.GlobalString(GenesisFileFlag.Name),
Mode: clientMode,
BlockChainVersion: ctx.GlobalInt(BlockchainVersionFlag.Name), BlockChainVersion: ctx.GlobalInt(BlockchainVersionFlag.Name),
DatabaseCache: ctx.GlobalInt(CacheFlag.Name), DatabaseCache: ctx.GlobalInt(CacheFlag.Name),
SkipBcVersionCheck: false, SkipBcVersionCheck: false,
@ -499,7 +518,6 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
glog.V(logger.Info).Infoln("dev mode enabled") glog.V(logger.Info).Infoln("dev mode enabled")
} }
return cfg return cfg
} }

@ -89,6 +89,7 @@ type Config struct {
GenesisFile string GenesisFile string
GenesisBlock *types.Block // used by block tests GenesisBlock *types.Block // used by block tests
Olympic bool Olympic bool
Mode Mode
BlockChainVersion int BlockChainVersion int
SkipBcVersionCheck bool // e.g. blockchain export SkipBcVersionCheck bool // e.g. blockchain export
@ -398,8 +399,9 @@ func New(config *Config) (*Ethereum, error) {
eth.blockProcessor = core.NewBlockProcessor(chainDb, eth.pow, eth.blockchain, eth.EventMux()) eth.blockProcessor = core.NewBlockProcessor(chainDb, eth.pow, eth.blockchain, eth.EventMux())
eth.blockchain.SetProcessor(eth.blockProcessor) eth.blockchain.SetProcessor(eth.blockProcessor)
eth.protocolManager = NewProtocolManager(config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb) if eth.protocolManager, err = NewProtocolManager(config.Mode, config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil {
return nil, err
}
eth.miner = miner.New(eth, eth.EventMux(), eth.pow) eth.miner = miner.New(eth, eth.EventMux(), eth.pow)
eth.miner.SetGasPrice(config.GasPrice) eth.miner.SetGasPrice(config.GasPrice)
eth.miner.SetExtra(config.ExtraData) eth.miner.SetExtra(config.ExtraData)

@ -17,6 +17,7 @@
package eth package eth
import ( import (
"errors"
"fmt" "fmt"
"math" "math"
"math/big" "math/big"
@ -42,6 +43,10 @@ const (
estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header
) )
// errIncompatibleConfig is returned if the requested protocols and configs are
// not compatible (low protocol version restrictions and high requirements).
var errIncompatibleConfig = errors.New("incompatible configuration")
func errResp(code errCode, format string, v ...interface{}) error { func errResp(code errCode, format string, v ...interface{}) error {
return fmt.Errorf("%v - %v", code, fmt.Sprintf(format, v...)) return fmt.Errorf("%v - %v", code, fmt.Sprintf(format, v...))
} }
@ -49,17 +54,8 @@ func errResp(code errCode, format string, v ...interface{}) error {
type hashFetcherFn func(common.Hash) error type hashFetcherFn func(common.Hash) error
type blockFetcherFn func([]common.Hash) error type blockFetcherFn func([]common.Hash) error
// extProt is an interface which is passed around so we can expose GetHashes and GetBlock without exposing it to the rest of the protocol
// extProt is passed around to peers which require to GetHashes and GetBlocks
type extProt struct {
getHashes hashFetcherFn
getBlocks blockFetcherFn
}
func (ep extProt) GetHashes(hash common.Hash) error { return ep.getHashes(hash) }
func (ep extProt) GetBlock(hashes []common.Hash) error { return ep.getBlocks(hashes) }
type ProtocolManager struct { type ProtocolManager struct {
mode Mode
txpool txPool txpool txPool
blockchain *core.BlockChain blockchain *core.BlockChain
chaindb ethdb.Database chaindb ethdb.Database
@ -87,9 +83,10 @@ type ProtocolManager struct {
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable // NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
// with the ethereum network. // with the ethereum network.
func NewProtocolManager(networkId int, mux *event.TypeMux, txpool txPool, pow pow.PoW, blockchain *core.BlockChain, chaindb ethdb.Database) *ProtocolManager { func NewProtocolManager(mode Mode, networkId int, mux *event.TypeMux, txpool txPool, pow pow.PoW, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) {
// Create the protocol manager with the base fields // Create the protocol manager with the base fields
manager := &ProtocolManager{ manager := &ProtocolManager{
mode: mode,
eventMux: mux, eventMux: mux,
txpool: txpool, txpool: txpool,
blockchain: blockchain, blockchain: blockchain,
@ -100,11 +97,15 @@ func NewProtocolManager(networkId int, mux *event.TypeMux, txpool txPool, pow po
quitSync: make(chan struct{}), quitSync: make(chan struct{}),
} }
// Initiate a sub-protocol for every implemented version we can handle // Initiate a sub-protocol for every implemented version we can handle
manager.SubProtocols = make([]p2p.Protocol, len(ProtocolVersions)) manager.SubProtocols = make([]p2p.Protocol, 0, len(ProtocolVersions))
for i := 0; i < len(manager.SubProtocols); i++ { for i, version := range ProtocolVersions {
version := ProtocolVersions[i] // Skip protocol version if incompatible with the mode of operation
if minimumProtocolVersion[mode] > version {
manager.SubProtocols[i] = p2p.Protocol{ continue
}
// Compatible, initialize the sub-protocol
version := version // Closure for the run
manager.SubProtocols = append(manager.SubProtocols, p2p.Protocol{
Name: "eth", Name: "eth",
Version: version, Version: version,
Length: ProtocolLengths[i], Length: ProtocolLengths[i],
@ -113,7 +114,10 @@ func NewProtocolManager(networkId int, mux *event.TypeMux, txpool txPool, pow po
manager.newPeerCh <- peer manager.newPeerCh <- peer
return manager.handle(peer) return manager.handle(peer)
}, },
})
} }
if len(manager.SubProtocols) == 0 {
return nil, errIncompatibleConfig
} }
// Construct the different synchronisation mechanisms // Construct the different synchronisation mechanisms
manager.downloader = downloader.New(manager.eventMux, manager.blockchain.HasBlock, manager.blockchain.GetBlock, manager.blockchain.CurrentBlock, manager.blockchain.GetTd, manager.blockchain.InsertChain, manager.removePeer) manager.downloader = downloader.New(manager.eventMux, manager.blockchain.HasBlock, manager.blockchain.GetBlock, manager.blockchain.CurrentBlock, manager.blockchain.GetTd, manager.blockchain.InsertChain, manager.removePeer)
@ -126,7 +130,7 @@ func NewProtocolManager(networkId int, mux *event.TypeMux, txpool txPool, pow po
} }
manager.fetcher = fetcher.New(manager.blockchain.GetBlock, validator, manager.BroadcastBlock, heighter, manager.blockchain.InsertChain, manager.removePeer) manager.fetcher = fetcher.New(manager.blockchain.GetBlock, validator, manager.BroadcastBlock, heighter, manager.blockchain.InsertChain, manager.removePeer)
return manager return manager, nil
} }
func (pm *ProtocolManager) removePeer(id string) { func (pm *ProtocolManager) removePeer(id string) {

@ -17,12 +17,42 @@ import (
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
// Tests that protocol versions and modes of operations are matched up properly.
func TestProtocolCompatibility(t *testing.T) {
// Define the compatibility chart
tests := []struct {
version uint
mode Mode
compatible bool
}{
{61, ArchiveMode, true}, {62, ArchiveMode, true}, {63, ArchiveMode, true}, {64, ArchiveMode, true},
{61, FullMode, false}, {62, FullMode, false}, {63, FullMode, true}, {64, FullMode, true},
{61, LightMode, false}, {62, LightMode, false}, {63, LightMode, false}, {64, LightMode, true},
}
// Make sure anything we screw up is restored
backup := ProtocolVersions
defer func() { ProtocolVersions = backup }()
// Try all available compatibility configs and check for errors
for i, tt := range tests {
ProtocolVersions = []uint{tt.version}
pm, err := newTestProtocolManager(tt.mode, 0, nil, nil)
if pm != nil {
defer pm.Stop()
}
if (err == nil && !tt.compatible) || (err != nil && tt.compatible) {
t.Errorf("test %d: compatibility mismatch: have error %v, want compatibility %v", i, err, tt.compatible)
}
}
}
// Tests that hashes can be retrieved from a remote chain by hashes in reverse // Tests that hashes can be retrieved from a remote chain by hashes in reverse
// order. // order.
func TestGetBlockHashes61(t *testing.T) { testGetBlockHashes(t, 61) } func TestGetBlockHashes61(t *testing.T) { testGetBlockHashes(t, 61) }
func testGetBlockHashes(t *testing.T, protocol int) { func testGetBlockHashes(t *testing.T, protocol int) {
pm := newTestProtocolManager(downloader.MaxHashFetch+15, nil, nil) pm := newTestProtocolManagerMust(t, ArchiveMode, downloader.MaxHashFetch+15, nil, nil)
peer, _ := newTestPeer("peer", protocol, pm, true) peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close() defer peer.close()
@ -65,7 +95,7 @@ func testGetBlockHashes(t *testing.T, protocol int) {
func TestGetBlockHashesFromNumber61(t *testing.T) { testGetBlockHashesFromNumber(t, 61) } func TestGetBlockHashesFromNumber61(t *testing.T) { testGetBlockHashesFromNumber(t, 61) }
func testGetBlockHashesFromNumber(t *testing.T, protocol int) { func testGetBlockHashesFromNumber(t *testing.T, protocol int) {
pm := newTestProtocolManager(downloader.MaxHashFetch+15, nil, nil) pm := newTestProtocolManagerMust(t, ArchiveMode, downloader.MaxHashFetch+15, nil, nil)
peer, _ := newTestPeer("peer", protocol, pm, true) peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close() defer peer.close()
@ -105,7 +135,7 @@ func testGetBlockHashesFromNumber(t *testing.T, protocol int) {
func TestGetBlocks61(t *testing.T) { testGetBlocks(t, 61) } func TestGetBlocks61(t *testing.T) { testGetBlocks(t, 61) }
func testGetBlocks(t *testing.T, protocol int) { func testGetBlocks(t *testing.T, protocol int) {
pm := newTestProtocolManager(downloader.MaxHashFetch+15, nil, nil) pm := newTestProtocolManagerMust(t, ArchiveMode, downloader.MaxHashFetch+15, nil, nil)
peer, _ := newTestPeer("peer", protocol, pm, true) peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close() defer peer.close()
@ -177,7 +207,7 @@ func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) }
func TestGetBlockHeaders64(t *testing.T) { testGetBlockHeaders(t, 64) } func TestGetBlockHeaders64(t *testing.T) { testGetBlockHeaders(t, 64) }
func testGetBlockHeaders(t *testing.T, protocol int) { func testGetBlockHeaders(t *testing.T, protocol int) {
pm := newTestProtocolManager(downloader.MaxHashFetch+15, nil, nil) pm := newTestProtocolManagerMust(t, ArchiveMode, downloader.MaxHashFetch+15, nil, nil)
peer, _ := newTestPeer("peer", protocol, pm, true) peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close() defer peer.close()
@ -303,7 +333,7 @@ func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) }
func TestGetBlockBodies64(t *testing.T) { testGetBlockBodies(t, 64) } func TestGetBlockBodies64(t *testing.T) { testGetBlockBodies(t, 64) }
func testGetBlockBodies(t *testing.T, protocol int) { func testGetBlockBodies(t *testing.T, protocol int) {
pm := newTestProtocolManager(downloader.MaxBlockFetch+15, nil, nil) pm := newTestProtocolManagerMust(t, ArchiveMode, downloader.MaxBlockFetch+15, nil, nil)
peer, _ := newTestPeer("peer", protocol, pm, true) peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close() defer peer.close()
@ -410,7 +440,7 @@ func testGetNodeData(t *testing.T, protocol int) {
} }
} }
// Assemble the test environment // Assemble the test environment
pm := newTestProtocolManager(4, generator, nil) pm := newTestProtocolManagerMust(t, ArchiveMode, 4, generator, nil)
peer, _ := newTestPeer("peer", protocol, pm, true) peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close() defer peer.close()
@ -500,7 +530,7 @@ func testGetReceipt(t *testing.T, protocol int) {
} }
} }
// Assemble the test environment // Assemble the test environment
pm := newTestProtocolManager(4, generator, nil) pm := newTestProtocolManagerMust(t, ArchiveMode, 4, generator, nil)
peer, _ := newTestPeer("peer", protocol, pm, true) peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close() defer peer.close()

@ -28,7 +28,7 @@ var (
// newTestProtocolManager creates a new protocol manager for testing purposes, // newTestProtocolManager creates a new protocol manager for testing purposes,
// with the given number of blocks already known, and potential notification // with the given number of blocks already known, and potential notification
// channels for different events. // channels for different events.
func newTestProtocolManager(blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) *ProtocolManager { func newTestProtocolManager(mode Mode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, error) {
var ( var (
evmux = new(event.TypeMux) evmux = new(event.TypeMux)
pow = new(core.FakePow) pow = new(core.FakePow)
@ -42,8 +42,23 @@ func newTestProtocolManager(blocks int, generator func(int, *core.BlockGen), new
if _, err := blockchain.InsertChain(chain); err != nil { if _, err := blockchain.InsertChain(chain); err != nil {
panic(err) panic(err)
} }
pm := NewProtocolManager(NetworkId, evmux, &testTxPool{added: newtx}, pow, blockchain, db) pm, err := NewProtocolManager(mode, NetworkId, evmux, &testTxPool{added: newtx}, pow, blockchain, db)
if err != nil {
return nil, err
}
pm.Start() pm.Start()
return pm, nil
}
// newTestProtocolManagerMust creates a new protocol manager for testing purposes,
// with the given number of blocks already known, and potential notification
// channels for different events. In case of an error, the constructor force-
// fails the test.
func newTestProtocolManagerMust(t *testing.T, mode Mode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) *ProtocolManager {
pm, err := newTestProtocolManager(mode, blocks, generator, newtx)
if err != nil {
t.Fatalf("Failed to create protocol manager: %v", err)
}
return pm return pm
} }

@ -26,6 +26,15 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
// Mode represents the mode of operation of the eth client.
type Mode int
const (
ArchiveMode Mode = iota // Maintain the entire blockchain history
FullMode // Maintain only a recent view of the blockchain
LightMode // Don't maintain any history, rather fetch on demand
)
// Constants to match up protocol versions and messages // Constants to match up protocol versions and messages
const ( const (
eth61 = 61 eth61 = 61
@ -34,6 +43,14 @@ const (
eth64 = 64 eth64 = 64
) )
// minimumProtocolVersion is the minimum version of the protocol eth must run to
// support the desired mode of operation.
var minimumProtocolVersion = map[Mode]uint{
ArchiveMode: eth61,
FullMode: eth63,
LightMode: eth64,
}
// Supported versions of the eth protocol (first is primary). // Supported versions of the eth protocol (first is primary).
var ProtocolVersions = []uint{eth64, eth63, eth62, eth61} var ProtocolVersions = []uint{eth64, eth63, eth62, eth61}

@ -44,7 +44,7 @@ func TestStatusMsgErrors63(t *testing.T) { testStatusMsgErrors(t, 63) }
func TestStatusMsgErrors64(t *testing.T) { testStatusMsgErrors(t, 64) } func TestStatusMsgErrors64(t *testing.T) { testStatusMsgErrors(t, 64) }
func testStatusMsgErrors(t *testing.T, protocol int) { func testStatusMsgErrors(t *testing.T, protocol int) {
pm := newTestProtocolManager(0, nil, nil) pm := newTestProtocolManagerMust(t, ArchiveMode, 0, nil, nil)
td, currentBlock, genesis := pm.blockchain.Status() td, currentBlock, genesis := pm.blockchain.Status()
defer pm.Stop() defer pm.Stop()
@ -99,7 +99,7 @@ func TestRecvTransactions64(t *testing.T) { testRecvTransactions(t, 64) }
func testRecvTransactions(t *testing.T, protocol int) { func testRecvTransactions(t *testing.T, protocol int) {
txAdded := make(chan []*types.Transaction) txAdded := make(chan []*types.Transaction)
pm := newTestProtocolManager(0, nil, txAdded) pm := newTestProtocolManagerMust(t, ArchiveMode, 0, nil, txAdded)
p, _ := newTestPeer("peer", protocol, pm, true) p, _ := newTestPeer("peer", protocol, pm, true)
defer pm.Stop() defer pm.Stop()
defer p.close() defer p.close()
@ -127,7 +127,7 @@ func TestSendTransactions63(t *testing.T) { testSendTransactions(t, 63) }
func TestSendTransactions64(t *testing.T) { testSendTransactions(t, 64) } func TestSendTransactions64(t *testing.T) { testSendTransactions(t, 64) }
func testSendTransactions(t *testing.T, protocol int) { func testSendTransactions(t *testing.T, protocol int) {
pm := newTestProtocolManager(0, nil, nil) pm := newTestProtocolManagerMust(t, ArchiveMode, 0, nil, nil)
defer pm.Stop() defer pm.Stop()
// Fill the pool with big transactions. // Fill the pool with big transactions.

Loading…
Cancel
Save