diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go
index 6f6cb39069f0..65c71ce4eb03 100644
--- a/cmd/faucet/faucet.go
+++ b/cmd/faucet/faucet.go
@@ -241,14 +241,14 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network u
cfg.SyncMode = downloader.LightSync
cfg.NetworkId = network
cfg.Genesis = genesis
- _, err = les.New(stack, &cfg)
+ lesBackend, err := les.New(stack, &cfg)
if err != nil {
return nil, fmt.Errorf("Failed to register the Ethereum service: %w", err)
}
// Assemble the ethstats monitoring and reporting service'
if stats != "" {
- if err := ethstats.New(stack, stats); err != nil {
+ if err := ethstats.New(stack, lesBackend.ApiBackend, lesBackend.Engine(), stats); err != nil {
return nil, err
}
}
diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go
index 147587004060..6bc595566f15 100644
--- a/cmd/geth/chaincmd.go
+++ b/cmd/geth/chaincmd.go
@@ -278,7 +278,7 @@ func importChain(ctx *cli.Context) error {
utils.SetupMetrics(ctx)
// Start system runtime metrics collection
go metrics.CollectProcessMetrics(3 * time.Second)
- stack := makeFullNode(ctx)
+ stack, _ := makeFullNode(ctx)
defer stack.Close()
chain, db := utils.MakeChain(ctx, stack, false)
@@ -372,7 +372,7 @@ func exportChain(ctx *cli.Context) error {
if len(ctx.Args()) < 1 {
utils.Fatalf("This command requires an argument.")
}
- stack := makeFullNode(ctx)
+ stack, _ := makeFullNode(ctx)
defer stack.Close()
chain, _ := utils.MakeChain(ctx, stack, true)
@@ -407,7 +407,7 @@ func importPreimages(ctx *cli.Context) error {
if len(ctx.Args()) < 1 {
utils.Fatalf("This command requires an argument.")
}
- stack := makeFullNode(ctx)
+ stack, _ := makeFullNode(ctx)
defer stack.Close()
db := utils.MakeChainDatabase(ctx, stack)
@@ -425,7 +425,7 @@ func exportPreimages(ctx *cli.Context) error {
if len(ctx.Args()) < 1 {
utils.Fatalf("This command requires an argument.")
}
- stack := makeFullNode(ctx)
+ stack, _ := makeFullNode(ctx)
defer stack.Close()
db := utils.MakeChainDatabase(ctx, stack)
@@ -447,7 +447,7 @@ func copyDb(ctx *cli.Context) error {
utils.Fatalf("Source ancient chain directory path argument missing")
}
// Initialize a new chain for the running node to sync into
- stack := makeFullNode(ctx)
+ stack, _ := makeFullNode(ctx)
defer stack.Close()
chain, chainDb := utils.MakeChain(ctx, stack, false)
@@ -555,7 +555,7 @@ func confirmAndRemoveDB(database string, kind string) {
}
func dump(ctx *cli.Context) error {
- stack := makeFullNode(ctx)
+ stack, _ := makeFullNode(ctx)
defer stack.Close()
chain, chainDb := utils.MakeChain(ctx, stack, true)
diff --git a/cmd/geth/config.go b/cmd/geth/config.go
index af08b7788611..a777f37050e5 100644
--- a/cmd/geth/config.go
+++ b/cmd/geth/config.go
@@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/eth"
+ "github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
@@ -144,10 +145,10 @@ func enableWhisper(ctx *cli.Context) bool {
return false
}
-func makeFullNode(ctx *cli.Context) *node.Node {
+func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
stack, cfg := makeConfigNode(ctx)
- utils.RegisterEthService(stack, &cfg.Eth)
+ backend := utils.RegisterEthService(stack, &cfg.Eth)
// Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode
shhEnabled := enableWhisper(ctx)
@@ -166,13 +167,13 @@ func makeFullNode(ctx *cli.Context) *node.Node {
}
// Configure GraphQL if requested
if ctx.GlobalIsSet(utils.GraphQLEnabledFlag.Name) {
- utils.RegisterGraphQLService(stack, cfg.Node.GraphQLEndpoint(), cfg.Node.GraphQLCors, cfg.Node.GraphQLVirtualHosts, cfg.Node.HTTPTimeouts)
+ utils.RegisterGraphQLService(stack, backend, cfg.Node)
}
// Add the Ethereum Stats daemon if requested.
if cfg.Ethstats.URL != "" {
- utils.RegisterEthStatsService(stack, cfg.Ethstats.URL)
+ utils.RegisterEthStatsService(stack, backend, cfg.Ethstats.URL)
}
- return stack
+ return stack, backend
}
// dumpConfig is the dumpconfig command.
diff --git a/cmd/geth/consolecmd.go b/cmd/geth/consolecmd.go
index 5a88414a4230..e2f733f844a4 100644
--- a/cmd/geth/consolecmd.go
+++ b/cmd/geth/consolecmd.go
@@ -78,8 +78,8 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/JavaScript-Cons
func localConsole(ctx *cli.Context) error {
// Create and start the node based on the CLI flags
prepare(ctx)
- stack := makeFullNode(ctx)
- startNode(ctx, stack)
+ stack, backend := makeFullNode(ctx)
+ startNode(ctx, stack, backend)
defer stack.Close()
// Attach to the newly started node and start the JavaScript console
@@ -190,8 +190,8 @@ func dialRPC(endpoint string) (*rpc.Client, error) {
// everything down.
func ephemeralConsole(ctx *cli.Context) error {
// Create and start the node based on the CLI flags
- stack := makeFullNode(ctx)
- startNode(ctx, stack)
+ stack, backend := makeFullNode(ctx)
+ startNode(ctx, stack, backend)
defer stack.Close()
// Attach to the newly started node and start the JavaScript console
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index 73a82f30656a..fbc8627498ce 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -32,12 +32,11 @@ import (
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/console/prompt"
- "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/internal/flags"
- "github.com/ethereum/go-ethereum/les"
+ "github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/node"
@@ -350,10 +349,10 @@ func geth(ctx *cli.Context) error {
}
prepare(ctx)
- stack := makeFullNode(ctx)
+ stack, backend := makeFullNode(ctx)
defer stack.Close()
- startNode(ctx, stack)
+ startNode(ctx, stack, backend)
stack.Wait()
return nil
}
@@ -361,7 +360,7 @@ func geth(ctx *cli.Context) error {
// startNode boots up the system node and all registered protocols, after which
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
// miner.
-func startNode(ctx *cli.Context, stack *node.Node) {
+func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend) {
debug.Memsize.Add("node", stack)
// Start up the node itself
@@ -384,20 +383,12 @@ func startNode(ctx *cli.Context, stack *node.Node) {
// Set contract backend for ethereum service if local node
// is serving LES requests.
if ctx.GlobalInt(utils.LegacyLightServFlag.Name) > 0 || ctx.GlobalInt(utils.LightServeFlag.Name) > 0 {
- var ethBackend *eth.Ethereum
- if err := stack.ServiceContext.Lifecycle(ðBackend); err != nil {
- utils.Fatalf("Failed to retrieve ethereum service: %v", err)
- }
- ethBackend.SetContractBackend(ethClient)
+ backend.SetContractBackend(ethClient)
}
// Set contract backend for les service if local node is
// running as a light client.
if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" {
- var lesBackend *les.LightEthereum
- if err := stack.ServiceContext.Lifecycle(&lesBackend); err != nil {
- utils.Fatalf("Failed to retrieve light ethereum service: %v", err)
- }
- lesBackend.SetContractBackend(ethClient)
+ backend.SetContractBackend(ethClient)
}
go func() {
@@ -463,24 +454,23 @@ func startNode(ctx *cli.Context, stack *node.Node) {
if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" {
utils.Fatalf("Light clients do not support mining")
}
- // Check if node's backend is eth
- var ethBackend *eth.Ethereum
- if err := stack.ServiceContext.Lifecycle(ðBackend); err != nil {
- utils.Fatalf("Ethereum service not running: %v", err)
- }
// Set the gas price to the limits from the CLI and start mining
gasprice := utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
if ctx.GlobalIsSet(utils.LegacyMinerGasPriceFlag.Name) && !ctx.GlobalIsSet(utils.MinerGasPriceFlag.Name) {
gasprice = utils.GlobalBig(ctx, utils.LegacyMinerGasPriceFlag.Name)
}
- ethBackend.TxPool().SetGasPrice(gasprice)
+ txpool := backend.TxPool()
+ if txpool == nil {
+ utils.Fatalf("Ethereum service not running: %v", err)
+ }
+ txpool.SetGasPrice(gasprice)
threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name)
if ctx.GlobalIsSet(utils.LegacyMinerThreadsFlag.Name) && !ctx.GlobalIsSet(utils.MinerThreadsFlag.Name) {
threads = ctx.GlobalInt(utils.LegacyMinerThreadsFlag.Name)
log.Warn("The flag --minerthreads is deprecated and will be removed in the future, please use --miner.threads")
}
- if err := ethBackend.StartMining(threads); err != nil {
+ if err := backend.StartMining(threads); err != nil {
utils.Fatalf("Failed to start mining: %v", err)
}
}
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 87be1d10fc59..d97c01fdb5ac 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -48,6 +48,7 @@ import (
"github.com/ethereum/go-ethereum/ethstats"
"github.com/ethereum/go-ethereum/graphql"
"github.com/ethereum/go-ethereum/internal/flags"
+ "github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/les"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
@@ -61,7 +62,6 @@ import (
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/p2p/netutil"
"github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rpc"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
pcsclite "github.com/gballet/go-libpcsclite"
cli "gopkg.in/urfave/cli.v1"
@@ -1654,12 +1654,13 @@ func setDNSDiscoveryDefaults(cfg *eth.Config, genesis common.Hash) {
}
// RegisterEthService adds an Ethereum client to the stack.
-func RegisterEthService(stack *node.Node, cfg *eth.Config) {
+func RegisterEthService(stack *node.Node, cfg *eth.Config) ethapi.Backend {
if cfg.SyncMode == downloader.LightSync {
- _, err := les.New(stack, cfg)
+ backend, err := les.New(stack, cfg)
if err != nil {
Fatalf("Failed to register the Ethereum service: %v", err)
}
+ return backend.ApiBackend
} else {
backend, err := eth.New(stack, cfg)
if err != nil {
@@ -1669,28 +1670,28 @@ func RegisterEthService(stack *node.Node, cfg *eth.Config) {
ls, _ := les.NewLesServer(backend, cfg)
backend.AddLesServer(ls)
}
+ return backend.APIBackend
}
}
// RegisterShhService configures Whisper and adds it to the given node.
func RegisterShhService(stack *node.Node, cfg *whisper.Config) {
- if err := whisper.New(stack, cfg); err != nil {
+ if _, err := whisper.New(stack, cfg); err != nil {
Fatalf("Failed to register the Whisper service: %v", err)
}
}
// RegisterEthStatsService configures the Ethereum Stats daemon and adds it to
// the given node.
-func RegisterEthStatsService(stack *node.Node, url string) {
- if err := ethstats.New(stack, url); err != nil {
+func RegisterEthStatsService(stack *node.Node, backend ethapi.Backend, url string) {
+ if err := ethstats.New(stack, backend, backend.Engine(), url); err != nil {
Fatalf("Failed to register the Ethereum Stats service: %v", err)
}
}
// RegisterGraphQLService is a utility function to construct a new service and register it against a node.
-func RegisterGraphQLService(stack *node.Node, endpoint string, cors, vhosts []string, timeouts rpc.HTTPTimeouts) {
- // create new graphQL service
- if err := graphql.New(stack, endpoint, cors, vhosts, timeouts); err != nil {
+func RegisterGraphQLService(stack *node.Node, backend ethapi.Backend, cfg node.Config) {
+ if err := graphql.New(stack, backend, cfg.GraphQLEndpoint(), cfg.GraphQLCors, cfg.GraphQLVirtualHosts, cfg.HTTPTimeouts); err != nil {
Fatalf("Failed to register the GraphQL service: %v", err)
}
}
diff --git a/eth/api_backend.go b/eth/api_backend.go
index a7122da2cba8..46f41e7cd678 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/rawdb"
@@ -31,8 +32,10 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/gasprice"
+ "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
)
@@ -257,6 +260,10 @@ func (b *EthAPIBackend) TxPoolContent() (map[common.Address]types.Transactions,
return b.eth.TxPool().Content()
}
+func (b *EthAPIBackend) TxPool() *core.TxPool {
+ return b.eth.TxPool()
+}
+
func (b *EthAPIBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
return b.eth.TxPool().SubscribeNewTxsEvent(ch)
}
@@ -307,3 +314,27 @@ func (b *EthAPIBackend) ServiceFilter(ctx context.Context, session *bloombits.Ma
go session.Multiplex(bloomRetrievalBatch, bloomRetrievalWait, b.eth.bloomRequests)
}
}
+
+func (b *EthAPIBackend) Engine() consensus.Engine {
+ return b.eth.engine
+}
+
+func (b *EthAPIBackend) GetBlockByNumber(ctx context.Context, number uint64) (*types.Block, error) {
+ return b.eth.blockchain.GetBlockByNumber(number), nil
+}
+
+func (b *EthAPIBackend) CurrentHeader() *types.Header {
+ return b.eth.blockchain.CurrentHeader()
+}
+
+func (b *EthAPIBackend) Miner() *miner.Miner {
+ return b.eth.Miner()
+}
+
+func (b *EthAPIBackend) StartMining(threads int) error {
+ return b.eth.StartMining(threads)
+}
+
+func (b *EthAPIBackend) SetContractBackend(client *ethclient.Client) {
+ b.eth.SetContractBackend(client)
+}
diff --git a/eth/backend.go b/eth/backend.go
index 174d6b79872d..c06bbe640114 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -138,7 +138,7 @@ func New(stack *node.Node, config *Config) (*Ethereum, error) {
log.Info("Allocated trie memory caches", "clean", common.StorageSize(config.TrieCleanCache)*1024*1024, "dirty", common.StorageSize(config.TrieDirtyCache)*1024*1024)
// Assemble the Ethereum object
- chainDb, err := stack.ServiceContext.OpenDatabaseWithFreezer("chaindata", config.DatabaseCache, config.DatabaseHandles, config.DatabaseFreezer, "eth/db/chaindata/")
+ chainDb, err := stack.OpenDatabaseWithFreezer("chaindata", config.DatabaseCache, config.DatabaseHandles, config.DatabaseFreezer, "eth/db/chaindata/")
if err != nil {
return nil, err
}
@@ -151,9 +151,9 @@ func New(stack *node.Node, config *Config) (*Ethereum, error) {
eth := &Ethereum{
config: config,
chainDb: chainDb,
- eventMux: stack.ServiceContext.EventMux,
- accountManager: stack.ServiceContext.AccountManager,
- engine: CreateConsensusEngine(stack.ServiceContext, chainConfig, &config.Ethash, config.Miner.Notify, config.Miner.Noverify, chainDb),
+ eventMux: stack.EventMux(),
+ accountManager: stack.AccountManager(),
+ engine: CreateConsensusEngine(stack, chainConfig, &config.Ethash, config.Miner.Notify, config.Miner.Noverify, chainDb),
closeBloomHandler: make(chan struct{}),
networkID: config.NetworkId,
gasPrice: config.Miner.GasPrice,
@@ -206,7 +206,7 @@ func New(stack *node.Node, config *Config) (*Ethereum, error) {
eth.bloomIndexer.Start(eth.blockchain)
if config.TxPool.Journal != "" {
- config.TxPool.Journal = stack.ServiceContext.ResolvePath(config.TxPool.Journal)
+ config.TxPool.Journal = stack.ResolvePath(config.TxPool.Journal)
}
eth.txPool = core.NewTxPool(config.TxPool, chainConfig, eth.blockchain)
@@ -222,14 +222,14 @@ func New(stack *node.Node, config *Config) (*Ethereum, error) {
eth.miner = miner.New(eth, &config.Miner, chainConfig, eth.EventMux(), eth.engine, eth.isLocalBlock)
eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData))
- eth.APIBackend = &EthAPIBackend{stack.ServiceContext.ExtRPCEnabled(), eth, nil}
+ eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), eth, nil}
gpoParams := config.GPO
if gpoParams.Default == nil {
gpoParams.Default = config.Miner.GasPrice
}
eth.APIBackend.gpo = gasprice.NewOracle(eth.APIBackend, gpoParams)
- eth.dialCandidates, err = eth.setupDiscovery(&stack.ServiceContext.Config.P2P)
+ eth.dialCandidates, err = eth.setupDiscovery(&stack.Config().P2P)
if err != nil {
return nil, err
}
@@ -259,7 +259,7 @@ func makeExtraData(extra []byte) []byte {
}
// CreateConsensusEngine creates the required type of consensus engine instance for an Ethereum service
-func CreateConsensusEngine(ctx *node.ServiceContext, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db ethdb.Database) consensus.Engine {
+func CreateConsensusEngine(stack *node.Node, chainConfig *params.ChainConfig, config *ethash.Config, notify []string, noverify bool, db ethdb.Database) consensus.Engine {
// If proof-of-authority is requested, set it up
if chainConfig.Clique != nil {
return clique.New(chainConfig.Clique, db)
@@ -277,7 +277,7 @@ func CreateConsensusEngine(ctx *node.ServiceContext, chainConfig *params.ChainCo
return ethash.NewShared()
default:
engine := ethash.New(ethash.Config{
- CacheDir: ctx.ResolvePath(config.CacheDir),
+ CacheDir: stack.ResolvePath(config.CacheDir),
CachesInMem: config.CachesInMem,
CachesOnDisk: config.CachesOnDisk,
CachesLockMmap: config.CachesLockMmap,
@@ -538,15 +538,6 @@ func (s *Ethereum) Protocols() []p2p.Protocol {
return protos
}
-// P2PServer registers the node's running p2p server with the Backend.
-func (s *Ethereum) P2PServer(server *p2p.Server) error {
- if server == nil {
- return node.ErrNodeStopped // TODO is this error okay to return?
- }
- s.p2pServer = server
- return nil
-}
-
// Start implements node.Lifecycle, starting all internal goroutines needed by the
// Ethereum protocol implementation.
func (s *Ethereum) Start() error {
diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go
index bc0305fc229d..e869f492e575 100644
--- a/ethclient/ethclient.go
+++ b/ethclient/ethclient.go
@@ -397,6 +397,14 @@ func (ec *Client) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuer
return ec.c.EthSubscribe(ctx, ch, "logs", arg)
}
+// ToFilterArg is for testing purposes only.
+func ToFilterArg(q ethereum.FilterQuery, test bool) (interface{}, error) {
+ if test {
+ return toFilterArg(q)
+ }
+ return nil, fmt.Errorf("functionality reserved for testing") // TODO is this okay?
+}
+
func toFilterArg(q ethereum.FilterQuery) (interface{}, error) {
arg := map[string]interface{}{
"address": q.Addresses,
diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go
index b49abe917732..09cbdefa7e72 100644
--- a/ethclient/ethclient_test.go
+++ b/ethclient/ethclient_test.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package ethclient
+package ethclient_test
import (
"context"
@@ -33,23 +33,24 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
+ "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params"
)
// Verify that Client implements the ethereum interfaces.
var (
- _ = ethereum.ChainReader(&Client{})
- _ = ethereum.TransactionReader(&Client{})
- _ = ethereum.ChainStateReader(&Client{})
- _ = ethereum.ChainSyncReader(&Client{})
- _ = ethereum.ContractCaller(&Client{})
- _ = ethereum.GasEstimator(&Client{})
- _ = ethereum.GasPricer(&Client{})
- _ = ethereum.LogFilterer(&Client{})
- _ = ethereum.PendingStateReader(&Client{})
+ _ = ethereum.ChainReader(ðclient.Client{})
+ _ = ethereum.TransactionReader(ðclient.Client{})
+ _ = ethereum.ChainStateReader(ðclient.Client{})
+ _ = ethereum.ChainSyncReader(ðclient.Client{})
+ _ = ethereum.ContractCaller(ðclient.Client{})
+ _ = ethereum.GasEstimator(ðclient.Client{})
+ _ = ethereum.GasPricer(ðclient.Client{})
+ _ = ethereum.LogFilterer(ðclient.Client{})
+ _ = ethereum.PendingStateReader(ðclient.Client{})
// _ = ethereum.PendingStateEventer(&Client{})
- _ = ethereum.PendingContractCaller(&Client{})
+ _ = ethereum.PendingContractCaller(ðclient.Client{})
)
func TestToFilterArg(t *testing.T) {
@@ -163,7 +164,7 @@ func TestToFilterArg(t *testing.T) {
},
} {
t.Run(testCase.name, func(t *testing.T) {
- output, err := toFilterArg(testCase.input)
+ output, err := ethclient.ToFilterArg(testCase.input, true)
if (testCase.err == nil) != (err == nil) {
t.Fatalf("expected error %v but got %v", testCase.err, err)
}
@@ -255,7 +256,7 @@ func TestHeader(t *testing.T) {
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
- ec := NewClient(client)
+ ec := ethclient.NewClient(client)
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
@@ -304,7 +305,7 @@ func TestBalanceAt(t *testing.T) {
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
- ec := NewClient(client)
+ ec := ethclient.NewClient(client)
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
@@ -325,7 +326,7 @@ func TestTransactionInBlockInterrupted(t *testing.T) {
defer backend.Stop()
defer client.Close()
- ec := NewClient(client)
+ ec := ethclient.NewClient(client)
ctx, cancel := context.WithCancel(context.Background())
cancel()
tx, err := ec.TransactionInBlock(ctx, common.Hash{1}, 1)
@@ -342,7 +343,7 @@ func TestChainID(t *testing.T) {
client, _ := backend.Attach()
defer backend.Stop()
defer client.Close()
- ec := NewClient(client)
+ ec := ethclient.NewClient(client)
id, err := ec.ChainID(context.Background())
if err != nil {
diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go
index 0989a9862b12..2f8766ef8cca 100644
--- a/ethstats/ethstats.go
+++ b/ethstats/ethstats.go
@@ -36,7 +36,7 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth"
- "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/les"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
@@ -56,23 +56,15 @@ const (
chainHeadChanSize = 10
)
-type txPool interface {
- // SubscribeNewTxsEvent should return an event subscription of
- // NewTxsEvent and send events to the given channel.
- SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
-}
-
-type blockChain interface {
- SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
-}
+// Backend encompasses the backend behaviors needed for the ethstats service.
+type Backend ethapi.Backend
// Service implements an Ethereum netstats reporting daemon that pushes local
// chain statistics up to a monitoring server.
type Service struct {
- server *p2p.Server // Peer-to-peer server to retrieve networking infos
- eth *eth.Ethereum // Full Ethereum service if monitoring a full node
- les *les.LightEthereum // Light Ethereum service if monitoring a light node
- engine consensus.Engine // Consensus engine to retrieve variadic block fields
+ server *p2p.Server // Peer-to-peer server to retrieve networking infos
+ backend Backend
+ engine consensus.Engine // Consensus engine to retrieve variadic block fields
node string // Name of the node to display on the monitoring page
pass string // Password to authorize access to the monitoring page
@@ -83,7 +75,7 @@ type Service struct {
}
// New returns a monitoring service ready for stats reporting.
-func New(node *node.Node, url string) error {
+func New(node *node.Node, backend Backend, engine consensus.Engine, url string) error {
// Parse the netstats connection url
re := regexp.MustCompile("([^:@]*)(:([^@]*))?@(.+)")
parts := re.FindStringSubmatch(url)
@@ -91,27 +83,14 @@ func New(node *node.Node, url string) error {
return fmt.Errorf("invalid netstats url: \"%s\", should be nodename:secret@host:port", url)
}
ethstats := &Service{
- server: node.Server(),
- node: parts[1],
- pass: parts[3],
- host: parts[4],
- pongCh: make(chan struct{}),
- histCh: make(chan []uint64, 1),
- }
-
- // fetch backend
- var ethServ *eth.Ethereum
- if err := node.ServiceContext.Lifecycle(ðServ); err == nil {
- ethstats.eth = ethServ
- ethstats.engine = ethServ.Engine()
- }
- var lesServ *les.LightEthereum
- if err := node.ServiceContext.Lifecycle(&lesServ); err == nil {
- ethstats.les = lesServ
- ethstats.engine = lesServ.Engine()
- }
- if ethstats.engine == nil { // TODO check to make sure at least one backend is not nil?
- return fmt.Errorf("Ethereum service not found")
+ backend: backend,
+ engine: engine,
+ server: node.Server(),
+ node: parts[1],
+ pass: parts[3],
+ host: parts[4],
+ pongCh: make(chan struct{}),
+ histCh: make(chan []uint64, 1),
}
node.RegisterLifecycle(ethstats)
@@ -136,22 +115,12 @@ func (s *Service) Stop() error {
// until termination.
func (s *Service) loop() {
// Subscribe to chain events to execute updates on
- var blockchain blockChain
- var txpool txPool
- if s.eth != nil {
- blockchain = s.eth.BlockChain()
- txpool = s.eth.TxPool()
- } else {
- blockchain = s.les.BlockChain()
- txpool = s.les.TxPool()
- }
-
chainHeadCh := make(chan core.ChainHeadEvent, chainHeadChanSize)
- headSub := blockchain.SubscribeChainHeadEvent(chainHeadCh)
+ headSub := s.backend.SubscribeChainHeadEvent(chainHeadCh)
defer headSub.Unsubscribe()
txEventCh := make(chan core.NewTxsEvent, txChanSize)
- txSub := txpool.SubscribeNewTxsEvent(txEventCh)
+ txSub := s.backend.SubscribeNewTxsEvent(txEventCh)
defer txSub.Unsubscribe()
// Start a goroutine that exhausts the subscriptions to avoid events piling up
@@ -560,29 +529,20 @@ func (s *Service) assembleBlockStats(block *types.Block) *blockStats {
txs []txStats
uncles []*types.Header
)
- if s.eth != nil {
- // Full nodes have all needed information available
- if block == nil {
- block = s.eth.BlockChain().CurrentBlock()
- }
- header = block.Header()
- td = s.eth.BlockChain().GetTd(header.Hash(), header.Number.Uint64())
- txs = make([]txStats, len(block.Transactions()))
- for i, tx := range block.Transactions() {
- txs[i].Hash = tx.Hash()
- }
- uncles = block.Uncles()
- } else {
- // Light nodes would need on-demand lookups for transactions/uncles, skip
- if block != nil {
- header = block.Header()
- } else {
- header = s.les.BlockChain().CurrentHeader()
- }
- td = s.les.BlockChain().GetTd(header.Hash(), header.Number.Uint64())
- txs = []txStats{}
+ // Full nodes have all needed information available
+ if block == nil {
+ block = s.backend.CurrentBlock()
+ }
+ header = block.Header()
+ td = s.backend.GetTd(header.Hash()) // TODO is it okay to just call `GetTD` with just the hash and not number?
+
+ txs = make([]txStats, len(block.Transactions()))
+ for i, tx := range block.Transactions() {
+ txs[i].Hash = tx.Hash()
}
+ uncles = block.Uncles()
+
// Assemble and return the block stats
author, _ := s.engine.Author(header)
@@ -613,12 +573,7 @@ func (s *Service) reportHistory(conn *websocket.Conn, list []uint64) error {
indexes = append(indexes, list...)
} else {
// No indexes requested, send back the top ones
- var head int64
- if s.eth != nil {
- head = s.eth.BlockChain().CurrentHeader().Number.Int64()
- } else {
- head = s.les.BlockChain().CurrentHeader().Number.Int64()
- }
+ head := s.backend.CurrentHeader().Number.Int64()
start := head - historyUpdateRange + 1
if start < 0 {
start = 0
@@ -631,14 +586,11 @@ func (s *Service) reportHistory(conn *websocket.Conn, list []uint64) error {
history := make([]*blockStats, len(indexes))
for i, number := range indexes {
// Retrieve the next block if it's known to us
- var block *types.Block
- if s.eth != nil {
- block = s.eth.BlockChain().GetBlockByNumber(number)
- } else {
- if header := s.les.BlockChain().GetHeaderByNumber(number); header != nil {
- block = types.NewBlockWithHeader(header)
- }
+ block, err := s.backend.GetBlockByNumber(context.Background(), number)
+ if err != nil {
+ return err
}
+
// If we do have the block, add to the history and continue
if block != nil {
history[len(history)-1-i] = s.assembleBlockStats(block)
@@ -673,12 +625,7 @@ type pendStats struct {
// it to the stats server.
func (s *Service) reportPending(conn *websocket.Conn) error {
// Retrieve the pending count from the local blockchain
- var pending int
- if s.eth != nil {
- pending, _ = s.eth.TxPool().Stats()
- } else {
- pending = s.les.TxPool().Stats()
- }
+ pending, _ := s.backend.Stats()
// Assemble the transaction stats and send it to the server
log.Trace("Sending pending transactions to ethstats", "count", pending)
@@ -712,22 +659,20 @@ func (s *Service) reportStats(conn *websocket.Conn) error {
var (
mining bool
hashrate int
- syncing bool
- gasprice int
)
- if s.eth != nil {
- mining = s.eth.Miner().Mining()
- hashrate = int(s.eth.Miner().HashRate())
- sync := s.eth.Downloader().Progress()
- syncing = s.eth.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock
-
- price, _ := s.eth.APIBackend.SuggestPrice(context.Background())
- gasprice = int(price.Uint64())
- } else {
- sync := s.les.Downloader().Progress()
- syncing = s.les.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock
+ mine := s.backend.Miner()
+ if mine != nil {
+ mining = mine.Mining()
+ hashrate = int(s.backend.Miner().HashRate())
}
+
+ sync := s.backend.Downloader().Progress()
+ syncing := s.backend.CurrentHeader().Number.Uint64() >= sync.HighestBlock
+
+ price, _ := s.backend.SuggestPrice(context.Background())
+ gasprice := int(price.Uint64())
+
// Assemble the node stats and send it to the server
log.Trace("Sending node details to ethstats")
diff --git a/graphql/graphql_test.go b/graphql/graphql_test.go
index 1c2a56ab0877..3b18cb5e670a 100644
--- a/graphql/graphql_test.go
+++ b/graphql/graphql_test.go
@@ -181,13 +181,13 @@ func createNode(t *testing.T, gqlEnabled bool) *node.Node {
func createGQLService(t *testing.T, stack *node.Node, endpoint string) {
// create backend
- _, err := eth.New(stack, ð.DefaultConfig)
+ ethBackend, err := eth.New(stack, ð.DefaultConfig)
if err != nil {
t.Fatalf("could not create eth backend: %v", err)
}
// create gql service
- err = New(stack, endpoint, []string{}, []string{}, rpc.DefaultHTTPTimeouts)
+ err = New(stack, ethBackend.APIBackend, endpoint, []string{}, []string{}, rpc.DefaultHTTPTimeouts)
if err != nil {
t.Fatalf("could not create graphql service: %v", err)
}
diff --git a/graphql/service.go b/graphql/service.go
index 8bec768ef6ef..f93f12a545e3 100644
--- a/graphql/service.go
+++ b/graphql/service.go
@@ -17,12 +17,9 @@
package graphql
import (
- "errors"
"net/http"
- "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/internal/ethapi"
- "github.com/ethereum/go-ethereum/les"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc"
"github.com/graph-gophers/graphql-go"
@@ -30,19 +27,9 @@ import (
)
// New constructs a new GraphQL service instance.
-func New(stack *node.Node, endpoint string, cors, vhosts []string, timeouts rpc.HTTPTimeouts) error {
- // fetch backend
- var backend ethapi.Backend
- var ethServ *eth.Ethereum
- if err := stack.ServiceContext.Lifecycle(ðServ); err == nil {
- backend = ethServ.APIBackend
- }
- var lesServ *les.LightEthereum
- if err := stack.ServiceContext.Lifecycle(&lesServ); err == nil {
- backend = lesServ.ApiBackend
- }
+func New(stack *node.Node, backend ethapi.Backend, endpoint string, cors, vhosts []string, timeouts rpc.HTTPTimeouts) error {
if backend == nil {
- return errors.New("No backend found") // TODO should this be a fatal error?
+ stack.Fatalf("missing backend")
}
// check if http server with given endpoint exists and enable graphQL on it
server := stack.ExistingHTTPServer(endpoint)
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index 074cd794a65a..6d8374eb53c9 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -23,14 +23,17 @@ import (
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader"
+ "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
)
@@ -53,9 +56,12 @@ type Backend interface {
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error)
HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error)
+ CurrentHeader() *types.Header
+ CurrentBlock() *types.Block
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error)
+ GetBlockByNumber(ctx context.Context, number uint64) (*types.Block, error)
StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error)
StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error)
GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
@@ -73,8 +79,13 @@ type Backend interface {
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
Stats() (pending int, queued int)
TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions)
+ TxPool() *core.TxPool
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
+ // Mining API
+ Miner() *miner.Miner
+ StartMining(threads int) error
+
// Filter API
BloomStatus() (uint64, uint64)
GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error)
@@ -84,7 +95,9 @@ type Backend interface {
SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription
ChainConfig() *params.ChainConfig
- CurrentBlock() *types.Block
+ Engine() consensus.Engine
+
+ SetContractBackend(client *ethclient.Client)
}
func GetAPIs(apiBackend Backend) []rpc.API {
diff --git a/les/api_backend.go b/les/api_backend.go
index 448260a19802..d905081077a6 100644
--- a/les/api_backend.go
+++ b/les/api_backend.go
@@ -19,10 +19,12 @@ package les
import (
"context"
"errors"
+ "fmt"
"math/big"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/rawdb"
@@ -31,9 +33,11 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/gasprice"
+ "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/light"
+ "github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
)
@@ -282,3 +286,32 @@ func (b *LesApiBackend) ServiceFilter(ctx context.Context, session *bloombits.Ma
go session.Multiplex(bloomRetrievalBatch, bloomRetrievalWait, b.eth.bloomRequests)
}
}
+
+func (b *LesApiBackend) Engine() consensus.Engine {
+ return b.eth.engine
+}
+
+func (b *LesApiBackend) GetBlockByNumber(ctx context.Context, number uint64) (*types.Block, error) {
+ header := b.eth.blockchain.GetHeaderByNumber(number)
+ return types.NewBlockWithHeader(header), nil // TODO is this okay?
+}
+
+func (b *LesApiBackend) CurrentHeader() *types.Header {
+ return b.eth.blockchain.CurrentHeader()
+}
+
+func (b *LesApiBackend) Miner() *miner.Miner {
+ return nil
+}
+
+func (b *LesApiBackend) StartMining(threads int) error {
+ return fmt.Errorf("Light clients do not support mining") // TODO is this okay?
+}
+
+func (b *LesApiBackend) SetContractBackend(client *ethclient.Client) {
+ b.eth.SetContractBackend(client)
+}
+
+func (b *LesApiBackend) TxPool() *core.TxPool {
+ return nil // TODO is this okay?
+}
diff --git a/les/client.go b/les/client.go
index dc6a86a0fc9f..b8bd3ccabc40 100644
--- a/les/client.go
+++ b/les/client.go
@@ -78,11 +78,11 @@ type LightEthereum struct {
}
func New(stack *node.Node, config *eth.Config) (*LightEthereum, error) {
- chainDb, err := stack.ServiceContext.OpenDatabase("lightchaindata", config.DatabaseCache, config.DatabaseHandles, "eth/db/chaindata/")
+ chainDb, err := stack.OpenDatabase("lightchaindata", config.DatabaseCache, config.DatabaseHandles, "eth/db/chaindata/")
if err != nil {
return nil, err
}
- lespayDb, err := stack.ServiceContext.OpenDatabase("lespay", 0, 0, "eth/db/lespay")
+ lespayDb, err := stack.OpenDatabase("lespay", 0, 0, "eth/db/lespay")
if err != nil {
return nil, err
}
@@ -103,10 +103,10 @@ func New(stack *node.Node, config *eth.Config) (*LightEthereum, error) {
closeCh: make(chan struct{}),
},
peers: peers,
- eventMux: stack.ServiceContext.EventMux,
+ eventMux: stack.EventMux(),
reqDist: newRequestDistributor(peers, &mclock.System{}),
- accountManager: stack.ServiceContext.AccountManager,
- engine: eth.CreateConsensusEngine(stack.ServiceContext, chainConfig, &config.Ethash, nil, false, chainDb),
+ accountManager: stack.AccountManager(),
+ engine: eth.CreateConsensusEngine(stack, chainConfig, &config.Ethash, nil, false, chainDb),
bloomRequests: make(chan chan *bloombits.Retrieval),
bloomIndexer: eth.NewBloomIndexer(chainDb, params.BloomBitsBlocksClient, params.HelperTrieConfirmations),
valueTracker: lpc.NewValueTracker(lespayDb, &mclock.System{}, requestList, time.Minute, 1/float64(time.Hour), 1/float64(time.Hour*100), 1/float64(time.Hour*1000)),
@@ -114,7 +114,7 @@ func New(stack *node.Node, config *eth.Config) (*LightEthereum, error) {
}
peers.subscribe((*vtSubscription)(leth.valueTracker))
- dnsdisc, err := leth.setupDiscovery(&stack.ServiceContext.Config.P2P)
+ dnsdisc, err := leth.setupDiscovery(&stack.Config().P2P)
if err != nil {
return nil, err
}
@@ -164,7 +164,7 @@ func New(stack *node.Node, config *eth.Config) (*LightEthereum, error) {
rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig)
}
- leth.ApiBackend = &LesApiBackend{stack.ServiceContext.ExtRPCEnabled(), leth, nil}
+ leth.ApiBackend = &LesApiBackend{stack.Config().ExtRPCEnabled(), leth, nil}
gpoParams := config.GPO
if gpoParams.Default == nil {
gpoParams.Default = config.Miner.GasPrice
diff --git a/miner/stress_clique.go b/miner/stress_clique.go
index 6f65ddddfca2..143ce3f5eb2e 100644
--- a/miner/stress_clique.go
+++ b/miner/stress_clique.go
@@ -22,6 +22,7 @@ package main
import (
"bytes"
"crypto/ecdsa"
+ "github.com/ethereum/go-ethereum/internal/ethapi"
"io/ioutil"
"math/big"
"math/rand"
@@ -59,12 +60,16 @@ func main() {
genesis := makeGenesis(faucets, sealers)
var (
- nodes []*node.Node
+ nodes []struct{
+ node *node.Node
+ backend ethapi.Backend
+ }
enodes []*enode.Node
)
+
for _, sealer := range sealers {
// Start the node and wait until it's up
- node, err := makeSealer(genesis)
+ node, ethBackend, err := makeSealer(genesis)
if err != nil {
panic(err)
}
@@ -78,7 +83,13 @@ func main() {
node.Server().AddPeer(n)
}
// Start tracking the node and it's enode
- nodes = append(nodes, node)
+ nodes = append(nodes, struct{
+ node *node.Node
+ backend ethapi.Backend
+ }{
+ node,
+ ethBackend,
+ })
enodes = append(enodes, node.Server().Self())
// Inject the signer key and start sealing with it
@@ -95,11 +106,7 @@ func main() {
time.Sleep(3 * time.Second)
for _, node := range nodes {
- var ethereum *eth.Ethereum
- if err := node.ServiceContext.Lifecycle(ðereum); err != nil {
- panic(err)
- }
- if err := ethereum.StartMining(1); err != nil {
+ if err := node.backend.StartMining(1); err != nil {
panic(err)
}
}
@@ -111,22 +118,24 @@ func main() {
index := rand.Intn(len(faucets))
// Fetch the accessor for the relevant signer
- var ethereum *eth.Ethereum
- if err := nodes[index%len(nodes)].Service(ðereum); err != nil {
- panic(err)
- }
+ backend := nodes[index%len(nodes)].backend
+
// Create a self transaction and inject into the pool
tx, err := types.SignTx(types.NewTransaction(nonces[index], crypto.PubkeyToAddress(faucets[index].PublicKey), new(big.Int), 21000, big.NewInt(100000000000), nil), types.HomesteadSigner{}, faucets[index])
if err != nil {
panic(err)
}
- if err := ethereum.TxPool().AddLocal(tx); err != nil {
+ txpool := backend.TxPool()
+ if txpool == nil {
+ panic(fmt.Errorf("Ethereum service not running"))
+ }
+ if err := txpool.AddLocal(tx); err != nil {
panic(err)
}
nonces[index]++
// Wait if we're too saturated
- if pend, _ := ethereum.TxPool().Stats(); pend > 2048 {
+ if pend, _ := txpool.Stats(); pend > 2048 {
time.Sleep(100 * time.Millisecond)
}
}
@@ -169,7 +178,7 @@ func makeGenesis(faucets []*ecdsa.PrivateKey, sealers []*ecdsa.PrivateKey) *core
return genesis
}
-func makeSealer(genesis *core.Genesis) (*node.Node, error) {
+func makeSealer(genesis *core.Genesis) (*node.Node, ethapi.Backend, error) {
// Define the basic configurations for the Ethereum node
datadir, _ := ioutil.TempDir("", "")
@@ -213,5 +222,5 @@ func makeSealer(genesis *core.Genesis) (*node.Node, error) {
stack.RegisterLifecycle(ethBackend)
// Start the node and return if successful
- return stack, stack.Start()
+ return stack, ethBackend, stack.Start()
}
diff --git a/miner/stress_ethash.go b/miner/stress_ethash.go
index 6893dff6e273..4e0a75174d3f 100644
--- a/miner/stress_ethash.go
+++ b/miner/stress_ethash.go
@@ -61,12 +61,15 @@ func main() {
genesis := makeGenesis(faucets)
var (
- nodes []*node.Node
+ nodes []struct{
+ node *node.Node
+ backend ethapi.Backend
+ }
enodes []*enode.Node
)
for i := 0; i < 4; i++ {
// Start the node and wait until it's up
- node, err := makeMiner(genesis)
+ node, ethBackend, err := makeMiner(genesis)
if err != nil {
panic(err)
}
@@ -80,7 +83,13 @@ func main() {
node.Server().AddPeer(n)
}
// Start tracking the node and it's enode
- nodes = append(nodes, node)
+ nodes = append(nodes, struct{
+ node *node.Node
+ backend ethapi.Backend
+ }{
+ node,
+ ethBackend,
+ })
enodes = append(enodes, node.Server().Self())
// Inject the signer key and start sealing with it
@@ -93,11 +102,7 @@ func main() {
time.Sleep(3 * time.Second)
for _, node := range nodes {
- var ethereum *eth.Ethereum
- if err := node.ServiceContext.Lifecycle(ðereum); err != nil {
- panic(err)
- }
- if err := ethereum.StartMining(1); err != nil {
+ if err := node.backend.StartMining(1); err != nil {
panic(err)
}
}
@@ -107,24 +112,24 @@ func main() {
nonces := make([]uint64, len(faucets))
for {
index := rand.Intn(len(faucets))
-
// Fetch the accessor for the relevant signer
- var ethereum *eth.Ethereum
- if err := nodes[index%len(nodes)].Service(ðereum); err != nil {
- panic(err)
- }
+ backend := nodes[index%len(nodes)].backend
// Create a self transaction and inject into the pool
tx, err := types.SignTx(types.NewTransaction(nonces[index], crypto.PubkeyToAddress(faucets[index].PublicKey), new(big.Int), 21000, big.NewInt(100000000000+rand.Int63n(65536)), nil), types.HomesteadSigner{}, faucets[index])
if err != nil {
panic(err)
}
- if err := ethereum.TxPool().AddLocal(tx); err != nil {
+ txpool := backend.TxPool()
+ if txpool == nil {
+ panic(fmt.Errorf("Ethereum service not running"))
+ }
+ if err := txpool.AddLocal(tx); err != nil {
panic(err)
}
nonces[index]++
// Wait if we're too saturated
- if pend, _ := ethereum.TxPool().Stats(); pend > 2048 {
+ if pend, _ := txpool.Stats(); pend > 2048 {
time.Sleep(100 * time.Millisecond)
}
}
@@ -149,7 +154,7 @@ func makeGenesis(faucets []*ecdsa.PrivateKey) *core.Genesis {
return genesis
}
-func makeMiner(genesis *core.Genesis) (*node.Node, error) {
+func makeMiner(genesis *core.Genesis) (*node.Node, ethapi.Backend, error) {
// Define the basic configurations for the Ethereum node
datadir, _ := ioutil.TempDir("", "")
@@ -194,5 +199,5 @@ func makeMiner(genesis *core.Genesis) (*node.Node, error) {
stack.RegisterLifecycle(ethBackend)
// Start the node and return if successful
- return stack, stack.Start()
+ return stack, ethBackend, stack.Start()
}
diff --git a/mobile/geth.go b/mobile/geth.go
index 053c304f6738..22567a5d107b 100644
--- a/mobile/geth.go
+++ b/mobile/geth.go
@@ -175,20 +175,20 @@ func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) {
ethConf.SyncMode = downloader.LightSync
ethConf.NetworkId = uint64(config.EthereumNetworkID)
ethConf.DatabaseCache = config.EthereumDatabaseCache
- _, err = les.New(rawStack, ðConf)
+ lesBackend, err := les.New(rawStack, ðConf)
if err != nil {
return nil, fmt.Errorf("ethereum init: %v", err)
}
// If netstats reporting is requested, do it
if config.EthereumNetStats != "" {
- if err := ethstats.New(rawStack, config.EthereumNetStats); err != nil {
+ if err := ethstats.New(rawStack, lesBackend.ApiBackend, lesBackend.Engine(), config.EthereumNetStats); err != nil {
return nil, fmt.Errorf("netstats init: %v", err)
}
}
}
// Register the Whisper protocol if requested
if config.WhisperEnabled {
- if err := whisper.New(rawStack, &whisper.DefaultConfig); err != nil {
+ if _, err := whisper.New(rawStack, &whisper.DefaultConfig); err != nil {
return nil, fmt.Errorf("whisper init: %v", err)
}
}
diff --git a/node/api.go b/node/api.go
index ec9b836d7203..d6f1901907aa 100644
--- a/node/api.go
+++ b/node/api.go
@@ -159,7 +159,7 @@ func (api *PrivateAdminAPI) StartRPC(host *string, port *int, cors *string, apis
}
endpoint := fmt.Sprintf("%s:%d", *host, *port)
// check if HTTP server already exists
- if server, exists := api.node.HTTPServers.servers[endpoint]; exists {
+ if server, exists := api.node.httpServers[endpoint]; exists {
if server.RPCAllowed {
return false, fmt.Errorf("HTTP RPC already running on %v", server.Listener.Addr())
}
@@ -220,7 +220,7 @@ func (api *PrivateAdminAPI) StopRPC() (bool, error) {
api.node.lock.Lock()
defer api.node.lock.Unlock()
- for _, httpServer := range api.node.HTTPServers.servers {
+ for _, httpServer := range api.node.httpServers {
if httpServer.RPCAllowed {
api.node.stopServer(httpServer)
return true, nil
@@ -235,7 +235,7 @@ func (api *PrivateAdminAPI) StartWS(host *string, port *int, allowedOrigins *str
api.node.lock.Lock()
defer api.node.lock.Unlock()
// check if an existing WS server already exists
- for _, server := range api.node.HTTPServers.servers {
+ for _, server := range api.node.httpServers {
if server.WSAllowed {
return false, fmt.Errorf("WebSocket RPC already running on %v", server.Listener.Addr())
}
@@ -253,7 +253,7 @@ func (api *PrivateAdminAPI) StartWS(host *string, port *int, allowedOrigins *str
}
endpoint := fmt.Sprintf("%s:%d", *host, *port)
// check if there is an existing server on the specified port, and if there is, enable ws on it
- if server, exists := api.node.HTTPServers.servers[endpoint]; exists {
+ if server, exists := api.node.httpServers[endpoint]; exists {
// else configure ws on the existing server
server.WSAllowed = true
// configure origins
@@ -322,7 +322,7 @@ func (api *PrivateAdminAPI) StopWS() (bool, error) {
api.node.lock.Lock()
defer api.node.lock.Unlock()
- for _, httpServer := range api.node.HTTPServers.servers {
+ for _, httpServer := range api.node.httpServers {
if httpServer.WSAllowed {
httpServer.WSAllowed = false
// if RPC is not enabled on the WS http server, shut it down
diff --git a/node/lifecycle.go b/node/lifecycle.go
new file mode 100644
index 000000000000..0d5f9a0680a0
--- /dev/null
+++ b/node/lifecycle.go
@@ -0,0 +1,31 @@
+// Copyright 2020 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package node
+
+// Lifecycle encompasses the behavior of services that can be started and stopped
+// on the node. Lifecycle management is delegated to the node, but it is the
+// responsibility of the service-specific package to configure and register the
+// service on the node using the `RegisterLifecycle` method.
+type Lifecycle interface {
+ // Start is called after all services have been constructed and the networking
+ // layer was also initialized to spawn any goroutines required by the service.
+ Start() error
+
+ // Stop terminates all goroutines belonging to the service, blocking until they
+ // are all terminated.
+ Stop() error
+}
diff --git a/node/node.go b/node/node.go
index f4c35254ac67..d347c7fbaa69 100644
--- a/node/node.go
+++ b/node/node.go
@@ -52,11 +52,9 @@ type Node struct {
server *p2p.Server // Currently running P2P networking layer
- ServiceContext *ServiceContext // TODO rename to LifecycleContext or just NodeContext?
-
lifecycles map[reflect.Type]Lifecycle // All registered backends, services, and auxiliary services that have a lifecycle
- HTTPServers *HTTPServers // HTTPServers stores information about the node's rpc, ws, and graphQL http servers.
+ httpServers serverMap // serverMap stores information about the node's rpc, ws, and graphQL http servers.
rpcAPIs []rpc.API // List of APIs currently provided by the node
inprocHandler *rpc.Server // In-process RPC request handler to process the API requests
@@ -109,13 +107,7 @@ func New(conf *Config) (*Node, error) {
ephemeralKeystore: ephemeralKeystore,
config: conf,
lifecycles: make(map[reflect.Type]Lifecycle),
- ServiceContext: &ServiceContext{
- Config: *conf,
- Lifecycles: make(map[reflect.Type]Lifecycle),
- },
- HTTPServers: &HTTPServers{
- servers: make(map[string]*HTTPServer),
- },
+ httpServers: make(serverMap),
ipc: &HTTPServer{
endpoint: conf.IPCEndpoint(),
},
@@ -138,9 +130,7 @@ func New(conf *Config) (*Node, error) {
if node.server.Config.NodeDatabase == "" {
node.server.Config.NodeDatabase = node.config.NodeDB()
}
- // Configure service context
- node.ServiceContext.EventMux = node.eventmux
- node.ServiceContext.AccountManager = node.accman
+
// Configure HTTP server(s)
if conf.HTTPHost != "" {
httpServ := &HTTPServer{
@@ -159,13 +149,14 @@ func New(conf *Config) (*Node, error) {
httpServ.WSAllowed = true
httpServ.WsOrigins = conf.WSOrigins
httpServ.Whitelist = append(httpServ.Whitelist, conf.WSModules...)
- node.HTTPServers.servers[conf.HTTPEndpoint()] = httpServ
+
+ node.httpServers[conf.HTTPEndpoint()] = httpServ
return node, nil
}
- node.HTTPServers.servers[conf.HTTPEndpoint()] = httpServ
+ node.httpServers[conf.HTTPEndpoint()] = httpServ
}
if conf.WSHost != "" {
- node.HTTPServers.servers[conf.WSEndpoint()] = &HTTPServer{
+ node.httpServers[conf.WSEndpoint()] = &HTTPServer{
WsOrigins: conf.WSOrigins,
Whitelist: conf.WSModules,
Srv: rpc.NewServer(),
@@ -206,11 +197,10 @@ func (n *Node) Close() error {
func (n *Node) RegisterLifecycle(lifecycle Lifecycle) {
kind := reflect.TypeOf(lifecycle)
if _, exists := n.lifecycles[kind]; exists {
- Fatalf("Lifecycle cannot be registered more than once", kind)
+ n.Fatalf("Lifecycle cannot be registered more than once", kind)
}
n.lifecycles[kind] = lifecycle
- n.ServiceContext.Lifecycles[kind] = lifecycle
}
// RegisterProtocols adds backend's protocols to the node's p2p server
@@ -225,12 +215,12 @@ func (n *Node) RegisterAPIs(apis []rpc.API) {
// RegisterHTTPServer registers the given HTTP server on the node
func (n *Node) RegisterHTTPServer(endpoint string, server *HTTPServer) {
- n.HTTPServers.servers[endpoint] = server
+ n.httpServers[endpoint] = server
}
// ExistingHTTPServer checks if an HTTP server is already configured on the given endpoint
func (n *Node) ExistingHTTPServer(endpoint string) *HTTPServer {
- if server, exists := n.HTTPServers.servers[endpoint]; exists {
+ if server, exists := n.httpServers[endpoint]; exists {
return server
}
return nil
@@ -258,8 +248,7 @@ func (n *Node) CreateHTTPServer(h *HTTPServer, exposeAll bool) error {
httpSrv.WriteTimeout = h.Timeouts.WriteTimeout
httpSrv.IdleTimeout = h.Timeouts.IdleTimeout
}
-
- // complete the HTTPServers
+ // add listener and http.Server to HTTPServer
h.Listener = listener
h.Server = httpSrv
@@ -294,7 +283,7 @@ func (n *Node) Start() error {
// Configure the RPC interfaces
if err := n.configureRPC(); err != nil {
- n.HTTPServers.Stop()
+ n.httpServers.Stop()
n.server.Stop()
return err
}
@@ -310,10 +299,6 @@ func (n *Node) Start() error {
started = append(started, lifecycle)
}
- // Finish initializing the service context
- n.ServiceContext.AccountManager = n.accman
- n.ServiceContext.EventMux = n.eventmux
-
// Finish initializing the startup
n.stop = make(chan struct{})
return nil
@@ -365,7 +350,7 @@ func (n *Node) configureRPC() error {
return err
}
- for _, server := range n.HTTPServers.servers {
+ for _, server := range n.httpServers {
// configure the handlers
if server.RPCAllowed {
server.handler = NewHTTPHandlerStack(server.Srv, server.CorsAllowedOrigins, server.Vhosts)
@@ -396,8 +381,8 @@ func (n *Node) configureRPC() error {
}
}
// only register http server as a lifecycle if it has not already been registered
- if _, exists := n.lifecycles[reflect.TypeOf(n.HTTPServers)]; !exists {
- n.RegisterLifecycle(n.HTTPServers)
+ if _, exists := n.lifecycles[reflect.TypeOf(n.httpServers)]; !exists {
+ n.RegisterLifecycle(n.httpServers)
}
// All API endpoints started successfully
return nil
@@ -467,7 +452,7 @@ func (n *Node) stopServer(server *HTTPServer) {
server.Srv = nil
}
// remove stopped http server from node's http servers // TODO is this preferable?
- delete(n.HTTPServers.servers, server.endpoint)
+ delete(n.httpServers, server.endpoint)
}
// Stop terminates a running node along with all it's services. In the node was
@@ -594,7 +579,7 @@ func (n *Node) WSEndpoint() string {
n.lock.Lock()
defer n.lock.Unlock()
- for _, httpServer := range n.HTTPServers.servers {
+ for _, httpServer := range n.httpServers {
if httpServer.WSAllowed {
if httpServer.Listener != nil {
return httpServer.Listener.Addr().String()
@@ -612,24 +597,6 @@ func (n *Node) EventMux() *event.TypeMux {
return n.eventmux
}
-// Lifecycle retrieves a currently running Lifecycle registered of a specific type.
-func (n *Node) Lifecycle(lifecycle interface{}) error {
- n.lock.RLock()
- defer n.lock.RUnlock()
-
- // Short circuit if the node's not running
- if !n.running() {
- return ErrNodeStopped
- }
- // Otherwise try to find the service to return
- element := reflect.ValueOf(lifecycle).Elem()
- if running, ok := n.lifecycles[element.Type()]; ok {
- element.Set(reflect.ValueOf(running))
- return nil
- }
- return ErrServiceUnknown
-}
-
// OpenDatabase opens an existing database with the given name (or creates one if no
// previous can be found) from within the node's instance directory. If the node is
// ephemeral, a memory database is returned.
@@ -712,11 +679,10 @@ func RegisterApisFromWhitelist(apis []rpc.API, modules []string, srv *rpc.Server
return nil
}
-// TODO change this when you figure out how else to do a nice fatal err
// Fatalf formats a message to standard error and exits the program.
// The message is also printed to standard output if standard error
// is redirected to a different file.
-func Fatalf(format string, args ...interface{}) {
+func (n *Node) Fatalf(format string, args ...interface{}) {
w := io.MultiWriter(os.Stdout, os.Stderr)
if runtime.GOOS == "windows" {
// The SameFile check below doesn't work on Windows.
diff --git a/node/node_test.go b/node/node_test.go
index e00c686058e4..d87a5612a5ce 100644
--- a/node/node_test.go
+++ b/node/node_test.go
@@ -364,47 +364,6 @@ func TestLifecycleTerminationGuarantee(t *testing.T) {
stack.server.PrivateKey = testNodeKey
}
-// TestLifecycleRetrieval tests that individual services can be retrieved.
-func TestLifecycleRetrieval(t *testing.T) {
- // Create a simple stack and register two service types
- stack, err := New(testNodeConfig())
- if err != nil {
- t.Fatalf("failed to create protocol stack: %v", err)
- }
- defer stack.Close()
-
- noop := NewNoop()
- stack.RegisterLifecycle(noop)
-
- is, err := NewInstrumentedService()
- if err != nil {
- t.Fatalf("instrumented service creation failed: %v", err)
- }
- stack.RegisterLifecycle(is)
-
- // Make sure none of the services can be retrieved until started
- var noopServ *Noop
- if err := stack.Lifecycle(&noopServ); err != ErrNodeStopped {
- t.Fatalf("noop service retrieval mismatch: have %v, want %v", err, ErrNodeStopped)
- }
- var instServ *InstrumentedService
- if err := stack.Lifecycle(&instServ); err != ErrNodeStopped {
- t.Fatalf("instrumented service retrieval mismatch: have %v, want %v", err, ErrNodeStopped)
- }
- // Start the stack and ensure everything is retrievable now
- if err := stack.Start(); err != nil {
- t.Fatalf("failed to start stack: %v", err)
- }
- defer stack.Stop()
-
- if err := stack.Lifecycle(&noopServ); err != nil {
- t.Fatalf("noop service retrieval mismatch: have %v, want %v", err, nil)
- }
- if err := stack.Lifecycle(&instServ); err != nil {
- t.Fatalf("instrumented service retrieval mismatch: have %v, want %v", err, nil)
- }
-}
-
// Tests whether a given HTTPServer can be registered on the node
func TestRegisterHTTPServer(t *testing.T) {
stack, err := New(testNodeConfig())
@@ -449,15 +408,21 @@ func TestRegisterHTTPServer(t *testing.T) {
func TestHTTPServerCreateAndStop(t *testing.T) {
// test on same ports
node1 := startHTTP(t, 7453, 7453)
- if len(node1.HTTPServers.servers) != 1 {
+ if len(node1.httpServers) != 1 {
t.Fatalf("node has more than 1 http server")
}
// check to make sure http servers are registered
- var httpSrv1 *HTTPServers
- if err := node1.Lifecycle(&httpSrv1); err != nil {
- t.Fatalf("HTTP servers not registered as lifecycles on the node: %v", err)
+ var exists bool
+ for _, lifecycle := range node1.lifecycles {
+ if reflect.DeepEqual(node1.httpServers, lifecycle) {
+ exists = true
+ }
+ }
+ if !exists {
+ t.Fatal("HTTP servers not registered as lifecycles on the node")
}
- for _, server := range node1.HTTPServers.servers {
+ // check to make sure http servers are configured properly
+ for _, server := range node1.httpServers {
if !(server.WSAllowed && server.RPCAllowed) {
t.Fatalf("node's http server is not configured to handle both rpc and ws")
}
@@ -466,21 +431,25 @@ func TestHTTPServerCreateAndStop(t *testing.T) {
t.Fatalf("failed to remove server %v from node after stopping it", server)
}
}
-
node1.Close()
// test on separate ports
node2 := startHTTP(t, 7453, 9393)
- if len(node2.HTTPServers.servers) != 2 {
+ if len(node2.httpServers) != 2 {
t.Fatalf("amount of http servers on the node is not equal to 2")
}
// check to make sure http servers are registered
- var httpSrv2 *HTTPServers
- if err := node2.Lifecycle(&httpSrv2); err != nil {
- t.Fatalf("HTTP servers not registered as lifecycles on the node: %v", err)
+ exists = false
+ for _, lifecycle := range node2.lifecycles {
+ if reflect.DeepEqual(node2.httpServers, lifecycle) {
+ exists = true
+ }
+ }
+ if !exists {
+ t.Fatal("HTTP servers not registered as lifecycles on the node")
}
// check that neither http server has both ws and rpc enabled
- for _, server := range node2.HTTPServers.servers {
+ for _, server := range node2.httpServers {
if server.WSAllowed && server.RPCAllowed {
t.Fatalf("both rpc and ws allowed on a single http server")
}
diff --git a/node/rpcstack.go b/node/rpcstack.go
index 3ceab681e687..d12089274b8d 100644
--- a/node/rpcstack.go
+++ b/node/rpcstack.go
@@ -32,9 +32,7 @@ import (
"github.com/rs/cors"
)
-type HTTPServers struct {
- servers map[string]*HTTPServer // Stores information about all http servers (if any) by their port, including http, ws, and graphql
-}
+type serverMap map[string]*HTTPServer // Stores information about all http servers (if any) by their endpoint, including http, ws, and graphql
type HTTPServer struct {
handler http.Handler
@@ -55,23 +53,23 @@ type HTTPServer struct {
Timeouts rpc.HTTPTimeouts
RPCAllowed bool
- WSAllowed bool
+ WSAllowed bool // TODO discuss this later bc possible race condition
GQLAllowed bool
GQLHandler http.Handler
}
-func (h *HTTPServers) Start() error {
- for _, server := range h.servers {
+func (sm serverMap) Start() error {
+ for _, server := range sm {
if err := server.Start(); err != nil {
- return h.Stop()
+ return sm.Stop()
}
}
return nil
}
-func (h *HTTPServers) Stop() error {
- for _, server := range h.servers {
+func (sm serverMap) Stop() error {
+ for _, server := range sm {
if err := server.Stop(); err != nil {
return err
}
@@ -79,14 +77,14 @@ func (h *HTTPServers) Stop() error {
return nil
}
-// Start starts the HTTPServers's HTTP server. // TODO I don't like the way this is written
+// Start starts the serverMap's HTTP server. // TODO I don't like the way this is written
func (h *HTTPServer) Start() error {
go h.Server.Serve(h.Listener)
log.Info("HTTP endpoint successfully opened", "url", fmt.Sprintf("http://%v/", h.Listener.Addr()))
return nil
}
-// Stop shuts down the HTTPServers's HTTP server. // TODO I don't like the way this is written
+// Stop shuts down the serverMap's HTTP server. // TODO I don't like the way this is written
func (h *HTTPServer) Stop() error {
if h.Server != nil {
url := fmt.Sprintf("http://%v/", h.Listener.Addr())
@@ -102,12 +100,12 @@ func (h *HTTPServer) Stop() error {
return nil
}
-// SetHandler assigns the given handler to the HTTPServers.
+// SetHandler assigns the given handler to the serverMap.
func (h *HTTPServer) SetHandler(handler http.Handler) {
h.handler = handler
}
-// SetEndpoints assigns the given endpoint to the HTTPServers.
+// SetEndpoints assigns the given endpoint to the serverMap.
func (h *HTTPServer) SetEndpoint(endpoint string) {
h.endpoint = endpoint
}
@@ -224,6 +222,7 @@ func newGzipHandler(next http.Handler) http.Handler {
// NewWebsocketUpgradeHandler returns a websocket handler that serves an incoming request only if it contains an upgrade
// request to the websocket protocol. If not, serves the the request with the http handler.
func (hs *HTTPServer) NewWebsocketUpgradeHandler(h http.Handler, ws http.Handler) http.Handler {
+ // TODO make sure you protect the pointer
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if hs.WSAllowed && isWebsocket(r) {
ws.ServeHTTP(w, r)
diff --git a/node/rpcstack_test.go b/node/rpcstack_test.go
index dc75afa93438..799b138a6917 100644
--- a/node/rpcstack_test.go
+++ b/node/rpcstack_test.go
@@ -40,7 +40,7 @@ func TestNewWebsocketUpgradeHandler_websocket(t *testing.T) {
assert.Equal(t, "websocket", response.Header.Get("Upgrade"))
}
-// Tests that a ws handler can be added to and enabled on an existing HTTPServers
+// Tests that a ws handler can be added to and enabled on an existing HTTPServer
func TestWSAllowed(t *testing.T) {
stack, err := New(&Config{
HTTPHost: DefaultHTTPHost,
diff --git a/node/service.go b/node/service.go
deleted file mode 100644
index e7746e92262c..000000000000
--- a/node/service.go
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package node
-
-import (
- "path/filepath"
- "reflect"
-
- "github.com/ethereum/go-ethereum/accounts"
- "github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/event"
-)
-
-// ServiceContext is a collection of service independent options inherited from
-// the protocol stack, that is passed to all constructors to be optionally used;
-// as well as utility methods to operate on the service environment.
-type ServiceContext struct {
- Config Config
- Lifecycles map[reflect.Type]Lifecycle // TODO should this be in the service context or should it be on the node itself .. ?
- EventMux *event.TypeMux // Event multiplexer used for decoupled notifications
- AccountManager *accounts.Manager // Account manager created by the node.
-}
-
-// OpenDatabase opens an existing database with the given name (or creates one
-// if no previous can be found) from within the node's data directory. If the
-// node is an ephemeral one, a memory database is returned.
-func (ctx *ServiceContext) OpenDatabase(name string, cache int, handles int, namespace string) (ethdb.Database, error) {
- if ctx.Config.DataDir == "" {
- return rawdb.NewMemoryDatabase(), nil
- }
- return rawdb.NewLevelDBDatabase(ctx.Config.ResolvePath(name), cache, handles, namespace)
-}
-
-// OpenDatabaseWithFreezer opens an existing database with the given name (or
-// creates one if no previous can be found) from within the node's data directory,
-// also attaching a chain freezer to it that moves ancient chain data from the
-// database to immutable append-only files. If the node is an ephemeral one, a
-// memory database is returned.
-func (ctx *ServiceContext) OpenDatabaseWithFreezer(name string, cache int, handles int, freezer string, namespace string) (ethdb.Database, error) {
- if ctx.Config.DataDir == "" {
- return rawdb.NewMemoryDatabase(), nil
- }
- root := ctx.Config.ResolvePath(name)
-
- switch {
- case freezer == "":
- freezer = filepath.Join(root, "ancient")
- case !filepath.IsAbs(freezer):
- freezer = ctx.Config.ResolvePath(freezer)
- }
- return rawdb.NewLevelDBDatabaseWithFreezer(root, cache, handles, freezer, namespace)
-}
-
-// ResolvePath resolves a user path into the data directory if that was relative
-// and if the user actually uses persistent storage. It will return an empty string
-// for emphemeral storage and the user's own input for absolute paths.
-func (ctx *ServiceContext) ResolvePath(path string) string {
- return ctx.Config.ResolvePath(path)
-}
-
-// Lifecycle retrieves a currently running Lifecycle registered of a specific type.
-func (ctx *ServiceContext) Lifecycle(lifecycle interface{}) error {
- element := reflect.ValueOf(lifecycle).Elem()
- if running, ok := ctx.Lifecycles[element.Type()]; ok {
- element.Set(reflect.ValueOf(running))
- return nil
- }
- return ErrServiceUnknown
-}
-
-// ExtRPCEnabled returns the indicator whether node enables the external
-// RPC(http, ws or graphql).
-func (ctx *ServiceContext) ExtRPCEnabled() bool {
- return ctx.Config.ExtRPCEnabled()
-}
-
-// Lifecycle encompasses the behavior of services that can be started and stopped
-// on the node. Lifecycle management is delegated to the node, but it is the
-// responsibility of the service-specific package to configure and register the
-// service on the node using the `RegisterLifecycle` method.
-type Lifecycle interface {
- // Start is called after all services have been constructed and the networking
- // layer was also initialized to spawn any goroutines required by the service.
- Start() error
-
- // Stop terminates all goroutines belonging to the service, blocking until they
- // are all terminated.
- Stop() error
-}
diff --git a/node/service_test.go b/node/service_test.go
deleted file mode 100644
index 80f40aeeb68b..000000000000
--- a/node/service_test.go
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of the go-ethereum library.
-//
-// The go-ethereum library is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Lesser General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// The go-ethereum library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see .
-
-package node
-
-import (
- "io/ioutil"
- "os"
- "path/filepath"
- "testing"
-)
-
-// Tests that databases are correctly created persistent or ephemeral based on
-// the configured service context.
-func TestContextDatabases(t *testing.T) {
- // Create a temporary folder and ensure no database is contained within
- dir, err := ioutil.TempDir("", "")
- if err != nil {
- t.Fatalf("failed to create temporary data directory: %v", err)
- }
- defer os.RemoveAll(dir)
-
- if _, err := os.Stat(filepath.Join(dir, "database")); err == nil {
- t.Fatalf("non-created database already exists")
- }
- // Request the opening/creation of a database and ensure it persists to disk
- ctx := &ServiceContext{Config: Config{Name: "unit-test", DataDir: dir}}
- db, err := ctx.OpenDatabase("persistent", 0, 0, "")
- if err != nil {
- t.Fatalf("failed to open persistent database: %v", err)
- }
- db.Close()
-
- if _, err := os.Stat(filepath.Join(dir, "unit-test", "persistent")); err != nil {
- t.Fatalf("persistent database doesn't exists: %v", err)
- }
- // Request th opening/creation of an ephemeral database and ensure it's not persisted
- ctx = &ServiceContext{Config: Config{DataDir: ""}}
- db, err = ctx.OpenDatabase("ephemeral", 0, 0, "")
- if err != nil {
- t.Fatalf("failed to open ephemeral database: %v", err)
- }
- db.Close()
-
- if _, err := os.Stat(filepath.Join(dir, "ephemeral")); err == nil {
- t.Fatalf("ephemeral database exists")
- }
-}
-
-// Tests that already constructed Lifecycles can be retrieved by later ones.
-func TestContextLifecycles(t *testing.T) {
- stack, err := New(testNodeConfig())
- if err != nil {
- t.Fatalf("failed to create protocol stack: %v", err)
- }
- defer stack.Close()
- // Define a verifier that ensures a NoopA is before it and NoopB after
-
- noop := NewNoop()
- stack.RegisterLifecycle(noop)
-
- is, err := NewInstrumentedService()
- if err != nil {
- t.Fatalf("could not create instrumented service %v", err)
-
- }
- is.startHook = func() {
- if err := stack.ServiceContext.Lifecycle(&noop); err != nil {
- t.Errorf("former service not found: %v", err)
- }
- }
- stack.RegisterLifecycle(is)
-
- // Start the protocol stack and ensure services are constructed in order
- if err := stack.Start(); err != nil {
- t.Fatalf("failed to start stack: %v", err)
- }
-
- defer stack.Stop()
-}
diff --git a/p2p/simulations/adapters/exec.go b/p2p/simulations/adapters/exec.go
index 2a6d3aeaaa7e..1f7a21470db0 100644
--- a/p2p/simulations/adapters/exec.go
+++ b/p2p/simulations/adapters/exec.go
@@ -443,9 +443,8 @@ func startExecNodeStack() (*node.Node, error) {
return nil, fmt.Errorf("unknown node service %q", err)
}
ctx := &ServiceContext{
- RPCDialer: &wsRPCDialer{addrs: conf.PeerAddrs},
- NodeContext: stack.ServiceContext,
- Config: conf.Node,
+ RPCDialer: &wsRPCDialer{addrs: conf.PeerAddrs},
+ Config: conf.Node,
}
if conf.Snapshots != nil {
ctx.Snapshot = conf.Snapshots[name]
diff --git a/p2p/simulations/adapters/inproc.go b/p2p/simulations/adapters/inproc.go
index 48c5bd9753f2..c786b9f9ddc1 100644
--- a/p2p/simulations/adapters/inproc.go
+++ b/p2p/simulations/adapters/inproc.go
@@ -250,9 +250,8 @@ func (sn *SimNode) Start(snapshots map[string][]byte) error {
sn.registerOnce.Do(func() {
for _, name := range sn.config.Lifecycles {
ctx := &ServiceContext{
- RPCDialer: sn.adapter,
- NodeContext: sn.node.ServiceContext,
- Config: sn.config,
+ RPCDialer: sn.adapter,
+ Config: sn.config,
}
if snapshots != nil {
ctx.Snapshot = snapshots[name]
diff --git a/p2p/simulations/adapters/types.go b/p2p/simulations/adapters/types.go
index 1ca60f4b9b58..716cde6a6c7d 100644
--- a/p2p/simulations/adapters/types.go
+++ b/p2p/simulations/adapters/types.go
@@ -233,9 +233,8 @@ func assignTCPPort() (uint16, error) {
type ServiceContext struct {
RPCDialer
- NodeContext *node.ServiceContext
- Config *NodeConfig
- Snapshot []byte
+ Config *NodeConfig
+ Snapshot []byte
}
// RPCDialer is used when initialising services which need to connect to
diff --git a/whisper/mailserver/server_test.go b/whisper/mailserver/server_test.go
index 959a1e32f629..069ec97d09b3 100644
--- a/whisper/mailserver/server_test.go
+++ b/whisper/mailserver/server_test.go
@@ -91,10 +91,10 @@ func TestMailServer(t *testing.T) {
var server WMailServer
- stack := newNode(t)
+ stack, w := newNode(t)
defer stack.Close()
+ shh = w
- shh = getWhisperFromNode(stack, t)
shh.RegisterServer(&server)
err = server.Init(shh, dir, password, powRequirement)
@@ -218,12 +218,12 @@ func createRequest(t *testing.T, p *ServerTestParams) *whisper.Envelope {
// newNode creates a new node using a default config and
// creates and registers a new Whisper service on it.
-func newNode(t *testing.T) *node.Node {
+func newNode(t *testing.T) (*node.Node, *whisper.Whisper) {
stack, err := node.New(&node.DefaultConfig)
if err != nil {
t.Fatalf("could not create new node: %v", err)
}
- err = whisper.New(stack, &whisper.DefaultConfig)
+ w, err := whisper.New(stack, &whisper.DefaultConfig)
if err != nil {
t.Fatalf("could not create new whisper service: %v", err)
}
@@ -231,15 +231,5 @@ func newNode(t *testing.T) *node.Node {
if err != nil {
t.Fatalf("could not start node: %v", err)
}
- return stack
-}
-
-// getWhisperFromNode retrieves the Whisper service from the running node.
-func getWhisperFromNode(stack *node.Node, t *testing.T) *whisper.Whisper {
- var w *whisper.Whisper
- err := stack.Lifecycle(&w)
- if err != nil {
- t.Fatalf("could not get whisper service from node: %v", err)
- }
- return w
+ return stack, w
}
diff --git a/whisper/whisperv6/api_test.go b/whisper/whisperv6/api_test.go
index 9aceaf456d6b..759ef221ed8d 100644
--- a/whisper/whisperv6/api_test.go
+++ b/whisper/whisperv6/api_test.go
@@ -23,11 +23,9 @@ import (
)
func TestMultipleTopicCopyInNewMessageFilter(t *testing.T) {
- stack := newNode(t)
+ stack, w := newNodeWithWhisper(t)
defer stack.Close()
- w := getWhisperFromNode(stack, t)
-
keyID, err := w.GenerateSymKey()
if err != nil {
t.Fatalf("Error generating symmetric key: %v", err)
diff --git a/whisper/whisperv6/filter_test.go b/whisper/whisperv6/filter_test.go
index 49c2b7688361..c95e506972dd 100644
--- a/whisper/whisperv6/filter_test.go
+++ b/whisper/whisperv6/filter_test.go
@@ -93,11 +93,9 @@ func TestInstallFilters(t *testing.T) {
const SizeTestFilters = 256
- stack := newNode(t)
+ stack, w := newNodeWithWhisper(t)
defer stack.Close()
- w := getWhisperFromNode(stack, t)
-
filters := NewFilters(w)
tst := generateTestCases(t, SizeTestFilters)
@@ -135,11 +133,9 @@ func TestInstallFilters(t *testing.T) {
func TestInstallSymKeyGeneratesHash(t *testing.T) {
InitSingleTest()
- stack := newNode(t)
+ stack, w := newNodeWithWhisper(t)
defer stack.Close()
- w := getWhisperFromNode(stack, t)
-
filters := NewFilters(w)
filter, _ := generateFilter(t, true)
@@ -166,11 +162,9 @@ func TestInstallSymKeyGeneratesHash(t *testing.T) {
func TestInstallIdenticalFilters(t *testing.T) {
InitSingleTest()
- stack := newNode(t)
+ stack, w := newNodeWithWhisper(t)
defer stack.Close()
- w := getWhisperFromNode(stack, t)
-
filters := NewFilters(w)
filter1, _ := generateFilter(t, true)
@@ -240,11 +234,9 @@ func TestInstallIdenticalFilters(t *testing.T) {
func TestInstallFilterWithSymAndAsymKeys(t *testing.T) {
InitSingleTest()
- stack := newNode(t)
+ stack, w := newNodeWithWhisper(t)
defer stack.Close()
- w := getWhisperFromNode(stack, t)
-
filters := NewFilters(w)
filter1, _ := generateFilter(t, true)
@@ -658,11 +650,9 @@ func TestWatchers(t *testing.T) {
var x, firstID string
var err error
- stack := newNode(t)
+ stack, w := newNodeWithWhisper(t)
defer stack.Close()
- w := getWhisperFromNode(stack, t)
-
filters := NewFilters(w)
tst := generateTestCases(t, NumFilters)
for i = 0; i < NumFilters; i++ {
diff --git a/whisper/whisperv6/whisper.go b/whisper/whisperv6/whisper.go
index 5799919db967..ac610367050c 100644
--- a/whisper/whisperv6/whisper.go
+++ b/whisper/whisperv6/whisper.go
@@ -94,7 +94,7 @@ type Whisper struct {
}
// New creates a Whisper client ready to communicate through the Ethereum P2P network.
-func New(stack *node.Node, cfg *Config) error {
+func New(stack *node.Node, cfg *Config) (*Whisper, error) {
if cfg == nil {
cfg = &DefaultConfig
}
@@ -136,7 +136,7 @@ func New(stack *node.Node, cfg *Config) error {
stack.RegisterAPIs(whisper.APIs())
stack.RegisterProtocols(whisper.Protocols())
stack.RegisterLifecycle(whisper)
- return nil
+ return whisper, nil
}
// MinPow returns the PoW value required by this node.
diff --git a/whisper/whisperv6/whisper_test.go b/whisper/whisperv6/whisper_test.go
index a8056d6776e1..7fb8f7c1cd85 100644
--- a/whisper/whisperv6/whisper_test.go
+++ b/whisper/whisperv6/whisper_test.go
@@ -30,10 +30,9 @@ import (
)
func TestWhisperBasic(t *testing.T) {
- stack := newNode(t)
+ stack, w := newNodeWithWhisper(t)
defer stack.Close()
- // get whisper service from node
- w := getWhisperFromNode(stack, t)
+
shh := w.Protocols()[0]
if shh.Name != ProtocolName {
t.Fatalf("failed Protocol Name: %v.", shh.Name)
@@ -114,11 +113,10 @@ func TestWhisperBasic(t *testing.T) {
}
func TestWhisperAsymmetricKeyImport(t *testing.T) {
- stack := newNode(t)
+ stack, w := newNodeWithWhisper(t)
defer stack.Close()
var privateKeys []*ecdsa.PrivateKey
- w := getWhisperFromNode(stack, t)
for i := 0; i < 50; i++ {
id, err := w.NewKeyPair()
if err != nil {
@@ -145,10 +143,9 @@ func TestWhisperAsymmetricKeyImport(t *testing.T) {
}
func TestWhisperIdentityManagement(t *testing.T) {
- stack := newNode(t)
+ stack, w := newNodeWithWhisper(t)
defer stack.Close()
- w := getWhisperFromNode(stack, t)
id1, err := w.NewKeyPair()
if err != nil {
t.Fatalf("failed to generate new key pair: %s.", err)
@@ -272,11 +269,9 @@ func TestWhisperSymKeyManagement(t *testing.T) {
id2 = string("arbitrary-string-2")
)
- stack := newNode(t)
+ stack, w := newNodeWithWhisper(t)
defer stack.Close()
- w := getWhisperFromNode(stack, t)
-
id1, err := w.GenerateSymKey()
if err != nil {
t.Fatalf("failed GenerateSymKey with seed %d: %s.", seed, err)
@@ -461,11 +456,9 @@ func TestWhisperSymKeyManagement(t *testing.T) {
func TestExpiry(t *testing.T) {
InitSingleTest()
- stack := newNode(t)
+ stack, w := newNodeWithWhisper(t)
defer stack.Close()
- w := getWhisperFromNode(stack, t)
-
w.SetMinimumPowTest(0.0000001)
defer w.SetMinimumPowTest(DefaultMinimumPoW)
w.Start()
@@ -530,11 +523,9 @@ func TestExpiry(t *testing.T) {
func TestCustomization(t *testing.T) {
InitSingleTest()
- stack := newNode(t)
+ stack, w := newNodeWithWhisper(t)
defer stack.Close()
- w := getWhisperFromNode(stack, t)
-
defer w.SetMinimumPowTest(DefaultMinimumPoW)
defer w.SetMaxMessageSize(DefaultMaxMessageSize)
w.Start()
@@ -626,11 +617,9 @@ func TestCustomization(t *testing.T) {
func TestSymmetricSendCycle(t *testing.T) {
InitSingleTest()
- stack := newNode(t)
+ stack, w := newNodeWithWhisper(t)
defer stack.Close()
- w := getWhisperFromNode(stack, t)
-
defer w.SetMinimumPowTest(DefaultMinimumPoW)
defer w.SetMaxMessageSize(DefaultMaxMessageSize)
w.Start()
@@ -720,11 +709,9 @@ func TestSymmetricSendCycle(t *testing.T) {
func TestSymmetricSendWithoutAKey(t *testing.T) {
InitSingleTest()
- stack := newNode(t)
+ stack, w := newNodeWithWhisper(t)
defer stack.Close()
- w := getWhisperFromNode(stack, t)
-
defer w.SetMinimumPowTest(DefaultMinimumPoW)
defer w.SetMaxMessageSize(DefaultMaxMessageSize)
w.Start()
@@ -793,11 +780,9 @@ func TestSymmetricSendWithoutAKey(t *testing.T) {
func TestSymmetricSendKeyMismatch(t *testing.T) {
InitSingleTest()
- stack := newNode(t)
+ stack, w := newNodeWithWhisper(t)
defer stack.Close()
- w := getWhisperFromNode(stack, t)
-
defer w.SetMinimumPowTest(DefaultMinimumPoW)
defer w.SetMaxMessageSize(DefaultMaxMessageSize)
w.Start()
@@ -907,11 +892,9 @@ func TestBloom(t *testing.T) {
t.Fatal("bloomFilterMatch false negative")
}
- stack := newNode(t)
+ stack, w := newNodeWithWhisper(t)
defer stack.Close()
- w := getWhisperFromNode(stack, t)
-
f := w.BloomFilter()
if f != nil {
t.Fatal("wrong bloom on creation")
@@ -926,14 +909,14 @@ func TestBloom(t *testing.T) {
}
}
-// newNode creates a new node using a default config and
+// newNodeWithWhisper creates a new node using a default config and
// creates and registers a new Whisper service on it.
-func newNode(t *testing.T) *node.Node {
+func newNodeWithWhisper(t *testing.T) (*node.Node, *Whisper) {
stack, err := node.New(&node.DefaultConfig)
if err != nil {
t.Fatalf("could not create new node: %v", err)
}
- err = New(stack, &DefaultConfig)
+ w, err := New(stack, &DefaultConfig)
if err != nil {
t.Fatalf("could not create new whisper service: %v", err)
}
@@ -941,15 +924,5 @@ func newNode(t *testing.T) *node.Node {
if err != nil {
t.Fatalf("could not start node: %v", err)
}
- return stack
-}
-
-// getWhisperFromNode retrieves the Whisper service from the running node.
-func getWhisperFromNode(stack *node.Node, t *testing.T) *Whisper {
- var w *Whisper
- err := stack.Lifecycle(&w)
- if err != nil {
- t.Fatalf("could not get whisper service from node: %v", err)
- }
- return w
+ return stack, w
}