mirror of https://github.com/status-im/nim-eth.git
Add uTP over discv5 test and small uTP performance improvements (#663)
- Add a multiple sockets use test for uTP over discv5 - Use assign2 for the biggest consumer of genericAssignAux in uTP - Avoid calling exists on the growable buffer when there is no place in the socket window.
This commit is contained in:
parent
d4927593a1
commit
974a995b21
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2021-2023 Status Research & Development GmbH
|
# Copyright (c) 2021-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).
|
||||||
|
@ -9,6 +9,8 @@
|
||||||
import
|
import
|
||||||
std/[options, math]
|
std/[options, math]
|
||||||
|
|
||||||
|
from stew/assign2 import assign
|
||||||
|
|
||||||
export options
|
export options
|
||||||
|
|
||||||
# Buffer implementation similar to the one in the reference implementation.
|
# Buffer implementation similar to the one in the reference implementation.
|
||||||
|
@ -32,7 +34,7 @@ proc init*[A](T: type GrowableCircularBuffer[A], size: uint32 = 16): T =
|
||||||
)
|
)
|
||||||
|
|
||||||
proc get*[A](buff: GrowableCircularBuffer[A], i: uint32): Option[A] =
|
proc get*[A](buff: GrowableCircularBuffer[A], i: uint32): Option[A] =
|
||||||
buff.items[i and buff.mask]
|
assign(result, buff.items[i and buff.mask])
|
||||||
|
|
||||||
proc putImpl[A](buff: var GrowableCircularBuffer[A], i: uint32, elem: Option[A]) =
|
proc putImpl[A](buff: var GrowableCircularBuffer[A], i: uint32, elem: Option[A]) =
|
||||||
buff.items[i and buff.mask] = elem
|
buff.items[i and buff.mask] = elem
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2021-2023 Status Research & Development GmbH
|
# Copyright (c) 2021-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).
|
||||||
|
@ -481,6 +481,10 @@ proc resetSendTimeout(socket: UtpSocket) =
|
||||||
socket.rtoTimeout = getMonoTimestamp().moment + socket.retransmitTimeout
|
socket.rtoTimeout = getMonoTimestamp().moment + socket.retransmitTimeout
|
||||||
|
|
||||||
proc flushPackets(socket: UtpSocket) =
|
proc flushPackets(socket: UtpSocket) =
|
||||||
|
if (socket.freeWindowBytes() == 0):
|
||||||
|
trace "No place in send window, not flushing"
|
||||||
|
return
|
||||||
|
|
||||||
let oldestOutgoingPacketSeqNr = socket.seqNr - socket.curWindowPackets
|
let oldestOutgoingPacketSeqNr = socket.seqNr - socket.curWindowPackets
|
||||||
var i: uint16 = oldestOutgoingPacketSeqNr
|
var i: uint16 = oldestOutgoingPacketSeqNr
|
||||||
while i != socket.seqNr:
|
while i != socket.seqNr:
|
||||||
|
|
|
@ -14,7 +14,7 @@ import
|
||||||
type TestObj = object
|
type TestObj = object
|
||||||
foo: string
|
foo: string
|
||||||
|
|
||||||
suite "Utp ring buffer":
|
suite "uTP ring buffer":
|
||||||
test "Empty buffer":
|
test "Empty buffer":
|
||||||
let buff = GrowableCircularBuffer[int].init(size = 4)
|
let buff = GrowableCircularBuffer[int].init(size = 4)
|
||||||
check:
|
check:
|
||||||
|
|
|
@ -11,8 +11,7 @@ import
|
||||||
unittest2,
|
unittest2,
|
||||||
../../eth/utp/clock_drift_calculator
|
../../eth/utp/clock_drift_calculator
|
||||||
|
|
||||||
suite "Clock drift calculator":
|
suite "uTP clock drift calculator":
|
||||||
|
|
||||||
test "Initial clock drift should be 0":
|
test "Initial clock drift should be 0":
|
||||||
let currentTime = Moment.now()
|
let currentTime = Moment.now()
|
||||||
let calculator = ClockDriftCalculator.init(currentTime)
|
let calculator = ClockDriftCalculator.init(currentTime)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright (c) 2020-2021 Status Research & Development GmbH
|
# Copyright (c) 2020-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).
|
||||||
|
@ -7,7 +7,7 @@
|
||||||
{.used.}
|
{.used.}
|
||||||
|
|
||||||
import
|
import
|
||||||
std/options,
|
std/[options, sequtils],
|
||||||
chronos,
|
chronos,
|
||||||
stew/shims/net, stew/byteutils,
|
stew/shims/net, stew/byteutils,
|
||||||
testutils/unittests,
|
testutils/unittests,
|
||||||
|
@ -19,7 +19,7 @@ import
|
||||||
../p2p/discv5_test_helper,
|
../p2p/discv5_test_helper,
|
||||||
../stubloglevel
|
../stubloglevel
|
||||||
|
|
||||||
procSuite "Utp protocol over discovery v5 tests":
|
procSuite "uTP over discovery v5 protocol":
|
||||||
let rng = newRng()
|
let rng = newRng()
|
||||||
let utpProtId = "test-utp".toBytes()
|
let utpProtId = "test-utp".toBytes()
|
||||||
|
|
||||||
|
@ -230,3 +230,90 @@ procSuite "Utp protocol over discovery v5 tests":
|
||||||
await serverSocket.destroyWait()
|
await serverSocket.destroyWait()
|
||||||
await node1.closeWait()
|
await node1.closeWait()
|
||||||
await node2.closeWait()
|
await node2.closeWait()
|
||||||
|
|
||||||
|
asyncTest "Data transfer over multiple sockets":
|
||||||
|
const
|
||||||
|
amountOfTransfers = 25
|
||||||
|
dataToSend: seq[byte] = repeat(byte 0xA0, 1_000_000)
|
||||||
|
|
||||||
|
var readFutures: seq[Future[void]]
|
||||||
|
|
||||||
|
proc readAndCheck(
|
||||||
|
socket: UtpSocket[NodeAddress],
|
||||||
|
): Future[void] {.async.} =
|
||||||
|
let readData = await socket.read()
|
||||||
|
check:
|
||||||
|
readData == dataToSend
|
||||||
|
socket.atEof()
|
||||||
|
|
||||||
|
proc handleIncomingConnection(
|
||||||
|
server: UtpRouter[NodeAddress],
|
||||||
|
client: UtpSocket[NodeAddress]
|
||||||
|
): Future[void] =
|
||||||
|
readFutures.add(client.readAndCheck())
|
||||||
|
|
||||||
|
var fut = newFuture[void]("test.AcceptConnectionCallback")
|
||||||
|
fut.complete()
|
||||||
|
return fut
|
||||||
|
|
||||||
|
proc handleIncomingConnectionDummy(
|
||||||
|
server: UtpRouter[NodeAddress],
|
||||||
|
client: UtpSocket[NodeAddress]
|
||||||
|
): Future[void] =
|
||||||
|
var fut = newFuture[void]("test.AcceptConnectionCallback")
|
||||||
|
fut.complete()
|
||||||
|
return fut
|
||||||
|
|
||||||
|
let
|
||||||
|
address1 = localAddress(20302)
|
||||||
|
address2 = localAddress(20303)
|
||||||
|
node1 = initDiscoveryNode(
|
||||||
|
rng, PrivateKey.random(rng[]), address1)
|
||||||
|
node2 = initDiscoveryNode(
|
||||||
|
rng, PrivateKey.random(rng[]), address2)
|
||||||
|
|
||||||
|
utp1 = UtpDiscv5Protocol.new(
|
||||||
|
node1, utpProtId, handleIncomingConnectionDummy)
|
||||||
|
utp2 {.used.} = UtpDiscv5Protocol.new(
|
||||||
|
node2, utpProtId, handleIncomingConnection)
|
||||||
|
|
||||||
|
# nodes must have session between each other
|
||||||
|
check:
|
||||||
|
(await node1.ping(node2.localNode)).isOk()
|
||||||
|
|
||||||
|
proc connectSendAndCheck(
|
||||||
|
utpProto: UtpDiscv5Protocol,
|
||||||
|
address: NodeAddress
|
||||||
|
): Future[void] {.async.} =
|
||||||
|
let socketRes = await utpProto.connectTo(address)
|
||||||
|
check:
|
||||||
|
socketRes.isOk()
|
||||||
|
let socket = socketRes.value()
|
||||||
|
let dataSend = await socket.write(dataToSend)
|
||||||
|
check:
|
||||||
|
dataSend.isOk()
|
||||||
|
dataSend.value() == dataToSend.len()
|
||||||
|
|
||||||
|
await socket.closeWait()
|
||||||
|
|
||||||
|
let t0 = Moment.now()
|
||||||
|
for i in 0..<amountOfTransfers:
|
||||||
|
asyncSpawn utp1.connectSendAndCheck(
|
||||||
|
NodeAddress.init(node2.localNode.id, address2))
|
||||||
|
|
||||||
|
while readFutures.len() < amountOfTransfers:
|
||||||
|
await sleepAsync(milliseconds(100))
|
||||||
|
|
||||||
|
await allFutures(readFutures)
|
||||||
|
let elapsed = Moment.now() - t0
|
||||||
|
|
||||||
|
await utp1.shutdownWait()
|
||||||
|
await utp2.shutdownWait()
|
||||||
|
|
||||||
|
let megabitsSent = amountOfTransfers * dataToSend.len() * 8 / 1_000_000
|
||||||
|
let seconds = float(elapsed.nanoseconds) / 1_000_000_000
|
||||||
|
let throughput = megabitsSent / seconds
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Sent ", amountOfTransfers, " asynchronous uTP transfers in ", seconds,
|
||||||
|
" seconds, payload throughput: ", throughput, " Mbit/s"
|
||||||
|
|
|
@ -12,7 +12,7 @@ import
|
||||||
../../eth/utp/packets,
|
../../eth/utp/packets,
|
||||||
../../eth/keys
|
../../eth/keys
|
||||||
|
|
||||||
suite "uTP Packet Encoding":
|
suite "uTP packet encoding":
|
||||||
test "Encode/decode SYN packet":
|
test "Encode/decode SYN packet":
|
||||||
let
|
let
|
||||||
synPacket = synPacket(5, 10, 20)
|
synPacket = synPacket(5, 10, 20)
|
||||||
|
|
|
@ -130,7 +130,7 @@ proc close(s: TwoClientsServerScenario) {.async.} =
|
||||||
await s.utp2.shutdownWait()
|
await s.utp2.shutdownWait()
|
||||||
await s.utp3.shutdownWait()
|
await s.utp3.shutdownWait()
|
||||||
|
|
||||||
procSuite "uTP over UDP protocol tests":
|
procSuite "uTP over UDP protocol":
|
||||||
let rng = newRng()
|
let rng = newRng()
|
||||||
|
|
||||||
asyncTest "Connect to remote host: test connection callback":
|
asyncTest "Connect to remote host: test connection callback":
|
||||||
|
|
|
@ -51,7 +51,7 @@ proc getServerSocket(
|
||||||
else:
|
else:
|
||||||
return some(srvSocket)
|
return some(srvSocket)
|
||||||
|
|
||||||
procSuite "Utp protocol over udp tests with loss and delays":
|
procSuite "uTP over UDP protocol with loss and delays":
|
||||||
let rng = newRng()
|
let rng = newRng()
|
||||||
|
|
||||||
proc sendBuilder(maxDelay: int, packetDropRate: int): SendCallbackBuilder =
|
proc sendBuilder(maxDelay: int, packetDropRate: int): SendCallbackBuilder =
|
||||||
|
|
|
@ -25,7 +25,7 @@ proc hash*(x: UtpSocketKey[int]): Hash =
|
||||||
type
|
type
|
||||||
TestError* = object of CatchableError
|
TestError* = object of CatchableError
|
||||||
|
|
||||||
procSuite "Utp router unit tests":
|
procSuite "uTP router unit":
|
||||||
let rng = newRng()
|
let rng = newRng()
|
||||||
let testSender = 1
|
let testSender = 1
|
||||||
let testSender2 = 2
|
let testSender2 = 2
|
||||||
|
|
|
@ -17,7 +17,7 @@ import
|
||||||
../../eth/keys,
|
../../eth/keys,
|
||||||
../stubloglevel
|
../stubloglevel
|
||||||
|
|
||||||
procSuite "uTP socket tests":
|
procSuite "uTP socket":
|
||||||
let
|
let
|
||||||
rng = newRng()
|
rng = newRng()
|
||||||
testAddress = initTAddress("127.0.0.1", 9079)
|
testAddress = initTAddress("127.0.0.1", 9079)
|
||||||
|
|
|
@ -18,7 +18,7 @@ import
|
||||||
../../eth/keys,
|
../../eth/keys,
|
||||||
../stubloglevel
|
../stubloglevel
|
||||||
|
|
||||||
procSuite "Utp socket selective acks unit test":
|
procSuite "uTP socket selective acks":
|
||||||
let rng = newRng()
|
let rng = newRng()
|
||||||
let testAddress = initTAddress("127.0.0.1", 9079)
|
let testAddress = initTAddress("127.0.0.1", 9079)
|
||||||
let defaultBufferSize = 1024'u32
|
let defaultBufferSize = 1024'u32
|
||||||
|
|
|
@ -13,7 +13,7 @@ import
|
||||||
../../eth/utp/packets,
|
../../eth/utp/packets,
|
||||||
../../eth/keys
|
../../eth/keys
|
||||||
|
|
||||||
suite "Utp packets test vectors":
|
suite "uTP packets test vectors":
|
||||||
test "SYN packet":
|
test "SYN packet":
|
||||||
let synPacket = Packet(
|
let synPacket = Packet(
|
||||||
header: PacketHeaderV1(
|
header: PacketHeaderV1(
|
||||||
|
|
Loading…
Reference in New Issue