Compare commits

...

35 Commits

Author SHA1 Message Date
Diego
e393835f9d test 2024-09-04 21:50:09 +02:00
Diego
81aa2bbded uncomment test 2024-09-04 21:34:47 +02:00
Diego
948d937178 comment test to save deps to cache 2024-09-04 21:25:51 +02:00
Diego
f8c1899cdf debug NIM_BRANCH var 2024-09-04 21:09:34 +02:00
Diego
330bbb39e7 upgrade quic 2024-09-04 20:52:38 +02:00
Diego
628bfa83f4 use sat solver for PRs when using Nim 2 2024-09-04 20:35:37 +02:00
Diego
f04b2dfb3c upgrade quic version in nimble file 2024-09-04 20:18:30 +02:00
Diego
56240f1011 formatting 2024-09-04 19:22:15 +02:00
Diego
7e589724af upgrade quic with support for Nim 2 2024-09-04 19:18:57 +02:00
diegomrsantos
cab28c2f8e Merge branch 'master' into quic 2024-09-03 11:07:49 +02:00
Diego
2fa44df1c5 upgrade quic to fix the error related json_serialization 2024-09-02 20:09:52 +02:00
Diego
b3f93851da fix multiaddress 2024-09-02 19:45:04 +02:00
Diego
ee3fdadfbb upgrade chronos 2024-09-02 19:22:56 +02:00
diegomrsantos
beaed7c4e3 Merge branch 'master' into quic 2024-09-02 19:13:09 +02:00
Diego
e61a190f66 fix compilation issues and tests 2024-09-02 19:12:34 +02:00
Diego
54031430dd upgrade quic and ngtcp2 2024-08-30 14:24:42 +02:00
Diego
6681116716 Merge branch 'master' into quic
# Conflicts:
#	libp2p.nimble
2024-08-30 14:18:34 +02:00
Diego
25011021ac upgrade quic 2024-06-26 21:34:30 +02:00
Diego
4078bf724e try to not declare upraises as a dep directly 2024-06-26 21:22:55 +02:00
Diego
e7bae90dc6 upgrade quic 2024-06-26 21:15:11 +02:00
Diego
c8c2d8e1e6 upgrade quick version and nimcrypto 2024-06-26 21:07:01 +02:00
Diego
6287bb02a1 Merge branch 'master' into quic
# Conflicts:
#	.pinned
#	libp2p.nim
#	libp2p.nimble
#	libp2p/wire.nim
#	tests/testswitch.nim
2024-06-26 15:27:34 +02:00
Tanguy
51c663956e fix tests 2023-01-17 18:07:43 +01:00
Tanguy
e6136ea8e9 Merge remote-tracking branch 'origin/unstable' into quic 2023-01-17 16:28:05 +01:00
Tanguy
905f221355 Observed address handling 2023-01-03 11:13:12 +01:00
Tanguy
af0a9ac66e Merge remote-tracking branch 'origin/unstable' into quic 2023-01-02 15:42:51 +01:00
Tanguy
3910728212 bump ngtcp2 2022-08-26 12:35:27 +02:00
Tanguy
8743bb92e2 Bump quic 2022-08-25 15:02:46 +02:00
Tanguy
0f6b934628 Merge remote-tracking branch 'origin/unstable' into quic 2022-08-25 14:59:52 +02:00
Tanguy
f3705a5fe1 Merge remote-tracking branch 'origin/unstable' into quic 2022-08-03 14:33:57 +02:00
Tanguy
54d6ca9789 Merge remote-tracking branch 'origin/unstable' into quic 2022-08-02 16:03:13 +02:00
Tanguy
7d6e66af39 fix ci 2022-08-02 16:00:45 +02:00
Tanguy
40ab9ff6d1 address review comments 2022-06-20 15:44:02 +02:00
Tanguy
9b7e1c6515 Bump nim-quic 2022-06-20 15:30:21 +02:00
Tanguy
c7eef2e0ae Initial implem
Co-authored-by: markspanbroek <mark@spanbroek.net>
2022-06-01 16:17:55 +02:00
10 changed files with 332 additions and 14 deletions

View File

@@ -104,9 +104,18 @@ jobs:
sudo update-alternatives --set gcc /usr/bin/gcc-14
- name: Run tests
env:
NIM_BRANCH: ${{ matrix.nim.branch }}
run: |
nim --version
nimble --version
gcc --version
NIMFLAGS="${NIMFLAGS} --mm:${{ matrix.nim.memory_management }}"
nimble test
if [[ "$NIM_BRANCH" == "version-2-0" ]]; then
dependency_solver="sat"
else
dependency_solver="legacy"
fi
NIMFLAGS="${NIMFLAGS} --mm:${{ matrix.nim.memory_management }} --solver:${dependency_solver}"
nimble test --solver:sat

