diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 18f3e231d..7f38f190c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,38 @@ jobs: with: path: nim-libp2p submodules: true + + - name: Derive environment variables + shell: bash + run: | + if [[ '${{ matrix.target.cpu }}' == 'amd64' ]]; then + ARCH=64 + PLATFORM=x64 + else + ARCH=32 + PLATFORM=x86 + fi + echo "ARCH=$ARCH" >> $GITHUB_ENV + echo "PLATFORM=$PLATFORM" >> $GITHUB_ENV + + ncpu= + ext= + case '${{ runner.os }}' in + 'Linux') + ncpu=$(nproc) + ;; + 'macOS') + ncpu=$(sysctl -n hw.ncpu) + ;; + 'Windows') + ncpu=$NUMBER_OF_PROCESSORS + ext=.exe + ;; + esac + [[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1 + echo "ncpu=$ncpu" >> $GITHUB_ENV + echo "ext=$ext" >> $GITHUB_ENV + - name: Install build dependencies (Linux i386) if: runner.os == 'Linux' && matrix.target.cpu == 'i386' run: | @@ -63,22 +95,50 @@ jobs: chmod 755 external/bin/gcc external/bin/g++ echo '${{ github.workspace }}/external/bin' >> $GITHUB_PATH - - name: Install build dependencies (Windows) + - name: Restore MinGW-W64 (Windows) from cache if: runner.os == 'Windows' + id: windows-mingw-cache + uses: actions/cache@v2 + with: + path: external/mingw-${{ matrix.target.cpu }} + key: 'mingw-${{ matrix.target.cpu }}' + + - name: Restore Nim DLLs dependencies (Windows) from cache + if: runner.os == 'Windows' + id: windows-dlls-cache + uses: actions/cache@v2 + with: + path: external/dlls-${{ matrix.target.cpu }} + key: 'dlls-${{ matrix.target.cpu }}' + + - name: Install MinGW64 dependency (Windows) + if: > + steps.windows-mingw-cache.outputs.cache-hit != 'true' && + runner.os == 'Windows' shell: bash run: | - mkdir external - if [[ '${{ matrix.target.cpu }}' == 'amd64' ]]; then - arch=64 - else - arch=32 - fi - curl -L "https://nim-lang.org/download/mingw$arch-6.3.0.7z" -o "external/mingw$arch.7z" + mkdir -p external + curl -L "https://nim-lang.org/download/mingw$ARCH.7z" -o "external/mingw-${{ matrix.target.cpu }}.7z" + 7z x -y "external/mingw-${{ matrix.target.cpu }}.7z" -oexternal/ + mv external/mingw$ARCH external/mingw-${{ matrix.target.cpu }} + + - name: Install DLLs dependencies (Windows) + if: > + steps.windows-dlls-cache.outputs.cache-hit != 'true' && + runner.os == 'Windows' + shell: bash + run: | + mkdir -p external curl -L "https://nim-lang.org/download/windeps.zip" -o external/windeps.zip - 7z x "external/mingw$arch.7z" -oexternal/ - 7z x external/windeps.zip -oexternal/dlls - echo '${{ github.workspace }}'"/external/mingw$arch/bin" >> $GITHUB_PATH - echo '${{ github.workspace }}'"/external/dlls" >> $GITHUB_PATH + 7z x -y external/windeps.zip -oexternal/dlls-${{ matrix.target.cpu }} + + - name: Path to cached dependencies (Windows) + if: > + runner.os == 'Windows' + shell: bash + run: | + echo "${{ github.workspace }}/external/mingw-${{ matrix.target.cpu }}/bin" >> $GITHUB_PATH + echo "${{ github.workspace }}/external/dlls-${{ matrix.target.cpu }}" >> $GITHUB_PATH - name: Setup environment shell: bash @@ -133,20 +193,6 @@ jobs: if: steps.nim-cache.outputs.cache-hit != 'true' shell: bash run: | - ncpu= - ext= - case '${{ runner.os }}' in - 'Linux') - ncpu=$(nproc) - ;; - 'macOS') - ncpu=$(sysctl -n hw.ncpu) - ;; - 'Windows') - ncpu=$NUMBER_OF_PROCESSORS - ext=.exe - ;; - esac [[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1 if [[ ! -e csources/bin/nim$ext ]]; then make -C csources -j $ncpu CC=gcc ucpu='${{ matrix.target.cpu }}' @@ -164,29 +210,6 @@ jobs: rm -rf dist rm -rf .git - # - name: Install library dependencies (Linux amd64) - # if: runner.os == 'Linux' && matrix.target.cpu == 'amd64' - # run: | - # sudo DEBIAN_FRONTEND='noninteractive' apt-get install \ - # --no-install-recommends -yq libgmp-dev - # - # - name: Install library dependencies (Linux i386) - # if: runner.os == 'Linux' && matrix.target.cpu == 'i386' - # run: | - # sudo DEBIAN_FRONTEND='noninteractive' apt-get install \ - # libgmp-dev:i386 - # - # - name: Install library dependencies (macOS) - # if: runner.os == 'macOS' - # run: brew install gmp - # - # - name: Install library dependencies (Windows) - # if: runner.os == 'Windows' - # shell: bash - # run: | - # choco install msys2 - # pacman -S mingw-w64-x86_64-gmp - - name: Setup Go uses: actions/setup-go@v2 with: diff --git a/libp2p/builders.nim b/libp2p/builders.nim index be03b35b8..0bf097feb 100644 --- a/libp2p/builders.nim +++ b/libp2p/builders.nim @@ -1,3 +1,14 @@ +## Nim-Libp2p +## Copyright (c) 2020 Status Research & Development GmbH +## Licensed under either of +## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) +## * MIT license ([LICENSE-MIT](LICENSE-MIT)) +## at your option. +## This file may not be copied, modified, or distributed except according to +## those terms. + +{.push raises: [Defect].} + import options, tables, chronos, bearssl, switch, peerid, peerinfo, stream/connection, multiaddress, @@ -36,9 +47,14 @@ type agentVersion: string proc new*(T: type[SwitchBuilder]): T = + + let address = MultiAddress + .init("/ip4/127.0.0.1/tcp/0") + .expect("address should initialize to default") + SwitchBuilder( privKey: none(PrivateKey), - address: MultiAddress.init("/ip4/127.0.0.1/tcp/0").tryGet(), + address: address, secureManagers: @[], tcpTransportOpts: TcpTransportOpts(), maxConnections: MaxConnections, @@ -109,10 +125,11 @@ proc withAgentVersion*(b: SwitchBuilder, agentVersion: string): SwitchBuilder = proc build*(b: SwitchBuilder): Switch = if b.rng == nil: # newRng could fail - raise (ref CatchableError)(msg: "Cannot initialize RNG") + raise newException(Defect, "Cannot initialize RNG") + let pkRes = PrivateKey.random(b.rng[]) let - seckey = b.privKey.get(otherwise = PrivateKey.random(b.rng[]).tryGet()) + seckey = b.privKey.get(otherwise = pkRes.expect("Should supply a valid RNG")) var secureManagerInstances: seq[Secure] @@ -120,11 +137,11 @@ proc build*(b: SwitchBuilder): Switch = secureManagerInstances.add(newNoise(b.rng, seckey).Secure) let - peerInfo = block: - var info = PeerInfo.init(seckey, [b.address]) - info.protoVersion = b.protoVersion - info.agentVersion = b.agentVersion - info + peerInfo = PeerInfo.init( + seckey, + [b.address], + protoVersion = b.protoVersion, + agentVersion = b.agentVersion) let muxers = block: @@ -149,16 +166,19 @@ proc build*(b: SwitchBuilder): Switch = if isNil(b.rng): b.rng = newRng() - let switch = newSwitch( - peerInfo = peerInfo, - transports = transports, - identity = identify, - muxers = muxers, - secureManagers = secureManagerInstances, - maxConnections = b.maxConnections, - maxIn = b.maxIn, - maxOut = b.maxOut, - maxConnsPerPeer = b.maxConnsPerPeer) + let switch = try: + newSwitch( + peerInfo = peerInfo, + transports = transports, + identity = identify, + muxers = muxers, + secureManagers = secureManagerInstances, + maxConnections = b.maxConnections, + maxIn = b.maxIn, + maxOut = b.maxOut, + maxConnsPerPeer = b.maxConnsPerPeer) + except CatchableError as exc: + raise newException(Defect, exc.msg) return switch diff --git a/libp2p/connmanager.nim b/libp2p/connmanager.nim index 423fe47cb..cfed78396 100644 --- a/libp2p/connmanager.nim +++ b/libp2p/connmanager.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import std/[options, tables, sequtils, sets] import chronos, chronicles, metrics import peerinfo, @@ -25,9 +27,10 @@ const MaxConnectionsPerPeer* = 5 type - TooManyConnectionsError* = object of CatchableError + TooManyConnectionsError* = object of LPError - ConnProvider* = proc(): Future[Connection] {.gcsafe, closure.} + ConnProvider* = proc(): Future[Connection] + {.gcsafe, closure, raises: [Defect].} ConnEventKind* {.pure.} = enum Connected, # A connection was made and securely upgraded - there may be @@ -45,7 +48,8 @@ type discard ConnEventHandler* = - proc(peerId: PeerID, event: ConnEvent): Future[void] {.gcsafe.} + proc(peerId: PeerID, event: ConnEvent): Future[void] + {.gcsafe, raises: [Defect].} PeerEventKind* {.pure.} = enum Left, @@ -105,13 +109,27 @@ proc addConnEventHandler*(c: ConnManager, ## Add peer event handler - handlers must not raise exceptions! ## - if isNil(handler): return - c.connEvents[kind].incl(handler) + try: + if isNil(handler): return + c.connEvents[kind].incl(handler) + except Exception as exc: + # TODO: there is an Exception being raised + # somewhere in the depths of the std. + # Might be related to https://github.com/nim-lang/Nim/issues/17382 + + raiseAssert exc.msg proc removeConnEventHandler*(c: ConnManager, handler: ConnEventHandler, kind: ConnEventKind) = - c.connEvents[kind].excl(handler) + try: + c.connEvents[kind].excl(handler) + except Exception as exc: + # TODO: there is an Exception being raised + # somewhere in the depths of the std. + # Might be related to https://github.com/nim-lang/Nim/issues/17382 + + raiseAssert exc.msg proc triggerConnEvent*(c: ConnManager, peerId: PeerID, @@ -138,12 +156,26 @@ proc addPeerEventHandler*(c: ConnManager, ## if isNil(handler): return - c.peerEvents[kind].incl(handler) + try: + c.peerEvents[kind].incl(handler) + except Exception as exc: + # TODO: there is an Exception being raised + # somewhere in the depths of the std. + # Might be related to https://github.com/nim-lang/Nim/issues/17382 + + raiseAssert exc.msg proc removePeerEventHandler*(c: ConnManager, handler: PeerEventHandler, kind: PeerEventKind) = - c.peerEvents[kind].excl(handler) + try: + c.peerEvents[kind].excl(handler) + except Exception as exc: + # TODO: there is an Exception being raised + # somewhere in the depths of the std. + # Might be related to https://github.com/nim-lang/Nim/issues/17382 + + raiseAssert exc.msg proc triggerPeerEvents*(c: ConnManager, peerId: PeerID, @@ -205,7 +237,7 @@ proc contains*(c: ConnManager, muxer: Muxer): bool = if conn notin c.muxed: return - return muxer == c.muxed[conn].muxer + return muxer == c.muxed.getOrDefault(conn).muxer proc closeMuxerHolder(muxerHolder: MuxerHolder) {.async.} = trace "Cleaning up muxer", m = muxerHolder.muxer @@ -338,11 +370,12 @@ proc selectMuxer*(c: ConnManager, conn: Connection): Muxer = return if conn in c.muxed: - return c.muxed[conn].muxer + return c.muxed.getOrDefault(conn).muxer else: debug "no muxer for connection", conn -proc storeConn*(c: ConnManager, conn: Connection) = +proc storeConn*(c: ConnManager, conn: Connection) + {.raises: [Defect, CatchableError].} = ## store a connection ## @@ -456,7 +489,8 @@ proc trackOutgoingConn*(c: ConnManager, proc storeMuxer*(c: ConnManager, muxer: Muxer, - handle: Future[void] = nil) = + handle: Future[void] = nil) + {.raises: [Defect, CatchableError].} = ## store the connection and muxer ## diff --git a/libp2p/daemon/daemonapi.nim b/libp2p/daemon/daemonapi.nim index 6e1ced9d8..3ecc16bc6 100644 --- a/libp2p/daemon/daemonapi.nim +++ b/libp2p/daemon/daemonapi.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + ## This module implementes API for `go-libp2p-daemon`. import std/[os, osproc, strutils, tables, strtabs] import chronos, chronicles @@ -467,7 +469,7 @@ proc checkResponse(pb: var ProtoBuffer): ResponseKind {.inline.} = else: result = ResponseKind.Error -proc getErrorMessage(pb: var ProtoBuffer): string {.inline.} = +proc getErrorMessage(pb: var ProtoBuffer): string {.inline, raises: [Defect, DaemonLocalError].} = if pb.enterSubmessage() == cast[int](ResponseType.ERROR): if pb.getString(1, result) == -1: raise newException(DaemonLocalError, "Error message is missing!") @@ -825,7 +827,8 @@ proc transactMessage(transp: StreamTransport, raise newException(DaemonLocalError, "Incorrect or empty message received!") result = initProtoBuffer(message) -proc getPeerInfo(pb: var ProtoBuffer): PeerInfo = +proc getPeerInfo(pb: var ProtoBuffer): PeerInfo + {.raises: [Defect, DaemonLocalError].} = ## Get PeerInfo object from ``pb``. result.addresses = newSeq[MultiAddress]() if pb.getValue(1, result.peer) == -1: @@ -834,7 +837,11 @@ proc getPeerInfo(pb: var ProtoBuffer): PeerInfo = while pb.getBytes(2, address) != -1: if len(address) != 0: var copyaddr = address - result.addresses.add(MultiAddress.init(copyaddr).tryGet()) + let addrRes = MultiAddress.init(copyaddr) + if addrRes.isErr: + raise newException(DaemonLocalError, addrRes.error) + + result.addresses.add(MultiAddress.init(copyaddr).get()) address.setLen(0) proc identity*(api: DaemonAPI): Future[PeerInfo] {.async.} = @@ -996,26 +1003,31 @@ proc cmTrimPeers*(api: DaemonAPI) {.async.} = finally: await api.closeConnection(transp) -proc dhtGetSinglePeerInfo(pb: var ProtoBuffer): PeerInfo = +proc dhtGetSinglePeerInfo(pb: var ProtoBuffer): PeerInfo + {.raises: [Defect, DaemonLocalError].} = if pb.enterSubmessage() == 2: result = pb.getPeerInfo() else: raise newException(DaemonLocalError, "Missing required field `peer`!") -proc dhtGetSingleValue(pb: var ProtoBuffer): seq[byte] = +proc dhtGetSingleValue(pb: var ProtoBuffer): seq[byte] + {.raises: [Defect, DaemonLocalError].} = result = newSeq[byte]() if pb.getLengthValue(3, result) == -1: raise newException(DaemonLocalError, "Missing field `value`!") -proc dhtGetSinglePublicKey(pb: var ProtoBuffer): PublicKey = +proc dhtGetSinglePublicKey(pb: var ProtoBuffer): PublicKey + {.raises: [Defect, DaemonLocalError].} = if pb.getValue(3, result) == -1: raise newException(DaemonLocalError, "Missing field `value`!") -proc dhtGetSinglePeerID(pb: var ProtoBuffer): PeerID = +proc dhtGetSinglePeerID(pb: var ProtoBuffer): PeerID + {.raises: [Defect, DaemonLocalError].} = if pb.getValue(3, result) == -1: raise newException(DaemonLocalError, "Missing field `value`!") -proc enterDhtMessage(pb: var ProtoBuffer, rt: DHTResponseType) {.inline.} = +proc enterDhtMessage(pb: var ProtoBuffer, rt: DHTResponseType) + {.inline, raises: [Defect, DaemonLocalError].} = var dtype: uint var res = pb.enterSubmessage() if res == cast[int](ResponseType.DHT): @@ -1026,12 +1038,14 @@ proc enterDhtMessage(pb: var ProtoBuffer, rt: DHTResponseType) {.inline.} = else: raise newException(DaemonLocalError, "Wrong message type!") -proc enterPsMessage(pb: var ProtoBuffer) {.inline.} = +proc enterPsMessage(pb: var ProtoBuffer) + {.inline, raises: [Defect, DaemonLocalError].} = var res = pb.enterSubmessage() if res != cast[int](ResponseType.PUBSUB): raise newException(DaemonLocalError, "Wrong message type!") -proc getDhtMessageType(pb: var ProtoBuffer): DHTResponseType {.inline.} = +proc getDhtMessageType(pb: var ProtoBuffer): DHTResponseType + {.inline, raises: [Defect, DaemonLocalError].} = var dtype: uint if pb.getVarintValue(1, dtype) == 0: raise newException(DaemonLocalError, "Missing required DHT field `type`!") diff --git a/libp2p/daemon/transpool.nim b/libp2p/daemon/transpool.nim index 8d907ff73..ae7ddab61 100644 --- a/libp2p/daemon/transpool.nim +++ b/libp2p/daemon/transpool.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + ## This module implements Pool of StreamTransport. import chronos diff --git a/libp2p/errors.nim b/libp2p/errors.nim index 605425350..7fa50d988 100644 --- a/libp2p/errors.nim +++ b/libp2p/errors.nim @@ -5,8 +5,14 @@ import chronos import chronicles import macros +type + # Base exception type for libp2p + LPError* = object of CatchableError + LPAllFuturesError* = object of LPError + errors*: seq[ref CatchableError] + # could not figure how to make it with a simple template -# sadly nim needs more love for hygenic templates +# sadly nim needs more love for hygienic templates # so here goes the macro, its based on the proc/template version # and uses quote do so it's quite readable diff --git a/libp2p/multiaddress.nim b/libp2p/multiaddress.nim index 628e43b1c..b28e6bcfa 100644 --- a/libp2p/multiaddress.nim +++ b/libp2p/multiaddress.nim @@ -11,11 +11,11 @@ {.push raises: [Defect].} -import nativesockets, hashes +import pkg/chronos +import std/[nativesockets, hashes] import tables, strutils, stew/shims/net -import chronos import multicodec, multihash, multibase, transcoder, vbuffer, peerid, - protobuf/minprotobuf + protobuf/minprotobuf, errors import stew/[base58, base32, endians2, results] export results, minprotobuf, vbuffer @@ -46,7 +46,7 @@ type MaResult*[T] = Result[T, string] - MaError* = object of CatchableError + MaError* = object of LPError MaInvalidAddress* = object of MaError IpTransportProtocol* = enum diff --git a/libp2p/multistream.nim b/libp2p/multistream.nim index cf16e25c2..3c3306991 100644 --- a/libp2p/multistream.nim +++ b/libp2p/multistream.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import std/[strutils] import chronos, chronicles, stew/byteutils import stream/connection, @@ -25,7 +27,7 @@ const Ls* = "\x03ls\n" type - Matcher* = proc (proto: string): bool {.gcsafe.} + Matcher* = proc (proto: string): bool {.gcsafe, raises: [Defect].} HandlerHolder* = object protos*: seq[string] diff --git a/libp2p/muxers/mplex/coder.nim b/libp2p/muxers/mplex/coder.nim index d5e328d19..c09fdd2bb 100644 --- a/libp2p/muxers/mplex/coder.nim +++ b/libp2p/muxers/mplex/coder.nim @@ -7,12 +7,15 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import chronos import nimcrypto/utils, chronicles, stew/byteutils import ../../stream/connection, ../../utility, ../../varint, - ../../vbuffer + ../../vbuffer, + ../muxer logScope: topics = "libp2p mplexcoder" @@ -32,7 +35,7 @@ type msgType: MessageType data: seq[byte] - InvalidMplexMsgType = object of CatchableError + InvalidMplexMsgType* = object of MuxerError # https://github.com/libp2p/specs/tree/master/mplex#writing-to-a-stream const MaxMsgSize* = 1 shl 20 # 1mb diff --git a/libp2p/muxers/mplex/lpchannel.nim b/libp2p/muxers/mplex/lpchannel.nim index 054e061ee..aa611240e 100644 --- a/libp2p/muxers/mplex/lpchannel.nim +++ b/libp2p/muxers/mplex/lpchannel.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import std/[oids, strformat] import chronos, chronicles, metrics import ./coder, @@ -51,11 +53,15 @@ type writes*: int # In-flight writes func shortLog*(s: LPChannel): auto = - if s.isNil: "LPChannel(nil)" - elif s.conn.peerInfo.isNil: $s.oid - elif s.name != $s.oid and s.name.len > 0: - &"{shortLog(s.conn.peerInfo.peerId)}:{s.oid}:{s.name}" - else: &"{shortLog(s.conn.peerInfo.peerId)}:{s.oid}" + try: + if s.isNil: "LPChannel(nil)" + elif s.conn.peerInfo.isNil: $s.oid + elif s.name != $s.oid and s.name.len > 0: + &"{shortLog(s.conn.peerInfo.peerId)}:{s.oid}:{s.name}" + else: &"{shortLog(s.conn.peerInfo.peerId)}:{s.oid}" + except ValueError as exc: + raise newException(Defect, exc.msg) + chronicles.formatIt(LPChannel): shortLog(it) proc open*(s: LPChannel) {.async, gcsafe.} = diff --git a/libp2p/muxers/mplex/mplex.nim b/libp2p/muxers/mplex/mplex.nim index f0f677bcb..d56912d13 100644 --- a/libp2p/muxers/mplex/mplex.nim +++ b/libp2p/muxers/mplex/mplex.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import tables, sequtils, oids import chronos, chronicles, stew/byteutils, metrics import ../muxer, @@ -32,8 +34,8 @@ when defined(libp2p_expensive_metrics): "mplex channels", labels = ["initiator", "peer"]) type - TooManyChannels* = object of CatchableError - InvalidChannelIdError* = object of CatchableError + TooManyChannels* = object of MuxerError + InvalidChannelIdError* = object of MuxerError Mplex* = ref object of Muxer channels: array[bool, Table[uint64, LPChannel]] @@ -44,7 +46,9 @@ type oid*: Oid maxChannCount: int -func shortLog*(m: MPlex): auto = shortLog(m.connection) +func shortLog*(m: MPlex): auto = + shortLog(m.connection) + chronicles.formatIt(Mplex): shortLog(it) proc newTooManyChannels(): ref TooManyChannels = @@ -66,17 +70,14 @@ proc cleanupChann(m: Mplex, chann: LPChannel) {.async, inline.} = m.channels[chann.initiator].len.int64, labelValues = [$chann.initiator, $m.connection.peerInfo.peerId]) except CatchableError as exc: - # This is top-level procedure which will work as separate task, so it - # do not need to propogate CancelledError, and no other exceptions should - # happen here warn "Error cleaning up mplex channel", m, chann, msg = exc.msg proc newStreamInternal*(m: Mplex, initiator: bool = true, chanId: uint64 = 0, name: string = "", - timeout: Duration): - LPChannel {.gcsafe.} = + timeout: Duration): LPChannel + {.gcsafe, raises: [Defect, InvalidChannelIdError].} = ## create new channel/stream ## let id = if initiator: @@ -117,8 +118,6 @@ proc handleStream(m: Mplex, chann: LPChannel) {.async.} = trace "finished handling stream", m, chann doAssert(chann.closed, "connection not closed by handler!") except CatchableError as exc: - # This is top-level procedure which will work as separate task, so it - # do not need to propogate CancelledError. trace "Exception in mplex stream handler", m, chann, msg = exc.msg await chann.reset() @@ -182,8 +181,6 @@ method handle*(m: Mplex) {.async, gcsafe.} = of MessageType.ResetIn, MessageType.ResetOut: await channel.reset() except CancelledError: - # This procedure is spawned as task and it is not part of public API, so - # there no way for this procedure to be cancelled implicitly. debug "Unexpected cancellation in mplex handler", m except LPStreamEOFError as exc: trace "Stream EOF", m, msg = exc.msg diff --git a/libp2p/muxers/muxer.nim b/libp2p/muxers/muxer.nim index 6509685d2..df8ca2737 100644 --- a/libp2p/muxers/muxer.nim +++ b/libp2p/muxers/muxer.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import chronos, chronicles import ../protocols/protocol, ../stream/connection, @@ -19,15 +21,17 @@ const DefaultChanTimeout* = 5.minutes type - StreamHandler* = proc(conn: Connection): Future[void] {.gcsafe.} - MuxerHandler* = proc(muxer: Muxer): Future[void] {.gcsafe.} + MuxerError* = object of LPError + + StreamHandler* = proc(conn: Connection): Future[void] {.gcsafe, raises: [Defect].} + MuxerHandler* = proc(muxer: Muxer): Future[void] {.gcsafe, raises: [Defect].} Muxer* = ref object of RootObj streamHandler*: StreamHandler connection*: Connection # user provider proc that returns a constructed Muxer - MuxerConstructor* = proc(conn: Connection): Muxer {.gcsafe, closure.} + MuxerConstructor* = proc(conn: Connection): Muxer {.gcsafe, closure, raises: [Defect].} # this wraps a creator proc that knows how to make muxers MuxerProvider* = ref object of LPProtocol diff --git a/libp2p/peerinfo.nim b/libp2p/peerinfo.nim index 3fac63a3b..81b41bd9e 100644 --- a/libp2p/peerinfo.nim +++ b/libp2p/peerinfo.nim @@ -59,52 +59,89 @@ template postInit(peerinfo: PeerInfo, if len(protocols) > 0: peerinfo.protocols = @protocols -proc init*(p: typedesc[PeerInfo], - key: PrivateKey, - addrs: openarray[MultiAddress] = [], - protocols: openarray[string] = []): PeerInfo {. - raises: [Defect, ResultError[cstring]].} = - result = PeerInfo(keyType: HasPrivate, peerId: PeerID.init(key).tryGet(), - privateKey: key) - result.postInit(addrs, protocols) +proc init*( + p: typedesc[PeerInfo], + key: PrivateKey, + addrs: openarray[MultiAddress] = [], + protocols: openarray[string] = [], + protoVersion: string = "", + agentVersion: string = ""): PeerInfo = + let peerInfo = PeerInfo( + keyType: HasPrivate, + peerId: PeerID.init(key).expect("Unable to create peer id from key"), + privateKey: key, + protoVersion: protoVersion, + agentVersion: agentVersion) -proc init*(p: typedesc[PeerInfo], - peerId: PeerID, - addrs: openarray[MultiAddress] = [], - protocols: openarray[string] = []): PeerInfo = - result = PeerInfo(keyType: HasPublic, peerId: peerId) - result.postInit(addrs, protocols) + peerInfo.postInit(addrs, protocols) + return peerInfo -proc init*(p: typedesc[PeerInfo], - peerId: string, - addrs: openarray[MultiAddress] = [], - protocols: openarray[string] = []): PeerInfo {. - raises: [Defect, ResultError[cstring]].} = - result = PeerInfo(keyType: HasPublic, peerId: PeerID.init(peerId).tryGet()) - result.postInit(addrs, protocols) +proc init*( + p: typedesc[PeerInfo], + peerId: PeerID, + addrs: openarray[MultiAddress] = [], + protocols: openarray[string] = [], + protoVersion: string = "", + agentVersion: string = ""): PeerInfo = + let peerInfo = PeerInfo( + keyType: HasPublic, + peerId: peerId, + protoVersion: protoVersion, + agentVersion: agentVersion) -proc init*(p: typedesc[PeerInfo], - key: PublicKey, - addrs: openarray[MultiAddress] = [], - protocols: openarray[string] = []): PeerInfo {. - raises: [Defect, ResultError[cstring]].}= - result = PeerInfo(keyType: HasPublic, - peerId: PeerID.init(key).tryGet(), - key: some(key)) + peerInfo.postInit(addrs, protocols) + return peerInfo - result.postInit(addrs, protocols) +proc init*( + p: typedesc[PeerInfo], + peerId: string, + addrs: openarray[MultiAddress] = [], + protocols: openarray[string] = [], + protoVersion: string = "", + agentVersion: string = ""): PeerInfo = -proc publicKey*(p: PeerInfo): Option[PublicKey] {. - raises: [Defect, ResultError[CryptoError]].} = + let peerInfo = PeerInfo( + keyType: HasPublic, + peerId: PeerID.init(peerId).expect("Unable to create peer id from string"), + protoVersion: protoVersion, + agentVersion: agentVersion) + + peerInfo.postInit(addrs, protocols) + return peerInfo + +proc init*( + p: typedesc[PeerInfo], + key: PublicKey, + addrs: openarray[MultiAddress] = [], + protocols: openarray[string] = [], + protoVersion: string = "", + agentVersion: string = ""): PeerInfo = + + let peerInfo = PeerInfo( + keyType: HasPublic, + peerId: PeerID.init(key).expect("Unable to create peer id from public key"), + key: some(key), + protoVersion: protoVersion, + agentVersion: agentVersion) + + peerInfo.postInit(addrs, protocols) + return peerInfo + +proc publicKey*(p: PeerInfo): Option[PublicKey] = + var res = none(PublicKey) if p.keyType == HasPublic: if p.peerId.hasPublicKey(): var pubKey: PublicKey if p.peerId.extractPublicKey(pubKey): - result = some(pubKey) + res = some(pubKey) elif p.key.isSome: - result = p.key + res = p.key else: - result = some(p.privateKey.getKey().tryGet()) + let pkeyRes = p.privateKey.getKey() + if pkeyRes.isOk: + res = some(pkeyRes.get()) + + return res func hash*(p: PeerInfo): Hash = cast[pointer](p).hash diff --git a/libp2p/protocols/identify.nim b/libp2p/protocols/identify.nim index 017a48258..7f034f1e6 100644 --- a/libp2p/protocols/identify.nim +++ b/libp2p/protocols/identify.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import options import chronos, chronicles import ../protobuf/minprotobuf, @@ -16,7 +18,8 @@ import ../protobuf/minprotobuf, ../crypto/crypto, ../multiaddress, ../protocols/protocol, - ../utility + ../utility, + ../errors logScope: topics = "libp2p identify" @@ -30,9 +33,10 @@ const #TODO: implement push identify, leaving out for now as it is not essential type - IdentifyError* = object of CatchableError + IdentifyError* = object of LPError IdentityNoMatchError* = object of IdentifyError IdentityInvalidMsgError* = object of IdentifyError + IdentifyNoPubKeyError* = object of IdentifyError IdentifyInfo* = object pubKey*: Option[PublicKey] @@ -45,9 +49,13 @@ type Identify* = ref object of LPProtocol peerInfo*: PeerInfo -proc encodeMsg*(peerInfo: PeerInfo, observedAddr: Multiaddress): ProtoBuffer = +proc encodeMsg*(peerInfo: PeerInfo, observedAddr: Multiaddress): ProtoBuffer + {.raises: [Defect, IdentifyNoPubKeyError].} = result = initProtoBuffer() - result.write(1, peerInfo.publicKey.get().getBytes().tryGet()) + if peerInfo.publicKey.isNone: + raise newException(IdentifyNoPubKeyError, "No public key found for peer!") + + result.write(1, peerInfo.publicKey.get().getBytes().get()) for ma in peerInfo.addrs: result.write(2, ma.data.buffer) for proto in peerInfo.protocols: diff --git a/libp2p/protocols/protocol.nim b/libp2p/protocols/protocol.nim index 8b7a464b3..aa734f46a 100644 --- a/libp2p/protocols/protocol.nim +++ b/libp2p/protocols/protocol.nim @@ -7,13 +7,17 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import chronos import ../stream/connection type - LPProtoHandler* = proc (conn: Connection, - proto: string): - Future[void] {.gcsafe, closure.} + LPProtoHandler* = proc ( + conn: Connection, + proto: string): + Future[void] + {.gcsafe, raises: [Defect].} LPProtocol* = ref object of RootObj codecs*: seq[string] diff --git a/libp2p/protocols/pubsub/floodsub.nim b/libp2p/protocols/pubsub/floodsub.nim index 6260b3bbf..ed29a72e8 100644 --- a/libp2p/protocols/pubsub/floodsub.nim +++ b/libp2p/protocols/pubsub/floodsub.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import std/[sequtils, sets, hashes, tables] import chronos, chronicles, metrics, bearssl import ./pubsub, @@ -201,7 +203,8 @@ method publish*(f: FloodSub, return peers.len -method initPubSub*(f: FloodSub) = +method initPubSub*(f: FloodSub) + {.raises: [Defect, InitializationError].} = procCall PubSub(f).initPubSub() f.seen = TimedCache[MessageID].init(2.minutes) var rng = newRng() diff --git a/libp2p/protocols/pubsub/gossipsub.nim b/libp2p/protocols/pubsub/gossipsub.nim index f266fdf49..f85057e8b 100644 --- a/libp2p/protocols/pubsub/gossipsub.nim +++ b/libp2p/protocols/pubsub/gossipsub.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import std/[tables, sets, options, sequtils, random] import chronos, chronicles, metrics import ./pubsub, @@ -26,10 +28,9 @@ import ./pubsub, import stew/results export results -import gossipsub/[types, scoring, behavior] -export types -export scoring -export behavior +import ./gossipsub/[types, scoring, behavior] + +export types, scoring, behavior, pubsub logScope: topics = "libp2p gossipsub" @@ -536,13 +537,16 @@ method stop*(g: GossipSub) {.async.} = trace "heartbeat stopped" g.heartbeatFut = nil -method initPubSub*(g: GossipSub) = +method initPubSub*(g: GossipSub) + {.raises: [Defect, InitializationError].} = procCall FloodSub(g).initPubSub() if not g.parameters.explicit: g.parameters = GossipSubParams.init() - g.parameters.validateParameters().tryGet() + let validationRes = g.parameters.validateParameters() + if validationRes.isErr: + raise newException(InitializationError, $validationRes.error) randomize() diff --git a/libp2p/protocols/pubsub/gossipsub/behavior.nim b/libp2p/protocols/pubsub/gossipsub/behavior.nim index f3a4e0a51..c9d8081e4 100644 --- a/libp2p/protocols/pubsub/gossipsub/behavior.nim +++ b/libp2p/protocols/pubsub/gossipsub/behavior.nim @@ -7,7 +7,7 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. -# {.push raises: [Defect].} TODO compile error on windows due to chronicles? +{.push raises: [Defect].} import std/[tables, sequtils, sets, algorithm] import random # for shuffle diff --git a/libp2p/protocols/pubsub/gossipsub/types.nim b/libp2p/protocols/pubsub/gossipsub/types.nim index 05b67769b..413b4fb0c 100644 --- a/libp2p/protocols/pubsub/gossipsub/types.nim +++ b/libp2p/protocols/pubsub/gossipsub/types.nim @@ -1,3 +1,14 @@ +## Nim-LibP2P +## Copyright (c) 2021 Status Research & Development GmbH +## Licensed under either of +## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE)) +## * MIT license ([LICENSE-MIT](LICENSE-MIT)) +## at your option. +## This file may not be copied, modified, or distributed except according to +## those terms. + +{.push raises: [Defect].} + import chronos import std/[tables, sets] import ".."/[floodsub, peertable, mcache, pubsubpeer] diff --git a/libp2p/protocols/pubsub/pubsub.nim b/libp2p/protocols/pubsub/pubsub.nim index 5f13eee18..e53e72399 100644 --- a/libp2p/protocols/pubsub/pubsub.nim +++ b/libp2p/protocols/pubsub/pubsub.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import std/[tables, sequtils, sets, strutils] import chronos, chronicles, metrics import ./pubsubpeer, @@ -68,6 +70,8 @@ declarePublicCounter(libp2p_pubsub_received_graft, "pubsub broadcast graft", lab declarePublicCounter(libp2p_pubsub_received_prune, "pubsub broadcast prune", labels = ["topic"]) type + InitializationError* = object of LPError + TopicHandler* = proc(topic: string, data: seq[byte]): Future[void] {.gcsafe, raises: [Defect].} @@ -468,7 +472,8 @@ method publish*(p: PubSub, return 0 -method initPubSub*(p: PubSub) {.base.} = +method initPubSub*(p: PubSub) + {.base, raises: [Defect, InitializationError].} = ## perform pubsub initialization p.observers = new(seq[PubSubObserver]) if p.msgIdProvider == nil: @@ -538,7 +543,8 @@ proc init*[PubParams: object | bool]( sign: bool = true, msgIdProvider: MsgIdProvider = defaultMsgIdProvider, subscriptionValidator: SubscriptionValidator = nil, - parameters: PubParams = false): P = + parameters: PubParams = false): P + {.raises: [Defect, InitializationError].} = let pubsub = when PubParams is bool: P(switch: switch, diff --git a/libp2p/protocols/pubsub/pubsubpeer.nim b/libp2p/protocols/pubsub/pubsubpeer.nim index a8ed07aad..3136d2193 100644 --- a/libp2p/protocols/pubsub/pubsubpeer.nim +++ b/libp2p/protocols/pubsub/pubsubpeer.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import std/[sequtils, strutils, tables, hashes] import chronos, chronicles, nimcrypto/sha2, metrics import rpc/[messages, message, protobuf], @@ -40,9 +42,9 @@ type PubsubPeerEvent* = object kind*: PubSubPeerEventKind - GetConn* = proc(): Future[Connection] {.gcsafe.} - DropConn* = proc(peer: PubsubPeer) {.gcsafe.} # have to pass peer as it's unknown during init - OnEvent* = proc(peer: PubSubPeer, event: PubsubPeerEvent) {.gcsafe.} + GetConn* = proc(): Future[Connection] {.gcsafe, raises: [Defect].} + DropConn* = proc(peer: PubsubPeer) {.gcsafe, raises: [Defect].} # have to pass peer as it's unknown during init + OnEvent* = proc(peer: PubSubPeer, event: PubsubPeerEvent) {.gcsafe, raises: [Defect].} PubSubPeer* = ref object of RootObj getConn*: GetConn # callback to establish a new send connection @@ -64,7 +66,8 @@ type when defined(libp2p_agents_metrics): shortAgent*: string - RPCHandler* = proc(peer: PubSubPeer, msg: RPCMsg): Future[void] {.gcsafe.} + RPCHandler* = proc(peer: PubSubPeer, msg: RPCMsg): Future[void] + {.gcsafe, raises: [Defect].} func hash*(p: PubSubPeer): Hash = p.peerId.hash diff --git a/libp2p/protocols/pubsub/rpc/messages.nim b/libp2p/protocols/pubsub/rpc/messages.nim index 59c199d77..0d4c41f26 100644 --- a/libp2p/protocols/pubsub/rpc/messages.nim +++ b/libp2p/protocols/pubsub/rpc/messages.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import options, sequtils import ../../../utility import ../../../peerid @@ -17,7 +19,7 @@ type PeerInfoMsg* = object peerID*: seq[byte] signedPeerRecord*: seq[byte] - + SubOpts* = object subscribe*: bool topic*: string diff --git a/libp2p/protocols/pubsub/rpc/protobuf.nim b/libp2p/protocols/pubsub/rpc/protobuf.nim index 3d499af20..2f2f4f500 100644 --- a/libp2p/protocols/pubsub/rpc/protobuf.nim +++ b/libp2p/protocols/pubsub/rpc/protobuf.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import options import stew/assign2 import chronicles diff --git a/libp2p/protocols/pubsub/timedcache.nim b/libp2p/protocols/pubsub/timedcache.nim index e4cfdf5ea..ab77e3581 100644 --- a/libp2p/protocols/pubsub/timedcache.nim +++ b/libp2p/protocols/pubsub/timedcache.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import std/[tables] import chronos/timer diff --git a/libp2p/protocols/secure/noise.nim b/libp2p/protocols/secure/noise.nim index b8101ed3c..c5af5361c 100644 --- a/libp2p/protocols/secure/noise.nim +++ b/libp2p/protocols/secure/noise.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import std/[oids, strformat] import chronos import chronicles @@ -18,8 +20,10 @@ import ../../peerid import ../../peerinfo import ../../protobuf/minprotobuf import ../../utility +import ../../errors + import secure, - ../../crypto/[crypto, chacha20poly1305, curve25519, hkdf] + ../../crypto/[crypto, chacha20poly1305, curve25519, hkdf] when defined(libp2p_dump): import ../../debugutils @@ -85,17 +89,22 @@ type readCs: CipherState writeCs: CipherState - NoiseHandshakeError* = object of CatchableError - NoiseDecryptTagError* = object of CatchableError - NoiseOversizedPayloadError* = object of CatchableError - NoiseNonceMaxError* = object of CatchableError # drop connection on purpose + NoiseError* = object of LPError + NoiseHandshakeError* = object of NoiseError + NoiseDecryptTagError* = object of NoiseError + NoiseOversizedPayloadError* = object of NoiseError + NoiseNonceMaxError* = object of NoiseError # drop connection on purpose # Utility func shortLog*(conn: NoiseConnection): auto = - if conn.isNil: "NoiseConnection(nil)" - elif conn.peerInfo.isNil: $conn.oid - else: &"{shortLog(conn.peerInfo.peerId)}:{conn.oid}" + try: + if conn.isNil: "NoiseConnection(nil)" + elif conn.peerInfo.isNil: $conn.oid + else: &"{shortLog(conn.peerInfo.peerId)}:{conn.oid}" + except ValueError as exc: + raise newException(Defect, exc.msg) + chronicles.formatIt(NoiseConnection): shortLog(it) proc genKeyPair(rng: var BrHmacDrbgContext): KeyPair = @@ -122,8 +131,11 @@ proc hasKey(cs: CipherState): bool = cs.k != EmptyKey proc encrypt( - state: var CipherState, data: var openArray[byte], - ad: openArray[byte]): ChaChaPolyTag {.noinit.} = + state: var CipherState, + data: var openArray[byte], + ad: openArray[byte]): ChaChaPolyTag + {.noinit, raises: [Defect, NoiseNonceMaxError].} = + var nonce: ChaChaPolyNonce nonce[4..<12] = toBytesLE(state.n) @@ -133,7 +145,8 @@ proc encrypt( if state.n > NonceMax: raise newException(NoiseNonceMaxError, "Noise max nonce value reached") -proc encryptWithAd(state: var CipherState, ad, data: openArray[byte]): seq[byte] = +proc encryptWithAd(state: var CipherState, ad, data: openArray[byte]): seq[byte] + {.raises: [Defect, NoiseNonceMaxError].} = result = newSeqOfCap[byte](data.len + sizeof(ChachaPolyTag)) result.add(data) @@ -144,7 +157,8 @@ proc encryptWithAd(state: var CipherState, ad, data: openArray[byte]): seq[byte] trace "encryptWithAd", tag = byteutils.toHex(tag), data = result.shortLog, nonce = state.n - 1 -proc decryptWithAd(state: var CipherState, ad, data: openArray[byte]): seq[byte] = +proc decryptWithAd(state: var CipherState, ad, data: openArray[byte]): seq[byte] + {.raises: [Defect, NoiseDecryptTagError, NoiseNonceMaxError].} = var tagIn = data.toOpenArray(data.len - ChaChaPolyTag.len, data.high).intoChaChaPolyTag tagOut: ChaChaPolyTag @@ -192,7 +206,8 @@ proc mixKeyAndHash(ss: var SymmetricState; ikm: openArray[byte]) {.used.} = ss.mixHash(temp_keys[1]) ss.cs = CipherState(k: temp_keys[2]) -proc encryptAndHash(ss: var SymmetricState, data: openArray[byte]): seq[byte] = +proc encryptAndHash(ss: var SymmetricState, data: openArray[byte]): seq[byte] + {.raises: [Defect, NoiseNonceMaxError].} = # according to spec if key is empty leave plaintext if ss.cs.hasKey: result = ss.cs.encryptWithAd(ss.h.data, data) @@ -200,7 +215,8 @@ proc encryptAndHash(ss: var SymmetricState, data: openArray[byte]): seq[byte] = result = @data ss.mixHash(result) -proc decryptAndHash(ss: var SymmetricState, data: openArray[byte]): seq[byte] = +proc decryptAndHash(ss: var SymmetricState, data: openArray[byte]): seq[byte] + {.raises: [Defect, NoiseDecryptTagError, NoiseNonceMaxError].} = # according to spec if key is empty leave plaintext if ss.cs.hasKey: result = ss.cs.decryptWithAd(ss.h.data, data) @@ -429,7 +445,10 @@ method readMessage*(sconn: NoiseConnection): Future[seq[byte]] {.async.} = proc encryptFrame( - sconn: NoiseConnection, cipherFrame: var openArray[byte], src: openArray[byte]) = + sconn: NoiseConnection, + cipherFrame: var openArray[byte], + src: openArray[byte]) + {.raises: [Defect, NoiseNonceMaxError].} = # Frame consists of length + cipher data + tag doAssert src.len <= MaxPlainSize doAssert cipherFrame.len == 2 + src.len + sizeof(ChaChaPolyTag) @@ -581,12 +600,19 @@ proc newNoise*( privateKey: PrivateKey; outgoing: bool = true; commonPrologue: seq[byte] = @[]): Noise = - result = Noise( + + let pkBytes = privateKey.getKey() + .expect("Expected valid private key") + .getBytes() + .expect("Couldn't get key bytes") + var noise = Noise( rng: rng, outgoing: outgoing, localPrivateKey: privateKey, - localPublicKey: privateKey.getKey().tryGet().getBytes().tryGet(), + localPublicKey: pkBytes, noiseKeys: genKeyPair(rng[]), commonPrologue: commonPrologue, ) - result.init() + + noise.init() + return noise diff --git a/libp2p/protocols/secure/plaintext.nim b/libp2p/protocols/secure/plaintext.nim index 7ce3a370f..39dfd9d56 100644 --- a/libp2p/protocols/secure/plaintext.nim +++ b/libp2p/protocols/secure/plaintext.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import chronos import secure, ../../stream/connection diff --git a/libp2p/protocols/secure/secio.nim b/libp2p/protocols/secure/secio.nim index a294138df..c4211abec 100644 --- a/libp2p/protocols/secure/secio.nim +++ b/libp2p/protocols/secure/secio.nim @@ -6,6 +6,9 @@ ## at your option. ## This file may not be copied, modified, or distributed except according to ## those terms. + +{.push raises: [Defect].} + import std/[oids, strformat] import chronos, chronicles, stew/endians2, bearssl import nimcrypto/[hmac, sha2, sha, hash, rijndael, twofish, bcmode] @@ -15,7 +18,8 @@ import secure, ../../crypto/crypto, ../../crypto/ecnist, ../../peerid, - ../../utility + ../../utility, + ../../errors export hmac, sha2, sha, hash, rijndael, bcmode @@ -68,12 +72,16 @@ type writerCoder: SecureCipher readerCoder: SecureCipher - SecioError* = object of CatchableError + SecioError* = object of LPError func shortLog*(conn: SecioConn): auto = - if conn.isNil: "SecioConn(nil)" - elif conn.peerInfo.isNil: $conn.oid - else: &"{shortLog(conn.peerInfo.peerId)}:{conn.oid}" + try: + if conn.isNil: "SecioConn(nil)" + elif conn.peerInfo.isNil: $conn.oid + else: &"{shortLog(conn.peerInfo.peerId)}:{conn.oid}" + except ValueError as exc: + raise newException(Defect, exc.msg) + chronicles.formatIt(SecioConn): shortLog(it) proc init(mac: var SecureMac, hash: string, key: openarray[byte]) = @@ -250,7 +258,8 @@ proc newSecioConn(conn: Connection, cipher: string, secrets: Secret, order: int, - remotePubKey: PublicKey): SecioConn = + remotePubKey: PublicKey): SecioConn + {.raises: [Defect, SecioError].} = ## Create new secure stream/lpstream, using specified hash algorithm ``hash``, ## cipher algorithm ``cipher``, stretched keys ``secrets`` and order ## ``order``. @@ -259,7 +268,7 @@ proc newSecioConn(conn: Connection, if conn.peerInfo != nil: conn.peerInfo else: - PeerInfo.init(PeerID.init(remotePubKey).tryGet()) + PeerInfo.init(remotePubKey) result = SecioConn.init(conn, conn.peerInfo, conn.observedAddr) @@ -423,9 +432,13 @@ method init(s: Secio) {.gcsafe.} = s.codec = SecioCodec proc newSecio*(rng: ref BrHmacDrbgContext, localPrivateKey: PrivateKey): Secio = + let pkRes = localPrivateKey.getKey() + if pkRes.isErr: + raise newException(Defect, "Can't fetch local private key") + result = Secio( rng: rng, localPrivateKey: localPrivateKey, - localPublicKey: localPrivateKey.getKey().tryGet(), + localPublicKey: localPrivateKey.getKey().get(), ) result.init() diff --git a/libp2p/protocols/secure/secure.nim b/libp2p/protocols/secure/secure.nim index 0dc6bc161..6c66cbdfc 100644 --- a/libp2p/protocols/secure/secure.nim +++ b/libp2p/protocols/secure/secure.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import std/[options, strformat] import chronos, chronicles, bearssl import ../protocol, @@ -32,9 +34,13 @@ type buf: StreamSeq func shortLog*(conn: SecureConn): auto = - if conn.isNil: "SecureConn(nil)" - elif conn.peerInfo.isNil: $conn.oid - else: &"{shortLog(conn.peerInfo.peerId)}:{conn.oid}" + try: + if conn.isNil: "SecureConn(nil)" + elif conn.peerInfo.isNil: $conn.oid + else: &"{shortLog(conn.peerInfo.peerId)}:{conn.oid}" + except ValueError as exc: + raise newException(Defect, exc.msg) + chronicles.formatIt(SecureConn): shortLog(it) proc init*(T: type SecureConn, diff --git a/libp2p/stream/bufferstream.nim b/libp2p/stream/bufferstream.nim index 84deb0dd3..9e3253017 100644 --- a/libp2p/stream/bufferstream.nim +++ b/libp2p/stream/bufferstream.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import std/strformat import stew/byteutils import chronos, chronicles, metrics @@ -34,9 +36,13 @@ type returnedEof*: bool # 0-byte readOnce has been completed func shortLog*(s: BufferStream): auto = - if s.isNil: "BufferStream(nil)" - elif s.peerInfo.isNil: $s.oid - else: &"{shortLog(s.peerInfo.peerId)}:{s.oid}" + try: + if s.isNil: "BufferStream(nil)" + elif s.peerInfo.isNil: $s.oid + else: &"{shortLog(s.peerInfo.peerId)}:{s.oid}" + except ValueError as exc: + raise newException(Defect, exc.msg) + chronicles.formatIt(BufferStream): shortLog(it) proc len*(s: BufferStream): int = @@ -190,14 +196,17 @@ method closeImpl*(s: BufferStream): Future[void] = # ------------|----------|------- # Reading | Push Eof | Na # Pushing | Na | Pop - if not(s.reading and s.pushing): - if s.reading: - if s.readQueue.empty(): - # There is an active reader - s.readQueue.addLastNoWait(Eof) - elif s.pushing: - if not s.readQueue.empty(): - discard s.readQueue.popFirstNoWait() + try: + if not(s.reading and s.pushing): + if s.reading: + if s.readQueue.empty(): + # There is an active reader + s.readQueue.addLastNoWait(Eof) + elif s.pushing: + if not s.readQueue.empty(): + discard s.readQueue.popFirstNoWait() + except AsyncQueueFullError, AsyncQueueEmptyError: + raise newException(Defect, getCurrentExceptionMsg()) trace "Closed BufferStream", s diff --git a/libp2p/stream/chronosstream.nim b/libp2p/stream/chronosstream.nim index bb8416b3e..c5da52f00 100644 --- a/libp2p/stream/chronosstream.nim +++ b/libp2p/stream/chronosstream.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import std/[oids, strformat] import chronos, chronicles, metrics import connection @@ -31,10 +33,14 @@ when defined(libp2p_agents_metrics): declareCounter(libp2p_peers_traffic_read, "incoming traffic", labels = ["agent"]) declareCounter(libp2p_peers_traffic_write, "outgoing traffic", labels = ["agent"]) -func shortLog*(conn: ChronosStream): string = - if conn.isNil: "ChronosStream(nil)" - elif conn.peerInfo.isNil: $conn.oid - else: &"{shortLog(conn.peerInfo.peerId)}:{conn.oid}" +func shortLog*(conn: ChronosStream): auto = + try: + if conn.isNil: "ChronosStream(nil)" + elif conn.peerInfo.isNil: $conn.oid + else: &"{shortLog(conn.peerInfo.peerId)}:{conn.oid}" + except ValueError as exc: + raise newException(Defect, exc.msg) + chronicles.formatIt(ChronosStream): shortLog(it) method initStream*(s: ChronosStream) = @@ -126,10 +132,10 @@ method write*(s: ChronosStream, msg: seq[byte]) {.async.} = if s.tracked: libp2p_peers_traffic_write.inc(msg.len.int64, labelValues = [s.shortAgent]) -method closed*(s: ChronosStream): bool {.raises: [Defect].} = +method closed*(s: ChronosStream): bool = result = s.client.closed -method atEof*(s: ChronosStream): bool {.raises: [Defect].} = +method atEof*(s: ChronosStream): bool = s.client.atEof() method closeImpl*(s: ChronosStream) {.async.} = diff --git a/libp2p/stream/connection.nim b/libp2p/stream/connection.nim index 005149c72..67c446ff8 100644 --- a/libp2p/stream/connection.nim +++ b/libp2p/stream/connection.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import std/[hashes, oids, strformat] import chronicles, chronos, metrics import lpstream, @@ -23,7 +25,7 @@ const DefaultConnectionTimeout* = 5.minutes type - TimeoutHandler* = proc(): Future[void] {.gcsafe.} + TimeoutHandler* = proc(): Future[void] {.gcsafe, raises: [Defect].} Connection* = ref object of LPStream activity*: bool # reset every time data is sent or received @@ -54,9 +56,13 @@ proc onUpgrade*(s: Connection) {.async.} = await s.upgraded func shortLog*(conn: Connection): string = - if conn.isNil: "Connection(nil)" - elif conn.peerInfo.isNil: $conn.oid - else: &"{shortLog(conn.peerInfo.peerId)}:{conn.oid}" + try: + if conn.isNil: "Connection(nil)" + elif conn.peerInfo.isNil: $conn.oid + else: &"{shortLog(conn.peerInfo.peerId)}:{conn.oid}" + except ValueError as exc: + raiseAssert(exc.msg) + chronicles.formatIt(Connection): shortLog(it) method initStream*(s: Connection) = diff --git a/libp2p/stream/lpstream.nim b/libp2p/stream/lpstream.nim index 6bf0f856c..bff5aee34 100644 --- a/libp2p/stream/lpstream.nim +++ b/libp2p/stream/lpstream.nim @@ -7,12 +7,15 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import std/oids import stew/byteutils import chronicles, chronos, metrics import ../varint, ../peerinfo, - ../multiaddress + ../multiaddress, + ../errors declareGauge(libp2p_open_streams, "open stream instances", labels = ["type", "dir"]) @@ -39,7 +42,7 @@ type dir*: Direction closedWithEOF: bool # prevent concurrent calls - LPStreamError* = object of CatchableError + LPStreamError* = object of LPError LPStreamIncompleteError* = object of LPStreamError LPStreamIncorrectDefect* = object of Defect LPStreamLimitError* = object of LPStreamError @@ -96,19 +99,19 @@ proc newLPStreamWriteError*(p: ref CatchableError): ref CatchableError = w.par = p result = w -proc newLPStreamIncompleteError*(): ref CatchableError = +proc newLPStreamIncompleteError*(): ref LPStreamIncompleteError = result = newException(LPStreamIncompleteError, "Incomplete data received") -proc newLPStreamLimitError*(): ref CatchableError = +proc newLPStreamLimitError*(): ref LPStreamLimitError = result = newException(LPStreamLimitError, "Buffer limit reached") -proc newLPStreamIncorrectDefect*(m: string): ref Defect = +proc newLPStreamIncorrectDefect*(m: string): ref LPStreamIncorrectDefect = result = newException(LPStreamIncorrectDefect, m) -proc newLPStreamEOFError*(): ref CatchableError = +proc newLPStreamEOFError*(): ref LPStreamEOFError = result = newException(LPStreamEOFError, "Stream EOF!") -proc newLPStreamClosedError*(): ref Exception = +proc newLPStreamClosedError*(): ref LPStreamClosedError = result = newException(LPStreamClosedError, "Stream Closed!") func shortLog*(s: LPStream): auto = @@ -130,17 +133,17 @@ method initStream*(s: LPStream) {.base.} = proc join*(s: LPStream): Future[void] = s.closeEvent.wait() -method closed*(s: LPStream): bool {.base, raises: [Defect].} = +method closed*(s: LPStream): bool {.base.} = s.isClosed -method atEof*(s: LPStream): bool {.base, raises: [Defect].} = +method atEof*(s: LPStream): bool {.base.} = s.isEof -method readOnce*(s: LPStream, - pbytes: pointer, - nbytes: int): - Future[int] - {.base, async.} = +method readOnce*( + s: LPStream, + pbytes: pointer, + nbytes: int): + Future[int] {.base, async.} = doAssert(false, "not implemented!") proc readExactly*(s: LPStream, @@ -236,7 +239,7 @@ proc readLp*(s: LPStream, maxSize: int): Future[seq[byte]] {.async, gcsafe.} = await s.readExactly(addr res[0], res.len) return res -method write*(s: LPStream, msg: seq[byte]): Future[void] {.base, raises: [Defect].} = +method write*(s: LPStream, msg: seq[byte]): Future[void] {.base.} = doAssert(false, "not implemented!") proc writeLp*(s: LPStream, msg: openArray[byte]): Future[void] = diff --git a/libp2p/stream/streamseq.nim b/libp2p/stream/streamseq.nim index b972edbb7..13ac06071 100644 --- a/libp2p/stream/streamseq.nim +++ b/libp2p/stream/streamseq.nim @@ -1,7 +1,7 @@ -import stew/bitops2 - {.push raises: [Defect].} +import stew/bitops2 + type StreamSeq* = object # Seq adapted to the stream use case where we add data at the back and diff --git a/libp2p/switch.nim b/libp2p/switch.nim index 0274b1dd7..37a7f1324 100644 --- a/libp2p/switch.nim +++ b/libp2p/switch.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import std/[tables, options, sets, @@ -54,7 +56,7 @@ const ConcurrentUpgrades* = 4 type - DialFailedError* = object of CatchableError + DialFailedError* = object of LPError Switch* = ref object of RootObj peerInfo*: PeerInfo @@ -276,7 +278,8 @@ proc dial*(s: Switch, proto: string): Future[Connection] = dial(s, peerId, addrs, @[proto]) -proc mount*[T: LPProtocol](s: Switch, proto: T, matcher: Matcher = nil) {.gcsafe.} = +proc mount*[T: LPProtocol](s: Switch, proto: T, matcher: Matcher = nil) + {.gcsafe, raises: [Defect, CatchableError].} = if isNil(proto.handler): raise newException(CatchableError, "Protocol has to define a handle method or proc") @@ -404,7 +407,9 @@ proc newSwitch*(peerInfo: PeerInfo, maxConnections = MaxConnections, maxIn = -1, maxOut = -1, - maxConnsPerPeer = MaxConnectionsPerPeer): Switch = + maxConnsPerPeer = MaxConnectionsPerPeer): Switch + {.raises: [Defect, CatchableError].} = + if secureManagers.len == 0: raise (ref CatchableError)(msg: "Provide at least one secure manager") diff --git a/libp2p/transports/tcptransport.nim b/libp2p/transports/tcptransport.nim index 97c977972..a3b2648ad 100644 --- a/libp2p/transports/tcptransport.nim +++ b/libp2p/transports/tcptransport.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import std/[oids, sequtils] import chronos, chronicles import transport, @@ -203,7 +205,9 @@ method dial*(t: TcpTransport, method handles*(t: TcpTransport, address: MultiAddress): bool {.gcsafe.} = if procCall Transport(t).handles(address): - return address.protocols - .tryGet() - .filterIt( it == multiCodec("tcp") ) - .len > 0 + if address.protocols.isOk: + return address.protocols + .get() + .filterIt( + it == multiCodec("tcp") + ).len > 0 diff --git a/libp2p/transports/transport.nim b/libp2p/transports/transport.nim index fa31c717e..9f9e79223 100644 --- a/libp2p/transports/transport.nim +++ b/libp2p/transports/transport.nim @@ -8,6 +8,8 @@ ## those terms. ## +{.push raises: [Defect].} + import sequtils import chronos, chronicles import ../stream/connection, @@ -77,7 +79,8 @@ method handles*(t: Transport, address: MultiAddress): bool {.base, gcsafe.} = # by default we skip circuit addresses to avoid # having to repeat the check in every transport - address.protocols.tryGet().filterIt( it == multiCodec("p2p-circuit") ).len == 0 + if address.protocols.isOk: + return address.protocols.get().filterIt( it == multiCodec("p2p-circuit") ).len == 0 method localAddress*(t: Transport): MultiAddress {.base, gcsafe.} = ## get the local address of the transport in case started with 0.0.0.0:0 diff --git a/libp2p/upgrademngrs/muxedupgrade.nim b/libp2p/upgrademngrs/muxedupgrade.nim index 1f57dfbbd..5b5a3257c 100644 --- a/libp2p/upgrademngrs/muxedupgrade.nim +++ b/libp2p/upgrademngrs/muxedupgrade.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import std/[tables, sequtils] import pkg/[chronos, chronicles, metrics] @@ -15,6 +17,9 @@ import ../upgrademngrs/upgrade, export Upgrade +logScope: + topics = "libp2p muxedupgrade" + type MuxedUpgrade* = ref object of Upgrade muxers*: Table[string, MuxerProvider] diff --git a/libp2p/upgrademngrs/upgrade.nim b/libp2p/upgrademngrs/upgrade.nim index 2af6d1b5b..475febe71 100644 --- a/libp2p/upgrademngrs/upgrade.nim +++ b/libp2p/upgrademngrs/upgrade.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import std/[options, sequtils] import pkg/[chronos, chronicles, metrics] @@ -14,14 +16,18 @@ import ../stream/connection, ../protocols/secure/secure, ../protocols/identify, ../multistream, - ../connmanager + ../connmanager, + ../errors export connmanager, connection, identify, secure, multistream declarePublicCounter(libp2p_failed_upgrade, "peers failed upgrade") +logScope: + topics = "libp2p upgrade" + type - UpgradeFailedError* = object of CatchableError + UpgradeFailedError* = object of LPError Upgrade* = ref object of RootObj ms*: MultistreamSelect diff --git a/libp2p/utils/semaphore.nim b/libp2p/utils/semaphore.nim index 301a3e517..8ded05e93 100644 --- a/libp2p/utils/semaphore.nim +++ b/libp2p/utils/semaphore.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + import sequtils import chronos, chronicles diff --git a/libp2p/wire.nim b/libp2p/wire.nim index 5fb4e55e1..f421a4d75 100644 --- a/libp2p/wire.nim +++ b/libp2p/wire.nim @@ -7,6 +7,8 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. +{.push raises: [Defect].} + ## This module implements wire network connection procedures. import chronos, stew/endians2 import multiaddress, multicodec @@ -28,11 +30,13 @@ const mapAnd(mapEq("unix")) ) -proc initTAddress*(ma: MultiAddress): MaResult[TransportAddress] {. - raises: [Defect, ResultError[string]] .} = +proc initTAddress*(ma: MultiAddress): MaResult[TransportAddress] + {.raises: [Defect, ResultError[string]].} = ## Initialize ``TransportAddress`` with MultiAddress ``ma``. ## ## MultiAddress must be wire address, e.g. ``{IP4, IP6, UNIX}/{TCP, UDP}``. + ## + if TRANSPMA.match(ma): var pbuf: array[2, byte] let code = ma[0].tryGet().protoCode().tryGet() @@ -85,14 +89,25 @@ proc createStreamServer*[T](ma: MultiAddress, backlog: int = 100, bufferSize: int = DefaultStreamBufferSize, child: StreamServer = nil, - init: TransportInitCallback = nil): StreamServer = + init: TransportInitCallback = nil): StreamServer + {.raises: [Defect, MaInvalidAddress].} = ## Create new TCP stream server which bounds to ``ma`` address. if not(RTRANSPMA.match(ma)): raise newException(MaInvalidAddress, "Incorrect or unsupported address!") - let address = initTAddress(ma) - result = createStreamServer(address.tryGet(), cbproc, flags, udata, sock, - backlog, bufferSize, child, init) + let address = try: + initTAddress(ma) + except ResultError[string] as exc: + raise newException(Defect, exc.msg) + + if address.isErr: + raise newException(MaInvalidAddress, address.error) + + try: + return createStreamServer(address.get(), cbproc, flags, udata, sock, + backlog, bufferSize, child, init) + except CatchableError as exc: + raise newException(Defect, exc.msg) proc createStreamServer*[T](ma: MultiAddress, flags: set[ServerFlags] = {}, @@ -101,16 +116,27 @@ proc createStreamServer*[T](ma: MultiAddress, backlog: int = 100, bufferSize: int = DefaultStreamBufferSize, child: StreamServer = nil, - init: TransportInitCallback = nil): StreamServer = + init: TransportInitCallback = nil): StreamServer + {.raises: [Defect, MaInvalidAddress].} = ## Create new TCP stream server which bounds to ``ma`` address. + ## + if not(RTRANSPMA.match(ma)): raise newException(MaInvalidAddress, "Incorrect or unsupported address!") - let address = initTAddress(ma) - result = createStreamServer(address.tryGet(), flags, udata, sock, backlog, - bufferSize, child, init) + let address = try: + initTAddress(ma) + except ResultError[string] as exc: + raise newException(Defect, exc.msg) -proc createAsyncSocket*(ma: MultiAddress): AsyncFD = + try: + return createStreamServer(address.get(), flags, udata, sock, backlog, + bufferSize, child, init) + except CatchableError as exc: + raise newException(Defect, exc.msg) + +proc createAsyncSocket*(ma: MultiAddress): AsyncFD + {.raises: [Defect, ResultError[string]].} = ## Create new asynchronous socket using MultiAddress' ``ma`` socket type and ## protocol information. ## @@ -126,7 +152,6 @@ proc createAsyncSocket*(ma: MultiAddress): AsyncFD = return asyncInvalidSocket let address = maddr.tryGet() - if address.family in {AddressFamily.IPv4, AddressFamily.IPv6}: if ma[1].tryGet().protoCode().tryGet() == multiCodec("udp"): socktype = SockType.SOCK_DGRAM @@ -139,9 +164,14 @@ proc createAsyncSocket*(ma: MultiAddress): AsyncFD = protocol = cast[Protocol](0) else: return asyncInvalidSocket - result = createAsyncSocket(address.getDomain(), socktype, protocol) -proc bindAsyncSocket*(sock: AsyncFD, ma: MultiAddress): bool = + try: + createAsyncSocket(address.getDomain(), socktype, protocol) + except CatchableError as exc: + raise newException(Defect, exc.msg) + +proc bindAsyncSocket*(sock: AsyncFD, ma: MultiAddress): bool + {.raises: [Defect, ResultError[string]].} = ## Bind socket ``sock`` to MultiAddress ``ma``. ## ## Note: This procedure only used in `go-libp2p-daemon` wrapper. @@ -153,7 +183,7 @@ proc bindAsyncSocket*(sock: AsyncFD, ma: MultiAddress): bool = if maddr.isErr(): return false - let address = maddr.tryGet() + let address = maddr.get() toSAddr(address, saddr, slen) if bindSocket(SocketHandle(sock), cast[ptr SockAddr](addr saddr), slen) == 0: diff --git a/tests/asyncunit.nim b/tests/asyncunit.nim new file mode 100644 index 000000000..9da6838a0 --- /dev/null +++ b/tests/asyncunit.nim @@ -0,0 +1,24 @@ +import unittest2 + +export unittest2 + +template asyncTeardown*(body: untyped): untyped = + teardown: + waitFor(( + proc() {.async, gcsafe.} = + body + )()) + +template asyncSetup*(body: untyped): untyped = + setup: + waitFor(( + proc() {.async, gcsafe.} = + body + )()) + +template asyncTest*(name: string, body: untyped): untyped = + test name: + waitFor(( + proc() {.async, gcsafe.} = + body + )()) diff --git a/tests/helpers.nim b/tests/helpers.nim index 390070439..8cf805b84 100644 --- a/tests/helpers.nim +++ b/tests/helpers.nim @@ -1,4 +1,4 @@ -import std/unittest +{.push raises: [Defect].} import chronos, bearssl @@ -9,7 +9,8 @@ import ../libp2p/stream/lpstream import ../libp2p/muxers/mplex/lpchannel import ../libp2p/protocols/secure/secure -export unittest +import ./asyncunit +export asyncunit const StreamTransportTrackerName = "stream.transport" @@ -48,27 +49,6 @@ template checkTrackers*() = # Also test the GC is not fooling with us GC_fullCollect() -template asyncTeardown*(body: untyped): untyped = - teardown: - waitFor(( - proc() {.async, gcsafe.} = - body - )()) - -template asyncSetup*(body: untyped): untyped = - setup: - waitFor(( - proc() {.async, gcsafe.} = - body - )()) - -template asyncTest*(name: string, body: untyped): untyped = - test name: - waitFor(( - proc() {.async, gcsafe.} = - body - )()) - type RngWrap = object rng: ref BrHmacDrbgContext @@ -99,7 +79,7 @@ proc newBufferStream*(writeHandler: WriteHandler): TestBufferStream = result.writeHandler = writeHandler result.initStream() -proc checkExpiringInternal(cond: proc(): bool): Future[bool] {.async, gcsafe.} = +proc checkExpiringInternal(cond: proc(): bool {.raises: [Defect].} ): Future[bool] {.async, gcsafe.} = {.gcsafe.}: let start = Moment.now() while true: diff --git a/tests/pubsub/testfloodsub.nim b/tests/pubsub/testfloodsub.nim index 347e668de..5e167be17 100644 --- a/tests/pubsub/testfloodsub.nim +++ b/tests/pubsub/testfloodsub.nim @@ -9,7 +9,7 @@ {.used.} -import unittest, sequtils, options, tables, sets +import sequtils, options, tables, sets import chronos, stew/byteutils import utils, ../../libp2p/[errors, diff --git a/tests/pubsub/testgossipinternal.nim b/tests/pubsub/testgossipinternal.nim index eda515604..ce0a637c4 100644 --- a/tests/pubsub/testgossipinternal.nim +++ b/tests/pubsub/testgossipinternal.nim @@ -3,7 +3,7 @@ include ../../libp2p/protocols/pubsub/gossipsub {.used.} import options -import unittest, bearssl +import bearssl import stew/byteutils import ../../libp2p/builders import ../../libp2p/errors @@ -34,7 +34,10 @@ proc getPubSubPeer(p: TestGossipSub, peerId: PeerID): PubSubPeer = pubSubPeer proc randomPeerInfo(): PeerInfo = - PeerInfo.init(PrivateKey.random(ECDSA, rng[]).get()) + try: + PeerInfo.init(PrivateKey.random(ECDSA, rng[]).get()) + except CatchableError as exc: + raise newException(Defect, exc.msg) suite "GossipSub internal": teardown: diff --git a/tests/pubsub/testgossipsub.nim b/tests/pubsub/testgossipsub.nim index 7cb0594b3..15fa04db6 100644 --- a/tests/pubsub/testgossipsub.nim +++ b/tests/pubsub/testgossipsub.nim @@ -9,7 +9,7 @@ {.used.} -import unittest, sequtils, options, tables, sets +import sequtils, options, tables, sets import chronos, stew/byteutils import chronicles import utils, ../../libp2p/[errors, diff --git a/tests/pubsub/testgossipsub2.nim b/tests/pubsub/testgossipsub2.nim index 6a3bc57d9..6aa30f364 100644 --- a/tests/pubsub/testgossipsub2.nim +++ b/tests/pubsub/testgossipsub2.nim @@ -9,7 +9,7 @@ {.used.} -import unittest, sequtils, options, tables, sets +import sequtils, options, tables, sets import chronos, stew/byteutils import chronicles import utils, ../../libp2p/[errors, diff --git a/tests/pubsub/testmcache.nim b/tests/pubsub/testmcache.nim index ad10ab3b8..ca2a8e689 100644 --- a/tests/pubsub/testmcache.nim +++ b/tests/pubsub/testmcache.nim @@ -1,6 +1,6 @@ {.used.} -import unittest, options, sets, sequtils, bearssl +import unittest2, options, sets, sequtils, bearssl import stew/byteutils import ../../libp2p/[peerid, crypto/crypto, diff --git a/tests/pubsub/testmessage.nim b/tests/pubsub/testmessage.nim index de11e8e03..8a2b64be6 100644 --- a/tests/pubsub/testmessage.nim +++ b/tests/pubsub/testmessage.nim @@ -1,4 +1,4 @@ -import unittest +import unittest2 {.used.} diff --git a/tests/pubsub/testpubsub.nim b/tests/pubsub/testpubsub.nim index 42c6d7c49..c33e6e7d0 100644 --- a/tests/pubsub/testpubsub.nim +++ b/tests/pubsub/testpubsub.nim @@ -1,7 +1,6 @@ {.used.} -import testgossipinternal, - testfloodsub, +import testfloodsub, testgossipsub, testgossipsub2, testmcache, diff --git a/tests/pubsub/testtimedcache.nim b/tests/pubsub/testtimedcache.nim index d714e3f96..3fcbf28f2 100644 --- a/tests/pubsub/testtimedcache.nim +++ b/tests/pubsub/testtimedcache.nim @@ -1,6 +1,6 @@ {.used.} -import std/unittest +import unittest2 import chronos/timer import ../../libp2p/protocols/pubsub/timedcache diff --git a/tests/testbufferstream.nim b/tests/testbufferstream.nim index f38a2d027..856271a53 100644 --- a/tests/testbufferstream.nim +++ b/tests/testbufferstream.nim @@ -1,4 +1,3 @@ -import unittest import chronos, stew/byteutils import ../libp2p/stream/bufferstream, ../libp2p/stream/lpstream, diff --git a/tests/testcid.nim b/tests/testcid.nim index 2609eee3b..9d038ed53 100644 --- a/tests/testcid.nim +++ b/tests/testcid.nim @@ -1,4 +1,4 @@ -import unittest +import unittest2 import ../libp2p/[cid, multihash, multicodec] when defined(nimHasUsed): {.used.} diff --git a/tests/testconnection.nim b/tests/testconnection.nim index 409cb3e1b..d4cb6f8ec 100644 --- a/tests/testconnection.nim +++ b/tests/testconnection.nim @@ -1,4 +1,3 @@ -import unittest import chronos, nimcrypto/utils import ../libp2p/[stream/connection, stream/bufferstream] diff --git a/tests/testconnmngr.nim b/tests/testconnmngr.nim index 86aaa67d1..190716569 100644 --- a/tests/testconnmngr.nim +++ b/tests/testconnmngr.nim @@ -1,4 +1,4 @@ -import unittest, sequtils +import sequtils import chronos import ../libp2p/[connmanager, stream/connection, diff --git a/tests/testcrypto.nim b/tests/testcrypto.nim index 73d5e6586..9c47c0ca3 100644 --- a/tests/testcrypto.nim +++ b/tests/testcrypto.nim @@ -9,7 +9,7 @@ ## Test vectors was made using Go implementation ## https://github.com/libp2p/go-libp2p-crypto/blob/master/key.go -import unittest +import unittest2 import nimcrypto/[utils, sysrand] import ../libp2p/crypto/[crypto, chacha20poly1305, curve25519, hkdf] diff --git a/tests/testdaemon.nim b/tests/testdaemon.nim index 485b27886..885cb1c5d 100644 --- a/tests/testdaemon.nim +++ b/tests/testdaemon.nim @@ -1,5 +1,4 @@ -import unittest -import chronos +import chronos, unittest2 import ../libp2p/daemon/daemonapi, ../libp2p/multiaddress, ../libp2p/multicodec, ../libp2p/cid, ../libp2p/multihash, ../libp2p/peerid diff --git a/tests/testecnist.nim b/tests/testecnist.nim index aa2245ce3..2ef5751b6 100644 --- a/tests/testecnist.nim +++ b/tests/testecnist.nim @@ -6,7 +6,7 @@ ## at your option. ## This file may not be copied, modified, or distributed except according to ## those terms. -import unittest +import unittest2 import nimcrypto/utils import ../libp2p/crypto/[crypto, ecnist] import stew/results diff --git a/tests/tested25519.nim b/tests/tested25519.nim index fd5415d08..b912048ec 100644 --- a/tests/tested25519.nim +++ b/tests/tested25519.nim @@ -8,7 +8,7 @@ ## those terms. ## Test vectors are from RFC 8032 (https://tools.ietf.org/html/rfc8032) -import unittest +import unittest2 import nimcrypto/utils import ../libp2p/crypto/crypto import ../libp2p/crypto/ed25519/ed25519 diff --git a/tests/testidentify.nim b/tests/testidentify.nim index 5b87be552..cbdd9b236 100644 --- a/tests/testidentify.nim +++ b/tests/testidentify.nim @@ -1,4 +1,4 @@ -import unittest, options, bearssl +import options, bearssl import chronos, strutils import ../libp2p/[protocols/identify, multiaddress, diff --git a/tests/testinterop.nim b/tests/testinterop.nim index 3ff50332d..338c1bdb4 100644 --- a/tests/testinterop.nim +++ b/tests/testinterop.nim @@ -1,5 +1,4 @@ import options, tables -import unittest import chronos, chronicles, stew/byteutils import helpers import ../libp2p/[daemon/daemonapi, diff --git a/tests/testminasn1.nim b/tests/testminasn1.nim index fe027a7e3..d73e5d4cb 100644 --- a/tests/testminasn1.nim +++ b/tests/testminasn1.nim @@ -6,7 +6,7 @@ ## at your option. ## This file may not be copied, modified, or distributed except according to ## those terms. -import unittest +import unittest2 import ../libp2p/crypto/minasn1 import nimcrypto/utils as ncrutils diff --git a/tests/testminprotobuf.nim b/tests/testminprotobuf.nim index d167285c4..ef539d587 100644 --- a/tests/testminprotobuf.nim +++ b/tests/testminprotobuf.nim @@ -7,7 +7,7 @@ ## This file may not be copied, modified, or distributed except according to ## those terms. -import unittest +import unittest2 import ../libp2p/protobuf/minprotobuf import stew/byteutils, strutils diff --git a/tests/testmplex.nim b/tests/testmplex.nim index 028a5bb86..1f830d2ea 100644 --- a/tests/testmplex.nim +++ b/tests/testmplex.nim @@ -1,4 +1,4 @@ -import unittest, strformat, strformat, random, oids, sequtils +import strformat, strformat, random, oids, sequtils import chronos, nimcrypto/utils, chronicles, stew/byteutils import ../libp2p/[errors, stream/connection, diff --git a/tests/testmultiaddress.nim b/tests/testmultiaddress.nim index 77042a58d..a1f3de106 100644 --- a/tests/testmultiaddress.nim +++ b/tests/testmultiaddress.nim @@ -1,4 +1,4 @@ -import unittest +import unittest2 import ../libp2p/[multicodec, multiaddress] when defined(nimHasUsed): {.used.} diff --git a/tests/testmultibase.nim b/tests/testmultibase.nim index a169f6a73..e0e590ac0 100644 --- a/tests/testmultibase.nim +++ b/tests/testmultibase.nim @@ -1,4 +1,4 @@ -import unittest +import unittest2 import ../libp2p/multibase import stew/results diff --git a/tests/testmultihash.nim b/tests/testmultihash.nim index 6436baf3b..e678150f1 100644 --- a/tests/testmultihash.nim +++ b/tests/testmultihash.nim @@ -1,4 +1,4 @@ -import unittest +import unittest2 import nimcrypto/utils import ../libp2p/multihash diff --git a/tests/testmultistream.nim b/tests/testmultistream.nim index ace3e1343..d5c02e6be 100644 --- a/tests/testmultistream.nim +++ b/tests/testmultistream.nim @@ -1,4 +1,4 @@ -import unittest, strutils, strformat, stew/byteutils +import strutils, strformat, stew/byteutils import chronos import ../libp2p/errors, ../libp2p/multistream, diff --git a/tests/testnoise.nim b/tests/testnoise.nim index 22ff2ac2d..11d556ef4 100644 --- a/tests/testnoise.nim +++ b/tests/testnoise.nim @@ -9,7 +9,7 @@ {.used.} -import unittest, tables, bearssl +import tables, bearssl import chronos, stew/byteutils import chronicles import ../libp2p/crypto/crypto diff --git a/tests/testpeerid.nim b/tests/testpeerid.nim index 47b4a6117..95e80e342 100644 --- a/tests/testpeerid.nim +++ b/tests/testpeerid.nim @@ -9,7 +9,7 @@ ## Test vectors was made using Go implementation ## https://github.com/libp2p/go-libp2p-peer -import unittest +import unittest2 import nimcrypto/utils, stew/base58 import ../libp2p/crypto/crypto, ../libp2p/peerid diff --git a/tests/testpeerinfo.nim b/tests/testpeerinfo.nim index 0bc8c713a..3f78b0d59 100644 --- a/tests/testpeerinfo.nim +++ b/tests/testpeerinfo.nim @@ -1,6 +1,6 @@ {.used.} -import unittest, options, bearssl +import options, bearssl import chronos import ../libp2p/crypto/crypto, ../libp2p/peerinfo, @@ -35,7 +35,11 @@ suite "PeerInfo": check seckey.getKey.get() == peerInfo.publicKey.get() test "Should init from CIDv0 string": - var peerInfo = PeerInfo.init("QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N") + var peerInfo: PeerInfo + try: + peerInfo = PeerInfo.init("QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N") + except CatchableError: + check false check: PeerID.init("QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N").get() == peerInfo.peerId diff --git a/tests/testpeerstore.nim b/tests/testpeerstore.nim index 7eba38962..44939872e 100644 --- a/tests/testpeerstore.nim +++ b/tests/testpeerstore.nim @@ -1,5 +1,5 @@ import - std/[unittest, tables, sequtils, sets], + std/[unittest2, tables, sequtils, sets], ../libp2p/crypto/crypto, ../libp2p/multiaddress, ../libp2p/peerid, @@ -21,12 +21,12 @@ suite "PeerStore": multiaddrStr2 = "/ip4/0.0.0.0/tcp/1234/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC" multiaddr2 = MultiAddress.init(multiaddrStr2).get() testcodec2 = "/nim/libp2p/test/0.0.2-beta1" - + test "PeerStore API": # Set up peer store var peerStore = PeerStore.new() - + peerStore.addressBook.add(peerId1, multiaddr1) peerStore.addressBook.add(peerId2, multiaddr2) peerStore.protoBook.add(peerId1, testcodec1) @@ -47,7 +47,7 @@ suite "PeerStore": peer2Stored.addrs == toHashSet([multiaddr2]) peer2Stored.protos == toHashSet([testcodec2]) peer2Stored.publicKey == keyPair2.pubkey - + # Test PeerStore::peers let peers = peerStore.peers() check: @@ -69,7 +69,7 @@ suite "PeerStore": # Now try and delete it again peerStore.delete(peerId1) == false - + test "PeerStore listeners": # Set up peer store with listener var @@ -77,13 +77,13 @@ suite "PeerStore": addrChanged = false protoChanged = false keyChanged = false - + proc addrChange(peerId: PeerID, addrs: HashSet[MultiAddress]) = addrChanged = true - + proc protoChange(peerId: PeerID, protos: HashSet[string]) = protoChanged = true - + proc keyChange(peerId: PeerID, publicKey: PublicKey) = keyChanged = true @@ -95,32 +95,32 @@ suite "PeerStore": peerStore.addressBook.add(peerId1, multiaddr1) check: addrChanged == true - + # Test listener triggered on setting addresses addrChanged = false peerStore.addressBook.set(peerId2, toHashSet([multiaddr1, multiaddr2])) check: addrChanged == true - + # Test listener triggered on adding proto peerStore.protoBook.add(peerId1, testcodec1) check: protoChanged == true - + # Test listener triggered on setting protos protoChanged = false peerStore.protoBook.set(peerId2, toHashSet([testcodec1, testcodec2])) check: protoChanged == true - + # Test listener triggered on setting public key peerStore.keyBook.set(peerId1, keyPair1.pubkey) check: keyChanged == true - + # Test listener triggered on changing public key keyChanged = false peerStore.keyBook.set(peerId1, @@ -132,18 +132,18 @@ suite "PeerStore": # Set up address book var addressBook = PeerStore.new().addressBook - + # Test AddressBook::add addressBook.add(peerId1, multiaddr1) - + check: toSeq(keys(addressBook.book))[0] == peerId1 toSeq(values(addressBook.book))[0] == toHashSet([multiaddr1]) - + # Test AddressBook::get check: addressBook.get(peerId1) == toHashSet([multiaddr1]) - + # Test AddressBook::delete check: # Try to delete peerId that doesn't exist @@ -153,7 +153,7 @@ suite "PeerStore": addressBook.book.len == 1 # sanity addressBook.delete(peerId1) == true addressBook.book.len == 0 - + # Test AddressBook::set # Set peerId2 with multiple multiaddrs addressBook.set(peerId2, @@ -161,23 +161,23 @@ suite "PeerStore": check: toSeq(keys(addressBook.book))[0] == peerId2 toSeq(values(addressBook.book))[0] == toHashSet([multiaddr1, multiaddr2]) - + test "ProtoBook API": # Set up protocol book var protoBook = PeerStore.new().protoBook - + # Test ProtoBook::add protoBook.add(peerId1, testcodec1) - + check: toSeq(keys(protoBook.book))[0] == peerId1 toSeq(values(protoBook.book))[0] == toHashSet([testcodec1]) - + # Test ProtoBook::get check: protoBook.get(peerId1) == toHashSet([testcodec1]) - + # Test ProtoBook::delete check: # Try to delete peerId that doesn't exist @@ -187,7 +187,7 @@ suite "PeerStore": protoBook.book.len == 1 # sanity protoBook.delete(peerId1) == true protoBook.book.len == 0 - + # Test ProtoBook::set # Set peerId2 with multiple protocols protoBook.set(peerId2, @@ -200,18 +200,18 @@ suite "PeerStore": # Set up key book var keyBook = PeerStore.new().keyBook - + # Test KeyBook::set keyBook.set(peerId1, keyPair1.pubkey) check: toSeq(keys(keyBook.book))[0] == peerId1 toSeq(values(keyBook.book))[0] == keyPair1.pubkey - + # Test KeyBook::get check: keyBook.get(peerId1) == keyPair1.pubkey - + # Test KeyBook::delete check: # Try to delete peerId that doesn't exist diff --git a/tests/testpkifilter.nim b/tests/testpkifilter.nim index e87641213..b1ea8a839 100644 --- a/tests/testpkifilter.nim +++ b/tests/testpkifilter.nim @@ -8,7 +8,7 @@ ## those terms. when defined(nimHasUsed): {.used.} -import unittest +import unittest2 import ../libp2p/crypto/crypto import nimcrypto/utils diff --git a/tests/testrsa.nim b/tests/testrsa.nim index fb37036f0..737a7851b 100644 --- a/tests/testrsa.nim +++ b/tests/testrsa.nim @@ -6,7 +6,7 @@ ## at your option. ## This file may not be copied, modified, or distributed except according to ## those terms. -import unittest +import unittest2 import nimcrypto/utils import ../libp2p/crypto/[crypto, rsa] diff --git a/tests/testsecp256k1.nim b/tests/testsecp256k1.nim index 19216c1d5..c87e463ad 100644 --- a/tests/testsecp256k1.nim +++ b/tests/testsecp256k1.nim @@ -6,7 +6,7 @@ ## at your option. ## This file may not be copied, modified, or distributed except according to ## those terms. -import unittest, bearssl +import unittest2, bearssl import ../libp2p/crypto/[crypto, secp] import nimcrypto/utils diff --git a/tests/teststreamseq.nim b/tests/teststreamseq.nim index 51932fce8..4050633e6 100644 --- a/tests/teststreamseq.nim +++ b/tests/teststreamseq.nim @@ -1,6 +1,6 @@ {.used.} -import unittest +import unittest2 import stew/byteutils import ../libp2p/stream/streamseq diff --git a/tests/testswitch.nim b/tests/testswitch.nim index 5537713db..effa2711a 100644 --- a/tests/testswitch.nim +++ b/tests/testswitch.nim @@ -1,6 +1,6 @@ {.used.} -import unittest, options, sequtils +import options, sequtils import chronos import stew/byteutils import nimcrypto/sysrand diff --git a/tests/testtransport.nim b/tests/testtransport.nim index dc0690668..dd59a6bdb 100644 --- a/tests/testtransport.nim +++ b/tests/testtransport.nim @@ -1,6 +1,6 @@ {.used.} -import unittest, sequtils +import sequtils import chronos, stew/byteutils import ../libp2p/[stream/connection, transports/transport, diff --git a/tests/testvarint.nim b/tests/testvarint.nim index 87bea83c9..40bfc2d7c 100644 --- a/tests/testvarint.nim +++ b/tests/testvarint.nim @@ -1,4 +1,4 @@ -import unittest +import unittest2 import ../libp2p/varint when defined(nimHasUsed): {.used.}