diff --git a/examples/v2/basic2.nim b/examples/v2/basic2.nim index 6c225be42..607f9f465 100644 --- a/examples/v2/basic2.nim +++ b/examples/v2/basic2.nim @@ -23,7 +23,7 @@ proc runBackground() {.async.} = Port(uint16(conf.tcpPort) + conf.portsShift), extIp, extTcpPort) await node.start() - node.mountRelay(rlnRelayEnabled = conf.rlnrelay) + node.mountRelay() # Subscribe to a topic let topic = cast[Topic]("foobar") diff --git a/examples/v2/chat2.nim b/examples/v2/chat2.nim index 043b19754..09b7a4596 100644 --- a/examples/v2/chat2.nim +++ b/examples/v2/chat2.nim @@ -326,7 +326,6 @@ proc processInput(rfd: AsyncFD, rng: ref BrHmacDrbgContext) {.async.} = await node.start() node.mountRelay(conf.topics.split(" "), - rlnRelayEnabled = conf.rlnRelay, relayMessages = conf.relay) # Indicates if node is capable to relay messages node.mountLibp2pPing() diff --git a/tests/v2/test_waku_rln_relay.nim b/tests/v2/test_waku_rln_relay.nim index 58f05985f..ced25033a 100644 --- a/tests/v2/test_waku_rln_relay.nim +++ b/tests/v2/test_waku_rln_relay.nim @@ -11,6 +11,8 @@ import ../test_helpers, ./test_utils +const RLNRELAY_PUBSUB_TOPIC = "waku/2/rlnrelay/proto" + # POSEIDON_HASHER_CODE holds the bytecode of Poseidon hasher solidity smart contract: # https://github.com/kilic/rlnapp/blob/master/packages/contracts/contracts/crypto/PoseidonHasher.sol # the solidity contract is compiled separately and the resultant bytecode is copied here @@ -244,7 +246,8 @@ procSuite "Waku rln relay": debug "expected root ", expectedRoot # start rln-relay - await node.mountRlnRelay(ethClientAddrOpt = some(EthClient), ethAccAddrOpt = some(ethAccountAddress), memContractAddOpt = some(membershipContractAddress), groupOpt = some(group), memKeyPairOpt = some(keypair.get()), memIndexOpt = some(index)) + node.mountRelay() + await node.mountRlnRelay(ethClientAddrOpt = some(EthClient), ethAccAddrOpt = some(ethAccountAddress), memContractAddOpt = some(membershipContractAddress), groupOpt = some(group), memKeyPairOpt = some(keypair.get()), memIndexOpt = some(index), pubsubTopic = RLNRELAY_PUBSUB_TOPIC) let calculatedRoot = node.wakuRlnRelay.rlnInstance.getMerkleRoot().value().toHex debug "calculated root ", calculatedRoot @@ -274,10 +277,11 @@ procSuite "Waku rln relay": # index indicates the position of a membership key pair in the static list of group keys i.e., groupKeyPairs # the corresponding key pair will be used to mount rlnRelay on the current node # index also represents the index of the leaf in the Merkle tree that contains node's commitment key - let index = MembeshipIndex(5) + let index = MembershipIndex(5) # -------- mount rln-relay in the off-chain mode - await node.mountRlnRelay(groupOpt = some(groupIDCommitments), memKeyPairOpt = some(groupKeyPairs[index]), memIndexOpt = some(index), onchainMode = false) + node.mountRelay() + await node.mountRlnRelay(groupOpt = some(groupIDCommitments), memKeyPairOpt = some(groupKeyPairs[index]), memIndexOpt = some(index), onchainMode = false, pubsubTopic = RLNRELAY_PUBSUB_TOPIC) # get the root of Merkle tree which is constructed inside the mountRlnRelay proc let calculatedRoot = node.wakuRlnRelay.rlnInstance.getMerkleRoot().value().toHex @@ -420,7 +424,7 @@ suite "Waku rln relay": rlnInstance.isOk == true # delete the first member - var deleted_member_index = MembeshipIndex(0) + var deleted_member_index = MembershipIndex(0) let deletion_success = delete_member(rlnInstance.value, deleted_member_index) doAssert(deletion_success) @@ -443,7 +447,7 @@ suite "Waku rln relay": rlnInstance.isOk == true var rln = rlnInstance.value check: - rln.removeMember(MembeshipIndex(0)) + rln.removeMember(MembershipIndex(0)) test "Merkle tree consistency check between deletion and insertion": # create an RLN instance @@ -478,7 +482,7 @@ suite "Waku rln relay": doAssert(root2.len == 32) # delete the first member - var deleted_member_index = MembeshipIndex(0) + var deleted_member_index = MembershipIndex(0) let deletion_success = delete_member(rlnInstance.value, deleted_member_index) doAssert(deletion_success) @@ -533,7 +537,7 @@ suite "Waku rln relay": # delete the first member - var deleted_member_index = MembeshipIndex(0) + var deleted_member_index = MembershipIndex(0) let deletion_success = rln.removeMember(deleted_member_index) doAssert(deletion_success) @@ -603,7 +607,7 @@ suite "Waku rln relay": var index = 5 # prepare the authentication object with peer's index and sk - var authObj: Auth = Auth(secret_buffer: addr skBuffer, index: MembeshipIndex(index)) + var authObj: Auth = Auth(secret_buffer: addr skBuffer, index: MembershipIndex(index)) # Create a Merkle tree with random members for i in 0..10: @@ -685,7 +689,7 @@ suite "Waku rln relay": # create and test a bad proof # prepare a bad authentication object with a wrong peer's index var badIndex = 8 - var badAuthObj: Auth = Auth(secret_buffer: addr skBuffer, index: MembeshipIndex(badIndex)) + var badAuthObj: Auth = Auth(secret_buffer: addr skBuffer, index: MembershipIndex(badIndex)) var badProof: Buffer let badProofIsSuccessful = generate_proof(rlnInstance.value, addr inputBuffer, addr badAuthObj, addr badProof) # check whether the generate_proof call is done successfully diff --git a/vendor/nim-libbacktrace/vendor/libbacktrace-upstream/libtool b/vendor/nim-libbacktrace/vendor/libbacktrace-upstream/libtool index 4ea06487e..436c47759 100755 --- a/vendor/nim-libbacktrace/vendor/libbacktrace-upstream/libtool +++ b/vendor/nim-libbacktrace/vendor/libbacktrace-upstream/libtool @@ -2,7 +2,7 @@ # libtool - Provide generalized library-building support services. # Generated automatically by config.status (libbacktrace) version-unused -# Libtool was configured on host fv-az129-673: +# Libtool was configured on host fv-az193-28: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, diff --git a/waku/v2/node/config.nim b/waku/v2/node/config.nim index ace69f9ff..4d0f31bde 100644 --- a/waku/v2/node/config.nim +++ b/waku/v2/node/config.nim @@ -5,7 +5,8 @@ import libp2p/crypto/crypto, libp2p/crypto/secp, nimcrypto/utils, - eth/keys + eth/keys, + ../protocol/waku_rln_relay/[waku_rln_relay_types] type WakuNodeConf* = object @@ -76,10 +77,14 @@ type name: "rln-relay" }: bool rlnRelayMemIndex* {. - desc: "(experimental) the index of node in the rln-relay group: a value between 0-49 inclusive", - defaultValue: 0 - name: "rln-relay-membership-index" }: uint32 - + desc: "(experimental) the index of node in the rln-relay group: a value between 0-99 inclusive", + defaultValue: MembershipIndex(0) + name: "rln-relay-membership-index" }: MembershipIndex + + rlnRelayPubsubTopic* {. + desc: "the pubsub topic for which rln-relay gets enabled", + defaultValue: "waku/2/rlnrelay/proto" + name: "rln-relay-pubsub-topic" }: string staticnodes* {. desc: "Peer multiaddr to directly connect with. Argument may be repeated." name: "staticnode" }: seq[string] diff --git a/waku/v2/node/wakunode2.nim b/waku/v2/node/wakunode2.nim index 96c3acc13..3a765c437 100644 --- a/waku/v2/node/wakunode2.nim +++ b/waku/v2/node/wakunode2.nim @@ -408,18 +408,42 @@ proc mountStore*(node: WakuNode, store: MessageStore = nil, persistMessages: boo node.wakuStore = WakuStore.init(node.peerManager, node.rng, store, node.wakuSwap, persistMessages=persistMessages) node.switch.mount(node.wakuStore, protocolMatcher(WakuStoreCodec)) - + 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): + return ValidationResult.Accept + # set a validator for the pubsubTopic + let pb = PubSub(node.wakuRelay) + pb.addValidator(pubsubTopic, validator) + proc mountRlnRelay*(node: WakuNode, ethClientAddrOpt: Option[string] = none(string), ethAccAddrOpt: Option[Address] = none(Address), memContractAddOpt: Option[Address] = none(Address), groupOpt: Option[seq[IDCommitment]] = none(seq[IDCommitment]), memKeyPairOpt: Option[MembershipKeyPair] = none(MembershipKeyPair), - memIndexOpt: Option[uint] = none(uint), - onchainMode: bool = true) {.async.} = + memIndexOpt: Option[MembershipIndex] = none(MembershipIndex), + onchainMode: bool = true, + pubsubTopic: string) {.async.} = # TODO return a bool value to indicate the success of the call # check whether inputs are provided + + # relay protocol is the prerequisite of rln-relay + if node.wakuRelay.isNil: + error "Failed to mount WakuRLNRelay. Relay protocol is not mounted." + return + # check whether the pubsub topic is supported at the relay level + if pubsubTopic notin node.wakuRelay.defaultTopics: + error "Failed to mount WakuRLNRelay. The relay protocol does not support the configured pubsub topic.", pubsubTopic=pubsubTopic + return if onchainMode: if memContractAddOpt.isNone(): error "failed to mount rln relay: membership contract address is not provided" @@ -495,23 +519,15 @@ when defined(rln): doAssert(is_successful) debug "peer is successfully registered into the membership contract" + # adds a topic validator for the supplied pubsub topic at the relay protocol + # messages published on this pubsub topic will be relayed upon a successful validation, otherwise they will be dropped + # the topic validator checks for the correct non-spamming proof of the message + addRLNRelayValidator(node, pubsubTopic) + debug "rln relay topic validator is mounted successfully", pubsubTopic=pubsubTopic + 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): - return ValidationResult.Accept - # set a validator for the pubsubTopic - let pb = PubSub(node.wakuRelay) - pb.addValidator(pubsubTopic, validator) - proc startRelay*(node: WakuNode) {.async.} = if node.wakuRelay.isNil: trace "Failed to start relay. Not mounted." @@ -534,12 +550,6 @@ proc startRelay*(node: WakuNode) {.async.} = await node.peerManager.reconnectPeers(WakuRelayCodec, protocolMatcher(WakuRelayCodec), backoffPeriod) - - when defined(rln): - if node.wakuRelay.rlnRelayEnabled: - # 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" # Start the WakuRelay protocol await node.wakuRelay.start() @@ -548,8 +558,6 @@ proc startRelay*(node: WakuNode) {.async.} = proc mountRelay*(node: WakuNode, topics: seq[string] = newSeq[string](), - rlnRelayEnabled = false, - rlnRelayMemIndex = uint(0), relayMessages = true, triggerSelf = true) # @TODO: Better error handling: CatchableError is raised by `waitFor` @@ -564,12 +572,11 @@ proc mountRelay*(node: WakuNode, verifySignature = false ) - info "mounting relay", rlnRelayEnabled=rlnRelayEnabled, relayMessages=relayMessages + info "mounting relay", relayMessages=relayMessages ## The default relay topics is the union of ## all configured topics plus the hard-coded defaultTopic(s) wakuRelay.defaultTopics = concat(@[defaultTopic], topics) - wakuRelay.rlnRelayEnabled = rlnRelayEnabled node.switch.mount(wakuRelay, protocolMatcher(WakuRelayCodec)) @@ -581,44 +588,6 @@ proc mountRelay*(node: WakuNode, ## topics. We also never start the relay protocol. node.wakuRelay remains nil. ## @TODO: in future, this WakuRelay dependency will be removed completely node.wakuRelay = wakuRelay - - when defined(rln): - if rlnRelayEnabled: - # TODO get user inputs via cli options - info "WakuRLNRelay is enabled" - - # a static list of 50 membership keys in hexadecimal format - let - groupKeys = STATIC_GROUP_KEYS - groupSize = int(groupKeys.len/2) - - debug "rln-relay membership index", rlnRelayMemIndex - - # validate the user-supplied membership index - if rlnRelayMemIndex < uint(0) or rlnRelayMemIndex >= uint(groupSize): - error "wrong membership index, failed to mount WakuRLNRelay" - else: - # prepare group related inputs from the hardcoded keys - let - groupKeyPairs = groupKeys.toMembershipKeyPairs() - groupIDCommitments = groupKeyPairs.mapIt(it.idCommitment) - - # mount rlnrelay in offline mode - waitFor node.mountRlnRelay(groupOpt= some(groupIDCommitments), memKeyPairOpt = some(groupKeyPairs[rlnRelayMemIndex]), memIndexOpt= some(rlnRelayMemIndex), onchainMode = false) - - info "membership id key", idkey=groupKeyPairs[rlnRelayMemIndex].idKey.toHex - info "membership id commitment key", idCommitmentkey=groupIDCommitments[rlnRelayMemIndex].toHex - - # check the correct construction of the tree by comparing the calculated root against the expected root - # no error should happen as it is already captured in the unit tests - # TODO have added this check to account for unseen corner cases, will remove it later - let - root = node.wakuRlnRelay.rlnInstance.getMerkleRoot.value.toHex() - expectedRoot = STATIC_GROUP_MERKLE_ROOT - if root != expectedRoot: - error "root mismatch: something went wrong not in Merkle tree construction" - debug "the calculated root", root - info "WakuRLNRelay is mounted successfully" info "relay mounted successfully" @@ -885,13 +854,38 @@ when isMainModule: # Mount relay on all nodes mountRelay(node, conf.topics.split(" "), - rlnRelayEnabled = conf.rlnRelay, relayMessages = conf.relay, # Indicates if node is capable to relay messages - rlnRelayMemIndex = conf.rlnRelayMemIndex) + ) # Keepalive mounted on all nodes mountLibp2pPing(node) + when defined(rln): + if conf.rlnRelay: + info "WakuRLNRelay is enabled" + + # set up rln relay inputs + let (groupOpt, memKeyPairOpt, memIndexOpt) = rlnRelaySetUp(conf.rlnRelayMemIndex) + if memIndexOpt.isNone: + error "failed to mount WakuRLNRelay" + else: + # mount rlnrelay in offline mode (for now) + waitFor node.mountRlnRelay(groupOpt = groupOpt, memKeyPairOpt = memKeyPairOpt, memIndexOpt= memIndexOpt, onchainMode = false, pubsubTopic = conf.rlnRelayPubsubTopic) + + info "membership id key", idkey=memKeyPairOpt.get().idKey.toHex + info "membership id commitment key", idCommitmentkey=memKeyPairOpt.get().idCommitment.toHex + + # check the correct construction of the tree by comparing the calculated root against the expected root + # no error should happen as it is already captured in the unit tests + # TODO have added this check to account for unseen corner cases, will remove it later + let + root = node.wakuRlnRelay.rlnInstance.getMerkleRoot.value.toHex() + expectedRoot = STATIC_GROUP_MERKLE_ROOT + if root != expectedRoot: + error "root mismatch: something went wrong not in Merkle tree construction" + debug "the calculated root", root + info "WakuRLNRelay is mounted successfully", pubsubtopic=conf.rlnRelayPubsubTopic + if conf.swap: mountSwap(node) # TODO Set swap peer, for now should be same as store peer diff --git a/waku/v2/protocol/waku_relay.nim b/waku/v2/protocol/waku_relay.nim index 57abd3c82..39927263f 100644 --- a/waku/v2/protocol/waku_relay.nim +++ b/waku/v2/protocol/waku_relay.nim @@ -20,7 +20,6 @@ const type WakuRelay* = ref object of GossipSub defaultTopics*: seq[string] # Default configured PubSub topics - rlnRelayEnabled*: bool # Flag indicating if RLN relay is enabled method init*(w: WakuRelay) = debug "init WakuRelay" diff --git a/waku/v2/protocol/waku_rln_relay/waku_rln_relay_types.nim b/waku/v2/protocol/waku_rln_relay/waku_rln_relay_types.nim index 0e084657f..6e112e05c 100644 --- a/waku/v2/protocol/waku_rln_relay/waku_rln_relay_types.nim +++ b/waku/v2/protocol/waku_rln_relay/waku_rln_relay_types.nim @@ -37,7 +37,7 @@ type WakuRLNRelay* = object ethAccountPrivateKey*: Option[PrivateKey] rlnInstance*: RLN[Bn256] -type MembeshipIndex* = uint +type MembershipIndex* = uint # inputs of the membership contract constructor # TODO may be able to make these constants private and put them inside the waku_rln_relay_utils @@ -67,6 +67,5 @@ const # STATIC_GROUP_MERKLE_ROOT is the root of the Merkle tree constructed from the STATIC_GROUP_KEYS above # only identity commitments are used for the Merkle tree construction # the root is created locally, using createMembershipList proc from waku_rln_relay_utils module, and the result is hardcoded in here - STATIC_GROUP_MERKLE_ROOT* = "25e03b7d551c3016cb21c057a113be5bf27eaa9b2529e806715bc0a1ef221e07" - + STATIC_GROUP_MERKLE_ROOT* = "25e03b7d551c3016cb21c057a113be5bf27eaa9b2529e806715bc0a1ef221e07" 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 b54a0ca68..9da35e202 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,6 +1,7 @@ {.push raises: [Defect].} import + std/sequtils, chronicles, options, chronos, stint, web3, stew/results, @@ -114,7 +115,7 @@ proc insertMember*(rlnInstance: RLN[Bn256], idComm: IDCommitment): bool = var member_is_added = update_next_member(rlnInstance, pkBufferPtr) return member_is_added -proc removeMember*(rlnInstance: RLN[Bn256], index: MembeshipIndex): bool = +proc removeMember*(rlnInstance: RLN[Bn256], index: MembershipIndex): bool = let deletion_success = delete_member(rlnInstance, index) return deletion_success @@ -189,4 +190,30 @@ proc createMembershipList*(n: int): (seq[(string,string)], string) {.raises: [De let root = rln.getMerkleRoot().value.toHex - return (output, root) \ No newline at end of file + return (output, root) + +proc rlnRelaySetUp*(rlnRelayMemIndex: MembershipIndex): (Option[seq[IDCommitment]],Option[MembershipKeyPair], Option[MembershipIndex]) {.raises:[Defect, ValueError].} = + let + # static group + groupKeys = STATIC_GROUP_KEYS + groupSize = STATIC_GROUP_SIZE + + debug "rln-relay membership index", rlnRelayMemIndex + + # validate the user-supplied membership index + if rlnRelayMemIndex < MembershipIndex(0) or rlnRelayMemIndex >= MembershipIndex(groupSize): + error "wrong membership index" + return(none(seq[IDCommitment]), none(MembershipKeyPair), none(MembershipIndex)) + + # prepare the outputs from the static group keys + let + # create a sequence of MembershipKeyPairs from the group keys (group keys are in string format) + groupKeyPairs = groupKeys.toMembershipKeyPairs() + # extract id commitment keys + groupIDCommitments = groupKeyPairs.mapIt(it.idCommitment) + groupOpt= some(groupIDCommitments) + # user selected membership key pair + memKeyPairOpt = some(groupKeyPairs[rlnRelayMemIndex]) + memIndexOpt= some(rlnRelayMemIndex) + + return (groupOpt, memKeyPairOpt, memIndexOpt) \ No newline at end of file