Cleanup utp_test and make the code more flexible to change amounts (#1972)
This commit is contained in:
parent
fb2aa1b6f9
commit
2df9e2b7b1
|
@ -1,9 +1,9 @@
|
||||||
# uTP testing infrastructure
|
# uTP testing infrastructure
|
||||||
|
|
||||||
Testing infrastructure which enables to test uTP implementation over different
|
Testing infrastructure which enables to test uTP implementation over different
|
||||||
network conditions on local machine.
|
network conditions on a local machine.
|
||||||
|
|
||||||
Highly based on tools developed to test quic protocol:
|
Uses following tools developed to test the QUIC protocol:
|
||||||
|
|
||||||
[quic-interop-runner](https://github.com/marten-seemann/quic-interop-runner)
|
[quic-interop-runner](https://github.com/marten-seemann/quic-interop-runner)
|
||||||
|
|
||||||
|
@ -11,19 +11,20 @@ Highly based on tools developed to test quic protocol:
|
||||||
|
|
||||||
## Prerequisities
|
## Prerequisities
|
||||||
|
|
||||||
- Machine with docker installed
|
- Machine with Docker installed
|
||||||
|
|
||||||
- nimbus-eth1 set-up to run `make utp_test`
|
- nimbus-eth1 set-up to run `make utp_test`
|
||||||
|
|
||||||
## How it works
|
## How it works
|
||||||
|
|
||||||
Test setup uses docker compose to start 3 docker containers:
|
Test setup uses Docker Compose to start 3 Docker containers:
|
||||||
- client - which is instance of uTP test app
|
- uTP client - which is an instance of the `utp_test_app`
|
||||||
- server - which is instance of uTP test app
|
- uTP server - which is an instance of the `utp_test_app`
|
||||||
- sim - which is instance with ns3 network simulator with several pre-compiled scenarios
|
- sim - which is an instance of the ns3 network simulator with several pre-compiled scenarios
|
||||||
|
|
||||||
The networking is setup in such way that network traffic is routed from client to server
|
The networking is set up in such way that network traffic is routed from client to server
|
||||||
and server to client thorugh sim which decideds what to do with flowing packets
|
and server to client through the simulator.
|
||||||
|
The simulator decides what to do with packets passing through based on the selected scneario.
|
||||||
|
|
||||||
Explanation from [quic-network-simulator](https://github.com/marten-seemann/quic-network-simulator):
|
Explanation from [quic-network-simulator](https://github.com/marten-seemann/quic-network-simulator):
|
||||||
|
|
||||||
|
@ -38,9 +39,9 @@ simulation sits in the middle and forwards packets between `leftnet` and
|
||||||
|
|
||||||
## Practicalities
|
## Practicalities
|
||||||
|
|
||||||
For now process is semi-manual (TODO automate this as much as possible)
|
For now the process is semi-manual (TODO automate this as much as possible)
|
||||||
|
|
||||||
To run integration testing scenarios with different network conditions
|
To run integration testing scenarios with different network conditions:
|
||||||
|
|
||||||
```
|
```
|
||||||
1. cd nimbus-eth1/
|
1. cd nimbus-eth1/
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
version: "3.5"
|
version: "3.5"
|
||||||
|
|
||||||
# TODO Add possibility to configure ports by env vcariables
|
# TODO Add possibility to configure ports by env variables
|
||||||
services:
|
services:
|
||||||
sim:
|
sim:
|
||||||
# TODO for now we are using simulator which is also used in quic, ultimatly
|
# TODO for now we are using simulator which is also used in quic, ultimatly
|
||||||
|
@ -14,7 +14,7 @@ services:
|
||||||
- ./logs/sim:/logs
|
- ./logs/sim:/logs
|
||||||
environment:
|
environment:
|
||||||
- SCENARIO=$SCENARIO
|
- SCENARIO=$SCENARIO
|
||||||
cap_add:
|
cap_add:
|
||||||
- NET_ADMIN
|
- NET_ADMIN
|
||||||
expose:
|
expose:
|
||||||
- "57832"
|
- "57832"
|
||||||
|
@ -30,8 +30,6 @@ services:
|
||||||
# TODO add building of docker images based on variable
|
# TODO add building of docker images based on variable
|
||||||
# build: ./$SERVER
|
# build: ./$SERVER
|
||||||
image: test-utp
|
image: test-utp
|
||||||
# this arg will go to container entry point
|
|
||||||
command: "--udp-listen-address=193.167.100.100 --rpc-listen-address=0.0.0.0 --udp-port=9041 --rpc-port=9041"
|
|
||||||
container_name: server
|
container_name: server
|
||||||
hostname: server
|
hostname: server
|
||||||
stdin_open: true
|
stdin_open: true
|
||||||
|
@ -43,7 +41,7 @@ services:
|
||||||
- CLIENT_PARAMS=--udp-listen-address=193.167.100.100 --rpc-listen-address=0.0.0.0 --udp-port=9041 --rpc-port=9041
|
- CLIENT_PARAMS=--udp-listen-address=193.167.100.100 --rpc-listen-address=0.0.0.0 --udp-port=9041 --rpc-port=9041
|
||||||
depends_on:
|
depends_on:
|
||||||
- sim
|
- sim
|
||||||
cap_add:
|
cap_add:
|
||||||
- NET_ADMIN
|
- NET_ADMIN
|
||||||
ports:
|
ports:
|
||||||
- "9041:9041"
|
- "9041:9041"
|
||||||
|
@ -60,7 +58,6 @@ services:
|
||||||
client:
|
client:
|
||||||
# build: ./$CLIENT
|
# build: ./$CLIENT
|
||||||
image: test-utp
|
image: test-utp
|
||||||
command: "--udp-listen-address=193.167.0.100 --rpc-listen-address=0.0.0.0 --udp-port=9042 --rpc-port=9042"
|
|
||||||
container_name: client
|
container_name: client
|
||||||
hostname: client
|
hostname: client
|
||||||
stdin_open: true
|
stdin_open: true
|
||||||
|
@ -72,7 +69,7 @@ services:
|
||||||
- CLIENT_PARAMS=--udp-listen-address=193.167.0.100 --rpc-listen-address=0.0.0.0 --udp-port=9042 --rpc-port=9042
|
- CLIENT_PARAMS=--udp-listen-address=193.167.0.100 --rpc-listen-address=0.0.0.0 --udp-port=9042 --rpc-port=9042
|
||||||
depends_on:
|
depends_on:
|
||||||
- sim
|
- sim
|
||||||
cap_add:
|
cap_add:
|
||||||
- NET_ADMIN
|
- NET_ADMIN
|
||||||
ports:
|
ports:
|
||||||
- "9042:9042"
|
- "9042:9042"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Nimbus
|
# Fluffy
|
||||||
# Copyright (c) 2022-2023 Status Research & Development GmbH
|
# Copyright (c) 2022-2024 Status Research & Development GmbH
|
||||||
# Licensed and distributed under either of
|
# Licensed and distributed under either of
|
||||||
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
# * 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).
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||||
|
@ -10,13 +10,13 @@ import
|
||||||
unittest2, testutils, chronos,
|
unittest2, testutils, chronos,
|
||||||
json_rpc/rpcclient, stew/byteutils,
|
json_rpc/rpcclient, stew/byteutils,
|
||||||
eth/keys,
|
eth/keys,
|
||||||
./utp_test_client
|
./utp_test_rpc_client
|
||||||
|
|
||||||
proc generateBytesHex(rng: var HmacDrbgContext, length: int): string =
|
proc generateBytesHex(rng: var HmacDrbgContext, length: int): string =
|
||||||
rng.generateBytes(length).toHex()
|
rng.generateBytes(length).toHex()
|
||||||
|
|
||||||
# Before running the test suite, there need to be two instances of the
|
# Before running this test suite, there need to be two instances of the
|
||||||
# utp_test_app running under provided ports (9042, 9041).
|
# utp_test_app running under the tested ports: 9042, 9041.
|
||||||
# Those could be launched locally by running either
|
# Those could be launched locally by running either
|
||||||
# ./utp_test_app --udp-listen-address=127.0.0.1 --rpc-listen-address=0.0.0.0 --udp-port=9041 --rpc-port=9041
|
# ./utp_test_app --udp-listen-address=127.0.0.1 --rpc-listen-address=0.0.0.0 --udp-port=9041 --rpc-port=9041
|
||||||
# ./utp_test_app --udp-listen-address=127.0.0.1 --rpc-listen-address=0.0.0.0 --udp-port=9042 --rpc-port=9042
|
# ./utp_test_app --udp-listen-address=127.0.0.1 --rpc-listen-address=0.0.0.0 --udp-port=9042 --rpc-port=9042
|
||||||
|
@ -24,13 +24,15 @@ proc generateBytesHex(rng: var HmacDrbgContext, length: int): string =
|
||||||
# running from docker dir:
|
# running from docker dir:
|
||||||
# 1. docker build -t test-utp --no-cache --build-arg BRANCH_NAME=branch-name .
|
# 1. docker build -t test-utp --no-cache --build-arg BRANCH_NAME=branch-name .
|
||||||
# 2. SCENARIO="scenario name and params " docker-compose up
|
# 2. SCENARIO="scenario name and params " docker-compose up
|
||||||
procSuite "uTP integration tests":
|
|
||||||
let rng = newRng()
|
|
||||||
let clientContainerAddress = "127.0.0.1"
|
|
||||||
let clientContainerPort = Port(9042)
|
|
||||||
|
|
||||||
let serverContainerAddress = "127.0.0.1"
|
procSuite "uTP network simulator tests":
|
||||||
let serverContainerPort = Port(9041)
|
const
|
||||||
|
clientContainerAddress = "127.0.0.1"
|
||||||
|
clientContainerPort = Port(9042)
|
||||||
|
serverContainerAddress = "127.0.0.1"
|
||||||
|
serverContainerPort = Port(9041)
|
||||||
|
|
||||||
|
let rng = newRng()
|
||||||
|
|
||||||
type
|
type
|
||||||
FutureCallback[A] = proc (): Future[A] {.gcsafe, raises: [].}
|
FutureCallback[A] = proc (): Future[A] {.gcsafe, raises: [].}
|
||||||
|
@ -56,9 +58,9 @@ procSuite "uTP integration tests":
|
||||||
raise canc
|
raise canc
|
||||||
|
|
||||||
proc findServerConnection(
|
proc findServerConnection(
|
||||||
connections: openArray[SKey],
|
connections: openArray[SKey],
|
||||||
clientId: NodeId,
|
clientId: NodeId,
|
||||||
clientConnectionId: uint16): Option[Skey] =
|
clientConnectionId: uint16): Option[Skey] =
|
||||||
let conns: seq[SKey] =
|
let conns: seq[SKey] =
|
||||||
connections.filter((key:Skey) => key.id == (clientConnectionId + 1) and
|
connections.filter((key:Skey) => key.id == (clientConnectionId + 1) and
|
||||||
key.nodeId == clientId)
|
key.nodeId == clientId)
|
||||||
|
@ -75,20 +77,20 @@ procSuite "uTP integration tests":
|
||||||
await client.connect(clientContainerAddress, clientContainerPort, false)
|
await client.connect(clientContainerAddress, clientContainerPort, false)
|
||||||
await server.connect(serverContainerAddress, serverContainerPort, false)
|
await server.connect(serverContainerAddress, serverContainerPort, false)
|
||||||
|
|
||||||
# we may need to retry few times if the simm is not ready yet
|
# we may need to retry few times if the sim is not ready yet
|
||||||
let clientInfo = await repeatTillSuccess(() => client.discv5_nodeInfo(), 10)
|
let clientInfo = await repeatTillSuccess(() => client.discv5_nodeInfo(), 10)
|
||||||
|
|
||||||
let serverInfo = await repeatTillSuccess(() => server.discv5_nodeInfo(), 10)
|
let serverInfo = await repeatTillSuccess(() => server.discv5_nodeInfo(), 10)
|
||||||
|
|
||||||
# nodes need to have established session before the utp try
|
# nodes need to have an established discv5 session before the uTP test
|
||||||
discard await repeatTillSuccess(() => client.discv5_ping(serverInfo.enr))
|
discard await repeatTillSuccess(() => client.discv5_ping(serverInfo.enr))
|
||||||
|
|
||||||
return (client, clientInfo, server, serverInfo)
|
return (client, clientInfo, server, serverInfo)
|
||||||
|
|
||||||
asyncTest "Transfer 100k bytes of data over utp stream from client to server":
|
asyncTest "100kb transfer from client to server":
|
||||||
let (client, clientInfo, server, serverInfo) = await setupTest()
|
const amountOfBytes = 100_000
|
||||||
let numOfBytes = 100000
|
|
||||||
let
|
let
|
||||||
|
(client, clientInfo, server, serverInfo) = await setupTest()
|
||||||
clientConnectionKey = await repeatTillSuccess(() =>
|
clientConnectionKey = await repeatTillSuccess(() =>
|
||||||
client.utp_connect(serverInfo.enr))
|
client.utp_connect(serverInfo.enr))
|
||||||
serverConnections = await repeatTillSuccess(() =>
|
serverConnections = await repeatTillSuccess(() =>
|
||||||
|
@ -99,24 +101,24 @@ procSuite "uTP integration tests":
|
||||||
check:
|
check:
|
||||||
maybeServerConnectionKey.isSome()
|
maybeServerConnectionKey.isSome()
|
||||||
|
|
||||||
let serverConnectionKey = maybeServerConnectionKey.unsafeGet()
|
|
||||||
|
|
||||||
let
|
let
|
||||||
bytesToWrite = generateBytesHex(rng[], numOfBytes)
|
serverConnectionKey = maybeServerConnectionKey.unsafeGet()
|
||||||
|
bytesToWrite = generateBytesHex(rng[], amountOfBytes)
|
||||||
writeRes = await client.utp_write(clientConnectionKey, bytesToWrite)
|
writeRes = await client.utp_write(clientConnectionKey, bytesToWrite)
|
||||||
readData = await server.utp_read(serverConnectionKey, numOfBytes)
|
dataRead = await server.utp_read(serverConnectionKey, amountOfBytes)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
writeRes == true
|
writeRes == true
|
||||||
readData == bytesToWrite
|
dataRead == bytesToWrite
|
||||||
|
|
||||||
asyncTest "Transfer 100k bytes of data over utp stream from server to client":
|
asyncTest "100kb transfer from server to client":
|
||||||
# In classic uTP this would not be possible, as when uTP works over UDP the
|
# In classic uTP this would not be possible, as when uTP works over UDP the
|
||||||
# client needs to transfer first, but when working over discv5 it should be
|
# client needs to transfer first, but when working over discv5 it should be
|
||||||
# possible to transfer data from server to client from the start.
|
# possible to transfer data from server to client from the start.
|
||||||
let (client, clientInfo, server, serverInfo) = await setupTest()
|
const amountOfBytes = 100_000
|
||||||
let numOfBytes = 100000
|
|
||||||
let
|
let
|
||||||
|
(client, clientInfo, server, serverInfo) = await setupTest()
|
||||||
clientConnectionKey = await repeatTillSuccess(() =>
|
clientConnectionKey = await repeatTillSuccess(() =>
|
||||||
client.utp_connect(serverInfo.enr))
|
client.utp_connect(serverInfo.enr))
|
||||||
serverConnections = await repeatTillSuccess(() =>
|
serverConnections = await repeatTillSuccess(() =>
|
||||||
|
@ -127,21 +129,23 @@ procSuite "uTP integration tests":
|
||||||
check:
|
check:
|
||||||
maybeServerConnectionKey.isSome()
|
maybeServerConnectionKey.isSome()
|
||||||
|
|
||||||
let serverConnectionKey = maybeServerConnectionKey.unsafeGet()
|
|
||||||
|
|
||||||
let
|
let
|
||||||
bytesToWrite = generateBytesHex(rng[], numOfBytes)
|
serverConnectionKey = maybeServerConnectionKey.unsafeGet()
|
||||||
|
bytesToWrite = generateBytesHex(rng[], amountOfBytes)
|
||||||
writeRes = await server.utp_write(serverConnectionKey, bytesToWrite)
|
writeRes = await server.utp_write(serverConnectionKey, bytesToWrite)
|
||||||
readData = await client.utp_read(clientConnectionKey, numOfBytes)
|
dataRead = await client.utp_read(clientConnectionKey, amountOfBytes)
|
||||||
|
|
||||||
check:
|
check:
|
||||||
writeRes == true
|
writeRes == true
|
||||||
readData == bytesToWrite
|
dataRead == bytesToWrite
|
||||||
|
|
||||||
|
asyncTest "Multiple 10kb transfers from client to server":
|
||||||
|
const
|
||||||
|
amountOfBytes = 10_000
|
||||||
|
amountOfTransfers = 3
|
||||||
|
|
||||||
asyncTest "Multiple 10k bytes transfers over utp stream":
|
|
||||||
let (client, clientInfo, server, serverInfo) = await setupTest()
|
|
||||||
let numOfBytes = 10000
|
|
||||||
let
|
let
|
||||||
|
(client, clientInfo, server, serverInfo) = await setupTest()
|
||||||
clientConnectionKey = await repeatTillSuccess(() =>
|
clientConnectionKey = await repeatTillSuccess(() =>
|
||||||
client.utp_connect(serverInfo.enr))
|
client.utp_connect(serverInfo.enr))
|
||||||
serverConnections = await repeatTillSuccess(() =>
|
serverConnections = await repeatTillSuccess(() =>
|
||||||
|
@ -154,71 +158,47 @@ procSuite "uTP integration tests":
|
||||||
|
|
||||||
let serverConnectionKey = maybeServerConnectionKey.unsafeGet()
|
let serverConnectionKey = maybeServerConnectionKey.unsafeGet()
|
||||||
|
|
||||||
let
|
var totalBytesToWrite: string
|
||||||
bytesToWrite = generateBytesHex(rng[], numOfBytes)
|
for i in 0..<amountOfTransfers:
|
||||||
bytesToWrite1 = generateBytesHex(rng[], numOfBytes)
|
let
|
||||||
bytesToWrite2 = generateBytesHex(rng[], numOfBytes)
|
bytesToWrite = generateBytesHex(rng[], amountOfBytes)
|
||||||
writeRes = await client.utp_write(clientConnectionKey, bytesToWrite)
|
writeRes = await client.utp_write(clientConnectionKey, bytesToWrite)
|
||||||
writeRes1 = await client.utp_write(clientConnectionKey, bytesToWrite1)
|
|
||||||
writeRes2 = await client.utp_write(clientConnectionKey, bytesToWrite2)
|
|
||||||
readData = await server.utp_read(serverConnectionKey, numOfBytes * 3)
|
|
||||||
|
|
||||||
let writtenData = join(@[bytesToWrite, bytesToWrite1, bytesToWrite2])
|
check writeRes == true
|
||||||
|
totalBytesToWrite.add(bytesToWrite)
|
||||||
|
|
||||||
check:
|
let dataRead = await server.utp_read(
|
||||||
writeRes == true
|
serverConnectionKey, amountOfBytes * amountOfTransfers)
|
||||||
writeRes1 == true
|
|
||||||
writeRes2 == true
|
check dataRead == totalBytesToWrite
|
||||||
readData == writtenData
|
|
||||||
|
asyncTest "Multiple 10kb transfers over multiple sockets from client to server":
|
||||||
|
const
|
||||||
|
amountOfBytes = 10_000
|
||||||
|
amountOfSockets = 3
|
||||||
|
|
||||||
asyncTest "Handle mulitplie sockets over one utp server instance ":
|
|
||||||
let (client, clientInfo, server, serverInfo) = await setupTest()
|
let (client, clientInfo, server, serverInfo) = await setupTest()
|
||||||
let numOfBytes = 10000
|
|
||||||
let
|
|
||||||
clientConnectionKey1 = await repeatTillSuccess(() =>
|
|
||||||
client.utp_connect(serverInfo.enr))
|
|
||||||
clientConnectionKey2 = await repeatTillSuccess(() =>
|
|
||||||
client.utp_connect(serverInfo.enr))
|
|
||||||
clientConnectionKey3 = await repeatTillSuccess(() =>
|
|
||||||
client.utp_connect(serverInfo.enr))
|
|
||||||
serverConnections = await repeatTillSuccess(() =>
|
|
||||||
server.utp_get_connections())
|
|
||||||
|
|
||||||
maybeServerConnectionKey1 = serverConnections.findServerConnection(
|
var connectionKeys: seq[(SKey, SKey)]
|
||||||
clientInfo.nodeId, clientConnectionKey1.id)
|
for i in 0..<amountOfSockets:
|
||||||
maybeServerConnectionKey2 = serverConnections.findServerConnection(
|
let
|
||||||
clientInfo.nodeId, clientConnectionKey2.id)
|
clientConnectionKey = await repeatTillSuccess(() =>
|
||||||
maybeServerConnectionKey3 = serverConnections.findServerConnection(
|
client.utp_connect(serverInfo.enr))
|
||||||
clientInfo.nodeId, clientConnectionKey3.id)
|
serverConnections = await repeatTillSuccess(() =>
|
||||||
|
server.utp_get_connections())
|
||||||
|
serverConnectionKeyRes = serverConnections.findServerConnection(
|
||||||
|
clientInfo.nodeId, clientConnectionKey.id)
|
||||||
|
|
||||||
check:
|
check serverConnectionKeyRes.isSome()
|
||||||
maybeServerConnectionKey1.isSome()
|
|
||||||
maybeServerConnectionKey2.isSome()
|
|
||||||
maybeServerConnectionKey3.isSome()
|
|
||||||
|
|
||||||
let serverConnectionKey1 = maybeServerConnectionKey1.unsafeGet()
|
connectionKeys.add((clientConnectionKey, serverConnectionKeyRes.unsafeGet()))
|
||||||
let serverConnectionKey2 = maybeServerConnectionKey2.unsafeGet()
|
|
||||||
let serverConnectionKey3 = maybeServerConnectionKey3.unsafeGet()
|
|
||||||
|
|
||||||
let
|
for (clientConnectionKey, serverConnectionKey) in connectionKeys:
|
||||||
bytesToWrite1 = generateBytesHex(rng[], numOfBytes)
|
let
|
||||||
bytesToWrite2 = generateBytesHex(rng[], numOfBytes)
|
bytesToWrite = generateBytesHex(rng[], amountOfBytes)
|
||||||
bytesToWrite3 = generateBytesHex(rng[], numOfBytes)
|
writeRes = await client.utp_write(clientConnectionKey, bytesToWrite)
|
||||||
|
dataRead = await server.utp_read(serverConnectionKey, amountOfBytes)
|
||||||
|
|
||||||
writeRes1 = await client.utp_write(clientConnectionKey1, bytesToWrite1)
|
check:
|
||||||
writeRes2 = await client.utp_write(clientConnectionKey2, bytesToWrite2)
|
writeRes == true
|
||||||
writeRes3 = await client.utp_write(clientConnectionKey3, bytesToWrite3)
|
dataRead == bytesToWrite
|
||||||
|
|
||||||
readData1 = await server.utp_read(serverConnectionKey1, numOfBytes)
|
|
||||||
readData2 = await server.utp_read(serverConnectionKey2, numOfBytes)
|
|
||||||
readData3 = await server.utp_read(serverConnectionKey3, numOfBytes)
|
|
||||||
|
|
||||||
check:
|
|
||||||
writeRes1 == true
|
|
||||||
writeRes2 == true
|
|
||||||
writeRes3 == true
|
|
||||||
|
|
||||||
# all data was delivered to correct sockets
|
|
||||||
readData1 == bytesToWrite1
|
|
||||||
readData2 == bytesToWrite2
|
|
||||||
readData3 == bytesToWrite3
|
|
||||||
|
|
Loading…
Reference in New Issue