Add ENode type/procedures/tests.

Fixed header with proper licenses.
Fixed nimble to allow ENode tests.
This commit is contained in:
cheatfate 2018-04-30 20:40:04 +03:00
parent d567911c14
commit 950e0c2d46
7 changed files with 282 additions and 17 deletions

View File

@ -3,9 +3,10 @@
# (c) Copyright 2018
# Status Research & Development GmbH
#
# See the file "LICENSE", included in this
# distribution, for details about the copyright.
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
#
import ethp2p/ecc, ethp2p/ecies, ethp2p/auth, ethp2p/hexdump
export ecc, ecies, auth, hexdump
import ethp2p/ecies, ethp2p/auth, ethp2p/hexdump, ethp2p/enode
export ecies, auth, enode, hexdump

View File

@ -8,13 +8,13 @@ license = "MIT"
skipDirs = @["tests", "Nim"]
requires "nim > 0.18.0",
"rlp >= 1.0.1",
"nimcrypto >= 0.3.0",
"secp256k1 >= 0.1.0",
"eth_keys",
"ranges",
"ttmath",
"https://github.com/status-im/byteutils"
"rlp >= 1.0.1",
"https://github.com/cheatfate/nimcrypto",
"secp256k1 >= 0.1.0",
"eth_keys",
"ranges",
"ttmath",
"https://github.com/status-im/byteutils"
proc runTest(name: string, lang = "c") = exec "nim " & lang & " -r tests/" & name
@ -22,4 +22,5 @@ task test, "Runs the test suite":
runTest "testecies"
runTest "testauth"
runTest "testcrypt"
runTest "testenode"
runTest("tdiscovery", "cpp")

View File

@ -3,8 +3,9 @@
# (c) Copyright 2018
# Status Research & Development GmbH
#
# See the file "LICENSE", included in this
# distribution, for details about the copyright.
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
#
## This module implements Ethereum authentication

View File

@ -3,8 +3,9 @@
# (c) Copyright 2018
# Status Research & Development GmbH
#
# See the file "LICENSE", included in this
# distribution, for details about the copyright.
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
#
## This module implements ECIES method encryption/decryption.

161
ethp2p/enode.nim Normal file
View File

@ -0,0 +1,161 @@
#
# Ethereum P2P
# (c) Copyright 2018
# Status Research & Development GmbH
#
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
#
import uri, eth_keys, strutils, net
type
ENodeStatus* = enum
## ENode status codes
Success, ## Conversion operation succeed
IncorrectNodeId, ## Incorrect public key supplied
IncorrectScheme, ## Incorrect URI scheme supplied
IncorrectIP, ## Incorrect IP address supplied
IncorrectPort, ## Incorrect TCP port supplied
IncorrectDiscPort, ## Incorrect UDP discovery port supplied
IncorrectUri, ## Incorrect URI supplied
IncompleteENode ## Incomplete ENODE object
Address* = object
## Network address object
ip*: IpAddress ## IPv4/IPv6 address
udpPort*: Port ## UDP discovery port number
tcpPort*: Port ## TCP port number
ENode* = object
## ENode object
pubkey*: PublicKey ## Node public key
address*: Address ## Node address
ENodeException* = object of Exception
proc raiseENodeError(status: ENodeStatus) =
if status == IncorrectIP:
raise newException(ENodeException, "Incorrect IP address")
elif status == IncorrectPort:
raise newException(ENodeException, "Incorrect port number")
elif status == IncorrectDiscPort:
raise newException(ENodeException, "Incorrect discovery port number")
elif status == IncorrectUri:
raise newException(ENodeException, "Incorrect URI")
elif status == IncorrectScheme:
raise newException(ENodeException, "Incorrect scheme")
elif status == IncorrectNodeId:
raise newException(ENodeException, "Incorrect node id")
elif status == IncompleteENode:
raise newException(ENodeException, "Incomplete enode")
proc initENode*(e: string, node: var ENode): ENodeStatus =
## Initialize ENode ``node`` from URI string ``uri``.
var
uport: int = 0
tport: int = 0
uri: Uri = initUri()
data: string
if len(e) == 0:
return IncorrectUri
parseUri(e, uri)
if len(uri.scheme) == 0 or uri.scheme.toLowerAscii() != "enode":
return IncorrectScheme
if len(uri.username) != 128:
return IncorrectNodeId
for i in uri.username:
if i notin {'A'..'F', 'a'..'f', '0'..'9'}:
return IncorrectNodeId
if len(uri.password) != 0 or len(uri.path) != 0 or len(uri.anchor) != 0:
return IncorrectUri
if len(uri.hostname) == 0:
return IncorrectIP
try:
if len(uri.port) == 0:
return IncorrectPort
tport = parseInt(uri.port)
if tport <= 0 or tport > 65535:
return IncorrectPort
except:
return IncorrectPort
if len(uri.query) > 0:
if not uri.query.toLowerAscii().startsWith("discport="):
return IncorrectDiscPort
try:
uport = parseInt(uri.query[9..^1])
if uport <= 0 or uport > 65535:
return IncorrectDiscPort
except:
return IncorrectDiscPort
else:
uport = tport
try:
data = parseHexStr(uri.username)
if recoverPublicKey(cast[seq[byte]](data),
node.pubkey) != EthKeysStatus.Success:
return IncorrectNodeId
except:
return IncorrectNodeId
try:
node.address.ip = parseIpAddress(uri.hostname)
except:
zeroMem(addr node.pubkey, KeyLength * 2)
return IncorrectIP
node.address.tcpPort = Port(tport)
node.address.udpPort = Port(uport)
result = Success
proc initENode*(uri: string): ENode {.inline.} =
## Returns ENode object from URI string ``uri``.
let res = initENode(uri, result)
if res != Success:
raiseENodeError(res)
proc isCorrect*(n: ENode): bool =
## Returns ``true`` if ENode ``n`` is properly filled.
if n.address.ip.family notin {IpAddressFamily.IPv4, IpAddressFamily.IPv6}:
return false
if n.address.tcpPort == Port(0):
return false
if n.address.udpPort == Port(0):
return false
result = false
for i in n.pubkey.data:
if i != 0x00'u8:
result = true
break
proc `$`*(n: ENode): string =
## Returns string representation of ENode.
var ipaddr: string
if not isCorrect(n):
raiseENodeError(IncompleteENode)
if n.address.ip.family == IpAddressFamily.IPv4:
ipaddr = $(n.address.ip)
else:
ipaddr = "[" & $(n.address.ip) & "]"
result = newString(0)
result.add("enode://")
result.add($n.pubkey)
result.add("@")
result.add(ipaddr)
result.add(":")
result.add($int(n.address.tcpPort))
if uint16(n.address.udpPort) != uint16(n.address.tcpPort):
result.add("?")
result.add("discport=")
result.add($int(n.address.udpPort))

