diff --git a/Makefile b/Makefile index 564a81903..ee8f3ebb6 100644 --- a/Makefile +++ b/Makefile @@ -68,10 +68,34 @@ else NIM_PARAMS := $(NIM_PARAMS) -d:release endif +# control rln code compilation ifeq ($(RLN), true) +NIM_PARAMS := $(NIM_PARAMS) -d:rln +else ifeq ($(CI), true) NIM_PARAMS := $(NIM_PARAMS) -d:rln endif +# detecting the os +ifeq ($(OS),Windows_NT) # is Windows_NT on XP, 2000, 7, Vista, 10... + detected_OS := Windows +else ifeq ($(strip $(shell uname)),Darwin) + detected_OS := macOS +else + # e.g. Linux + detected_OS := $(strip $(shell uname)) +endif + +# control compilation of rln tests that require on chain interaction +ifeq ($(ONCHAIN_RLN), true) +NIM_PARAMS := $(NIM_PARAMS) -d:onchain_rln +else +ifeq ($(CI), true) +ifeq ($(detected_OS), macOS) +NIM_PARAMS := $(NIM_PARAMS) -d:onchain_rln +endif +endif +endif + deps: | deps-common nat-libs waku.nims rlnlib ifneq ($(USE_LIBBACKTRACE), 0) deps: | libbacktrace @@ -123,24 +147,23 @@ example2: | build deps echo -e $(BUILD_MSG) "build/$@" && \ $(ENV_SCRIPT) nim example2 $(NIM_PARAMS) waku.nims -# detecting the os -ifeq ($(OS),Windows_NT) # is Windows_NT on XP, 2000, 7, Vista, 10... - detected_OS := Windows -else ifeq ($(strip $(shell uname)),Darwin) - detected_OS := macOS -else - # e.g. Linux - detected_OS := $(strip $(shell uname)) -endif -installganache: -ifeq ($(RLN), true) +installganache: +ifeq ($(ONCHAIN_RLN), true) npm install ganache-cli; npx ganache-cli -p 8540 -g 0 -l 3000000000000& +else +ifeq ($(CI), true) +ifeq ($(detected_OS), macOS) + npm install ganache-cli; npx ganache-cli -p 8540 -g 0 -l 3000000000000& +endif +endif endif rlnlib: ifeq ($(RLN), true) cargo build --manifest-path vendor/rln/Cargo.toml +else ifeq ($(CI), true) + cargo build --manifest-path vendor/rln/Cargo.toml endif test2: | build deps installganache diff --git a/tests/v2/test_waku_rln_relay.nim b/tests/v2/test_waku_rln_relay.nim index 838105fb4..2b8655fa7 100644 --- a/tests/v2/test_waku_rln_relay.nim +++ b/tests/v2/test_waku_rln_relay.nim @@ -136,125 +136,127 @@ proc uploadContract(ethClientAddress: string): Future[Address] {.async.} = return contractAddress procSuite "Waku rln relay": - asyncTest "contract membership": - let contractAddress = await uploadContract(EthClient) - # connect to the eth client - let web3 = await newWeb3(EthClient) - debug "web3 connected to", EthClient + when defined(onchain_rln): + asyncTest "contract membership": + debug "ethereum client address", ETH_CLIENT + let contractAddress = await uploadContract(ETH_CLIENT) + # connect to the eth client + let web3 = await newWeb3(ETH_CLIENT) + debug "web3 connected to", ETH_CLIENT - # fetch the list of registered accounts - let accounts = await web3.provider.eth_accounts() - web3.defaultAccount = accounts[1] - let add = web3.defaultAccount - debug "contract deployer account address ", add + # fetch the list of registered accounts + let accounts = await web3.provider.eth_accounts() + web3.defaultAccount = accounts[1] + let add = web3.defaultAccount + debug "contract deployer account address ", add - # prepare a contract sender to interact with it - var sender = web3.contractSender(MembershipContract, contractAddress) # creates a Sender object with a web3 field and contract address of type Address + # prepare a contract sender to interact with it + var sender = web3.contractSender(MembershipContract, contractAddress) # creates a Sender object with a web3 field and contract address of type Address - # send takes three parameters, c: ContractCallBase, value = 0.u256, gas = 3000000'u64 gasPrice = 0 - # should use send proc for the contract functions that update the state of the contract - let tx = await sender.register(20.u256).send(value = MembershipFee) - debug "The hash of registration tx: ", tx # value is the membership fee + # send takes three parameters, c: ContractCallBase, value = 0.u256, gas = 3000000'u64 gasPrice = 0 + # should use send proc for the contract functions that update the state of the contract + let tx = await sender.register(20.u256).send(value = MembershipFee) + debug "The hash of registration tx: ", tx # value is the membership fee - # var members: array[2, uint256] = [20.u256, 21.u256] - # debug "This is the batch registration result ", await sender.registerBatch(members).send(value = (members.len * membershipFee)) # value is the membership fee + # var members: array[2, uint256] = [20.u256, 21.u256] + # debug "This is the batch registration result ", await sender.registerBatch(members).send(value = (members.len * membershipFee)) # value is the membership fee - # balance = await web3.provider.eth_getBalance(web3.defaultAccount , "latest") - # debug "Balance after registration: ", balance + # balance = await web3.provider.eth_getBalance(web3.defaultAccount , "latest") + # debug "Balance after registration: ", balance - await web3.close() - debug "disconnected from", EthClient + await web3.close() + debug "disconnected from", ETH_CLIENT - asyncTest "registration procedure": - # deploy the contract - let contractAddress = await uploadContract(EthClient) + asyncTest "registration procedure": + # deploy the contract + let contractAddress = await uploadContract(ETH_CLIENT) - # prepare rln-relay peer inputs - let - web3 = await newWeb3(EthClient) - accounts = await web3.provider.eth_accounts() - # choose one of the existing accounts for the rln-relay peer - ethAccountAddress = accounts[9] - await web3.close() + # prepare rln-relay peer inputs + let + web3 = await newWeb3(ETH_CLIENT) + accounts = await web3.provider.eth_accounts() + # choose one of the existing accounts for the rln-relay peer + ethAccountAddress = accounts[9] + await web3.close() - # create an RLN instance - var rlnInstance = createRLNInstance() - check: rlnInstance.isOk == true + # create an RLN instance + var rlnInstance = createRLNInstance() + check: rlnInstance.isOk == true - # generate the membership keys - let membershipKeyPair = membershipKeyGen(rlnInstance.value) - - check: membershipKeyPair.isSome + # generate the membership keys + let membershipKeyPair = membershipKeyGen(rlnInstance.value) + + check: membershipKeyPair.isSome - # initialize the WakuRLNRelay - var rlnPeer = WakuRLNRelay(membershipKeyPair: membershipKeyPair.get(), - membershipIndex: MembershipIndex(0), - ethClientAddress: EthClient, - ethAccountAddress: ethAccountAddress, - membershipContractAddress: contractAddress) - - # register the rln-relay peer to the membership contract - let is_successful = await rlnPeer.register() - check: - is_successful - asyncTest "mounting waku rln-relay": - let - nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[] - node = WakuNode.new(nodeKey, ValidIpAddress.init("0.0.0.0"), - Port(60000)) - await node.start() + # initialize the WakuRLNRelay + var rlnPeer = WakuRLNRelay(membershipKeyPair: membershipKeyPair.get(), + membershipIndex: MembershipIndex(0), + ethClientAddress: ETH_CLIENT, + ethAccountAddress: ethAccountAddress, + membershipContractAddress: contractAddress) + + # register the rln-relay peer to the membership contract + let is_successful = await rlnPeer.register() + check: + is_successful + asyncTest "mounting waku rln-relay": + let + nodeKey = crypto.PrivateKey.random(Secp256k1, rng[])[] + node = WakuNode.new(nodeKey, ValidIpAddress.init("0.0.0.0"), + Port(60000)) + await node.start() - # deploy the contract - let membershipContractAddress = await uploadContract(EthClient) + # deploy the contract + let membershipContractAddress = await uploadContract(ETH_CLIENT) - # prepare rln-relay inputs - let - web3 = await newWeb3(EthClient) - accounts = await web3.provider.eth_accounts() - # choose one of the existing account for the rln-relay peer - ethAccountAddress = accounts[9] - await web3.close() + # prepare rln-relay inputs + let + web3 = await newWeb3(ETH_CLIENT) + accounts = await web3.provider.eth_accounts() + # choose one of the existing account for the rln-relay peer + ethAccountAddress = accounts[9] + await web3.close() - # create current peer's pk - var rlnInstance = createRLNInstance() - check rlnInstance.isOk == true - var rln = rlnInstance.value - # generate a key pair - var keypair = rln.membershipKeyGen() - doAssert(keypair.isSome()) + # create current peer's pk + var rlnInstance = createRLNInstance() + check rlnInstance.isOk == true + var rln = rlnInstance.value + # generate a key pair + var keypair = rln.membershipKeyGen() + doAssert(keypair.isSome()) - # current peer index in the Merkle tree - let index = uint(5) + # current peer index in the Merkle tree + let index = uint(5) - # Create a group of 10 members - var group = newSeq[IDCommitment]() - for i in 0..10: - var member_is_added: bool = false - if (uint(i) == index): - # insert the current peer's pk - group.add(keypair.get().idCommitment) - member_is_added = rln.insertMember(keypair.get().idCommitment) - doAssert(member_is_added) - debug "member key", key=keypair.get().idCommitment.toHex - else: - var memberKeypair = rln.membershipKeyGen() - doAssert(memberKeypair.isSome()) - group.add(memberKeypair.get().idCommitment) - member_is_added = rln.insertMember(memberKeypair.get().idCommitment) - doAssert(member_is_added) - debug "member key", key=memberKeypair.get().idCommitment.toHex - let expectedRoot = rln.getMerkleRoot().value().toHex - debug "expected root ", expectedRoot + # Create a group of 10 members + var group = newSeq[IDCommitment]() + for i in 0..10: + var member_is_added: bool = false + if (uint(i) == index): + # insert the current peer's pk + group.add(keypair.get().idCommitment) + member_is_added = rln.insertMember(keypair.get().idCommitment) + doAssert(member_is_added) + debug "member key", key=keypair.get().idCommitment.toHex + else: + var memberKeypair = rln.membershipKeyGen() + doAssert(memberKeypair.isSome()) + group.add(memberKeypair.get().idCommitment) + member_is_added = rln.insertMember(memberKeypair.get().idCommitment) + doAssert(member_is_added) + debug "member key", key=memberKeypair.get().idCommitment.toHex + let expectedRoot = rln.getMerkleRoot().value().toHex + debug "expected root ", expectedRoot - # start rln-relay - node.mountRelay(@[RLNRELAY_PUBSUB_TOPIC]) - 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 + # start rln-relay + node.mountRelay(@[RLNRELAY_PUBSUB_TOPIC]) + await node.mountRlnRelay(ethClientAddrOpt = some(ETH_CLIENT), 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 - check expectedRoot == calculatedRoot + check expectedRoot == calculatedRoot - await node.stop() + await node.stop() asyncTest "mount waku-rln-relay in the off-chain mode": let @@ -885,13 +887,13 @@ suite "Waku rln relay": # validate messages # validateMessage proc checks the validity of the message fields and adds it to the log (if valid) let - msgValidate1 = wakuRlnRelay.validateMessage(wm1) + msgValidate1 = wakuRlnRelay.validateMessage(wm1, some(time)) # wm2 is published within the same Epoch as wm1 and should be found as spam - msgValidate2 = wakuRlnRelay.validateMessage(wm2) + msgValidate2 = wakuRlnRelay.validateMessage(wm2, some(time)) # a valid message should be validated successfully - msgValidate3 = wakuRlnRelay.validateMessage(wm3) + msgValidate3 = wakuRlnRelay.validateMessage(wm3, some(time)) # wm4 has no rln proof and should not be validated - msgValidate4 = wakuRlnRelay.validateMessage(wm4) + msgValidate4 = wakuRlnRelay.validateMessage(wm4, some(time)) check: 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 018532a6b..031623be1 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 @@ -427,18 +427,25 @@ proc compare*(e1, e2: Epoch): int64 = return int64(epoch1) - int64(epoch2) -proc validateMessage*(rlnPeer: WakuRLNRelay, msg: WakuMessage): MessageValidationResult = +proc validateMessage*(rlnPeer: WakuRLNRelay, msg: WakuMessage, timeOption: Option[float64] = none(float64)): MessageValidationResult = ## validate the supplied `msg` based on the waku-rln-relay routing protocol i.e., ## the `msg`'s epoch is within MAX_EPOCH_GAP of the current epoch ## the `msg` has valid rate limit proof ## the `msg` does not violate the rate limit + ## `timeOption` indicates Unix epoch time (fractional part holds sub-seconds) + ## if `timeOption` is supplied, then the current epoch is calculated based on that # checks if the `msg`'s epoch is far from the current epoch # it corresponds to the validation of rln external nullifier - let + var epoch: Epoch + if timeOption.isSome(): + epoch = calcEpoch(timeOption.get()) + else: # get current rln epoch epoch = getCurrentEpoch() + + let msgEpoch = msg.proof.epoch # calculate the gaps gap = compare(epoch, msgEpoch)