2020-04-21 05:29:56 +00:00
import
confutils , config , strutils , chronos , json_rpc / rpcserver , metrics ,
chronicles / topics_registry , # TODO: What? Need this for setLoglevel, weird.
eth / [ keys , p2p , async_utils ] , eth / common / utils , eth / net / nat ,
eth / p2p / [ discovery , enode , peer_pool , bootnodes , whispernodes ] ,
eth / p2p / rlpx_protocols / [ whisper_protocol , waku_protocol , waku_bridge ] ,
2020-04-23 03:33:42 +00:00
.. / vendor / nimbus / nimbus / rpc / [ waku , wakusim , key_storage ] ,
.. / vendor / nim - libp2p / libp2p / standard_setup ,
2020-04-27 04:23:01 +00:00
.. / vendor / nim - libp2p / libp2p / crypto / crypto ,
.. / vendor / nim - libp2p / libp2p / protocols / protocol
2020-04-23 03:33:42 +00:00
# TODO: Better aliasing of vendor dirs
# TODO: Something wrong with module imports, "invalid module name" with this
# ../vendor/nim-libp2p/libp2p/[switch, standard_setup, peerinfo, peer, connection,
# multiaddress, multicodec, crypto/crypto, protocols/identify, protocols/protocol]
# key and crypto modules different
type
KeyPair * = crypto . KeyPair
PublicKey * = crypto . PublicKey
PrivateKey * = crypto . PrivateKey
2020-04-21 05:29:56 +00:00
2020-04-27 04:23:01 +00:00
# handler defined in parent object
type WakuProto = ref object of LPProtocol
switch : Switch
conn : Connection
connected : bool
started : bool
2020-04-21 05:29:56 +00:00
const clientId = " Nimbus waku node "
2020-04-27 04:23:01 +00:00
const WakuCodec = " /vac/waku/2.0.0-alpha0 "
2020-04-21 05:29:56 +00:00
let globalListeningAddr = parseIpAddress ( " 0.0.0.0 " )
proc setBootNodes ( nodes : openArray [ string ] ) : seq [ ENode ] =
result = newSeqOfCap [ ENode ] ( nodes . len )
for nodeId in nodes :
# TODO: something more user friendly than an expect
result . add ( ENode . fromString ( nodeId ) . expect ( " correct node " ) )
proc connectToNodes ( node : EthereumNode , nodes : openArray [ string ] ) =
for nodeId in nodes :
# TODO: something more user friendly than an assert
let whisperENode = ENode . fromString ( nodeId ) . expect ( " correct node " )
traceAsyncErrors node . peerPool . connectToNode ( newNode ( whisperENode ) )
2020-04-23 03:33:42 +00:00
# NOTE: Looks almost identical to beacon_chain/eth2_network.nim
2020-04-21 05:29:56 +00:00
proc setupNat ( conf : WakuNodeConf ) : tuple [ ip : IpAddress ,
tcpPort : Port ,
udpPort : Port ] =
# defaults
result . ip = globalListeningAddr
result . tcpPort = Port ( conf . tcpPort + conf . portsShift )
result . udpPort = Port ( conf . udpPort + conf . portsShift )
var nat : NatStrategy
case conf . nat . toLowerAscii ( ) :
of " any " :
nat = NatAny
of " none " :
nat = NatNone
of " upnp " :
nat = NatUpnp
of " pmp " :
nat = NatPmp
else :
if conf . nat . startsWith ( " extip: " ) and isIpAddress ( conf . nat [ 6 .. ^ 1 ] ) :
# any required port redirection is assumed to be done by hand
result . ip = parseIpAddress ( conf . nat [ 6 .. ^ 1 ] )
nat = NatNone
else :
error " not a valid NAT mechanism, nor a valid IP address " , value = conf . nat
quit ( QuitFailure )
if nat ! = NatNone :
let extIP = getExternalIP ( nat )
if extIP . isSome :
result . ip = extIP . get ( )
let extPorts = redirectPorts ( tcpPort = result . tcpPort ,
udpPort = result . udpPort ,
description = clientId )
if extPorts . isSome :
( result . tcpPort , result . udpPort ) = extPorts . get ( )
2020-04-22 03:05:41 +00:00
proc runWithDevP2P ( config : WakuNodeConf ) =
2020-04-21 05:29:56 +00:00
if config . logLevel ! = LogLevel . NONE :
setLogLevel ( config . logLevel )
let
( ip , tcpPort , udpPort ) = setupNat ( config )
address = Address ( ip : ip , tcpPort : tcpPort , udpPort : udpPort )
# Set-up node
var node = newEthereumNode ( config . nodekey , address , 1 , nil , clientId ,
addAllCapabilities = false )
if not config . bootnodeOnly :
node . addCapability Waku # Always enable Waku protocol
var topicInterest : Option [ seq [ waku_protocol . Topic ] ]
var bloom : Option [ Bloom ]
if config . wakuTopicInterest :
var topics : seq [ waku_protocol . Topic ]
topicInterest = some ( topics )
else :
bloom = some ( fullBloom ( ) )
let wakuConfig = WakuConfig ( powRequirement : config . wakuPow ,
bloom : bloom ,
isLightNode : config . lightNode ,
maxMsgSize : waku_protocol . defaultMaxMsgSize ,
topics : topicInterest )
node . configureWaku ( wakuConfig )
if config . whisper or config . whisperBridge :
node . addCapability Whisper
node . protocolState ( Whisper ) . config . powRequirement = 0 .002
if config . whisperBridge :
node . shareMessageQueue ( )
# TODO: Status fleet bootnodes are discv5? That will not work.
let bootnodes = if config . bootnodes . len > 0 : setBootNodes ( config . bootnodes )
elif config . fleet = = prod : setBootNodes ( StatusBootNodes )
elif config . fleet = = staging : setBootNodes ( StatusBootNodesStaging )
elif config . fleet = = test : setBootNodes ( StatusBootNodesTest )
else : @ [ ]
traceAsyncErrors node . connectToNetwork ( bootnodes , not config . noListen ,
config . discovery )
if not config . bootnodeOnly :
# Optionally direct connect with a set of nodes
if config . staticnodes . len > 0 : connectToNodes ( node , config . staticnodes )
elif config . fleet = = prod : connectToNodes ( node , WhisperNodes )
elif config . fleet = = staging : connectToNodes ( node , WhisperNodesStaging )
elif config . fleet = = test : connectToNodes ( node , WhisperNodesTest )
if config . rpc :
let ta = initTAddress ( config . rpcAddress ,
Port ( config . rpcPort + config . portsShift ) )
var rpcServer = newRpcHttpServer ( [ ta ] )
let keys = newKeyStorage ( )
setupWakuRPC ( node , keys , rpcServer )
setupWakuSimRPC ( node , rpcServer )
rpcServer . start ( )
when defined ( insecure ) :
if config . metricsServer :
let
address = config . metricsServerAddress
port = config . metricsServerPort + config . portsShift
info " Starting metrics HTTP server " , address , port
metrics . startHttpServer ( $ address , Port ( port ) )
if config . logMetrics :
proc logMetrics ( udata : pointer ) {. closure , gcsafe . } =
{. gcsafe . } :
let
connectedPeers = connected_peers . value
validEnvelopes = waku_protocol . valid_envelopes . value
invalidEnvelopes = waku_protocol . dropped_expired_envelopes . value +
waku_protocol . dropped_from_future_envelopes . value +
waku_protocol . dropped_low_pow_envelopes . value +
waku_protocol . dropped_too_large_envelopes . value +
waku_protocol . dropped_bloom_filter_mismatch_envelopes . value +
waku_protocol . dropped_topic_mismatch_envelopes . value +
waku_protocol . dropped_benign_duplicate_envelopes . value +
waku_protocol . dropped_duplicate_envelopes . value
info " Node metrics " , connectedPeers , validEnvelopes , invalidEnvelopes
addTimer ( Moment . fromNow ( 2 . seconds ) , logMetrics )
addTimer ( Moment . fromNow ( 2 . seconds ) , logMetrics )
runForever ( )
2020-04-27 04:23:01 +00:00
proc newWakuProto ( switch : Switch ) : WakuProto =
var wakuproto = WakuProto ( switch : switch , codec : WakuCodec )
proc handle ( conn : Connection , proto : string ) {. async , gcsafe . } =
let msg = cast [ string ] ( await conn . readLp ( ) )
await conn . writeLp ( " Hello! " )
await conn . close ( )
wakuproto . handler = handle
return wakuproto
2020-04-22 03:05:41 +00:00
proc runWithLibP2P ( config : WakuNodeConf ) =
info " libp2p support NYI "
if config . logLevel ! = LogLevel . NONE :
setLogLevel ( config . logLevel )
let
2020-04-23 03:33:42 +00:00
# External TCP and UDP ports
2020-04-22 03:05:41 +00:00
( ip , tcpPort , udpPort ) = setupNat ( config )
address = Address ( ip : ip , tcpPort : tcpPort , udpPort : udpPort )
2020-04-23 03:33:42 +00:00
port = tcpPort
# Using this for now
DefaultAddr = " /ip4/127.0.0.1/tcp/55505 "
hostAddress = MultiAddress . init ( DefaultAddr )
# Difference between announced and host address relevant for running behind NAT, however doesn't seem like nim-libp2p supports this. GHI?
#
# TODO: Convert config.nodekey eth.key to libp2p.crypto key. Should work with Secp256k1, just need to ensure representation etc is the same. Using random now for spike.
#nodekey = config.nodekey
#keys = crypto.KeyPair(nodekey)
privKey = PrivateKey . random ( Secp256k1 )
keys = KeyPair ( seckey : privKey , pubkey : privKey . getKey ( ) )
info " Initializing networking (host address and announced same) " , address
2020-04-22 03:05:41 +00:00
# TODO: Here setup a libp2p node
2020-04-22 03:34:57 +00:00
# Essentially something like this in nbc/eth2_network:
# proc createEth2Node*(conf: BeaconNodeConf): Future[Eth2Node]
# TODO: Also see beacon_chain/beaconnode, RPC server etc
# Also probably start with floodsub for simplicity
# Slice it up only minimal parts here
# HERE ATM
2020-04-23 03:33:42 +00:00
# config.nodekey = KeyPair.random().tryGet()
# address = set above; ip, tcp and udp port (incl NAT)
# clientId = "Nimbus waku node"
#let network = await createLibP2PNode(conf) # doing in-line
# let rpcServer ...
2020-04-27 04:23:01 +00:00
# Is it a "Standard" Switch? Assume it is for now
2020-04-23 03:33:42 +00:00
var switch = newStandardSwitch ( some keys . seckey , hostAddress , triggerSelf = true , gossip = true )
2020-04-27 04:23:01 +00:00
let wakuProto = newWakuProto ( switch )
switch . mount ( wakuProto )
2020-04-23 03:33:42 +00:00
2020-04-22 03:05:41 +00:00
# Set-up node
# var node = newEthereumNode(config.nodekey, address, 1, nil, clientId,
# addAllCapabilities = false)
# if not config.bootnodeOnly:
# node.addCapability Waku # Always enable Waku protocol
# var topicInterest: Option[seq[waku_protocol.Topic]]
# var bloom: Option[Bloom]
# if config.wakuTopicInterest:
# var topics: seq[waku_protocol.Topic]
# topicInterest = some(topics)
# else:
# bloom = some(fullBloom())
# let wakuConfig = WakuConfig(powRequirement: config.wakuPow,
# bloom: bloom,
# isLightNode: config.lightNode,
# maxMsgSize: waku_protocol.defaultMaxMsgSize,
# topics: topicInterest)
# node.configureWaku(wakuConfig)
# if config.whisper or config.whisperBridge:
# node.addCapability Whisper
# node.protocolState(Whisper).config.powRequirement = 0.002
# if config.whisperBridge:
# node.shareMessageQueue()
#
# # TODO: Status fleet bootnodes are discv5? That will not work.
# let bootnodes = if config.bootnodes.len > 0: setBootNodes(config.bootnodes)
# elif config.fleet == prod: setBootNodes(StatusBootNodes)
# elif config.fleet == staging: setBootNodes(StatusBootNodesStaging)
# elif config.fleet == test : setBootNodes(StatusBootNodesTest)
# else: @[]
#
# traceAsyncErrors node.connectToNetwork(bootnodes, not config.noListen,
# config.discovery)
#
# if not config.bootnodeOnly:
# # Optionally direct connect with a set of nodes
# if config.staticnodes.len > 0: connectToNodes(node, config.staticnodes)
# elif config.fleet == prod: connectToNodes(node, WhisperNodes)
# elif config.fleet == staging: connectToNodes(node, WhisperNodesStaging)
# elif config.fleet == test: connectToNodes(node, WhisperNodesTest)
#
# if config.rpc:
# let ta = initTAddress(config.rpcAddress,
# Port(config.rpcPort + config.portsShift))
# var rpcServer = newRpcHttpServer([ta])
# let keys = newKeyStorage()
# setupWakuRPC(node, keys, rpcServer)
# setupWakuSimRPC(node, rpcServer)
# rpcServer.start()
#
# when defined(insecure):
# if config.metricsServer:
# let
# address = config.metricsServerAddress
# port = config.metricsServerPort + config.portsShift
# info "Starting metrics HTTP server", address, port
# metrics.startHttpServer($address, Port(port))
#
# if config.logMetrics:
# proc logMetrics(udata: pointer) {.closure, gcsafe.} =
# {.gcsafe.}:
# let
# connectedPeers = connected_peers.value
# validEnvelopes = waku_protocol.valid_envelopes.value
# invalidEnvelopes = waku_protocol.dropped_expired_envelopes.value +
# waku_protocol.dropped_from_future_envelopes.value +
# waku_protocol.dropped_low_pow_envelopes.value +
# waku_protocol.dropped_too_large_envelopes.value +
# waku_protocol.dropped_bloom_filter_mismatch_envelopes.value +
# waku_protocol.dropped_topic_mismatch_envelopes.value +
# waku_protocol.dropped_benign_duplicate_envelopes.value +
# waku_protocol.dropped_duplicate_envelopes.value
#
# info "Node metrics", connectedPeers, validEnvelopes, invalidEnvelopes
# addTimer(Moment.fromNow(2.seconds), logMetrics)
# addTimer(Moment.fromNow(2.seconds), logMetrics)
#
runForever ( )
proc run ( config : WakuNodeConf ) =
if config . libp2p :
info " Using libp2p (GossipSub) "
runWithLibP2P ( config )
else :
info " Using devp2p "
runWithDevP2P ( config )
2020-04-21 05:29:56 +00:00
when isMainModule :
let conf = WakuNodeConf . load ( )
run ( conf )