diff --git a/examples/tutorial_5_discovery.nim b/examples/tutorial_5_discovery.nim index 014d6ae58..9cb168ef7 100644 --- a/examples/tutorial_5_discovery.nim +++ b/examples/tutorial_5_discovery.nim @@ -6,8 +6,8 @@ ## we'll try to discover a specific peer to ping on the network. ## ## First, as usual, we import the dependencies: -import sequtils import chronos +import stew/byteutils import libp2p import libp2p/protocols/rendezvous @@ -30,63 +30,138 @@ proc createSwitch(rdv: RendezVous = RendezVous.new()): Switch = .withRendezVous(rdv) .build() +const DumbCodec = "/dumb/proto/1.0.0" +type DumbProto = ref object of LPProtocol +proc new(T: typedesc[DumbProto], nodeNumber: int): T = + proc handle(conn: Connection, proto: string) {.async, gcsafe.} = + echo "Node", nodeNumber, " received: ", string.fromBytes(await conn.readLp(1024)) + await conn.close() + + return T(codecs: @[DumbCodec], handler: handle) + ## We can create our main procedure: proc main() {.async, gcsafe.} = - # Create a "network" of six nodes looking like that: - # node0 <-> node1 <-> node2 <-> node3 <-> node4 <-> node5 + # First of all, we'll create and start a boot node that'll be + # used as an information hub on the network + let bootNode = createSwitch() + await bootNode.start() + var switches: seq[Switch] = @[] discManagers: seq[DiscoveryManager] = @[] - for _ in 0..5: + + for i in 0..5: # Create a RendezVous Protocol let rdv = RendezVous.new() # Create a switch, mount the RendezVous protocol to it, start it and - # enventually connect it to the previous node in the sequence + # eventually connect it to the bootNode let switch = createSwitch(rdv) + switch.mount(DumbProto.new(i)) await switch.start() - if switches.len > 0: - await switches[^1].connect(switch.peerInfo.peerId, switch.peerInfo.addrs) + await switch.connect(bootNode.peerInfo.peerId, bootNode.peerInfo.addrs) switches.add(switch) - # A discovery manager is a simple tool, you setup it by adding discovery + # A discovery manager is a simple tool, you set it up by adding discovery # interfaces (such as RendezVousInterface) then you can use it to advertise # something on the network or to request something from it. let dm = DiscoveryManager() # A RendezVousInterface is a RendezVous protocol wrapped to be usable by the # DiscoveryManager. You can initialize a time to advertise/request (tta/ttr) # for advertise/request every x seconds. - dm.add(RendezVousInterface.new(rdv, ttr = 250.milliseconds)) + dm.add(RendezVousInterface.new(rdv)) + # Each nodes of the network'll advertise on some topics (even gang or odd club) + if i mod 2 == 0: + dm.advertise(RdvNamespace("Even Gang")) + else: + dm.advertise(RdvNamespace("Odd Club")) discManagers.add(dm) - # The first node of the network advertise on someTopic - discManagers[0].advertise(RdvNamespace("someTopic")) - for i in 1..4: - # All the four "middle" nodes request informations on someTopic - # to make the message spread. With a time to request of 250ms, it should - # spread on the four nodes in a little over 1 second. - discard discManagers[i].request(RdvNamespace("someTopic")) let - # The ending node request on someTopic aswell and await for information. - query = discManagers[5].request(RdvNamespace("someTopic")) - # getPeer give you a PeerAttribute containing informations about the peer. - # The most useful are the PeerId and the MultiAddresses - res = await query.getPeer() + rdv = RendezVous.new() + newcomer = createSwitch(rdv) + dm = DiscoveryManager() + await newcomer.start() + await newcomer.connect(bootNode.peerInfo.peerId, bootNode.peerInfo.addrs) + dm.add(RendezVousInterface.new(rdv, ttr = 250.milliseconds)) - doAssert(res[PeerId] == switches[0].peerInfo.peerId) - let resMa = res.getAll(MultiAddress) - for ma in resMa: - doAssert(ma in switches[0].peerInfo.addrs) - echo "Peer Found: ", res[PeerId], " ", resMa - # The peer Id and the address of the node0 is discovered by the node5 after - # being pass by all the intermediate node. + let query = dm.request(RdvNamespace("Odd Club")) + for _ in 0..2: + echo "start" + let + res = await query.getPeer() + echo "0 ", res[PeerId], " ", res.getAll(MultiAddress) + let + conn = await newcomer.dial(res[PeerId], res.getAll(MultiAddress), DumbCodec) + echo "1" + await conn.writeLp("Odd Club suuuucks! Even Gang is better!") + echo "2" + await sleepAsync(300.milliseconds) + await conn.close() + echo "stop" + query.stop() - # The node5 can connect the node0 to talk about someTopic directly. - # Stop all the DiscoveryManagers - for dm in discManagers: dm.stop() - # Stop all the switches - let futStop = switches.mapIt(it.stop()) - await allFutures(futStop) +# # Create a "network" of six nodes looking like that: +# # node0 <-> node1 <-> node2 <-> node3 <-> node4 <-> node5 +# var +# switches: seq[Switch] = @[] +# discManagers: seq[DiscoveryManager] = @[] +# for _ in 0..5: +# # Create a RendezVous Protocol +# let rdv = RendezVous.new() +# # Create a switch, mount the RendezVous protocol to it, start it and +# # enventually connect it to the previous node in the sequence +# let switch = createSwitch(rdv) +# if switches.len == 0: +# +# await switch.start() +# if switches.len > 0: +# await switches[^1].connect(switch.peerInfo.peerId, switch.peerInfo.addrs) +# switches.add(switch) +# +# # A discovery manager is a simple tool, you setup it by adding discovery +# # interfaces (such as RendezVousInterface) then you can use it to advertise +# # something on the network or to request something from it. +# let dm = DiscoveryManager() +# # A RendezVousInterface is a RendezVous protocol wrapped to be usable by the +# # DiscoveryManager. You can initialize a time to advertise/request (tta/ttr) +# # for advertise/request every x seconds. +# dm.add(RendezVousInterface.new(rdv, ttr = 250.milliseconds)) +# discManagers.add(dm) +# +# # The first node of the network advertise on someTopic +# discManagers[0].advertise(RdvNamespace("someTopic")) +# for i in 1..4: +# # All the four "middle" nodes request informations on someTopic +# # to make the message spread. With a time to request of 250ms, it should +# # spread on the four nodes in a little over 1 second. +# discard discManagers[i].request(RdvNamespace("someTopic")) +# let +# # The ending node request on someTopic aswell and await for information. +# query = discManagers[5].request(RdvNamespace("someTopic")) +# # getPeer give you a PeerAttribute containing informations about the peer. +# # The most useful are the PeerId and the MultiAddresses +# res = await query.getPeer() +# +# # Now that a peer have been found, we can stop the query +# query.stop() +# +# doAssert(res[PeerId] == switches[0].peerInfo.peerId) +# let resMa = res.getAll(MultiAddress) +# for ma in resMa: +# doAssert(ma in switches[0].peerInfo.addrs) +# echo "Peer Found: ", res[PeerId], " ", resMa +# # The peer Id and the address of the node0 is discovered by the node5 after +# # being pass by all the intermediate node. +# +# # The node5 can connect the node0 to talk about someTopic directly. +# +# # Stop all the DiscoveryManagers +# for dm in discManagers: dm.stop() +# +# # Stop all the switches +# let futStop = switches.mapIt(it.stop()) +# await allFutures(futStop) waitFor(main()) diff --git a/libp2p.nimble b/libp2p.nimble index 44cfc698c..9613a578c 100644 --- a/libp2p.nimble +++ b/libp2p.nimble @@ -90,6 +90,7 @@ task website, "Build the website": tutorialToMd("examples/tutorial_1_connect.nim") tutorialToMd("examples/tutorial_2_customproto.nim") tutorialToMd("examples/tutorial_3_protobuf.nim") + tutorialToMd("examples/tutorial_5_discovery.nim") tutorialToMd("examples/circuitrelay.nim") exec "mkdocs build" diff --git a/libp2p/multistream.nim b/libp2p/multistream.nim index e2797a6a3..39bb24278 100644 --- a/libp2p/multistream.nim +++ b/libp2p/multistream.nim @@ -71,25 +71,36 @@ proc select*(m: MultistreamSelect, else: trace "multistream handshake success", conn + echo " 0 ", proto if proto.len() == 0: # no protocols, must be a handshake call return Codec else: + echo " 1" s = string.fromBytes(await conn.readLp(MsgSize)) # read the first proto + echo " 2" validateSuffix(s) + echo " 3" trace "reading first requested proto", conn if s == proto[0]: + echo " 4.1" trace "successfully selected ", conn, proto = proto[0] conn.protocol = proto[0] + echo " 4.2 ", proto[0] return proto[0] elif proto.len > 1: + echo " 5.1" # Try to negotiate alternatives let protos = proto[1.. 0: return (await m.select(conn, @[proto])) == proto else: diff --git a/libp2p/upgrademngrs/upgrade.nim b/libp2p/upgrademngrs/upgrade.nim index c5733e658..0dcf3bff8 100644 --- a/libp2p/upgrademngrs/upgrade.nim +++ b/libp2p/upgrademngrs/upgrade.nim @@ -76,7 +76,9 @@ proc identify*( conn: Connection) {.async, gcsafe.} = ## identify the connection + echo "==>0", self.identity.codec if (await self.ms.select(conn, self.identity.codec)): + echo "==>1" let info = await self.identity.identify(conn, conn.peerId) peerStore = self.connManager.peerStore