chore(rln-relay): make account address optional (#1258)

* chore(rln-relay): make account address optional

* fix(rln-relay): onchain test

* chore(chat2): update docs, and handle error case in chat2

* fix(rln-relay): handle registration better
This commit is contained in:
Aaryamann Challani 2022-10-12 07:48:11 +05:30 committed by GitHub
parent a36de4036b
commit 04154ab90f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 90 additions and 46 deletions

View File

@ -40,7 +40,7 @@ You can test it by connecting two chat2 clients (running Waku-RLN-Relay) directl
## Build chat2
First, build chat2 with the RLN flag set to true.
```
```bash
make chat2 RLN=true
```
@ -48,7 +48,7 @@ make chat2 RLN=true
Run the following command to set up your chat2 client.
```
```bash
./build/chat2 --fleet:test --content-topic:/toy-chat/2/luzhou/proto --rln-relay:true --rln-relay-dynamic:true --rln-relay-eth-contract-address:0x4252105670fe33d2947e8ead304969849e64f2a6 --rln-relay-eth-account-address:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --rln-relay-eth-account-private-key:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --rln-relay-eth-client-address:xxxx --ports-shift=1
```
@ -166,10 +166,18 @@ RLN credential is persisted in the `rlnCredentials.txt` file under the specified
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 a new credential and will use, instead, the persisted RLN credential.
```
```bash
./build/chat2 --fleet:test --content-topic:/toy-chat/2/luzhou/proto --rln-relay:true --rln-relay-dynamic:true --rln-relay-eth-contract-address:0x4252105670fe33d2947e8ead304969849e64f2a6 --rln-relay-eth-account-address:your_eth_account --rln-relay-eth-account-private-key:your_eth_private_key --rln-relay-eth-client-address:your_goerli_node --ports-shift=1 --rln-relay-cred-path:./
```
Note: If you are reusing credentials, you can omit the `rln-relay-eth-account-address` and `rln-relay-eth-account-private-key` flags
Therefore, the command to start chat2 would be -
```bash
./build/chat2 --fleet:test --content-topic:/toy-chat/2/luzhou/proto --rln-relay:true --rln-relay-dynamic:true --rln-relay-eth-contract-address:0x4252105670fe33d2947e8ead304969849e64f2a6 --rln-relay-eth-client-address:your_goerli_node --ports-shift=1 --rln-relay-cred-path:./
```
# Sample test output
In this section, a sample test of running two chat clients is provided.
Note that the values used for `rln-relay-eth-account-address`, `rln-relay-eth-account-private-key`, and `rln-relay-eth-client-address` in the following code snippets are junk and not valid.
@ -183,9 +191,11 @@ You can check this fact by looking at `Bob`'s console, where `message3` is missi
**Alice**
```
```bash
./build/chat2 --fleet:test --content-topic:/toy-chat/2/luzhou/proto --rln-relay:true --rln-relay-dynamic:true --rln-relay-eth-contract-address:0x4252105670fe33d2947e8ead304969849e64f2a6 --rln-relay-eth-account-address:0x1234567890123456789012345678901234567890 --rln-relay-eth-account-private-key:0x1234567890123456789012345678901234567890123456789012345678901234 --rln-relay-eth-client-address:wss://goerli.infura.io/ws/v3/12345678901234567890123456789012 --ports-shift=1
```
```
Choose a nickname >> Alice
Welcome, Alice!
Connecting to test fleet using DNS discovery...
@ -225,9 +235,11 @@ your rln identity commitment key is: bd093cbf14fb933d53f596c33f98b3df83b7e9f7a19
```
**Bob**
```
```bash
./build/chat2 --fleet:test --content-topic:/toy-chat/2/luzhou/proto --rln-relay:true --rln-relay-dynamic:true --rln-relay-eth-contract-address:0x4252105670fe33d2947e8ead304969849e64f2a6 --rln-relay-eth-account-address:0x1234567890123456789012345678901234567890 --rln-relay-eth-account-private-key:0x1234567890123456789012345678901234567890123456789012345678901234 --rln-relay-eth-client-address:wss://goerli.infura.io/ws/v3/12345678901234567890123456789012 --ports-shift=2
```
```
Choose a nickname >> Bob
Welcome, Bob!
Connecting to test fleet using DNS discovery...

View File

@ -377,6 +377,10 @@ proc processInput(rfd: AsyncFD) {.async.} =
wssEnabled = conf.websocketSecureSupport)
await node.start()
if conf.rlnRelayEthAccountPrivateKey == "" and conf.rlnRelayCredPath == "":
raise newException(ConfigurationError,
"Either rln-relay-eth-private-key or rln-relay-cred-path MUST be passed")
if conf.relay:
await node.mountRelay(conf.topics.split(" "))
@ -531,7 +535,7 @@ proc processInput(rfd: AsyncFD) {.async.} =
showChatPrompt(chat)
proc registrationHandler(txHash: string) {.gcsafe, closure.} =
echo "You are registered to the rln membership contract, find details of your registration transaction in https://goerli.etherscan.io/tx/0x", txHash
echo "rln-relay preparation is in progress..."
let res = node.mountRlnRelay(conf = conf, spamHandler = some(spamHandler), registrationHandler = some(registrationHandler))
if res.isErr:
@ -555,11 +559,19 @@ proc main() {.async.} =
var thread: Thread[AsyncFD]
thread.createThread(readInput, wfd)
try:
await processInput(rfd)
# Handle only ConfigurationError for now
# TODO: Throw other errors from the mounting procedure
except ConfigurationError as e:
raise e
await processInput(rfd)
when isMainModule: # isMainModule = true when the module is compiled as the main file
waitFor(main())
try:
waitFor(main())
except CatchableError as e:
raise e
## Dump of things that can be improved:
##

