mirror of https://github.com/waku-org/nwaku.git
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
This commit is contained in:
parent
cc9f8d4254
commit
01634f57f0
|
@ -6,7 +6,7 @@ else:
|
||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
import
|
import
|
||||||
std/[options, osproc, streams, strutils, tempfiles],
|
std/[options, osproc, sequtils, deques, streams, strutils, tempfiles],
|
||||||
stew/[results, byteutils],
|
stew/[results, byteutils],
|
||||||
stew/shims/net as stewNet,
|
stew/shims/net as stewNet,
|
||||||
testutils/unittests,
|
testutils/unittests,
|
||||||
|
@ -351,6 +351,9 @@ suite "Onchain group manager":
|
||||||
|
|
||||||
await fut
|
await fut
|
||||||
|
|
||||||
|
check:
|
||||||
|
manager.rlnInstance.getMetadata().get().validRoots == manager.validRoots.toSeq()
|
||||||
|
|
||||||
asyncTest "withdraw: should guard against uninitialized state":
|
asyncTest "withdraw: should guard against uninitialized state":
|
||||||
let manager = await setup()
|
let manager = await setup()
|
||||||
let idSecretHash = generateCredentials(manager.rlnInstance).idSecretHash
|
let idSecretHash = generateCredentials(manager.rlnInstance).idSecretHash
|
||||||
|
|
|
@ -75,6 +75,22 @@ template initializedGuard(g: OnchainGroupManager): untyped =
|
||||||
if not g.initialized:
|
if not g.initialized:
|
||||||
raise newException(ValueError, "OnchainGroupManager is not 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,
|
method atomicBatch*(g: OnchainGroupManager,
|
||||||
start: MembershipIndex,
|
start: MembershipIndex,
|
||||||
idCommitments = newSeq[IDCommitment](),
|
idCommitments = newSeq[IDCommitment](),
|
||||||
|
@ -91,12 +107,15 @@ method atomicBatch*(g: OnchainGroupManager,
|
||||||
var membersSeq = newSeq[Membership]()
|
var membersSeq = newSeq[Membership]()
|
||||||
for i in 0 ..< idCommitments.len():
|
for i in 0 ..< idCommitments.len():
|
||||||
var index = start + MembershipIndex(i)
|
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)
|
let member = Membership(idCommitment: idCommitments[i], index: index)
|
||||||
membersSeq.add(member)
|
membersSeq.add(member)
|
||||||
await g.registerCb.get()(membersSeq)
|
await g.registerCb.get()(membersSeq)
|
||||||
|
|
||||||
g.validRootBuffer = g.slideRootQueue()
|
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.} =
|
method register*(g: OnchainGroupManager, idCommitment: IDCommitment): Future[void] {.async.} =
|
||||||
initializedGuard(g)
|
initializedGuard(g)
|
||||||
|
@ -286,20 +305,6 @@ proc handleRemovedEvents(g: OnchainGroupManager, blockTable: BlockTable): Future
|
||||||
|
|
||||||
await g.backfillRootQueue(numRemovedBlocks)
|
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,
|
proc getAndHandleEvents(g: OnchainGroupManager,
|
||||||
fromBlock: BlockNumber,
|
fromBlock: BlockNumber,
|
||||||
toBlock: Option[BlockNumber] = none(BlockNumber)): Future[void] {.async.} =
|
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
|
# this is not a fatal error, hence we don't raise an exception
|
||||||
warn "failed to persist rln metadata", error=metadataSetRes.error()
|
warn "failed to persist rln metadata", error=metadataSetRes.error()
|
||||||
else:
|
else:
|
||||||
debug "rln metadata persisted", blockNumber = latestProcessedBlock
|
trace "rln metadata persisted", blockNumber = latestProcessedBlock
|
||||||
|
|
||||||
proc getNewHeadCallback(g: OnchainGroupManager): BlockHeaderHandler =
|
proc getNewHeadCallback(g: OnchainGroupManager): BlockHeaderHandler =
|
||||||
proc newHeadCallback(blockheader: BlockHeader) {.gcsafe.} =
|
proc newHeadCallback(blockheader: BlockHeader) {.gcsafe.} =
|
||||||
|
@ -461,6 +466,7 @@ method init*(g: OnchainGroupManager): Future[void] {.async.} =
|
||||||
if metadata.contractAddress != g.ethContractAddress.toLower():
|
if metadata.contractAddress != g.ethContractAddress.toLower():
|
||||||
raise newException(ValueError, "persisted data: contract address mismatch")
|
raise newException(ValueError, "persisted data: contract address mismatch")
|
||||||
g.latestProcessedBlock = some(metadata.lastProcessedBlock)
|
g.latestProcessedBlock = some(metadata.lastProcessedBlock)
|
||||||
|
g.validRoots = metadata.validRoots.toDeque()
|
||||||
|
|
||||||
# check if the contract exists by calling a static function
|
# check if the contract exists by calling a static function
|
||||||
var membershipFee: Uint256
|
var membershipFee: Uint256
|
||||||
|
|
|
@ -353,13 +353,36 @@ type
|
||||||
lastProcessedBlock*: uint64
|
lastProcessedBlock*: uint64
|
||||||
chainId*: uint64
|
chainId*: uint64
|
||||||
contractAddress*: string
|
contractAddress*: string
|
||||||
|
validRoots*: seq[MerkleNode]
|
||||||
|
|
||||||
proc serialize(metadata: RlnMetadata): seq[byte] =
|
proc serialize(metadata: RlnMetadata): seq[byte] =
|
||||||
## serializes the metadata
|
## serializes the metadata
|
||||||
## returns the serialized metadata
|
## returns the serialized metadata
|
||||||
return concat(@(metadata.lastProcessedBlock.toBytes()),
|
return concat(@(metadata.lastProcessedBlock.toBytes()),
|
||||||
@(metadata.chainId.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] =
|
proc setMetadata*(rlnInstance: ptr RLN, metadata: RlnMetadata): RlnRelayResult[void] =
|
||||||
## sets the metadata of the RLN instance
|
## sets the metadata of the RLN instance
|
||||||
|
@ -368,6 +391,7 @@ proc setMetadata*(rlnInstance: ptr RLN, metadata: RlnMetadata): RlnRelayResult[v
|
||||||
|
|
||||||
# serialize the metadata
|
# serialize the metadata
|
||||||
let metadataBytes = serialize(metadata)
|
let metadataBytes = serialize(metadata)
|
||||||
|
trace "setting metadata", metadata = metadata, metadataBytes = metadataBytes, len = metadataBytes.len
|
||||||
var metadataBuffer = metadataBytes.toBuffer()
|
var metadataBuffer = metadataBytes.toBuffer()
|
||||||
let metadataBufferPtr = addr metadataBuffer
|
let metadataBufferPtr = addr metadataBuffer
|
||||||
|
|
||||||
|
@ -390,26 +414,31 @@ proc getMetadata*(rlnInstance: ptr RLN): RlnRelayResult[RlnMetadata] =
|
||||||
getMetadataSuccessful = get_metadata(rlnInstance, metadataPtr)
|
getMetadataSuccessful = get_metadata(rlnInstance, metadataPtr)
|
||||||
if not getMetadataSuccessful:
|
if not getMetadataSuccessful:
|
||||||
return err("could not get the metadata")
|
return err("could not get the metadata")
|
||||||
if not metadata.len == 36:
|
trace "metadata length", metadataLen = metadata.len
|
||||||
return err("wrong output size")
|
|
||||||
|
|
||||||
let
|
let
|
||||||
lastProcessedBlockOffset = 0
|
lastProcessedBlockOffset = 0
|
||||||
chainIdOffset = lastProcessedBlockOffset + 8
|
chainIdOffset = lastProcessedBlockOffset + 8
|
||||||
contractAddressOffset = chainIdOffset + 8
|
contractAddressOffset = chainIdOffset + 8
|
||||||
|
validRootsOffset = contractAddressOffset + 20
|
||||||
|
|
||||||
var
|
var
|
||||||
lastProcessedBlock: uint64
|
lastProcessedBlock: uint64
|
||||||
chainId: uint64
|
chainId: uint64
|
||||||
contractAddress: string
|
contractAddress: string
|
||||||
|
validRoots: MerkleNodeSeq
|
||||||
|
|
||||||
var metadataValue = cast[ptr array[36, byte]] (metadata.`ptr`)
|
# 8 + 8 + 20 + 8 + (5*32) = 204
|
||||||
let metadataBytes: array[36, byte] = metadataValue[]
|
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])
|
lastProcessedBlock = uint64.fromBytes(metadataBytes[lastProcessedBlockOffset..chainIdOffset-1])
|
||||||
chainId = uint64.fromBytes(metadataBytes[chainIdOffset..contractAddressOffset-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,
|
return ok(RlnMetadata(lastProcessedBlock: lastProcessedBlock,
|
||||||
chainId: chainId,
|
chainId: chainId,
|
||||||
contractAddress: "0x" & contractAddress))
|
contractAddress: "0x" & contractAddress,
|
||||||
|
validRoots: validRoots))
|
||||||
|
|
Loading…
Reference in New Issue