mirror of https://github.com/status-im/nim-eth.git
Remove portal wire code which was moved to nimbus-eth1 repo (#370)
This commit is contained in:
parent
2dfd352fd0
commit
41127eaee8
|
@ -77,7 +77,6 @@ build_script:
|
|||
test_script:
|
||||
- nimble test
|
||||
- nimble build_dcli
|
||||
- nimble build_portalcli
|
||||
|
||||
deploy: off
|
||||
|
||||
|
|
|
@ -237,4 +237,3 @@ jobs:
|
|||
nimble install -y --depsOnly
|
||||
nimble test
|
||||
nimble build_dcli
|
||||
nimble build_portalcli
|
||||
|
|
|
@ -47,4 +47,3 @@ script:
|
|||
- nimble install -y --depsOnly
|
||||
- nimble test
|
||||
- nimble build_dcli
|
||||
- nimble build_portalcli
|
||||
|
|
|
@ -46,9 +46,6 @@ task test_discv5, "Run discovery v5 tests":
|
|||
task test_discv4, "Run discovery v4 tests":
|
||||
runTest("tests/p2p/test_discovery")
|
||||
|
||||
task test_portal, "Run Portal network tests":
|
||||
runTest("tests/p2p/all_portal_tests")
|
||||
|
||||
task test_p2p, "Run p2p tests":
|
||||
runTest("tests/p2p/all_tests")
|
||||
|
||||
|
@ -89,6 +86,3 @@ task test_discv5_full, "Run discovery v5 and its dependencies tests":
|
|||
|
||||
task build_dcli, "Build dcli":
|
||||
buildBinary("eth/p2p/discoveryv5/dcli")
|
||||
|
||||
task build_portalcli, "Build portalcli":
|
||||
buildBinary("eth/p2p/portal/portalcli")
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
# Portal Network Wire Protocol
|
||||
## Introduction
|
||||
The `eth/p2p/portal` directory holds a Nim implementation of the
|
||||
[Portal Network Wire Protocol](https://github.com/ethereum/stateless-ethereum-specs/blob/master/state-network.md#wire-protocol).
|
||||
|
||||
Both specification, at above link, and implementations are still WIP.
|
||||
|
||||
The protocol builds on top of the Node Discovery v5.1 protocol its `talkreq` and
|
||||
`talkresp` messages.
|
||||
|
||||
For further information on the Nim implementation of the Node Discovery v5.1
|
||||
protocol check out the [discv5](../../../doc/discv5.md) page.
|
||||
|
||||
## Test suite
|
||||
To run the test suite specifically for the Portal wire protocol, run following
|
||||
command:
|
||||
```sh
|
||||
# Install required modules
|
||||
nimble install
|
||||
# Run only Portal tests
|
||||
nimble test_portal
|
||||
```
|
||||
|
||||
## portalcli
|
||||
This is a small command line application that allows you to run a
|
||||
Discovery v5.1 + Portal node.
|
||||
|
||||
*Note:* Its objective is only to test the protocol wire component, not to actually
|
||||
serve content. This means it will always return empty lists on content requests.
|
||||
Perhaps in the future some hardcoded data could added and maybe some test vectors
|
||||
can be created in such form.
|
||||
|
||||
The `portalcli` application allows you to either run a node, or to specifically
|
||||
send one of the message types, wait for the response, and then shut down.
|
||||
|
||||
### Example usage
|
||||
```sh
|
||||
# Install required modules
|
||||
# Make sure you have the latest modules, do NOT trust nimble on this.
|
||||
nimble install
|
||||
# Build portalcli
|
||||
nimble build_portalcli
|
||||
# See all options
|
||||
./eth/p2p/portal/portalcli --help
|
||||
# Example command: Ping another node
|
||||
./eth/p2p/portal/portalcli ping enr:<base64 encoding of ENR>
|
||||
# Example command: Run discovery + portal node
|
||||
./eth/p2p/portal/portalcli --log-level:debug --bootnode:enr:<base64 encoding of ENR>
|
|
@ -1,153 +0,0 @@
|
|||
# nim-eth - Portal Network- Message types
|
||||
# Copyright (c) 2021 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
# As per spec:
|
||||
# https://github.com/ethereum/stateless-ethereum-specs/blob/master/state-network.md#wire-protocol
|
||||
|
||||
{.push raises: [Defect].}
|
||||
|
||||
import
|
||||
stint, stew/[results, objects],
|
||||
../../ssz/ssz_serialization
|
||||
|
||||
export ssz_serialization, stint
|
||||
|
||||
type
|
||||
ByteList* = List[byte, 2048]
|
||||
|
||||
MessageKind* = enum
|
||||
unused = 0x00
|
||||
|
||||
ping = 0x01
|
||||
pong = 0x02
|
||||
findnode = 0x03
|
||||
nodes = 0x04
|
||||
findcontent = 0x05
|
||||
foundcontent = 0x06
|
||||
advertise = 0x07
|
||||
requestproofs = 0x08
|
||||
|
||||
PingMessage* = object
|
||||
enrSeq*: uint64
|
||||
dataRadius*: UInt256
|
||||
|
||||
PongMessage* = object
|
||||
enrSeq*: uint64
|
||||
dataRadius*: UInt256
|
||||
|
||||
FindNodeMessage* = object
|
||||
distances*: List[uint16, 256]
|
||||
|
||||
NodesMessage* = object
|
||||
total*: uint8
|
||||
enrs*: List[ByteList, 32] # ByteList here is the rlp encoded ENR. This could
|
||||
# also be limited to 300 bytes instead of 2048
|
||||
|
||||
FindContentMessage* = object
|
||||
contentKey*: ByteList
|
||||
|
||||
FoundContentMessage* = object
|
||||
enrs*: List[ByteList, 32]
|
||||
payload*: ByteList
|
||||
|
||||
AdvertiseMessage* = List[ByteList, 32] # No container, heh...
|
||||
|
||||
# This would be more consistent with the other messages
|
||||
# AdvertiseMessage* = object
|
||||
# contentKeys*: List[ByteList, 32]
|
||||
|
||||
RequestProofsMessage* = object
|
||||
connectionId*: List[byte, 4]
|
||||
contentKeys*: List[ByteList, 32]
|
||||
|
||||
Message* = object
|
||||
case kind*: MessageKind
|
||||
of ping:
|
||||
ping*: PingMessage
|
||||
of pong:
|
||||
pong*: PongMessage
|
||||
of findnode:
|
||||
findNode*: FindNodeMessage
|
||||
of nodes:
|
||||
nodes*: NodesMessage
|
||||
of findcontent:
|
||||
findcontent*: FindContentMessage
|
||||
of foundcontent:
|
||||
foundcontent*: FoundContentMessage
|
||||
of advertise:
|
||||
advertise*: AdvertiseMessage
|
||||
of requestproofs:
|
||||
requestproofs*: RequestProofsMessage
|
||||
else:
|
||||
discard
|
||||
|
||||
SomeMessage* =
|
||||
PingMessage or PongMessage or
|
||||
FindNodeMessage or NodesMessage or
|
||||
FindContentMessage or FoundContentMessage or
|
||||
AdvertiseMessage or RequestProofsMessage
|
||||
|
||||
template messageKind*(T: typedesc[SomeMessage]): MessageKind =
|
||||
when T is PingMessage: ping
|
||||
elif T is PongMessage: pong
|
||||
elif T is FindNodeMessage: findNode
|
||||
elif T is NodesMessage: nodes
|
||||
elif T is FindContentMessage: findcontent
|
||||
elif T is FoundContentMessage: foundcontent
|
||||
elif T is AdvertiseMessage: advertise
|
||||
elif T is RequestProofsMessage: requestproofs
|
||||
|
||||
template toSszType*(x: auto): auto =
|
||||
mixin toSszType
|
||||
|
||||
when x is UInt256: toBytesLE(x)
|
||||
else: x
|
||||
|
||||
func fromSszBytes*(T: type UInt256, data: openArray[byte]):
|
||||
T {.raises: [MalformedSszError, Defect].} =
|
||||
if data.len != sizeof(result):
|
||||
raiseIncorrectSize T
|
||||
|
||||
T.fromBytesLE(data)
|
||||
|
||||
proc encodeMessage*[T: SomeMessage](m: T): seq[byte] =
|
||||
ord(messageKind(T)).byte & SSZ.encode(m)
|
||||
|
||||
proc decodeMessage*(body: openarray[byte]): Result[Message, cstring] =
|
||||
# Decodes to the specific `Message` type.
|
||||
if body.len < 1:
|
||||
return err("No message data")
|
||||
|
||||
var kind: MessageKind
|
||||
if not checkedEnumAssign(kind, body[0]):
|
||||
return err("Invalid message type")
|
||||
|
||||
var message = Message(kind: kind)
|
||||
|
||||
try:
|
||||
case kind
|
||||
of unused: return err("Invalid message type")
|
||||
of ping:
|
||||
message.ping = SSZ.decode(body.toOpenArray(1, body.high), PingMessage)
|
||||
of pong:
|
||||
message.pong = SSZ.decode(body.toOpenArray(1, body.high), PongMessage)
|
||||
of findNode:
|
||||
message.findNode = SSZ.decode(body.toOpenArray(1, body.high), FindNodeMessage)
|
||||
of nodes:
|
||||
message.nodes = SSZ.decode(body.toOpenArray(1, body.high), NodesMessage)
|
||||
of findcontent:
|
||||
message.findcontent = SSZ.decode(body.toOpenArray(1, body.high), FindContentMessage)
|
||||
of foundcontent:
|
||||
message.foundcontent = SSZ.decode(body.toOpenArray(1, body.high), FoundContentMessage)
|
||||
of advertise:
|
||||
message.advertise = SSZ.decode(body.toOpenArray(1, body.high), AdvertiseMessage)
|
||||
of requestproofs:
|
||||
message.requestproofs = SSZ.decode(body.toOpenArray(1, body.high), RequestProofsMessage)
|
||||
except SszError:
|
||||
return err("Invalid message encoding")
|
||||
|
||||
ok(message)
|
|
@ -1,221 +0,0 @@
|
|||
# nim-eth - Portal Network
|
||||
# Copyright (c) 2021 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
import
|
||||
std/[options, strutils, tables],
|
||||
confutils, confutils/std/net, chronicles, chronicles/topics_registry,
|
||||
chronos, metrics, metrics/chronos_httpserver, stew/byteutils,
|
||||
../../keys, ../../net/nat,
|
||||
".."/discoveryv5/[enr, node], ".."/discoveryv5/protocol as discv5_protocol,
|
||||
./messages, ./protocol as portal_protocol
|
||||
|
||||
type
|
||||
PortalCmd* = enum
|
||||
noCommand
|
||||
ping
|
||||
findnode
|
||||
findcontent
|
||||
|
||||
DiscoveryConf* = object
|
||||
logLevel* {.
|
||||
defaultValue: LogLevel.DEBUG
|
||||
desc: "Sets the log level"
|
||||
name: "log-level" .}: LogLevel
|
||||
|
||||
udpPort* {.
|
||||
defaultValue: 9009
|
||||
desc: "UDP listening port"
|
||||
name: "udp-port" .}: uint16
|
||||
|
||||
listenAddress* {.
|
||||
defaultValue: defaultListenAddress(config)
|
||||
desc: "Listening address for the Discovery v5 traffic"
|
||||
name: "listen-address" }: ValidIpAddress
|
||||
|
||||
bootnodes* {.
|
||||
desc: "ENR URI of node to bootstrap discovery with. Argument may be repeated"
|
||||
name: "bootnode" .}: seq[enr.Record]
|
||||
|
||||
nat* {.
|
||||
desc: "Specify method to use for determining public address. " &
|
||||
"Must be one of: any, none, upnp, pmp, extip:<IP>"
|
||||
defaultValue: NatConfig(hasExtIp: false, nat: NatAny)
|
||||
name: "nat" .}: NatConfig
|
||||
|
||||
enrAutoUpdate* {.
|
||||
defaultValue: false
|
||||
desc: "Discovery can automatically update its ENR with the IP address " &
|
||||
"and UDP port as seen by other nodes it communicates with. " &
|
||||
"This option allows to enable/disable this functionality"
|
||||
name: "enr-auto-update" .}: bool
|
||||
|
||||
nodeKey* {.
|
||||
desc: "P2P node private key as hex",
|
||||
defaultValue: PrivateKey.random(keys.newRng()[])
|
||||
name: "nodekey" .}: PrivateKey
|
||||
|
||||
metricsEnabled* {.
|
||||
defaultValue: false
|
||||
desc: "Enable the metrics server"
|
||||
name: "metrics" .}: bool
|
||||
|
||||
metricsAddress* {.
|
||||
defaultValue: defaultAdminListenAddress(config)
|
||||
desc: "Listening address of the metrics server"
|
||||
name: "metrics-address" .}: ValidIpAddress
|
||||
|
||||
metricsPort* {.
|
||||
defaultValue: 8008
|
||||
desc: "Listening HTTP port of the metrics server"
|
||||
name: "metrics-port" .}: Port
|
||||
|
||||
case cmd* {.
|
||||
command
|
||||
defaultValue: noCommand }: PortalCmd
|
||||
of noCommand:
|
||||
discard
|
||||
of ping:
|
||||
pingTarget* {.
|
||||
argument
|
||||
desc: "ENR URI of the node to a send ping message"
|
||||
name: "node" .}: Node
|
||||
of findnode:
|
||||
distance* {.
|
||||
defaultValue: 255
|
||||
desc: "Distance parameter for the findNode message"
|
||||
name: "distance" .}: uint16
|
||||
# TODO: Order here matters as else the help message does not show all the
|
||||
# information, see: https://github.com/status-im/nim-confutils/issues/15
|
||||
findNodeTarget* {.
|
||||
argument
|
||||
desc: "ENR URI of the node to send a findNode message"
|
||||
name: "node" .}: Node
|
||||
of findcontent:
|
||||
findContentTarget* {.
|
||||
argument
|
||||
desc: "ENR URI of the node to send a findContent message"
|
||||
name: "node" .}: Node
|
||||
|
||||
func defaultListenAddress*(conf: DiscoveryConf): ValidIpAddress =
|
||||
(static ValidIpAddress.init("0.0.0.0"))
|
||||
|
||||
func defaultAdminListenAddress*(conf: DiscoveryConf): ValidIpAddress =
|
||||
(static ValidIpAddress.init("127.0.0.1"))
|
||||
|
||||
proc parseCmdArg*(T: type enr.Record, p: TaintedString): T =
|
||||
if not fromURI(result, p):
|
||||
raise newException(ConfigurationError, "Invalid ENR")
|
||||
|
||||
proc completeCmdArg*(T: type enr.Record, val: TaintedString): seq[string] =
|
||||
return @[]
|
||||
|
||||
proc parseCmdArg*(T: type Node, p: TaintedString): T =
|
||||
var record: enr.Record
|
||||
if not fromURI(record, p):
|
||||
raise newException(ConfigurationError, "Invalid ENR")
|
||||
|
||||
let n = newNode(record)
|
||||
if n.isErr:
|
||||
raise newException(ConfigurationError, $n.error)
|
||||
|
||||
if n[].address.isNone():
|
||||
raise newException(ConfigurationError, "ENR without address")
|
||||
|
||||
n[]
|
||||
|
||||
proc completeCmdArg*(T: type Node, val: TaintedString): seq[string] =
|
||||
return @[]
|
||||
|
||||
proc parseCmdArg*(T: type PrivateKey, p: TaintedString): T =
|
||||
try:
|
||||
result = PrivateKey.fromHex(string(p)).tryGet()
|
||||
except CatchableError:
|
||||
raise newException(ConfigurationError, "Invalid private key")
|
||||
|
||||
proc completeCmdArg*(T: type PrivateKey, val: TaintedString): seq[string] =
|
||||
return @[]
|
||||
|
||||
proc discover(d: discv5_protocol.Protocol) {.async.} =
|
||||
while true:
|
||||
let discovered = await d.queryRandom()
|
||||
info "Lookup finished", nodes = discovered.len
|
||||
await sleepAsync(30.seconds)
|
||||
|
||||
proc run(config: DiscoveryConf) =
|
||||
let
|
||||
rng = newRng()
|
||||
bindIp = config.listenAddress
|
||||
udpPort = Port(config.udpPort)
|
||||
# TODO: allow for no TCP port mapping!
|
||||
(extIp, _, extUdpPort) = setupAddress(config.nat,
|
||||
config.listenAddress, udpPort, udpPort, "dcli")
|
||||
|
||||
let d = newProtocol(config.nodeKey,
|
||||
extIp, none(Port), extUdpPort,
|
||||
bootstrapRecords = config.bootnodes,
|
||||
bindIp = bindIp, bindPort = udpPort,
|
||||
enrAutoUpdate = config.enrAutoUpdate,
|
||||
rng = rng)
|
||||
|
||||
d.open()
|
||||
|
||||
let portal = PortalProtocol.new(d)
|
||||
|
||||
if config.metricsEnabled:
|
||||
let
|
||||
address = config.metricsAddress
|
||||
port = config.metricsPort
|
||||
notice "Starting metrics HTTP server",
|
||||
url = "http://" & $address & ":" & $port & "/metrics"
|
||||
try:
|
||||
chronos_httpserver.startMetricsHttpServer($address, port)
|
||||
except CatchableError as exc: raise exc
|
||||
except Exception as exc: raiseAssert exc.msg # TODO fix metrics
|
||||
|
||||
case config.cmd
|
||||
of ping:
|
||||
let pong = waitFor portal.ping(config.pingTarget)
|
||||
|
||||
if pong.isOk():
|
||||
echo pong.get()
|
||||
else:
|
||||
echo pong.error
|
||||
of findnode:
|
||||
let distances = List[uint16, 256](@[config.distance])
|
||||
let nodes = waitFor portal.findNode(config.findNodeTarget, distances)
|
||||
|
||||
if nodes.isOk():
|
||||
echo nodes.get()
|
||||
else:
|
||||
echo nodes.error
|
||||
of findcontent:
|
||||
proc random(T: type UInt256, rng: var BrHmacDrbgContext): T =
|
||||
var key: UInt256
|
||||
brHmacDrbgGenerate(addr rng, addr key, csize_t(sizeof(key)))
|
||||
|
||||
key
|
||||
|
||||
# For now just random content keys
|
||||
let contentKey = ByteList(@(UInt256.random(rng[]).toBytes()))
|
||||
let foundContent = waitFor portal.findContent(config.findContentTarget,
|
||||
contentKey)
|
||||
|
||||
if foundContent.isOk():
|
||||
echo foundContent.get()
|
||||
else:
|
||||
echo foundContent.error
|
||||
|
||||
of noCommand:
|
||||
d.start()
|
||||
waitfor(discover(d))
|
||||
|
||||
when isMainModule:
|
||||
let config = DiscoveryConf.load()
|
||||
|
||||
setLogLevel(config.logLevel)
|
||||
|
||||
run(config)
|
|
@ -1,164 +0,0 @@
|
|||
# nim-eth - Portal Network
|
||||
# Copyright (c) 2021 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
{.push raises: [Defect].}
|
||||
|
||||
import
|
||||
stew/[results, byteutils], chronicles,
|
||||
../../rlp,
|
||||
../discoveryv5/[protocol, node],
|
||||
./messages
|
||||
|
||||
export messages
|
||||
|
||||
logScope:
|
||||
topics = "portal"
|
||||
|
||||
const
|
||||
PortalProtocolId* = "portal".toBytes()
|
||||
|
||||
type
|
||||
PortalProtocol* = ref object of TalkProtocol
|
||||
baseProtocol*: protocol.Protocol
|
||||
dataRadius*: UInt256
|
||||
|
||||
proc handlePing(p: PortalProtocol, ping: PingMessage):
|
||||
seq[byte] =
|
||||
let p = PongMessage(enrSeq: p.baseProtocol.localNode.record.seqNum,
|
||||
dataRadius: p.dataRadius)
|
||||
|
||||
encodeMessage(p)
|
||||
|
||||
proc handleFindNode(p: PortalProtocol, fn: FindNodeMessage): seq[byte] =
|
||||
if fn.distances.len == 0:
|
||||
let enrs = List[ByteList, 32](@[])
|
||||
encodeMessage(NodesMessage(total: 1, enrs: enrs))
|
||||
elif fn.distances.contains(0):
|
||||
# A request for our own record.
|
||||
let enr = ByteList(rlp.encode(p.baseProtocol.localNode.record))
|
||||
encodeMessage(NodesMessage(total: 1, enrs: List[ByteList, 32](@[enr])))
|
||||
else:
|
||||
# TODO: Not implemented for now, sending empty back.
|
||||
let enrs = List[ByteList, 32](@[])
|
||||
encodeMessage(NodesMessage(total: 1, enrs: enrs))
|
||||
|
||||
proc handleFindContent(p: PortalProtocol, ping: FindContentMessage): seq[byte] =
|
||||
# TODO: Neither payload nor enrs implemented, sending empty back.
|
||||
let
|
||||
enrs = List[ByteList, 32](@[])
|
||||
payload = ByteList(@[])
|
||||
encodeMessage(FoundContentMessage(enrs: enrs, payload: payload))
|
||||
|
||||
proc handleAdvertise(p: PortalProtocol, ping: AdvertiseMessage): seq[byte] =
|
||||
# TODO: Not implemented
|
||||
let
|
||||
connectionId = List[byte, 4](@[])
|
||||
contentKeys = List[ByteList, 32](@[])
|
||||
encodeMessage(RequestProofsMessage(connectionId: connectionId,
|
||||
contentKeys: contentKeys))
|
||||
|
||||
proc messageHandler*(protocol: TalkProtocol, request: seq[byte]): seq[byte] =
|
||||
doAssert(protocol of PortalProtocol)
|
||||
|
||||
let p = PortalProtocol(protocol)
|
||||
|
||||
let decoded = decodeMessage(request)
|
||||
if decoded.isOk():
|
||||
let message = decoded.get()
|
||||
trace "Received message response", kind = message.kind
|
||||
case message.kind
|
||||
of MessageKind.ping:
|
||||
p.handlePing(message.ping)
|
||||
of MessageKind.findnode:
|
||||
p.handleFindNode(message.findNode)
|
||||
of MessageKind.findcontent:
|
||||
p.handleFindContent(message.findcontent)
|
||||
of MessageKind.advertise:
|
||||
p.handleAdvertise(message.advertise)
|
||||
else:
|
||||
@[]
|
||||
else:
|
||||
@[]
|
||||
|
||||
proc new*(T: type PortalProtocol, baseProtocol: protocol.Protocol,
|
||||
dataRadius = UInt256.high()): T =
|
||||
let proto = PortalProtocol(
|
||||
protocolHandler: messageHandler,
|
||||
baseProtocol: baseProtocol,
|
||||
dataRadius: dataRadius)
|
||||
|
||||
proto.baseProtocol.registerTalkProtocol(PortalProtocolId, proto).expect(
|
||||
"Only one protocol should have this id")
|
||||
|
||||
return proto
|
||||
|
||||
proc ping*(p: PortalProtocol, dst: Node):
|
||||
Future[DiscResult[PongMessage]] {.async.} =
|
||||
let ping = PingMessage(enrSeq: p.baseProtocol.localNode.record.seqNum,
|
||||
dataRadius: p.dataRadius)
|
||||
|
||||
# TODO: This send and response handling code could be more generalized for the
|
||||
# different message types.
|
||||
trace "Send message request", dstId = dst.id, kind = MessageKind.ping
|
||||
let talkresp = await talkreq(p.baseProtocol, dst, PortalProtocolId,
|
||||
encodeMessage(ping))
|
||||
|
||||
if talkresp.isOk():
|
||||
let decoded = decodeMessage(talkresp.get().response)
|
||||
if decoded.isOk():
|
||||
let message = decoded.get()
|
||||
if message.kind == pong:
|
||||
return ok(message.pong)
|
||||
else:
|
||||
return err("Invalid message response received")
|
||||
else:
|
||||
return err(decoded.error)
|
||||
else:
|
||||
return err(talkresp.error)
|
||||
|
||||
proc findNode*(p: PortalProtocol, dst: Node, distances: List[uint16, 256]):
|
||||
Future[DiscResult[NodesMessage]] {.async.} =
|
||||
let fn = FindNodeMessage(distances: distances)
|
||||
|
||||
trace "Send message request", dstId = dst.id, kind = MessageKind.findnode
|
||||
let talkresp = await talkreq(p.baseProtocol, dst, PortalProtocolId,
|
||||
encodeMessage(fn))
|
||||
|
||||
if talkresp.isOk():
|
||||
let decoded = decodeMessage(talkresp.get().response)
|
||||
if decoded.isOk():
|
||||
let message = decoded.get()
|
||||
if message.kind == nodes:
|
||||
# TODO: Verify nodes here
|
||||
return ok(message.nodes)
|
||||
else:
|
||||
return err("Invalid message response received")
|
||||
else:
|
||||
return err(decoded.error)
|
||||
else:
|
||||
return err(talkresp.error)
|
||||
|
||||
proc findContent*(p: PortalProtocol, dst: Node, contentKey: ByteList):
|
||||
Future[DiscResult[FoundContentMessage]] {.async.} =
|
||||
let fc = FindContentMessage(contentKey: contentKey)
|
||||
|
||||
trace "Send message request", dstId = dst.id, kind = MessageKind.findcontent
|
||||
let talkresp = await talkreq(p.baseProtocol, dst, PortalProtocolId,
|
||||
encodeMessage(fc))
|
||||
|
||||
if talkresp.isOk():
|
||||
let decoded = decodeMessage(talkresp.get().response)
|
||||
if decoded.isOk():
|
||||
let message = decoded.get()
|
||||
if message.kind == foundcontent:
|
||||
return ok(message.foundcontent)
|
||||
else:
|
||||
return err("Invalid message response received")
|
||||
else:
|
||||
return err(decoded.error)
|
||||
else:
|
||||
return err(talkresp.error)
|
|
@ -1,5 +0,0 @@
|
|||
{.used.}
|
||||
|
||||
import
|
||||
./test_portal_encoding,
|
||||
./test_portal
|
|
@ -1,6 +1,5 @@
|
|||
import
|
||||
./all_discv5_tests,
|
||||
./all_portal_tests,
|
||||
./test_auth,
|
||||
./test_crypt,
|
||||
./test_discovery,
|
||||
|
|
|
@ -1,103 +0,0 @@
|
|||
# nim-eth - Portal Network
|
||||
# Copyright (c) 2021 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
{.used.}
|
||||
|
||||
import
|
||||
chronos, testutils/unittests,
|
||||
../../eth/keys, # for rng
|
||||
../../eth/p2p/discoveryv5/protocol as discv5_protocol,
|
||||
../../eth/p2p/portal/protocol as portal_protocol,
|
||||
./discv5_test_helper
|
||||
|
||||
proc random(T: type UInt256, rng: var BrHmacDrbgContext): T =
|
||||
var key: UInt256
|
||||
brHmacDrbgGenerate(addr rng, addr key, csize_t(sizeof(key)))
|
||||
|
||||
key
|
||||
|
||||
procSuite "Portal Tests":
|
||||
let rng = newRng()
|
||||
|
||||
asyncTest "Portal Ping/Pong":
|
||||
let
|
||||
node1 = initDiscoveryNode(
|
||||
rng, PrivateKey.random(rng[]), localAddress(20302))
|
||||
node2 = initDiscoveryNode(
|
||||
rng, PrivateKey.random(rng[]), localAddress(20303))
|
||||
|
||||
proto1 = PortalProtocol.new(node1)
|
||||
proto2 = PortalProtocol.new(node2)
|
||||
|
||||
let pong = await proto1.ping(proto2.baseProtocol.localNode)
|
||||
|
||||
check:
|
||||
pong.isOk()
|
||||
pong.get().enrSeq == 1'u64
|
||||
pong.get().dataRadius == UInt256.high()
|
||||
|
||||
await node1.closeWait()
|
||||
await node2.closeWait()
|
||||
|
||||
asyncTest "Portal FindNode/Nodes":
|
||||
let
|
||||
node1 = initDiscoveryNode(
|
||||
rng, PrivateKey.random(rng[]), localAddress(20302))
|
||||
node2 = initDiscoveryNode(
|
||||
rng, PrivateKey.random(rng[]), localAddress(20303))
|
||||
|
||||
proto1 = PortalProtocol.new(node1)
|
||||
proto2 = PortalProtocol.new(node2)
|
||||
|
||||
block: # Find itself
|
||||
let nodes = await proto1.findNode(proto2.baseProtocol.localNode,
|
||||
List[uint16, 256](@[0'u16]))
|
||||
|
||||
check:
|
||||
nodes.isOk()
|
||||
nodes.get().total == 1'u8
|
||||
nodes.get().enrs.len() == 1
|
||||
|
||||
block: # Find nothing
|
||||
let nodes = await proto1.findNode(proto2.baseProtocol.localNode,
|
||||
List[uint16, 256](@[]))
|
||||
|
||||
check:
|
||||
nodes.isOk()
|
||||
nodes.get().total == 1'u8
|
||||
nodes.get().enrs.len() == 0
|
||||
|
||||
block: # Find for distance
|
||||
# TODO: Add test when implemented
|
||||
discard
|
||||
|
||||
await node1.closeWait()
|
||||
await node2.closeWait()
|
||||
|
||||
asyncTest "Portal FindContent/FoundContent":
|
||||
let
|
||||
node1 = initDiscoveryNode(
|
||||
rng, PrivateKey.random(rng[]), localAddress(20302))
|
||||
node2 = initDiscoveryNode(
|
||||
rng, PrivateKey.random(rng[]), localAddress(20303))
|
||||
|
||||
proto1 = PortalProtocol.new(node1)
|
||||
proto2 = PortalProtocol.new(node2)
|
||||
|
||||
let contentKey = ByteList(@(UInt256.random(rng[]).toBytes()))
|
||||
|
||||
let foundContent = await proto1.findContent(proto2.baseProtocol.localNode,
|
||||
contentKey)
|
||||
|
||||
check:
|
||||
foundContent.isOk()
|
||||
# TODO: adjust when implemented
|
||||
foundContent.get().enrs.len() == 0
|
||||
foundContent.get().payload.len() == 0
|
||||
|
||||
await node1.closeWait()
|
||||
await node2.closeWait()
|
|
@ -1,156 +0,0 @@
|
|||
# nim-eth - Portal Network
|
||||
# Copyright (c) 2021 Status Research & Development GmbH
|
||||
# Licensed and distributed under either of
|
||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||
|
||||
{.used.}
|
||||
|
||||
import
|
||||
std/unittest,
|
||||
stint, stew/[byteutils, results],
|
||||
../../eth/p2p/portal/messages
|
||||
|
||||
suite "Portal Protocol Message Encodings":
|
||||
test "Ping Request":
|
||||
var dataRadius: UInt256
|
||||
let
|
||||
enrSeq = 1'u64
|
||||
p = PingMessage(enrSeq: enrSeq, dataRadius: dataRadius)
|
||||
|
||||
let encoded = encodeMessage(p)
|
||||
check encoded.toHex ==
|
||||
"0101000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
let decoded = decodeMessage(encoded)
|
||||
check decoded.isOk()
|
||||
|
||||
let message = decoded.get()
|
||||
check:
|
||||
message.kind == ping
|
||||
message.ping.enrSeq == enrSeq
|
||||
message.ping.dataRadius == dataRadius
|
||||
|
||||
test "Pong Response":
|
||||
var dataRadius: UInt256
|
||||
let
|
||||
enrSeq = 1'u64
|
||||
p = PongMessage(enrSeq: enrSeq, dataRadius: dataRadius)
|
||||
|
||||
let encoded = encodeMessage(p)
|
||||
check encoded.toHex ==
|
||||
"0201000000000000000000000000000000000000000000000000000000000000000000000000000000"
|
||||
let decoded = decodeMessage(encoded)
|
||||
check decoded.isOk()
|
||||
|
||||
let message = decoded.get()
|
||||
check:
|
||||
message.kind == pong
|
||||
message.pong.enrSeq == enrSeq
|
||||
message.pong.dataRadius == dataRadius
|
||||
|
||||
test "FindNode Request":
|
||||
let
|
||||
distances = List[uint16, 256](@[0x0100'u16])
|
||||
fn = FindNodeMessage(distances: distances)
|
||||
|
||||
let encoded = encodeMessage(fn)
|
||||
check encoded.toHex == "03040000000001"
|
||||
|
||||
let decoded = decodeMessage(encoded)
|
||||
check decoded.isOk()
|
||||
|
||||
let message = decoded.get()
|
||||
check:
|
||||
message.kind == findnode
|
||||
message.findnode.distances == distances
|
||||
|
||||
test "Nodes Response (empty)":
|
||||
let
|
||||
total = 0x1'u8
|
||||
n = NodesMessage(total: total)
|
||||
|
||||
let encoded = encodeMessage(n)
|
||||
check encoded.toHex == "040105000000"
|
||||
|
||||
let decoded = decodeMessage(encoded)
|
||||
check decoded.isOk()
|
||||
|
||||
let message = decoded.get()
|
||||
check:
|
||||
message.kind == nodes
|
||||
message.nodes.total == total
|
||||
message.nodes.enrs.len() == 0
|
||||
|
||||
test "FindContent Request":
|
||||
let
|
||||
contentKey = ByteList(@[byte 0x01, 0x02, 0x03])
|
||||
fn = FindContentMessage(contentKey: contentKey)
|
||||
|
||||
let encoded = encodeMessage(fn)
|
||||
check encoded.toHex == "0504000000010203"
|
||||
|
||||
let decoded = decodeMessage(encoded)
|
||||
check decoded.isOk()
|
||||
|
||||
let message = decoded.get()
|
||||
check:
|
||||
message.kind == findcontent
|
||||
message.findcontent.contentKey == contentKey
|
||||
|
||||
test "FoundContent Response (empty enrs)":
|
||||
let
|
||||
enrs = List[ByteList, 32](@[])
|
||||
payload = ByteList(@[byte 0x01, 0x02, 0x03])
|
||||
n = FoundContentMessage(enrs: enrs, payload: payload)
|
||||
|
||||
let encoded = encodeMessage(n)
|
||||
check encoded.toHex == "060800000008000000010203"
|
||||
|
||||
let decoded = decodeMessage(encoded)
|
||||
check decoded.isOk()
|
||||
|
||||
let message = decoded.get()
|
||||
check:
|
||||
message.kind == foundcontent
|
||||
message.foundcontent.enrs.len() == 0
|
||||
message.foundcontent.payload == payload
|
||||
|
||||
test "Advertise Request":
|
||||
let
|
||||
contentKeys = List[ByteList, 32](List(@[ByteList(@[byte 0x01, 0x02, 0x03])]))
|
||||
am = AdvertiseMessage(contentKeys)
|
||||
# am = AdvertiseMessage(contentKeys: contentKeys)
|
||||
|
||||
let encoded = encodeMessage(am)
|
||||
check encoded.toHex == "0704000000010203"
|
||||
# "070400000004000000010203"
|
||||
|
||||
let decoded = decodeMessage(encoded)
|
||||
check decoded.isOk()
|
||||
|
||||
let message = decoded.get()
|
||||
check:
|
||||
message.kind == advertise
|
||||
message.advertise == contentKeys
|
||||
# message.advertise.contentKeys == contentKeys
|
||||
|
||||
test "RequestProofs Response": # That sounds weird
|
||||
let
|
||||
connectionId = List[byte, 4](@[byte 0x01, 0x02, 0x03, 0x04])
|
||||
contentKeys =
|
||||
List[ByteList, 32](List(@[ByteList(@[byte 0x01, 0x02, 0x03])]))
|
||||
n = RequestProofsMessage(connectionId: connectionId,
|
||||
contentKeys: contentKeys)
|
||||
|
||||
let encoded = encodeMessage(n)
|
||||
check encoded.toHex == "08080000000c0000000102030404000000010203"
|
||||
|
||||
let decoded = decodeMessage(encoded)
|
||||
check decoded.isOk()
|
||||
|
||||
let message = decoded.get()
|
||||
check:
|
||||
message.kind == requestproofs
|
||||
message.requestproofs.connectionId == connectionId
|
||||
message.requestproofs.contentKeys == contentKeys
|
Loading…
Reference in New Issue