View File

@@ -6,7 +6,9 @@ faststreams;https://github.com/status-im/nim-faststreams@#720fc5e5c8e428d9d0af61
httputils;https://github.com/status-im/nim-http-utils@#3b491a40c60aad9e8d3407443f46f62511e63b18
json_serialization;https://github.com/status-im/nim-json-serialization@#85b7ea093cb85ee4f433a617b97571bd709d30df
metrics;https://github.com/status-im/nim-metrics@#6142e433fc8ea9b73379770a788017ac528d46ff
ngtcp2;https://github.com/status-im/nim-ngtcp2@#6834f4756b6af58356ac9c4fef3d71db3c3ae5fe
nimcrypto;https://github.com/cheatfate/nimcrypto@#1c8d6e3caf3abc572136ae9a1da81730c4eb4288
quic;https://github.com/status-im/nim-quic.git@#d98c75dce82853b9833f6d5fe35bf0a1db1f920c
results;https://github.com/arnetheduck/nim-results@#f3c666a272c69d70cb41e7245e7f6844797303ad
secp256k1;https://github.com/status-im/nim-secp256k1@#7246d91c667f4cc3759fdd50339caa45a2ecd8be
serialization;https://github.com/status-im/nim-serialization@#4bdbc29e54fe54049950e352bb969aab97173b35

View File

@@ -52,6 +52,7 @@ else:
stream/connection,
transports/transport,
transports/tcptransport,
transports/quictransport,
protocols/secure/noise,
cid,
multihash,

View File

@@ -8,9 +8,10 @@ license = "MIT"
skipDirs = @["tests", "examples", "Nim", "tools", "scripts", "docs"]
requires "nim >= 1.6.0",
"nimcrypto >= 0.4.1", "dnsclient >= 0.3.0 & < 0.4.0", "bearssl >= 0.2.5",
"nimcrypto >= 0.6.0 & < 0.7.0", "dnsclient >= 0.3.0 & < 0.4.0", "bearssl >= 0.2.5",
"chronicles >= 0.10.2", "chronos >= 4.0.2", "metrics", "secp256k1", "stew#head",
"websock", "unittest2"
"websock", "unittest2",
"https://github.com/status-im/nim-quic.git#d98c75dce82853b9833f6d5fe35bf0a1db1f920c"
let nimc = getEnv("NIMC", "nim") # Which nim compiler to use
let lang = getEnv("NIMLANG", "c") # Which backend (c/cpp/js)

View File

@@ -408,7 +408,12 @@ const
UDP_IP* = mapAnd(IP, mapEq("udp"))
UDP* = mapOr(UDP_DNS, UDP_IP)
UTP* = mapAnd(UDP, mapEq("utp"))
QUIC* = mapAnd(UDP, mapEq("quic"))
QUIC_IP* = mapAnd(UDP_IP, mapEq("quic"))
QUIC_DNS* = mapAnd(UDP_DNS, mapEq("quic"))
QUIC* = mapOr(QUIC_DNS, QUIC_IP)
QUIC_V1_IP* = mapAnd(UDP_IP, mapEq("quic-v1"))
QUIC_V1_DNS* = mapAnd(UDP_DNS, mapEq("quic-v1"))
QUIC_V1* = mapOr(QUIC_V1_DNS, QUIC_V1_IP)
UNIX* = mapEq("unix")
WS_DNS* = mapAnd(TCP_DNS, mapEq("ws"))
WS_IP* = mapAnd(TCP_IP, mapEq("ws"))

View File

