Integrated P2P & RPC servers.
This commit is contained in:
parent
361e41f688
commit
533bd6caf4
|
@ -13,6 +13,7 @@ requires "nim >= 0.18.1",
|
||||||
"rlp",
|
"rlp",
|
||||||
"stint",
|
"stint",
|
||||||
"https://github.com/status-im/nim-eth-common",
|
"https://github.com/status-im/nim-eth-common",
|
||||||
|
"https://github.com/status-im/nim-eth-rpc",
|
||||||
"eth_p2p",
|
"eth_p2p",
|
||||||
"eth_keyfile"
|
"eth_keyfile"
|
||||||
|
|
||||||
|
|
|
@ -7,8 +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, eth_p2p, eth_keyfile, eth_keys, json,
|
import parseopt, strutils
|
||||||
nimcrypto
|
import asyncdispatch2, eth_keys, eth_p2p
|
||||||
|
|
||||||
const
|
const
|
||||||
NimbusName* = "Nimbus"
|
NimbusName* = "Nimbus"
|
||||||
|
@ -29,6 +29,52 @@ const
|
||||||
NimbusVersion* = $NimbusMajor & "." & $NimbusMinor & "." & $NimbusPatch
|
NimbusVersion* = $NimbusMajor & "." & $NimbusMinor & "." & $NimbusPatch
|
||||||
## is the version of Nimbus as a string.
|
## is the version of Nimbus as a string.
|
||||||
|
|
||||||
|
NimbusHeader* = NimbusName & " Version " & NimbusVersion &
|
||||||
|
" [" & hostOS & ": " & hostCPU & "]\r\n" &
|
||||||
|
NimbusCopyright
|
||||||
|
## is the header which printed, when nimbus binary got executed
|
||||||
|
|
||||||
|
NimbusIdent* = "$1/$2 ($3/$4)" % [NimbusName, NimbusVersion, hostCPU, hostOS]
|
||||||
|
## project ident name for networking services
|
||||||
|
|
||||||
|
const
|
||||||
|
MainnetBootnodes = [
|
||||||
|
"enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@52.16.188.185:30303" , # IE
|
||||||
|
"enode://3f1d12044546b76342d59d4a05532c14b85aa669704bfe1f864fe079415aa2c02d743e03218e57a33fb94523adb54032871a6c51b2cc5514cb7c7e35b3ed0a99@13.93.211.84:30303", # US-WEST
|
||||||
|
"enode://78de8a0916848093c73790ead81d1928bec737d565119932b98c6b100d944b7a95e94f847f689fc723399d2e31129d182f7ef3863f2b4c820abbf3ab2722344d@191.235.84.50:30303", # BR
|
||||||
|
"enode://158f8aab45f6d19c6cbf4a089c2670541a8da11978a2f90dbf6a502a4a3bab80d288afdbeb7ec0ef6d92de563767f3b1ea9e8e334ca711e9f8e2df5a0385e8e6@13.75.154.138:30303", # AU
|
||||||
|
"enode://1118980bf48b0a3640bdba04e0fe78b1add18e1cd99bf22d53daac1fd9972ad650df52176e7c7d89d1114cfef2bc23a2959aa54998a46afcf7d91809f0855082@52.74.57.123:30303", # SG
|
||||||
|
"enode://979b7fa28feeb35a4741660a16076f1943202cb72b6af70d327f053e248bab9ba81760f39d0701ef1d8f89cc1fbd2cacba0710a12cd5314d5e0c9021aa3637f9@5.1.83.226:30303" # DE
|
||||||
|
]
|
||||||
|
|
||||||
|
RopstenBootnodes = [
|
||||||
|
"enode://30b7ab30a01c124a6cceca36863ece12c4f5fa68e3ba9b0b51407ccc002eeed3b3102d20a88f1c1d3c3154e2449317b8ef95090e77b312d5cc39354f86d5d606@52.176.7.10:30303", # US-Azure geth
|
||||||
|
"enode://865a63255b3bb68023b6bffd5095118fcc13e79dcf014fe4e47e065c350c7cc72af2e53eff895f11ba1bbb6a2b33271c1116ee870f266618eadfc2e78aa7349c@52.176.100.77:30303", # US-Azure parity
|
||||||
|
"enode://6332792c4a00e3e4ee0926ed89e0d27ef985424d97b6a45bf0f23e51f0dcb5e66b875777506458aea7af6f9e4ffb69f43f3778ee73c81ed9d34c51c4b16b0b0f@52.232.243.152:30303", # Parity
|
||||||
|
"enode://94c15d1b9e2fe7ce56e458b9a3b672ef11894ddedd0c6f247e0f1d3487f52b66208fb4aeb8179fce6e3a749ea93ed147c37976d67af557508d199d9594c35f09@192.81.208.223:30303" # @gpip
|
||||||
|
]
|
||||||
|
|
||||||
|
RinkebyBootnodes = [
|
||||||
|
"enode://a24ac7c5484ef4ed0c5eb2d36620ba4e4aa13b8c84684e1b4aab0cebea2ae45cb4d375b77eab56516d34bfbd3c1a833fc51296ff084b770b94fb9028c4d25ccf@52.169.42.101:30303", # IE
|
||||||
|
"enode://343149e4feefa15d882d9fe4ac7d88f885bd05ebb735e547f12e12080a9fa07c8014ca6fd7f373123488102fe5e34111f8509cf0b7de3f5b44339c9f25e87cb8@52.3.158.184:30303", # INFURA
|
||||||
|
"enode://b6b28890b006743680c52e64e0d16db57f28124885595fa03a562be1d2bf0f3a1da297d56b13da25fb992888fd556d4c1a27b1f39d531bde7de1921c90061cc6@159.89.28.211:30303", # AKASHA
|
||||||
|
]
|
||||||
|
|
||||||
|
DiscoveryV5Bootnodes = [
|
||||||
|
"enode://06051a5573c81934c9554ef2898eb13b33a34b94cf36b202b69fde139ca17a85051979867720d4bdae4323d4943ddf9aeeb6643633aa656e0be843659795007a@35.177.226.168:30303",
|
||||||
|
"enode://0cc5f5ffb5d9098c8b8c62325f3797f56509bff942704687b6530992ac706e2cb946b90a34f1f19548cd3c7baccbcaea354531e5983c7d1bc0dee16ce4b6440b@40.118.3.223:30304",
|
||||||
|
"enode://1c7a64d76c0334b0418c004af2f67c50e36a3be60b5e4790bdac0439d21603469a85fad36f2473c9a80eb043ae60936df905fa28f1ff614c3e5dc34f15dcd2dc@40.118.3.223:30306",
|
||||||
|
"enode://85c85d7143ae8bb96924f2b54f1b3e70d8c4d367af305325d30a61385a432f247d2c75c45c6b4a60335060d072d7f5b35dd1d4c45f76941f62a4f83b6e75daaf@40.118.3.223:30307"
|
||||||
|
]
|
||||||
|
|
||||||
|
KovanBootnodes = [
|
||||||
|
"enode://56abaf065581a5985b8c5f4f88bd202526482761ba10be9bfdcd14846dd01f652ec33fde0f8c0fd1db19b59a4c04465681fcef50e11380ca88d25996191c52de@40.71.221.215:30303",
|
||||||
|
"enode://d07827483dc47b368eaf88454fb04b41b7452cf454e194e2bd4c14f98a3278fed5d819dbecd0d010407fc7688d941ee1e58d4f9c6354d3da3be92f55c17d7ce3@52.166.117.77:30303",
|
||||||
|
"enode://8fa162563a8e5a05eef3e1cd5abc5828c71344f7277bb788a395cce4a0e30baf2b34b92fe0b2dbbba2313ee40236bae2aab3c9811941b9f5a7e8e90aaa27ecba@52.165.239.18:30303",
|
||||||
|
"enode://7e2e7f00784f516939f94e22bdc6cf96153603ca2b5df1c7cc0f90a38e7a2f218ffb1c05b156835e8b49086d11fdd1b3e2965be16baa55204167aa9bf536a4d9@52.243.47.56:30303",
|
||||||
|
"enode://0518a3d35d4a7b3e8c433e7ffd2355d84a1304ceb5ef349787b556197f0c87fad09daed760635b97d52179d645d3e6d16a37d2cc0a9945c2ddf585684beb39ac@40.68.248.100:30303"
|
||||||
|
]
|
||||||
|
|
||||||
type
|
type
|
||||||
ConfigStatus* = enum
|
ConfigStatus* = enum
|
||||||
## Configuration status flags
|
## Configuration status flags
|
||||||
|
@ -36,6 +82,7 @@ type
|
||||||
EmptyOption, ## No options in category
|
EmptyOption, ## No options in category
|
||||||
ErrorUnknownOption, ## Unknown option in command line found
|
ErrorUnknownOption, ## Unknown option in command line found
|
||||||
ErrorParseOption, ## Error in parsing command line option
|
ErrorParseOption, ## Error in parsing command line option
|
||||||
|
ErrorIncorrectOption, ## Option has incorrect value
|
||||||
Error ## Unspecified error
|
Error ## Unspecified error
|
||||||
|
|
||||||
RpcFlags* {.pure.} = enum
|
RpcFlags* {.pure.} = enum
|
||||||
|
@ -45,16 +92,15 @@ type
|
||||||
RpcConfiguration* = object
|
RpcConfiguration* = object
|
||||||
## JSON-RPC configuration object
|
## JSON-RPC configuration object
|
||||||
flags*: set[RpcFlags] ## RPC flags
|
flags*: set[RpcFlags] ## RPC flags
|
||||||
bindAddress*: IpAddress ## RPC bind address
|
binds*: seq[TransportAddress] ## RPC bind address
|
||||||
bindPort*: uint16 ## RPC bind port
|
|
||||||
allowedIPs*: seq[IpAddress] ## Sequence of allowed IP addresses
|
|
||||||
username*: string ## RPC authorization username
|
|
||||||
password*: string ## RPC authorization password
|
|
||||||
|
|
||||||
NetworkFlags* = enum
|
NetworkFlags* = enum
|
||||||
## Ethereum network flags
|
## Ethereum network flags
|
||||||
LocalNet, ## Use local network only
|
RopstenNet, ## Use test Ropsten network
|
||||||
TestNet, ## Use test network only
|
RinkebyNet, ## Use test Rinkeby network
|
||||||
|
MordenNet, ## Use test Morden network
|
||||||
|
KovanNet, ## Use test Kovan network
|
||||||
|
CustomNet, ## Use custom network
|
||||||
MainNet, ## Use main network only
|
MainNet, ## Use main network only
|
||||||
NoDiscover, ## Peer discovery disabled
|
NoDiscover, ## Peer discovery disabled
|
||||||
V5Discover, ## Dicovery V5 enabled
|
V5Discover, ## Dicovery V5 enabled
|
||||||
|
@ -68,19 +114,19 @@ type
|
||||||
|
|
||||||
NetConfiguration* = object
|
NetConfiguration* = object
|
||||||
## Network configuration object
|
## Network configuration object
|
||||||
flags*: set[NetworkFlags]
|
flags*: set[NetworkFlags] ## Network flags
|
||||||
bootNodes*: seq[ENode]
|
bootNodes*: seq[ENode] ## List of bootnodes
|
||||||
bootNodes4*: seq[ENode]
|
bindPort*: uint16 ## Main TCP bind port
|
||||||
bootNodes5*: seq[ENode]
|
discPort*: uint16 ## Discovery UDP bind port
|
||||||
bindPort*: uint16
|
maxPeers*: int ## Maximum allowed number of peers
|
||||||
discPort*: uint16
|
maxPendingPeers*: int ## Maximum allowed pending peers
|
||||||
maxPeers*: int
|
networkId*: int ## Network ID as integer
|
||||||
maxPendingPeers*: int
|
ident*: string ## Server ident name string
|
||||||
nodeKey*: PrivateKey
|
nodeKey*: PrivateKey ## Server private key
|
||||||
|
|
||||||
DebugConfiguration* = object
|
DebugConfiguration* = object
|
||||||
## Debug configuration object
|
## Debug configuration object
|
||||||
flags*: set[DebugFlags]
|
flags*: set[DebugFlags] ## Debug flags
|
||||||
|
|
||||||
NimbusConfiguration* = ref object
|
NimbusConfiguration* = ref object
|
||||||
## Main Nimbus configuration object
|
## Main Nimbus configuration object
|
||||||
|
@ -90,36 +136,7 @@ type
|
||||||
|
|
||||||
var nimbusConfig {.threadvar.}: NimbusConfiguration
|
var nimbusConfig {.threadvar.}: NimbusConfiguration
|
||||||
|
|
||||||
proc initConfiguration(): NimbusConfiguration =
|
proc getConfiguration*(): NimbusConfiguration {.gcsafe.}
|
||||||
## Allocates and initializes `NimbusConfiguration` with default values
|
|
||||||
result = new NimbusConfiguration
|
|
||||||
|
|
||||||
## RPC defaults
|
|
||||||
result.rpc.flags = {}
|
|
||||||
result.rpc.bindAddress = parseIpAddress("127.0.0.1")
|
|
||||||
result.rpc.bindPort = uint16(7654)
|
|
||||||
result.rpc.username = ""
|
|
||||||
result.rpc.password = ""
|
|
||||||
result.rpc.allowedIPs = newSeq[IpAddress]()
|
|
||||||
|
|
||||||
## Network defaults
|
|
||||||
result.net.flags = {TestNet}
|
|
||||||
result.net.bootNodes = newSeq[ENode]()
|
|
||||||
result.net.bootNodes4 = newSeq[ENode]()
|
|
||||||
result.net.bootNodes5 = newSeq[ENode]()
|
|
||||||
result.net.maxPeers = 25
|
|
||||||
result.net.maxPendingPeers = 0
|
|
||||||
result.net.bindPort = 30303'u16
|
|
||||||
result.net.discPort = 30303'u16
|
|
||||||
|
|
||||||
## Debug defaults
|
|
||||||
result.debug.flags = {}
|
|
||||||
|
|
||||||
proc getConfiguration*(): NimbusConfiguration =
|
|
||||||
## Retreive current configuration object `NimbusConfiguration`.
|
|
||||||
if isNil(nimbusConfig):
|
|
||||||
nimbusConfig = initConfiguration()
|
|
||||||
result = nimbusConfig
|
|
||||||
|
|
||||||
proc processList(v: string, o: var seq[string]) =
|
proc processList(v: string, o: var seq[string]) =
|
||||||
## Process comma-separated list of strings.
|
## Process comma-separated list of strings.
|
||||||
|
@ -136,26 +153,29 @@ proc processInteger(v: string, o: var int): ConfigStatus =
|
||||||
except:
|
except:
|
||||||
result = ErrorParseOption
|
result = ErrorParseOption
|
||||||
|
|
||||||
proc processIpAddress(v: string, o: var IpAddress): ConfigStatus =
|
proc processAddressPortsList(v: string,
|
||||||
## Convert string to IpAddress.
|
o: var seq[TransportAddress]): ConfigStatus =
|
||||||
try:
|
## Convert <hostname:port>;...;<hostname:port> to list of `TransportAddress`.
|
||||||
o = parseIpAddress(v)
|
var list = newSeq[string]()
|
||||||
result = Success
|
|
||||||
except:
|
|
||||||
result = ErrorParseOption
|
|
||||||
|
|
||||||
proc processAddressesList(v: string, o: var seq[IpAddress]): ConfigStatus =
|
|
||||||
## Convert comma-separated list of strings to list of IpAddress.
|
|
||||||
var
|
|
||||||
address: IpAddress
|
|
||||||
list = newSeq[string]()
|
|
||||||
processList(v, list)
|
processList(v, list)
|
||||||
for item in list:
|
for item in list:
|
||||||
result = processIpAddress(item, address)
|
var tas4: seq[TransportAddress]
|
||||||
if result == Success:
|
var tas6: seq[TransportAddress]
|
||||||
o.add(address)
|
try:
|
||||||
else:
|
tas4 = resolveTAddress(item, IpAddressFamily.IPv4)
|
||||||
|
except:
|
||||||
|
discard
|
||||||
|
try:
|
||||||
|
tas6 = resolveTAddress(item, IpAddressFamily.IPv6)
|
||||||
|
except:
|
||||||
|
discard
|
||||||
|
if len(tas4) == 0 and len(tas6) == 0:
|
||||||
|
result = ErrorParseOption
|
||||||
break
|
break
|
||||||
|
else:
|
||||||
|
for a in tas4: o.add(a)
|
||||||
|
for a in tas6: o.add(a)
|
||||||
|
result = Success
|
||||||
|
|
||||||
proc processENode(v: string, o: var ENode): ConfigStatus =
|
proc processENode(v: string, o: var ENode): ConfigStatus =
|
||||||
## Convert string to ENode.
|
## Convert string to ENode.
|
||||||
|
@ -218,21 +238,47 @@ proc processRpcArguments(key, value: string): ConfigStatus =
|
||||||
if skey == "rpc":
|
if skey == "rpc":
|
||||||
config.rpc.flags.incl(Enabled)
|
config.rpc.flags.incl(Enabled)
|
||||||
elif skey == "rpcbind":
|
elif skey == "rpcbind":
|
||||||
result = processIpAddress(value, config.rpc.bindAddress)
|
config.rpc.binds.setLen(0)
|
||||||
elif skey == "rpcport":
|
result = processAddressPortsList(value, config.rpc.binds)
|
||||||
var res = 0
|
|
||||||
result = processInteger(value, res)
|
|
||||||
if result == Success:
|
|
||||||
config.rpc.bindPort = uint16(res and 0xFFFF)
|
|
||||||
elif skey == "rpcuser":
|
|
||||||
config.rpc.username = value
|
|
||||||
elif skey == "rpcpassword":
|
|
||||||
config.rpc.password = value
|
|
||||||
elif skey == "rpcallowip":
|
|
||||||
result = processAddressesList(value, config.rpc.allowedIPs)
|
|
||||||
else:
|
else:
|
||||||
result = EmptyOption
|
result = EmptyOption
|
||||||
|
|
||||||
|
template setBootnodes(onodes, nodes: untyped): untyped =
|
||||||
|
var node: ENode
|
||||||
|
for item in (nodes):
|
||||||
|
doAssert(processENode(item, node) == Success)
|
||||||
|
(onodes).add(node)
|
||||||
|
|
||||||
|
proc setNetwork(conf: var NetConfiguration, network: NetworkFlags,
|
||||||
|
id: int = 0) =
|
||||||
|
## Set network id and default network bootnodes
|
||||||
|
conf.flags.excl({MainNet, MordenNet, RopstenNet, RinkebyNet, KovanNet,
|
||||||
|
CustomNet})
|
||||||
|
conf.flags.incl(network)
|
||||||
|
case network
|
||||||
|
of MainNet:
|
||||||
|
conf.networkId = 1
|
||||||
|
conf.bootNodes.setLen(0)
|
||||||
|
conf.bootNodes.setBootnodes(MainnetBootnodes)
|
||||||
|
of MordenNet:
|
||||||
|
conf.networkId = 2
|
||||||
|
of RopstenNet:
|
||||||
|
conf.networkId = 3
|
||||||
|
conf.bootNodes.setLen(0)
|
||||||
|
conf.bootNodes.setBootnodes(RopstenBootnodes)
|
||||||
|
of RinkebyNet:
|
||||||
|
conf.networkId = 4
|
||||||
|
conf.bootNodes.setLen(0)
|
||||||
|
conf.bootNodes.setBootnodes(RinkebyBootnodes)
|
||||||
|
of KovanNet:
|
||||||
|
conf.networkId = 42
|
||||||
|
conf.bootNodes.setLen(0)
|
||||||
|
conf.bootNodes.setBootnodes(KovanBootnodes)
|
||||||
|
of CustomNet:
|
||||||
|
conf.networkId = id
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
|
||||||
proc processNetArguments(key, value: string): ConfigStatus =
|
proc processNetArguments(key, value: string): ConfigStatus =
|
||||||
## Processes only `Networking` related command line options
|
## Processes only `Networking` related command line options
|
||||||
result = Success
|
result = Success
|
||||||
|
@ -241,25 +287,44 @@ proc processNetArguments(key, value: string): ConfigStatus =
|
||||||
if skey == "bootnodes":
|
if skey == "bootnodes":
|
||||||
result = processENodesList(value, config.net.bootnodes)
|
result = processENodesList(value, config.net.bootnodes)
|
||||||
elif skey == "bootnodesv4":
|
elif skey == "bootnodesv4":
|
||||||
result = processENodesList(value, config.net.bootNodes4)
|
result = processENodesList(value, config.net.bootNodes)
|
||||||
elif skey == "bootnodesv5":
|
elif skey == "bootnodesv5":
|
||||||
result = processENodesList(value, config.net.bootNodes5)
|
result = processENodesList(value, config.net.bootNodes)
|
||||||
elif skey == "testnet":
|
elif skey == "testnet":
|
||||||
config.net.flags.incl(TestNet)
|
config.net.setNetwork(RopstenNet)
|
||||||
config.net.flags.excl(LocalNet)
|
|
||||||
config.net.flags.excl(MainNet)
|
|
||||||
elif skey == "localnet":
|
|
||||||
config.net.flags.incl(LocalNet)
|
|
||||||
config.net.flags.excl(TestNet)
|
|
||||||
config.net.flags.excl(MainNet)
|
|
||||||
elif skey == "mainnet":
|
elif skey == "mainnet":
|
||||||
config.net.flags.incl(MainNet)
|
config.net.setNetwork(MainNet)
|
||||||
config.net.flags.excl(LocalNet)
|
elif skey == "ropsten":
|
||||||
config.net.flags.excl(TestNet)
|
config.net.setNetwork(RopstenNet)
|
||||||
|
elif skey == "rinkeby":
|
||||||
|
config.net.setNetwork(RinkebyNet)
|
||||||
|
elif skey == "morden":
|
||||||
|
config.net.setNetwork(MordenNet)
|
||||||
|
elif skey == "kovan":
|
||||||
|
config.net.setNetwork(KovanNet)
|
||||||
|
elif skey == "networkid":
|
||||||
|
var res = 0
|
||||||
|
result = processInteger(value, res)
|
||||||
|
if result == Success:
|
||||||
|
case res
|
||||||
|
of 1:
|
||||||
|
config.net.setNetwork(MainNet)
|
||||||
|
of 2:
|
||||||
|
config.net.setNetwork(MordenNet)
|
||||||
|
of 3:
|
||||||
|
config.net.setNetwork(RopstenNet)
|
||||||
|
of 4:
|
||||||
|
config.net.setNetwork(RinkebyNet)
|
||||||
|
of 42:
|
||||||
|
config.net.setNetwork(KovanNet)
|
||||||
|
else:
|
||||||
|
config.net.setNetwork(CustomNet, res)
|
||||||
elif skey == "nodiscover":
|
elif skey == "nodiscover":
|
||||||
config.net.flags.incl(NoDiscover)
|
config.net.flags.incl(NoDiscover)
|
||||||
elif skey == "v5discover":
|
elif skey == "v5discover":
|
||||||
config.net.flags.incl(V5Discover)
|
config.net.flags.incl(V5Discover)
|
||||||
|
config.net.bootNodes.setLen(0)
|
||||||
|
config.net.bootNodes.setBootnodes(DiscoveryV5Bootnodes)
|
||||||
elif skey == "port":
|
elif skey == "port":
|
||||||
var res = 0
|
var res = 0
|
||||||
result = processInteger(value, res)
|
result = processInteger(value, res)
|
||||||
|
@ -285,6 +350,8 @@ proc processNetArguments(key, value: string): ConfigStatus =
|
||||||
result = processPrivateKey(value, res)
|
result = processPrivateKey(value, res)
|
||||||
if result == Success:
|
if result == Success:
|
||||||
config.net.nodeKey = res
|
config.net.nodeKey = res
|
||||||
|
elif skey == "ident":
|
||||||
|
config.net.ident = value
|
||||||
else:
|
else:
|
||||||
result = EmptyOption
|
result = EmptyOption
|
||||||
|
|
||||||
|
@ -314,6 +381,7 @@ proc dumpConfiguration*(): string =
|
||||||
result = repr config
|
result = repr config
|
||||||
|
|
||||||
template checkArgument(a, b, c, e: untyped) =
|
template checkArgument(a, b, c, e: untyped) =
|
||||||
|
## Checks if arguments got processed successfully
|
||||||
var res = (a)(string((b)), string((c)))
|
var res = (a)(string((b)), string((c)))
|
||||||
if res == Success:
|
if res == Success:
|
||||||
continue
|
continue
|
||||||
|
@ -321,13 +389,37 @@ template checkArgument(a, b, c, e: untyped) =
|
||||||
(e) = "Error processing option [" & key & "] with value [" & value & "]"
|
(e) = "Error processing option [" & key & "] with value [" & value & "]"
|
||||||
result = res
|
result = res
|
||||||
break
|
break
|
||||||
|
elif res == ErrorIncorrectOption:
|
||||||
|
(e) = "Incorrect value for option [" & key & "] value [" & value & "]"
|
||||||
|
result = res
|
||||||
|
break
|
||||||
|
|
||||||
proc getVersionString*(): string =
|
proc initConfiguration(): NimbusConfiguration =
|
||||||
result = NimbusName & ", " & NimbusVersion & "\n" & NimbusCopyright & "\n"
|
## Allocates and initializes `NimbusConfiguration` with default values
|
||||||
|
result = new NimbusConfiguration
|
||||||
|
## RPC defaults
|
||||||
|
result.rpc.flags = {}
|
||||||
|
result.rpc.binds = @[initTAddress("127.0.0.1:8545")]
|
||||||
|
|
||||||
|
## Network defaults
|
||||||
|
result.net.setNetwork(RopstenNet)
|
||||||
|
result.net.maxPeers = 25
|
||||||
|
result.net.maxPendingPeers = 0
|
||||||
|
result.net.bindPort = 30303'u16
|
||||||
|
result.net.discPort = 30303'u16
|
||||||
|
result.net.ident = NimbusIdent
|
||||||
|
|
||||||
|
## Debug defaults
|
||||||
|
result.debug.flags = {}
|
||||||
|
|
||||||
|
proc getConfiguration*(): NimbusConfiguration =
|
||||||
|
## Retreive current configuration object `NimbusConfiguration`.
|
||||||
|
if isNil(nimbusConfig):
|
||||||
|
nimbusConfig = initConfiguration()
|
||||||
|
result = nimbusConfig
|
||||||
|
|
||||||
proc getHelpString*(): string =
|
proc getHelpString*(): string =
|
||||||
result = getVersionString()
|
result = """
|
||||||
result &= """
|
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
nimbus [options]
|
nimbus [options]
|
||||||
|
@ -340,31 +432,34 @@ NETWORKING OPTIONS:
|
||||||
--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 TCP port (default: 30303)
|
--port:<value> Network listening TCP port (default: 30303)
|
||||||
--discport:<value> Netowkr listening UDP port (default: 30303)
|
--discport:<value> Network 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)
|
--nodekey:<value> P2P node private key (as hexadecimal string)
|
||||||
--testnet Use Ethereum Test Network
|
--testnet Use Ethereum Ropsten Test Network (default)
|
||||||
|
--rinkeby Use Ethereum Rinkeby Test Network
|
||||||
|
--ropsten Use Ethereum Test Network (Ropsten Network)
|
||||||
--mainnet Use Ethereum Main Network
|
--mainnet Use Ethereum Main Network
|
||||||
--localnet Use local network only
|
--morden Use Ethereum Morden Test Network
|
||||||
|
--networkid:<value> Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten, 4=Rinkeby) (default: 3)
|
||||||
|
--ident:<value> Client identifier (default is '$1')
|
||||||
|
|
||||||
API AND CONSOLE OPTIONS:
|
API AND CONSOLE OPTIONS:
|
||||||
--rpc Enable the HTTP-RPC server
|
--rpc Enable the HTTP-RPC server
|
||||||
--rpcbind:<value> HTTP-RPC server will bind to given address (default: 127.0.0.1)
|
--rpcbind:<value> HTTP-RPC server will bind to given comma separated address:port pairs (default: 127.0.0.1:8545)
|
||||||
--rpcport:<value> HTTP-RPC server listening port (default: 7654)
|
|
||||||
--rpcuser:<value> HTTP-RPC authorization username
|
|
||||||
--rpcpassword:<value> HTTP-RPC authorization password
|
|
||||||
--rpcallowip:<value> Allow HTTP-RPC connections from specified IP addresses
|
|
||||||
|
|
||||||
LOGGING AND DEBUGGING OPTIONS:
|
LOGGING AND DEBUGGING OPTIONS:
|
||||||
--debug Enable debug mode
|
--debug Enable debug mode
|
||||||
--test:<value> Perform specified test
|
--test:<value> Perform specified test
|
||||||
"""
|
""" % [
|
||||||
|
NimbusIdent
|
||||||
|
]
|
||||||
|
|
||||||
proc processArguments*(msg: var string): ConfigStatus =
|
proc processArguments*(msg: var string): ConfigStatus =
|
||||||
## Process command line argument and update `NimbusConfiguration`.
|
## Process command line argument and update `NimbusConfiguration`.
|
||||||
|
discard getConfiguration()
|
||||||
var opt = initOptParser()
|
var opt = initOptParser()
|
||||||
var length = 0
|
var length = 0
|
||||||
for kind, key, value in opt.getopt():
|
for kind, key, value in opt.getopt():
|
||||||
|
@ -379,7 +474,7 @@ proc processArguments*(msg: var string): ConfigStatus =
|
||||||
result = Success
|
result = Success
|
||||||
break
|
break
|
||||||
of "version", "ver", "v":
|
of "version", "ver", "v":
|
||||||
msg = getVersionString()
|
msg = NimbusVersion
|
||||||
result = Success
|
result = Success
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -7,24 +7,100 @@
|
||||||
# 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 config
|
import strutils, net
|
||||||
import p2p/service, p2p/disc4service
|
import asyncdispatch2, rpcserver, eth_p2p, eth_keys
|
||||||
|
import config, rpc/common, rpc/p2p
|
||||||
|
|
||||||
|
## TODO:
|
||||||
|
## * No IPv6 support
|
||||||
|
## * No multiple bind addresses support
|
||||||
|
## * No database support
|
||||||
|
|
||||||
|
when not defined(windows):
|
||||||
|
from posix import SIGINT, SIGTERM
|
||||||
|
|
||||||
|
type
|
||||||
|
NimbusState = enum
|
||||||
|
Starting, Running, Stopping, Stopped
|
||||||
|
|
||||||
|
NimbusObject = ref object
|
||||||
|
rpcServer*: RpcServer
|
||||||
|
p2pServer*: P2PServer
|
||||||
|
state*: NimbusState
|
||||||
|
|
||||||
|
proc start(): NimbusObject =
|
||||||
|
var nimbus = NimbusObject()
|
||||||
|
var conf = getConfiguration()
|
||||||
|
|
||||||
|
## Creating RPC Server
|
||||||
|
if RpcFlags.Enabled in conf.rpc.flags:
|
||||||
|
nimbus.rpcServer = newRpcServer(conf.rpc.binds)
|
||||||
|
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)
|
||||||
|
|
||||||
|
nimbus.p2pServer = newP2PServer(keypair, address, nil, conf.net.bootNodes,
|
||||||
|
conf.net.ident, conf.net.networkId)
|
||||||
|
|
||||||
|
if RpcFlags.Enabled in conf.rpc.flags:
|
||||||
|
setupP2PRpc(nimbus.p2pServer, nimbus.rpcServer)
|
||||||
|
|
||||||
|
## Starting servers
|
||||||
|
nimbus.state = Starting
|
||||||
|
if RpcFlags.Enabled in conf.rpc.flags:
|
||||||
|
nimbus.rpcServer.rpc("admin_quit") do() -> string:
|
||||||
|
nimbus.state = Stopping
|
||||||
|
result = "EXITING"
|
||||||
|
nimbus.rpcServer.start()
|
||||||
|
nimbus.p2pServer.start()
|
||||||
|
nimbus.state = Running
|
||||||
|
result = nimbus
|
||||||
|
|
||||||
|
proc stop*(nimbus: NimbusObject) {.async.} =
|
||||||
|
echo "Graceful shutdown"
|
||||||
|
nimbus.rpcServer.stop()
|
||||||
|
|
||||||
|
proc process*(nimbus: NimbusObject) =
|
||||||
|
if nimbus.state == Running:
|
||||||
|
when not defined(windows):
|
||||||
|
proc signalBreak(udata: pointer) =
|
||||||
|
nimbus.state = Stopping
|
||||||
|
# Adding SIGINT, SIGTERM handlers
|
||||||
|
discard addSignal(SIGINT, signalBreak)
|
||||||
|
discard addSignal(SIGTERM, signalBreak)
|
||||||
|
|
||||||
|
# Main loop
|
||||||
|
while nimbus.state == Running:
|
||||||
|
poll()
|
||||||
|
|
||||||
|
# Stop loop
|
||||||
|
waitFor nimbus.stop()
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
var message: string
|
var message: string
|
||||||
|
|
||||||
|
## Pring Nimbus header
|
||||||
|
echo NimbusHeader
|
||||||
|
|
||||||
|
## Processing command line arguments
|
||||||
if processArguments(message) != ConfigStatus.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
|
||||||
|
quit(QuitSuccess)
|
||||||
|
|
||||||
var disc4: Discovery4Service
|
var nimbus = start()
|
||||||
if disc4.init() != ServiceStatus.Success:
|
nimbus.process()
|
||||||
quit(QuitFailure)
|
|
||||||
if disc4.configure() != ServiceStatus.Success:
|
|
||||||
echo disc4.errorMessage()
|
|
||||||
quit(QuitFailure)
|
|
||||||
if disc4.start() != ServiceStatus.Success:
|
|
||||||
echo disc4.errorMessage()
|
|
||||||
quit(QuitFailure)
|
|
||||||
|
|
|
@ -1,88 +0,0 @@
|
||||||
# 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 eth_p2p, 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
|
|
|
@ -1,55 +0,0 @@
|
||||||
# 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
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
# 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 eth-rpc/server, nimcrypto
|
||||||
|
import ../config
|
||||||
|
|
||||||
|
proc setupCommonRPC*(server: RpcServer) =
|
||||||
|
server.rpc("web3_clientVersion") do() -> string:
|
||||||
|
result = NimbusIdent
|
||||||
|
|
||||||
|
server.rpc("web3_sha3") do(data: string) -> string:
|
||||||
|
var rawdata = fromHex(data)
|
||||||
|
result = "0x" & $keccak_256.digest(rawdata)
|
|
@ -0,0 +1,15 @@
|
||||||
|
# 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 nimcrypto, eth-rpc/server, eth_p2p
|
||||||
|
import ../config
|
||||||
|
|
||||||
|
proc setupP2PRPC*(server: P2PServer, rpcsrv: RpcServer) =
|
||||||
|
rpcsrv.rpc("net_version") do() -> int:
|
||||||
|
let conf = getConfiguration()
|
||||||
|
result = conf.net.networkId
|
Loading…
Reference in New Issue