chore(rln-relay): integrate waku rln registry (#1943)

This commit is contained in:
Aaryamann Challani 2023-08-25 22:48:52 +05:30 committed by GitHub
parent 32aa1c5b61
commit cc9f8d4254
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 80 additions and 130 deletions

View File

@ -60,26 +60,26 @@ proc uploadRLNContract*(ethClientAddress: string): Future[Address] {.async.} =
debug "hasher address: ", hasherAddress
# encode membership contract inputs to 32 bytes zero-padded
# encode registry contract inputs to 32 bytes zero-padded
let
membershipFeeEncoded = encode(MembershipFee).data
depthEncoded = encode(MerkleTreeDepth.u256).data
hasherAddressEncoded = encode(hasherAddress).data
# this is the contract constructor input
contractInput = membershipFeeEncoded & depthEncoded & hasherAddressEncoded
contractInput = hasherAddressEncoded
debug "encoded membership fee: ", membershipFeeEncoded
debug "encoded depth: ", depthEncoded
debug "encoded hasher address: ", hasherAddressEncoded
debug "encoded contract input:", contractInput
# deploy membership contract with its constructor inputs
let receipt = await web3.deployContract(MembershipContractCode,
contractInput = contractInput)
let contractAddress = receipt.contractAddress.get
debug "Address of the deployed membership contract: ", contractAddress
# deploy registry contract with its constructor inputs
let receipt = await web3.deployContract(RegistryContractCode,
contractInput = contractInput)
let contractAddress = receipt.contractAddress.get()
debug "Address of the deployed registry contract: ", contractAddress
let registryContract = web3.contractSender(WakuRlnRegistry, contractAddress)
let newStorageReceipt = await registryContract.newStorage().send()
debug "Receipt of the newStorage transaction: ", newStorageReceipt
let newBalance = await web3.provider.eth_getBalance(web3.defaultAccount, "latest")
debug "Account balance after the contract deployment: ", newBalance
@ -149,14 +149,11 @@ proc stopGanache(runGanache: Process) {.used.} =
try:
# We terminate Ganache daemon by sending a SIGTERM signal to the runGanache PID to trigger RPC server termination and clean-up
discard startProcess("pkill", args = ["-f", "ganache"], options = {poUsePath})
# NOTE: the below line must remain commented out, otherwise it will cause a deadlocked state
# ref: https://nim-lang.org/docs/osproc.html#waitForExit%2CProcess%2Cint
# debug "ganache logs", logs=runGanache.outputstream.readAll()
debug "Sent SIGTERM to Ganache", ganachePID=ganachePID
except:
error "Ganache daemon termination failed: ", err = getCurrentExceptionMsg()
proc setup(signer = true): Future[OnchainGroupManager] {.async.} =
proc setup(): Future[OnchainGroupManager] {.async.} =
let rlnInstanceRes = createRlnInstance(tree_path = genTempPath("rln_tree", "group_manager_onchain"))
require:
rlnInstanceRes.isOk()
@ -168,18 +165,16 @@ proc setup(signer = true): Future[OnchainGroupManager] {.async.} =
let web3 = await newWeb3(EthClient)
let accounts = await web3.provider.eth_accounts()
web3.defaultAccount = accounts[1]
web3.defaultAccount = accounts[0]
var pk = none(string)
if signer:
let (privateKey, _) = await createEthAccount()
pk = some($privateKey)
let (privateKey, _) = await createEthAccount()
pk = some($privateKey)
let manager = OnchainGroupManager(ethClientUrl: EthClient,
ethContractAddress: $contractAddress,
ethPrivateKey: pk,
rlnInstance: rlnInstance,
saveKeystore: false)
rlnInstance: rlnInstance)
return manager
@ -211,13 +206,12 @@ suite "Onchain group manager":
metadata.contractAddress == manager.ethContractAddress
await manager.stop()
let differentContractAddress = await uploadRLNContract(manager.ethClientUrl)
# simulating a change in the contractAddress
let manager2 = OnchainGroupManager(ethClientUrl: EthClient,
ethContractAddress: "0x0000000000000000000000000000000000000000",
ethPrivateKey: manager.ethPrivateKey,
rlnInstance: manager.rlnInstance,
saveKeystore: false)
ethContractAddress: $differentContractAddress,
rlnInstance: manager.rlnInstance)
expect(ValueError): await manager2.init()
asyncTest "startGroupSync: should start group sync":
@ -234,7 +228,7 @@ suite "Onchain group manager":
asyncTest "startGroupSync: should sync to the state of the group":
let manager = await setup()
let credentials = generateCredentials(manager.rlnInstance)
await manager.init()
let merkleRootBeforeRes = manager.rlnInstance.getMerkleRoot()
@ -248,13 +242,14 @@ suite "Onchain group manager":
proc callback(registrations: seq[Membership]): Future[void] {.async.} =
require:
registrations.len == 1
registrations[0].idCommitment == manager.idCredentials.get().idCommitment
registrations[0].index == 0
registrations[0].idCommitment == credentials.idCommitment
registrations[0].index == 1
fut.complete()
return callback
manager.onRegister(generateCallback(fut))
await manager.register(credentials)
await manager.startGroupSync()
await fut
@ -291,7 +286,6 @@ suite "Onchain group manager":
futs[futureIndex].complete()
futureIndex += 1
return callback
manager.onRegister(generateCallback(futures, credentials))
await manager.startGroupSync()
@ -317,7 +311,7 @@ suite "Onchain group manager":
await manager.register(dummyCommitment)
asyncTest "register: should register successfully":
let manager = await setup(false)
let manager = await setup()
await manager.init()
await manager.startGroupSync()
@ -336,7 +330,7 @@ suite "Onchain group manager":
manager.latestIndex == 1
asyncTest "register: callback is called":
let manager = await setup(false)
let manager = await setup()
let idCommitment = generateCredentials(manager.rlnInstance).idCommitment
@ -366,19 +360,24 @@ suite "Onchain group manager":
asyncTest "validateRoot: should validate good root":
let manager = await setup()
let credentials = generateCredentials(manager.rlnInstance)
await manager.init()
let fut = newFuture[void]()
proc callback(registrations: seq[Membership]): Future[void] {.async.} =
if registrations.len == 1 and
registrations[0].idCommitment == manager.idCredentials.get().idCommitment and
registrations[0].index == 0:
registrations[0].idCommitment == credentials.idCommitment and
registrations[0].index == 1:
manager.idCredentials = some(credentials)
manager.membershipIndex = some(registrations[0].index)
fut.complete()
manager.onRegister(callback)
await manager.startGroupSync()
await manager.register(credentials)
await fut
let messageBytes = "Hello".toBytes()
@ -405,10 +404,10 @@ suite "Onchain group manager":
await manager.init()
await manager.startGroupSync()
let idCredential = generateCredentials(manager.rlnInstance)
let credentials = generateCredentials(manager.rlnInstance)
## Assume the registration occured out of band
manager.idCredentials = some(idCredential)
manager.idCredentials = some(credentials)
manager.membershipIndex = some(MembershipIndex(0))
let messageBytes = "Hello".toBytes()
@ -432,19 +431,23 @@ suite "Onchain group manager":
asyncTest "verifyProof: should verify valid proof":
let manager = await setup()
let credentials = generateCredentials(manager.rlnInstance)
await manager.init()
let fut = newFuture[void]()
proc callback(registrations: seq[Membership]): Future[void] {.async.} =
if registrations.len == 1 and
registrations[0].idCommitment == manager.idCredentials.get().idCommitment and
registrations[0].index == 0:
registrations[0].idCommitment == credentials.idCommitment and
registrations[0].index == 1:
manager.idCredentials = some(credentials)
manager.membershipIndex = some(registrations[0].index)
fut.complete()
manager.onRegister(callback)
await manager.startGroupSync()
await manager.register(credentials)
await fut
let messageBytes = "Hello".toBytes()

