nwaku/tests/node/test_wakunode_relay_rln.nim
Sergei Tikhomirov 505ec84ce8
feat: add txhash-based eligibility checks for incentivization PoC (#3166)
Implement data structures and tests for checking transaction eligibility based on tx hash. This work will be continues in future PRs. All code added in this PR is only used in tests.

* feat: add simple txid-based eligibility check with hard-coded params (#3166)

* use new proc to generate eligibility status

Co-authored-by: gabrielmer <101006718+gabrielmer@users.noreply.github.com>

* minor fixes

* add comments to clarify eligibility definition

* use Address.fromHex conversion from eth-web3

* move isEligible to common

* refactor: avoid result and unnecesary branching

* define const for simple transfer gas usage

* avoid unnecessary parentheses

* chore: run nph linter manually

* refactor, move all hard-coded constants to tests

* use Result type in eligibility tests

* use standard method of error handling

* make try-block smaller

* add a try-block in case of connection failure to web3 provider

* make queries to web3 provider in parallel

* move Web3 provider RPC URL into env variable

* remove unused import

* rename functions

* use await in async proc

Co-authored-by: gabrielmer <101006718+gabrielmer@users.noreply.github.com>

* add timeout to tx receipt query

* parallelize queries for tx and txreceipt

* make test txids non public

Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>

* use assert in txid i13n test

Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>

* use parentheses when calling verb-methods without arguments

Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>

* remove unused import

Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>

* use init for stack-allocated objects

* add txReceipt error message to error

Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>

* introduce eligibility manager

* [WIP] use Anvil for eligibility testing

* add eligibility test with contract deployment tx

* add eligibility test with contract call

* add asyncSetup and asyncTeardown for eligibility tests

* minor refactor

* refactor tests for onchain group manager with asyncSetup and asyncTeardown

* minor refactor

* remove unnecessary defer in asyncTeardown

Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>

* remove unnecessary call in test (moved to asyncTeardown)

Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>

* add comment justidying the use of discard

* rename file txid_proof to eligibility_manager

---------

Co-authored-by: gabrielmer <101006718+gabrielmer@users.noreply.github.com>
Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>
2025-01-22 11:16:49 +01:00

758 lines
28 KiB
Nim

{.used.}
import
std/[tempfiles, strutils, options],
stew/shims/net as stewNet,
stew/results,
testutils/unittests,
chronos,
libp2p/switch,
libp2p/protocols/pubsub/pubsub,
eth/keys
from std/times import epochTime
import
../../../waku/[
node/waku_node,
node/peer_manager,
waku_core,
waku_node,
common/error_handling,
waku_rln_relay,
waku_rln_relay/rln,
waku_rln_relay/protocol_types,
waku_keystore/keystore,
],
../waku_store/store_utils,
../waku_archive/archive_utils,
../testlib/[wakucore, wakunode, testasync, futures, common, assertions],
../resources/payloads,
../waku_rln_relay/[utils_static, utils_onchain]
from ../../waku/waku_noise/noise_utils import randomSeqByte
proc buildRandomIdentityCredentials(): IdentityCredential =
# We generate a random identity credential (inter-value constrains are not enforced, otherwise we need to load e.g. zerokit RLN keygen)
let
idTrapdoor = randomSeqByte(rng[], 32)
idNullifier = randomSeqByte(rng[], 32)
idSecretHash = randomSeqByte(rng[], 32)
idCommitment = randomSeqByte(rng[], 32)
IdentityCredential(
idTrapdoor: idTrapdoor,
idNullifier: idNullifier,
idSecretHash: idSecretHash,
idCommitment: idCommitment,
)
proc addMembershipCredentialsToKeystore(
credentials: IdentityCredential,
keystorePath: string,
appInfo: AppInfo,
rlnRelayEthContractAddress: string,
password: string,
membershipIndex: uint,
): KeystoreResult[void] =
let
contract = MembershipContract(chainId: "0x539", address: rlnRelayEthContractAddress)
# contract = MembershipContract(chainId: "1337", address: rlnRelayEthContractAddress)
index = MembershipIndex(membershipIndex)
membershipCredential = KeystoreMembership(
membershipContract: contract, treeIndex: index, identityCredential: credentials
)
addMembershipCredentials(
path = keystorePath,
membership = membershipCredential,
password = password,
appInfo = appInfo,
)
proc fatalErrorVoidHandler(errMsg: string) {.gcsafe, raises: [].} =
discard
proc getWakuRlnConfigOnChain*(
keystorePath: string,
appInfo: AppInfo,
rlnRelayEthContractAddress: string,
password: string,
credIndex: uint,
fatalErrorHandler: Option[OnFatalErrorHandler] = none(OnFatalErrorHandler),
ethClientAddress: Option[string] = none(string),
): WakuRlnConfig =
return WakuRlnConfig(
rlnRelayDynamic: true,
rlnRelayCredIndex: some(credIndex),
rlnRelayEthContractAddress: rlnRelayEthContractAddress,
rlnRelayEthClientAddress: ethClientAddress.get(EthClient),
rlnRelayTreePath: genTempPath("rln_tree", "wakunode_" & $credIndex),
rlnEpochSizeSec: 1,
onFatalErrorAction: fatalErrorHandler.get(fatalErrorVoidHandler),
# If these are used, initialisation fails with "failed to mount WakuRlnRelay: could not initialize the group manager: the commitment does not have a membership"
rlnRelayCredPath: keystorePath,
rlnRelayCredPassword: password,
)
proc setupRelayWithOnChainRln*(
node: WakuNode, shards: seq[RelayShard], wakuRlnConfig: WakuRlnConfig
) {.async.} =
await node.mountRelay(shards)
await node.mountRlnRelay(wakuRlnConfig)
suite "Waku RlnRelay - End to End - Static":
var
pubsubTopic {.threadvar.}: PubsubTopic
contentTopic {.threadvar.}: ContentTopic
var
server {.threadvar.}: WakuNode
client {.threadvar.}: WakuNode
var
serverRemotePeerInfo {.threadvar.}: RemotePeerInfo
clientPeerId {.threadvar.}: PeerId
asyncSetup:
pubsubTopic = DefaultPubsubTopic
contentTopic = DefaultContentTopic
let
serverKey = generateSecp256k1Key()
clientKey = generateSecp256k1Key()
server = newTestWakuNode(serverKey, ValidIpAddress.init("0.0.0.0"), Port(0))
client = newTestWakuNode(clientKey, ValidIpAddress.init("0.0.0.0"), Port(0))
await allFutures(server.start(), client.start())
serverRemotePeerInfo = server.switch.peerInfo.toRemotePeerInfo()
clientPeerId = client.switch.peerInfo.toRemotePeerInfo().peerId
asyncTeardown:
await allFutures(client.stop(), server.stop())
suite "Mount":
asyncTest "Can't mount if relay is not mounted":
# Given Relay and RLN are not mounted
check:
server.wakuRelay == nil
server.wakuRlnRelay == nil
# When RlnRelay is mounted
let catchRes = catch:
await server.setupStaticRln(1)
# Then Relay and RLN are not mounted,and the process fails
check:
server.wakuRelay == nil
server.wakuRlnRelay == nil
catchRes.error()[].msg ==
"WakuRelay protocol is not mounted, cannot mount WakuRlnRelay"
asyncTest "Pubsub topics subscribed before mounting RlnRelay are added to it":
# Given the node enables Relay and Rln while subscribing to a pubsub topic
await server.setupRelayWithStaticRln(1.uint, @[pubsubTopic])
await client.setupRelayWithStaticRln(2.uint, @[pubsubTopic])
check:
server.wakuRelay != nil
server.wakuRlnRelay != nil
client.wakuRelay != nil
client.wakuRlnRelay != nil
# And the nodes are connected
await client.connectToNodes(@[serverRemotePeerInfo])
# And the node registers the completion handler
var completionFuture = subscribeCompletionHandler(server, pubsubTopic)
# When the client sends a valid RLN message
let isCompleted1 =
await sendRlnMessage(client, pubsubTopic, contentTopic, completionFuture)
# Then the valid RLN message is relayed
check:
isCompleted1
completionFuture.read()
# When the client sends an invalid RLN message
completionFuture = newBoolFuture()
let isCompleted2 = await sendRlnMessageWithInvalidProof(
client, pubsubTopic, contentTopic, completionFuture
)
# Then the invalid RLN message is not relayed
check:
not isCompleted2
asyncTest "Pubsub topics subscribed after mounting RlnRelay are added to it":
# Given the node enables Relay and Rln without subscribing to a pubsub topic
await server.setupRelayWithStaticRln(1.uint, @[])
await client.setupRelayWithStaticRln(2.uint, @[])
# And the nodes are connected
await client.connectToNodes(@[serverRemotePeerInfo])
# await sleepAsync(FUTURE_TIMEOUT)
# And the node registers the completion handler
var completionFuture = subscribeCompletionHandler(server, pubsubTopic)
await sleepAsync(FUTURE_TIMEOUT)
# When the client sends a valid RLN message
let isCompleted1 =
await sendRlnMessage(client, pubsubTopic, contentTopic, completionFuture)
# Then the valid RLN message is relayed
check:
isCompleted1
completionFuture.read()
# When the client sends an invalid RLN message
completionFuture = newBoolFuture()
let isCompleted2 = await sendRlnMessageWithInvalidProof(
client, pubsubTopic, contentTopic, completionFuture
)
# Then the invalid RLN message is not relayed
check:
not isCompleted2
asyncTest "rln-relay-max-message-limit testing":
let
nodekey = generateSecp256k1Key()
node = newTestWakuNode(nodekey, parseIpAddress("0.0.0.0"), Port(0))
await node.mountRelay(@[DefaultRelayShard])
let contractAddress = await uploadRLNContract(EthClient)
let wakuRlnConfig = WakuRlnConfig(
rlnRelayDynamic: true,
rlnRelayCredIndex: some(0.uint),
rlnRelayUserMessageLimit: 111,
rlnRelayTreepath: genTempPath("rln_tree", "wakunode_0"),
rlnRelayEthClientAddress: EthClient,
rlnRelayEthContractAddress: $contractAddress,
rlnRelayChainId: 1337,
onFatalErrorAction: proc(errStr: string) =
raiseAssert errStr
,
)
try:
await node.mountRlnRelay(wakuRlnConfig)
except CatchableError as e:
check e.msg ==
"failed to mount WakuRlnRelay: rln-relay-user-message-limit can't exceed the MAX_MESSAGE_LIMIT in the rln contract"
suite "Analysis of Bandwith Limitations":
asyncTest "Valid Payload Sizes":
# Given the node enables Relay and Rln while subscribing to a pubsub topic
await server.setupRelayWithStaticRln(1.uint, @[pubsubTopic])
await client.setupRelayWithStaticRln(2.uint, @[pubsubTopic])
# And the nodes are connected
await client.connectToNodes(@[serverRemotePeerInfo])
# Register Relay Handler
var completionFut = newPushHandlerFuture()
proc relayHandler(
topic: PubsubTopic, msg: WakuMessage
): Future[void] {.async, gcsafe.} =
if topic == pubsubTopic:
completionFut.complete((topic, msg))
let subscriptionEvent = (kind: PubsubSub, topic: pubsubTopic)
server.subscribe(subscriptionEvent, some(relayHandler))
await sleepAsync(FUTURE_TIMEOUT)
# Generate Messages
let
epoch = epochTime()
payload1b = getByteSequence(1)
payload1kib = getByteSequence(1024)
overhead: uint64 = 419
payload150kib = getByteSequence((150 * 1024) - overhead)
payload150kibPlus = getByteSequence((150 * 1024) - overhead + 1)
var
message1b = WakuMessage(payload: @payload1b, contentTopic: contentTopic)
message1kib = WakuMessage(payload: @payload1kib, contentTopic: contentTopic)
message150kib = WakuMessage(payload: @payload150kib, contentTopic: contentTopic)
message151kibPlus =
WakuMessage(payload: @payload150kibPlus, contentTopic: contentTopic)
doAssert(
client.wakuRlnRelay
.appendRLNProof(
message1b, epoch + float64(client.wakuRlnRelay.rlnEpochSizeSec * 0)
)
.isOk()
)
doAssert(
client.wakuRlnRelay
.appendRLNProof(
message1kib, epoch + float64(client.wakuRlnRelay.rlnEpochSizeSec * 1)
)
.isOk()
)
doAssert(
client.wakuRlnRelay
.appendRLNProof(
message150kib, epoch + float64(client.wakuRlnRelay.rlnEpochSizeSec * 2)
)
.isOk()
)
doAssert(
client.wakuRlnRelay
.appendRLNProof(
message151kibPlus, epoch + float64(client.wakuRlnRelay.rlnEpochSizeSec * 3)
)
.isOk()
)
# When sending the 1B message
discard await client.publish(some(pubsubTopic), message1b)
discard await completionFut.withTimeout(FUTURE_TIMEOUT_LONG)
# Then the message is relayed
check completionFut.read() == (pubsubTopic, message1b)
# When sending the 1KiB message
completionFut = newPushHandlerFuture() # Reset Future
discard await client.publish(some(pubsubTopic), message1kib)
discard await completionFut.withTimeout(FUTURE_TIMEOUT_LONG)
# Then the message is relayed
check completionFut.read() == (pubsubTopic, message1kib)
# When sending the 150KiB message
completionFut = newPushHandlerFuture() # Reset Future
discard await client.publish(some(pubsubTopic), message150kib)
discard await completionFut.withTimeout(FUTURE_TIMEOUT_LONG)
# Then the message is relayed
check completionFut.read() == (pubsubTopic, message150kib)
# When sending the 150KiB plus message
completionFut = newPushHandlerFuture() # Reset Future
discard await client.publish(some(pubsubTopic), message151kibPlus)
# Then the message is not relayed
check not await completionFut.withTimeout(FUTURE_TIMEOUT_LONG)
asyncTest "Invalid Payload Sizes":
# Given the node enables Relay and Rln while subscribing to a pubsub topic
await server.setupRelayWithStaticRln(1.uint, @[pubsubTopic])
await client.setupRelayWithStaticRln(2.uint, @[pubsubTopic])
# And the nodes are connected
await client.connectToNodes(@[serverRemotePeerInfo])
# Register Relay Handler
var completionFut = newPushHandlerFuture()
proc relayHandler(
topic: PubsubTopic, msg: WakuMessage
): Future[void] {.async, gcsafe.} =
if topic == pubsubTopic:
completionFut.complete((topic, msg))
let subscriptionEvent = (kind: PubsubSub, topic: pubsubTopic)
server.subscribe(subscriptionEvent, some(relayHandler))
await sleepAsync(FUTURE_TIMEOUT)
# Generate Messages
let
epoch = epochTime()
overhead: uint64 = 419
payload150kibPlus = getByteSequence((150 * 1024) - overhead + 1)
var message151kibPlus =
WakuMessage(payload: @payload150kibPlus, contentTopic: contentTopic)
doAssert(
client.wakuRlnRelay
.appendRLNProof(
message151kibPlus, epoch + float64(client.wakuRlnRelay.rlnEpochSizeSec * 3)
)
.isOk()
)
# When sending the 150KiB plus message
completionFut = newPushHandlerFuture() # Reset Future
discard await client.publish(some(pubsubTopic), message151kibPlus)
# Then the message is not relayed
check not await completionFut.withTimeout(FUTURE_TIMEOUT_LONG)
suite "Waku RlnRelay - End to End - OnChain":
let runAnvil {.used.} = runAnvil()
var
pubsubTopic {.threadvar.}: PubsubTopic
contentTopic {.threadvar.}: ContentTopic
var
server {.threadvar.}: WakuNode
client {.threadvar.}: WakuNode
var
serverRemotePeerInfo {.threadvar.}: RemotePeerInfo
clientPeerId {.threadvar.}: PeerId
asyncSetup:
pubsubTopic = DefaultPubsubTopic
contentTopic = DefaultContentTopic
let
serverKey = generateSecp256k1Key()
clientKey = generateSecp256k1Key()
server = newTestWakuNode(serverKey, ValidIpAddress.init("0.0.0.0"), Port(0))
client = newTestWakuNode(clientKey, ValidIpAddress.init("0.0.0.0"), Port(0))
await allFutures(server.start(), client.start())
serverRemotePeerInfo = server.switch.peerInfo.toRemotePeerInfo()
clientPeerId = client.switch.peerInfo.toRemotePeerInfo().peerId
asyncTeardown:
await allFutures(client.stop(), server.stop())
suite "Smart Contract Availability and Interaction":
asyncTest "Invalid format contract":
let
# One character missing
invalidContractAddress = "0x000000000000000000000000000000000000000"
keystorePath =
genTempPath("rln_keystore", "test_wakunode_relay_rln-no_valid_contract")
appInfo = RlnAppInfo
password = "1234"
wakuRlnConfig1 = getWakuRlnConfigOnChain(
keystorePath, appInfo, invalidContractAddress, password, 0
)
wakuRlnConfig2 = getWakuRlnConfigOnChain(
keystorePath, appInfo, invalidContractAddress, password, 1
)
idCredential = buildRandomIdentityCredentials()
persistRes = addMembershipCredentialsToKeystore(
idCredential, keystorePath, appInfo, invalidContractAddress, password, 1
)
assertResultOk(persistRes)
# Given the node enables Relay and Rln while subscribing to a pubsub topic
try:
await server.setupRelayWithOnChainRln(@[pubsubTopic], wakuRlnConfig1)
assert false, "Relay should fail mounting when using an invalid contract"
except CatchableError:
assert true
try:
await client.setupRelayWithOnChainRln(@[pubsubTopic], wakuRlnConfig2)
assert false, "Relay should fail mounting when using an invalid contract"
except CatchableError:
assert true
asyncTest "Unregistered contract":
# This is a very slow test due to the retries RLN does. Might take upwards of 1m-2m to finish.
let
invalidContractAddress = "0x0000000000000000000000000000000000000000"
keystorePath =
genTempPath("rln_keystore", "test_wakunode_relay_rln-no_valid_contract")
appInfo = RlnAppInfo
password = "1234"
# Connect to the eth client
discard await newWeb3(EthClient)
var serverErrorFuture = Future[string].new()
proc serverFatalErrorHandler(errMsg: string) {.gcsafe, closure, raises: [].} =
serverErrorFuture.complete(errMsg)
var clientErrorFuture = Future[string].new()
proc clientFatalErrorHandler(errMsg: string) {.gcsafe, closure, raises: [].} =
clientErrorFuture.complete(errMsg)
let
wakuRlnConfig1 = getWakuRlnConfigOnChain(
keystorePath,
appInfo,
invalidContractAddress,
password,
0,
some(serverFatalErrorHandler),
)
wakuRlnConfig2 = getWakuRlnConfigOnChain(
keystorePath,
appInfo,
invalidContractAddress,
password,
1,
some(clientFatalErrorHandler),
)
# Given the node enable Relay and Rln while subscribing to a pubsub topic.
# The withTimeout call is a workaround for the test not to terminate with an exception.
# However, it doesn't reduce the retries against the blockchain that the mounting rln process attempts (until it accepts failure).
# Note: These retries might be an unintended library issue.
discard await server
.setupRelayWithOnChainRln(@[pubsubTopic], wakuRlnConfig1)
.withTimeout(FUTURE_TIMEOUT)
discard await client
.setupRelayWithOnChainRln(@[pubsubTopic], wakuRlnConfig2)
.withTimeout(FUTURE_TIMEOUT)
check:
(await serverErrorFuture.waitForResult()).get() ==
"Failed to get the storage index: No response from the Web3 provider"
(await clientErrorFuture.waitForResult()).get() ==
"Failed to get the storage index: No response from the Web3 provider"
asyncTest "Valid contract":
#[
# Notes
## Issues
### TreeIndex
For some reason the calls to `getWakuRlnConfigOnChain` need to be made with `treeIndex` = 0 and 1, in that order.
But the registration needs to be made with 1 and 2.
#### Solutions
Requires investigation
### Monkeypatching
Instead of running the idCredentials monkeypatch, passing the correct membershipIndex and keystorePath and keystorePassword should work.
#### Solutions
A) Using the register callback to fetch the correct membership
B) Using two different keystores, one for each rlnconfig. If there's only one key, it will fetch it regardless of membershipIndex.
##### A
- Register is not calling callback even though register is happening, this should happen.
- This command should be working, but it doesn't on the current HEAD of the branch, it does work on master, which suggest there's something wrong with the branch.
- nim c -r --out:build/onchain -d:chronicles_log_level=NOTICE --verbosity:0 --hints:off -d:git_version="v0.27.0-rc.0-3-gaa9c30" -d:release --passL:librln_v0.3.7.a --passL:-lm tests/waku_rln_relay/test_rln_group_manager_onchain.nim && onchain_group_test
- All modified files are tests/*, which is a bit weird. Might be interesting re-creating the branch slowly, and checking out why this is happening.
##### B
Untested
]#
let
onChainGroupManager = await setup()
contractAddress = onChainGroupManager.ethContractAddress
keystorePath =
genTempPath("rln_keystore", "test_wakunode_relay_rln-valid_contract")
appInfo = RlnAppInfo
password = "1234"
rlnInstance = onChainGroupManager.rlnInstance
assertResultOk(createAppKeystore(keystorePath, appInfo))
# Generate configs before registering the credentials. Otherwise the file gets cleared up.
let
wakuRlnConfig1 =
getWakuRlnConfigOnChain(keystorePath, appInfo, contractAddress, password, 0)
wakuRlnConfig2 =
getWakuRlnConfigOnChain(keystorePath, appInfo, contractAddress, password, 1)
# Generate credentials
let
idCredential1 = rlnInstance.membershipKeyGen().get()
idCredential2 = rlnInstance.membershipKeyGen().get()
discard await onChainGroupManager.init()
try:
# Register credentials in the chain
waitFor onChainGroupManager.register(idCredential1)
waitFor onChainGroupManager.register(idCredential2)
except Exception:
assert false, "Failed to register credentials: " & getCurrentExceptionMsg()
# Add credentials to keystore
let
persistRes1 = addMembershipCredentialsToKeystore(
idCredential1, keystorePath, appInfo, contractAddress, password, 0
)
persistRes2 = addMembershipCredentialsToKeystore(
idCredential2, keystorePath, appInfo, contractAddress, password, 1
)
assertResultOk(persistRes1)
assertResultOk(persistRes2)
await onChainGroupManager.stop()
# Given the node enables Relay and Rln while subscribing to a pubsub topic
await server.setupRelayWithOnChainRln(@[pubsubTopic], wakuRlnConfig1)
await client.setupRelayWithOnChainRln(@[pubsubTopic], wakuRlnConfig2)
try:
(await server.wakuRlnRelay.groupManager.startGroupSync()).isOkOr:
raiseAssert $error
(await client.wakuRlnRelay.groupManager.startGroupSync()).isOkOr:
raiseAssert $error
# Test Hack: Monkeypatch the idCredentials into the groupManager
server.wakuRlnRelay.groupManager.idCredentials = some(idCredential1)
client.wakuRlnRelay.groupManager.idCredentials = some(idCredential2)
except Exception, CatchableError:
assert false, "exception raised: " & getCurrentExceptionMsg()
# And the nodes are connected
let serverRemotePeerInfo = server.switch.peerInfo.toRemotePeerInfo()
await client.connectToNodes(@[serverRemotePeerInfo])
# And the node registers the completion handler
var completionFuture = subscribeCompletionHandler(server, pubsubTopic)
# When the client sends a valid RLN message
let isCompleted =
await sendRlnMessage(client, pubsubTopic, contentTopic, completionFuture)
# Then the valid RLN message is relayed
check isCompleted
assertResultOk(await completionFuture.waitForResult())
asyncTest "Not enough gas":
let
onChainGroupManager = await setupOnchainGroupManager(amountWei = 0.u256)
contractAddress = onChainGroupManager.ethContractAddress
keystorePath =
genTempPath("rln_keystore", "test_wakunode_relay_rln-valid_contract")
appInfo = RlnAppInfo
password = "1234"
rlnInstance = onChainGroupManager.rlnInstance
assertResultOk(createAppKeystore(keystorePath, appInfo))
# Generate credentials
let idCredential = rlnInstance.membershipKeyGen().get()
discard await onChainGroupManager.init()
var errorFuture = Future[string].new()
onChainGroupManager.onFatalErrorAction = proc(
errMsg: string
) {.gcsafe, closure.} =
errorFuture.complete(errMsg)
try:
# Register credentials in the chain
waitFor onChainGroupManager.register(idCredential)
assert false, "Should have failed to register credentials given there is 0 gas"
except Exception:
assert true
check (await errorFuture.waitForResult()).get() ==
"Failed to register the member: {\"code\":-32003,\"message\":\"Insufficient funds for gas * price + value\"}"
await onChainGroupManager.stop()
suite "RLN Relay Configuration and Parameters":
asyncTest "RLN Relay Credential Path":
let
onChainGroupManager = await setup()
contractAddress = onChainGroupManager.ethContractAddress
keystorePath =
genTempPath("rln_keystore", "test_wakunode_relay_rln-valid_contract")
appInfo = RlnAppInfo
password = "1234"
rlnInstance = onChainGroupManager.rlnInstance
assertResultOk(createAppKeystore(keystorePath, appInfo))
# Generate configs before registering the credentials. Otherwise the file gets cleared up.
let
wakuRlnConfig1 =
getWakuRlnConfigOnChain(keystorePath, appInfo, contractAddress, password, 0)
wakuRlnConfig2 =
getWakuRlnConfigOnChain(keystorePath, appInfo, contractAddress, password, 1)
# Given the node enables Relay and Rln while subscribing to a pubsub topic
await server.setupRelayWithOnChainRln(@[pubsubTopic], wakuRlnConfig1)
await client.setupRelayWithOnChainRln(@[pubsubTopic], wakuRlnConfig2)
try:
(await server.wakuRlnRelay.groupManager.startGroupSync()).isOkOr:
raiseAssert $error
(await client.wakuRlnRelay.groupManager.startGroupSync()).isOkOr:
raiseAssert $error
# Test Hack: Monkeypatch the idCredentials into the groupManager
echo server.wakuRlnRelay.groupManager.idCredentials
echo client.wakuRlnRelay.groupManager.idCredentials
except Exception, CatchableError:
assert false, "exception raised: " & getCurrentExceptionMsg()
# And the nodes are connected
let serverRemotePeerInfo = server.switch.peerInfo.toRemotePeerInfo()
await client.connectToNodes(@[serverRemotePeerInfo])
# And the node registers the completion handler
var completionFuture = subscribeCompletionHandler(server, pubsubTopic)
# When the client attempts to send a message
try:
let isCompleted =
await sendRlnMessage(client, pubsubTopic, contentTopic, completionFuture)
assert false, "Should have failed to send a message"
except AssertionDefect as e:
# Then the message is not relayed
assert e.msg.endsWith("identity credentials are not set")
suite "RLN Relay Resilience, Security and Compatibility":
asyncTest "Key Management and Integrity":
let
onChainGroupManager = await setup()
contractAddress = onChainGroupManager.ethContractAddress
keystorePath =
genTempPath("rln_keystore", "test_wakunode_relay_rln-valid_contract")
appInfo = RlnAppInfo
password = "1234"
rlnInstance = onChainGroupManager.rlnInstance
assertResultOk(createAppKeystore(keystorePath, appInfo))
# Generate configs before registering the credentials. Otherwise the file gets cleared up.
let
wakuRlnConfig1 =
getWakuRlnConfigOnChain(keystorePath, appInfo, contractAddress, password, 0)
wakuRlnConfig2 =
getWakuRlnConfigOnChain(keystorePath, appInfo, contractAddress, password, 1)
# Generate credentials
let
idCredential1 = rlnInstance.membershipKeyGen().get()
idCredential2 = rlnInstance.membershipKeyGen().get()
discard await onChainGroupManager.init()
try:
# Register credentials in the chain
waitFor onChainGroupManager.register(idCredential1)
waitFor onChainGroupManager.register(idCredential2)
except Exception:
assert false, "Failed to register credentials: " & getCurrentExceptionMsg()
# Add credentials to keystore
let
persistRes1 = addMembershipCredentialsToKeystore(
idCredential1, keystorePath, appInfo, contractAddress, password, 0
)
persistRes2 = addMembershipCredentialsToKeystore(
idCredential2, keystorePath, appInfo, contractAddress, password, 1
)
assertResultOk(persistRes1)
assertResultOk(persistRes2)
# await onChainGroupManager.stop()
let
registryContract = onChainGroupManager.registryContract.get()
storageIndex = (await registryContract.usingStorageIndex().call())
rlnContractAddress = await registryContract.storages(storageIndex).call()
contract = onChainGroupManager.ethRpc.get().contractSender(
RlnStorage, rlnContractAddress
)
contract2 = onChainGroupManager.rlnContract.get()
echo "###"
echo await (contract.memberExists(idCredential1.idCommitment.toUInt256()).call())
echo await (contract.memberExists(idCredential2.idCommitment.toUInt256()).call())
echo await (contract2.memberExists(idCredential1.idCommitment.toUInt256()).call())
echo await (contract2.memberExists(idCredential2.idCommitment.toUInt256()).call())
echo "###"
################################
## Terminating/removing Anvil
################################
# We stop Anvil daemon
stopAnvil(runAnvil)