nimbus-eth1/nimbus/nimbus.nim

201 lines
6.6 KiB
Nim
Raw Normal View History

2018-04-27 08:53:53 +00:00
# Nimbus
# Copyright (c) 2018 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
import
2019-04-17 01:56:28 +00:00
os, strutils, net, options,
eth/keys, db/[storage_types, db_chain, select_backend],
2019-02-05 19:15:50 +00:00
eth/common as eth_common, eth/p2p as eth_p2p,
chronos, json_rpc/rpcserver, chronicles,
eth/p2p/rlpx_protocols/[eth_protocol, les_protocol, whisper_protocol],
eth/p2p/blockchain_sync, eth/net/nat, eth/p2p/peer_pool,
2020-01-20 16:45:49 +00:00
config, genesis, rpc/[common, p2p, debug, whisper, key_storage], p2p/chain,
eth/trie/db, metrics, metrics/chronicles_support
2018-06-20 17:27:32 +00:00
## TODO:
## * No IPv6 support
## * No multiple bind addresses support
## * No database support
const
nimbusClientId = "nimbus 0.1.0"
2018-06-20 17:27:32 +00:00
type
NimbusState = enum
2020-05-21 01:33:11 +00:00
Starting, Running, Stopping
2018-06-20 17:27:32 +00:00
2020-03-25 18:00:04 +00:00
NimbusNode = ref object
rpcServer*: RpcHttpServer
ethNode*: EthereumNode
2018-06-20 17:27:32 +00:00
state*: NimbusState
2020-03-25 18:00:04 +00:00
proc start(nimbus: NimbusNode) =
2018-06-20 17:27:32 +00:00
var conf = getConfiguration()
## logging
setLogLevel(conf.debug.logLevel)
if len(conf.debug.logFile) != 0:
defaultChroniclesStream.output.outFile = nil # to avoid closing stdout
discard defaultChroniclesStream.output.open(conf.debug.logFile, fmAppend)
# metrics logging
if conf.debug.logMetrics:
proc logMetrics(udata: pointer) {.closure, gcsafe.} =
{.gcsafe.}:
let registry = defaultRegistry
info "metrics", registry
addTimer(Moment.fromNow(conf.debug.logMetricsInterval.seconds), logMetrics)
addTimer(Moment.fromNow(conf.debug.logMetricsInterval.seconds), logMetrics)
2018-06-20 17:27:32 +00:00
## Creating RPC Server
if RpcFlags.Enabled in conf.rpc.flags:
nimbus.rpcServer = newRpcHttpServer(conf.rpc.binds)
2018-06-20 17:27:32 +00:00
setupCommonRpc(nimbus.rpcServer)
## Creating P2P Server
let keypair = conf.net.nodekey.toKeyPair()
2018-06-20 17:27:32 +00:00
var address: Address
address.ip = parseIpAddress("0.0.0.0")
address.tcpPort = Port(conf.net.bindPort)
address.udpPort = Port(conf.net.discPort)
2019-04-17 01:56:28 +00:00
if conf.net.nat == NatNone:
if conf.net.externalIP != "":
# any required port redirection is assumed to be done by hand
address.ip = parseIpAddress(conf.net.externalIP)
else:
# automated NAT traversal
let extIP = getExternalIP(conf.net.nat)
2019-04-17 23:17:06 +00:00
# This external IP only appears in the logs, so don't worry about dynamic
# IPs. Don't remove it either, because the above call does initialisation
# and discovery for NAT-related objects.
2019-04-17 01:56:28 +00:00
if extIP.isSome:
address.ip = extIP.get()
let extPorts = redirectPorts(tcpPort = address.tcpPort,
udpPort = address.udpPort,
description = NIMBUS_NAME & " " & NIMBUS_VERSION)
if extPorts.isSome:
(address.tcpPort, address.udpPort) = extPorts.get()
2018-06-20 17:27:32 +00:00
createDir(conf.dataDir)
let trieDB = trieDB newChainDb(conf.dataDir)
2020-01-13 18:35:40 +00:00
var chainDB = newBaseChainDB(trieDB,
2019-04-06 14:50:43 +00:00
conf.prune == PruneMode.Full,
conf.net.networkId.toPublicNetwork())
if canonicalHeadHashKey().toOpenArray notin trieDB:
initializeEmptyDb(chainDb)
2019-03-13 21:36:54 +00:00
doAssert(canonicalHeadHashKey().toOpenArray in trieDB)
2020-03-25 18:00:04 +00:00
nimbus.ethNode = newEthereumNode(keypair, address, conf.net.networkId,
nil, nimbusClientId,
addAllCapabilities = false,
minPeers = conf.net.maxPeers)
# Add protocol capabilities based on protocol flags
if ProtocolFlags.Eth in conf.net.protocols:
nimbus.ethNode.addCapability eth
if ProtocolFlags.Shh in conf.net.protocols:
nimbus.ethNode.addCapability Whisper
nimbus.ethNode.configureWhisper(conf.shh)
if ProtocolFlags.Les in conf.net.protocols:
nimbus.ethNode.addCapability les
2018-06-20 17:27:32 +00:00
2018-08-29 08:49:01 +00:00
nimbus.ethNode.chain = newChain(chainDB)
# Enable RPC APIs based on RPC flags and protocol flags
if RpcFlags.Eth in conf.rpc.flags and ProtocolFlags.Eth in conf.net.protocols:
2018-08-29 08:49:01 +00:00
setupEthRpc(nimbus.ethNode, chainDB, nimbus.rpcServer)
if RpcFlags.Shh in conf.rpc.flags and ProtocolFlags.Shh in conf.net.protocols:
2020-01-20 16:45:49 +00:00
let keys = newKeyStorage()
2019-03-23 20:54:28 +00:00
setupWhisperRPC(nimbus.ethNode, keys, nimbus.rpcServer)
if RpcFlags.Debug in conf.rpc.flags:
setupDebugRpc(chainDB, nimbus.rpcServer)
2018-06-20 17:27:32 +00:00
## Starting servers
if RpcFlags.Enabled in conf.rpc.flags:
nimbus.rpcServer.rpc("admin_quit") do() -> string:
{.gcsafe.}:
nimbus.state = Stopping
2018-06-20 17:27:32 +00:00
result = "EXITING"
nimbus.rpcServer.start()
2020-03-25 18:00:04 +00:00
# metrics server
when defined(insecure):
if conf.net.metricsServer:
let metricsAddress = "127.0.0.1"
info "Starting metrics HTTP server", address = metricsAddress, port = conf.net.metricsServerPort
metrics.startHttpServer(metricsAddress, Port(conf.net.metricsServerPort))
# Connect directly to the static nodes
for enode in conf.net.staticNodes:
asyncCheck nimbus.ethNode.peerPool.connectToNode(newNode(enode))
# Connect via discovery
2018-09-25 13:30:44 +00:00
waitFor nimbus.ethNode.connectToNetwork(conf.net.bootNodes,
enableDiscovery = NoDiscover notin conf.net.flags)
if ProtocolFlags.Eth in conf.net.protocols:
# TODO: temp code until the CLI/RPC interface is fleshed out
let status = waitFor nimbus.ethNode.fastBlockchainSync()
if status != syncSuccess:
debug "Block sync failed: ", status
if nimbus.state == Starting:
# it might have been set to "Stopping" with Ctrl+C
nimbus.state = Running
2018-06-20 17:27:32 +00:00
2020-03-25 18:00:04 +00:00
proc stop*(nimbus: NimbusNode) {.async, gcsafe.} =
trace "Graceful shutdown"
var conf = getConfiguration()
if RpcFlags.Enabled in conf.rpc.flags:
nimbus.rpcServer.stop()
2018-06-20 17:27:32 +00:00
2020-03-25 18:00:04 +00:00
proc process*(nimbus: NimbusNode) =
2020-05-21 01:33:11 +00:00
# Main event loop
while nimbus.state == Running:
try:
poll()
except CatchableError as e:
debug "Exception in poll()", exc = e.name, err = e.msg
2018-06-20 17:27:32 +00:00
# Stop loop
2020-03-25 18:00:04 +00:00
waitFor nimbus.stop()
2018-04-27 08:53:53 +00:00
when isMainModule:
2020-05-21 01:33:11 +00:00
var nimbus = NimbusNode(state: Starting)
2020-03-25 18:00:04 +00:00
## Ctrl+C handling
proc controlCHandler() {.noconv.} =
when defined(windows):
# workaround for https://github.com/nim-lang/Nim/issues/4057
setupForeignThreadGc()
nimbus.state = Stopping
echo "\nCtrl+C pressed. Waiting for a graceful shutdown."
setControlCHook(controlCHandler)
2018-04-27 08:53:53 +00:00
var message: string
2018-06-20 17:27:32 +00:00
## Print Nimbus header
2018-06-20 17:27:32 +00:00
echo NimbusHeader
## Show logs on stdout until we get the user's logging choice
2019-04-17 01:56:28 +00:00
discard defaultChroniclesStream.output.open(stdout)
2018-06-20 17:27:32 +00:00
## Processing command line arguments
if processArguments(message) != ConfigStatus.Success:
2018-04-27 08:53:53 +00:00
echo message
quit(QuitFailure)
else:
if len(message) > 0:
echo message
2018-06-20 17:27:32 +00:00
quit(QuitSuccess)
2018-04-27 08:53:53 +00:00
2020-03-25 18:00:04 +00:00
nimbus.start()
nimbus.process()