nim-eth/tests/p2p/tserver.nim

142 lines
3.9 KiB
Nim

#
# 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
std/[sequtils, strformat, options],
testutils/unittests, chronicles, chronos,
../../eth/[rlp, keys, p2p], ../../eth/p2p/mock_peers
const
clientId = "nim-eth-p2p/0.0.1"
type
AbcPeer = ref object
peerName: string
lastResponse: string
XyzPeer = ref object
messages: int
AbcNetwork = ref object
peers: seq[string]
p2pProtocol abc(version = 1,
peerState = AbcPeer,
networkState = AbcNetwork,
timeout = 100):
onPeerConnected do (peer: Peer):
await peer.hi "Bob"
let response = await peer.nextMsg(abc.hi)
peer.networkState.peers.add response.name
onPeerDisconnected do (peer: Peer, reason: DisconnectionReason):
echo "peer disconnected", peer
requestResponse:
proc abcReq(p: Peer, n: int) =
echo "got req ", n
await response.send(&"response to #{n}")
proc abcRes(p: Peer, data: string) =
echo "got response ", data
proc hi(p: Peer, name: string) =
echo "got hi from ", name
p.state.peerName = name
let query = 123
echo "sending req #", query
var r = await p.abcReq(query)
if r.isSome:
p.state.lastResponse = r.get.data
else:
p.state.lastResponse = "timeout"
p2pProtocol xyz(version = 1,
peerState = XyzPeer,
useRequestIds = false,
timeout = 100):
proc foo(p: Peer, s: string, a, z: int) =
p.state.messages += 1
if p.supports(abc):
echo p.state(abc).peerName
proc bar(p: Peer, i: int, s: string)
requestResponse:
proc xyzReq(p: Peer, n: int, timeout = 3.seconds) =
echo "got req ", n
proc xyzRes(p: Peer, data: string) =
echo "got response ", data
proc defaultTestingHandshake(_: type abc): abc.hi =
result.name = "John Doe"
proc localAddress(port: int): Address =
let port = Port(port)
result = Address(udpPort: port, tcpPort: port, ip: parseIpAddress("127.0.0.1"))
template asyncTest(name, body: untyped) =
test name:
proc scenario {.async.} = body
waitFor scenario()
template sendResponseWithId(peer: Peer, proto, msg: untyped, reqId: int, data: varargs[untyped]): auto =
msg(ResponseWithId[proto.msg](peer: peer, id: reqId), data)
template sendResponse(peer: Peer, proto, msg: untyped, data: varargs[untyped]): auto =
msg(Response[proto.msg](peer), data)
asyncTest "network with 3 peers using custom protocols":
const useCompression = defined(useSnappy)
let localKeys = KeyPair.random()[]
let localAddress = localAddress(30303)
var localNode = newEthereumNode(localKeys, localAddress, 1, nil, useCompression = useCompression)
localNode.startListening()
var mock1 = newMockPeer do (m: MockConf):
m.addHandshake abc.hi(name: "Alice")
m.expect(abc.abcReq) do (peer: Peer, data: Rlp):
let reqId = data.readReqId()
await sendResponseWithId(peer, abc, abcRes, reqId, "mock response")
await sleepAsync(100)
let r = await peer.abcReq(1)
doAssert r.get.data == "response to #1"
m.expect(abc.abcRes)
var mock2 = newMockPeer do (m: MockConf):
m.addCapability xyz
m.addCapability abc
m.expect(abc.abcReq) # we'll let this one time out
m.expect(xyz.xyzReq) do (peer: Peer):
echo "got xyz req"
await sendResponse(peer, xyz, xyzRes, "mock peer data")
when useCompression:
m.useCompression = useCompression
discard await mock1.rlpxConnect(localNode)
let mock2Connection = await localNode.rlpxConnect(mock2)
let r = await mock2Connection.xyzReq(10)
check r.get.data == "mock peer data"
let abcNetState = localNode.protocolState(abc)
check:
abcNetState.peers.len == 2
"Alice" in abcNetState.peers
"John Doe" in abcNetState.peers