View File

@ -193,7 +193,7 @@ procSuite "Waku-rln-relay":
var rlnPeer = WakuRLNRelay(membershipKeyPair: keyPair.get(),
membershipIndex: MembershipIndex(0),
ethClientAddress: EthClient,
ethAccountAddress: accounts[0],
ethAccountAddress: some(accounts[0]),
membershipContractAddress: contractAddress,
rlnInstance: rln)
@ -296,7 +296,7 @@ procSuite "Waku-rln-relay":
membershipIndex: MembershipIndex(0),
ethClientAddress: EthClient,
ethAccountPrivateKey: some(ethPrivKey),
ethAccountAddress: ethacc,
ethAccountAddress: some(ethacc),
membershipContractAddress: contractAddress)
# register the rln-relay peer to the membership contract
@ -431,7 +431,7 @@ procSuite "Waku-rln-relay":
# start rln-relay
await node.mountRelay(@[RlnRelayPubsubTopic])
discard await node.mountRlnRelayDynamic(ethClientAddr = EthClient,
ethAccAddr = ethacc,
ethAccountAddress = some(ethacc),
ethAccountPrivKeyOpt = some(ethPrivKey),
memContractAddr = contractAddress,
memKeyPair = keyPair1,
@ -484,7 +484,7 @@ procSuite "Waku-rln-relay":
# start rln-relay on the first node, leave rln-relay credentials empty
await node.mountRelay(@[RlnRelayPubsubTopic])
discard await node.mountRlnRelayDynamic(ethClientAddr = EthClient,
ethAccAddr = ethacc,
ethAccountAddress = some(ethacc),
ethAccountPrivKeyOpt = some(ethPrivKey),
memContractAddr = contractAddress,
memKeyPair = none(MembershipKeyPair),
@ -497,7 +497,7 @@ procSuite "Waku-rln-relay":
# start rln-relay on the second node, leave rln-relay credentials empty
await node2.mountRelay(@[RlnRelayPubsubTopic])
discard await node2.mountRlnRelayDynamic(ethClientAddr = EthClient,
ethAccAddr = ethacc,
ethAccountAddress = some(ethacc),
ethAccountPrivKeyOpt = some(ethPrivKey),
memContractAddr = contractAddress,
memKeyPair = none(MembershipKeyPair),

View File

@ -92,7 +92,7 @@ when defined(rln) or (not defined(rln) and not defined(rlnzerokit)):
membershipIndex*: MembershipIndex
membershipContractAddress*: Address
ethClientAddress*: string
ethAccountAddress*: Address
ethAccountAddress*: Option[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
@ -116,7 +116,7 @@ when defined(rlnzerokit):
membershipIndex*: MembershipIndex
membershipContractAddress*: Address
ethClientAddress*: string
ethAccountAddress*: Address
ethAccountAddress*: Option[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

View File

@ -189,7 +189,7 @@ proc toMembershipIndex(v: UInt256): MembershipIndex =
let result: MembershipIndex = cast[MembershipIndex](v)
return result
proc register*(idComm: IDCommitment, ethAccountAddress: Address, ethAccountPrivKey: keys.PrivateKey, ethClientAddress: string, membershipContractAddress: Address, registrationHandler: Option[RegistrationHandler] = none(RegistrationHandler)): Future[Result[MembershipIndex, string]] {.async.} =
proc register*(idComm: IDCommitment, ethAccountAddress: Option[Address], ethAccountPrivKey: keys.PrivateKey, ethClientAddress: string, membershipContractAddress: Address, registrationHandler: Option[RegistrationHandler] = none(RegistrationHandler)): 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
@ -199,7 +199,8 @@ proc register*(idComm: IDCommitment, ethAccountAddress: Address, ethAccountPrivK
except:
return err("could not connect to the Ethereum client")
web3.defaultAccount = ethAccountAddress
if ethAccountAddress.isSome():
web3.defaultAccount = ethAccountAddress.get()
# set the account private key
web3.privateKey = some(ethAccountPrivKey)
# set the gas price twice the suggested price in order for the fast mining
@ -920,7 +921,7 @@ proc subscribeToMemberRegistrations(web3: Web3, contractAddress: Address, handle
do (err: CatchableError):
error "Error from subscription: ", err=err.msg
proc subscribeToGroupEvents(ethClientUri: string, ethAccountAddress: Address, contractAddress: Address, blockNumber: string = "0x0", handler: RegistrationEventHandler) {.async, gcsafe.} =
proc subscribeToGroupEvents(ethClientUri: string, ethAccountAddress: Option[Address] = none(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`
@ -1052,7 +1053,7 @@ proc mountRlnRelayStatic*(node: WakuNode,
proc mountRlnRelayDynamic*(node: WakuNode,
ethClientAddr: string = "",
ethAccAddr: web3.Address,
ethAccountAddress: Option[web3.Address] = none(web3.Address),
ethAccountPrivKeyOpt: Option[keys.PrivateKey],
memContractAddr: web3.Address,
memKeyPair: Option[MembershipKeyPair] = none(MembershipKeyPair),
@ -1090,7 +1091,7 @@ proc mountRlnRelayDynamic*(node: WakuNode,
keyPair = keyPairOpt.get()
# register the rln-relay peer to the membership contract
waku_rln_registration_duration_seconds.nanosecondTime:
let regIndexRes = await register(idComm = keyPair.idCommitment, ethAccountAddress = ethAccAddr, ethAccountPrivKey = ethAccountPrivKeyOpt.get(), ethClientAddress = ethClientAddr, membershipContractAddress = memContractAddr, registrationHandler = registrationHandler)
let regIndexRes = await register(idComm = keyPair.idCommitment, ethAccountAddress = ethAccountAddress, ethAccountPrivKey = ethAccountPrivKeyOpt.get(), ethClientAddress = ethClientAddr, membershipContractAddress = memContractAddr, registrationHandler = registrationHandler)
# check whether registration is done
if regIndexRes.isErr():
debug "membership registration failed", err=regIndexRes.error()
@ -1109,7 +1110,7 @@ proc mountRlnRelayDynamic*(node: WakuNode,
membershipIndex: rlnIndex,
membershipContractAddress: memContractAddr,
ethClientAddress: ethClientAddr,
ethAccountAddress: ethAccAddr,
ethAccountAddress: ethAccountAddress,
ethAccountPrivateKey: ethAccountPrivKeyOpt,
rlnInstance: rln,
pubsubTopic: pubsubTopic,
@ -1186,18 +1187,24 @@ proc mount(node: WakuNode,
info "WakuRLNRelay is mounted successfully", pubsubtopic=conf.rlnRelayPubsubTopic, contentTopic=conf.rlnRelayContentTopic
return ok(true)
else: # mount the rln relay protocol in the on-chain/dynamic mode
echo "setting up waku-rln-relay in on-chain mode... "
debug "setting up waku-rln-relay in on-chain mode... "
debug "on-chain parameters", contractAddress=conf.rlnRelayEthContractAddress
debug "on-chain setup parameters", contractAddress=conf.rlnRelayEthContractAddress
# read related inputs to run rln-relay in on-chain mode and do type conversion when needed
let
ethAccountAddr = web3.fromHex(web3.Address, conf.rlnRelayEthAccountAddress)
ethClientAddr = conf.rlnRelayEthClientAddress
ethMemContractAddress = web3.fromHex(web3.Address, conf.rlnRelayEthContractAddress)
var ethAccountPrivKeyOpt = none(keys.PrivateKey)
var ethAccountAddressOpt = none(Address)
var credentials = none(RlnMembershipCredentials)
var res: RlnRelayResult[bool]
if conf.rlnRelayEthAccountPrivateKey != "":
ethAccountPrivKeyOpt = some(keys.PrivateKey(SkSecretKey.fromHex(conf.rlnRelayEthAccountPrivateKey).value))
if conf.rlnRelayEthAccountAddress != "":
ethAccountAddressOpt = some(web3.fromHex(web3.Address, conf.rlnRelayEthAccountAddress))
# 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
@ -1207,41 +1214,54 @@ proc mount(node: WakuNode,
# 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
let res = 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, registrationHandler = registrationHandler)
if res.isErr:
return err("dynamic rln-relay could not be mounted: " & res.error())
credentials = some(readPersistentRlnCredentials(rlnRelayCredPath))
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"
let res = waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr,
ethAccAddr = ethAccountAddr, ethAccountPrivKeyOpt = ethAccountPrivKeyOpt, pubsubTopic = conf.rlnRelayPubsubTopic,
contentTopic = conf.rlnRelayContentTopic, spamHandler = spamHandler, registrationHandler = registrationHandler)
if res.isErr:
return err("dynamic rln-relay could not be mounted: " & res.error())
# Persist generated credentials
var rlnMembershipCredentials =
RlnMembershipCredentials(membershipKeyPair: node.wakuRlnRelay.membershipKeyPair, rlnIndex: node.wakuRlnRelay.membershipIndex)
if credentials.isSome():
# mount rln-relay in on-chain mode, with credentials that were read or generated
res = waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress,
ethClientAddr = ethClientAddr,
ethAccountAddress = ethAccountAddressOpt,
ethAccountPrivKeyOpt = ethAccountPrivKeyOpt,
pubsubTopic = conf.rlnRelayPubsubTopic,
contentTopic = conf.rlnRelayContentTopic,
spamHandler = spamHandler,
registrationHandler = registrationHandler,
memKeyPair = some(credentials.get().membershipKeyPair),
memIndex = some(credentials.get().rlnIndex))
else:
# mount rln-relay in on-chain mode, with the provided private key
res = waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress,
ethClientAddr = ethClientAddr,
ethAccountAddress = ethAccountAddressOpt,
ethAccountPrivKeyOpt = ethAccountPrivKeyOpt,
pubsubTopic = conf.rlnRelayPubsubTopic,
contentTopic = conf.rlnRelayContentTopic,
spamHandler = spamHandler,
registrationHandler = registrationHandler)
# TODO should be replaced with key-store with proper encryption
# persist rln credential
writeFile(rlnRelayCredPath, pretty(%rlnMembershipCredentials))
credentials = some(RlnMembershipCredentials(rlnIndex: node.wakuRlnRelay.membershipIndex,
membershipKeyPair: node.wakuRlnRelay.membershipKeyPair))
writeFile(rlnRelayCredPath, pretty(%credentials.get()))
else:
# 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"
let res = waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr,
ethAccAddr = ethAccountAddr, ethAccountPrivKeyOpt = ethAccountPrivKeyOpt, pubsubTopic = conf.rlnRelayPubsubTopic,
res = waitFor node.mountRlnRelayDynamic(memContractAddr = ethMemContractAddress, ethClientAddr = ethClientAddr,
ethAccountAddress = ethAccountAddressOpt, ethAccountPrivKeyOpt = ethAccountPrivKeyOpt, pubsubTopic = conf.rlnRelayPubsubTopic,
contentTopic = conf.rlnRelayContentTopic, spamHandler = spamHandler, registrationHandler = registrationHandler)
if res.isErr:
if res.isErr():
return err("dynamic rln-relay could not be mounted: " & res.error())
return ok(true)
return ok(res.value())
proc mountRlnRelay*(node: WakuNode,