diff --git a/tests/v2/test_wakunode.nim b/tests/v2/test_wakunode.nim index d12871f71..5b89e59dd 100644 --- a/tests/v2/test_wakunode.nim +++ b/tests/v2/test_wakunode.nim @@ -394,4 +394,58 @@ procSuite "WakuNode": await node1.stop() await node2.stop() await node3.stop() +asyncTest "testing rln-relay with mocked zkp": + + let + # publisher node + nodeKey1 = crypto.PrivateKey.random(Secp256k1, rng[])[] + node1 = WakuNode.init(nodeKey1, ValidIpAddress.init("0.0.0.0"), Port(60000)) + # Relay node + nodeKey2 = crypto.PrivateKey.random(Secp256k1, rng[])[] + node2 = WakuNode.init(nodeKey2, ValidIpAddress.init("0.0.0.0"), Port(60002)) + # Subscriber + nodeKey3 = crypto.PrivateKey.random(Secp256k1, rng[])[] + node3 = WakuNode.init(nodeKey3, ValidIpAddress.init("0.0.0.0"), Port(60003)) + pubSubTopic = "defaultTopic" + contentTopic1 = ContentTopic(1) + payload = "hello world".toBytes() + message1 = WakuMessage(payload: payload, contentTopic: contentTopic1) + + # start all the nodes + await node1.start() + node1.mountRelay(@[pubSubTopic]) + + await node2.start() + node2.mountRelay(@[pubSubTopic]) + node2.addRLNRelayValidator(pubSubTopic) + + await node3.start() + node3.mountRelay(@[pubSubTopic]) + + await node1.connectToNodes(@[node2.peerInfo]) + await node3.connectToNodes(@[node2.peerInfo]) + + var completionFut = newFuture[bool]() + proc relayHandler(topic: string, data: seq[byte]) {.async, gcsafe.} = + let msg = WakuMessage.init(data) + if msg.isOk(): + let val = msg.value() + debug "The received topic:", topic + if topic == pubSubTopic: + completionFut.complete(true) + + + node3.subscribe(pubSubTopic, relayHandler) + await sleepAsync(2000.millis) + + await node1.publish(pubSubTopic, message1, rlnRelayEnabled = true) + await sleepAsync(2000.millis) + + + check: + (await completionFut.withTimeout(10.seconds)) == true + + await node1.stop() + await node2.stop() + await node3.stop() diff --git a/waku/v2/node/wakunode2.nim b/waku/v2/node/wakunode2.nim index e29568e78..626fc2c2c 100644 --- a/waku/v2/node/wakunode2.nim +++ b/waku/v2/node/wakunode2.nim @@ -8,9 +8,10 @@ import libp2p/crypto/crypto, libp2p/protocols/protocol, # NOTE For TopicHandler, solve with exports? + libp2p/protocols/pubsub/rpc/messages, libp2p/protocols/pubsub/pubsub, libp2p/standard_setup, - ../protocol/[waku_relay, message_notifier], + ../protocol/[waku_relay, waku_message, message_notifier], ../protocol/waku_store/waku_store, ../protocol/waku_swap/waku_swap, ../protocol/waku_filter/waku_filter, @@ -239,17 +240,26 @@ proc unsubscribe*(node: WakuNode, request: FilterRequest) {.async, gcsafe.} = waku_node_filters.set(node.filters.len.int64) -proc publish*(node: WakuNode, topic: Topic, message: WakuMessage) {.async, gcsafe.} = +proc publish*(node: WakuNode, topic: Topic, message: WakuMessage, rlnRelayEnabled: bool = false) {.async, gcsafe.} = ## Publish a `WakuMessage` to a PubSub topic. `WakuMessage` should contain a ## `contentTopic` field for light node functionality. This field may be also ## be omitted. ## ## Status: Implemented. - ## + ## When rlnRelayEnabled is true, a zkp will be generated and attached to the message (it is an experimental feature) let wakuRelay = node.wakuRelay - debug "publish", topic=topic, contentTopic=message.contentTopic + var publishingMessage = message + + if rlnRelayEnabled: + # if rln relay is enabled then a proof must be generated and added to the waku message + let + proof = proofGen(message.payload) + ## TODO here since the message is immutable we have to make a copy of it and then attach the proof to its duplicate + ## TODO however, it might be better to change message type to mutable (i.e., var) so that we can add the proof field to the original message + publishingMessage = WakuMessage(payload: message.payload, contentTopic: message.contentTopic, version: message.version, proof: proof) + let data = message.encode().buffer discard await wakuRelay.publish(topic, data) @@ -343,6 +353,20 @@ proc mountRlnRelay*(node: WakuNode, ethClientAddress: Option[string] = none(stri node.wakuRlnRelay = rlnPeer +proc addRLNRelayValidator*(node: WakuNode, pubsubTopic: string) = + ## this procedure is a thin wrapper for the pubsub addValidator method + ## it sets message validator on the given pubsubTopic, the validator will check that + ## all the messages published in the pubsubTopic have a valid zero-knowledge proof + proc validator(topic: string, message: messages.Message): Future[ValidationResult] {.async.} = + let msg = WakuMessage.init(message.data) + if msg.isOk(): + # check the proof + if proofVrfy(msg.value().payload, msg.value().proof): + result = ValidationResult.Accept + # set a validator for the pubsubTopic + let pb = PubSub(node.wakuRelay) + pb.addValidator(pubsubTopic, validator) + proc mountRelay*(node: WakuNode, topics: seq[string] = newSeq[string](), rlnRelayEnabled = false) {.gcsafe.} = let wakuRelay = WakuRelay.init( switch = node.switch, @@ -376,8 +400,11 @@ proc mountRelay*(node: WakuNode, topics: seq[string] = newSeq[string](), rlnRela # TODO pass rln relay inputs to this proc, right now it uses default values that are set in the mountRlnRelay proc info "WakuRLNRelay is enabled" waitFor mountRlnRelay(node) + # TODO currently the message validator is set for the defaultTopic, this can be configurable to accept other pubsub topics as well + addRLNRelayValidator(node, defaultTopic) info "WakuRLNRelay is mounted successfully" + ## Helpers proc dialPeer*(n: WakuNode, address: string) {.async.} = info "dialPeer", address = address diff --git a/waku/v2/protocol/waku_message.nim b/waku/v2/protocol/waku_message.nim index 3a2f679bc..324aedc1a 100644 --- a/waku/v2/protocol/waku_message.nim +++ b/waku/v2/protocol/waku_message.nim @@ -16,6 +16,9 @@ type payload*: seq[byte] contentTopic*: ContentTopic version*: uint32 + # the proof field indicates that the message is not a spam + # this field will be used in the rln-relay protocol + proof*: seq[byte] # Encoding and decoding ------------------------------------------------------- proc init*(T: type WakuMessage, buffer: seq[byte]): ProtoResult[T] = @@ -25,6 +28,7 @@ proc init*(T: type WakuMessage, buffer: seq[byte]): ProtoResult[T] = discard ? pb.getField(1, msg.payload) discard ? pb.getField(2, msg.contentTopic) discard ? pb.getField(3, msg.version) + discard ? pb.getField(4, msg.proof) ok(msg) @@ -34,3 +38,4 @@ proc encode*(message: WakuMessage): ProtoBuffer = result.write(1, message.payload) result.write(2, message.contentTopic) result.write(3, message.version) + result.write(4, message.proof) diff --git a/waku/v2/protocol/waku_rln_relay/rln.nim b/waku/v2/protocol/waku_rln_relay/rln.nim index d80a68956..0458a0a51 100644 --- a/waku/v2/protocol/waku_rln_relay/rln.nim +++ b/waku/v2/protocol/waku_rln_relay/rln.nim @@ -66,4 +66,6 @@ proc hash*(ctx: ptr RLNBn256, inputs_buffer:ptr Buffer, input_len: ptr csize_t, # output_buffer: *mut Buffer, # ) -> bool -{.pop.} \ No newline at end of file +{.pop.} + + diff --git a/waku/v2/protocol/waku_rln_relay/waku_rln_relay_utils.nim b/waku/v2/protocol/waku_rln_relay/waku_rln_relay_utils.nim index 7cac4bc2d..98e9c673a 100644 --- a/waku/v2/protocol/waku_rln_relay/waku_rln_relay_utils.nim +++ b/waku/v2/protocol/waku_rln_relay/waku_rln_relay_utils.nim @@ -1,5 +1,5 @@ import - chronicles, options, chronos, stint, + chronicles, options, chronos, stint, sequtils, web3, stew/byteutils, eth/keys, @@ -98,4 +98,12 @@ proc register*(rlnPeer: WakuRLNRelay): Future[bool] {.async.} = discard await sender.register(pk).send(MembershipFee) # TODO check the receipt and then return true/false await web3.close() - return true \ No newline at end of file + return true + +proc proofGen*(data: seq[byte]): seq[byte] = + # TODO to implement the actual proof generation logic + return "proof".toBytes() + +proc proofVrfy*(data, proof: seq[byte]): bool = + # TODO to implement the actual proof verification logic + return true \ No newline at end of file