Make rln optional (dependencies and compilation) (#602)

* makes rlnlib target optional

* makes the rln-related targets conditioned to the RLN command line flag

* adds the rln compiler flag condition

* adds the rln compiler flag condition to the rln module

* wakunode2 rln conditional compilation

* updates wakunode2 test

* updates waku rln relay tests

* removing the rln test from the default imports of the v2 tests

* imports rln module conditionally

* removes the rln flag condition from the rln module

* separates rln data types from its procs

* adds the import statement

* brings back the contract def to the rln utils

* adds rln module import to the rln unit tests

* clean up and reorganization

* adds a todo

* minor edits on a comment
This commit is contained in:
Sanaz Taheri Boshrooyeh 2021-06-08 11:56:32 -07:00 committed by GitHub
parent e2185caf27
commit 2fe53c334c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 175 additions and 148 deletions

View File

@ -59,6 +59,10 @@ else
NIM_PARAMS := $(NIM_PARAMS) -d:release
endif
ifeq ($(RLN), true)
NIM_PARAMS := $(NIM_PARAMS) -d:rln
endif
deps: | deps-common nat-libs waku.nims rlnlib
ifneq ($(USE_LIBBACKTRACE), 0)
deps: | libbacktrace
@ -116,11 +120,15 @@ else
endif
installganache:
ifeq ($(RLN), true)
npm install ganache-cli; npx ganache-cli -p 8540 -g 0 -l 3000000000000&
endif
rlnlib:
ifeq ($(RLN), true)
cargo build --manifest-path vendor/rln/Cargo.toml
endif
test2: | build deps installganache
echo -e $(BUILD_MSG) "build/$@" && \
$(ENV_SCRIPT) nim test2 $(NIM_PARAMS) waku.nims

View File

@ -12,11 +12,14 @@ import
./v2/test_jsonrpc_waku,
./v2/test_peer_manager,
./v2/test_web3, # TODO remove it when rln-relay tests get finalized
./v2/test_waku_rln_relay,
./v2/test_waku_bridge,
./v2/test_peer_storage,
./v2/test_waku_keepalive
when defined(rln):
import ./v2/test_waku_rln_relay
# TODO Only enable this once swap module is integrated more nicely as a dependency, i.e. as submodule with CI etc
# For PoC execute it manually and run separate module here: https://github.com/vacp2p/swap-contracts-module
# ./v2/test_waku_swap_contracts

View File

@ -1,3 +1,4 @@
{.used.}
import
@ -5,11 +6,12 @@ import
testutils/unittests, chronos, chronicles, stint, web3,
stew/byteutils, stew/shims/net as stewNet,
libp2p/crypto/crypto,
../../waku/v2/protocol/waku_rln_relay/[rln, waku_rln_relay_utils],
../../waku/v2/protocol/waku_rln_relay/[rln, waku_rln_relay_utils, waku_rln_relay_types],
../../waku/v2/node/wakunode2,
../test_helpers,
./test_utils
# the address of Ethereum client (ganache-cli for now)
# TODO this address in hardcoded in the code, we may need to take it as input from the user
const EthClient = "ws://localhost:8540/"
@ -266,7 +268,7 @@ suite "Waku rln relay":
# the public and secret keys together are 64 bytes
generatedKeys.len == 64
debug "generated keys: ", generatedKeys
test "membership Key Gen":
# create an RLN instance
var
@ -284,7 +286,7 @@ suite "Waku rln relay":
key.get().publicKey != empty
debug "the generated membership key pair: ", key
test "get_root Nim binding":
# create an RLN instance which also includes an empty Merkle tree
var
@ -334,7 +336,7 @@ suite "Waku rln relay":
var member_is_added = update_next_member(ctxPtr, pkBufferPtr)
check:
member_is_added == true
test "delete_member Nim wrapper":
# create an RLN instance which also includes an empty Merkle tree
var
@ -346,7 +348,7 @@ suite "Waku rln relay":
var deleted_member_index = uint(0)
let deletion_success = delete_member(ctxPtr, deleted_member_index)
doAssert(deletion_success)
test "Merkle tree consistency check between deletion and insertion":
# create an RLN instance
var
@ -534,15 +536,12 @@ suite "Waku rln relay":
debug "shareY", shareY
debug "nullifier", nullifier
var f = 0.uint32
let verifyIsSuccessful = verify(ctxPtr, addr proof, addr f)
doAssert(verifyIsSuccessful)
# f = 0 means the proof is verified
doAssert(f == 0)
# create and test a bad proof
# prepare a bad authentication object with a wrong peer's index
var badIndex = 8

View File

@ -524,61 +524,62 @@ procSuite "WakuNode":
await node2.stop()
await node3.stop()
asyncTest "testing rln-relay with mocked zkp":
when defined(rln):
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))
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("/waku/2/default-content/proto")
payload = "hello world".toBytes()
message1 = WakuMessage(payload: payload, contentTopic: contentTopic1)
pubSubTopic = "defaultTopic"
contentTopic1 = ContentTopic("/waku/2/default-content/proto")
payload = "hello world".toBytes()
message1 = WakuMessage(payload: payload, contentTopic: contentTopic1)
# start all the nodes
await node1.start()
node1.mountRelay(@[pubSubTopic])
# start all the nodes
await node1.start()
node1.mountRelay(@[pubSubTopic])
await node2.start()
node2.mountRelay(@[pubSubTopic])
node2.addRLNRelayValidator(pubSubTopic)
await node2.start()
node2.mountRelay(@[pubSubTopic])
node2.addRLNRelayValidator(pubSubTopic)
await node3.start()
node3.mountRelay(@[pubSubTopic])
await node3.start()
node3.mountRelay(@[pubSubTopic])
await node1.connectToNodes(@[node2.peerInfo])
await node3.connectToNodes(@[node2.peerInfo])
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)
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)
node3.subscribe(pubSubTopic, relayHandler)
await sleepAsync(2000.millis)
await node1.publish(pubSubTopic, message1, rlnRelayEnabled = true)
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()
check:
(await completionFut.withTimeout(10.seconds)) == true
await node1.stop()
await node2.stop()
await node3.stop()
asyncTest "Relay protocol is started correctly":
let