@@ -0,0 +1,229 @@
import std/sequtils
import pkg/chronos
import pkg/chronicles
import pkg/quic
import ../multiaddress
import ../multicodec
import ../stream/connection
import ../wire
import ../muxers/muxer
import ../upgrademngrs/upgrade
import ./transport
export multiaddress
export multicodec
export connection
export transport
when (NimMajor, NimMinor) < (1, 4):
{.push raises: [Defect].}
else:
{.push raises: [].}
logScope:
topics = "libp2p quictransport"
type
P2PConnection = connection.Connection
QuicConnection = quic.Connection
# Stream
type QuicStream* = ref object of P2PConnection
stream: Stream
cached: seq[byte]
proc new(
_: type QuicStream, stream: Stream, oaddr: Opt[MultiAddress], peerId: PeerId
): QuicStream =
let quicstream = QuicStream(stream: stream, observedAddr: oaddr, peerId: peerId)
procCall P2PConnection(quicstream).initStream()
quicstream
template mapExceptions(body: untyped) =
try:
body
except QuicError:
raise newLPStreamEOFError()
except CatchableError:
raise newLPStreamEOFError()
method readOnce*(
stream: QuicStream, pbytes: pointer, nbytes: int
): Future[int] {.async: (raises: [CancelledError, LPStreamError]).} =
try:
if stream.cached.len == 0:
stream.cached = await stream.stream.read()
result = min(nbytes, stream.cached.len)
copyMem(pbytes, addr stream.cached[0], result)
stream.cached = stream.cached[result ..^ 1]
except CatchableError as exc:
raise newLPStreamEOFError()
{.push warning[LockLevel]: off.}
method write*(
stream: QuicStream, bytes: seq[byte]
) {.async: (raises: [CancelledError, LPStreamError]).} =
mapExceptions(await stream.stream.write(bytes))
{.pop.}
method closeImpl*(stream: QuicStream) {.async: (raises: []).} =
try:
await stream.stream.close()
except CatchableError as exc:
discard
await procCall P2PConnection(stream).closeImpl()
# Session
type QuicSession* = ref object of P2PConnection
connection: QuicConnection
method close*(session: QuicSession) {.async, base.} =
await session.connection.close()
await procCall P2PConnection(session).close()
proc getStream*(
session: QuicSession, direction = Direction.In
): Future[QuicStream] {.async.} =
var stream: Stream
case direction
of Direction.In:
stream = await session.connection.incomingStream()
of Direction.Out:
stream = await session.connection.openStream()
await stream.write(@[]) # QUIC streams do not exist until data is sent
return QuicStream.new(stream, session.observedAddr, session.peerId)
method getWrapped*(self: QuicSession): P2PConnection =
nil
# Muxer
type QuicMuxer = ref object of Muxer
quicSession: QuicSession
handleFut: Future[void]
method newStream*(
m: QuicMuxer, name: string = "", lazy: bool = false
): Future[P2PConnection] {.
async: (raises: [CancelledError, LPStreamError, MuxerError])
.} =
try:
return await m.quicSession.getStream(Direction.Out)
except CatchableError as exc:
raise newException(MuxerError, exc.msg, exc)
proc handleStream(m: QuicMuxer, chann: QuicStream) {.async.} =
## call the muxer stream handler for this channel
##
try:
await m.streamHandler(chann)
trace "finished handling stream"
doAssert(chann.closed, "connection not closed by handler!")
except CatchableError as exc:
trace "Exception in mplex stream handler", msg = exc.msg
await chann.close()
method handle*(m: QuicMuxer): Future[void] {.async: (raises: []).} =
try:
while not m.quicSession.atEof:
let incomingStream = await m.quicSession.getStream(Direction.In)
asyncSpawn m.handleStream(incomingStream)
except CatchableError as exc:
trace "Exception in mplex handler", msg = exc.msg
method close*(m: QuicMuxer) {.async: (raises: []).} =
try:
await m.quicSession.close()
m.handleFut.cancel()
except CatchableError as exc:
discard
# Transport
type QuicUpgrade = ref object of Upgrade
type QuicTransport* = ref object of Transport
listener: Listener
connections: seq[P2PConnection]
func new*(_: type QuicTransport, u: Upgrade): QuicTransport =
QuicTransport(upgrader: QuicUpgrade(ms: u.ms))
method handles*(transport: QuicTransport, address: MultiAddress): bool =
if not procCall Transport(transport).handles(address):
return false
QUIC_V1.match(address)
method start*(transport: QuicTransport, addrs: seq[MultiAddress]) {.async.} =
doAssert transport.listener.isNil, "start() already called"
#TODO handle multiple addr
transport.listener = listen(initTAddress(addrs[0]).tryGet)
await procCall Transport(transport).start(addrs)
transport.addrs[0] =
MultiAddress.init(transport.listener.localAddress(), IPPROTO_UDP).tryGet() &
MultiAddress.init("/quic-v1").get()
transport.running = true
method stop*(transport: QuicTransport) {.async.} =
if transport.running:
for c in transport.connections:
await c.close()
await procCall Transport(transport).stop()
await transport.listener.stop()
transport.running = false
transport.listener = nil
proc wrapConnection(
transport: QuicTransport, connection: QuicConnection
): P2PConnection {.raises: [Defect, TransportOsError, LPError].} =
let
remoteAddr = connection.remoteAddress()
observedAddr =
MultiAddress.init(remoteAddr, IPPROTO_UDP).get() &
MultiAddress.init("/quic-v1").get()
conres = QuicSession(connection: connection, observedAddr: Opt.some(observedAddr))
conres.initStream()
transport.connections.add(conres)
proc onClose() {.async.} =
await conres.join()
transport.connections.keepItIf(it != conres)
trace "Cleaned up client"
asyncSpawn onClose()
return conres
method accept*(transport: QuicTransport): Future[P2PConnection] {.async.} =
doAssert not transport.listener.isNil, "call start() before calling accept()"
let connection = await transport.listener.accept()
return transport.wrapConnection(connection)
method dial*(
transport: QuicTransport,
hostname: string,
address: MultiAddress,
peerId: Opt[PeerId] = Opt.none(PeerId),
): Future[P2PConnection] {.async, gcsafe.} =
let connection = await dial(initTAddress(address).tryGet)
return transport.wrapConnection(connection)
method upgrade*(
self: QuicTransport, conn: P2PConnection, peerId: Opt[PeerId]
): Future[Muxer] {.async: (raises: [CancelledError, LPError]).} =
let qs = QuicSession(conn)
if peerId.isSome:
qs.peerId = peerId.get()
let muxer = QuicMuxer(quicSession: qs, connection: conn)
muxer.streamHandler = proc(conn: P2PConnection) {.async: (raises: []).} =
trace "Starting stream handler"
try:
await self.upgrader.ms.handle(conn) # handle incoming connection
except CancelledError as exc:
return
except CatchableError as exc:
trace "exception in stream handler", conn, msg = exc.msg
finally:
await conn.closeWithEOF()
trace "Stream handler done", conn
muxer.handleFut = muxer.handle()
return muxer

