Added Discovery4Service as working prototype.
This commit is contained in:
parent
155287c21b
commit
bb49e935f6
|
@ -11,7 +11,8 @@ requires "nim >= 0.18.1",
|
||||||
"https://github.com/cheatfate/nimcrypto",
|
"https://github.com/cheatfate/nimcrypto",
|
||||||
"https://github.com/status-im/nim-rlp",
|
"https://github.com/status-im/nim-rlp",
|
||||||
"https://github.com/status-im/nim-ttmath",
|
"https://github.com/status-im/nim-ttmath",
|
||||||
"https://github.com/status-im/nim-eth-p2p"
|
"https://github.com/status-im/nim-eth-p2p",
|
||||||
|
"https://github.com/status-im/nim-eth-keyfile"
|
||||||
|
|
||||||
proc test(name: string, lang = "cpp") =
|
proc test(name: string, lang = "cpp") =
|
||||||
if not dirExists "build":
|
if not dirExists "build":
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
# This file may not be copied, modified, or distributed except according to
|
# This file may not be copied, modified, or distributed except according to
|
||||||
# those terms.
|
# those terms.
|
||||||
|
|
||||||
import parseopt, strutils, net, ethp2p
|
import parseopt, strutils, net, ethp2p, eth_keyfile, eth_keys, json,
|
||||||
|
nimcrypto
|
||||||
|
|
||||||
const
|
const
|
||||||
NimbusName* = "Nimbus"
|
NimbusName* = "Nimbus"
|
||||||
|
@ -68,13 +69,14 @@ type
|
||||||
NetConfiguration* = object
|
NetConfiguration* = object
|
||||||
## Network configuration object
|
## Network configuration object
|
||||||
flags*: set[NetworkFlags]
|
flags*: set[NetworkFlags]
|
||||||
bootNodes: seq[ENode]
|
bootNodes*: seq[ENode]
|
||||||
bootNodes4: seq[ENode]
|
bootNodes4*: seq[ENode]
|
||||||
bootNodes5: seq[ENode]
|
bootNodes5*: seq[ENode]
|
||||||
bindPort: uint16
|
bindPort*: uint16
|
||||||
maxPeers: int
|
discPort*: uint16
|
||||||
maxPendingPeers: int
|
maxPeers*: int
|
||||||
nodeKey: string
|
maxPendingPeers*: int
|
||||||
|
nodeKey*: PrivateKey
|
||||||
|
|
||||||
DebugConfiguration* = object
|
DebugConfiguration* = object
|
||||||
## Debug configuration object
|
## Debug configuration object
|
||||||
|
@ -108,6 +110,7 @@ proc initConfiguration(): NimbusConfiguration =
|
||||||
result.net.maxPeers = 25
|
result.net.maxPeers = 25
|
||||||
result.net.maxPendingPeers = 0
|
result.net.maxPendingPeers = 0
|
||||||
result.net.bindPort = 30303'u16
|
result.net.bindPort = 30303'u16
|
||||||
|
result.net.discPort = 30303'u16
|
||||||
|
|
||||||
## Debug defaults
|
## Debug defaults
|
||||||
result.debug.flags = {}
|
result.debug.flags = {}
|
||||||
|
@ -175,6 +178,38 @@ proc processENodesList(v: string, o: var seq[ENode]): ConfigStatus =
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
proc processPrivateKey(v: string, o: var PrivateKey): ConfigStatus =
|
||||||
|
## Convert hexadecimal string to private key object.
|
||||||
|
try:
|
||||||
|
o = initPrivateKey(v)
|
||||||
|
result = Success
|
||||||
|
except:
|
||||||
|
result = ErrorParseOption
|
||||||
|
|
||||||
|
# proc processHexBytes(v: string, o: var seq[byte]): ConfigStatus =
|
||||||
|
# ## Convert hexadecimal string to seq[byte].
|
||||||
|
# try:
|
||||||
|
# o = fromHex(v)
|
||||||
|
# result = Success
|
||||||
|
# except:
|
||||||
|
# result = ErrorParseOption
|
||||||
|
|
||||||
|
# proc processHexString(v: string, o: var string): ConfigStatus =
|
||||||
|
# ## Convert hexadecimal string to string.
|
||||||
|
# try:
|
||||||
|
# o = parseHexStr(v)
|
||||||
|
# result = Success
|
||||||
|
# except:
|
||||||
|
# result = ErrorParseOption
|
||||||
|
|
||||||
|
# proc processJson(v: string, o: var JsonNode): ConfigStatus =
|
||||||
|
# ## Convert string to JSON.
|
||||||
|
# try:
|
||||||
|
# o = parseJson(v)
|
||||||
|
# result = Success
|
||||||
|
# except:
|
||||||
|
# result = ErrorParseOption
|
||||||
|
|
||||||
proc processRpcArguments(key, value: string): ConfigStatus =
|
proc processRpcArguments(key, value: string): ConfigStatus =
|
||||||
## Processes only `RPC` related command line options
|
## Processes only `RPC` related command line options
|
||||||
result = Success
|
result = Success
|
||||||
|
@ -230,6 +265,11 @@ proc processNetArguments(key, value: string): ConfigStatus =
|
||||||
result = processInteger(value, res)
|
result = processInteger(value, res)
|
||||||
if result == Success:
|
if result == Success:
|
||||||
config.net.bindPort = uint16(res and 0xFFFF)
|
config.net.bindPort = uint16(res and 0xFFFF)
|
||||||
|
elif skey == "discport":
|
||||||
|
var res = 0
|
||||||
|
result = processInteger(value, res)
|
||||||
|
if result == Success:
|
||||||
|
config.net.discPort = uint16(res and 0xFFFF)
|
||||||
elif skey == "maxpeers":
|
elif skey == "maxpeers":
|
||||||
var res = 0
|
var res = 0
|
||||||
result = processInteger(value, res)
|
result = processInteger(value, res)
|
||||||
|
@ -240,6 +280,11 @@ proc processNetArguments(key, value: string): ConfigStatus =
|
||||||
result = processInteger(value, res)
|
result = processInteger(value, res)
|
||||||
if result == Success:
|
if result == Success:
|
||||||
config.net.maxPendingPeers = res
|
config.net.maxPendingPeers = res
|
||||||
|
elif skey == "nodekey":
|
||||||
|
var res: PrivateKey
|
||||||
|
result = processPrivateKey(value, res)
|
||||||
|
if result == Success:
|
||||||
|
config.net.nodeKey = res
|
||||||
else:
|
else:
|
||||||
result = EmptyOption
|
result = EmptyOption
|
||||||
|
|
||||||
|
@ -287,15 +332,20 @@ proc getHelpString*(): string =
|
||||||
USAGE:
|
USAGE:
|
||||||
nimbus [options]
|
nimbus [options]
|
||||||
|
|
||||||
|
ETHEREUM OPTIONS:
|
||||||
|
--keyfile:<value> Use keyfile storage file
|
||||||
|
|
||||||
NETWORKING OPTIONS:
|
NETWORKING OPTIONS:
|
||||||
--bootnodes:<value> Comma separated enode URLs for P2P discovery bootstrap (set v4+v5 instead for light servers)
|
--bootnodes:<value> Comma separated enode URLs for P2P discovery bootstrap (set v4+v5 instead for light servers)
|
||||||
--bootnodesv4:<value> Comma separated enode URLs for P2P v4 discovery bootstrap (light server, full nodes)
|
--bootnodesv4:<value> Comma separated enode URLs for P2P v4 discovery bootstrap (light server, full nodes)
|
||||||
--botnoodesv5:<value> Comma separated enode URLs for P2P v5 discovery bootstrap (light server, light nodes)
|
--botnoodesv5:<value> Comma separated enode URLs for P2P v5 discovery bootstrap (light server, light nodes)
|
||||||
--port:<value> Network listening port (default: 30303)
|
--port:<value> Network listening TCP port (default: 30303)
|
||||||
|
--discport:<value> Netowkr listening UDP port (default: 30303)
|
||||||
--maxpeers:<value> Maximum number of network peers (default: 25)
|
--maxpeers:<value> Maximum number of network peers (default: 25)
|
||||||
--maxpendpeers:<value> Maximum number of pending connection attempts (default: 0)
|
--maxpendpeers:<value> Maximum number of pending connection attempts (default: 0)
|
||||||
--nodiscover Disables the peer discovery mechanism (manual peer addition)
|
--nodiscover Disables the peer discovery mechanism (manual peer addition)
|
||||||
--v5discover Enables the experimental RLPx V5 (Topic Discovery) mechanism
|
--v5discover Enables the experimental RLPx V5 (Topic Discovery) mechanism
|
||||||
|
--nodekey:<value> P2P node private key (as hexadecimal string)
|
||||||
--testnet Use Ethereum Test Network
|
--testnet Use Ethereum Test Network
|
||||||
--mainnet Use Ethereum Main Network
|
--mainnet Use Ethereum Main Network
|
||||||
--localnet Use local network only
|
--localnet Use local network only
|
||||||
|
|
|
@ -8,14 +8,20 @@
|
||||||
# those terms.
|
# those terms.
|
||||||
|
|
||||||
import config, asyncdispatch
|
import config, asyncdispatch
|
||||||
|
import p2p/service, p2p/disc4service
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
var message: string
|
var message: string
|
||||||
if processArguments(message) != Success:
|
if processArguments(message) != ConfigStatus.Success:
|
||||||
echo message
|
echo message
|
||||||
quit(QuitFailure)
|
quit(QuitFailure)
|
||||||
else:
|
else:
|
||||||
if len(message) > 0:
|
if len(message) > 0:
|
||||||
echo message
|
echo message
|
||||||
|
|
||||||
echo dumpConfiguration()
|
var disc4: Discovery4Service
|
||||||
|
echo disc4.init()
|
||||||
|
echo disc4.configure()
|
||||||
|
echo disc4.errorMessage()
|
||||||
|
echo disc4.start()
|
||||||
|
echo disc4.errorMessage()
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
# 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 asyncdispatch, net # stdlib modules
|
||||||
|
import ethp2p, eth_keys, nimcrypto # external modules
|
||||||
|
import service, ../config # internal modules
|
||||||
|
|
||||||
|
type
|
||||||
|
Discovery4Service* = object of NetworkService
|
||||||
|
address: Address
|
||||||
|
dproto: DiscoveryProtocol
|
||||||
|
bootnodes: seq[ENode]
|
||||||
|
|
||||||
|
proc init*(s: var Discovery4Service): ServiceStatus =
|
||||||
|
s.id = "Nimbus.Discovery.4"
|
||||||
|
s.flags = {}
|
||||||
|
s.state = Stopped
|
||||||
|
s.error = ""
|
||||||
|
result = ServiceStatus.Success
|
||||||
|
|
||||||
|
proc configure*(s: var Discovery4Service): ServiceStatus =
|
||||||
|
let conf = getConfiguration()
|
||||||
|
cleanError(s)
|
||||||
|
checkState(s, {Stopped, Paused})
|
||||||
|
|
||||||
|
var bootnodes = newSeq[ENode]()
|
||||||
|
if TestNet in conf.net.flags:
|
||||||
|
for item in ROPSTEN_BOOTNODES:
|
||||||
|
bootnodes.add(initENode(item))
|
||||||
|
elif MainNet in conf.net.flags:
|
||||||
|
for item in MAINNET_BOOTNODES:
|
||||||
|
bootnodes.add(initENode(item))
|
||||||
|
|
||||||
|
for item in conf.net.bootNodes:
|
||||||
|
bootnodes.add(item)
|
||||||
|
for item in conf.net.bootNodes4:
|
||||||
|
bootnodes.add(item)
|
||||||
|
|
||||||
|
if isFullZero(conf.net.nodeKey):
|
||||||
|
s.setFailure("P2P Node private key is not set!")
|
||||||
|
return ServiceStatus.Error
|
||||||
|
|
||||||
|
if Configured notin s.flags:
|
||||||
|
s.address.ip = parseIpAddress("0.0.0.0")
|
||||||
|
s.address.tcpPort = Port(conf.net.bindPort)
|
||||||
|
s.address.udpPort = Port(conf.net.discPort)
|
||||||
|
s.dproto = newDiscoveryProtocol(conf.net.nodeKey, s.address, bootnodes)
|
||||||
|
|
||||||
|
s.flags.incl(Configured)
|
||||||
|
result = ServiceStatus.Success
|
||||||
|
|
||||||
|
proc start*(s: var Discovery4Service): ServiceStatus =
|
||||||
|
cleanError(s)
|
||||||
|
checkState(s, {Stopped})
|
||||||
|
checkFlags(s, {Configured}, "not configured!")
|
||||||
|
try:
|
||||||
|
s.dproto.open()
|
||||||
|
waitFor s.dproto.bootstrap()
|
||||||
|
except ValueError as e:
|
||||||
|
s.setFailure(e.msg)
|
||||||
|
result = ServiceStatus.Error
|
||||||
|
except OSError as e:
|
||||||
|
s.setFailure(e.msg)
|
||||||
|
result = ServiceStatus.Error
|
||||||
|
result = ServiceStatus.Success
|
||||||
|
|
||||||
|
proc stop*(s: var Discovery4Service): ServiceStatus =
|
||||||
|
cleanError(s)
|
||||||
|
checkState(s, {Running, Paused})
|
||||||
|
checkFlags(s, {Configured}, "not configured!")
|
||||||
|
result = ServiceStatus.Success
|
||||||
|
|
||||||
|
proc pause*(s: var Discovery4Service): ServiceStatus =
|
||||||
|
cleanError(s)
|
||||||
|
checkState(s, {Running})
|
||||||
|
s.state = Paused
|
||||||
|
result = ServiceStatus.Success
|
||||||
|
|
||||||
|
proc resume*(s: var Discovery4Service): ServiceStatus =
|
||||||
|
cleanError(s)
|
||||||
|
checkState(s, {Paused})
|
||||||
|
result = ServiceStatus.Success
|
|
@ -0,0 +1,55 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
type
|
||||||
|
ServiceState* = enum
|
||||||
|
Stopped,
|
||||||
|
Starting,
|
||||||
|
Running,
|
||||||
|
Pausing,
|
||||||
|
Paused,
|
||||||
|
Resuming,
|
||||||
|
Failure
|
||||||
|
|
||||||
|
ServiceStatus* = enum
|
||||||
|
Success,
|
||||||
|
Error
|
||||||
|
|
||||||
|
ServiceFlags* = enum
|
||||||
|
Configured
|
||||||
|
|
||||||
|
NetworkService* = object of RootObj
|
||||||
|
id*: string
|
||||||
|
flags*: set[ServiceFlags]
|
||||||
|
state*: ServiceState
|
||||||
|
error*: string
|
||||||
|
|
||||||
|
template checkState*(s: var NetworkService,
|
||||||
|
need: set[ServiceState]) =
|
||||||
|
if s.state notin need:
|
||||||
|
s.error = "Service [" & s.id & "] state is {" & $s.state & "} but " &
|
||||||
|
$need & " required!"
|
||||||
|
return(Error)
|
||||||
|
|
||||||
|
template cleanError*(s: var NetworkService) =
|
||||||
|
s.error.setLen(0)
|
||||||
|
|
||||||
|
template checkFlags*(s: var NetworkService,
|
||||||
|
need: set[ServiceFlags],
|
||||||
|
msg: string) =
|
||||||
|
if s.flags * need != need:
|
||||||
|
s.error = "Service [" & s.id & "] is " & msg
|
||||||
|
return(Error)
|
||||||
|
|
||||||
|
template setFailure*(s: var NetworkService, msg: string) =
|
||||||
|
s.state = Failure
|
||||||
|
s.error = "Service [" & s.id & "] returns error: " & msg
|
||||||
|
|
||||||
|
template errorMessage*(s: NetworkService): string =
|
||||||
|
s.error
|
Loading…
Reference in New Issue