Basic proof gen and vrfy (#421)

* adds the proof field to the WakuMessage

* adds a basic zkProof api

* adds proof gen to the publish proc

* relocates the proofGen and proofVrfy to rln_relay_utils

* wip: test of proof gen

* adds a procedure to pass rln-relay message validator, adds the proof gen

* tests the proof gen and verify

* relocates zkp API

* adds documentation and todos

* adds todos and documentations

* removes an unnecessary comment

* adds todos
This commit is contained in:
Sanaz Taheri Boshrooyeh 2021-03-16 11:18:40 -07:00 committed by GitHub
parent b3208b343f
commit d64aadc8ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 103 additions and 7 deletions

View File

@ -394,4 +394,58 @@ procSuite "WakuNode":
await node1.stop() await node1.stop()
await node2.stop() await node2.stop()
await node3.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()

View File

@ -8,9 +8,10 @@ import
libp2p/crypto/crypto, libp2p/crypto/crypto,
libp2p/protocols/protocol, libp2p/protocols/protocol,
# NOTE For TopicHandler, solve with exports? # NOTE For TopicHandler, solve with exports?
libp2p/protocols/pubsub/rpc/messages,
libp2p/protocols/pubsub/pubsub, libp2p/protocols/pubsub/pubsub,
libp2p/standard_setup, libp2p/standard_setup,
../protocol/[waku_relay, message_notifier], ../protocol/[waku_relay, waku_message, message_notifier],
../protocol/waku_store/waku_store, ../protocol/waku_store/waku_store,
../protocol/waku_swap/waku_swap, ../protocol/waku_swap/waku_swap,
../protocol/waku_filter/waku_filter, ../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) 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 ## Publish a `WakuMessage` to a PubSub topic. `WakuMessage` should contain a
## `contentTopic` field for light node functionality. This field may be also ## `contentTopic` field for light node functionality. This field may be also
## be omitted. ## be omitted.
## ##
## Status: Implemented. ## 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 let wakuRelay = node.wakuRelay
debug "publish", topic=topic, contentTopic=message.contentTopic 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 let data = message.encode().buffer
discard await wakuRelay.publish(topic, data) discard await wakuRelay.publish(topic, data)
@ -343,6 +353,20 @@ proc mountRlnRelay*(node: WakuNode, ethClientAddress: Option[string] = none(stri
node.wakuRlnRelay = rlnPeer 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.} = proc mountRelay*(node: WakuNode, topics: seq[string] = newSeq[string](), rlnRelayEnabled = false) {.gcsafe.} =
let wakuRelay = WakuRelay.init( let wakuRelay = WakuRelay.init(
switch = node.switch, 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 # 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" info "WakuRLNRelay is enabled"
waitFor mountRlnRelay(node) 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" info "WakuRLNRelay is mounted successfully"
## Helpers ## Helpers
proc dialPeer*(n: WakuNode, address: string) {.async.} = proc dialPeer*(n: WakuNode, address: string) {.async.} =
info "dialPeer", address = address info "dialPeer", address = address

View File

@ -16,6 +16,9 @@ type
payload*: seq[byte] payload*: seq[byte]
contentTopic*: ContentTopic contentTopic*: ContentTopic
version*: uint32 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 ------------------------------------------------------- # Encoding and decoding -------------------------------------------------------
proc init*(T: type WakuMessage, buffer: seq[byte]): ProtoResult[T] = 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(1, msg.payload)
discard ? pb.getField(2, msg.contentTopic) discard ? pb.getField(2, msg.contentTopic)
discard ? pb.getField(3, msg.version) discard ? pb.getField(3, msg.version)
discard ? pb.getField(4, msg.proof)
ok(msg) ok(msg)
@ -34,3 +38,4 @@ proc encode*(message: WakuMessage): ProtoBuffer =
result.write(1, message.payload) result.write(1, message.payload)
result.write(2, message.contentTopic) result.write(2, message.contentTopic)
result.write(3, message.version) result.write(3, message.version)
result.write(4, message.proof)

View File

@ -66,4 +66,6 @@ proc hash*(ctx: ptr RLNBn256, inputs_buffer:ptr Buffer, input_len: ptr csize_t,
# output_buffer: *mut Buffer, # output_buffer: *mut Buffer,
# ) -> bool # ) -> bool
{.pop.} {.pop.}

View File

@ -1,5 +1,5 @@
import import
chronicles, options, chronos, stint, chronicles, options, chronos, stint, sequtils,
web3, web3,
stew/byteutils, stew/byteutils,
eth/keys, eth/keys,
@ -98,4 +98,12 @@ proc register*(rlnPeer: WakuRLNRelay): Future[bool] {.async.} =
discard await sender.register(pk).send(MembershipFee) discard await sender.register(pk).send(MembershipFee)
# TODO check the receipt and then return true/false # TODO check the receipt and then return true/false
await web3.close() await web3.close()
return true 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