cmd, eth: support switching client modes of operation
This commit is contained in:
		
							parent
							
								
									10ed107ba2
								
							
						
					
					
						commit
						92f9a3e5fa
					
				| @ -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, | ||||
|  | ||||
| @ -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 := ð.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 | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -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) | ||||
|  | ||||
| @ -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) { | ||||
|  | ||||
| @ -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() | ||||
| 
 | ||||
|  | ||||
| @ -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 | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -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} | ||||
| 
 | ||||
|  | ||||
| @ -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.
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user