aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/geth/main.go1
-rw-r--r--cmd/utils/flags.go24
-rw-r--r--eth/backend.go6
-rw-r--r--eth/handler.go40
-rw-r--r--eth/handler_test.go44
-rw-r--r--eth/helper_test.go19
-rw-r--r--eth/protocol.go17
-rw-r--r--eth/protocol_test.go6
8 files changed, 122 insertions, 35 deletions
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index 3422d9500..ca86fb26f 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -304,6 +304,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
utils.DataDirFlag,
utils.BlockchainVersionFlag,
utils.OlympicFlag,
+ utils.EthModeFlag,
utils.EthVersionFlag,
utils.CacheFlag,
utils.JSpathFlag,
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index ca9dd76fd..792fcb41d 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -28,6 +28,7 @@ import (
"path/filepath"
"runtime"
"strconv"
+ "strings"
"github.com/codegangsta/cli"
"github.com/ethereum/ethash"
@@ -148,9 +149,14 @@ var (
Name: "olympic",
Usage: "Use olympic style protocol",
}
+ EthModeFlag = cli.StringFlag{
+ Name: "mode",
+ Value: "archive",
+ Usage: "Client mode of operation (archive, full, light)",
+ }
EthVersionFlag = cli.IntFlag{
Name: "eth",
- Value: 62,
+ Value: 63,
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 {
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{
Name: common.MakeName(clientID, version),
DataDir: MustDataDir(ctx),
GenesisNonce: ctx.GlobalInt(GenesisNonceFlag.Name),
GenesisFile: ctx.GlobalString(GenesisFileFlag.Name),
+ Mode: clientMode,
BlockChainVersion: ctx.GlobalInt(BlockchainVersionFlag.Name),
DatabaseCache: ctx.GlobalInt(CacheFlag.Name),
SkipBcVersionCheck: false,
@@ -499,7 +518,6 @@ func MakeEthConfig(clientID, version string, ctx *cli.Context) *eth.Config {
glog.V(logger.Info).Infoln("dev mode enabled")
}
-
return cfg
}
diff --git a/eth/backend.go b/eth/backend.go
index 9ec3c1440..04dd3767a 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -89,6 +89,7 @@ type Config struct {
GenesisFile string
GenesisBlock *types.Block // used by block tests
Olympic bool
+ Mode Mode
BlockChainVersion int
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.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.SetGasPrice(config.GasPrice)
eth.miner.SetExtra(config.ExtraData)
diff --git a/eth/handler.go b/eth/handler.go
index 3fc909672..5716350af 100644
--- a/eth/handler.go
+++ b/eth/handler.go
@@ -17,6 +17,7 @@
package eth
import (
+ "errors"
"fmt"
"math"
"math/big"
@@ -42,6 +43,10 @@ const (
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 {
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 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 {
+ mode Mode
txpool txPool
blockchain *core.BlockChain
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
// 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
manager := &ProtocolManager{
+ mode: mode,
eventMux: mux,
txpool: txpool,
blockchain: blockchain,
@@ -100,11 +97,15 @@ func NewProtocolManager(networkId int, mux *event.TypeMux, txpool txPool, pow po
quitSync: make(chan struct{}),
}
// Initiate a sub-protocol for every implemented version we can handle
- manager.SubProtocols = make([]p2p.Protocol, len(ProtocolVersions))
- for i := 0; i < len(manager.SubProtocols); i++ {
- version := ProtocolVersions[i]
-
- manager.SubProtocols[i] = p2p.Protocol{
+ manager.SubProtocols = make([]p2p.Protocol, 0, len(ProtocolVersions))
+ for i, version := range ProtocolVersions {
+ // Skip protocol version if incompatible with the mode of operation
+ if minimumProtocolVersion[mode] > version {
+ continue
+ }
+ // Compatible, initialize the sub-protocol
+ version := version // Closure for the run
+ manager.SubProtocols = append(manager.SubProtocols, p2p.Protocol{
Name: "eth",
Version: version,
Length: ProtocolLengths[i],
@@ -113,7 +114,10 @@ func NewProtocolManager(networkId int, mux *event.TypeMux, txpool txPool, pow po
manager.newPeerCh <- peer
return manager.handle(peer)
},
- }
+ })
+ }
+ if len(manager.SubProtocols) == 0 {
+ return nil, errIncompatibleConfig
}
// 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)
@@ -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)
- return manager
+ return manager, nil
}
func (pm *ProtocolManager) removePeer(id string) {
diff --git a/eth/handler_test.go b/eth/handler_test.go
index dde2ecbd5..8ab5c1aad 100644
--- a/eth/handler_test.go
+++ b/eth/handler_test.go
@@ -17,12 +17,42 @@ import (
"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
// order.
func TestGetBlockHashes61(t *testing.T) { testGetBlockHashes(t, 61) }
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)
defer peer.close()
@@ -65,7 +95,7 @@ func testGetBlockHashes(t *testing.T, protocol int) {
func TestGetBlockHashesFromNumber61(t *testing.T) { testGetBlockHashesFromNumber(t, 61) }
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)
defer peer.close()
@@ -105,7 +135,7 @@ func testGetBlockHashesFromNumber(t *testing.T, protocol int) {
func TestGetBlocks61(t *testing.T) { testGetBlocks(t, 61) }
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)
defer peer.close()
@@ -177,7 +207,7 @@ func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) }
func TestGetBlockHeaders64(t *testing.T) { testGetBlockHeaders(t, 64) }
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)
defer peer.close()
@@ -303,7 +333,7 @@ func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) }
func TestGetBlockBodies64(t *testing.T) { testGetBlockBodies(t, 64) }
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)
defer peer.close()
@@ -410,7 +440,7 @@ func testGetNodeData(t *testing.T, protocol int) {
}
}
// Assemble the test environment
- pm := newTestProtocolManager(4, generator, nil)
+ pm := newTestProtocolManagerMust(t, ArchiveMode, 4, generator, nil)
peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close()
@@ -500,7 +530,7 @@ func testGetReceipt(t *testing.T, protocol int) {
}
}
// Assemble the test environment
- pm := newTestProtocolManager(4, generator, nil)
+ pm := newTestProtocolManagerMust(t, ArchiveMode, 4, generator, nil)
peer, _ := newTestPeer("peer", protocol, pm, true)
defer peer.close()
diff --git a/eth/helper_test.go b/eth/helper_test.go
index 9314884ef..bd65b49f8 100644
--- a/eth/helper_test.go
+++ b/eth/helper_test.go
@@ -28,7 +28,7 @@ var (
// newTestProtocolManager creates a new protocol manager for testing purposes,
// with the given number of blocks already known, and potential notification
// 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 (
evmux = new(event.TypeMux)
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 {
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()
+ 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
}
diff --git a/eth/protocol.go b/eth/protocol.go
index 49f096a3b..0d2b5128d 100644
--- a/eth/protocol.go
+++ b/eth/protocol.go
@@ -26,6 +26,15 @@ import (
"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
const (
eth61 = 61
@@ -34,6 +43,14 @@ const (
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).
var ProtocolVersions = []uint{eth64, eth63, eth62, eth61}
diff --git a/eth/protocol_test.go b/eth/protocol_test.go
index 523e6c1eb..bac519ae3 100644
--- a/eth/protocol_test.go
+++ b/eth/protocol_test.go
@@ -44,7 +44,7 @@ func TestStatusMsgErrors63(t *testing.T) { testStatusMsgErrors(t, 63) }
func TestStatusMsgErrors64(t *testing.T) { testStatusMsgErrors(t, 64) }
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()
defer pm.Stop()
@@ -99,7 +99,7 @@ func TestRecvTransactions64(t *testing.T) { testRecvTransactions(t, 64) }
func testRecvTransactions(t *testing.T, protocol int) {
txAdded := make(chan []*types.Transaction)
- pm := newTestProtocolManager(0, nil, txAdded)
+ pm := newTestProtocolManagerMust(t, ArchiveMode, 0, nil, txAdded)
p, _ := newTestPeer("peer", protocol, pm, true)
defer pm.Stop()
defer p.close()
@@ -127,7 +127,7 @@ func TestSendTransactions63(t *testing.T) { testSendTransactions(t, 63) }
func TestSendTransactions64(t *testing.T) { testSendTransactions(t, 64) }
func testSendTransactions(t *testing.T, protocol int) {
- pm := newTestProtocolManager(0, nil, nil)
+ pm := newTestProtocolManagerMust(t, ArchiveMode, 0, nil, nil)
defer pm.Stop()
// Fill the pool with big transactions.