Feat (Rln relay): adds utility procs to listen to the registration events emitted by the rln membership contract (#976)

* adds the contract handler file

* adds integration test for the group listening

* adds groupManagement proc

* deletes rln relay contract handler file

* brings back all the tests

* replaces toUINT256 with getIdCommitment proc

* replaces individual futures with an array of futures

* adds code documentation

* asyncSpawn instead of await

* adds untitest for toIDCommitment and toUInt256

* reorganizes the test and add rlnInstance

* mounts handleGroupUpdates on the rln peer

* asyncSpawn to await

* implements toIDCommitment

* updates the unittest

* improves the code documentation

* removes unused tests

* removes registration of the dynamic group management handler

* adds a comment

* adds a comment

* adds a TODO

* removes getIdCommitment
This commit is contained in:
Sanaz Taheri Boshrooyeh 2022-05-30 12:14:07 -07:00 committed by GitHub
parent 32d230b474
commit 1ac029025a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 138 additions and 9 deletions

View File

@ -703,4 +703,22 @@ suite "Waku rln relay":
msgValidate2 == MessageValidationResult.Spam
msgValidate3 == MessageValidationResult.Valid
msgValidate4 == MessageValidationResult.Invalid
test "toIDCommitment and toUInt256":
# create an instance of rln
var rlnInstance = createRLNInstance()
check:
rlnInstance.isOk == true
# create a key pair
var keypair = rlnInstance.value.membershipKeyGen()
check:
keypair.isSome()
# convert the idCommitment to UInt256
let idCUInt = keypair.get().idCommitment.toUInt256()
# convert the UInt256 back to ICommitment
let idCommitment = toIDCommitment(idCUInt)
# check that the conversion has not distorted the original value
check:
keypair.get().idCommitment == idCommitment

View File

@ -102,7 +102,7 @@ procSuite "Waku-rln-relay":
let membershipKeyPair = membershipKeyGen(rlnInstance.value)
check:
membershipKeyPair.isSome
let pk = membershipKeyPair.get().idCommitment.toUInt256()
let pk = membershipKeyPair.get().idCommitment.toUInt256()
debug "membership commitment key", pk = pk
# test ------------------------------
@ -128,6 +128,80 @@ procSuite "Waku-rln-relay":
# wait for the event to be received
await fut
# release resources -----------------------
await web3.close()
asyncTest "dynamic group management":
# preparation ------------------------------
debug "ethereum client address", ETH_CLIENT
let contractAddress = await uploadRLNContract(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]
debug "contract deployer account address ",
defaultAccount = web3.defaultAccount
# prepare a contract sender to interact with it
var contractObj = web3.contractSender(MembershipContract,
contractAddress) # creates a Sender object with a web3 field and contract address of type Address
# test ------------------------------
# create an RLN instance
var rlnInstance = createRLNInstance()
check:
rlnInstance.isOk == true
var rln = rlnInstance.value
# create rln membership key pair
let keyPair = rln.membershipKeyGen()
check:
keyPair.isSome
let pk = keyPair.get().idCommitment.toUInt256()
debug "membership commitment key", pk = pk
# initialize the WakuRLNRelay
var rlnPeer = WakuRLNRelay(membershipKeyPair: keyPair.get(),
membershipIndex: MembershipIndex(0),
ethClientAddress: ETH_CLIENT,
ethAccountAddress: accounts[0],
membershipContractAddress: contractAddress,
rlnInstance: rln)
# generate another membership key pair
let keyPair2 = rln.membershipKeyGen()
check:
keyPair2.isSome
let pk2 = keyPair2.get().idCommitment.toUInt256()
debug "membership commitment key", pk2 = pk2
var events = [newFuture[void](), newFuture[void]()]
proc handler(pubkey: Uint256, index: Uint256) =
debug "handler is called", pubkey = pubkey, index = index
if pubkey == pk:
events[0].complete()
if pubkey == pk2:
events[1].complete()
let isSuccessful = rlnPeer.rlnInstance.insertMember(pubkey.toIDCommitment())
check:
isSuccessful
# mount the handler for listening to the contract events
await rlnPeer.handleGroupUpdates(handler)
# register a member to the contract
let tx = await contractObj.register(pk).send(value = MEMBERSHIP_FEE)
debug "a member is registered", tx = tx
# register another member to the contract
let tx2 = await contractObj.register(pk2).send(value = MEMBERSHIP_FEE)
debug "a member is registered", tx2 = tx2
# wait for all the events to be received by the rlnPeer
await all(events)
# release resources -----------------------
await web3.close()
@ -215,7 +289,7 @@ procSuite "Waku-rln-relay":
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]
ethAccountAddress = accounts[0]
await web3.close()
# create current peer's pk
@ -267,5 +341,4 @@ procSuite "Waku-rln-relay":
check:
expectedRoot == calculatedRoot
await node.stop()
await node.stop()

