2019-02-05 15:40:29 +00:00
|
|
|
#
|
|
|
|
# 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
|
|
|
|
sequtils, strformat, options, unittest,
|
2019-02-06 16:01:04 +00:00
|
|
|
chronicles, chronos, eth/[rlp, keys, p2p],
|
2019-02-05 15:40:29 +00:00
|
|
|
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
|
2019-03-11 09:22:06 +00:00
|
|
|
await response.send(&"response to #{n}")
|
2019-02-05 15:40:29 +00:00
|
|
|
|
|
|
|
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 = 3000) =
|
|
|
|
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()
|
|
|
|
|
2019-03-11 09:22:06 +00:00
|
|
|
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)
|
|
|
|
|
2019-02-05 15:40:29 +00:00
|
|
|
asyncTest "network with 3 peers using custom protocols":
|
|
|
|
const useCompression = defined(useSnappy)
|
|
|
|
let localKeys = newKeyPair()
|
|
|
|
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()
|
2019-03-11 09:22:06 +00:00
|
|
|
await sendResponseWithId(peer, abc, abcRes, reqId, "mock response")
|
2019-02-05 15:40:29 +00:00
|
|
|
await sleepAsync(100)
|
|
|
|
let r = await peer.abcReq(1)
|
2019-03-13 22:15:26 +00:00
|
|
|
doAssert r.get.data == "response to #1"
|
2019-02-05 15:40:29 +00:00
|
|
|
|
|
|
|
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"
|
2019-03-11 09:22:06 +00:00
|
|
|
await sendResponse(peer, xyz, xyzRes, "mock peer data")
|
2019-02-05 15:40:29 +00:00
|
|
|
|
|
|
|
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
|