From 01634f57f0386efebfcfcb1d6e6c88de345a17e7 Mon Sep 17 00:00:00 2001 From: Aaryamann Challani <43716372+rymnc@users.noreply.github.com> Date: Fri, 25 Aug 2023 23:29:17 +0530 Subject: [PATCH] fix(rln-relay): window of acceptable roots synced to rln metadata (#1953) * fix(rln-relay): window of acceptable roots synced to rln metadata * fix(rln-relay): s/var/let, use for loop --- .../test_rln_group_manager_onchain.nim | 5 ++- .../group_manager/on_chain/group_manager.nim | 38 +++++++++------- waku/waku_rln_relay/rln/wrappers.nim | 45 +++++++++++++++---- 3 files changed, 63 insertions(+), 25 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 47efcae0a..797cce0a3 100644 --- a/tests/waku_rln_relay/test_rln_group_manager_onchain.nim +++ b/tests/waku_rln_relay/test_rln_group_manager_onchain.nim @@ -6,7 +6,7 @@ else: {.push raises: [].} import - std/[options, osproc, streams, strutils, tempfiles], + std/[options, osproc, sequtils, deques, streams, strutils, tempfiles], stew/[results, byteutils], stew/shims/net as stewNet, testutils/unittests, @@ -351,6 +351,9 @@ suite "Onchain group manager": await fut + check: + manager.rlnInstance.getMetadata().get().validRoots == manager.validRoots.toSeq() + asyncTest "withdraw: should guard against uninitialized state": let manager = await setup() let idSecretHash = generateCredentials(manager.rlnInstance).idSecretHash 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 b3c5cefed..4ca571c2a 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 @@ -75,6 +75,22 @@ template initializedGuard(g: OnchainGroupManager): untyped = if not g.initialized: raise newException(ValueError, "OnchainGroupManager is not initialized") + +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, + validRoots: g.validRoots.toSeq())) + if metadataSetRes.isErr(): + return err("failed to persist rln metadata: " & metadataSetRes.error) + except CatchableError: + return err("failed to persist rln metadata: " & getCurrentExceptionMsg()) + return ok() + method atomicBatch*(g: OnchainGroupManager, start: MembershipIndex, idCommitments = newSeq[IDCommitment](), @@ -91,12 +107,15 @@ method atomicBatch*(g: OnchainGroupManager, var membersSeq = newSeq[Membership]() for i in 0 ..< idCommitments.len(): var index = start + MembershipIndex(i) - debug "registering member", idCommitment = idCommitments[i], index = index + trace "registering member", idCommitment = idCommitments[i], index = index let member = Membership(idCommitment: idCommitments[i], index: index) membersSeq.add(member) await g.registerCb.get()(membersSeq) g.validRootBuffer = g.slideRootQueue() + let setMetadataRes = g.setMetadata() + if setMetadataRes.isErr(): + error "failed to persist rln metadata", error=setMetadataRes.error method register*(g: OnchainGroupManager, idCommitment: IDCommitment): Future[void] {.async.} = initializedGuard(g) @@ -286,20 +305,6 @@ 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.} = @@ -317,7 +322,7 @@ proc getAndHandleEvents(g: OnchainGroupManager, # this is not a fatal error, hence we don't raise an exception warn "failed to persist rln metadata", error=metadataSetRes.error() else: - debug "rln metadata persisted", blockNumber = latestProcessedBlock + trace "rln metadata persisted", blockNumber = latestProcessedBlock proc getNewHeadCallback(g: OnchainGroupManager): BlockHeaderHandler = proc newHeadCallback(blockheader: BlockHeader) {.gcsafe.} = @@ -461,6 +466,7 @@ method init*(g: OnchainGroupManager): Future[void] {.async.} = if metadata.contractAddress != g.ethContractAddress.toLower(): raise newException(ValueError, "persisted data: contract address mismatch") g.latestProcessedBlock = some(metadata.lastProcessedBlock) + g.validRoots = metadata.validRoots.toDeque() # check if the contract exists by calling a static function var membershipFee: Uint256 diff --git a/waku/waku_rln_relay/rln/wrappers.nim b/waku/waku_rln_relay/rln/wrappers.nim index d92f716ee..0edbcced7 100644 --- a/waku/waku_rln_relay/rln/wrappers.nim +++ b/waku/waku_rln_relay/rln/wrappers.nim @@ -353,13 +353,36 @@ type lastProcessedBlock*: uint64 chainId*: uint64 contractAddress*: string + validRoots*: seq[MerkleNode] proc serialize(metadata: RlnMetadata): seq[byte] = ## serializes the metadata ## returns the serialized metadata return concat(@(metadata.lastProcessedBlock.toBytes()), @(metadata.chainId.toBytes()), - @(hexToSeqByte(toLower(metadata.contractAddress)))) + @(hexToSeqByte(toLower(metadata.contractAddress))), + @(uint64(metadata.validRoots.len()).toBytes()), + @(serialize(metadata.validRoots))) + +type MerkleNodeSeq = seq[MerkleNode] + +proc deserialize*(T: type MerkleNodeSeq, merkleNodeByteSeq: seq[byte]): T = + ## deserializes a byte seq to a seq of MerkleNodes + ## the order of serialization is |merkle_node_len<8>|merkle_node[len]| + + var roots = newSeq[MerkleNode]() + var i = 1'u64 + let len = uint64.fromBytes(merkleNodeByteSeq[0..7], Endianness.littleEndian) + trace "length of valid roots", len + let offset = 8'u64 + for i in 1'u64..len: + # convert seq[byte] to array[32, byte] + let rawRoot = merkleNodeByteSeq[offset*i .. offset*i + 31] + trace "raw root", rawRoot = rawRoot + var root: MerkleNode + discard root.copyFrom(rawRoot) + roots.add(root) + return roots proc setMetadata*(rlnInstance: ptr RLN, metadata: RlnMetadata): RlnRelayResult[void] = ## sets the metadata of the RLN instance @@ -368,6 +391,7 @@ proc setMetadata*(rlnInstance: ptr RLN, metadata: RlnMetadata): RlnRelayResult[v # serialize the metadata let metadataBytes = serialize(metadata) + trace "setting metadata", metadata = metadata, metadataBytes = metadataBytes, len = metadataBytes.len var metadataBuffer = metadataBytes.toBuffer() let metadataBufferPtr = addr metadataBuffer @@ -390,26 +414,31 @@ 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 == 36: - return err("wrong output size") + trace "metadata length", metadataLen = metadata.len let lastProcessedBlockOffset = 0 chainIdOffset = lastProcessedBlockOffset + 8 contractAddressOffset = chainIdOffset + 8 - + validRootsOffset = contractAddressOffset + 20 + var lastProcessedBlock: uint64 chainId: uint64 contractAddress: string + validRoots: MerkleNodeSeq - var metadataValue = cast[ptr array[36, byte]] (metadata.`ptr`) - let metadataBytes: array[36, byte] = metadataValue[] + # 8 + 8 + 20 + 8 + (5*32) = 204 + var metadataBytes = cast[ptr array[204, byte]](metadata.`ptr`)[] + trace "received metadata bytes", metadataBytes = metadataBytes, len = metadataBytes.len lastProcessedBlock = uint64.fromBytes(metadataBytes[lastProcessedBlockOffset..chainIdOffset-1]) chainId = uint64.fromBytes(metadataBytes[chainIdOffset..contractAddressOffset-1]) - contractAddress = byteutils.toHex(metadataBytes[contractAddressOffset..metadataBytes.high]) + contractAddress = byteutils.toHex(metadataBytes[contractAddressOffset..validRootsOffset - 1]) + let validRootsBytes = metadataBytes[validRootsOffset..metadataBytes.high] + validRoots = MerkleNodeSeq.deserialize(validRootsBytes) return ok(RlnMetadata(lastProcessedBlock: lastProcessedBlock, chainId: chainId, - contractAddress: "0x" & contractAddress)) + contractAddress: "0x" & contractAddress, + validRoots: validRoots))