View File

@ -16,8 +16,8 @@ import
../protocol/waku_store/waku_store,
../protocol/waku_swap/waku_swap,
../protocol/waku_filter/waku_filter,
../protocol/waku_rln_relay/[rln,waku_rln_relay_utils],
../protocol/waku_lightpush/waku_lightpush,
../protocol/waku_rln_relay/waku_rln_relay_types,
../protocol/waku_keepalive/waku_keepalive,
../utils/peers,
./storage/message/message_store,
@ -25,6 +25,9 @@ import
../utils/requests,
./peer_manager/peer_manager
when defined(rln):
import ../protocol/waku_rln_relay/[rln, waku_rln_relay_utils]
declarePublicCounter waku_node_messages, "number of messages received", ["type"]
declarePublicGauge waku_node_filters, "number of content filter subscriptions"
declarePublicGauge waku_node_errors, "number of wakunode errors", ["type"]
@ -304,13 +307,14 @@ proc publish*(node: WakuNode, topic: Topic, message: WakuMessage, rlnRelayEnabl
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)
when defined(rln):
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
@ -408,52 +412,54 @@ proc mountStore*(node: WakuNode, store: MessageStore = nil, persistMessages: boo
if persistMessages:
node.subscriptions.subscribe(WakuStoreCodec, node.wakuStore.subscription())
proc mountRlnRelay*(node: WakuNode, ethClientAddress: Option[string] = none(string), ethAccountAddress: Option[Address] = none(Address), membershipContractAddress: Option[Address] = none(Address)) {.async.} =
# TODO return a bool value to indicate the success of the call
# check whether inputs are provided
doAssert(ethClientAddress.isSome())
doAssert(ethAccountAddress.isSome())
doAssert(membershipContractAddress.isSome())
when defined(rln):
proc mountRlnRelay*(node: WakuNode, ethClientAddress: Option[string] = none(string), ethAccountAddress: Option[Address] = none(Address), membershipContractAddress: Option[Address] = none(Address)) {.async.} =
# TODO return a bool value to indicate the success of the call
# check whether inputs are provided
doAssert(ethClientAddress.isSome())
doAssert(ethAccountAddress.isSome())
doAssert(membershipContractAddress.isSome())
# create an RLN instance
var
ctx = RLN[Bn256]()
ctxPtr = addr(ctx)
doAssert(createRLNInstance(32, ctxPtr))
# create an RLN instance
var
ctx = RLN[Bn256]()
ctxPtr = addr(ctx)
doAssert(createRLNInstance(32, ctxPtr))
# generate the membership keys
let membershipKeyPair = membershipKeyGen(ctxPtr)
# check whether keys are generated
doAssert(membershipKeyPair.isSome())
debug "the membership key for the rln relay is generated"
# generate the membership keys
let membershipKeyPair = membershipKeyGen(ctxPtr)
# check whether keys are generated
doAssert(membershipKeyPair.isSome())
debug "the membership key for the rln relay is generated"
# initialize the WakuRLNRelay
var rlnPeer = WakuRLNRelay(membershipKeyPair: membershipKeyPair.get(),
ethClientAddress: ethClientAddress.get(),
ethAccountAddress: ethAccountAddress.get(),
membershipContractAddress: membershipContractAddress.get())
# register the rln-relay peer to the membership contract
let is_successful = await rlnPeer.register()
# check whether registration is done
doAssert(is_successful)
debug "peer is successfully registered into the membership contract"
# initialize the WakuRLNRelay
var rlnPeer = WakuRLNRelay(membershipKeyPair: membershipKeyPair.get(),
ethClientAddress: ethClientAddress.get(),
ethAccountAddress: ethAccountAddress.get(),
membershipContractAddress: membershipContractAddress.get())
# register the rln-relay peer to the membership contract
let is_successful = await rlnPeer.register()
# check whether registration is done
doAssert(is_successful)
debug "peer is successfully registered into the membership contract"
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)
when defined(rln):
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](),
@ -494,14 +500,14 @@ proc mountRelay*(node: WakuNode,
# Reconnect to previous relay peers. This will respect a backoff period, if necessary
waitFor node.peerManager.reconnectPeers(WakuRelayCodec,
wakuRelay.parameters.pruneBackoff + chronos.seconds(BackoffSlackTime))
if rlnRelayEnabled:
# 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"
when defined(rln):
if rlnRelayEnabled:
# 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"
if node.started:
# Node has already started. Start the WakuRelay protocol

View File

@ -1,7 +1,8 @@
# this module contains the Nim wrappers for the rln library https://github.com/kilic/rln/blob/3bbec368a4adc68cd5f9bfae80b17e1bbb4ef373/src/ffi.rs
import os
import
os,
waku_rln_relay_types
const libPath = "vendor/rln/target/debug/"
when defined(Windows):
@ -11,11 +12,9 @@ elif defined(Linux):
elif defined(MacOsX):
const libName* = libPath / "librln.dylib"
# all the following procedures are Nim wrappers for the functions defined in libName
# all the following procedures are Nim wrappers for the functions defined in libName
{.push dynlib: libName, raises: [Defect].}
type RLN*[E] {.incompleteStruct.} = object
type Bn256* = pointer
## Buffer struct is taken from
# https://github.com/celo-org/celo-threshold-bls-rs/blob/master/crates/threshold-bls-ffi/src/ffi.rs
@ -26,11 +25,11 @@ type Buffer* = object
type Auth* = object
secret_buffer*: ptr Buffer
index*: uint
#------------------------------ Merkle Tree operations -----------------------------------------
proc update_next_member*(ctx: ptr RLN[Bn256],
input_buffer: ptr Buffer): bool {.importc: "update_next_member".}
input_buffer: ptr Buffer): bool {.importc: "update_next_member".}
proc delete_member*(ctx: ptr RLN[Bn256], index: uint): bool {.importc: "delete_member".}
@ -41,13 +40,13 @@ proc get_root*(ctx: ptr RLN[Bn256], output_buffer: ptr Buffer): bool {.importc:
proc key_gen*(ctx: ptr RLN[Bn256], keypair_buffer: ptr Buffer): bool {.importc: "key_gen".}
proc generate_proof*(ctx: ptr RLN[Bn256],
input_buffer: ptr Buffer,
auth: ptr Auth,
output_buffer: ptr Buffer): bool {.importc: "generate_proof".}
input_buffer: ptr Buffer,
auth: ptr Auth,
output_buffer: ptr Buffer): bool {.importc: "generate_proof".}
proc verify*(ctx: ptr RLN[Bn256],
proof_buffer: ptr Buffer,
result_ptr: ptr uint32): bool {.importc: "verify".}
proof_buffer: ptr Buffer,
result_ptr: ptr uint32): bool {.importc: "verify".}
#----------------------------------------------------------------------------------------------
#-------------------------------- Common procedures -------------------------------------------
@ -56,7 +55,7 @@ proc new_circuit_from_params*(merkle_depth: uint,
ctx: ptr (ptr RLN[Bn256])): bool {.importc: "new_circuit_from_params".}
proc hash*(ctx: ptr RLN[Bn256],
inputs_buffer: ptr Buffer,
input_len: uint,
output_buffer: ptr Buffer): bool {.importc: "hash".}
inputs_buffer: ptr Buffer,
input_len: uint,
output_buffer: ptr Buffer): bool {.importc: "hash".}
{.pop.}

