diff --git a/eth/p2p/mock_peers.nim b/eth/p2p/mock_peers.nim deleted file mode 100644 index 3a9cfe6..0000000 --- a/eth/p2p/mock_peers.nim +++ /dev/null @@ -1,217 +0,0 @@ -import - std/[macros, deques, algorithm], - chronos, - ".."/[keys, rlp, p2p], ../common/eth_types, - ./private/p2p_types, ./rlpx - -type - Action = proc (p: Peer, data: Rlp): Future[void] {.gcsafe.} - - ProtocolMessagePair = object - protocol: ProtocolInfo - id: int - - ExpectedMsg = object - msg: ProtocolMessagePair - response: Action - - MockConf* = ref object - keys*: KeyPair - address*: Address - networkId*: uint - chain*: AbstractChainDB - clientId*: string - waitForHello*: bool - - devp2pHandshake: ExpectedMsg - handshakes: seq[ExpectedMsg] - protocols: seq[ProtocolInfo] - - expectedMsgs: Deque[ExpectedMsg] - receivedMsgsCount: int - when useSnappy: - useCompression*: bool - -var - nextUnusedMockPort = 40304 - -proc toAction(a: Action): Action = a - -proc toAction[N](actions: array[N, Action]): Action = - mixin await - result = proc (peer: Peer, data: Rlp) {.async.} = - for a in actions: - await a(peer, data) - -proc toAction(a: proc (): Future[void]): Action = - result = proc (peer: Peer, data: Rlp) {.async.} = - await a() - -proc toAction(a: proc (peer: Peer): Future[void]): Action = - result = proc (peer: Peer, data: Rlp) {.async.} = - await a(peer) - -proc delay*(duration: int): Action = - result = proc (p: Peer, data: Rlp) {.async.} = - await sleepAsync(duration) - -proc reply(bytes: Bytes): Action = - result = proc (p: Peer, data: Rlp) {.async.} = - await p.sendMsg(bytes) - -proc reply*[Msg](msg: Msg): Action = - mixin await - result = proc (p: Peer, data: Rlp) {.async.} = - await p.send(msg) - -proc localhostAddress*(port: int): Address = - let port = Port(port) - result = Address(udpPort: port, tcpPort: port, ip: parseIpAddress("127.0.0.1")) - -proc makeProtoMsgPair(MsgType: type): ProtocolMessagePair = - mixin msgProtocol, protocolInfo - result.protocol = MsgType.msgProtocol.protocolInfo - result.id = MsgType.msgId - -proc readReqId*(rlp: Rlp): int = - var r = rlp - return r.read(int) - -proc expectationViolationMsg(mock: MockConf, - reason: string, - receivedMsg: ptr MessageInfo): string = - result = "[Mock expectation violated] " & reason & ": " & receivedMsg.name - for i in 0 ..< mock.expectedMsgs.len: - let expected = mock.expectedMsgs[i].msg - result.add "\n " & expected.protocol.messages[expected.id].name - if i == mock.receivedMsgsCount: result.add " <- we are here" - result.add "\n" - -proc addProtocol(mock: MockConf, p: ProtocolInfo): ProtocolInfo = - result = create ProtocolInfoObj - deepCopy(result[], p[]) - - proc incomingMsgHandler(p: Peer, receivedMsgId: int, rlp: Rlp): Future[void] {.gcsafe.} = - let (receivedMsgProto, receivedMsgInfo) = p.getMsgMetadata(receivedMsgId) - let expectedMsgIdx = mock.receivedMsgsCount - - template fail(reason: string) = - stdout.write mock.expectationViolationMsg(reason, receivedMsgInfo) - quit 1 - - if expectedMsgIdx > mock.expectedMsgs.len: - fail "Mock peer received more messages than expected" - - let expectedMsg = mock.expectedMsgs[expectedMsgIdx] - if receivedMsgInfo.id != expectedMsg.msg.id or - receivedMsgProto.name != expectedMsg.msg.protocol.name: - fail "Mock peer received an unexpected message" - - inc mock.receivedMsgsCount - if expectedMsg.response != nil: - return expectedMsg.response(p, rlp) - else: - result = newFuture[void]() - result.complete() - - for m in mitems(result.messages): - m.thunk = incomingMsgHandler - - result.handshake = nil - - # TODO This mock conf can override this - result.disconnectHandler = nil - - mock.protocols.add result - -proc addHandshake*(mock: MockConf, msg: auto) = - var msgInfo = makeProtoMsgPair(msg.type) - msgInfo.protocol = mock.addProtocol(msgInfo.protocol) - let expectedMsg = ExpectedMsg(msg: msgInfo, response: reply(msg)) - - when msg is DevP2P.hello: - devp2pHandshake = expectedMsg - else: - mock.handshakes.add expectedMsg - -proc addCapability*(mock: MockConf, Protocol: type) = - mixin defaultTestingHandshake, protocolInfo - - when compiles(defaultTestingHandshake(Protocol)): - mock.addHandshake(defaultTestingHandshake(Protocol)) - else: - discard mock.addProtocol(Protocol.protocolInfo) - -proc expectImpl(mock: MockConf, msg: ProtocolMessagePair, action: Action) = - mock.expectedMsgs.addLast ExpectedMsg(msg: msg, response: action) - -macro expect*(mock: MockConf, MsgType: type, handler: untyped = nil): untyped = - if handler.kind in {nnkLambda, nnkDo}: - handler.addPragma ident("async") - - result = newCall( - bindSym("expectImpl"), - mock, - newCall(bindSym"makeProtoMsgPair", MsgType.getType), - newCall(bindSym"toAction", handler)) - -template compression(m: MockConf): bool = - when useSnappy: - m.useCompression - else: - false - -proc newMockPeer*(userConfigurator: proc (m: MockConf)): EthereumNode = - var mockConf = new MockConf - mockConf.keys = KeyPair.random()[] - mockConf.address = localhostAddress(nextUnusedMockPort) - inc nextUnusedMockPort - mockConf.networkId = 1'u - mockConf.clientId = "Mock Peer" - mockConf.waitForHello = true - mockConf.expectedMsgs = initDeque[ExpectedMsg]() - - userConfigurator(mockConf) - - var node = newEthereumNode(mockConf.keys, - mockConf.address, - mockConf.networkId, - mockConf.chain, - mockConf.clientId, - addAllCapabilities = false, - mockConf.compression()) - - mockConf.handshakes.sort do (lhs, rhs: ExpectedMsg) -> int: - # this is intentially sorted in reverse order, so we - # can add them in the correct order below. - return -cmp(lhs.msg.protocol.index, rhs.msg.protocol.index) - - for h in mockConf.handshakes: - mockConf.expectedMsgs.addFirst h - - for p in mockConf.protocols: - node.addCapability p - - when false: - # TODO: This part doesn't work correctly yet. - # rlpx{Connect,Accept} control the handshake. - if mockConf.devp2pHandshake.response != nil: - mockConf.expectedMsgs.addFirst mockConf.devp2pHandshake - else: - proc sendHello(p: Peer, data: Rlp) {.async.} = - await p.hello(devp2pVersion, - mockConf.clientId, - node.capabilities, - uint(node.address.tcpPort), - node.keys.pubkey.getRaw()) - - mockConf.expectedMsgs.addFirst ExpectedMsg( - msg: makeProtoMsgPair(p2p.hello), - response: sendHello) - - node.startListening() - return node - -proc rlpxConnect*(node, otherNode: EthereumNode): Future[Peer] = - let otherAsRemote = newNode(otherNode.toENode()) - return rlpx.rlpxConnect(node, otherAsRemote) diff --git a/tests/p2p/tserver.nim b/tests/p2p/tserver.nim deleted file mode 100644 index 323f9a5..0000000 --- a/tests/p2p/tserver.nim +++ /dev/null @@ -1,141 +0,0 @@ -# -# 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