From 1ae5b5a951ed5f94fa46e1b4a651795da09cbab0 Mon Sep 17 00:00:00 2001 From: Aaryamann Challani <43716372+rymnc@users.noreply.github.com> Date: Wed, 23 Aug 2023 18:23:30 +0530 Subject: [PATCH] fix(rln-relay): RLN DB should be aware of chain and contract address (#1932) --- .../test_rln_group_manager_onchain.nim | 23 ++++++++++ tests/waku_rln_relay/test_waku_rln_relay.nim | 11 +++-- .../group_manager/on_chain/group_manager.nim | 43 +++++++++++++------ waku/waku_rln_relay/rln/wrappers.nim | 38 +++++++++++++--- 4 files changed, 93 insertions(+), 22 deletions(-) diff --git a/tests/waku_rln_relay/test_rln_group_manager_onchain.nim b/tests/waku_rln_relay/test_rln_group_manager_onchain.nim index 3ea705d51..fe1dd2a6f 100644 --- a/tests/waku_rln_relay/test_rln_group_manager_onchain.nim +++ b/tests/waku_rln_relay/test_rln_group_manager_onchain.nim @@ -197,6 +197,29 @@ suite "Onchain group manager": manager.membershipFee.isSome() manager.initialized + asyncTest "should error on initialization when loaded metadata does not match": + let manager = await setup() + await manager.init() + + let metadataSetRes = manager.setMetadata() + assert metadataSetRes.isOk(), metadataSetRes.error + let metadataRes = manager.rlnInstance.getMetadata() + assert metadataRes.isOk(), metadataRes.error + let metadata = metadataRes.get() + require: + metadata.chainId == 1337 + metadata.contractAddress == manager.ethContractAddress + + await manager.stop() + + # simulating a change in the contractAddress + let manager2 = OnchainGroupManager(ethClientUrl: EthClient, + ethContractAddress: "0x0000000000000000000000000000000000000000", + ethPrivateKey: manager.ethPrivateKey, + rlnInstance: manager.rlnInstance, + saveKeystore: false) + expect(ValueError): await manager2.init() + asyncTest "startGroupSync: should start group sync": let manager = await setup() diff --git a/tests/waku_rln_relay/test_waku_rln_relay.nim b/tests/waku_rln_relay/test_waku_rln_relay.nim index cf67de02a..4797ee92c 100644 --- a/tests/waku_rln_relay/test_waku_rln_relay.nim +++ b/tests/waku_rln_relay/test_waku_rln_relay.nim @@ -245,7 +245,9 @@ suite "Waku rln relay": rlnInstance.isOk() let rln = rlnInstance.get() check: - rln.setMetadata(RlnMetadata(lastProcessedBlock: 128)).isOk() + rln.setMetadata(RlnMetadata(lastProcessedBlock: 128, + chainId: 1155511, + contractAddress: "0x9c09146844c1326c2dbc41c451766c7138f88155")).isOk() test "getMetadata rln utils": # create an RLN instance which also includes an empty Merkle tree @@ -255,7 +257,9 @@ suite "Waku rln relay": let rln = rlnInstance.get() require: - rln.setMetadata(RlnMetadata(lastProcessedBlock: 128)).isOk() + rln.setMetadata(RlnMetadata(lastProcessedBlock: 128, + chainId: 1155511, + contractAddress: "0x9c09146844c1326c2dbc41c451766c7138f88155")).isOk() let metadataRes = rln.getMetadata() @@ -266,7 +270,8 @@ suite "Waku rln relay": check: metadata.lastProcessedBlock == 128 - + metadata.chainId == 1155511 + metadata.contractAddress == "0x9c09146844c1326c2dbc41c451766c7138f88155" test "Merkle tree consistency check between deletion and insertion": # create an RLN instance diff --git a/waku/waku_rln_relay/group_manager/on_chain/group_manager.nim b/waku/waku_rln_relay/group_manager/on_chain/group_manager.nim index ae404d142..a0f146aeb 100644 --- a/waku/waku_rln_relay/group_manager/on_chain/group_manager.nim +++ b/waku/waku_rln_relay/group_manager/on_chain/group_manager.nim @@ -12,7 +12,8 @@ import json, std/tables, stew/[byteutils, arrayops], - sequtils + sequtils, + strutils import ../../../waku_keystore, ../../rln, @@ -281,6 +282,20 @@ proc handleRemovedEvents(g: OnchainGroupManager, blockTable: BlockTable): Future await g.backfillRootQueue(numRemovedBlocks) +proc setMetadata*(g: OnchainGroupManager): RlnRelayResult[void] = + if g.latestProcessedBlock.isNone(): + return err("latest processed block is not set") + try: + let metadataSetRes = g.rlnInstance.setMetadata(RlnMetadata( + lastProcessedBlock: g.latestProcessedBlock.get(), + chainId: uint64(g.chainId.get()), + contractAddress: g.ethContractAddress)) + if metadataSetRes.isErr(): + return err("failed to persist rln metadata: " & metadataSetRes.error()) + except CatchableError: + return err("failed to persist rln metadata: " & getCurrentExceptionMsg()) + return ok() + proc getAndHandleEvents(g: OnchainGroupManager, fromBlock: BlockNumber, toBlock: Option[BlockNumber] = none(BlockNumber)): Future[void] {.async.} = @@ -293,8 +308,7 @@ proc getAndHandleEvents(g: OnchainGroupManager, let latestProcessedBlock = if toBlock.isSome(): toBlock.get() else: fromBlock g.latestProcessedBlock = some(latestProcessedBlock) - let metadataSetRes = g.rlnInstance.setMetadata(RlnMetadata( - lastProcessedBlock: latestProcessedBlock)) + let metadataSetRes = g.setMetadata() if metadataSetRes.isErr(): # this is not a fatal error, hence we don't raise an exception warn "failed to persist rln metadata", error=metadataSetRes.error() @@ -465,17 +479,8 @@ method init*(g: OnchainGroupManager): Future[void] {.async.} = let contractAddress = web3.fromHex(web3.Address, g.ethContractAddress) contract = ethRpc.contractSender(RlnContract, contractAddress) - # check if the contract exists by calling a static function - var membershipFee: Uint256 - try: - membershipFee = await contract.MEMBERSHIP_DEPOSIT().call() - except CatchableError: - raise newException(ValueError, "could not get the membership deposit: {}") - - g.ethRpc = some(ethRpc) g.rlnContract = some(contract) - g.membershipFee = some(membershipFee) if g.keystorePath.isSome() and g.keystorePassword.isSome(): waku_rln_membership_credentials_import_duration_seconds.nanosecondTime: @@ -498,8 +503,22 @@ method init*(g: OnchainGroupManager): Future[void] {.async.} = g.latestProcessedBlock = some(BlockNumber(0)) else: let metadata = metadataGetRes.get() + if metadata.chainId != uint64(g.chainId.get()): + raise newException(ValueError, "persisted data: chain id mismatch") + + if metadata.contractAddress != g.ethContractAddress.toLower(): + raise newException(ValueError, "persisted data: contract address mismatch") g.latestProcessedBlock = some(metadata.lastProcessedBlock) + # check if the contract exists by calling a static function + var membershipFee: Uint256 + try: + membershipFee = await contract.MEMBERSHIP_DEPOSIT().call() + except CatchableError: + raise newException(ValueError, + "could not get the membership deposit: " & getCurrentExceptionMsg()) + g.membershipFee = some(membershipFee) + ethRpc.ondisconnect = proc() = error "Ethereum client disconnected" let fromBlock = g.latestProcessedBlock.get() diff --git a/waku/waku_rln_relay/rln/wrappers.nim b/waku/waku_rln_relay/rln/wrappers.nim index 1b9c53b53..d9798be1f 100644 --- a/waku/waku_rln_relay/rln/wrappers.nim +++ b/waku/waku_rln_relay/rln/wrappers.nim @@ -3,7 +3,9 @@ import import chronicles, options, - stew/[arrayops, results], + eth/keys, + stew/[arrayops, byteutils, results, endians2], + std/[sequtils, strformat, strutils, tables], nimcrypto/utils import @@ -120,7 +122,6 @@ proc createRLNInstance*(d = MerkleTreeDepth, proc sha256*(data: openArray[byte]): RlnRelayResult[MerkleNode] = ## a thin layer on top of the Nim wrapper of the sha256 hasher - trace "sha256 hash input", hashhex = data.toHex() var lenPrefData = encodeLengthPrefix(data) var hashInputBuffer = lenPrefData.toBuffer() @@ -247,7 +248,7 @@ proc proofVerify*(rlnInstance: ptr RLN, rootsBytes = serialize(validRoots) rootsBuffer = rootsBytes.toBuffer() - trace "serialized proof", proof = proofBytes.toHex() + trace "serialized proof", proof = byteutils.toHex(proofBytes) let verifyIsSuccessful = verify_with_roots(rlnInstance, addr proofBuffer, addr rootsBuffer, addr validProof) if not verifyIsSuccessful: @@ -350,11 +351,15 @@ proc getMerkleRoot*(rlnInstance: ptr RLN): MerkleNodeResult = type RlnMetadata* = object lastProcessedBlock*: uint64 + chainId*: uint64 + contractAddress*: string proc serialize(metadata: RlnMetadata): seq[byte] = ## serializes the metadata ## returns the serialized metadata - return @(metadata.lastProcessedBlock.toBytes()) + return concat(@(metadata.lastProcessedBlock.toBytes()), + @(metadata.chainId.toBytes()), + @(hexToSeqByte(toLower(metadata.contractAddress)))) proc setMetadata*(rlnInstance: ptr RLN, metadata: RlnMetadata): RlnRelayResult[void] = ## sets the metadata of the RLN instance @@ -368,6 +373,7 @@ proc setMetadata*(rlnInstance: ptr RLN, metadata: RlnMetadata): RlnRelayResult[v # set the metadata let metadataSet = set_metadata(rlnInstance, metadataBufferPtr) + if not metadataSet: return err("could not set the metadata") return ok() @@ -384,8 +390,26 @@ proc getMetadata*(rlnInstance: ptr RLN): RlnRelayResult[RlnMetadata] = getMetadataSuccessful = get_metadata(rlnInstance, metadataPtr) if not getMetadataSuccessful: return err("could not get the metadata") - if not metadata.len == 8: + if not metadata.len == 36: return err("wrong output size") - var metadataValue = cast[ptr uint64] (metadata.`ptr`)[] - return ok(RlnMetadata(lastProcessedBlock: metadataValue)) + let + lastProcessedBlockOffset = 0 + chainIdOffset = lastProcessedBlockOffset + 8 + contractAddressOffset = chainIdOffset + 8 + + var + lastProcessedBlock: uint64 + chainId: uint64 + contractAddress: string + + var metadataValue = cast[ptr array[36, byte]] (metadata.`ptr`) + let metadataBytes: array[36, byte] = metadataValue[] + + lastProcessedBlock = uint64.fromBytes(metadataBytes[lastProcessedBlockOffset..chainIdOffset-1]) + chainId = uint64.fromBytes(metadataBytes[chainIdOffset..contractAddressOffset-1]) + contractAddress = byteutils.toHex(metadataBytes[contractAddressOffset..metadataBytes.high]) + + return ok(RlnMetadata(lastProcessedBlock: lastProcessedBlock, + chainId: chainId, + contractAddress: "0x" & contractAddress))