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.
|
|
|
|
|
2018-07-22 21:34:43 +00:00
|
|
|
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,
|
2019-02-06 18:03:36 +00:00
|
|
|
chronos, json_rpc/rpcserver, chronicles,
|
2019-04-23 13:49:49 +00:00
|
|
|
eth/p2p/rlpx_protocols/[eth_protocol, les_protocol, whisper_protocol],
|
|
|
|
eth/p2p/blockchain_sync, eth/net/nat, eth/p2p/peer_pool,
|
2018-11-22 06:40:09 +00:00
|
|
|
config, genesis, rpc/[common, p2p, debug, whisper], p2p/chain,
|
2019-07-16 10:43:05 +00:00
|
|
|
eth/trie/db, metrics, metrics/chronicles_support
|
2018-07-26 20:08:43 +00:00
|
|
|
|
2018-06-20 17:27:32 +00:00
|
|
|
## TODO:
|
|
|
|
## * No IPv6 support
|
|
|
|
## * No multiple bind addresses support
|
|
|
|
## * No database support
|
|
|
|
|
2018-07-12 11:14:04 +00:00
|
|
|
const
|
|
|
|
nimbusClientId = "nimbus 0.1.0"
|
|
|
|
|
2018-06-20 17:27:32 +00:00
|
|
|
when not defined(windows):
|
|
|
|
from posix import SIGINT, SIGTERM
|
|
|
|
|
|
|
|
type
|
|
|
|
NimbusState = enum
|
|
|
|
Starting, Running, Stopping, Stopped
|
|
|
|
|
|
|
|
NimbusObject = ref object
|
2018-07-20 17:02:19 +00:00
|
|
|
rpcServer*: RpcHttpServer
|
2018-07-12 11:14:04 +00:00
|
|
|
ethNode*: EthereumNode
|
2018-06-20 17:27:32 +00:00
|
|
|
state*: NimbusState
|
|
|
|
|
2019-07-10 21:23:11 +00:00
|
|
|
var nimbus: NimbusObject
|
|
|
|
|
|
|
|
proc start() =
|
2018-06-20 17:27:32 +00:00
|
|
|
var conf = getConfiguration()
|
|
|
|
|
2019-07-10 21:23:11 +00:00
|
|
|
nimbus = NimbusObject()
|
|
|
|
nimbus.state = Starting
|
|
|
|
|
|
|
|
## Ctrl+C handling
|
2019-07-10 22:15:05 +00:00
|
|
|
proc controlCHandler() {.noconv.} =
|
|
|
|
when defined(windows):
|
|
|
|
# workaround for https://github.com/nim-lang/Nim/issues/4057
|
|
|
|
setupForeignThreadGc()
|
2019-07-10 21:23:11 +00:00
|
|
|
nimbus.state = Stopping
|
2019-07-19 23:40:31 +00:00
|
|
|
echo "\nCtrl+C pressed. Waiting for a graceful shutdown."
|
2019-07-10 22:15:05 +00:00
|
|
|
setControlCHook(controlCHandler)
|
2019-07-10 21:23:11 +00:00
|
|
|
|
2019-05-08 00:25:04 +00:00
|
|
|
## logging
|
2019-03-20 13:41:25 +00:00
|
|
|
setLogLevel(conf.debug.logLevel)
|
|
|
|
if len(conf.debug.logFile) != 0:
|
2019-05-08 00:25:04 +00:00
|
|
|
defaultChroniclesStream.output.outFile = nil # to avoid closing stdout
|
2019-03-20 13:41:25 +00:00
|
|
|
discard defaultChroniclesStream.output.open(conf.debug.logFile, fmAppend)
|
|
|
|
|
2019-07-16 10:43:05 +00:00
|
|
|
# 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:
|
2018-07-20 17:02:19 +00:00
|
|
|
nimbus.rpcServer = newRpcHttpServer(conf.rpc.binds)
|
2018-06-20 17:27:32 +00:00
|
|
|
setupCommonRpc(nimbus.rpcServer)
|
|
|
|
|
|
|
|
## Creating P2P Server
|
|
|
|
if conf.net.nodekey.isZeroKey():
|
|
|
|
conf.net.nodekey = newPrivateKey()
|
|
|
|
|
|
|
|
var keypair: KeyPair
|
|
|
|
keypair.seckey = conf.net.nodekey
|
|
|
|
keypair.pubkey = conf.net.nodekey.getPublicKey()
|
|
|
|
|
|
|
|
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
|
|
|
|
2018-09-24 22:25:17 +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())
|
2018-07-26 20:08:43 +00:00
|
|
|
|
|
|
|
if canonicalHeadHashKey().toOpenArray notin trieDB:
|
|
|
|
initializeEmptyDb(chainDb)
|
2019-03-13 21:36:54 +00:00
|
|
|
doAssert(canonicalHeadHashKey().toOpenArray in trieDB)
|
2020-01-13 18:35:40 +00:00
|
|
|
|
2018-07-12 11:14:04 +00:00
|
|
|
nimbus.ethNode = newEthereumNode(keypair, address, conf.net.networkId,
|
2019-04-23 13:49:49 +00:00
|
|
|
nil, nimbusClientId,
|
2019-06-05 13:18:26 +00:00
|
|
|
addAllCapabilities = false,
|
|
|
|
minPeers = conf.net.maxPeers)
|
2019-04-23 13:49:49 +00:00
|
|
|
# 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)
|
2018-07-26 20:08:43 +00:00
|
|
|
|
2019-04-23 13:49:49 +00:00
|
|
|
# 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)
|
2019-04-23 13:49:49 +00:00
|
|
|
if RpcFlags.Shh in conf.rpc.flags and ProtocolFlags.Shh in conf.net.protocols:
|
2019-03-23 20:54:28 +00:00
|
|
|
let keys = newWhisperKeys()
|
|
|
|
setupWhisperRPC(nimbus.ethNode, keys, nimbus.rpcServer)
|
2018-11-22 06:40:09 +00:00
|
|
|
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:
|
2019-07-10 21:23:11 +00:00
|
|
|
{.gcsafe.}:
|
|
|
|
nimbus.state = Stopping
|
2018-06-20 17:27:32 +00:00
|
|
|
result = "EXITING"
|
|
|
|
nimbus.rpcServer.start()
|
2020-01-13 18:35:40 +00:00
|
|
|
|
2019-07-16 10:43:05 +00:00
|
|
|
# metrics server
|
2019-07-17 22:50:16 +00:00
|
|
|
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))
|
2019-06-26 12:51:59 +00:00
|
|
|
|
2019-04-23 13:49:49 +00:00
|
|
|
# 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)
|
2018-07-22 21:34:43 +00:00
|
|
|
|
2019-04-23 13:49:49 +00:00
|
|
|
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
|
2018-07-12 11:14:04 +00:00
|
|
|
|
2019-07-10 21:23:11 +00:00
|
|
|
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
|
|
|
|
2019-07-10 21:23:11 +00:00
|
|
|
proc stop*() {.async.} =
|
2018-12-06 23:16:34 +00:00
|
|
|
trace "Graceful shutdown"
|
2019-07-10 21:23:11 +00:00
|
|
|
var conf = getConfiguration()
|
|
|
|
if RpcFlags.Enabled in conf.rpc.flags:
|
|
|
|
nimbus.rpcServer.stop()
|
2018-06-20 17:27:32 +00:00
|
|
|
|
2019-07-10 21:23:11 +00:00
|
|
|
proc process*() =
|
2018-06-20 17:27:32 +00:00
|
|
|
if nimbus.state == Running:
|
|
|
|
# Main loop
|
|
|
|
while nimbus.state == Running:
|
2019-05-28 10:49:36 +00:00
|
|
|
try:
|
|
|
|
poll()
|
2019-12-04 12:36:16 +00:00
|
|
|
except CatchableError as e:
|
|
|
|
debug "Exception in poll()", exc = e.name, err = e.msg
|
2018-06-20 17:27:32 +00:00
|
|
|
|
2019-07-10 21:23:11 +00:00
|
|
|
# Stop loop
|
|
|
|
waitFor stop()
|
2018-04-27 08:53:53 +00:00
|
|
|
|
|
|
|
when isMainModule:
|
|
|
|
var message: string
|
2018-06-20 17:27:32 +00:00
|
|
|
|
2019-07-10 21:23:11 +00:00
|
|
|
## Print Nimbus header
|
2018-06-20 17:27:32 +00:00
|
|
|
echo NimbusHeader
|
|
|
|
|
2019-07-10 21:23:11 +00:00
|
|
|
## 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
|
2018-05-02 15:01:10 +00:00
|
|
|
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
|
|
|
|
2019-07-10 21:23:11 +00:00
|
|
|
start()
|
|
|
|
process()
|
|
|
|
|