View File

@ -0,0 +1,32 @@
import
chronicles, options, chronos, stint,
web3,
eth/keys
## Bn256 and RLN are Nim wrappers for the data types used in
## the rln library https://github.com/kilic/rln/blob/3bbec368a4adc68cd5f9bfae80b17e1bbb4ef373/src/ffi.rs
type Bn256* = pointer
type RLN*[E] {.incompleteStruct.} = object
# Custom data types defined for waku rln relay -------------------------
type MembershipKeyPair* = object
secretKey*: array[32, byte]
publicKey*: array[32, byte]
type WakuRLNRelay* = object
membershipKeyPair*: MembershipKeyPair
ethClientAddress*: string
ethAccountAddress*: Address
# this field is required for signing transactions
# TODO may need to erase this ethAccountPrivateKey when is not used
# TODO may need to make ethAccountPrivateKey mandatory
ethAccountPrivateKey*: Option[PrivateKey]
membershipContractAddress*: Address
# inputs of the membership contract constructor
# TODO may be able to make these constants private and put them inside the waku_rln_relay_utils
const
MembershipFee* = 5.u256
Depth* = 32.u256
# TODO the EthClient should be an input to the rln-relay
EthClient* = "ws://localhost:8540/"

View File

@ -2,29 +2,8 @@ import
chronicles, options, chronos, stint,
web3,
stew/byteutils,
eth/keys,
rln
type MembershipKeyPair* = object
secretKey*: array[32, byte]
publicKey*: array[32, byte]
type WakuRLNRelay* = object
membershipKeyPair*: MembershipKeyPair
ethClientAddress*: string
ethAccountAddress*: Address
# this field is required for signing transactions
# TODO may need to erase this ethAccountPrivateKey when is not used
# TODO may need to make ethAccountPrivateKey mandatory
ethAccountPrivateKey*: Option[PrivateKey]
membershipContractAddress*: Address
# inputs of the membership contract constructor
const
MembershipFee* = 5.u256
Depth* = 32.u256
# TODO the EthClient should be an input to the rln-relay
EthClient* = "ws://localhost:8540/"
rln,
waku_rln_relay_types
# membership contract interface
contract(MembershipContract):