mirror of
https://github.com/waku-org/nwaku.git
synced 2025-02-24 12:58:35 +00:00
feat(rln-relay-v2): rln-keystore-generator updates (#2392)
* chore: init rln-v2 in OnchainGroupManager * chore: update wrappers * fix: units for userMessageLimit * valueOr for error handling * fix: len usage
This commit is contained in:
parent
a81092e952
commit
2d46c35117
@ -81,6 +81,11 @@ type
|
||||
desc: "Private key for broadcasting transactions",
|
||||
defaultValue: "",
|
||||
name: "rln-relay-eth-private-key" }: string
|
||||
|
||||
rlnRelayUserMessageLimit* {.
|
||||
desc: "Set a user message limit for the rln membership registration. Must be a positive integer. Default is 1.",
|
||||
defaultValue: 1,
|
||||
name: "rln-relay-user-message-limit" .}: uint64
|
||||
|
||||
maxMessageSize* {.
|
||||
desc: "Maximum message size. Accepted units: KiB, KB, and B. e.g. 1024KiB; 1500 B; etc."
|
||||
|
@ -63,7 +63,10 @@ proc doRlnKeystoreGenerator*(conf: WakuNodeConf) =
|
||||
|
||||
# 5. register on-chain
|
||||
try:
|
||||
waitFor groupManager.register(credential)
|
||||
when defined(rln_v2):
|
||||
waitFor groupManager.register(credential, conf.rlnRelayUserMessageLimit)
|
||||
else:
|
||||
waitFor groupManager.register(credential)
|
||||
except Exception, CatchableError:
|
||||
error "failure while registering credentials on-chain", error=getCurrentExceptionMsg()
|
||||
quit(1)
|
||||
@ -73,16 +76,29 @@ proc doRlnKeystoreGenerator*(conf: WakuNodeConf) =
|
||||
info "Your membership has been registered on-chain.", chainId = $groupManager.chainId.get(),
|
||||
contractAddress = conf.rlnRelayEthContractAddress,
|
||||
membershipIndex = groupManager.membershipIndex.get()
|
||||
when defined(rln_v2):
|
||||
info "Your user message limit is", userMessageLimit = conf.rlnRelayUserMessageLimit
|
||||
|
||||
# 6. write to keystore
|
||||
let keystoreCred = KeystoreMembership(
|
||||
membershipContract: MembershipContract(
|
||||
chainId: $groupManager.chainId.get(),
|
||||
address: conf.rlnRelayEthContractAddress,
|
||||
),
|
||||
treeIndex: groupManager.membershipIndex.get(),
|
||||
identityCredential: credential,
|
||||
)
|
||||
when defined(rln_v2):
|
||||
let keystoreCred = KeystoreMembership(
|
||||
membershipContract: MembershipContract(
|
||||
chainId: $groupManager.chainId.get(),
|
||||
address: conf.rlnRelayEthContractAddress,
|
||||
),
|
||||
treeIndex: groupManager.membershipIndex.get(),
|
||||
identityCredential: credential,
|
||||
userMessageLimit: conf.rlnRelayUserMessageLimit,
|
||||
)
|
||||
else:
|
||||
let keystoreCred = KeystoreMembership(
|
||||
membershipContract: MembershipContract(
|
||||
chainId: $groupManager.chainId.get(),
|
||||
address: conf.rlnRelayEthContractAddress,
|
||||
),
|
||||
treeIndex: groupManager.membershipIndex.get(),
|
||||
identityCredential: credential,
|
||||
)
|
||||
|
||||
let persistRes = addMembershipCredentials(conf.rlnRelayCredPath,
|
||||
keystoreCred,
|
||||
|
@ -42,8 +42,7 @@ proc toIDCommitment*(idCommitmentUint: UInt256): IDCommitment =
|
||||
type MembershipIndex* = uint
|
||||
|
||||
proc toMembershipIndex*(v: UInt256): MembershipIndex =
|
||||
let membershipIndex: MembershipIndex = cast[MembershipIndex](v)
|
||||
return membershipIndex
|
||||
return cast[MembershipIndex](v)
|
||||
|
||||
# Converts a sequence of tuples containing 4 string (i.e. identity trapdoor, nullifier, secret hash and commitment) to an IndentityCredential
|
||||
type RawMembershipCredentials* = (string, string, string, string)
|
||||
@ -93,18 +92,35 @@ type KeystoreMembership* = ref object of RootObj
|
||||
membershipContract*: MembershipContract
|
||||
treeIndex*: MembershipIndex
|
||||
identityCredential*: IdentityCredential
|
||||
when defined(rln_v2):
|
||||
userMessageLimit*: uint64
|
||||
|
||||
proc `$`*(m: KeystoreMembership): string =
|
||||
return "KeystoreMembership(chainId: " & m.membershipContract.chainId & ", contractAddress: " & m.membershipContract.address & ", treeIndex: " & $m.treeIndex & ", identityCredential: " & $m.identityCredential & ")"
|
||||
when defined(rln_v2):
|
||||
proc `$`*(m: KeystoreMembership): string =
|
||||
return "KeystoreMembership(chainId: " & m.membershipContract.chainId & ", contractAddress: " & m.membershipContract.address & ", treeIndex: " & $m.treeIndex & ", userMessageLimit: " & $m.userMessageLimit & ", identityCredential: " & $m.identityCredential & ")"
|
||||
else:
|
||||
proc `$`*(m: KeystoreMembership): string =
|
||||
return "KeystoreMembership(chainId: " & m.membershipContract.chainId & ", contractAddress: " & m.membershipContract.address & ", treeIndex: " & $m.treeIndex & ", identityCredential: " & $m.identityCredential & ")"
|
||||
|
||||
proc `==`*(x, y: KeystoreMembership): bool =
|
||||
return x.membershipContract.chainId == y.membershipContract.chainId and
|
||||
x.membershipContract.address == y.membershipContract.address and
|
||||
x.treeIndex == y.treeIndex and
|
||||
x.identityCredential.idTrapdoor == y.identityCredential.idTrapdoor and
|
||||
x.identityCredential.idNullifier == y.identityCredential.idNullifier and
|
||||
x.identityCredential.idSecretHash == y.identityCredential.idSecretHash and
|
||||
x.identityCredential.idCommitment == y.identityCredential.idCommitment
|
||||
when defined(rln_v2):
|
||||
proc `==`*(x, y: KeystoreMembership): bool =
|
||||
return x.membershipContract.chainId == y.membershipContract.chainId and
|
||||
x.membershipContract.address == y.membershipContract.address and
|
||||
x.treeIndex == y.treeIndex and
|
||||
x.userMessageLimit == y.userMessageLimit and
|
||||
x.identityCredential.idTrapdoor == y.identityCredential.idTrapdoor and
|
||||
x.identityCredential.idNullifier == y.identityCredential.idNullifier and
|
||||
x.identityCredential.idSecretHash == y.identityCredential.idSecretHash and
|
||||
x.identityCredential.idCommitment == y.identityCredential.idCommitment
|
||||
else:
|
||||
proc `==`*(x, y: KeystoreMembership): bool =
|
||||
return x.membershipContract.chainId == y.membershipContract.chainId and
|
||||
x.membershipContract.address == y.membershipContract.address and
|
||||
x.treeIndex == y.treeIndex and
|
||||
x.identityCredential.idTrapdoor == y.identityCredential.idTrapdoor and
|
||||
x.identityCredential.idNullifier == y.identityCredential.idNullifier and
|
||||
x.identityCredential.idSecretHash == y.identityCredential.idSecretHash and
|
||||
x.identityCredential.idCommitment == y.identityCredential.idCommitment
|
||||
|
||||
proc hash*(m: KeystoreMembership): string =
|
||||
# hash together the chainId, address and treeIndex
|
||||
|
@ -41,6 +41,10 @@ proc inHex*(value: IdentityTrapdoor or
|
||||
valueHex = "0" & valueHex
|
||||
return toLowerAscii(valueHex)
|
||||
|
||||
when defined(rln_v2):
|
||||
proc toUserMessageLimit*(v: UInt256): UserMessageLimit =
|
||||
return cast[UserMessageLimit](v)
|
||||
|
||||
proc encodeLengthPrefix*(input: openArray[byte]): seq[byte] =
|
||||
## returns length prefixed version of the input
|
||||
## with the following format [len<8>|input<var>]
|
||||
|
@ -23,8 +23,11 @@ export
|
||||
# It should also be used to sync the group state with the rest of the group members
|
||||
|
||||
type Membership* = object
|
||||
idCommitment*: IDCommitment
|
||||
index*: MembershipIndex
|
||||
when defined(rln_v2):
|
||||
rateCommitment*: RateCommitment
|
||||
else:
|
||||
idCommitment*: IDCommitment
|
||||
|
||||
type OnRegisterCallback* = proc (registrations: seq[Membership]): Future[void] {.gcsafe.}
|
||||
type OnWithdrawCallback* = proc (withdrawals: seq[Membership]): Future[void] {.gcsafe.}
|
||||
@ -41,6 +44,8 @@ type
|
||||
initialized*: bool
|
||||
latestIndex*: MembershipIndex
|
||||
validRoots*: Deque[MerkleNode]
|
||||
when defined(rln_v2):
|
||||
userMessageLimit*: Option[UserMessageLimit]
|
||||
|
||||
# This proc is used to initialize the group manager
|
||||
# Any initialization logic should be implemented here
|
||||
@ -55,20 +60,35 @@ method startGroupSync*(g: GroupManager): Future[void] {.base, async: (raises: [E
|
||||
# This proc is used to register a new identity commitment into the merkle tree
|
||||
# The user may or may not have the identity secret to this commitment
|
||||
# It should be used when detecting new members in the group, and syncing the group state
|
||||
method register*(g: GroupManager, idCommitment: IDCommitment): Future[void] {.base,async: (raises: [Exception]).} =
|
||||
raise newException(CatchableError, "register proc for " & $g.type & " is not implemented yet")
|
||||
when defined(rln_v2):
|
||||
method register*(g: GroupManager,
|
||||
rateCommitment: RateCommitment): Future[void] {.base,async: (raises: [Exception]).} =
|
||||
raise newException(CatchableError, "register proc for " & $g.type & " is not implemented yet")
|
||||
else:
|
||||
method register*(g: GroupManager, idCommitment: IDCommitment): Future[void] {.base,async: (raises: [Exception]).} =
|
||||
raise newException(CatchableError, "register proc for " & $g.type & " is not implemented yet")
|
||||
|
||||
# This proc is used to register a new identity commitment into the merkle tree
|
||||
# The user should have the identity secret to this commitment
|
||||
# It should be used when the user wants to join the group
|
||||
method register*(g: GroupManager, credentials: IdentityCredential): Future[void] {.base,async: (raises: [Exception]).} =
|
||||
raise newException(CatchableError, "register proc for " & $g.type & " is not implemented yet")
|
||||
when defined(rln_v2):
|
||||
method register*(g: GroupManager,
|
||||
credentials: IdentityCredential,
|
||||
userMessageLimit: UserMessageLimit): Future[void] {.base,async: (raises: [Exception]).} =
|
||||
raise newException(CatchableError, "register proc for " & $g.type & " is not implemented yet")
|
||||
else:
|
||||
method register*(g: GroupManager, credentials: IdentityCredential): Future[void] {.base,async: (raises: [Exception]).} =
|
||||
raise newException(CatchableError, "register proc for " & $g.type & " is not implemented yet")
|
||||
|
||||
# This proc is used to register a batch of new identity commitments into the merkle tree
|
||||
# The user may or may not have the identity secret to these commitments
|
||||
# It should be used when detecting a batch of new members in the group, and syncing the group state
|
||||
method registerBatch*(g: GroupManager, idCommitments: seq[IDCommitment]): Future[void] {.base,async: (raises: [Exception]).} =
|
||||
raise newException(CatchableError, "registerBatch proc for " & $g.type & " is not implemented yet")
|
||||
when defined(rln_v2):
|
||||
method registerBatch*(g: GroupManager, rateCommitments: seq[RateCommitment]): Future[void] {.base,async: (raises: [Exception]).} =
|
||||
raise newException(CatchableError, "registerBatch proc for " & $g.type & " is not implemented yet")
|
||||
else:
|
||||
method registerBatch*(g: GroupManager, idCommitments: seq[IDCommitment]): Future[void] {.base,async: (raises: [Exception]).} =
|
||||
raise newException(CatchableError, "registerBatch proc for " & $g.type & " is not implemented yet")
|
||||
|
||||
# This proc is used to set a callback that will be called when a new identity commitment is registered
|
||||
# The callback may be called multiple times, and should be used to for any post processing
|
||||
@ -86,8 +106,16 @@ method withdrawBatch*(g: GroupManager, identitySecretHashes: seq[IdentitySecretH
|
||||
raise newException(CatchableError, "withdrawBatch proc for " & $g.type & " is not implemented yet")
|
||||
|
||||
# This proc is used to insert and remove a set of commitments from the merkle tree
|
||||
method atomicBatch*(g: GroupManager, idCommitments: seq[IDCommitment], toRemoveIndices: seq[MembershipIndex]): Future[void] {.base,async: (raises: [Exception]).} =
|
||||
raise newException(CatchableError, "atomicBatch proc for " & $g.type & " is not implemented yet")
|
||||
when defined(rln_v2):
|
||||
method atomicBatch*(g: GroupManager,
|
||||
rateCommitments: seq[RateCommitment],
|
||||
toRemoveIndices: seq[MembershipIndex]): Future[void] {.base,async: (raises: [Exception]).} =
|
||||
raise newException(CatchableError, "atomicBatch proc for " & $g.type & " is not implemented yet")
|
||||
else:
|
||||
method atomicBatch*(g: GroupManager,
|
||||
idCommitments: seq[IDCommitment],
|
||||
toRemoveIndices: seq[MembershipIndex]): Future[void] {.base,async: (raises: [Exception]).} =
|
||||
raise newException(CatchableError, "atomicBatch proc for " & $g.type & " is not implemented yet")
|
||||
|
||||
method stop*(g: GroupManager): Future[void] {.base,async.} =
|
||||
raise newException(CatchableError, "stop proc for " & $g.type & " is not implemented yet")
|
||||
@ -99,7 +127,7 @@ method onWithdraw*(g: GroupManager, cb: OnWithdrawCallback) {.base,gcsafe.} =
|
||||
|
||||
proc slideRootQueue*(rootQueue: var Deque[MerkleNode], root: MerkleNode): seq[MerkleNode] =
|
||||
## updates the root queue with the latest root and pops the oldest one when the capacity of `AcceptableRootWindowSize` is reached
|
||||
let overflowCount = rootQueue.len() - AcceptableRootWindowSize + 1
|
||||
let overflowCount = rootQueue.len - AcceptableRootWindowSize + 1
|
||||
var overflowedRoots = newSeq[MerkleNode]()
|
||||
if overflowCount > 0:
|
||||
# Delete the oldest `overflowCount` roots in the deque (index 0..`overflowCount`)
|
||||
@ -135,45 +163,55 @@ template slideRootQueue*(g: GroupManager): untyped =
|
||||
discard rootBuffer.slideRootQueue(root)
|
||||
rootBuffer
|
||||
|
||||
method verifyProof*(g: GroupManager,
|
||||
input: openArray[byte],
|
||||
proof: RateLimitProof): GroupManagerResult[bool] {.base,gcsafe,raises:[].} =
|
||||
## verifies the proof against the input and the current merkle root
|
||||
let proofVerifyRes = g.rlnInstance.proofVerify(input, proof, g.validRoots.items().toSeq())
|
||||
if proofVerifyRes.isErr():
|
||||
return err("proof verification failed: " & $proofVerifyRes.error())
|
||||
return ok(proofVerifyRes.value())
|
||||
|
||||
when defined(rln_v2):
|
||||
method verifyProof*(g: GroupManager,
|
||||
input: openArray[byte],
|
||||
proof: RateLimitProof): GroupManagerResult[bool] {.base,gcsafe,raises:[].} =
|
||||
## verifies the proof against the input and the current merkle root
|
||||
## TODO: verify the external nullifier with provided RateLimitProof
|
||||
let proofVerifyRes = g.rlnInstance.proofVerify(input, RateLimitProof(proof), g.validRoots.items().toSeq())
|
||||
if proofVerifyRes.isErr():
|
||||
return err("proof verification failed: " & $proofVerifyRes.error())
|
||||
return ok(proofVerifyRes.value())
|
||||
method generateProof*(g: GroupManager,
|
||||
data: openArray[byte],
|
||||
epoch: Epoch,
|
||||
messageId: MessageId,
|
||||
rlnIdentifier = DefaultRlnIdentifier): GroupManagerResult[RateLimitProof] {.base,gcsafe,raises:[].} =
|
||||
## generates a proof for the given data and epoch
|
||||
## the proof is generated using the current merkle root
|
||||
if g.idCredentials.isNone():
|
||||
return err("identity credentials are not set")
|
||||
if g.membershipIndex.isNone():
|
||||
return err("membership index is not set")
|
||||
if g.userMessageLimit.isNone():
|
||||
return err("user message limit is not set")
|
||||
waku_rln_proof_generation_duration_seconds.nanosecondTime:
|
||||
let proof = proofGen(rlnInstance = g.rlnInstance,
|
||||
data = data,
|
||||
memKeys = g.idCredentials.get(),
|
||||
memIndex = g.membershipIndex.get(),
|
||||
epoch = epoch).valueOr:
|
||||
return err("proof generation failed: " & $error)
|
||||
return ok(proof)
|
||||
else:
|
||||
method verifyProof*(g: GroupManager,
|
||||
input: openArray[byte],
|
||||
proof: RateLimitProof): GroupManagerResult[bool] {.base,gcsafe,raises:[].} =
|
||||
## verifies the proof against the input and the current merkle root
|
||||
let proofVerifyRes = g.rlnInstance.proofVerify(input, proof, g.validRoots.items().toSeq())
|
||||
if proofVerifyRes.isErr():
|
||||
return err("proof verification failed: " & $proofVerifyRes.error())
|
||||
return ok(proofVerifyRes.value())
|
||||
|
||||
|
||||
method generateProof*(g: GroupManager,
|
||||
data: openArray[byte],
|
||||
epoch: Epoch): GroupManagerResult[RateLimitProof] {.base,gcsafe,raises:[].} =
|
||||
## generates a proof for the given data and epoch
|
||||
## the proof is generated using the current merkle root
|
||||
if g.idCredentials.isNone():
|
||||
return err("identity credentials are not set")
|
||||
if g.membershipIndex.isNone():
|
||||
return err("membership index is not set")
|
||||
waku_rln_proof_generation_duration_seconds.nanosecondTime:
|
||||
let proofGenRes = proofGen(rlnInstance = g.rlnInstance,
|
||||
data = data,
|
||||
memKeys = g.idCredentials.get(),
|
||||
memIndex = g.membershipIndex.get(),
|
||||
epoch = epoch)
|
||||
if proofGenRes.isErr():
|
||||
return err("proof generation failed: " & $proofGenRes.error())
|
||||
return ok(proofGenRes.value())
|
||||
method generateProof*(g: GroupManager,
|
||||
data: openArray[byte],
|
||||
epoch: Epoch): GroupManagerResult[RateLimitProof] {.base,gcsafe,raises:[].} =
|
||||
## generates a proof for the given data and epoch
|
||||
## the proof is generated using the current merkle root
|
||||
if g.idCredentials.isNone():
|
||||
return err("identity credentials are not set")
|
||||
if g.membershipIndex.isNone():
|
||||
return err("membership index is not set")
|
||||
waku_rln_proof_generation_duration_seconds.nanosecondTime:
|
||||
let proof = proofGen(rlnInstance = g.rlnInstance,
|
||||
data = data,
|
||||
memKeys = g.idCredentials.get(),
|
||||
memIndex = g.membershipIndex.get(),
|
||||
epoch = epoch).valueOr:
|
||||
return err("proof generation failed: " & $error)
|
||||
return ok(proof)
|
||||
|
||||
method isReady*(g: GroupManager): Future[bool] {.base,async.} =
|
||||
raise newException(CatchableError, "isReady proc for " & $g.type & " is not implemented yet")
|
||||
|
@ -8,6 +8,7 @@ import
|
||||
web3/ethtypes,
|
||||
eth/keys as keys,
|
||||
chronicles,
|
||||
nimcrypto/keccak,
|
||||
stint,
|
||||
json,
|
||||
std/tables,
|
||||
@ -28,28 +29,54 @@ export group_manager_base
|
||||
logScope:
|
||||
topics = "waku rln_relay onchain_group_manager"
|
||||
|
||||
contract(WakuRlnRegistry):
|
||||
# this describes the storage slot to use
|
||||
proc usingStorageIndex(): Uint16 {.pure.}
|
||||
# this map contains the address of a given storage slot
|
||||
proc storages(index: Uint16): Address {.pure.}
|
||||
# this serves as an entrypoint into the rln storage contract
|
||||
proc register(storageIndex: Uint16, idCommitment: Uint256)
|
||||
# this creates a new storage on the rln registry
|
||||
proc newStorage()
|
||||
# using the when predicate does not work within the contract macro, hence need to dupe
|
||||
when defined(rln_v2):
|
||||
contract(WakuRlnRegistry):
|
||||
# this describes the storage slot to use
|
||||
proc usingStorageIndex(): Uint16 {.pure.}
|
||||
# this map contains the address of a given storage slot
|
||||
proc storages(index: Uint16): Address {.pure.}
|
||||
# this serves as an entrypoint into the rln storage contract
|
||||
proc register(storageIndex: Uint16, idCommitment: Uint256, userMessageLimit: Uint256)
|
||||
# this creates a new storage on the rln registry
|
||||
proc newStorage()
|
||||
|
||||
# membership contract interface
|
||||
contract(RlnStorage):
|
||||
# this event is raised when a new member is registered
|
||||
proc MemberRegistered(idCommitment: Uint256, userMessageLimit: Uint256, index: Uint256) {.event.}
|
||||
# this constant contains the membership deposit of the contract
|
||||
proc MEMBERSHIP_DEPOSIT(): Uint256 {.pure.}
|
||||
# this map denotes existence of a given user
|
||||
proc memberExists(idCommitment: Uint256): Uint256 {.view.}
|
||||
# this constant describes the next index of a new member
|
||||
proc idCommitmentIndex(): Uint256 {.view.}
|
||||
# this constant describes the block number this contract was deployed on
|
||||
proc deployedBlockNumber(): Uint256 {.view.}
|
||||
else:
|
||||
contract(WakuRlnRegistry):
|
||||
# this describes the storage slot to use
|
||||
proc usingStorageIndex(): Uint16 {.pure.}
|
||||
# this map contains the address of a given storage slot
|
||||
proc storages(index: Uint16): Address {.pure.}
|
||||
# this serves as an entrypoint into the rln storage contract
|
||||
proc register(storageIndex: Uint16, idCommitment: Uint256)
|
||||
# this creates a new storage on the rln registry
|
||||
proc newStorage()
|
||||
|
||||
# membership contract interface
|
||||
contract(RlnStorage):
|
||||
# this event is raised when a new member is registered
|
||||
proc MemberRegistered(idCommitment: Uint256, index: Uint256) {.event.}
|
||||
# this constant contains the membership deposit of the contract
|
||||
proc MEMBERSHIP_DEPOSIT(): Uint256 {.pure.}
|
||||
# this map denotes existence of a given user
|
||||
proc memberExists(idCommitment: Uint256): Uint256 {.view.}
|
||||
# this constant describes the next index of a new member
|
||||
proc idCommitmentIndex(): Uint256 {.view.}
|
||||
# this constant describes the block number this contract was deployed on
|
||||
proc deployedBlockNumber(): Uint256 {.view.}
|
||||
|
||||
# membership contract interface
|
||||
contract(RlnStorage):
|
||||
# this event is raised when a new member is registered
|
||||
proc MemberRegistered(idCommitment: Uint256, index: Uint256) {.event.}
|
||||
# this constant contains the membership deposit of the contract
|
||||
proc MEMBERSHIP_DEPOSIT(): Uint256 {.pure.}
|
||||
# this map denotes existence of a given user
|
||||
proc memberExists(idCommitment: Uint256): Uint256 {.view.}
|
||||
# this constant describes the next index of a new member
|
||||
proc idCommitmentIndex(): Uint256 {.view.}
|
||||
# this constant describes the block number this contract was deployed on
|
||||
proc deployedBlockNumber(): Uint256 {.view.}
|
||||
|
||||
type
|
||||
RegistryContractWithSender = Sender[WakuRlnRegistry]
|
||||
@ -100,96 +127,197 @@ proc setMetadata*(g: OnchainGroupManager): RlnRelayResult[void] =
|
||||
return err("failed to persist rln metadata: " & getCurrentExceptionMsg())
|
||||
return ok()
|
||||
|
||||
method atomicBatch*(g: OnchainGroupManager,
|
||||
start: MembershipIndex,
|
||||
idCommitments = newSeq[IDCommitment](),
|
||||
toRemoveIndices = newSeq[MembershipIndex]()):
|
||||
Future[void] {.async: (raises: [Exception]).} =
|
||||
initializedGuard(g)
|
||||
|
||||
waku_rln_membership_insertion_duration_seconds.nanosecondTime:
|
||||
let operationSuccess = g.rlnInstance.atomicWrite(some(start), idCommitments, toRemoveIndices)
|
||||
if not operationSuccess:
|
||||
raise newException(ValueError, "atomic batch operation failed")
|
||||
# TODO: when slashing is enabled, we need to track slashed members
|
||||
waku_rln_number_registered_memberships.set(int64(g.rlnInstance.leavesSet()))
|
||||
|
||||
if g.registerCb.isSome():
|
||||
var membersSeq = newSeq[Membership]()
|
||||
for i in 0 ..< idCommitments.len():
|
||||
var index = start + MembershipIndex(i)
|
||||
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: (raises: [Exception]).} =
|
||||
initializedGuard(g)
|
||||
|
||||
await g.registerBatch(@[idCommitment])
|
||||
|
||||
|
||||
method registerBatch*(g: OnchainGroupManager, idCommitments: seq[IDCommitment]):
|
||||
when defined(rln_v2):
|
||||
method atomicBatch*(g: OnchainGroupManager,
|
||||
start: MembershipIndex,
|
||||
rateCommitments = newSeq[RateCommitment](),
|
||||
toRemoveIndices = newSeq[MembershipIndex]()):
|
||||
Future[void] {.async: (raises: [Exception]).} =
|
||||
initializedGuard(g)
|
||||
initializedGuard(g)
|
||||
|
||||
await g.atomicBatch(g.latestIndex, idCommitments)
|
||||
g.latestIndex += MembershipIndex(idCommitments.len())
|
||||
# convert the rateCommitment struct to a leaf value
|
||||
let leavesRes = rateCommitments.toLeaves()
|
||||
if leavesRes.isErr():
|
||||
raise newException(CatchableError, "failed to convert rateCommitments to leaves: " & leavesRes.error)
|
||||
let leaves = cast[seq[seq[byte]]](leavesRes.get())
|
||||
|
||||
waku_rln_membership_insertion_duration_seconds.nanosecondTime:
|
||||
let operationSuccess = g.rlnInstance.atomicWrite(some(start),
|
||||
leaves,
|
||||
toRemoveIndices)
|
||||
if not operationSuccess:
|
||||
raise newException(CatchableError, "atomic batch operation failed")
|
||||
# TODO: when slashing is enabled, we need to track slashed members
|
||||
waku_rln_number_registered_memberships.set(int64(g.rlnInstance.leavesSet()))
|
||||
|
||||
if g.registerCb.isSome():
|
||||
var membersSeq = newSeq[Membership]()
|
||||
for i in 0 ..< rateCommitments.len:
|
||||
var index = start + MembershipIndex(i)
|
||||
trace "registering member", rateCommitment = rateCommitments[i], index = index
|
||||
let member = Membership(rateCommitment: rateCommitments[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
|
||||
else:
|
||||
method atomicBatch*(g: OnchainGroupManager,
|
||||
start: MembershipIndex,
|
||||
idCommitments = newSeq[IDCommitment](),
|
||||
toRemoveIndices = newSeq[MembershipIndex]()):
|
||||
Future[void] {.async: (raises: [Exception]).} =
|
||||
initializedGuard(g)
|
||||
|
||||
waku_rln_membership_insertion_duration_seconds.nanosecondTime:
|
||||
let operationSuccess = g.rlnInstance.atomicWrite(some(start), idCommitments, toRemoveIndices)
|
||||
if not operationSuccess:
|
||||
raise newException(ValueError, "atomic batch operation failed")
|
||||
# TODO: when slashing is enabled, we need to track slashed members
|
||||
waku_rln_number_registered_memberships.set(int64(g.rlnInstance.leavesSet()))
|
||||
|
||||
if g.registerCb.isSome():
|
||||
var membersSeq = newSeq[Membership]()
|
||||
for i in 0 ..< idCommitments.len:
|
||||
var index = start + MembershipIndex(i)
|
||||
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
|
||||
|
||||
when defined(rln_v2):
|
||||
method register*(g: OnchainGroupManager,
|
||||
rateCommitment: RateCommitment): Future[void] {.async: (raises: [Exception]).} =
|
||||
initializedGuard(g)
|
||||
|
||||
await g.registerBatch(@[rateCommitment])
|
||||
else:
|
||||
method register*(g: OnchainGroupManager,
|
||||
idCommitment: IDCommitment): Future[void] {.async: (raises: [Exception]).} =
|
||||
initializedGuard(g)
|
||||
|
||||
await g.registerBatch(@[idCommitment])
|
||||
|
||||
|
||||
method register*(g: OnchainGroupManager, identityCredentials: IdentityCredential):
|
||||
Future[void] {.async: (raises: [Exception]).} =
|
||||
initializedGuard(g)
|
||||
when defined(rln_v2):
|
||||
method registerBatch*(g: OnchainGroupManager,
|
||||
rateCommitments: seq[RateCommitment]): Future[void] {.async: (raises: [Exception]).} =
|
||||
initializedGuard(g)
|
||||
|
||||
let ethRpc = g.ethRpc.get()
|
||||
let registryContract = g.registryContract.get()
|
||||
let membershipFee = g.membershipFee.get()
|
||||
await g.atomicBatch(g.latestIndex, rateCommitments)
|
||||
g.latestIndex += MembershipIndex(rateCommitments.len)
|
||||
else:
|
||||
method registerBatch*(g: OnchainGroupManager,
|
||||
idCommitments: seq[IDCommitment]): Future[void] {.async: (raises: [Exception]).} =
|
||||
initializedGuard(g)
|
||||
|
||||
var gasPrice: int
|
||||
retryWrapper(gasPrice, RetryStrategy.new(), "Failed to get gas price"):
|
||||
int(await ethRpc.provider.eth_gasPrice()) * 2
|
||||
let idCommitment = identityCredentials.idCommitment.toUInt256()
|
||||
await g.atomicBatch(g.latestIndex, idCommitments)
|
||||
g.latestIndex += MembershipIndex(idCommitments.len)
|
||||
|
||||
var txHash: TxHash
|
||||
let storageIndex = g.usingStorageIndex.get()
|
||||
debug "registering the member", idCommitment = idCommitment, storageIndex = storageIndex
|
||||
retryWrapper(txHash, RetryStrategy.new(), "Failed to register the member"):
|
||||
await registryContract.register(storageIndex, idCommitment).send(gasPrice = gasPrice)
|
||||
|
||||
# wait for the transaction to be mined
|
||||
var tsReceipt: ReceiptObject
|
||||
retryWrapper(tsReceipt, RetryStrategy.new(), "Failed to get the transaction receipt"):
|
||||
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
|
||||
let firstTopic = tsReceipt.logs[0].topics[0]
|
||||
# the hash of the signature of MemberRegistered(uint256,uint256) event is equal to the following hex value
|
||||
if firstTopic != cast[FixedBytes[32]](hexToByteArray[32](
|
||||
"0x5a92c2530f207992057b9c3e544108ffce3beda4a63719f316967c49bf6159d2"
|
||||
)):
|
||||
raise newException(ValueError, "unexpected event signature")
|
||||
when defined(rln_v2):
|
||||
method register*(g: OnchainGroupManager,
|
||||
identityCredential: IdentityCredential,
|
||||
userMessageLimit: UserMessageLimit): Future[void] {.async: (raises: [Exception]).} =
|
||||
initializedGuard(g)
|
||||
|
||||
# the arguments of the raised event i.e., MemberRegistered are encoded inside the data field
|
||||
# data = pk encoded as 256 bits || index encoded as 256 bits
|
||||
let arguments = tsReceipt.logs[0].data
|
||||
debug "tx log data", arguments=arguments
|
||||
let
|
||||
argumentsBytes = arguments
|
||||
# In TX log data, uints are encoded in big endian
|
||||
eventIndex = UInt256.fromBytesBE(argumentsBytes[32..^1])
|
||||
let ethRpc = g.ethRpc.get()
|
||||
let registryContract = g.registryContract.get()
|
||||
let membershipFee = g.membershipFee.get()
|
||||
|
||||
g.membershipIndex = some(eventIndex.toMembershipIndex())
|
||||
var gasPrice: int
|
||||
retryWrapper(gasPrice, RetryStrategy.new(), "Failed to get gas price"):
|
||||
int(await ethRpc.provider.eth_gasPrice()) * 2
|
||||
let idCommitment = identityCredential.idCommitment.toUInt256()
|
||||
|
||||
# don't handle member insertion into the tree here, it will be handled by the event listener
|
||||
return
|
||||
var txHash: TxHash
|
||||
let storageIndex = g.usingStorageIndex.get()
|
||||
debug "registering the member", idCommitment = idCommitment, storageIndex = storageIndex, userMessageLimit = userMessageLimit
|
||||
retryWrapper(txHash, RetryStrategy.new(), "Failed to register the member"):
|
||||
await registryContract.register(storageIndex, idCommitment, u256(userMessageLimit)).send(gasPrice = gasPrice)
|
||||
|
||||
# wait for the transaction to be mined
|
||||
var tsReceipt: ReceiptObject
|
||||
retryWrapper(tsReceipt, RetryStrategy.new(), "Failed to get the transaction receipt"):
|
||||
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
|
||||
let firstTopic = tsReceipt.logs[0].topics[0]
|
||||
# the hash of the signature of MemberRegistered(uint256,uint256,uint256) event is equal to the following hex value
|
||||
if firstTopic != cast[FixedBytes[32]](keccak256.digest("MemberRegistered(uint256,uint256,uint256)").data):
|
||||
raise newException(ValueError, "unexpected event signature")
|
||||
|
||||
# the arguments of the raised event i.e., MemberRegistered are encoded inside the data field
|
||||
# data = pk encoded as 256 bits || index encoded as 256 bits || userMessageLimit encoded as 256 bits
|
||||
let arguments = tsReceipt.logs[0].data
|
||||
debug "tx log data", arguments=arguments
|
||||
let
|
||||
argumentsBytes = arguments
|
||||
# In TX log data, uints are encoded in big endian
|
||||
userMessageLimit = UInt256.fromBytesBE(argumentsBytes[32..64])
|
||||
membershipIndex = UInt256.fromBytesBE(argumentsBytes[64..^1])
|
||||
|
||||
g.membershipIndex = some(membershipIndex.toMembershipIndex())
|
||||
g.userMessageLimit = some(userMessageLimit.toUserMessageLimit())
|
||||
|
||||
# don't handle member insertion into the tree here, it will be handled by the event listener
|
||||
return
|
||||
else:
|
||||
method register*(g: OnchainGroupManager,
|
||||
credentials: IdentityCredential): Future[void] {.async: (raises: [Exception]).} =
|
||||
initializedGuard(g)
|
||||
|
||||
let ethRpc = g.ethRpc.get()
|
||||
let registryContract = g.registryContract.get()
|
||||
let membershipFee = g.membershipFee.get()
|
||||
|
||||
var gasPrice: int
|
||||
retryWrapper(gasPrice, RetryStrategy.new(), "Failed to get gas price"):
|
||||
int(await ethRpc.provider.eth_gasPrice()) * 2
|
||||
let idCommitment = credentials.idCommitment.toUInt256()
|
||||
|
||||
var txHash: TxHash
|
||||
let storageIndex = g.usingStorageIndex.get()
|
||||
debug "registering the member", idCommitment = idCommitment, storageIndex = storageIndex
|
||||
retryWrapper(txHash, RetryStrategy.new(), "Failed to register the member"):
|
||||
await registryContract.register(storageIndex, idCommitment).send(gasPrice = gasPrice)
|
||||
|
||||
# wait for the transaction to be mined
|
||||
var tsReceipt: ReceiptObject
|
||||
retryWrapper(tsReceipt, RetryStrategy.new(), "Failed to get the transaction receipt"):
|
||||
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
|
||||
let firstTopic = tsReceipt.logs[0].topics[0]
|
||||
# the hash of the signature of MemberRegistered(uint256,uint256) event is equal to the following hex value
|
||||
if firstTopic != cast[FixedBytes[32]](keccak256.digest("MemberRegistered(uint256,uint256)").data):
|
||||
raise newException(ValueError, "unexpected event signature")
|
||||
|
||||
# the arguments of the raised event i.e., MemberRegistered are encoded inside the data field
|
||||
# data = pk encoded as 256 bits || index encoded as 256 bits
|
||||
let arguments = tsReceipt.logs[0].data
|
||||
debug "tx log data", arguments=arguments
|
||||
let
|
||||
argumentsBytes = arguments
|
||||
# In TX log data, uints are encoded in big endian
|
||||
eventIndex = UInt256.fromBytesBE(argumentsBytes[32..^1])
|
||||
|
||||
g.membershipIndex = some(eventIndex.toMembershipIndex())
|
||||
|
||||
# don't handle member insertion into the tree here, it will be handled by the event listener
|
||||
return
|
||||
|
||||
method withdraw*(g: OnchainGroupManager, idCommitment: IDCommitment):
|
||||
Future[void] {.async: (raises: [Exception]).} =
|
||||
@ -209,6 +337,8 @@ proc parseEvent(event: type MemberRegistered,
|
||||
## returns an error if it cannot parse the `data` parameter
|
||||
var idComm: UInt256
|
||||
var index: UInt256
|
||||
when defined(rln_v2):
|
||||
var userMessageLimit: UInt256
|
||||
var data: string
|
||||
# Remove the 0x prefix
|
||||
try:
|
||||
@ -221,7 +351,15 @@ proc parseEvent(event: type MemberRegistered,
|
||||
offset += decode(data, offset, idComm)
|
||||
# Parse the index
|
||||
offset += decode(data, offset, index)
|
||||
return ok(Membership(idCommitment: idComm.toIDCommitment(), index: index.toMembershipIndex()))
|
||||
when defined(rln_v2):
|
||||
# Parse the userMessageLimit
|
||||
offset += decode(data, offset, userMessageLimit)
|
||||
when defined(rln_v2):
|
||||
return ok(Membership(rateCommitment: RateCommitment(idCommitment: idComm.toIDCommitment(),
|
||||
userMessageLimit: userMessageLimit.toUserMessageLimit()),
|
||||
index: index.toMembershipIndex()))
|
||||
else:
|
||||
return ok(Membership(idCommitment: idComm.toIDCommitment(), index: index.toMembershipIndex()))
|
||||
except CatchableError:
|
||||
return err("failed to parse the data field of the MemberRegistered event")
|
||||
|
||||
@ -294,15 +432,23 @@ proc handleEvents(g: OnchainGroupManager,
|
||||
try:
|
||||
let startIndex = blockTable[blockNumber].filterIt(not it[1])[0][0].index
|
||||
let removalIndices = members.filterIt(it[1]).mapIt(it[0].index)
|
||||
let idCommitments = members.mapIt(it[0].idCommitment)
|
||||
await g.atomicBatch(start = startIndex,
|
||||
idCommitments = idCommitments,
|
||||
toRemoveIndices = removalIndices)
|
||||
g.latestIndex = startIndex + MembershipIndex(idCommitments.len())
|
||||
when defined(rln_v2):
|
||||
let rateCommitments = members.mapIt(it[0].rateCommitment)
|
||||
await g.atomicBatch(start = startIndex,
|
||||
rateCommitments = rateCommitments,
|
||||
toRemoveIndices = removalIndices)
|
||||
g.latestIndex = startIndex + MembershipIndex(rateCommitments.len)
|
||||
trace "new members added to the Merkle tree", commitments=rateCommitments
|
||||
else:
|
||||
let idCommitments = members.mapIt(it[0].idCommitment)
|
||||
await g.atomicBatch(start = startIndex,
|
||||
idCommitments = idCommitments,
|
||||
toRemoveIndices = removalIndices)
|
||||
g.latestIndex = startIndex + MembershipIndex(idCommitments.len)
|
||||
trace "new members added to the Merkle tree", commitments=idCommitments
|
||||
except CatchableError:
|
||||
error "failed to insert members into the tree", error=getCurrentExceptionMsg()
|
||||
raise newException(ValueError, "failed to insert members into the tree")
|
||||
trace "new members added to the Merkle tree", commitments=members.mapIt(it[0].idCommitment.inHex())
|
||||
|
||||
return
|
||||
|
||||
@ -494,6 +640,8 @@ method init*(g: OnchainGroupManager): Future[void] {.async.} =
|
||||
raise newException(CatchableError, "could not parse the keystore: " & $keystoreCredRes.error)
|
||||
let keystoreCred = keystoreCredRes.get()
|
||||
g.membershipIndex = some(keystoreCred.treeIndex)
|
||||
when defined(rln_v2):
|
||||
g.userMessageLimit = some(keystoreCred.userMessageLimit)
|
||||
# now we check on the contract if the commitment actually has a membership
|
||||
try:
|
||||
let membershipExists = await rlnContract.memberExists(keystoreCred
|
||||
|
@ -34,7 +34,7 @@ method init*(g: StaticGroupManager): Future[void] {.async.} =
|
||||
|
||||
discard g.slideRootQueue()
|
||||
|
||||
g.latestIndex += MembershipIndex(idCommitments.len() - 1)
|
||||
g.latestIndex += MembershipIndex(idCommitments.len - 1)
|
||||
|
||||
g.initialized = true
|
||||
|
||||
@ -44,51 +44,90 @@ method startGroupSync*(g: StaticGroupManager): Future[void] {.async: (raises: [E
|
||||
initializedGuard(g)
|
||||
# No-op
|
||||
|
||||
method register*(g: StaticGroupManager, idCommitment: IDCommitment):
|
||||
Future[void] {.async: (raises: [Exception]).} =
|
||||
initializedGuard(g)
|
||||
when defined(rln_v2):
|
||||
method register*(g: StaticGroupManager,
|
||||
rateCommitment: RateCommitment): Future[void] {.async: (raises: [Exception]).} =
|
||||
initializedGuard(g)
|
||||
|
||||
await g.registerBatch(@[idCommitment])
|
||||
await g.registerBatch(@[rateCommitment])
|
||||
|
||||
else:
|
||||
method register*(g: StaticGroupManager, idCommitment: IDCommitment):
|
||||
Future[void] {.async: (raises: [Exception]).} =
|
||||
initializedGuard(g)
|
||||
|
||||
method registerBatch*(g: StaticGroupManager, idCommitments: seq[IDCommitment]):
|
||||
Future[void] {.async: (raises: [Exception]).} =
|
||||
initializedGuard(g)
|
||||
await g.registerBatch(@[idCommitment])
|
||||
|
||||
let membersInserted = g.rlnInstance.insertMembers(g.latestIndex + 1, idCommitments)
|
||||
if not membersInserted:
|
||||
raise newException(ValueError, "Failed to insert members into the merkle tree")
|
||||
when defined(rln_v2):
|
||||
method registerBatch*(g: StaticGroupManager,
|
||||
rateCommitments: seq[RateCommitment]): Future[void] {.async: (raises: [Exception]).} =
|
||||
initializedGuard(g)
|
||||
|
||||
if g.registerCb.isSome():
|
||||
var memberSeq = newSeq[Membership]()
|
||||
for i in 0..<idCommitments.len():
|
||||
memberSeq.add(Membership(idCommitment: idCommitments[i], index: g.latestIndex + MembershipIndex(i) + 1))
|
||||
await g.registerCb.get()(memberSeq)
|
||||
let leavesRes = rateCommitments.toLeaves()
|
||||
if not leavesRes.isOk():
|
||||
raise newException(ValueError, "Failed to convert rate commitments to leaves")
|
||||
let leaves = cast[seq[seq[byte]]](leavesRes.get())
|
||||
|
||||
discard g.slideRootQueue()
|
||||
let membersInserted = g.rlnInstance.insertMembers(g.latestIndex + 1, leaves)
|
||||
if not membersInserted:
|
||||
raise newException(ValueError, "Failed to insert members into the merkle tree")
|
||||
|
||||
g.latestIndex += MembershipIndex(idCommitments.len())
|
||||
if g.registerCb.isSome():
|
||||
var memberSeq = newSeq[Membership]()
|
||||
for i in 0..<rateCommitments.len:
|
||||
memberSeq.add(Membership(rateCommitment: rateCommitments[i], index: g.latestIndex + MembershipIndex(i) + 1))
|
||||
await g.registerCb.get()(memberSeq)
|
||||
|
||||
return
|
||||
discard g.slideRootQueue()
|
||||
|
||||
method withdraw*(g: StaticGroupManager, idSecretHash: IdentitySecretHash):
|
||||
Future[void] {.async: (raises: [Exception]).} =
|
||||
initializedGuard(g)
|
||||
g.latestIndex += MembershipIndex(rateCommitments.len)
|
||||
|
||||
let groupKeys = g.groupKeys
|
||||
return
|
||||
else:
|
||||
method registerBatch*(g: StaticGroupManager, idCommitments: seq[IDCommitment]):
|
||||
Future[void] {.async: (raises: [Exception]).} =
|
||||
initializedGuard(g)
|
||||
|
||||
for i in 0..<groupKeys.len():
|
||||
if groupKeys[i].idSecretHash == idSecretHash:
|
||||
let idCommitment = groupKeys[i].idCommitment
|
||||
let index = MembershipIndex(i)
|
||||
let memberRemoved = g.rlnInstance.removeMember(index)
|
||||
if not memberRemoved:
|
||||
raise newException(ValueError, "Failed to remove member from the merkle tree")
|
||||
let membersInserted = g.rlnInstance.insertMembers(g.latestIndex + 1, idCommitments)
|
||||
if not membersInserted:
|
||||
raise newException(ValueError, "Failed to insert members into the merkle tree")
|
||||
|
||||
if g.withdrawCb.isSome():
|
||||
await g.withdrawCb.get()(@[Membership(idCommitment: idCommitment, index: index)])
|
||||
if g.registerCb.isSome():
|
||||
var memberSeq = newSeq[Membership]()
|
||||
for i in 0..<idCommitments.len:
|
||||
memberSeq.add(Membership(idCommitment: idCommitments[i], index: g.latestIndex + MembershipIndex(i) + 1))
|
||||
await g.registerCb.get()(memberSeq)
|
||||
|
||||
return
|
||||
discard g.slideRootQueue()
|
||||
|
||||
g.latestIndex += MembershipIndex(idCommitments.len)
|
||||
|
||||
return
|
||||
|
||||
when defined(rln_v2):
|
||||
method withdraw*(g: StaticGroupManager,
|
||||
idSecretHash: IdentitySecretHash): Future[void] {.async: (raises: [Exception]).} =
|
||||
# No-op
|
||||
return
|
||||
else:
|
||||
method withdraw*(g: StaticGroupManager, idSecretHash: IdentitySecretHash):
|
||||
Future[void] {.async: (raises: [Exception]).} =
|
||||
initializedGuard(g)
|
||||
|
||||
let groupKeys = g.groupKeys
|
||||
|
||||
for i in 0..<groupKeys.len:
|
||||
if groupKeys[i].idSecretHash == idSecretHash:
|
||||
let idCommitment = groupKeys[i].idCommitment
|
||||
let index = MembershipIndex(i)
|
||||
let memberRemoved = g.rlnInstance.removeMember(index)
|
||||
if not memberRemoved:
|
||||
raise newException(ValueError, "Failed to remove member from the merkle tree")
|
||||
|
||||
if g.withdrawCb.isSome():
|
||||
await g.withdrawCb.get()(@[Membership(idCommitment: idCommitment, index: index)])
|
||||
|
||||
return
|
||||
|
||||
|
||||
method withdrawBatch*(g: StaticGroupManager, idSecretHashes: seq[IdentitySecretHash]):
|
||||
|
@ -37,6 +37,10 @@ when defined(rln_v2):
|
||||
MessageId* = uint64
|
||||
ExternalNullifier* = array[32, byte]
|
||||
|
||||
type RateCommitment* = object
|
||||
idCommitment*: IDCommitment
|
||||
userMessageLimit*: UserMessageLimit
|
||||
|
||||
# Custom data types defined for waku rln relay -------------------------
|
||||
type RateLimitProof* = object
|
||||
## RateLimitProof holds the public inputs to rln circuit as
|
||||
|
@ -161,6 +161,21 @@ proc poseidon*(data: seq[seq[byte]]): RlnRelayResult[array[32, byte]] =
|
||||
return ok(output)
|
||||
|
||||
when defined(rln_v2):
|
||||
func toLeaf*(rateCommitment: RateCommitment): RlnRelayResult[MerkleNode] {.inline.} =
|
||||
let idCommitment = rateCommitment.idCommitment
|
||||
let userMessageLimit = rateCommitment.userMessageLimit
|
||||
let leafRes = poseidon(@[@idCommitment, cast[seq[byte]](userMessageLimit)])
|
||||
return leafRes
|
||||
|
||||
func toLeaves*(rateCommitments: seq[RateCommitment]): RlnRelayResult[seq[MerkleNode]] {.inline.} =
|
||||
var leaves = newSeq[MerkleNode](rateCommitments.len)
|
||||
for rateCommitment in rateCommitments:
|
||||
let leafRes = toLeaf(rateCommitment)
|
||||
if leafRes.isErr():
|
||||
return err("could not convert the rate commitment to a leaf: " & leafRes.error)
|
||||
leaves.add(leafRes.get())
|
||||
return ok(leaves)
|
||||
|
||||
# TODO: collocate this proc with the definition of the RateLimitProof
|
||||
# and the ProofMetadata types
|
||||
proc extractMetadata*(proof: RateLimitProof): RlnRelayResult[ProofMetadata] =
|
||||
@ -168,7 +183,7 @@ when defined(rln_v2):
|
||||
nullifier: proof.nullifier,
|
||||
shareX: proof.shareX,
|
||||
shareY: proof.shareY,
|
||||
externalNullifier: externalNullifierRes.get()
|
||||
externalNullifier: proof.externalNullifier
|
||||
))
|
||||
else:
|
||||
proc extractMetadata*(proof: RateLimitProof): RlnRelayResult[ProofMetadata] =
|
||||
@ -190,22 +205,23 @@ when defined(rln_v2):
|
||||
userMessageLimit: UserMessageLimit,
|
||||
messageId: MessageId,
|
||||
index: MembershipIndex,
|
||||
epoch: Epoch): RateLimitProofResult =
|
||||
epoch: Epoch,
|
||||
rlnIdentifier = DefaultRlnIdentifier): RateLimitProofResult =
|
||||
|
||||
# obtain the external nullifier
|
||||
let externalNullifierRes = poseidon(@[@(epoch),
|
||||
@(DefaultRlnIdentifier)])
|
||||
@(rlnIdentifier)])
|
||||
|
||||
if externalNullifierRes.isErr():
|
||||
return err("could not construct the external nullifier")
|
||||
|
||||
# serialize inputs
|
||||
let serializedInputs = serialize(idSecretHash = membership.idSecretHash,
|
||||
memIndex = index,
|
||||
userMessageLimit = userMessageLimit,
|
||||
messageId = messageId,
|
||||
externalNullifier = externalNullifierRes.get(),
|
||||
msg = data)
|
||||
memIndex = index,
|
||||
userMessageLimit = userMessageLimit,
|
||||
messageId = messageId,
|
||||
externalNullifier = externalNullifierRes.get(),
|
||||
msg = data)
|
||||
var inputBuffer = toBuffer(serializedInputs)
|
||||
|
||||
debug "input buffer ", inputBuffer= repr(inputBuffer)
|
||||
|
@ -78,6 +78,7 @@ type WakuRLNRelay* = ref object of RootObj
|
||||
nullifierLog*: OrderedTable[Epoch, seq[ProofMetadata]]
|
||||
lastEpoch*: Epoch # the epoch of the last published rln message
|
||||
groupManager*: GroupManager
|
||||
nonce*: uint64
|
||||
|
||||
method stop*(rlnPeer: WakuRLNRelay) {.async: (raises: [Exception]).} =
|
||||
## stops the rln-relay protocol
|
||||
@ -290,7 +291,11 @@ proc appendRLNProof*(rlnPeer: WakuRLNRelay,
|
||||
let input = msg.toRLNSignal()
|
||||
let epoch = calcEpoch(senderEpochTime)
|
||||
|
||||
let proofGenRes = rlnPeer.groupManager.generateProof(input, epoch)
|
||||
when defined(rln_v2):
|
||||
# TODO: add support for incrementing nonce, will address in another PR
|
||||
let proofGenRes = rlnPeer.groupManager.generateProof(input, epoch, 1)
|
||||
else:
|
||||
let proofGenRes = rlnPeer.groupManager.generateProof(input, epoch)
|
||||
|
||||
if proofGenRes.isErr():
|
||||
return false
|
||||
|
Loading…
x
Reference in New Issue
Block a user