status-go/geth/node/node.go

251 lines
7.4 KiB
Go
Raw Normal View History

package node
import (
"encoding/json"
"errors"
"fmt"
"os"
"path"
"path/filepath"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/les"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
2017-02-20 10:44:38 +01:00
"github.com/ethereum/go-ethereum/p2p/discover"
2018-04-10 08:44:09 +02:00
"github.com/ethereum/go-ethereum/p2p/discv5"
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/whisper/mailserver"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
"github.com/status-im/status-go/geth/mailservice"
"github.com/status-im/status-go/geth/params"
shhmetrics "github.com/status-im/status-go/metrics/whisper"
"github.com/status-im/status-go/shhext"
)
// Errors related to node and services creation.
var (
ErrNodeMakeFailure = errors.New("error creating p2p node")
ErrWhisperServiceRegistrationFailure = errors.New("failed to register the Whisper service")
ErrLightEthRegistrationFailure = errors.New("failed to register the LES service")
)
// All general log messages in this package should be routed through this logger.
var logger = log.New("package", "status-go/geth/node")
// MakeNode create a geth node entity
2018-01-19 15:53:16 +01:00
func MakeNode(config *params.NodeConfig) (*node.Node, error) {
// If DataDir is empty, it means we want to create an ephemeral node
// keeping data only in memory.
if config.DataDir != "" {
// make sure data directory exists
if err := os.MkdirAll(filepath.Clean(config.DataDir), os.ModePerm); err != nil {
return nil, fmt.Errorf("make node: make data directory: %v", err)
}
// make sure keys directory exists
if err := os.MkdirAll(filepath.Clean(config.KeyStoreDir), os.ModePerm); err != nil {
return nil, fmt.Errorf("make node: make keys directory: %v", err)
}
}
stackConfig := defaultEmbeddedNodeConfig(config)
if len(config.NodeKeyFile) > 0 {
logger.Info("Loading private key file", "file", config.NodeKeyFile)
pk, err := crypto.LoadECDSA(config.NodeKeyFile)
if err != nil {
logger.Error("Failed loading private key file", "file", config.NodeKeyFile, "error", err)
}
// override node's private key
stackConfig.P2P.PrivateKey = pk
}
2017-01-26 01:39:20 +01:00
stack, err := node.New(stackConfig)
if err != nil {
return nil, ErrNodeMakeFailure
}
// start Ethereum service if we are not expected to use an upstream server
if !config.UpstreamConfig.Enabled {
if err := activateLightEthService(stack, config); err != nil {
return nil, fmt.Errorf("%v: %v", ErrLightEthRegistrationFailure, err)
}
}
// start Whisper service.
2018-01-19 15:53:16 +01:00
if err := activateShhService(stack, config); err != nil {
return nil, fmt.Errorf("%v: %v", ErrWhisperServiceRegistrationFailure, err)
}
return stack, nil
}
// defaultEmbeddedNodeConfig returns default stack configuration for mobile client node
func defaultEmbeddedNodeConfig(config *params.NodeConfig) *node.Config {
nc := &node.Config{
DataDir: config.DataDir,
KeyStoreDir: config.KeyStoreDir,
UseLightweightKDF: true,
NoUSB: true,
Name: config.Name,
Version: config.Version,
P2P: p2p.Config{
2018-04-10 08:44:09 +02:00
NoDiscovery: true,
DiscoveryV5: config.Discovery,
ListenAddr: config.ListenAddr,
NAT: nat.Any(),
MaxPeers: config.MaxPeers,
MaxPendingPeers: config.MaxPendingPeers,
},
IPCPath: makeIPCPath(config),
HTTPCors: nil,
HTTPModules: config.FormatAPIModules(),
HTTPVirtualHosts: []string{"localhost"},
}
if config.RPCEnabled {
nc.HTTPHost = config.HTTPHost
nc.HTTPPort = config.HTTPPort
}
if config.ClusterConfig != nil && config.ClusterConfig.Enabled {
nc.P2P.StaticNodes = parseNodes(config.ClusterConfig.StaticNodes)
2018-04-10 08:44:09 +02:00
nc.P2P.BootstrapNodesV5 = parseNodesV5(config.ClusterConfig.BootNodes)
2017-12-07 13:07:45 +01:00
}
return nc
}
// activateLightEthService configures and registers the eth.Ethereum service with a given node.
func activateLightEthService(stack *node.Node, config *params.NodeConfig) error {
if config.LightEthConfig == nil || !config.LightEthConfig.Enabled {
logger.Info("LES protocol is disabled")
return nil
}
var genesis *core.Genesis
if config.LightEthConfig.Genesis != "" {
genesis = new(core.Genesis)
if err := json.Unmarshal([]byte(config.LightEthConfig.Genesis), genesis); err != nil {
return fmt.Errorf("invalid genesis spec: %v", err)
}
}
ethConf := eth.DefaultConfig
ethConf.Genesis = genesis
ethConf.SyncMode = downloader.LightSync
ethConf.NetworkId = config.NetworkID
ethConf.DatabaseCache = config.LightEthConfig.DatabaseCache
return stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
return les.New(ctx, &ethConf)
})
}
// activateShhService configures Whisper and adds it to the given node.
func activateShhService(stack *node.Node, config *params.NodeConfig) (err error) {
if config.WhisperConfig == nil || !config.WhisperConfig.Enabled {
logger.Info("SHH protocol is disabled")
return nil
}
err = stack.Register(func(*node.ServiceContext) (node.Service, error) {
whisperServiceConfig := &whisper.Config{
MaxMessageSize: whisper.DefaultMaxMessageSize,
MinimumAcceptedPOW: 0.001,
}
whisperService := whisper.New(whisperServiceConfig)
// enable metrics
whisperService.RegisterEnvelopeTracer(&shhmetrics.EnvelopeTracer{})
// enable mail service
if config.WhisperConfig.EnableMailServer {
if config.WhisperConfig.Password == "" {
if err := config.WhisperConfig.ReadPasswordFile(); err != nil {
return nil, err
}
}
logger.Info("Register MailServer")
var mailServer mailserver.WMailServer
whisperService.RegisterServer(&mailServer)
mailServer.Init(
whisperService,
config.WhisperConfig.DataDir,
config.WhisperConfig.Password,
config.WhisperConfig.MinimumPoW,
)
}
if config.WhisperConfig.LightClient {
emptyBloomFilter := make([]byte, 64)
if err := whisperService.SetBloomFilter(emptyBloomFilter); err != nil {
return nil, err
}
}
return whisperService, nil
})
if err != nil {
return
}
// TODO(dshulyak) add a config option to enable it by default, but disable if app is started from statusd
err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
var whisper *whisper.Whisper
if err := ctx.Service(&whisper); err != nil {
return nil, err
}
svc := shhext.New(whisper, shhext.EnvelopeSignalHandler{})
return svc, nil
})
if err != nil {
return
}
return stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
var whisper *whisper.Whisper
if err := ctx.Service(&whisper); err != nil {
return nil, err
}
return mailservice.New(whisper), nil
})
}
// makeIPCPath returns IPC-RPC filename
func makeIPCPath(config *params.NodeConfig) string {
if !config.IPCEnabled {
return ""
}
return path.Join(config.DataDir, config.IPCFile)
}
// parseNodes creates list of discover.Node out of enode strings.
func parseNodes(enodes []string) []*discover.Node {
nodes := make([]*discover.Node, len(enodes))
for i, enode := range enodes {
nodes[i] = discover.MustParseNode(enode)
2017-02-20 10:44:38 +01:00
}
return nodes
2017-02-20 10:44:38 +01:00
}
2018-04-10 08:44:09 +02:00
// parseNodesV5 creates list of discv5.Node out of enode strings.
func parseNodesV5(enodes []string) []*discv5.Node {
nodes := make([]*discv5.Node, len(enodes))
for i, enode := range enodes {
nodes[i] = discv5.MustParseNode(enode)
}
return nodes
}