View File

@ -62,9 +62,7 @@ when isMainModule:
rlnInstance: rlnInstance,
keystorePath: none(string),
keystorePassword: none(string),
ethPrivateKey: some(conf.rlnRelayEthPrivateKey),
# saveKeystore = false, since we're managing it
saveKeystore: false)
ethPrivateKey: some(conf.rlnRelayEthPrivateKey))
try:
waitFor groupManager.init()
except CatchableError:

File diff suppressed because one or more lines are too long

View File

@ -27,24 +27,30 @@ export group_manager_base
logScope:
topics = "waku rln_relay onchain_group_manager"
contract(WakuRlnRegistry):
proc usingStorageIndex(): Uint16 {.pure.}
proc storages(index: Uint16): Address {.pure.}
proc register(storageIndex: Uint16, idCommitment: Uint256)
proc newStorage()
# membership contract interface
contract(RlnContract):
proc register(idCommitment: Uint256) {.payable.} # external payable
contract(RlnStorage):
proc MemberRegistered(idCommitment: Uint256, index: Uint256) {.event.}
proc MEMBERSHIP_DEPOSIT(): Uint256
# TODO the following 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 MEMBERSHIP_DEPOSIT(): Uint256 {.pure.}
proc members(idCommitment: Uint256): Uint256 {.view.}
proc idCommitmentIndex(): Uint256 {.view.}
type
RlnContractWithSender = Sender[RlnContract]
RegistryContractWithSender = Sender[WakuRlnRegistry]
RlnContractWithSender = Sender[RlnStorage]
OnchainGroupManager* = ref object of GroupManager
ethClientUrl*: string
ethPrivateKey*: Option[string]
ethContractAddress*: string
ethRpc*: Option[Web3]
rlnContract*: Option[RlnContractWithSender]
registryContract*: Option[RegistryContractWithSender]
usingStorageIndex: Option[Uint16]
membershipFee*: Option[Uint256]
latestProcessedBlock*: Option[BlockNumber]
registrationTxHash*: Option[TxHash]
@ -53,7 +59,6 @@ type
keystoreIndex*: uint
membershipGroupIndex*: uint
keystorePassword*: Option[string]
saveKeystore*: bool
registrationHandler*: Option[RegistrationHandler]
# this buffer exists to backfill appropriate roots for the merkle tree,
# in event of a reorg. we store 5 in the buffer. Maybe need to revisit this,
@ -99,7 +104,6 @@ method register*(g: OnchainGroupManager, idCommitment: IDCommitment): Future[voi
await g.registerBatch(@[idCommitment])
method registerBatch*(g: OnchainGroupManager, idCommitments: seq[IDCommitment]): Future[void] {.async.} =
initializedGuard(g)
@ -111,7 +115,7 @@ method register*(g: OnchainGroupManager, identityCredentials: IdentityCredential
initializedGuard(g)
let ethRpc = g.ethRpc.get()
let rlnContract = g.rlnContract.get()
let registryContract = g.registryContract.get()
let membershipFee = g.membershipFee.get()
let gasPrice = int(await ethRpc.provider.eth_gasPrice()) * 2
@ -119,16 +123,16 @@ method register*(g: OnchainGroupManager, identityCredentials: IdentityCredential
var txHash: TxHash
try: # send the registration transaction and check if any error occurs
txHash = await rlnContract.register(idCommitment).send(value = membershipFee,
gasPrice = gasPrice,
gas = 100000'u64)
except ValueError as e:
error "error while registering the member", msg = e.msg
raise newException(ValueError, "could not register the member: " & e.msg)
let storageIndex = g.usingStorageIndex.get()
debug "registering the member", idCommitment = idCommitment, storageIndex = storageIndex
txHash = await registryContract.register(storageIndex, idCommitment).send(gasPrice = gasPrice)
except CatchableError:
error "error while registering the member", msg = getCurrentExceptionMsg()
raise newException(CatchableError, "could not register the member: " & getCurrentExceptionMsg())
# wait for the transaction to be mined
let tsReceipt = await ethRpc.getMinedTransactionReceipt(txHash)
debug "registration transaction mined", txHash = txHash
g.registrationTxHash = some(txHash)
# the receipt topic holds the hash of signature of the raised events
# TODO: make this robust. search within the event list for the event
@ -382,70 +386,13 @@ proc startOnchainSync(g: OnchainGroupManager): Future[void] {.async.} =
except CatchableError:
raise newException(ValueError, "failed to start listening to events: " & getCurrentExceptionMsg())
proc persistCredentials(g: OnchainGroupManager): GroupManagerResult[void] =
if not g.saveKeystore:
return ok()
if g.idCredentials.isNone():
return err("no credentials to persist")
let index = g.membershipIndex.get()
let idCredential = g.idCredentials.get()
var path = DefaultKeystorePath
var password = DefaultKeystorePassword
if g.keystorePath.isSome():
path = g.keystorePath.get()
else:
warn "keystore: no credentials path set, using default path", path=DefaultKeystorePath
if g.keystorePassword.isSome():
password = g.keystorePassword.get()
else:
warn "keystore: no credentials password set, using default password", password=DefaultKeystorePassword
let keystoreCred = MembershipCredentials(
identityCredential: idCredential,
membershipGroups: @[MembershipGroup(
membershipContract: MembershipContract(
chainId: $g.chainId.get(),
address: g.ethContractAddress
),
treeIndex: index
)]
)
let persistRes = addMembershipCredentials(path, @[keystoreCred], password, RLNAppInfo)
if persistRes.isErr():
error "keystore: failed to persist credentials", error=persistRes.error()
return ok()
method startGroupSync*(g: OnchainGroupManager): Future[void] {.async.} =
initializedGuard(g)
# Get archive history
try:
await startOnchainSync(g)
except CatchableError:
raise newException(ValueError, "failed to start onchain sync service: " & getCurrentExceptionMsg())
if g.ethPrivateKey.isSome() and g.idCredentials.isNone():
let idCredentialRes = g.rlnInstance.membershipKeyGen()
if idCredentialRes.isErr():
raise newException(CatchableError, "Identity credential generation failed")
let idCredential = idCredentialRes.get()
g.idCredentials = some(idCredential)
debug "registering commitment on contract"
await g.register(idCredential)
if g.registrationHandler.isSome():
# We need to callback with the tx hash
let handler = g.registrationHandler.get()
handler($g.registrationTxHash.get())
let persistRes = g.persistCredentials()
if persistRes.isErr():
error "failed to persist credentials", error=persistRes.error()
raise newException(CatchableError, "failed to start onchain sync service: " & getCurrentExceptionMsg())
return
method onRegister*(g: OnchainGroupManager, cb: OnRegisterCallback) {.gcsafe.} =
@ -456,7 +403,6 @@ method onWithdraw*(g: OnchainGroupManager, cb: OnWithdrawCallback) {.gcsafe.} =
method init*(g: OnchainGroupManager): Future[void] {.async.} =
var ethRpc: Web3
var contract: RlnContractWithSender
# check if the Ethereum client is reachable
try:
ethRpc = await newWeb3(g.ethClientUrl)
@ -475,12 +421,18 @@ method init*(g: OnchainGroupManager): Future[void] {.async.} =
ethRpc.privateKey = some(pkParseRes.get())
ethRpc.defaultAccount = ethRpc.privateKey.get().toPublicKey().toCanonicalAddress().Address
let registryAddress = web3.fromHex(web3.Address, g.ethContractAddress)
let registryContract = ethRpc.contractSender(WakuRlnRegistry, registryAddress)
let contractAddress = web3.fromHex(web3.Address, g.ethContractAddress)
contract = ethRpc.contractSender(RlnContract, contractAddress)
# get the current storage index
let usingStorageIndex = await registryContract.usingStorageIndex().call()
g.usingStorageIndex = some(usingStorageIndex)
let rlnContractAddress = await registryContract.storages(usingStorageIndex).call()
let rlnContract = ethRpc.contractSender(RlnStorage, rlnContractAddress)
g.ethRpc = some(ethRpc)
g.rlnContract = some(contract)
g.rlnContract = some(rlnContract)
g.registryContract = some(registryContract)
if g.keystorePath.isSome() and g.keystorePassword.isSome():
waku_rln_membership_credentials_import_duration_seconds.nanosecondTime:
@ -513,7 +465,7 @@ method init*(g: OnchainGroupManager): Future[void] {.async.} =
# check if the contract exists by calling a static function
var membershipFee: Uint256
try:
membershipFee = await contract.MEMBERSHIP_DEPOSIT().call()
membershipFee = await rlnContract.MEMBERSHIP_DEPOSIT().call()
except CatchableError:
raise newException(ValueError,
"could not get the membership deposit: " & getCurrentExceptionMsg())

View File

@ -344,7 +344,6 @@ proc mount(conf: WakuRlnConfig,
var
groupManager: GroupManager
credentials: MembershipCredentials
persistCredentials = false
# create an RLN instance
let rlnInstanceRes = createRLNInstance(tree_path = conf.rlnRelayTreePath)
if rlnInstanceRes.isErr():
@ -374,9 +373,7 @@ proc mount(conf: WakuRlnConfig,
keystorePath: rlnRelayCredPath,
keystorePassword: rlnRelayCredentialsPassword,
keystoreIndex: conf.rlnRelayCredIndex,
membershipGroupIndex: conf.rlnRelayMembershipGroupIndex,
saveKeystore: true)
membershipGroupIndex: conf.rlnRelayMembershipGroupIndex)
# Initialize the groupManager
await groupManager.init()
# Start the group sync