package node import ( "errors" "fmt" "os" "path/filepath" "github.com/syndtr/goleveldb/leveldb" "go.uber.org/zap" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discv5" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/nat" "github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/logutils" "github.com/status-im/status-go/params" ) // Errors related to node and services creation. var ( ErrNodeMakeFailureFormat = "error creating p2p node: %s" ErrWakuServiceRegistrationFailure = errors.New("failed to register the Waku service") ErrWakuV2ServiceRegistrationFailure = errors.New("failed to register the WakuV2 service") ErrLightEthRegistrationFailure = errors.New("failed to register the LES service") ErrLightEthRegistrationFailureUpstreamEnabled = errors.New("failed to register the LES service, upstream is also configured") ErrPersonalServiceRegistrationFailure = errors.New("failed to register the personal api service") ErrStatusServiceRegistrationFailure = errors.New("failed to register the Status service") ErrPeerServiceRegistrationFailure = errors.New("failed to register the Peer service") ) // All general log messages in this package should be routed through this logger. var logger = logutils.ZapLogger().Named("node") // MakeNode creates a geth node entity func MakeNode(config *params.NodeConfig, accs *accounts.Manager, db *leveldb.DB) (*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, err := newGethNodeConfig(config) if err != nil { return nil, err } stack, err := node.New(stackConfig) if err != nil { return nil, fmt.Errorf(ErrNodeMakeFailureFormat, err.Error()) } return stack, nil } // newGethNodeConfig returns default stack configuration for mobile client node func newGethNodeConfig(config *params.NodeConfig) (*node.Config, error) { // NOTE: I haven't changed anything related to this parameters, but // it seems they were previously ignored if set to 0, but now they seem // to be used, so they need to be set to something maxPeers := 100 maxPendingPeers := 100 if config.MaxPeers != 0 { maxPeers = config.MaxPeers } if config.MaxPendingPeers != 0 { maxPendingPeers = config.MaxPendingPeers } nc := &node.Config{ DataDir: config.DataDir, KeyStoreDir: config.KeyStoreDir, UseLightweightKDF: true, NoUSB: true, Name: config.Name, Version: config.Version, P2P: p2p.Config{ NoDiscovery: true, // we always use only v5 server ListenAddr: config.ListenAddr, NAT: nat.Any(), MaxPeers: maxPeers, MaxPendingPeers: maxPendingPeers, }, } if config.IPCEnabled { // use well-known defaults if config.IPCFile == "" { config.IPCFile = "geth.ipc" } nc.IPCPath = config.IPCFile } if config.HTTPEnabled { nc.HTTPModules = config.FormatAPIModules() nc.HTTPHost = config.HTTPHost nc.HTTPPort = config.HTTPPort nc.HTTPVirtualHosts = config.HTTPVirtualHosts nc.HTTPCors = config.HTTPCors } if config.WSEnabled { nc.WSModules = config.FormatAPIModules() nc.WSHost = config.WSHost nc.WSPort = config.WSPort // FIXME: this is a temporary solution to allow all origins nc.WSOrigins = []string{"*"} } if config.ClusterConfig.Enabled { nc.P2P.BootstrapNodesV5 = parseNodesV5(config.ClusterConfig.BootNodes) nc.P2P.StaticNodes = parseNodes(config.ClusterConfig.StaticNodes) } if config.NodeKey != "" { sk, err := crypto.HexToECDSA(config.NodeKey) if err != nil { return nil, err } // override node's private key nc.P2P.PrivateKey = sk } return nc, nil } // parseNodes creates list of enode.Node out of enode strings. func parseNodes(enodes []string) []*enode.Node { var nodes []*enode.Node for _, item := range enodes { parsedPeer, err := enode.ParseV4(item) if err == nil { nodes = append(nodes, parsedPeer) } else { logger.Error("Failed to parse enode", zap.String("enode", item), zap.Error(err)) } } return nodes } // parseNodesV5 creates list of discv5.Node out of enode strings. func parseNodesV5(enodes []string) []*discv5.Node { var nodes []*discv5.Node for _, enode := range enodes { parsedPeer, err := discv5.ParseNode(enode) if err == nil { nodes = append(nodes, parsedPeer) } else { logger.Error("Failed to parse enode", zap.String("enode", enode), zap.Error(err)) } } return nodes } func parseNodesToNodeID(enodes []string) []enode.ID { nodeIDs := make([]enode.ID, 0, len(enodes)) for _, node := range parseNodes(enodes) { nodeIDs = append(nodeIDs, node.ID()) } return nodeIDs }