From f13adff4453a235797b76dfdf1258b19bd60cd60 Mon Sep 17 00:00:00 2001 From: Emil Ivanichkov Date: Thu, 8 Feb 2024 18:02:34 +0200 Subject: [PATCH] feat(waku examples): Add examples of pairing with `js-waku` instance --- libs/waku-utils/example/js-waku/initiator.nim | 206 ++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 libs/waku-utils/example/js-waku/initiator.nim diff --git a/libs/waku-utils/example/js-waku/initiator.nim b/libs/waku-utils/example/js-waku/initiator.nim new file mode 100644 index 0000000..be95e7c --- /dev/null +++ b/libs/waku-utils/example/js-waku/initiator.nim @@ -0,0 +1,206 @@ +import + std/[tables, times, sequtils], + stew/shims/net, + chronicles, + chronos, + confutils, + libp2p/crypto/crypto, + eth/[keys, p2p/discoveryv5/enr], + nimcrypto/utils + +import + waku/[waku_core, waku_node, waku_enr, waku_discv5], + waku/waku_noise/[noise_types, noise_utils, noise_handshake_processing], + waku/utils/noise, + waku/node/peer_manager, + waku/common/[logging, protobuf] + +import ../../waku_handshake_utils + +const bootstrapNode = "enr:-P-4QGVNANzbhCI49du6Moyw98AjuMhKoOpE_Jges9JlCq-I" & + "CAVadktjfcNpuhQgT0g1cu86_S3nbM7eYkCsqDAQG7UBgmlkgnY0" & + "gmlwhI_G-a6KbXVsdGlhZGRyc7hgAC02KG5vZGUtMDEuZG8tYW1z" & + "My5zdGF0dXMucHJvZC5zdGF0dXNpbS5uZXQGdl8ALzYobm9kZS0w" & + "MS5kby1hbXMzLnN0YXR1cy5wcm9kLnN0YXR1c2ltLm5ldAYBu94D" & + "iXNlY3AyNTZrMaECoVyonsTGEQvVioM562Q1fjzTb_vKD152PPId" & + "sV7sM6SDdGNwgnZfg3VkcIIjKIV3YWt1Mg8" + +const + wakuPort = 60000 + discv5Port = 9000 + requiredConnectedPeers = 2 + # Make sure it matches the publisher. Use default value + # see spec: https://rfc.vac.dev/spec/23/ + pubSubTopic = PubsubTopic("/waku/2/default-waku/proto") + +proc exampleJSWaku(rng: ref HmacDrbgContext) {.async.} = + setupLogLevel(logging.LogLevel.NOTICE) + + var readyForFinalization = false + + ################################ + # Initiator static/ephemeral key initialization and commitment + let initiatorInfo = initAgentKeysAndCommitment(rng) + + # Read the QR + let + qr = readFile("build/data/qr.txt") + (_, _, _, readEphemeralKey, _) = fromQr(qr) + qrMessageNameTag = fromHex(readFile("build/data/qrMessageNametag.txt")) + # We set the contentTopic from the content topic parameters exchanged in the QR + contentTopic = initContentTopicFromQr(qr) + + notice "Starting `nwaku`-`js-waku` pairing example", wakuPort = wakuPort, + discv5Port = discv5Port + + notice "Initial information parsed from the QR", contentTopic = contentTopic, + qrMessageNameTag = qrMessageNameTag + + var + initiatorHS = initHS(initiatorInfo, qr, true) + initiatorHSResult: HandshakeResult + + # Setup the Waku node + let + nodeKey = crypto.PrivateKey.random(Secp256k1, rng[]).get() + ip = parseIpAddress("0.0.0.0") + flags = CapabilitiesBitfield.init(lightpush = false, filter = false, + store = false, relay = true) + + var enrBuilder = EnrBuilder.init(nodeKey) + + let recordRes = enrBuilder.build() + let record = + if recordRes.isErr(): + error "failed to create enr record", error = recordRes.error + quit(QuitFailure) + else: recordRes.get() + + var builder = WakuNodeBuilder.init() + builder.withNodeKey(nodeKey) + builder.withRecord(record) + builder.withNetworkConfigurationDetails(ip, Port(wakuPort)).tryGet() + let node = builder.build().tryGet() + + var bootstrapNodeEnr: enr.Record + discard bootstrapNodeEnr.fromURI(bootstrapNode) + + let discv5Conf = WakuDiscoveryV5Config(discv5Config: none(DiscoveryConfig), + address: ip, port: Port(discv5Port), + privateKey: keys.PrivateKey(nodeKey.skkey), + bootstrapRecords: @[bootstrapNodeEnr], + autoupdateRecord: true) + + # assumes behind a firewall, so not care about being discoverable + let wakuDiscv5 = WakuDiscoveryV5.new(node.rng, discv5Conf, some(node.enr), + some(node.peerManager), + node.topicSubscriptionQueue) + + await node.start() + await node.mountRelay() + node.peerManager.start() + + (await wakuDiscv5.start()).isOkOr: + error "failed to start discv5", error = error + quit(1) + + # Wait for a minimum of peers to be connected, otherwise messages wont be gossiped + while true: + let numConnectedPeers = node.peerManager.peerStore[ + ConnectionBook].book.values().countIt(it == Connected) + if numConnectedPeers >= requiredConnectedPeers: + notice "Node is ready", connectedPeers = numConnectedPeers, + required = requiredConnectedPeers + break + notice "Waiting for the node to be ready", + connectedPeers = numConnectedPeers, required = requiredConnectedPeers + await sleepAsync(5000) + + # Perform the handshake + initiatorHSResult = await initiatorHandshake(rng, node, pubSubTopic, + contentTopic, qr, + qrMessageNameTag, + initiatorInfo) + await sleepAsync(1000) # Just in case there is some kind of delay on the other side + + + ## Scenario 1: Dump a lof of messages + var + payload: PayloadV2 + realMessage: seq[byte] + readMessage: seq[byte] + + var i = 150 + while i > 0: + realMessage = @[(byte)42,42,42,42] + payload = writeMessage(initiatorHSResult, realMessage, + initiatorHSResult.nametagsOutbound) + + let wakuMsg = encodePayloadV2( payload, contentTopic) + await node.publish(some(pubSubTopic), wakuMsg.get) + notice "Sending real message", payload=payload.messageNametag + await sleepAsync(100) + i = i - 1 + + await sleepAsync(5000) + + ## Scenario 2: Fake lost messages + let + message1 = @[(byte)1, 42, 42, 42] + payload1 = writeMessage(initiatorHSResult, message1, + initiatorHSResult.nametagsOutbound) + wakuMessage1 = encodePayloadV2(payload1, contentTopic) + notice "Sending first message" + await node.publish(some(pubSubTopic), wakuMessage1.get) + + let + lostMessage1 = @[(byte)1, 5, 5, 5] + payloadLost1 = writeMessage(initiatorHSResult, lostMessage1, + initiatorHSResult.nametagsOutbound) + wakuLostMessage1 = encodePayloadV2(payloadLost1, contentTopic) + + let + lostMessage2 = @[(byte)2, 5, 5, 5] + payloadLost2 = writeMessage(initiatorHSResult, lostMessage2, + initiatorHSResult.nametagsOutbound) + wakuLostMessage2 = encodePayloadV2(payloadLost2, contentTopic) + + let + message2 = @[(byte)2, 42, 42, 42] + payload2 = writeMessage(initiatorHSResult, message2, + initiatorHSResult.nametagsOutbound) + wakuMessage2 = encodePayloadV2(payload2, contentTopic) + notice "Sending second message" + await node.publish(some(pubSubTopic), wakuMessage2.get) + + let + lostMessage3 = @[(byte)3, 5, 5, 5] + payloadLost3 = writeMessage(initiatorHSResult, lostMessage3, + initiatorHSResult.nametagsOutbound) + wakuLostMessage3 = encodePayloadV2(payloadLost3, contentTopic) + + await sleepAsync(10000) + notice "Sending first lost message" + await node.publish(some(pubSubTopic), wakuLostMessage1.get) + + let + message3 = @[(byte)3, 42, 42, 42] + payload3 = writeMessage(initiatorHSResult, message3, + initiatorHSResult.nametagsOutbound) + wakuMessage3 = encodePayloadV2(payload3, contentTopic) + notice "Sending third message" + await node.publish(some(pubSubTopic), wakuMessage3.get) + + await sleepAsync(10000) + notice "Sending second lost message" + await node.publish(some(pubSubTopic), wakuLostMessage2.get) + + await sleepAsync(1000) + notice "Sending third lost message" + await node.publish(some(pubSubTopic), wakuLostMessage3.get) + + +when isMainModule: + let rng = crypto.newRng() + asyncSpawn exampleJSWaku(rng) + runForever()