From 7b103e02f2fe93ad9e367a9620e5117a18437eb2 Mon Sep 17 00:00:00 2001 From: Tanguy Date: Thu, 20 Oct 2022 12:22:28 +0200 Subject: [PATCH] Allow public address mapping (#767) --- examples/circuitrelay.nim | 14 +++++------ libp2p/peerinfo.nim | 35 ++++++++++++++++++++-------- libp2p/switch.nim | 8 +++---- tests/testidentify.nim | 14 +++++------ tests/testnoise.nim | 3 +-- tests/testpeerinfo.nim | 24 +++++++++++++++---- tests/testrelayv2.nim | 49 ++++++++++++++++++++++++++------------- 7 files changed, 97 insertions(+), 50 deletions(-) diff --git a/examples/circuitrelay.nim b/examples/circuitrelay.nim index bf90d97..accbd11 100644 --- a/examples/circuitrelay.nim +++ b/examples/circuitrelay.nim @@ -48,19 +48,19 @@ proc main() {.async.} = swSrc = createCircuitRelaySwitch(clSrc) swDst = createCircuitRelaySwitch(clDst) - # Create a relay address to swDst using swRel as the relay - addrs = MultiAddress.init($swRel.peerInfo.addrs[0] & "/p2p/" & - $swRel.peerInfo.peerId & "/p2p-circuit/p2p/" & - $swDst.peerInfo.peerId).get() - swDst.mount(proto) await swRel.start() await swSrc.start() await swDst.start() - # Connect both Src and Dst to the relay, but not to each other. - await swSrc.connect(swRel.peerInfo.peerId, swRel.peerInfo.addrs) + let + # Create a relay address to swDst using swRel as the relay + addrs = MultiAddress.init($swRel.peerInfo.addrs[0] & "/p2p/" & + $swRel.peerInfo.peerId & "/p2p-circuit/p2p/" & + $swDst.peerInfo.peerId).get() + + # Connect Dst to the relay await swDst.connect(swRel.peerInfo.peerId, swRel.peerInfo.addrs) # Dst reserve a slot on the relay. diff --git a/libp2p/peerinfo.nim b/libp2p/peerinfo.nim index 59fbe74..39a6ad6 100644 --- a/libp2p/peerinfo.nim +++ b/libp2p/peerinfo.nim @@ -22,11 +22,17 @@ export peerid, multiaddress, crypto, routing_record, errors, results ## Our local peer info type - PeerInfoError* = LPError + PeerInfoError* = object of LPError + + AddressMapper* = + proc(listenAddrs: seq[MultiAddress]): Future[seq[MultiAddress]] + {.gcsafe, raises: [Defect].} PeerInfo* {.public.} = ref object peerId*: PeerId - addrs*: seq[MultiAddress] + listenAddrs*: seq[MultiAddress] + addrs: seq[MultiAddress] + addressMappers*: seq[AddressMapper] protocols*: seq[string] protoVersion*: string agentVersion*: string @@ -37,6 +43,7 @@ type func shortLog*(p: PeerInfo): auto = ( peerId: $p.peerId, + listenAddrs: mapIt(p.listenAddrs, $it), addrs: mapIt(p.addrs, $it), protocols: mapIt(p.protocols, $it), protoVersion: p.protoVersion, @@ -44,7 +51,11 @@ func shortLog*(p: PeerInfo): auto = ) chronicles.formatIt(PeerInfo): shortLog(it) -proc update*(p: PeerInfo) = +proc update*(p: PeerInfo) {.async.} = + p.addrs = p.listenAddrs + for mapper in p.addressMappers: + p.addrs = await mapper(p.addrs) + let sprRes = SignedPeerRecord.init( p.privateKey, PeerRecord.init(p.peerId, p.addrs) @@ -55,20 +66,25 @@ proc update*(p: PeerInfo) = discard #info "Can't update the signed peer record" +proc addrs*(p: PeerInfo): seq[MultiAddress] = + p.addrs + proc new*( p: typedesc[PeerInfo], key: PrivateKey, - addrs: openArray[MultiAddress] = [], + listenAddrs: openArray[MultiAddress] = [], protocols: openArray[string] = [], protoVersion: string = "", - agentVersion: string = ""): PeerInfo - {.raises: [Defect, PeerInfoError].} = + agentVersion: string = "", + addressMappers = newSeq[AddressMapper](), + ): PeerInfo + {.raises: [Defect, LPError].} = let pubkey = try: key.getPublicKey().tryGet() except CatchableError: raise newException(PeerInfoError, "invalid private key") - + let peerId = PeerId.init(key).tryGet() let peerInfo = PeerInfo( @@ -77,10 +93,9 @@ proc new*( privateKey: key, protoVersion: protoVersion, agentVersion: agentVersion, - addrs: @addrs, + listenAddrs: @listenAddrs, protocols: @protocols, + addressMappers: addressMappers ) - peerInfo.update() - return peerInfo diff --git a/libp2p/switch.nim b/libp2p/switch.nim index 93f2b5f..b6fc22f 100644 --- a/libp2p/switch.nim +++ b/libp2p/switch.nim @@ -299,11 +299,11 @@ proc start*(s: Switch) {.async, gcsafe, public.} = trace "starting switch for peer", peerInfo = s.peerInfo var startFuts: seq[Future[void]] for t in s.transports: - let addrs = s.peerInfo.addrs.filterIt( + let addrs = s.peerInfo.listenAddrs.filterIt( t.handles(it) ) - s.peerInfo.addrs.keepItIf( + s.peerInfo.listenAddrs.keepItIf( it notin addrs ) @@ -320,9 +320,9 @@ proc start*(s: Switch) {.async, gcsafe, public.} = for t in s.transports: # for each transport if t.addrs.len > 0 or t.running: s.acceptFuts.add(s.accept(t)) - s.peerInfo.addrs &= t.addrs + s.peerInfo.listenAddrs &= t.addrs - s.peerInfo.update() + await s.peerInfo.update() await s.ms.start() diff --git a/tests/testidentify.nim b/tests/testidentify.nim index 8fc5292..b83eecd 100644 --- a/tests/testidentify.nim +++ b/tests/testidentify.nim @@ -52,6 +52,9 @@ suite "Identify": msListen = MultistreamSelect.new() msDial = MultistreamSelect.new() + serverFut = transport1.start(ma) + await remotePeerInfo.update() + asyncTeardown: await conn.close() await acceptFut @@ -61,7 +64,6 @@ suite "Identify": asyncTest "default agent version": msListen.addHandler(IdentifyCodec, identifyProto1) - serverFut = transport1.start(ma) proc acceptHandler(): Future[void] {.async, gcsafe.} = let c = await transport1.accept() await msListen.handle(c) @@ -84,8 +86,6 @@ suite "Identify": remotePeerInfo.agentVersion = customAgentVersion msListen.addHandler(IdentifyCodec, identifyProto1) - serverFut = transport1.start(ma) - proc acceptHandler(): Future[void] {.async, gcsafe.} = let c = await transport1.accept() await msListen.handle(c) @@ -105,7 +105,6 @@ suite "Identify": asyncTest "handle failed identify": msListen.addHandler(IdentifyCodec, identifyProto1) - asyncSpawn transport1.start(ma) proc acceptHandler() {.async.} = var conn: Connection @@ -128,7 +127,6 @@ suite "Identify": asyncTest "can send signed peer record": msListen.addHandler(IdentifyCodec, identifyProto1) identifyProto1.sendSignedPeerRecord = true - serverFut = transport1.start(ma) proc acceptHandler(): Future[void] {.async, gcsafe.} = let c = await transport1.accept() await msListen.handle(c) @@ -195,7 +193,8 @@ suite "Identify": asyncTest "simple push identify": switch2.peerInfo.protocols.add("/newprotocol/") - switch2.peerInfo.addrs.add(MultiAddress.init("/ip4/127.0.0.1/tcp/5555").tryGet()) + switch2.peerInfo.listenAddrs.add(MultiAddress.init("/ip4/127.0.0.1/tcp/5555").tryGet()) + await switch2.peerInfo.update() check: switch1.peerStore[AddressBook][switch2.peerInfo.peerId] != switch2.peerInfo.addrs @@ -216,7 +215,8 @@ suite "Identify": asyncTest "wrong peer id push identify": switch2.peerInfo.protocols.add("/newprotocol/") - switch2.peerInfo.addrs.add(MultiAddress.init("/ip4/127.0.0.1/tcp/5555").tryGet()) + switch2.peerInfo.listenAddrs.add(MultiAddress.init("/ip4/127.0.0.1/tcp/5555").tryGet()) + await switch2.peerInfo.update() check: switch1.peerStore[AddressBook][switch2.peerInfo.peerId] != switch2.peerInfo.addrs diff --git a/tests/testnoise.nim b/tests/testnoise.nim index a94714c..f715a7b 100644 --- a/tests/testnoise.nim +++ b/tests/testnoise.nim @@ -60,8 +60,7 @@ method init(p: TestProto) {.gcsafe.} = proc createSwitch(ma: MultiAddress; outgoing: bool, secio: bool = false): (Switch, PeerInfo) = var privateKey = PrivateKey.random(ECDSA, rng[]).get() - peerInfo = PeerInfo.new(privateKey) - peerInfo.addrs.add(ma) + peerInfo = PeerInfo.new(privateKey, @[ma]) proc createMplex(conn: Connection): Muxer = result = Mplex.new(conn) diff --git a/tests/testpeerinfo.nim b/tests/testpeerinfo.nim index 339836b..e4f2739 100644 --- a/tests/testpeerinfo.nim +++ b/tests/testpeerinfo.nim @@ -18,22 +18,24 @@ suite "PeerInfo": check peerId == peerInfo.peerId check seckey.getPublicKey().get() == peerInfo.publicKey - + test "Signed peer record": const ExpectedDomain = $multiCodec("libp2p-peer-record") ExpectedPayloadType = @[(byte) 0x03, (byte) 0x01] - + let seckey = PrivateKey.random(rng[]).tryGet() peerId = PeerId.init(seckey).get() multiAddresses = @[MultiAddress.init("/ip4/0.0.0.0/tcp/24").tryGet(), MultiAddress.init("/ip4/0.0.0.0/tcp/25").tryGet()] peerInfo = PeerInfo.new(seckey, multiAddresses) - + + waitFor(peerInfo.update()) + let env = peerInfo.signedPeerRecord.envelope rec = PeerRecord.decode(env.payload()).tryGet() - + # Check envelope fields check: env.publicKey == peerInfo.publicKey @@ -47,3 +49,17 @@ suite "PeerInfo": rec.addresses.len == 2 rec.addresses[0].address == multiAddresses[0] rec.addresses[1].address == multiAddresses[1] + + test "Public address mapping": + let + seckey = PrivateKey.random(ECDSA, rng[]).get() + multiAddresses = @[MultiAddress.init("/ip4/0.0.0.0/tcp/24").tryGet(), MultiAddress.init("/ip4/0.0.0.0/tcp/25").tryGet()] + multiAddresses2 = @[MultiAddress.init("/ip4/8.8.8.8/tcp/33").tryGet()] + + proc addressMapper(input: seq[MultiAddress]): Future[seq[MultiAddress]] {.async.} = + check input == multiAddresses + await sleepAsync(0.seconds) + return multiAddresses2 + var peerInfo = PeerInfo.new(seckey, multiAddresses, addressMappers = @[addressMapper]) + waitFor peerInfo.update() + check peerInfo.addrs == multiAddresses2 diff --git a/tests/testrelayv2.nim b/tests/testrelayv2.nim index 8565b36..16d37d9 100644 --- a/tests/testrelayv2.nim +++ b/tests/testrelayv2.nim @@ -118,7 +118,6 @@ suite "Circuit Relay V2": asyncTeardown: checkTrackers() var - addrs {.threadvar.}: MultiAddress customProtoCodec {.threadvar.}: string proto {.threadvar.}: LPProtocol ttl {.threadvar.}: int @@ -145,9 +144,6 @@ suite "Circuit Relay V2": src = createSwitch(srcCl) dst = createSwitch(dstCl) rel = newStandardSwitch() - addrs = MultiAddress.init($rel.peerInfo.addrs[0] & "/p2p/" & - $rel.peerInfo.peerId & "/p2p-circuit/p2p/" & - $dst.peerInfo.peerId).get() asyncTest "Connection succeed": proto.handler = proc(conn: Connection, proto: string) {.async.} = @@ -167,6 +163,10 @@ suite "Circuit Relay V2": await src.start() await dst.start() + let addrs = MultiAddress.init($rel.peerInfo.addrs[0] & "/p2p/" & + $rel.peerInfo.peerId & "/p2p-circuit/p2p/" & + $dst.peerInfo.peerId).get() + await src.connect(rel.peerInfo.peerId, rel.peerInfo.addrs) await dst.connect(rel.peerInfo.peerId, rel.peerInfo.addrs) @@ -200,6 +200,10 @@ suite "Circuit Relay V2": await src.start() await dst.start() + let addrs = MultiAddress.init($rel.peerInfo.addrs[0] & "/p2p/" & + $rel.peerInfo.peerId & "/p2p-circuit/p2p/" & + $dst.peerInfo.peerId).get() + await src.connect(rel.peerInfo.peerId, rel.peerInfo.addrs) await dst.connect(rel.peerInfo.peerId, rel.peerInfo.addrs) @@ -245,6 +249,10 @@ take to the ship.""") await src.start() await dst.start() + let addrs = MultiAddress.init($rel.peerInfo.addrs[0] & "/p2p/" & + $rel.peerInfo.peerId & "/p2p-circuit/p2p/" & + $dst.peerInfo.peerId).get() + await src.connect(rel.peerInfo.peerId, rel.peerInfo.addrs) await dst.connect(rel.peerInfo.peerId, rel.peerInfo.addrs) @@ -277,6 +285,10 @@ take to the ship.""") await src.start() await dst.start() + let addrs = MultiAddress.init($rel.peerInfo.addrs[0] & "/p2p/" & + $rel.peerInfo.peerId & "/p2p-circuit/p2p/" & + $dst.peerInfo.peerId).get() + await src.connect(rel.peerInfo.peerId, rel.peerInfo.addrs) await dst.connect(rel.peerInfo.peerId, rel.peerInfo.addrs) @@ -308,11 +320,6 @@ take to the ship.""") rel2Cl = RelayClient.new(canHop = true) rel2 = createSwitch(rel2Cl) rv2 = Relay.new() - addrs = @[ MultiAddress.init($rel.peerInfo.addrs[0] & "/p2p/" & - $rel.peerInfo.peerId & "/p2p-circuit/p2p/" & - $rel2.peerInfo.peerId & "/p2p/" & - $rel2.peerInfo.peerId & "/p2p-circuit/p2p/" & - $dst.peerInfo.peerId).get() ] rv2.setup(rel) rel.mount(rv2) dst.mount(proto) @@ -321,6 +328,13 @@ take to the ship.""") await src.start() await dst.start() + let + addrs = @[ MultiAddress.init($rel.peerInfo.addrs[0] & "/p2p/" & + $rel.peerInfo.peerId & "/p2p-circuit/p2p/" & + $rel2.peerInfo.peerId & "/p2p/" & + $rel2.peerInfo.peerId & "/p2p-circuit/p2p/" & + $dst.peerInfo.peerId).get() ] + await src.connect(rel.peerInfo.peerId, rel.peerInfo.addrs) await rel2.connect(rel.peerInfo.peerId, rel.peerInfo.addrs) await dst.connect(rel2.peerInfo.peerId, rel2.peerInfo.addrs) @@ -367,6 +381,16 @@ take to the ship.""") switchA = createSwitch(clientA) switchB = createSwitch(clientB) switchC = createSwitch(clientC) + + switchA.mount(protoBCA) + switchB.mount(protoCAB) + switchC.mount(protoABC) + + await switchA.start() + await switchB.start() + await switchC.start() + + let addrsABC = MultiAddress.init($switchB.peerInfo.addrs[0] & "/p2p/" & $switchB.peerInfo.peerId & "/p2p-circuit/p2p/" & $switchC.peerInfo.peerId).get() @@ -376,13 +400,6 @@ take to the ship.""") addrsCAB = MultiAddress.init($switchA.peerInfo.addrs[0] & "/p2p/" & $switchA.peerInfo.peerId & "/p2p-circuit/p2p/" & $switchB.peerInfo.peerId).get() - switchA.mount(protoBCA) - switchB.mount(protoCAB) - switchC.mount(protoABC) - - await switchA.start() - await switchB.start() - await switchC.start() await switchA.connect(switchB.peerInfo.peerId, switchB.peerInfo.addrs) await switchB.connect(switchC.peerInfo.peerId, switchC.peerInfo.addrs)