View File

@ -636,9 +636,9 @@ when defined(rln):
if onchainMode:
# register the rln-relay peer to the membership contract
let is_successful = await rlnPeer.register()
let isSuccessful = await rlnPeer.register()
# check whether registration is done
doAssert(is_successful)
doAssert(isSuccessful)
debug "peer is successfully registered into the membership contract"
# adds a topic validator for the supplied pubsub topic at the relay protocol

View File

@ -3,7 +3,7 @@
import
std/sequtils, tables, times,
chronicles, options, chronos, stint,
web3,
web3, json,
stew/results,
stew/[byteutils, arrayops, endians2],
rln,
@ -21,8 +21,12 @@ type SpamHandler* = proc(wakuMessage: WakuMessage): void {.gcsafe, closure,
# membership contract interface
contract(MembershipContract):
# TODO define a return type of bool for register method to signify a successful registration
proc register(pubkey: Uint256) # external payable
proc MemberRegistered(pubkey: Uint256, index: Uint256) {.event.}
# TODO the followings are to be supported
# proc registerBatch(pubkeys: seq[Uint256]) # external payable
# proc withdraw(secret: Uint256, pubkeyIndex: Uint256, receiver: Address)
# proc withdrawBatch( secrets: seq[Uint256], pubkeyIndex: seq[Uint256], receiver: seq[Address])
proc createRLNInstance*(d: int = MERKLE_TREE_DEPTH): RLNResult
{.raises: [Defect, IOError].} =
@ -98,6 +102,10 @@ proc toUInt256*(idCommitment: IDCommitment): UInt256 =
let pk = cast[UInt256](idCommitment)
return pk
proc toIDCommitment*(idCommitment: UInt256): IDCommitment =
let pk = cast[IDCommitment](idCommitment)
return pk
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
@ -108,7 +116,7 @@ proc register*(rlnPeer: WakuRLNRelay): Future[bool] {.async.} =
web3.privateKey = rlnPeer.ethAccountPrivateKey
var sender = web3.contractSender(MembershipContract,
rlnPeer.membershipContractAddress) # creates a Sender object with a web3 field and contract address of type Address
let pk = toUInt256(rlnPeer.membershipKeyPair.idCommitment)
let pk = rlnPeer.membershipKeyPair.idCommitment.toUInt256()
discard await sender.register(pk).send(MEMBERSHIP_FEE)
debug "pk", pk = pk
# TODO check the receipt and then return true/false
@ -548,3 +556,33 @@ proc addAll*(rlnInstance: RLN[Bn256], list: seq[IDCommitment]): bool =
if not member_is_added:
return false
return true
# the types of inputs to this handler matches the MemberRegistered event/proc defined in the MembershipContract interface
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.} =
## 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)
# prepare a contract sender to interact with it
var contractObj = web3.contractSender(MembershipContract, contractAddress)
# subscribe to the MemberRegistered events
# TODO can do similarly for deletion events, though it is not yet supported
discard await contractObj.subscribe(MemberRegistered, %*{"fromBlock": blockNumber, "address": contractAddress}) do(pubkey: Uint256, index: Uint256){.raises: [Defect], gcsafe.}:
try:
debug "onRegister", pubkey = pubkey, index = index
handler(pubkey, index)
except Exception as err:
doAssert false, err.msg
do (err: CatchableError):
echo "Error from subscription: ", err.msg
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)