View File

@ -3,8 +3,9 @@
# (c) Copyright 2018
# Status Research & Development GmbH
#
# See the file "LICENSE", included in this
# distribution, for details about the copyright.
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
#
## This module implements RLPx cryptography

99
tests/testenode.nim Normal file
View File

@ -0,0 +1,99 @@
#
# Ethereum P2P
# (c) Copyright 2018
# Status Research & Development GmbH
#
# Licensed under either of
# Apache License, version 2.0, (LICENSE-APACHEv2)
# MIT license (LICENSE-MIT)
import unittest, net, ethp2p/enode
suite "ENode":
test "Go-Ethereum tests":
const enodes = [
"http://foobar",
"enode://01010101@123.124.125.126:3",
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@hostname:3",
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:foo",
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:3?discport=foo",
"01010101",
"enode://01010101",
"://foo",
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150",
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[::]:52150",
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@[2001:db8:3c4d:15::abcd:ef12]:52150",
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@127.0.0.1:52150?discport=22334",
]
const results = [
IncorrectScheme,
IncorrectNodeId,
IncorrectIP,
IncorrectPort,
IncorrectDiscPort,
IncorrectScheme,
IncorrectNodeId,
IncorrectScheme,
ENodeStatus.Success,
ENodeStatus.Success,
ENodeStatus.Success,
ENodeStatus.Success
]
for index in 0..<len(enodes):
var node: ENode
let res = initENode(enodes[index], node)
check res == results[index]
if res == ENodeStatus.Success:
check enodes[index] == $node
test "Custom validation tests":
const enodes = [
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@256.0.0.1:52150",
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@1.256.0.1:52150",
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@1.1.256.1:52150",
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@1.1.1.256:52150",
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439:@1.1.1.255:52150",
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439:bar@1.1.1.255:52150",
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@1.1.1.255:-1",
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@1.1.1.255:65536",
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@1.1.1.255:1024?discport=-1",
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@1.1.1.255:1024?discport=65536",
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@1.1.1.255:1024?discport=65535#bar",
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a439@",
"enode://1dd9d65c4552b5eb43d5ad55a2ee3f56c6cbc1c64a5c8d659f51fcd51bace24351232b8d7821617d2b29b54b81cdefb9b3e9c37d7fd5f63270bcc9e1a6f6a43Z@1.1.1.1:25?discport=22"
]
const results = [
IncorrectIP,
IncorrectIP,
IncorrectIP,
IncorrectIP,
Success,
IncorrectUri,
IncorrectPort,
IncorrectPort,
IncorrectDiscPort,
IncorrectDiscPort,
IncorrectUri,
IncorrectIP,
IncorrectNodeId
]
for index in 0..<len(enodes):
var node: ENode
let res = initENode(enodes[index], node)
check res == results[index]
test "isCorrect() tests":
var node: ENode
check isCorrect(node) == false
node.address.ip.family = IpAddressFamily.IPv4
check isCorrect(node) == false
node.address.tcpPort = Port(25)
check isCorrect(node) == false
node.address.udpPort = Port(25)
check isCorrect(node) == false
node.pubkey.data[0] = 1'u8
check isCorrect(node) == true