cmd, eth: added light client and light server modes
This commit is contained in:
		| @@ -180,7 +180,7 @@ func exportChain(ctx *cli.Context) error { | |||||||
|  |  | ||||||
| func removeDB(ctx *cli.Context) error { | func removeDB(ctx *cli.Context) error { | ||||||
| 	stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) | 	stack := utils.MakeNode(ctx, clientIdentifier, gitCommit) | ||||||
| 	dbdir := stack.ResolvePath("chaindata") | 	dbdir := stack.ResolvePath(utils.ChainDbName(ctx)) | ||||||
| 	if !common.FileExist(dbdir) { | 	if !common.FileExist(dbdir) { | ||||||
| 		fmt.Println(dbdir, "does not exist") | 		fmt.Println(dbdir, "does not exist") | ||||||
| 		return nil | 		return nil | ||||||
|   | |||||||
| @@ -41,6 +41,7 @@ import ( | |||||||
| 	"github.com/ethereum/go-ethereum/logger/glog" | 	"github.com/ethereum/go-ethereum/logger/glog" | ||||||
| 	"github.com/ethereum/go-ethereum/metrics" | 	"github.com/ethereum/go-ethereum/metrics" | ||||||
| 	"github.com/ethereum/go-ethereum/node" | 	"github.com/ethereum/go-ethereum/node" | ||||||
|  | 	"github.com/ethereum/go-ethereum/p2p/discover" | ||||||
| 	"gopkg.in/urfave/cli.v1" | 	"gopkg.in/urfave/cli.v1" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -118,6 +119,10 @@ participating. | |||||||
| 		utils.KeyStoreDirFlag, | 		utils.KeyStoreDirFlag, | ||||||
| 		utils.OlympicFlag, | 		utils.OlympicFlag, | ||||||
| 		utils.FastSyncFlag, | 		utils.FastSyncFlag, | ||||||
|  | 		utils.LightModeFlag, | ||||||
|  | 		utils.NoDefSrvFlag, | ||||||
|  | 		utils.LightServFlag, | ||||||
|  | 		utils.LightPeersFlag, | ||||||
| 		utils.LightKDFFlag, | 		utils.LightKDFFlag, | ||||||
| 		utils.CacheFlag, | 		utils.CacheFlag, | ||||||
| 		utils.TrieCacheGenFlag, | 		utils.TrieCacheGenFlag, | ||||||
| @@ -280,6 +285,31 @@ func startNode(ctx *cli.Context, stack *node.Node) { | |||||||
| 	// Start up the node itself | 	// Start up the node itself | ||||||
| 	utils.StartNode(stack) | 	utils.StartNode(stack) | ||||||
|  |  | ||||||
|  | 	if ctx.GlobalBool(utils.LightModeFlag.Name) && !ctx.GlobalBool(utils.NoDefSrvFlag.Name) { | ||||||
|  | 		// add default light server; test phase only | ||||||
|  | 		addPeer := func(url string) { | ||||||
|  | 			node, err := discover.ParseNode(url) | ||||||
|  | 			if err == nil { | ||||||
|  | 				stack.Server().AddPeer(node) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if ctx.GlobalBool(utils.TestNetFlag.Name) { | ||||||
|  | 			// TestNet (John Gerryts @phonikg) | ||||||
|  | 			addPeer("enode://d72af45ba9b60851a8077a4eb07700484b585e5f2e55024e0c93b7ec7d114f2e3fa3c8f3a3358f89da00a609f5a062415deb857ada863b8cdad02b0b0bc90da3@50.112.52.169:30301") | ||||||
|  | 		} else { | ||||||
|  | 			if ctx.GlobalBool(utils.OpposeDAOFork.Name) { | ||||||
|  | 				// Classic (Azure) | ||||||
|  | 				addPeer("enode://fc3d7b57e5d317946bf421411632ec98d5ffcbf94548cd7bc10088e4fef176670f8ec70280d301a9d0b22fe498203f62b323da15b3acc18b02a1fee2a06b7d3f@40.118.3.223:30305") | ||||||
|  | 			} else { | ||||||
|  | 				// MainNet (Azure) | ||||||
|  | 				addPeer("enode://feaf206a308a669a789be45f4dadcb351246051727f12415ad69e44f8080daf0569c10fe1d9944d245dd1f3e1c89cedda8ce03d7e3d5ed8975a35cad4b4f7ec1@40.118.3.223:30303") | ||||||
|  | 				// MainNet (John Gerryts @phonikg) | ||||||
|  | 				addPeer("enode://02b80f0d47c7c157c069d0584067a284cdf188b9267666234b872e70d936a803ad20ea27f78ef1fd6425ae4b7108907e1875adbca96b038004114ac4d1e529a3@50.112.52.169:30300") | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// Unlock any account specifically requested | 	// Unlock any account specifically requested | ||||||
| 	accman := stack.AccountManager() | 	accman := stack.AccountManager() | ||||||
| 	passwords := utils.MakePasswordList(ctx) | 	passwords := utils.MakePasswordList(ctx) | ||||||
|   | |||||||
| @@ -72,6 +72,10 @@ var AppHelpFlagGroups = []flagGroup{ | |||||||
| 			utils.DevModeFlag, | 			utils.DevModeFlag, | ||||||
| 			utils.IdentityFlag, | 			utils.IdentityFlag, | ||||||
| 			utils.FastSyncFlag, | 			utils.FastSyncFlag, | ||||||
|  | 			utils.LightModeFlag, | ||||||
|  | 			utils.NoDefSrvFlag, | ||||||
|  | 			utils.LightServFlag, | ||||||
|  | 			utils.LightPeersFlag, | ||||||
| 			utils.LightKDFFlag, | 			utils.LightKDFFlag, | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
|   | |||||||
| @@ -39,6 +39,8 @@ import ( | |||||||
| 	"github.com/ethereum/go-ethereum/eth" | 	"github.com/ethereum/go-ethereum/eth" | ||||||
| 	"github.com/ethereum/go-ethereum/ethdb" | 	"github.com/ethereum/go-ethereum/ethdb" | ||||||
| 	"github.com/ethereum/go-ethereum/event" | 	"github.com/ethereum/go-ethereum/event" | ||||||
|  | 	"github.com/ethereum/go-ethereum/les" | ||||||
|  | 	"github.com/ethereum/go-ethereum/light" | ||||||
| 	"github.com/ethereum/go-ethereum/logger" | 	"github.com/ethereum/go-ethereum/logger" | ||||||
| 	"github.com/ethereum/go-ethereum/logger/glog" | 	"github.com/ethereum/go-ethereum/logger/glog" | ||||||
| 	"github.com/ethereum/go-ethereum/metrics" | 	"github.com/ethereum/go-ethereum/metrics" | ||||||
| @@ -145,6 +147,24 @@ var ( | |||||||
| 		Name:  "fast", | 		Name:  "fast", | ||||||
| 		Usage: "Enable fast syncing through state downloads", | 		Usage: "Enable fast syncing through state downloads", | ||||||
| 	} | 	} | ||||||
|  | 	LightModeFlag = cli.BoolFlag{ | ||||||
|  | 		Name:  "light", | ||||||
|  | 		Usage: "Enable light client mode", | ||||||
|  | 	} | ||||||
|  | 	NoDefSrvFlag = cli.BoolFlag{ | ||||||
|  | 		Name:  "nodefsrv", | ||||||
|  | 		Usage: "Don't add default LES server (only for test version)", | ||||||
|  | 	} | ||||||
|  | 	LightServFlag = cli.IntFlag{ | ||||||
|  | 		Name:  "lightserv", | ||||||
|  | 		Usage: "Maximum percentage of time allowed for serving LES requests (0-90)", | ||||||
|  | 		Value: 0, | ||||||
|  | 	} | ||||||
|  | 	LightPeersFlag = cli.IntFlag{ | ||||||
|  | 		Name:  "lightpeers", | ||||||
|  | 		Usage: "Maximum number of LES client peers", | ||||||
|  | 		Value: 20, | ||||||
|  | 	} | ||||||
| 	LightKDFFlag = cli.BoolFlag{ | 	LightKDFFlag = cli.BoolFlag{ | ||||||
| 		Name:  "lightkdf", | 		Name:  "lightkdf", | ||||||
| 		Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength", | 		Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength", | ||||||
| @@ -680,6 +700,11 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) { | |||||||
| 		Etherbase:               MakeEtherbase(stack.AccountManager(), ctx), | 		Etherbase:               MakeEtherbase(stack.AccountManager(), ctx), | ||||||
| 		ChainConfig:             MakeChainConfig(ctx, stack), | 		ChainConfig:             MakeChainConfig(ctx, stack), | ||||||
| 		FastSync:                ctx.GlobalBool(FastSyncFlag.Name), | 		FastSync:                ctx.GlobalBool(FastSyncFlag.Name), | ||||||
|  | 		LightMode:               ctx.GlobalBool(LightModeFlag.Name), | ||||||
|  | 		NoDefSrv:                ctx.GlobalBool(NoDefSrvFlag.Name), | ||||||
|  | 		LightServ:               ctx.GlobalInt(LightServFlag.Name), | ||||||
|  | 		LightPeers:              ctx.GlobalInt(LightPeersFlag.Name), | ||||||
|  | 		MaxPeers:                ctx.GlobalInt(MaxPeersFlag.Name), | ||||||
| 		DatabaseCache:           ctx.GlobalInt(CacheFlag.Name), | 		DatabaseCache:           ctx.GlobalInt(CacheFlag.Name), | ||||||
| 		DatabaseHandles:         MakeDatabaseHandles(), | 		DatabaseHandles:         MakeDatabaseHandles(), | ||||||
| 		NetworkId:               ctx.GlobalInt(NetworkIdFlag.Name), | 		NetworkId:               ctx.GlobalInt(NetworkIdFlag.Name), | ||||||
| @@ -714,6 +739,7 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) { | |||||||
| 		} | 		} | ||||||
| 		ethConf.Genesis = core.TestNetGenesisBlock() | 		ethConf.Genesis = core.TestNetGenesisBlock() | ||||||
| 		state.StartingNonce = 1048576 // (2**20) | 		state.StartingNonce = 1048576 // (2**20) | ||||||
|  | 		light.StartingNonce = 1048576 // (2**20) | ||||||
|  |  | ||||||
| 	case ctx.GlobalBool(DevModeFlag.Name): | 	case ctx.GlobalBool(DevModeFlag.Name): | ||||||
| 		ethConf.Genesis = core.OlympicGenesisBlock() | 		ethConf.Genesis = core.OlympicGenesisBlock() | ||||||
| @@ -727,10 +753,23 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) { | |||||||
| 		state.MaxTrieCacheGen = uint16(gen) | 		state.MaxTrieCacheGen = uint16(gen) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { | 	if ethConf.LightMode { | ||||||
| 		return eth.New(ctx, ethConf) | 		if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { | ||||||
| 	}); err != nil { | 			return les.New(ctx, ethConf) | ||||||
| 		Fatalf("Failed to register the Ethereum service: %v", err) | 		}); err != nil { | ||||||
|  | 			Fatalf("Failed to register the Ethereum light node service: %v", err) | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { | ||||||
|  | 			fullNode, err := eth.New(ctx, ethConf) | ||||||
|  | 			if fullNode != nil && ethConf.LightServ > 0 { | ||||||
|  | 				ls, _ := les.NewLesServer(fullNode, ethConf) | ||||||
|  | 				fullNode.AddLesServer(ls) | ||||||
|  | 			} | ||||||
|  | 			return fullNode, err | ||||||
|  | 		}); err != nil { | ||||||
|  | 			Fatalf("Failed to register the Ethereum full node service: %v", err) | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -830,14 +869,23 @@ func MakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainConfi | |||||||
| 	return config | 	return config | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func ChainDbName(ctx *cli.Context) string { | ||||||
|  | 	if ctx.GlobalBool(LightModeFlag.Name) { | ||||||
|  | 		return "lightchaindata" | ||||||
|  | 	} else { | ||||||
|  | 		return "chaindata" | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails. | // MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails. | ||||||
| func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database { | func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database { | ||||||
| 	var ( | 	var ( | ||||||
| 		cache   = ctx.GlobalInt(CacheFlag.Name) | 		cache   = ctx.GlobalInt(CacheFlag.Name) | ||||||
| 		handles = MakeDatabaseHandles() | 		handles = MakeDatabaseHandles() | ||||||
|  | 		name    = ChainDbName(ctx) | ||||||
| 	) | 	) | ||||||
|  |  | ||||||
| 	chainDb, err := stack.OpenDatabase("chaindata", cache, handles) | 	chainDb, err := stack.OpenDatabase(name, cache, handles) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		Fatalf("Could not open database: %v", err) | 		Fatalf("Could not open database: %v", err) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -27,6 +27,8 @@ import ( | |||||||
| 	"github.com/ethereum/go-ethereum/accounts/abi/bind" | 	"github.com/ethereum/go-ethereum/accounts/abi/bind" | ||||||
| 	"github.com/ethereum/go-ethereum/common" | 	"github.com/ethereum/go-ethereum/common" | ||||||
| 	"github.com/ethereum/go-ethereum/eth" | 	"github.com/ethereum/go-ethereum/eth" | ||||||
|  | 	"github.com/ethereum/go-ethereum/internal/ethapi" | ||||||
|  | 	"github.com/ethereum/go-ethereum/les" | ||||||
| 	"github.com/ethereum/go-ethereum/logger" | 	"github.com/ethereum/go-ethereum/logger" | ||||||
| 	"github.com/ethereum/go-ethereum/logger/glog" | 	"github.com/ethereum/go-ethereum/logger/glog" | ||||||
| 	"github.com/ethereum/go-ethereum/node" | 	"github.com/ethereum/go-ethereum/node" | ||||||
| @@ -60,12 +62,20 @@ type ReleaseService struct { | |||||||
| // releases and notify the user of such. | // releases and notify the user of such. | ||||||
| func NewReleaseService(ctx *node.ServiceContext, config Config) (node.Service, error) { | func NewReleaseService(ctx *node.ServiceContext, config Config) (node.Service, error) { | ||||||
| 	// Retrieve the Ethereum service dependency to access the blockchain | 	// Retrieve the Ethereum service dependency to access the blockchain | ||||||
|  | 	var apiBackend ethapi.Backend | ||||||
| 	var ethereum *eth.Ethereum | 	var ethereum *eth.Ethereum | ||||||
| 	if err := ctx.Service(ðereum); err != nil { | 	if err := ctx.Service(ðereum); err == nil { | ||||||
| 		return nil, err | 		apiBackend = ethereum.ApiBackend | ||||||
|  | 	} else { | ||||||
|  | 		var ethereum *les.LightEthereum | ||||||
|  | 		if err := ctx.Service(ðereum); err == nil { | ||||||
|  | 			apiBackend = ethereum.ApiBackend | ||||||
|  | 		} else { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	// Construct the release service | 	// Construct the release service | ||||||
| 	contract, err := NewReleaseOracle(config.Oracle, eth.NewContractBackend(ethereum)) | 	contract, err := NewReleaseOracle(config.Oracle, eth.NewContractBackend(apiBackend)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -66,9 +66,14 @@ var ( | |||||||
| type Config struct { | type Config struct { | ||||||
| 	ChainConfig *core.ChainConfig // chain configuration | 	ChainConfig *core.ChainConfig // chain configuration | ||||||
|  |  | ||||||
| 	NetworkId int    // Network ID to use for selecting peers to connect to | 	NetworkId  int    // Network ID to use for selecting peers to connect to | ||||||
| 	Genesis   string // Genesis JSON to seed the chain database with | 	Genesis    string // Genesis JSON to seed the chain database with | ||||||
| 	FastSync  bool   // Enables the state download based fast synchronisation algorithm | 	FastSync   bool   // Enables the state download based fast synchronisation algorithm | ||||||
|  | 	LightMode  bool   // Running in light client mode | ||||||
|  | 	NoDefSrv   bool   // No default LES server | ||||||
|  | 	LightServ  int    // Maximum percentage of time allowed for serving LES requests | ||||||
|  | 	LightPeers int    // Maximum number of LES client peers | ||||||
|  | 	MaxPeers   int    // Maximum number of global peers | ||||||
|  |  | ||||||
| 	SkipBcVersionCheck bool // e.g. blockchain export | 	SkipBcVersionCheck bool // e.g. blockchain export | ||||||
| 	DatabaseCache      int | 	DatabaseCache      int | ||||||
| @@ -100,6 +105,12 @@ type Config struct { | |||||||
| 	TestGenesisState ethdb.Database // Genesis state to seed the database with (testing only!) | 	TestGenesisState ethdb.Database // Genesis state to seed the database with (testing only!) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type LesServer interface { | ||||||
|  | 	Start() | ||||||
|  | 	Stop() | ||||||
|  | 	Protocols() []p2p.Protocol | ||||||
|  | } | ||||||
|  |  | ||||||
| // Ethereum implements the Ethereum full node service. | // Ethereum implements the Ethereum full node service. | ||||||
| type Ethereum struct { | type Ethereum struct { | ||||||
| 	chainConfig *core.ChainConfig | 	chainConfig *core.ChainConfig | ||||||
| @@ -111,6 +122,7 @@ type Ethereum struct { | |||||||
| 	txMu            sync.Mutex | 	txMu            sync.Mutex | ||||||
| 	blockchain      *core.BlockChain | 	blockchain      *core.BlockChain | ||||||
| 	protocolManager *ProtocolManager | 	protocolManager *ProtocolManager | ||||||
|  | 	lesServer       LesServer | ||||||
| 	// DB interfaces | 	// DB interfaces | ||||||
| 	chainDb ethdb.Database // Block chain database | 	chainDb ethdb.Database // Block chain database | ||||||
|  |  | ||||||
| @@ -119,7 +131,7 @@ type Ethereum struct { | |||||||
| 	httpclient     *httpclient.HTTPClient | 	httpclient     *httpclient.HTTPClient | ||||||
| 	accountManager *accounts.Manager | 	accountManager *accounts.Manager | ||||||
|  |  | ||||||
| 	apiBackend *EthApiBackend | 	ApiBackend *EthApiBackend | ||||||
|  |  | ||||||
| 	miner        *miner.Miner | 	miner        *miner.Miner | ||||||
| 	Mining       bool | 	Mining       bool | ||||||
| @@ -135,10 +147,14 @@ type Ethereum struct { | |||||||
| 	netRPCService *ethapi.PublicNetAPI | 	netRPCService *ethapi.PublicNetAPI | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (s *Ethereum) AddLesServer(ls LesServer) { | ||||||
|  | 	s.lesServer = ls | ||||||
|  | } | ||||||
|  |  | ||||||
| // New creates a new Ethereum object (including the | // New creates a new Ethereum object (including the | ||||||
| // initialisation of the common Ethereum object) | // initialisation of the common Ethereum object) | ||||||
| func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { | func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { | ||||||
| 	chainDb, err := createDB(ctx, config) | 	chainDb, err := CreateDB(ctx, config, "chaindata") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @@ -217,7 +233,18 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { | |||||||
| 	newPool := core.NewTxPool(eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit) | 	newPool := core.NewTxPool(eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit) | ||||||
| 	eth.txPool = newPool | 	eth.txPool = newPool | ||||||
|  |  | ||||||
| 	if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.FastSync, config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil { | 	maxPeers := config.MaxPeers | ||||||
|  | 	if config.LightServ > 0 { | ||||||
|  | 		// if we are running a light server, limit the number of ETH peers so that we reserve some space for incoming LES connections | ||||||
|  | 		// temporary solution until the new peer connectivity API is finished | ||||||
|  | 		halfPeers := maxPeers / 2 | ||||||
|  | 		maxPeers -= config.LightPeers | ||||||
|  | 		if maxPeers < halfPeers { | ||||||
|  | 			maxPeers = halfPeers | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.FastSync, config.NetworkId, maxPeers, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.pow) | 	eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.pow) | ||||||
| @@ -233,14 +260,14 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { | |||||||
| 		GpobaseCorrectionFactor: config.GpobaseCorrectionFactor, | 		GpobaseCorrectionFactor: config.GpobaseCorrectionFactor, | ||||||
| 	} | 	} | ||||||
| 	gpo := gasprice.NewGasPriceOracle(eth.blockchain, chainDb, eth.eventMux, gpoParams) | 	gpo := gasprice.NewGasPriceOracle(eth.blockchain, chainDb, eth.eventMux, gpoParams) | ||||||
| 	eth.apiBackend = &EthApiBackend{eth, gpo} | 	eth.ApiBackend = &EthApiBackend{eth, gpo} | ||||||
|  |  | ||||||
| 	return eth, nil | 	return eth, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // createDB creates the chain database. | // CreateDB creates the chain database. | ||||||
| func createDB(ctx *node.ServiceContext, config *Config) (ethdb.Database, error) { | func CreateDB(ctx *node.ServiceContext, config *Config, name string) (ethdb.Database, error) { | ||||||
| 	db, err := ctx.OpenDatabase("chaindata", config.DatabaseCache, config.DatabaseHandles) | 	db, err := ctx.OpenDatabase(name, config.DatabaseCache, config.DatabaseHandles) | ||||||
| 	if db, ok := db.(*ethdb.LDBDatabase); ok { | 	if db, ok := db.(*ethdb.LDBDatabase); ok { | ||||||
| 		db.Meter("eth/db/chaindata/") | 		db.Meter("eth/db/chaindata/") | ||||||
| 	} | 	} | ||||||
| @@ -288,7 +315,7 @@ func CreatePoW(config *Config) (*ethash.Ethash, error) { | |||||||
| // APIs returns the collection of RPC services the ethereum package offers. | // APIs returns the collection of RPC services the ethereum package offers. | ||||||
| // NOTE, some of these services probably need to be moved to somewhere else. | // NOTE, some of these services probably need to be moved to somewhere else. | ||||||
| func (s *Ethereum) APIs() []rpc.API { | func (s *Ethereum) APIs() []rpc.API { | ||||||
| 	return append(ethapi.GetAPIs(s.apiBackend, s.solcPath), []rpc.API{ | 	return append(ethapi.GetAPIs(s.ApiBackend, s.solcPath), []rpc.API{ | ||||||
| 		{ | 		{ | ||||||
| 			Namespace: "eth", | 			Namespace: "eth", | ||||||
| 			Version:   "1.0", | 			Version:   "1.0", | ||||||
| @@ -391,7 +418,11 @@ func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManage | |||||||
| // Protocols implements node.Service, returning all the currently configured | // Protocols implements node.Service, returning all the currently configured | ||||||
| // network protocols to start. | // network protocols to start. | ||||||
| func (s *Ethereum) Protocols() []p2p.Protocol { | func (s *Ethereum) Protocols() []p2p.Protocol { | ||||||
| 	return s.protocolManager.SubProtocols | 	if s.lesServer == nil { | ||||||
|  | 		return s.protocolManager.SubProtocols | ||||||
|  | 	} else { | ||||||
|  | 		return append(s.protocolManager.SubProtocols, s.lesServer.Protocols()...) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // Start implements node.Service, starting all internal goroutines needed by the | // Start implements node.Service, starting all internal goroutines needed by the | ||||||
| @@ -402,6 +433,9 @@ func (s *Ethereum) Start(srvr *p2p.Server) error { | |||||||
| 		s.StartAutoDAG() | 		s.StartAutoDAG() | ||||||
| 	} | 	} | ||||||
| 	s.protocolManager.Start() | 	s.protocolManager.Start() | ||||||
|  | 	if s.lesServer != nil { | ||||||
|  | 		s.lesServer.Start() | ||||||
|  | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -413,6 +447,9 @@ func (s *Ethereum) Stop() error { | |||||||
| 	} | 	} | ||||||
| 	s.blockchain.Stop() | 	s.blockchain.Stop() | ||||||
| 	s.protocolManager.Stop() | 	s.protocolManager.Stop() | ||||||
|  | 	if s.lesServer != nil { | ||||||
|  | 		s.lesServer.Stop() | ||||||
|  | 	} | ||||||
| 	s.txPool.Stop() | 	s.txPool.Stop() | ||||||
| 	s.miner.Stop() | 	s.miner.Stop() | ||||||
| 	s.eventMux.Stop() | 	s.eventMux.Stop() | ||||||
|   | |||||||
| @@ -43,11 +43,11 @@ type ContractBackend struct { | |||||||
|  |  | ||||||
| // NewContractBackend creates a new native contract backend using an existing | // NewContractBackend creates a new native contract backend using an existing | ||||||
| // Etheruem object. | // Etheruem object. | ||||||
| func NewContractBackend(eth *Ethereum) *ContractBackend { | func NewContractBackend(apiBackend ethapi.Backend) *ContractBackend { | ||||||
| 	return &ContractBackend{ | 	return &ContractBackend{ | ||||||
| 		eapi:  ethapi.NewPublicEthereumAPI(eth.apiBackend), | 		eapi:  ethapi.NewPublicEthereumAPI(apiBackend), | ||||||
| 		bcapi: ethapi.NewPublicBlockChainAPI(eth.apiBackend), | 		bcapi: ethapi.NewPublicBlockChainAPI(apiBackend), | ||||||
| 		txapi: ethapi.NewPublicTransactionPoolAPI(eth.apiBackend), | 		txapi: ethapi.NewPublicTransactionPoolAPI(apiBackend), | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -164,13 +164,13 @@ type Downloader struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| // New creates a new downloader to fetch hashes and blocks from remote peers. | // New creates a new downloader to fetch hashes and blocks from remote peers. | ||||||
| func New(stateDb ethdb.Database, mux *event.TypeMux, hasHeader headerCheckFn, hasBlockAndState blockAndStateCheckFn, | func New(mode SyncMode, stateDb ethdb.Database, mux *event.TypeMux, hasHeader headerCheckFn, hasBlockAndState blockAndStateCheckFn, | ||||||
| 	getHeader headerRetrievalFn, getBlock blockRetrievalFn, headHeader headHeaderRetrievalFn, headBlock headBlockRetrievalFn, | 	getHeader headerRetrievalFn, getBlock blockRetrievalFn, headHeader headHeaderRetrievalFn, headBlock headBlockRetrievalFn, | ||||||
| 	headFastBlock headFastBlockRetrievalFn, commitHeadBlock headBlockCommitterFn, getTd tdRetrievalFn, insertHeaders headerChainInsertFn, | 	headFastBlock headFastBlockRetrievalFn, commitHeadBlock headBlockCommitterFn, getTd tdRetrievalFn, insertHeaders headerChainInsertFn, | ||||||
| 	insertBlocks blockChainInsertFn, insertReceipts receiptChainInsertFn, rollback chainRollbackFn, dropPeer peerDropFn) *Downloader { | 	insertBlocks blockChainInsertFn, insertReceipts receiptChainInsertFn, rollback chainRollbackFn, dropPeer peerDropFn) *Downloader { | ||||||
|  |  | ||||||
| 	dl := &Downloader{ | 	dl := &Downloader{ | ||||||
| 		mode:             FullSync, | 		mode:             mode, | ||||||
| 		mux:              mux, | 		mux:              mux, | ||||||
| 		queue:            newQueue(stateDb), | 		queue:            newQueue(stateDb), | ||||||
| 		peers:            newPeerSet(), | 		peers:            newPeerSet(), | ||||||
| @@ -1179,10 +1179,23 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { | |||||||
| 			for i, header := range rollback { | 			for i, header := range rollback { | ||||||
| 				hashes[i] = header.Hash() | 				hashes[i] = header.Hash() | ||||||
| 			} | 			} | ||||||
| 			lastHeader, lastFastBlock, lastBlock := d.headHeader().Number, d.headFastBlock().Number(), d.headBlock().Number() | 			lastHeader, lastFastBlock, lastBlock := d.headHeader().Number, common.Big0, common.Big0 | ||||||
|  | 			if d.headFastBlock != nil { | ||||||
|  | 				lastFastBlock = d.headFastBlock().Number() | ||||||
|  | 			} | ||||||
|  | 			if d.headBlock != nil { | ||||||
|  | 				lastBlock = d.headBlock().Number() | ||||||
|  | 			} | ||||||
| 			d.rollback(hashes) | 			d.rollback(hashes) | ||||||
|  | 			curFastBlock, curBlock := common.Big0, common.Big0 | ||||||
|  | 			if d.headFastBlock != nil { | ||||||
|  | 				curFastBlock = d.headFastBlock().Number() | ||||||
|  | 			} | ||||||
|  | 			if d.headBlock != nil { | ||||||
|  | 				curBlock = d.headBlock().Number() | ||||||
|  | 			} | ||||||
| 			glog.V(logger.Warn).Infof("Rolled back %d headers (LH: %d->%d, FB: %d->%d, LB: %d->%d)", | 			glog.V(logger.Warn).Infof("Rolled back %d headers (LH: %d->%d, FB: %d->%d, LB: %d->%d)", | ||||||
| 				len(hashes), lastHeader, d.headHeader().Number, lastFastBlock, d.headFastBlock().Number(), lastBlock, d.headBlock().Number()) | 				len(hashes), lastHeader, d.headHeader().Number, lastFastBlock, curFastBlock, lastBlock, curBlock) | ||||||
|  |  | ||||||
| 			// If we're already past the pivot point, this could be an attack, thread carefully | 			// If we're already past the pivot point, this could be an attack, thread carefully | ||||||
| 			if rollback[len(rollback)-1].Number.Uint64() > pivot { | 			if rollback[len(rollback)-1].Number.Uint64() > pivot { | ||||||
| @@ -1229,8 +1242,10 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error { | |||||||
| 				// L: Sync begins, and finds common ancestor at 11 | 				// L: Sync begins, and finds common ancestor at 11 | ||||||
| 				// L: Request new headers up from 11 (R's TD was higher, it must have something) | 				// L: Request new headers up from 11 (R's TD was higher, it must have something) | ||||||
| 				// R: Nothing to give | 				// R: Nothing to give | ||||||
| 				if !gotHeaders && td.Cmp(d.getTd(d.headBlock().Hash())) > 0 { | 				if d.mode != LightSync { | ||||||
| 					return errStallingPeer | 					if !gotHeaders && td.Cmp(d.getTd(d.headBlock().Hash())) > 0 { | ||||||
|  | 						return errStallingPeer | ||||||
|  | 					} | ||||||
| 				} | 				} | ||||||
| 				// If fast or light syncing, ensure promised headers are indeed delivered. This is | 				// If fast or light syncing, ensure promised headers are indeed delivered. This is | ||||||
| 				// needed to detect scenarios where an attacker feeds a bad pivot and then bails out | 				// needed to detect scenarios where an attacker feeds a bad pivot and then bails out | ||||||
|   | |||||||
| @@ -96,7 +96,7 @@ func newTester() *downloadTester { | |||||||
| 	tester.stateDb, _ = ethdb.NewMemDatabase() | 	tester.stateDb, _ = ethdb.NewMemDatabase() | ||||||
| 	tester.stateDb.Put(genesis.Root().Bytes(), []byte{0x00}) | 	tester.stateDb.Put(genesis.Root().Bytes(), []byte{0x00}) | ||||||
|  |  | ||||||
| 	tester.downloader = New(tester.stateDb, new(event.TypeMux), tester.hasHeader, tester.hasBlock, tester.getHeader, | 	tester.downloader = New(FullSync, tester.stateDb, new(event.TypeMux), tester.hasHeader, tester.hasBlock, tester.getHeader, | ||||||
| 		tester.getBlock, tester.headHeader, tester.headBlock, tester.headFastBlock, tester.commitHeadBlock, tester.getTd, | 		tester.getBlock, tester.headHeader, tester.headBlock, tester.headFastBlock, tester.commitHeadBlock, tester.getTd, | ||||||
| 		tester.insertHeaders, tester.insertBlocks, tester.insertReceipts, tester.rollback, tester.dropPeer) | 		tester.insertHeaders, tester.insertBlocks, tester.insertReceipts, tester.rollback, tester.dropPeer) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -52,6 +52,8 @@ type filter struct { | |||||||
| // PublicFilterAPI offers support to create and manage filters. This will allow external clients to retrieve various | // PublicFilterAPI offers support to create and manage filters. This will allow external clients to retrieve various | ||||||
| // information related to the Ethereum protocol such als blocks, transactions and logs. | // information related to the Ethereum protocol such als blocks, transactions and logs. | ||||||
| type PublicFilterAPI struct { | type PublicFilterAPI struct { | ||||||
|  | 	backend   Backend | ||||||
|  | 	useMipMap bool | ||||||
| 	mux       *event.TypeMux | 	mux       *event.TypeMux | ||||||
| 	quit      chan struct{} | 	quit      chan struct{} | ||||||
| 	chainDb   ethdb.Database | 	chainDb   ethdb.Database | ||||||
| @@ -316,7 +318,7 @@ func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) rpc.ID { | |||||||
| // GetLogs returns logs matching the given argument that are stored within the state. | // GetLogs returns logs matching the given argument that are stored within the state. | ||||||
| // | // | ||||||
| // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs | // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs | ||||||
| func (api *PublicFilterAPI) GetLogs(crit FilterCriteria) []Log { | func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]Log, error) { | ||||||
| 	if crit.FromBlock == nil { | 	if crit.FromBlock == nil { | ||||||
| 		crit.FromBlock = big.NewInt(rpc.LatestBlockNumber.Int64()) | 		crit.FromBlock = big.NewInt(rpc.LatestBlockNumber.Int64()) | ||||||
| 	} | 	} | ||||||
| @@ -324,13 +326,14 @@ func (api *PublicFilterAPI) GetLogs(crit FilterCriteria) []Log { | |||||||
| 		crit.ToBlock = big.NewInt(rpc.LatestBlockNumber.Int64()) | 		crit.ToBlock = big.NewInt(rpc.LatestBlockNumber.Int64()) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	filter := New(api.chainDb) | 	filter := New(api.backend, api.useMipMap) | ||||||
| 	filter.SetBeginBlock(crit.FromBlock.Int64()) | 	filter.SetBeginBlock(crit.FromBlock.Int64()) | ||||||
| 	filter.SetEndBlock(crit.ToBlock.Int64()) | 	filter.SetEndBlock(crit.ToBlock.Int64()) | ||||||
| 	filter.SetAddresses(crit.Addresses) | 	filter.SetAddresses(crit.Addresses) | ||||||
| 	filter.SetTopics(crit.Topics) | 	filter.SetTopics(crit.Topics) | ||||||
|  |  | ||||||
| 	return returnLogs(filter.Find()) | 	logs, err := filter.Find(ctx) | ||||||
|  | 	return returnLogs(logs), err | ||||||
| } | } | ||||||
|  |  | ||||||
| // UninstallFilter removes the filter with the given filter id. | // UninstallFilter removes the filter with the given filter id. | ||||||
| @@ -354,22 +357,23 @@ func (api *PublicFilterAPI) UninstallFilter(id rpc.ID) bool { | |||||||
| // If the filter could not be found an empty array of logs is returned. | // If the filter could not be found an empty array of logs is returned. | ||||||
| // | // | ||||||
| // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterlogs | // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterlogs | ||||||
| func (api *PublicFilterAPI) GetFilterLogs(id rpc.ID) []Log { | func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]Log, error) { | ||||||
| 	api.filtersMu.Lock() | 	api.filtersMu.Lock() | ||||||
| 	f, found := api.filters[id] | 	f, found := api.filters[id] | ||||||
| 	api.filtersMu.Unlock() | 	api.filtersMu.Unlock() | ||||||
|  |  | ||||||
| 	if !found || f.typ != LogsSubscription { | 	if !found || f.typ != LogsSubscription { | ||||||
| 		return []Log{} | 		return []Log{}, nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	filter := New(api.chainDb) | 	filter := New(api.backend, api.useMipMap) | ||||||
| 	filter.SetBeginBlock(f.crit.FromBlock.Int64()) | 	filter.SetBeginBlock(f.crit.FromBlock.Int64()) | ||||||
| 	filter.SetEndBlock(f.crit.ToBlock.Int64()) | 	filter.SetEndBlock(f.crit.ToBlock.Int64()) | ||||||
| 	filter.SetAddresses(f.crit.Addresses) | 	filter.SetAddresses(f.crit.Addresses) | ||||||
| 	filter.SetTopics(f.crit.Topics) | 	filter.SetTopics(f.crit.Topics) | ||||||
|  |  | ||||||
| 	return returnLogs(filter.Find()) | 	logs, err := filter.Find(ctx) | ||||||
|  | 	return returnLogs(logs), err | ||||||
| } | } | ||||||
|  |  | ||||||
| // GetFilterChanges returns the logs for the filter with the given id since | // GetFilterChanges returns the logs for the filter with the given id since | ||||||
|   | |||||||
| @@ -24,10 +24,23 @@ import ( | |||||||
| 	"github.com/ethereum/go-ethereum/core" | 	"github.com/ethereum/go-ethereum/core" | ||||||
| 	"github.com/ethereum/go-ethereum/core/types" | 	"github.com/ethereum/go-ethereum/core/types" | ||||||
| 	"github.com/ethereum/go-ethereum/ethdb" | 	"github.com/ethereum/go-ethereum/ethdb" | ||||||
|  | 	"github.com/ethereum/go-ethereum/event" | ||||||
|  | 	"github.com/ethereum/go-ethereum/rpc" | ||||||
|  | 	"golang.org/x/net/context" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | type Backend interface { | ||||||
|  | 	ChainDb() ethdb.Database | ||||||
|  | 	EventMux() *event.TypeMux | ||||||
|  | 	HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) | ||||||
|  | 	GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) | ||||||
|  | } | ||||||
|  |  | ||||||
| // Filter can be used to retrieve and filter logs | // Filter can be used to retrieve and filter logs | ||||||
| type Filter struct { | type Filter struct { | ||||||
|  | 	backend   Backend | ||||||
|  | 	useMipMap bool | ||||||
|  |  | ||||||
| 	created time.Time | 	created time.Time | ||||||
|  |  | ||||||
| 	db         ethdb.Database | 	db         ethdb.Database | ||||||
| @@ -38,8 +51,12 @@ type Filter struct { | |||||||
|  |  | ||||||
| // New creates a new filter which uses a bloom filter on blocks to figure out whether | // New creates a new filter which uses a bloom filter on blocks to figure out whether | ||||||
| // a particular block is interesting or not. | // a particular block is interesting or not. | ||||||
| func New(db ethdb.Database) *Filter { | func New(backend Backend, useMipMap bool) *Filter { | ||||||
| 	return &Filter{db: db} | 	return &Filter{ | ||||||
|  | 		backend:   backend, | ||||||
|  | 		useMipMap: useMipMap, | ||||||
|  | 		db:        backend.ChainDb(), | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // SetBeginBlock sets the earliest block for filtering. | // SetBeginBlock sets the earliest block for filtering. | ||||||
| @@ -66,30 +83,29 @@ func (f *Filter) SetTopics(topics [][]common.Hash) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // Run filters logs with the current parameters set | // Run filters logs with the current parameters set | ||||||
| func (f *Filter) Find() []Log { | func (f *Filter) Find(ctx context.Context) ([]Log, error) { | ||||||
| 	latestHash := core.GetHeadBlockHash(f.db) | 	head, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) | ||||||
| 	latestBlock := core.GetBlock(f.db, latestHash, core.GetBlockNumber(f.db, latestHash)) | 	if head == nil { | ||||||
| 	if latestBlock == nil { | 		return nil, nil | ||||||
| 		return []Log{} |  | ||||||
| 	} | 	} | ||||||
|  | 	headBlockNumber := head.Number.Uint64() | ||||||
|  |  | ||||||
| 	var beginBlockNo uint64 = uint64(f.begin) | 	var beginBlockNo uint64 = uint64(f.begin) | ||||||
| 	if f.begin == -1 { | 	if f.begin == -1 { | ||||||
| 		beginBlockNo = latestBlock.NumberU64() | 		beginBlockNo = headBlockNumber | ||||||
| 	} | 	} | ||||||
|  | 	var endBlockNo uint64 = uint64(f.end) | ||||||
| 	endBlockNo := uint64(f.end) |  | ||||||
| 	if f.end == -1 { | 	if f.end == -1 { | ||||||
| 		endBlockNo = latestBlock.NumberU64() | 		endBlockNo = headBlockNumber | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// if no addresses are present we can't make use of fast search which | 	// if no addresses are present we can't make use of fast search which | ||||||
| 	// uses the mipmap bloom filters to check for fast inclusion and uses | 	// uses the mipmap bloom filters to check for fast inclusion and uses | ||||||
| 	// higher range probability in order to ensure at least a false positive | 	// higher range probability in order to ensure at least a false positive | ||||||
| 	if len(f.addresses) == 0 { | 	if !f.useMipMap || len(f.addresses) == 0 { | ||||||
| 		return f.getLogs(beginBlockNo, endBlockNo) | 		return f.getLogs(ctx, beginBlockNo, endBlockNo) | ||||||
| 	} | 	} | ||||||
| 	return f.mipFind(beginBlockNo, endBlockNo, 0) | 	return f.mipFind(beginBlockNo, endBlockNo, 0), nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (f *Filter) mipFind(start, end uint64, depth int) (logs []Log) { | func (f *Filter) mipFind(start, end uint64, depth int) (logs []Log) { | ||||||
| @@ -107,7 +123,8 @@ func (f *Filter) mipFind(start, end uint64, depth int) (logs []Log) { | |||||||
| 				start := uint64(math.Max(float64(num), float64(start))) | 				start := uint64(math.Max(float64(num), float64(start))) | ||||||
| 				end := uint64(math.Min(float64(num+level-1), float64(end))) | 				end := uint64(math.Min(float64(num+level-1), float64(end))) | ||||||
| 				if depth+1 == len(core.MIPMapLevels) { | 				if depth+1 == len(core.MIPMapLevels) { | ||||||
| 					logs = append(logs, f.getLogs(start, end)...) | 					l, _ := f.getLogs(context.Background(), start, end) | ||||||
|  | 					logs = append(logs, l...) | ||||||
| 				} else { | 				} else { | ||||||
| 					logs = append(logs, f.mipFind(start, end, depth+1)...) | 					logs = append(logs, f.mipFind(start, end, depth+1)...) | ||||||
| 				} | 				} | ||||||
| @@ -122,28 +139,22 @@ func (f *Filter) mipFind(start, end uint64, depth int) (logs []Log) { | |||||||
| 	return logs | 	return logs | ||||||
| } | } | ||||||
|  |  | ||||||
| func (f *Filter) getLogs(start, end uint64) (logs []Log) { | func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, err error) { | ||||||
| 	var block *types.Block |  | ||||||
|  |  | ||||||
| 	for i := start; i <= end; i++ { | 	for i := start; i <= end; i++ { | ||||||
| 		hash := core.GetCanonicalHash(f.db, i) | 		header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(i)) | ||||||
| 		if hash != (common.Hash{}) { | 		if header == nil || err != nil { | ||||||
| 			block = core.GetBlock(f.db, hash, i) | 			return logs, err | ||||||
| 		} else { // block not found |  | ||||||
| 			return logs |  | ||||||
| 		} |  | ||||||
| 		if block == nil { // block not found/written |  | ||||||
| 			return logs |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// Use bloom filtering to see if this block is interesting given the | 		// Use bloom filtering to see if this block is interesting given the | ||||||
| 		// current parameters | 		// current parameters | ||||||
| 		if f.bloomFilter(block) { | 		if f.bloomFilter(header.Bloom) { | ||||||
| 			// Get the logs of the block | 			// Get the logs of the block | ||||||
| 			var ( | 			receipts, err := f.backend.GetReceipts(ctx, header.Hash()) | ||||||
| 				receipts   = core.GetBlockReceipts(f.db, block.Hash(), i) | 			if err != nil { | ||||||
| 				unfiltered []Log | 				return nil, err | ||||||
| 			) | 			} | ||||||
|  | 			var unfiltered []Log | ||||||
| 			for _, receipt := range receipts { | 			for _, receipt := range receipts { | ||||||
| 				rl := make([]Log, len(receipt.Logs)) | 				rl := make([]Log, len(receipt.Logs)) | ||||||
| 				for i, l := range receipt.Logs { | 				for i, l := range receipt.Logs { | ||||||
| @@ -155,7 +166,7 @@ func (f *Filter) getLogs(start, end uint64) (logs []Log) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return logs | 	return logs, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func includes(addresses []common.Address, a common.Address) bool { | func includes(addresses []common.Address, a common.Address) bool { | ||||||
| @@ -229,7 +240,7 @@ func bloomFilter(bloom types.Bloom, addresses []common.Address, topics [][]commo | |||||||
| 	for _, sub := range topics { | 	for _, sub := range topics { | ||||||
| 		var included bool | 		var included bool | ||||||
| 		for _, topic := range sub { | 		for _, topic := range sub { | ||||||
| 			if (topic == common.Hash{}) || types.BloomLookup(block.Bloom(), topic) { | 			if (topic == common.Hash{}) || types.BloomLookup(bloom, topic) { | ||||||
| 				included = true | 				included = true | ||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
|   | |||||||
| @@ -22,6 +22,8 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/net/context" | ||||||
|  |  | ||||||
| 	"github.com/ethereum/go-ethereum/common" | 	"github.com/ethereum/go-ethereum/common" | ||||||
| 	"github.com/ethereum/go-ethereum/core" | 	"github.com/ethereum/go-ethereum/core" | ||||||
| 	"github.com/ethereum/go-ethereum/core/types" | 	"github.com/ethereum/go-ethereum/core/types" | ||||||
| @@ -38,6 +40,37 @@ var ( | |||||||
| 	api     = NewPublicFilterAPI(backend, false) | 	api     = NewPublicFilterAPI(backend, false) | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | type testBackend struct { | ||||||
|  | 	mux *event.TypeMux | ||||||
|  | 	db  ethdb.Database | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *testBackend) ChainDb() ethdb.Database { | ||||||
|  | 	return b.db | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *testBackend) EventMux() *event.TypeMux { | ||||||
|  | 	return b.mux | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { | ||||||
|  | 	var hash common.Hash | ||||||
|  | 	var num uint64 | ||||||
|  | 	if blockNr == rpc.LatestBlockNumber { | ||||||
|  | 		hash = core.GetHeadBlockHash(b.db) | ||||||
|  | 		num = core.GetBlockNumber(b.db, hash) | ||||||
|  | 	} else { | ||||||
|  | 		num = uint64(blockNr) | ||||||
|  | 		hash = core.GetCanonicalHash(b.db, num) | ||||||
|  | 	} | ||||||
|  | 	return core.GetHeader(b.db, hash, num), nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *testBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) { | ||||||
|  | 	num := core.GetBlockNumber(b.db, blockHash) | ||||||
|  | 	return core.GetBlockReceipts(b.db, blockHash, num), nil | ||||||
|  | } | ||||||
|  |  | ||||||
| // TestBlockSubscription tests if a block subscription returns block hashes for posted chain events. | // TestBlockSubscription tests if a block subscription returns block hashes for posted chain events. | ||||||
| // It creates multiple subscriptions: | // It creates multiple subscriptions: | ||||||
| // - one at the start and should receive all posted chain events and a second (blockHashes) | // - one at the start and should receive all posted chain events and a second (blockHashes) | ||||||
|   | |||||||
| @@ -22,6 +22,8 @@ import ( | |||||||
| 	"os" | 	"os" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	"golang.org/x/net/context" | ||||||
|  |  | ||||||
| 	"github.com/ethereum/go-ethereum/common" | 	"github.com/ethereum/go-ethereum/common" | ||||||
| 	"github.com/ethereum/go-ethereum/core" | 	"github.com/ethereum/go-ethereum/core" | ||||||
| 	"github.com/ethereum/go-ethereum/core/types" | 	"github.com/ethereum/go-ethereum/core/types" | ||||||
| @@ -48,6 +50,7 @@ func BenchmarkMipmaps(b *testing.B) { | |||||||
|  |  | ||||||
| 	var ( | 	var ( | ||||||
| 		db, _   = ethdb.NewLDBDatabase(dir, 0, 0) | 		db, _   = ethdb.NewLDBDatabase(dir, 0, 0) | ||||||
|  | 		backend = &testBackend{mux, db} | ||||||
| 		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") | 		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") | ||||||
| 		addr1   = crypto.PubkeyToAddress(key1.PublicKey) | 		addr1   = crypto.PubkeyToAddress(key1.PublicKey) | ||||||
| 		addr2   = common.BytesToAddress([]byte("jeff")) | 		addr2   = common.BytesToAddress([]byte("jeff")) | ||||||
| @@ -100,13 +103,13 @@ func BenchmarkMipmaps(b *testing.B) { | |||||||
| 	} | 	} | ||||||
| 	b.ResetTimer() | 	b.ResetTimer() | ||||||
|  |  | ||||||
| 	filter := New(db) | 	filter := New(backend, true) | ||||||
| 	filter.SetAddresses([]common.Address{addr1, addr2, addr3, addr4}) | 	filter.SetAddresses([]common.Address{addr1, addr2, addr3, addr4}) | ||||||
| 	filter.SetBeginBlock(0) | 	filter.SetBeginBlock(0) | ||||||
| 	filter.SetEndBlock(-1) | 	filter.SetEndBlock(-1) | ||||||
|  |  | ||||||
| 	for i := 0; i < b.N; i++ { | 	for i := 0; i < b.N; i++ { | ||||||
| 		logs := filter.Find() | 		logs, _ := filter.Find(context.Background()) | ||||||
| 		if len(logs) != 4 { | 		if len(logs) != 4 { | ||||||
| 			b.Fatal("expected 4 log, got", len(logs)) | 			b.Fatal("expected 4 log, got", len(logs)) | ||||||
| 		} | 		} | ||||||
| @@ -122,6 +125,7 @@ func TestFilters(t *testing.T) { | |||||||
|  |  | ||||||
| 	var ( | 	var ( | ||||||
| 		db, _   = ethdb.NewLDBDatabase(dir, 0, 0) | 		db, _   = ethdb.NewLDBDatabase(dir, 0, 0) | ||||||
|  | 		backend = &testBackend{mux, db} | ||||||
| 		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") | 		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") | ||||||
| 		addr    = crypto.PubkeyToAddress(key1.PublicKey) | 		addr    = crypto.PubkeyToAddress(key1.PublicKey) | ||||||
|  |  | ||||||
| @@ -201,23 +205,23 @@ func TestFilters(t *testing.T) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	filter := New(db) | 	filter := New(backend, true) | ||||||
| 	filter.SetAddresses([]common.Address{addr}) | 	filter.SetAddresses([]common.Address{addr}) | ||||||
| 	filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2, hash3, hash4}}) | 	filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2, hash3, hash4}}) | ||||||
| 	filter.SetBeginBlock(0) | 	filter.SetBeginBlock(0) | ||||||
| 	filter.SetEndBlock(-1) | 	filter.SetEndBlock(-1) | ||||||
|  |  | ||||||
| 	logs := filter.Find() | 	logs, _ := filter.Find(context.Background()) | ||||||
| 	if len(logs) != 4 { | 	if len(logs) != 4 { | ||||||
| 		t.Error("expected 4 log, got", len(logs)) | 		t.Error("expected 4 log, got", len(logs)) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	filter = New(db) | 	filter = New(backend, true) | ||||||
| 	filter.SetAddresses([]common.Address{addr}) | 	filter.SetAddresses([]common.Address{addr}) | ||||||
| 	filter.SetTopics([][]common.Hash{[]common.Hash{hash3}}) | 	filter.SetTopics([][]common.Hash{[]common.Hash{hash3}}) | ||||||
| 	filter.SetBeginBlock(900) | 	filter.SetBeginBlock(900) | ||||||
| 	filter.SetEndBlock(999) | 	filter.SetEndBlock(999) | ||||||
| 	logs = filter.Find() | 	logs, _ = filter.Find(context.Background()) | ||||||
| 	if len(logs) != 1 { | 	if len(logs) != 1 { | ||||||
| 		t.Error("expected 1 log, got", len(logs)) | 		t.Error("expected 1 log, got", len(logs)) | ||||||
| 	} | 	} | ||||||
| @@ -225,12 +229,12 @@ func TestFilters(t *testing.T) { | |||||||
| 		t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) | 		t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	filter = New(db) | 	filter = New(backend, true) | ||||||
| 	filter.SetAddresses([]common.Address{addr}) | 	filter.SetAddresses([]common.Address{addr}) | ||||||
| 	filter.SetTopics([][]common.Hash{[]common.Hash{hash3}}) | 	filter.SetTopics([][]common.Hash{[]common.Hash{hash3}}) | ||||||
| 	filter.SetBeginBlock(990) | 	filter.SetBeginBlock(990) | ||||||
| 	filter.SetEndBlock(-1) | 	filter.SetEndBlock(-1) | ||||||
| 	logs = filter.Find() | 	logs, _ = filter.Find(context.Background()) | ||||||
| 	if len(logs) != 1 { | 	if len(logs) != 1 { | ||||||
| 		t.Error("expected 1 log, got", len(logs)) | 		t.Error("expected 1 log, got", len(logs)) | ||||||
| 	} | 	} | ||||||
| @@ -238,44 +242,44 @@ func TestFilters(t *testing.T) { | |||||||
| 		t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) | 		t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	filter = New(db) | 	filter = New(backend, true) | ||||||
| 	filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2}}) | 	filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2}}) | ||||||
| 	filter.SetBeginBlock(1) | 	filter.SetBeginBlock(1) | ||||||
| 	filter.SetEndBlock(10) | 	filter.SetEndBlock(10) | ||||||
|  |  | ||||||
| 	logs = filter.Find() | 	logs, _ = filter.Find(context.Background()) | ||||||
| 	if len(logs) != 2 { | 	if len(logs) != 2 { | ||||||
| 		t.Error("expected 2 log, got", len(logs)) | 		t.Error("expected 2 log, got", len(logs)) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	failHash := common.BytesToHash([]byte("fail")) | 	failHash := common.BytesToHash([]byte("fail")) | ||||||
| 	filter = New(db) | 	filter = New(backend, true) | ||||||
| 	filter.SetTopics([][]common.Hash{[]common.Hash{failHash}}) | 	filter.SetTopics([][]common.Hash{[]common.Hash{failHash}}) | ||||||
| 	filter.SetBeginBlock(0) | 	filter.SetBeginBlock(0) | ||||||
| 	filter.SetEndBlock(-1) | 	filter.SetEndBlock(-1) | ||||||
|  |  | ||||||
| 	logs = filter.Find() | 	logs, _ = filter.Find(context.Background()) | ||||||
| 	if len(logs) != 0 { | 	if len(logs) != 0 { | ||||||
| 		t.Error("expected 0 log, got", len(logs)) | 		t.Error("expected 0 log, got", len(logs)) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	failAddr := common.BytesToAddress([]byte("failmenow")) | 	failAddr := common.BytesToAddress([]byte("failmenow")) | ||||||
| 	filter = New(db) | 	filter = New(backend, true) | ||||||
| 	filter.SetAddresses([]common.Address{failAddr}) | 	filter.SetAddresses([]common.Address{failAddr}) | ||||||
| 	filter.SetBeginBlock(0) | 	filter.SetBeginBlock(0) | ||||||
| 	filter.SetEndBlock(-1) | 	filter.SetEndBlock(-1) | ||||||
|  |  | ||||||
| 	logs = filter.Find() | 	logs, _ = filter.Find(context.Background()) | ||||||
| 	if len(logs) != 0 { | 	if len(logs) != 0 { | ||||||
| 		t.Error("expected 0 log, got", len(logs)) | 		t.Error("expected 0 log, got", len(logs)) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	filter = New(db) | 	filter = New(backend, true) | ||||||
| 	filter.SetTopics([][]common.Hash{[]common.Hash{failHash}, []common.Hash{hash1}}) | 	filter.SetTopics([][]common.Hash{[]common.Hash{failHash}, []common.Hash{hash1}}) | ||||||
| 	filter.SetBeginBlock(0) | 	filter.SetBeginBlock(0) | ||||||
| 	filter.SetEndBlock(-1) | 	filter.SetEndBlock(-1) | ||||||
|  |  | ||||||
| 	logs = filter.Find() | 	logs, _ = filter.Find(context.Background()) | ||||||
| 	if len(logs) != 0 { | 	if len(logs) != 0 { | ||||||
| 		t.Error("expected 0 log, got", len(logs)) | 		t.Error("expected 0 log, got", len(logs)) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -68,6 +68,7 @@ type ProtocolManager struct { | |||||||
| 	blockchain  *core.BlockChain | 	blockchain  *core.BlockChain | ||||||
| 	chaindb     ethdb.Database | 	chaindb     ethdb.Database | ||||||
| 	chainconfig *core.ChainConfig | 	chainconfig *core.ChainConfig | ||||||
|  | 	maxPeers    int | ||||||
|  |  | ||||||
| 	downloader *downloader.Downloader | 	downloader *downloader.Downloader | ||||||
| 	fetcher    *fetcher.Fetcher | 	fetcher    *fetcher.Fetcher | ||||||
| @@ -94,7 +95,7 @@ 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(config *core.ChainConfig, fastSync bool, networkId int, mux *event.TypeMux, txpool txPool, pow pow.PoW, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) { | func NewProtocolManager(config *core.ChainConfig, fastSync bool, networkId int, maxPeers 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{ | ||||||
| 		networkId:   networkId, | 		networkId:   networkId, | ||||||
| @@ -103,6 +104,7 @@ func NewProtocolManager(config *core.ChainConfig, fastSync bool, networkId int, | |||||||
| 		blockchain:  blockchain, | 		blockchain:  blockchain, | ||||||
| 		chaindb:     chaindb, | 		chaindb:     chaindb, | ||||||
| 		chainconfig: config, | 		chainconfig: config, | ||||||
|  | 		maxPeers:    maxPeers, | ||||||
| 		peers:       newPeerSet(), | 		peers:       newPeerSet(), | ||||||
| 		newPeerCh:   make(chan *peer), | 		newPeerCh:   make(chan *peer), | ||||||
| 		noMorePeers: make(chan struct{}), | 		noMorePeers: make(chan struct{}), | ||||||
| @@ -156,7 +158,7 @@ func NewProtocolManager(config *core.ChainConfig, fastSync bool, networkId int, | |||||||
| 		return nil, errIncompatibleConfig | 		return nil, errIncompatibleConfig | ||||||
| 	} | 	} | ||||||
| 	// Construct the different synchronisation mechanisms | 	// Construct the different synchronisation mechanisms | ||||||
| 	manager.downloader = downloader.New(chaindb, manager.eventMux, blockchain.HasHeader, blockchain.HasBlockAndState, blockchain.GetHeaderByHash, | 	manager.downloader = downloader.New(downloader.FullSync, chaindb, manager.eventMux, blockchain.HasHeader, blockchain.HasBlockAndState, blockchain.GetHeaderByHash, | ||||||
| 		blockchain.GetBlockByHash, blockchain.CurrentHeader, blockchain.CurrentBlock, blockchain.CurrentFastBlock, blockchain.FastSyncCommitHead, | 		blockchain.GetBlockByHash, blockchain.CurrentHeader, blockchain.CurrentBlock, blockchain.CurrentFastBlock, blockchain.FastSyncCommitHead, | ||||||
| 		blockchain.GetTdByHash, blockchain.InsertHeaderChain, manager.insertChain, blockchain.InsertReceiptChain, blockchain.Rollback, | 		blockchain.GetTdByHash, blockchain.InsertHeaderChain, manager.insertChain, blockchain.InsertReceiptChain, blockchain.Rollback, | ||||||
| 		manager.removePeer) | 		manager.removePeer) | ||||||
| @@ -253,6 +255,10 @@ func (pm *ProtocolManager) newPeer(pv int, p *p2p.Peer, rw p2p.MsgReadWriter) *p | |||||||
| // handle is the callback invoked to manage the life cycle of an eth peer. When | // handle is the callback invoked to manage the life cycle of an eth peer. When | ||||||
| // this function terminates, the peer is disconnected. | // this function terminates, the peer is disconnected. | ||||||
| func (pm *ProtocolManager) handle(p *peer) error { | func (pm *ProtocolManager) handle(p *peer) error { | ||||||
|  | 	if pm.peers.Len() >= pm.maxPeers { | ||||||
|  | 		return p2p.DiscTooManyPeers | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	glog.V(logger.Debug).Infof("%v: peer connected [%s]", p, p.Name()) | 	glog.V(logger.Debug).Infof("%v: peer connected [%s]", p, p.Name()) | ||||||
|  |  | ||||||
| 	// Execute the Ethereum handshake | 	// Execute the Ethereum handshake | ||||||
|   | |||||||
| @@ -469,7 +469,7 @@ func testDAOChallenge(t *testing.T, localForked, remoteForked bool, timeout bool | |||||||
| 		config        = &core.ChainConfig{DAOForkBlock: big.NewInt(1), DAOForkSupport: localForked} | 		config        = &core.ChainConfig{DAOForkBlock: big.NewInt(1), DAOForkSupport: localForked} | ||||||
| 		blockchain, _ = core.NewBlockChain(db, config, pow, evmux) | 		blockchain, _ = core.NewBlockChain(db, config, pow, evmux) | ||||||
| 	) | 	) | ||||||
| 	pm, err := NewProtocolManager(config, false, NetworkId, evmux, new(testTxPool), pow, blockchain, db) | 	pm, err := NewProtocolManager(config, false, NetworkId, 1000, evmux, new(testTxPool), pow, blockchain, db) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("failed to start test protocol manager: %v", err) | 		t.Fatalf("failed to start test protocol manager: %v", err) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -62,7 +62,7 @@ func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core | |||||||
| 		panic(err) | 		panic(err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	pm, err := NewProtocolManager(chainConfig, fastSync, NetworkId, evmux, &testTxPool{added: newtx}, pow, blockchain, db) | 	pm, err := NewProtocolManager(chainConfig, fastSync, NetworkId, 1000, evmux, &testTxPool{added: newtx}, pow, blockchain, db) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -40,13 +40,15 @@ var OpenFileLimit = 64 | |||||||
| // cacheRatio specifies how the total allotted cache is distributed between the | // cacheRatio specifies how the total allotted cache is distributed between the | ||||||
| // various system databases. | // various system databases. | ||||||
| var cacheRatio = map[string]float64{ | var cacheRatio = map[string]float64{ | ||||||
| 	"chaindata": 1.0, | 	"chaindata":      1.0, | ||||||
|  | 	"lightchaindata": 1.0, | ||||||
| } | } | ||||||
|  |  | ||||||
| // handleRatio specifies how the total allotted file descriptors is distributed | // handleRatio specifies how the total allotted file descriptors is distributed | ||||||
| // between the various system databases. | // between the various system databases. | ||||||
| var handleRatio = map[string]float64{ | var handleRatio = map[string]float64{ | ||||||
| 	"chaindata": 1.0, | 	"chaindata":      1.0, | ||||||
|  | 	"lightchaindata": 1.0, | ||||||
| } | } | ||||||
|  |  | ||||||
| type LDBDatabase struct { | type LDBDatabase struct { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user