From a21d300e3e9a15f9a0ba5232c4115b48cf95bf4e Mon Sep 17 00:00:00 2001 From: Dmitriy Ryajov Date: Mon, 5 Apr 2021 13:37:14 -0600 Subject: [PATCH] Builders (#558) * use a builder pattern to build the switch (#551) * use a builder pattern to build the switch * with with * more refs * Merge master (#555) * Revisit Floodsub (#543) Fixes #525 add coverage to unsubscribeAll and testing * add mounted protos to identify message (#546) * add stable/unstable auto bumps * fix auto-bump CI * merge nbc auto bump with CI in order to bump only on CI success * put conditional locks on nbc bump (#549) * Fix minor exception issues (#550) Makes code compatible with https://github.com/status-im/nim-chronos/pull/166 without requiring it. * fix nimbus ref for auto-bump stable's PR * Split dialer (#542) * extracting dialing logic to dialer * exposing upgrade methods on transport * cleanup * fixing tests to use new interfaces * add comments * add base exception class and fix hierarchy * fix imports * `doAssert` is `ValueError` not `AssertionError`? * revert back to `AssertionError` Co-authored-by: Giovanni Petrantoni <7008900+sinkingsugar@users.noreply.github.com> Co-authored-by: Jacek Sieka * `doAssert` is `ValueError` not `AssertionError`? * revert back to `AssertionError` * fix builders * more builder stuff * more builders Co-authored-by: Giovanni Petrantoni <7008900+sinkingsugar@users.noreply.github.com> Co-authored-by: Jacek Sieka --- libp2p.nim | 4 +- libp2p/builders.nim | 198 ++++++++++++++++++++++++++++ libp2p/protocols/secure/noise.nim | 6 +- libp2p/standard_setup.nim | 71 ---------- tests/pubsub/testgossipinternal.nim | 2 +- tests/pubsub/utils.nim | 4 +- tests/testinterop.nim | 2 +- tests/testswitch.nim | 2 +- 8 files changed, 209 insertions(+), 80 deletions(-) create mode 100644 libp2p/builders.nim delete mode 100644 libp2p/standard_setup.nim diff --git a/libp2p.nim b/libp2p.nim index 4d6c4a93b..668fbb80f 100644 --- a/libp2p.nim +++ b/libp2p.nim @@ -26,7 +26,7 @@ import peerid, peerinfo, multiaddress, - standard_setup, + builders, crypto/crypto] import bearssl @@ -36,4 +36,4 @@ export connection, multiaddress, crypto, lpstream, bufferstream, bearssl, muxer, mplex, transport, tcptransport, noise, errors, cid, multihash, - multicodec, standard_setup + multicodec, builders diff --git a/libp2p/builders.nim b/libp2p/builders.nim new file mode 100644 index 000000000..cbe05e054 --- /dev/null +++ b/libp2p/builders.nim @@ -0,0 +1,198 @@ +import + options, tables, chronos, bearssl, + switch, peerid, peerinfo, stream/connection, multiaddress, + crypto/crypto, transports/[transport, tcptransport], + muxers/[muxer, mplex/mplex], + protocols/[identify, secure/secure, secure/noise], + connmanager, upgrademngrs/muxedupgrade + +export + switch, peerid, peerinfo, connection, multiaddress, crypto + +type + SecureProtocol* {.pure.} = enum + Noise, + Secio {.deprecated.} + + TcpTransportOpts = object + enable: bool + flags: set[ServerFlags] + + MplexOpts = object + enable: bool + newMuxer: MuxerConstructor + + SwitchBuilder* = ref object + privKey: Option[PrivateKey] + address: MultiAddress + secureManagers: seq[SecureProtocol] + mplexOpts: MplexOpts + tcpTransportOpts: TcpTransportOpts + rng: ref BrHmacDrbgContext + inTimeout: Duration + outTimeout: Duration + maxConnections: int + maxIn: int + maxOut: int + maxConnsPerPeer: int + protoVersion: string + agentVersion: string + +proc init*(T: type[SwitchBuilder]): T = + SwitchBuilder( + privKey: none(PrivateKey), + address: MultiAddress.init("/ip4/127.0.0.1/tcp/0").tryGet(), + secureManagers: @[], + tcpTransportOpts: TcpTransportOpts(), + rng: newRng(), + maxConnections: MaxConnections, + maxIn: -1, + maxOut: -1, + maxConnsPerPeer: MaxConnectionsPerPeer, + protoVersion: ProtoVersion, + agentVersion: AgentVersion) + +proc withPrivateKey*(b: SwitchBuilder, privateKey: PrivateKey): SwitchBuilder = + b.privKey = some(privateKey) + b + +proc withAddress*(b: SwitchBuilder, address: MultiAddress): SwitchBuilder = + b.address = address + b + +proc withMplex*(b: SwitchBuilder, inTimeout = 5.minutes, outTimeout = 5.minutes): SwitchBuilder = + proc newMuxer(conn: Connection): Muxer = + Mplex.init( + conn, + inTimeout = inTimeout, + outTimeout = outTimeout) + + b.mplexOpts = MplexOpts( + enable: true, + newMuxer: newMuxer, + ) + + b + +proc withNoise*(b: SwitchBuilder): SwitchBuilder = + b.secureManagers.add(SecureProtocol.Noise) + b + +proc withTcpTransport*(b: SwitchBuilder, flags: set[ServerFlags] = {}): SwitchBuilder = + b.tcpTransportOpts.enable = true + b.tcpTransportOpts.flags = flags + b + +proc withRng*(b: SwitchBuilder, rng: ref BrHmacDrbgContext): SwitchBuilder = + b.rng = rng + b + +proc withMaxConnections*(b: SwitchBuilder, maxConnections: int): SwitchBuilder = + b.maxConnections = maxConnections + b + +proc withMaxIn*(b: SwitchBuilder, maxIn: int): SwitchBuilder = + b.maxIn = maxIn + b + +proc withMaxOut*(b: SwitchBuilder, maxOut: int): SwitchBuilder = + b.maxOut = maxOut + b + +proc withMaxConnsPerPeer*(b: SwitchBuilder, maxConnsPerPeer: int): SwitchBuilder = + b.maxConnsPerPeer = maxConnsPerPeer + b + +proc withProtoVersion*(b: SwitchBuilder, protoVersion: string): SwitchBuilder = + b.protoVersion = protoVersion + b + +proc withAgentVersion*(b: SwitchBuilder, agentVersion: string): SwitchBuilder = + b.agentVersion = agentVersion + b + +proc build*(b: SwitchBuilder): Switch = + if b.rng == nil: # newRng could fail + raise (ref CatchableError)(msg: "Cannot initialize RNG") + + let + seckey = b.privKey.get(otherwise = PrivateKey.random(b.rng[]).tryGet()) + + var + secureManagerInstances: seq[Secure] + if SecureProtocol.Noise in b.secureManagers: + 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 + + let + muxers = block: + var muxers: Table[string, MuxerProvider] + if b.mplexOpts.enable: + muxers.add(MplexCodec, newMuxerProvider(b.mplexOpts.newMuxer, MplexCodec)) + muxers + + let + identify = newIdentify(peerInfo) + connManager = ConnManager.init(b.maxConnsPerPeer, b.maxConnections, b.maxIn, b.maxOut) + ms = newMultistream() + muxedUpgrade = MuxedUpgrade.init(identify, muxers, secureManagerInstances, connManager, ms) + + let + transports = block: + var transports: seq[Transport] + if b.tcpTransportOpts.enable: + transports.add(Transport(TcpTransport.init(b.tcpTransportOpts.flags, muxedUpgrade))) + transports + + if b.secureManagers.len == 0: + b.secureManagers &= SecureProtocol.Noise + + let switch = newSwitch( + peerInfo = peerInfo, + transports = transports, + identity = identify, + muxers = muxers, + secureManagers = secureManagerInstances, + connManager = connManager, + ms = ms) + + return switch + +proc newStandardSwitch*(privKey = none(PrivateKey), + address = MultiAddress.init("/ip4/127.0.0.1/tcp/0").tryGet(), + secureManagers: openarray[SecureProtocol] = [ + SecureProtocol.Noise, + ], + transportFlags: set[ServerFlags] = {}, + rng = newRng(), + inTimeout: Duration = 5.minutes, + outTimeout: Duration = 5.minutes, + maxConnections = MaxConnections, + maxIn = -1, + maxOut = -1, + maxConnsPerPeer = MaxConnectionsPerPeer): Switch = + if SecureProtocol.Secio in secureManagers: + quit("Secio is deprecated!") # use of secio is unsafe + + var b = SwitchBuilder + .init() + .withAddress(address) + .withRng(rng) + .withMaxConnections(maxConnections) + .withMaxIn(maxIn) + .withMaxOut(maxOut) + .withMaxConnsPerPeer(maxConnsPerPeer) + .withMplex(inTimeout, outTimeout) + .withTcpTransport(transportFlags) + .withNoise() + + if privKey.isSome(): + b = b.withPrivateKey(privKey.get()) + + b.build() diff --git a/libp2p/protocols/secure/noise.nim b/libp2p/protocols/secure/noise.nim index ed1408553..b09cdb1f2 100644 --- a/libp2p/protocols/secure/noise.nim +++ b/libp2p/protocols/secure/noise.nim @@ -578,8 +578,10 @@ method init*(p: Noise) {.gcsafe.} = p.codec = NoiseCodec proc newNoise*( - rng: ref BrHmacDrbgContext, privateKey: PrivateKey; - outgoing: bool = true; commonPrologue: seq[byte] = @[]): Noise = + rng: ref BrHmacDrbgContext, + privateKey: PrivateKey; + outgoing: bool = true; + commonPrologue: seq[byte] = @[]): Noise = result = Noise( rng: rng, outgoing: outgoing, diff --git a/libp2p/standard_setup.nim b/libp2p/standard_setup.nim deleted file mode 100644 index b3a2d3c53..000000000 --- a/libp2p/standard_setup.nim +++ /dev/null @@ -1,71 +0,0 @@ -import - options, tables, chronos, bearssl, - switch, peerid, peerinfo, stream/connection, multiaddress, - crypto/crypto, transports/[transport, tcptransport], - muxers/[muxer, mplex/mplex], - protocols/[identify, secure/secure, secure/noise], - upgrademngrs/[upgrade, muxedupgrade], connmanager - -export - switch, peerid, peerinfo, connection, multiaddress, crypto - -type - SecureProtocol* {.pure.} = enum - Noise, - Secio {.deprecated.} - -proc newStandardSwitch*(privKey = none(PrivateKey), - address = MultiAddress.init("/ip4/127.0.0.1/tcp/0").tryGet(), - secureManagers: openarray[SecureProtocol] = [ - SecureProtocol.Noise, - ], - transportFlags: set[ServerFlags] = {}, - rng = newRng(), - inTimeout: Duration = 5.minutes, - outTimeout: Duration = 5.minutes, - maxConnections = MaxConnections, - maxIn = -1, - maxOut = -1, - maxConnsPerPeer = MaxConnectionsPerPeer): Switch = - proc createMplex(conn: Connection): Muxer = - Mplex.init( - conn, - inTimeout = inTimeout, - outTimeout = outTimeout) - - if rng == nil: # newRng could fail - raise (ref CatchableError)(msg: "Cannot initialize RNG") - - let - seckey = privKey.get(otherwise = PrivateKey.random(rng[]).tryGet()) - peerInfo = PeerInfo.init(seckey, [address]) - - var - secureManagerInstances: seq[Secure] - - for sec in secureManagers: - case sec - of SecureProtocol.Noise: - secureManagerInstances &= newNoise(rng, seckey).Secure - of SecureProtocol.Secio: - quit("Secio is deprecated!") # use of secio is unsafe - - let - mplexProvider = newMuxerProvider(createMplex, MplexCodec) - ms = newMultistream() - identify = newIdentify(peerInfo) - muxers = {MplexCodec: mplexProvider}.toTable - connManager = ConnManager.init(maxConnsPerPeer, maxConnections, maxIn, maxOut) - muxedUpgrade = MuxedUpgrade.init(identify, muxers, secureManagerInstances, connManager, ms) - transports = @[Transport(TcpTransport.init(transportFlags, muxedUpgrade))] - - let switch = newSwitch( - peerInfo, - transports, - identify, - muxers, - secureManagers = secureManagerInstances, - connManager = connManager, - ms = ms) - - return switch diff --git a/tests/pubsub/testgossipinternal.nim b/tests/pubsub/testgossipinternal.nim index 921aea815..f944602b1 100644 --- a/tests/pubsub/testgossipinternal.nim +++ b/tests/pubsub/testgossipinternal.nim @@ -5,7 +5,7 @@ include ../../libp2p/protocols/pubsub/gossipsub import options import unittest, bearssl import stew/byteutils -import ../../libp2p/standard_setup +import ../../libp2p/builders import ../../libp2p/errors import ../../libp2p/crypto/crypto import ../../libp2p/stream/bufferstream diff --git a/tests/pubsub/utils.nim b/tests/pubsub/utils.nim index 254fff365..305d3d521 100644 --- a/tests/pubsub/utils.nim +++ b/tests/pubsub/utils.nim @@ -6,13 +6,13 @@ const import random, tables import chronos -import ../../libp2p/[standard_setup, +import ../../libp2p/[builders, protocols/pubsub/pubsub, protocols/pubsub/gossipsub, protocols/pubsub/floodsub, protocols/secure/secure] -export standard_setup +export builders randomize() diff --git a/tests/testinterop.nim b/tests/testinterop.nim index b841008d0..3ff50332d 100644 --- a/tests/testinterop.nim +++ b/tests/testinterop.nim @@ -10,7 +10,7 @@ import ../libp2p/[daemon/daemonapi, cid, varint, multihash, - standard_setup, + builders, peerid, peerinfo, switch, diff --git a/tests/testswitch.nim b/tests/testswitch.nim index a36a99b52..dc8dfe401 100644 --- a/tests/testswitch.nim +++ b/tests/testswitch.nim @@ -7,7 +7,7 @@ import nimcrypto/sysrand import ../libp2p/[errors, switch, multistream, - standard_setup, + builders, stream/bufferstream, stream/connection, multiaddress,