View File

@@ -20,7 +20,7 @@ when defined(windows): import winlean else: import posix
const
RTRANSPMA* = mapOr(TCP, WebSockets, UNIX)
TRANSPMA* = mapOr(RTRANSPMA, UDP)
TRANSPMA* = mapOr(RTRANSPMA, QUIC, UDP)
proc initTAddress*(ma: MultiAddress): MaResult[TransportAddress] =
## Initialize ``TransportAddress`` with MultiAddress ``ma``.
@@ -28,7 +28,7 @@ proc initTAddress*(ma: MultiAddress): MaResult[TransportAddress] =
## MultiAddress must be wire address, e.g. ``{IP4, IP6, UNIX}/{TCP, UDP}``.
##
if mapOr(TCP_IP, WebSockets_IP, UNIX, UDP_IP).match(ma):
if mapOr(TCP_IP, WebSockets_IP, UNIX, UDP_IP, QUIC_V1_IP).match(ma):
var pbuf: array[2, byte]
let code = (?(?ma[0]).protoCode())
if code == multiCodec("unix"):

View File

@@ -26,10 +26,11 @@ const
SuccessVectors = [
"/ip4/1.2.3.4", "/ip4/0.0.0.0", "/ip6/::1",
"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21",
"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21/udp/1234/quic", "/ip6zone/x/ip6/fe80::1",
"/ip6zone/x%y/ip6/fe80::1", "/ip6zone/x%y/ip6/::",
"/ip6zone/x/ip6/fe80::1/udp/1234/quic", "/onion/timaq4ygg2iegci7:1234",
"/onion/timaq4ygg2iegci7:80/http",
"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21/udp/1234/quic",
"/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21/udp/1234/quic-v1",
"/ip6zone/x/ip6/fe80::1", "/ip6zone/x%y/ip6/fe80::1", "/ip6zone/x%y/ip6/::",
"/ip6zone/x/ip6/fe80::1/udp/1234/quic", "/ip6zone/x/ip6/fe80::1/udp/1234/quic-v1",
"/onion/timaq4ygg2iegci7:1234", "/onion/timaq4ygg2iegci7:80/http",
"/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd:1234",
"/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd:80/http",
"/udp/0", "/tcp/0", "/sctp/0", "/udp/1234", "/tcp/1234", "/sctp/1234", "/udp/65535",
@@ -57,7 +58,7 @@ const
FailureVectors = [
"", "/", "/ip4", "/ip4/::1", "/ip4/fdpsofodsajfdoisa", "/ip6", "/ip6zone",
"/ip6zone/", "/ip6zone//ip6/fe80::1", "/udp", "/tcp", "/sctp", "/udp/65536",
"/tcp/65536", "/quic/65536", "/onion/9imaq4ygg2iegci7:80",
"/tcp/65536", "/quic/65536", "/quic-v1/65536", "/onion/9imaq4ygg2iegci7:80",
"/onion/aaimaq4ygg2iegci7:80", "/onion/timaq4ygg2iegci7:0",
"/onion/timaq4ygg2iegci7:-1", "/onion/timaq4ygg2iegci7",
"/onion/timaq4ygg2iegci@:666",
@@ -70,8 +71,8 @@ const
"/udp/1234/sctp", "/udp/1234/udt/1234", "/udp/1234/utp/1234",
"/ip4/127.0.0.1/udp/jfodsajfidosajfoidsa", "/ip4/127.0.0.1/udp",
"/ip4/127.0.0.1/tcp/jfodsajfidosajfoidsa", "/ip4/127.0.0.1/tcp",
"/ip4/127.0.0.1/quic/1234", "/ip4/127.0.0.1/ipfs", "/ip4/127.0.0.1/ipfs/tcp",
"/ip4/127.0.0.1/p2p", "/ip4/127.0.0.1/p2p/tcp", "/unix",
"/ip4/127.0.0.1/quic/1234", "/ip4/127.0.0.1/quic-v1/1234", "/ip4/127.0.0.1/ipfs",
"/ip4/127.0.0.1/ipfs/tcp", "/ip4/127.0.0.1/p2p", "/ip4/127.0.0.1/p2p/tcp", "/unix",
]
RustSuccessVectors = [
@@ -160,6 +161,15 @@ const
"/quic",
],
),
PatternVector(
pattern: QUIC_V1,
good: @["/ip4/1.2.3.4/udp/1234/quic-v1", "/ip6/::/udp/1234/quic-v1"],
bad:
@[
"/ip4/0.0.0.0/tcp/12345/quic-v1", "/ip6/fc00::/ip4/0.0.0.0/udp/1234/quic-v1",
"/quic-v1",
],
),
PatternVector(
pattern: IPFS,
good:

24
tests/testquic.nim Normal file
View File

@@ -0,0 +1,24 @@
{.used.}
import sequtils
import chronos, stew/byteutils
import
../libp2p/[
stream/connection,
transports/transport,
transports/quictransport,
upgrademngrs/upgrade,
multiaddress,
errors,
wire,
]
import ./helpers, ./commontransport
suite "Quic transport":
asyncTest "can handle local address":
let ma = @[MultiAddress.init("/ip4/127.0.0.1/udp/0/quic-v1").tryGet()]
let transport1 = QuicTransport.new()
await transport1.start(ma)
check transport1.handles(transport1.addrs[0])
await transport1.stop()

View File

@@ -34,6 +34,7 @@ import
utils/semaphore,
transports/tcptransport,
transports/wstransport,
transports/quictransport,
]
import ./helpers
@@ -988,6 +989,42 @@ suite "Switch":
await srcWsSwitch.stop()
await srcTcpSwitch.stop()
asyncTest "e2e quic transport":
let
quicAddress1 = MultiAddress.init("/ip4/127.0.0.1/udp/0/quic-v1").tryGet()
quicAddress2 = MultiAddress.init("/ip4/127.0.0.1/udp/0/quic-v1").tryGet()
srcSwitch = SwitchBuilder
.new()
.withAddress(quicAddress1)
.withRng(crypto.newRng())
.withTransport(
proc(upgr: Upgrade): Transport =
QuicTransport.new(upgr)
)
.withNoise()
.build()
destSwitch = SwitchBuilder
.new()
.withAddress(quicAddress2)
.withRng(crypto.newRng())
.withTransport(
proc(upgr: Upgrade): Transport =
QuicTransport.new(upgr)
)
.withNoise()
.build()
await destSwitch.start()
await srcSwitch.start()
await srcSwitch.connect(destSwitch.peerInfo.peerId, destSwitch.peerInfo.addrs)
check srcSwitch.isConnected(destSwitch.peerInfo.peerId)
await destSwitch.stop()
await srcSwitch.stop()
asyncTest "mount unstarted protocol":
proc handle(conn: Connection, proto: string) {.async.} =
check "test123" == string.fromBytes(await conn.readLp(1024))