diff --git a/docs/tutorial/onchain-rln-relay-chat2.md b/docs/tutorial/onchain-rln-relay-chat2.md index 97737e82a..f9f5a5606 100644 --- a/docs/tutorial/onchain-rln-relay-chat2.md +++ b/docs/tutorial/onchain-rln-relay-chat2.md @@ -22,7 +22,7 @@ This transaction will also transfer `0.001` Ethers to the contract as membership This amount plus the transaction fee will be deducted from the supplied Goerli account. Once the transaction is mined and the registration is successful, the registered credential will get displayed on the console of your chat2 client. You may copy the displayed RLN credential and reuse them for the future execution of the chat2 application. -Proper instructions in this regard is provided in the following [section](#how-to-reuse-rln-credential). +Proper instructions in this regard is provided in the following [section](#how-to-persist-and-reuse-rln-credential). If you choose not to reuse the same credential, then for each execution, a new registration will take place and more funds will get deducted from your Goerli account. Under the hood, the chat2 client constantly listens to the membership contract and keeps itself updated with the latest state of the group. @@ -150,19 +150,15 @@ Once you are done with the test, make sure you close all the chat2 clients by ty quitting... ``` -## How to reuse RLN credential +## How to persist and reuse RLN credential -You may reuse your old RLN credential using `rln-relay-membership-index`, `rln-relay-id` and `rln-relay-id-commitment` options. -For instance, if the previously generated credential are -``` -your membership index is: xx -your rln identity key is: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -your rln identity commitment key is: 6c6598126ba10d1b70100893b76d7f8d7343eeb8f5ecfd48371b421c5aa6f012 -``` -Then, the execution command will look like this (inspect the last three config options): -``` -./build/chat2 --fleet:test --content-topic:/toy-chat/2/luzhou/proto --rln-relay:true --rln-relay-dynamic:true --eth-mem-contract-address:0x4252105670fe33d2947e8ead304969849e64f2a6 --eth-account-address:your_eth_account --eth-account-privatekey:your_eth_private_key --eth-client-address:your_goerli_node --ports-shift=1 --rln-relay-membership-index:63 --rln-relay-id:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --rln-relay-id-commitment:6c6598126ba10d1b70100893b76d7f8d7343eeb8f5ecfd48371b421c5aa6f012 +You may pass the `rln-relay-cred-path` config option to specify a path for 1) persisting RLN credential 2) retrieving persisted RLN credential. +RLN credential is persisted in the `rlnCredentials.txt` file under the specified path. +If this file does not already exist under the supplied path, then a new credential is generated and persisted in the `rlnCredentials.txt` file. +Otherwise, the chat client does not generate new credential, instead uses the persisted RLN credential. +``` +./build/chat2 --fleet:test --content-topic:/toy-chat/2/luzhou/proto --rln-relay:true --rln-relay-dynamic:true --eth-mem-contract-address:0x4252105670fe33d2947e8ead304969849e64f2a6 --eth-account-address:your_eth_account --eth-account-privatekey:your_eth_private_key --eth-client-address:your_goerli_node --ports-shift=1 --rln-relay-cred-path:./ ``` # Sample test output diff --git a/examples/v2/config_chat2.nim b/examples/v2/config_chat2.nim index 7df585d1e..1cd89de6c 100644 --- a/examples/v2/config_chat2.nim +++ b/examples/v2/config_chat2.nim @@ -233,7 +233,12 @@ type desc: "Enable spam protection through rln-relay: true|false", defaultValue: false name: "rln-relay" }: bool - + + rlnRelayCredPath* {. + desc: "The path for peristing rln-relay credential", + defaultValue: "" + name: "rln-relay-cred-path" }: string + rlnRelayMemIndex* {. desc: "(experimental) the index of node in the rln-relay group: a value between 0-99 inclusive", defaultValue: MembershipIndex(0) diff --git a/waku/v2/node/config.nim b/waku/v2/node/config.nim index 12a57e3fc..198126a94 100644 --- a/waku/v2/node/config.nim +++ b/waku/v2/node/config.nim @@ -114,6 +114,11 @@ type defaultValue: false name: "rln-relay" }: bool + rlnRelayCredPath* {. + desc: "The path for peristing rln-relay credential", + defaultValue: "" + name: "rln-relay-cred-path" }: string + rlnRelayMemIndex* {. desc: "(experimental) the index of node in the rln-relay group: a value between 0-99 inclusive", defaultValue: MembershipIndex(0) 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 bfa2d8e51..bf70eebb9 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 @@ -148,7 +148,7 @@ type MessageValidationResult* {.pure.} = enum # RLN membership key and index files path const - RLN_CREDENTIALS_FILEPATH* = "rlnCredentials.txt" + RLN_CREDENTIALS_FILENAME* = "rlnCredentials.txt" # inputs of the membership contract constructor # TODO may be able to make these constants private and put them inside the waku_rln_relay_utils 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 54ca41ee2..0cbd357fe 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 @@ -998,15 +998,6 @@ proc mountRlnRelayDynamic*(node: WakuNode, keyPair = memKeyPair.get() rlnIndex = memIndex.get() - var - rlnMembershipCredentials = RlnMembershipCredentials(membershipKeyPair: keyPair, rlnIndex: rlnIndex) - - # Since the files are stored as a raw text file, it is highly susceptible to theft. - # The files needs some encryption to resolve this. - - # Write RLN credentials - writeFile(RLN_CREDENTIALS_FILEPATH, pretty(%rlnMembershipCredentials)) - # create the WakuRLNRelay var rlnPeer = WakuRLNRelay(membershipKeyPair: keyPair, membershipIndex: rlnIndex, @@ -1075,7 +1066,7 @@ proc mountRlnRelay*(node: WakuNode, conf: WakuNodeConf|Chat2Conf, spamHandler: O error "root mismatch: something went wrong not in Merkle tree construction" debug "the calculated root", root info "WakuRLNRelay is mounted successfully", pubsubtopic=conf.rlnRelayPubsubTopic, contentTopic=conf.rlnRelayContentTopic - else: + else: # mount the rln relay protocol in the on-chain/dynamic mode info " setting up waku-rln-relay in on-chain mode... " # read related inputs to run rln-relay in on-chain mode and do type conversion when needed @@ -1083,30 +1074,42 @@ proc mountRlnRelay*(node: WakuNode, conf: WakuNodeConf|Chat2Conf, spamHandler: O ethAccountAddr = web3.fromHex(web3.Address, conf.rlnRelayEthAccount) ethClientAddr = conf.rlnRelayEthClientAddress ethMemContractAddress = web3.fromHex(web3.Address, conf.rlnRelayEthMemContractAddress) - rlnRelayId = conf.rlnRelayIdKey - rlnRelayIdCommitmentKey = conf.rlnRelayIdCommitmentKey - rlnRelayIndex = conf.rlnRelayMemIndex var ethAccountPrivKeyOpt = none(keys.PrivateKey) if conf.rlnRelayEthAccountPrivKey != "": ethAccountPrivKeyOpt = some(keys.PrivateKey(SkSecretKey.fromHex(conf.rlnRelayEthAccountPrivKey).value)) - # check if the peer has provided its rln credentials - if rlnRelayIdCommitmentKey != "" and rlnRelayId != "": - # type conversation from hex strings to MembershipKeyPair - 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, - ethAccountPrivKeyOpt = ethAccountPrivKeyOpt, pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic, spamHandler = spamHandler) - elif fileExists(RLN_CREDENTIALS_FILEPATH): - var credentials = readPersistentRlnCredentials(RLN_CREDENTIALS_FILEPATH) - waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr, + + # if the rlnRelayCredPath config option is non-empty, then rln-relay credentials should be persisted + # if the path does not contain any credential file, then a new set is generated and pesisted in the same path + # if there is a credential file, then no new credentials are generated, instead the content of the file is read and used to mount rln-relay + if conf.rlnRelayCredPath != "": + let rlnRelayCredPath = joinPath(conf.rlnRelayCredPath, RLN_CREDENTIALS_FILENAME) + debug "rln-relay credential path", rlnRelayCredPath=rlnRelayCredPath + # check if there is an rln-relay credential file in the supplied path + if fileExists(rlnRelayCredPath): + # retrieve rln-relay credential + var credentials = readPersistentRlnCredentials(rlnRelayCredPath) + # mount rln-relay with the provided rln-relay credential + waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr, memKeyPair = some(credentials.membershipKeyPair), memIndex = some(credentials.rlnIndex), ethAccAddr = ethAccountAddr, ethAccountPrivKeyOpt = ethAccountPrivKeyOpt, pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic, spamHandler = spamHandler) + else: # there is no credential file available in the supplied path + # mount the rln-relay protocol leaving rln-relay credentials arguments unassigned + # this infroms mountRlnRelayDynamic proc that new credentials should be generated and registered to the membership contract + info "no rln credential is provided" + waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr, + ethAccAddr = ethAccountAddr, ethAccountPrivKeyOpt = ethAccountPrivKeyOpt, pubsubTopic = conf.rlnRelayPubsubTopic, + contentTopic = conf.rlnRelayContentTopic, spamHandler = spamHandler) + # Persist generated credentials + var rlnMembershipCredentials = + RlnMembershipCredentials(membershipKeyPair: node.wakuRlnRelay.membershipKeyPair, rlnIndex: node.wakuRlnRelay.membershipIndex) + # TODO should be replaced with key-store with proper encryption + # persist rln credential + writeFile(rlnRelayCredPath, pretty(%rlnMembershipCredentials)) + else: - # no rln credential is provided - # mount the rln relay protocol in the on-chain/dynamic mode - info "no rln credential is provided" + # do not persist or use a persisted rln-relay credential + # a new credential will be generated during the mount process but will not be persisted + info "no need to persist or use a persisted rln-relay credential" waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr, ethAccAddr = ethAccountAddr, ethAccountPrivKeyOpt = ethAccountPrivKeyOpt, pubsubTopic = conf.rlnRelayPubsubTopic, contentTopic = conf.rlnRelayContentTopic, spamHandler = spamHandler) \ No newline at end of file