diff --git a/tests/v2/test_waku_rln_relay_onchain.nim b/tests/v2/test_waku_rln_relay_onchain.nim index e450aec77..20ffc086d 100644 --- a/tests/v2/test_waku_rln_relay_onchain.nim +++ b/tests/v2/test_waku_rln_relay_onchain.nim @@ -7,6 +7,7 @@ import testutils/unittests, chronos, chronicles, stint, web3, json, stew/byteutils, stew/shims/net as stewNet, libp2p/crypto/crypto, + eth/keys, ../../waku/v2/protocol/waku_rln_relay/[waku_rln_relay_utils, waku_rln_relay_types, rln_relay_contract], ../../waku/v2/node/wakunode2, @@ -75,6 +76,31 @@ proc uploadRLNContract*(ethClientAddress: string): Future[Address] {.async.} = return contractAddress + +proc createEthAccount(): Future[(keys.PrivateKey, Address)] {.async.} = + let theRNG = keys.newRng() + + let web3 = await newWeb3(ETH_CLIENT) + let accounts = await web3.provider.eth_accounts() + let gasPrice = int(await web3.provider.eth_gasPrice()) + web3.defaultAccount = accounts[0] + + let pk = keys.PrivateKey.random(theRNG[]) + let acc = Address(toCanonicalAddress(pk.toPublicKey())) + + var tx: EthSend + tx.source = accounts[0] + tx.value = some(ethToWei(10.u256)) + tx.to = some(acc) + tx.gasPrice = some(gasPrice) + + # Send 10 eth to acc + discard await web3.send(tx) + var balance = await web3.provider.eth_getBalance(acc, "latest") + assert(balance == ethToWei(10.u256)) + + return (pk, acc) + procSuite "Waku-rln-relay": asyncTest "event subscription": # preparation ------------------------------ @@ -259,12 +285,16 @@ procSuite "Waku-rln-relay": check: membershipKeyPair.isSome + # create an Ethereum private key and the corresponding account + let (ethPrivKey, ethacc) = await createEthAccount() + # test ------------------------------ # initialize the WakuRLNRelay var rlnPeer = WakuRLNRelay(membershipKeyPair: membershipKeyPair.get(), membershipIndex: MembershipIndex(0), ethClientAddress: ETH_CLIENT, - ethAccountAddress: ethAccountAddress, + ethAccountPrivateKey: ethPrivKey, + ethAccountAddress: ethacc, membershipContractAddress: contractAddress) # register the rln-relay peer to the membership contract @@ -347,6 +377,8 @@ procSuite "Waku-rln-relay": # choose one of the existing accounts for the rln-relay peer ethAccountAddress = accounts[0] web3.defaultAccount = accounts[0] + + # create an rln instance var rlnInstance = createRLNInstance() @@ -389,11 +421,16 @@ procSuite "Waku-rln-relay": let tx2Hash = await contractObj.register(pk2).send(value = MEMBERSHIP_FEE) debug "a member is registered", tx2 = tx2Hash + # create an Ethereum private key and the corresponding account + let (ethPrivKey, ethacc) = await createEthAccount() + + # test ------------------------------ # start rln-relay node.mountRelay(@[RLNRELAY_PUBSUB_TOPIC]) await node.mountRlnRelayDynamic(ethClientAddr = EthClient, - ethAccAddr = ethAccountAddress, + ethAccAddr = ethacc, + ethAccountPrivKey = ethPrivKey, memContractAddr = contractAddress, memKeyPair = keyPair1, memIndex = some(MembershipIndex(0)), @@ -439,10 +476,14 @@ procSuite "Waku-rln-relay": node2 = WakuNode.new(nodeKey2, ValidIpAddress.init("0.0.0.0"), Port(60001)) await node2.start() + # create an Ethereum private key and the corresponding account + let (ethPrivKey, ethacc) = await createEthAccount() + # start rln-relay on the first node, leave rln-relay credentials empty node.mountRelay(@[RLNRELAY_PUBSUB_TOPIC]) await node.mountRlnRelayDynamic(ethClientAddr = EthClient, - ethAccAddr = ethAccountAddress1, + ethAccAddr = ethacc, + ethAccountPrivKey = ethPrivKey, memContractAddr = contractAddress, memKeyPair = none(MembershipKeyPair), memIndex = none(MembershipIndex), @@ -454,7 +495,8 @@ procSuite "Waku-rln-relay": # start rln-relay on the second node, leave rln-relay credentials empty node2.mountRelay(@[RLNRELAY_PUBSUB_TOPIC]) await node2.mountRlnRelayDynamic(ethClientAddr = EthClient, - ethAccAddr = ethAccountAddress2, + ethAccAddr = ethacc, + ethAccountPrivKey = ethPrivKey, memContractAddr = contractAddress, memKeyPair = none(MembershipKeyPair), memIndex = none(MembershipIndex), diff --git a/waku/v2/node/config.nim b/waku/v2/node/config.nim index 3e0d89b2c..a18e5b71a 100644 --- a/waku/v2/node/config.nim +++ b/waku/v2/node/config.nim @@ -145,9 +145,14 @@ type name: "rln-relay-id-commitment" }: string rlnRelayEthAccount* {. - desc: "Ethereum testnet account address", + desc: "Account address for the Ethereum testnet Goerli", defaultValue: "" name: "eth-account-address" }: string + + rlnRelayEthAccountPrivKey* {. + desc: "Account private key for the Ethereum testnet Goerli", + defaultValue: "" + name: "eth-account-privatekey" }: string rlnRelayEthClientAddress* {. desc: "Ethereum testnet client address e.g., ws://localhost:8540/", 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 a7ddac384..b41f42043 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 @@ -90,8 +90,8 @@ type MessageValidationResult* {.pure.} = enum # 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 - MEMBERSHIP_FEE* = 5.u256 - # the current implementation of the rln lib only supports a circuit for Merkle tree with depth 32 + MEMBERSHIP_FEE* = 1000000000000000.u256 + # the current implementation of the rln lib supports a circuit for Merkle tree with depth 20 MERKLE_TREE_DEPTH* = 20 # TODO the ETH_CLIENT should be an input to the rln-relay, though hardcoded for now # the current address is the address of ganache-cli when run locally 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 8b698640e..b1f08b05d 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 @@ -119,11 +119,15 @@ proc toMembershipIndex(v: UInt256): MembershipIndex = let result: MembershipIndex = cast[MembershipIndex](v) return result -proc register*(idComm: IDCommitment, ethAccountAddress: Address, ethClientAddress: string, membershipContractAddress: Address): Future[Result[MembershipIndex, string]] {.async.} = +proc register*(idComm: IDCommitment, ethAccountAddress: Address, ethAccountPrivKey: keys.PrivateKey, ethClientAddress: string, membershipContractAddress: Address): Future[Result[MembershipIndex, string]] {.async.} = # TODO may need to also get eth Account Private Key as PrivateKey ## registers the idComm into the membership contract whose address is in rlnPeer.membershipContractAddress let web3 = await newWeb3(ethClientAddress) web3.defaultAccount = ethAccountAddress + # set the account private key + web3.privateKey = some(ethAccountPrivKey) + # set the gas price twice the suggested price in order for the fast mining + let gasPrice = int(await web3.provider.eth_gasPrice()) * 2 # when the private key is set in a web3 instance, the send proc (sender.register(pk).send(MEMBERSHIP_FEE)) # does the signing using the provided key @@ -133,7 +137,7 @@ proc register*(idComm: IDCommitment, ethAccountAddress: Address, ethClientAddres debug "registering an id commitment", idComm=idComm let pk = idComm.toUInt256() - txHash = await sender.register(pk).send(MEMBERSHIP_FEE) + txHash = await sender.register(pk).send(value = MEMBERSHIP_FEE, gasPrice = gasPrice) tsReceipt = await web3.getMinedTransactionReceipt(txHash) # the receipt topic holds the hash of signature of the raised events @@ -165,7 +169,7 @@ proc register*(rlnPeer: WakuRLNRelay): Future[bool] {.async.} = ## registers the public key of the rlnPeer which is rlnPeer.membershipKeyPair.publicKey ## into the membership contract whose address is in rlnPeer.membershipContractAddress let pk = rlnPeer.membershipKeyPair.idCommitment - discard await register(idComm = pk, ethAccountAddress = rlnPeer.ethAccountAddress, ethClientAddress = rlnPeer.ethClientAddress, membershipContractAddress = rlnPeer.membershipContractAddress ) + discard await register(idComm = pk, ethAccountAddress = rlnPeer.ethAccountAddress, ethAccountPrivKey = rlnPeer.ethAccountPrivateKey, ethClientAddress = rlnPeer.ethClientAddress, membershipContractAddress = rlnPeer.membershipContractAddress ) return true @@ -607,16 +611,19 @@ proc addAll*(rlnInstance: RLN[Bn256], list: seq[IDCommitment]): bool = type RegistrationEventHandler = proc(pubkey: Uint256, index: Uint256): void {.gcsafe, closure, raises: [Defect].} -proc subscribeToGroupEvents(ethClientUri: string, contractAddress: Address, blockNumber: string = "0x0", handler: RegistrationEventHandler) {.async, gcsafe.} = +proc subscribeToGroupEvents(ethClientUri: string, ethAccountAddress: Address, contractAddress: Address, blockNumber: string = "0x0", handler: RegistrationEventHandler) {.async, gcsafe.} = ## connects to the eth client whose URI is supplied as `ethClientUri` ## subscribes to the `MemberRegistered` event emitted from the `MembershipContract` which is available on the supplied `contractAddress` ## it collects all the events starting from the given `blockNumber` ## for every received event, it calls the `handler` # connect to the eth client - let web3 = await newWeb3(ETH_CLIENT) + let web3 = await newWeb3(ethClientUri) # prepare a contract sender to interact with it var contractObj = web3.contractSender(MembershipContract, contractAddress) + web3.defaultAccount = ethAccountAddress + # set the gas price twice the suggested price in order for the fast mining + # let gasPrice = int(await web3.provider.eth_gasPrice()) * 2 # subscribe to the MemberRegistered events # TODO can do similarly for deletion events, though it is not yet supported @@ -632,7 +639,7 @@ proc subscribeToGroupEvents(ethClientUri: string, contractAddress: Address, bloc proc handleGroupUpdates*(rlnPeer: WakuRLNRelay, handler: RegistrationEventHandler) {.async, gcsafe.} = # mounts the supplied handler for the registration events emitting from the membership contract - await subscribeToGroupEvents(ethClientUri = rlnPeer.ethClientAddress, contractAddress = rlnPeer.membershipContractAddress, handler = handler) + await subscribeToGroupEvents(ethClientUri = rlnPeer.ethClientAddress, ethAccountAddress = rlnPeer.ethAccountAddress, contractAddress = rlnPeer.membershipContractAddress, handler = handler) proc addRLNRelayValidator*(node: WakuNode, pubsubTopic: string, contentTopic: ContentTopic, spamHandler: Option[SpamHandler] = none(SpamHandler)) = ## this procedure is a thin wrapper for the pubsub addValidator method @@ -737,6 +744,7 @@ proc mountRlnRelayStatic*(node: WakuNode, proc mountRlnRelayDynamic*(node: WakuNode, ethClientAddr: string = "", ethAccAddr: web3.Address, + ethAccountPrivKey: keys.PrivateKey, memContractAddr: web3.Address, memKeyPair: Option[MembershipKeyPair] = none(MembershipKeyPair), memIndex: Option[MembershipIndex] = none(MembershipIndex), @@ -770,7 +778,7 @@ proc mountRlnRelayDynamic*(node: WakuNode, doAssert(keyPairOpt.isSome) keyPair = keyPairOpt.get() # register the rln-relay peer to the membership contract - let regIndexRes = await register(idComm = keyPair.idCommitment, ethAccountAddress = ethAccAddr, ethClientAddress = ethClientAddr, membershipContractAddress = memContractAddr) + let regIndexRes = await register(idComm = keyPair.idCommitment, ethAccountAddress = ethAccAddr, ethAccountPrivKey = ethAccountPrivKey, ethClientAddress = ethClientAddr, membershipContractAddress = memContractAddr) # check whether registration is done doAssert(regIndexRes.isOk()) rlnIndex = regIndexRes.value @@ -785,6 +793,7 @@ proc mountRlnRelayDynamic*(node: WakuNode, membershipContractAddress: memContractAddr, ethClientAddress: ethClientAddr, ethAccountAddress: ethAccAddr, + ethAccountPrivateKey: ethAccountPrivKey, rlnInstance: rln, pubsubTopic: pubsubTopic, contentTopic: contentTopic) @@ -839,6 +848,7 @@ proc mountRlnRelay*(node: WakuNode, conf: WakuNodeConf) {.raises: [Defect, Value # read related inputs to run rln-relay in on-chain mode and do type conversion when needed let ethAccountAddr = web3.fromHex(web3.Address, conf.rlnRelayEthAccount) + ethAccountPrivKey = keys.PrivateKey(SkSecretKey.fromHex(conf.rlnRelayEthAccountPrivKey).value) ethClientAddr = conf.rlnRelayEthClientAddress ethMemContractAddress = web3.fromHex(web3.Address, conf.rlnRelayEthMemContractAddress) rlnRelayId = conf.rlnRelayIdKey @@ -850,9 +860,9 @@ proc mountRlnRelay*(node: WakuNode, conf: WakuNodeConf) {.raises: [Defect, Value let keyPair = @[(rlnRelayId, rlnRelayIdCommitmentKey)] let memKeyPair = keyPair.toMembershipKeyPairs()[0] # mount the rln relay protocol in the on-chain/dynamic mode - waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr, memKeyPair = some(memKeyPair), memIndex = some(rlnRelayIndex), ethAccAddr = ethAccountAddr, pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic) + waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr, memKeyPair = some(memKeyPair), memIndex = some(rlnRelayIndex), ethAccAddr = ethAccountAddr, ethAccountPrivKey = ethAccountPrivKey, pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic) else: # no rln credential is provided # mount the rln relay protocol in the on-chain/dynamic mode - waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr, ethAccAddr = ethAccountAddr, pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic) + waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr, ethAccAddr = ethAccountAddr, ethAccountPrivKey = ethAccountPrivKey, pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic)