mirror of
https://github.com/logos-messaging/logos-messaging-nim.git
synced 2026-01-02 14:03:06 +00:00
feat: Update implementation for new contract abi (#3390)
* update RLN contract abi functions and procs * Clean up debugging lines * Use more descriptive object field names for MembershipInfo * Fix formatting * fix group_manager after rebase to use new contract method sig * Fix linting for group_manager.nim * Test idcommitment to BE and debug logs * Improve IdCommitment logging * Update all keystore credentials to use BE format * Add workaround for groupmanager web3 eth_call * Add await to sendEthCallWithChainID * Add error handling for failed eth_call * Improve error handling for eth_call workaround * Revert keystore credentials back to using LE * Update toRateCommitment proc to use LE instead of BE * Add IdCommitment to calldata as BE * feat: Update rln contract deployment and tests (#3408) * update RLN contract abi functions and procs * update waku-rlnv2-contract submodule commit to latest * Add RlnV2 contract deployment using forge scripts * Clean up output of forge script command, debug logs to trace, warn to error * Move TestToken deployment to own proc * first implementation of token minting and approval * Update rln tests with usermessagelimit new minimum * Clean up code and error handling * Rework RLN tests WIP * Fix RLN test for new contract * RLN Tests updated * Fix formatting * Improve error logs * Fix error message formatting * Fix linting * Add pnpm dependency installation for rln tests * Update test dependencies in makefile * Minor updates, error messages etc * Code cleanup and change some debug logging to trace * Improve handling of Result return value * Use absolute path for waku-rlnv2-contract * Simplify token approval and balance check * Remove unused Anvil options * Add additional checks for stopAnvil process * Fix anvil process call to null * Add lock to tests for rln_group_manager_onchain * Debug for forge command * Verify paths * Install pnpm as global * Cleanup anvil running procs * Add check before installing anvil * CLean up onchain group_manager * Add proc to setup environment for contract deployer * Refactoring and improved error handling * Fix anvil install directory string * Fix linting in test_range_split * Add const for the contract address length * Add separate checks for why Approval transaction fails * Update RLN contract address and chainID for TWN
This commit is contained in:
parent
fd5780eae7
commit
d3cf24f7a2
11
Makefile
11
Makefile
@ -112,11 +112,8 @@ ifeq (, $(shell which cargo))
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain stable
|
||||
endif
|
||||
|
||||
anvil: rustup
|
||||
ifeq (, $(shell which anvil 2> /dev/null))
|
||||
# Install Anvil if it's not installed
|
||||
./scripts/install_anvil.sh
|
||||
endif
|
||||
rln-deps: rustup
|
||||
./scripts/install_rln_tests_dependencies.sh
|
||||
|
||||
deps: | deps-common nat-libs waku.nims
|
||||
|
||||
@ -205,8 +202,8 @@ testcommon: | build deps
|
||||
##########
|
||||
.PHONY: testwaku wakunode2 testwakunode2 example2 chat2 chat2bridge liteprotocoltester
|
||||
|
||||
# install anvil only for the testwaku target
|
||||
testwaku: | build deps anvil librln
|
||||
# install rln-deps only for the testwaku target
|
||||
testwaku: | build deps rln-deps librln
|
||||
echo -e $(BUILD_MSG) "build/$@" && \
|
||||
$(ENV_SCRIPT) nim test -d:os=$(shell uname) $(NIM_PARAMS) waku.nims
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ x-logging: &logging
|
||||
x-rln-relay-eth-client-address: &rln_relay_eth_client_address ${RLN_RELAY_ETH_CLIENT_ADDRESS:-} # Add your RLN_RELAY_ETH_CLIENT_ADDRESS after the "-"
|
||||
|
||||
x-rln-environment: &rln_env
|
||||
RLN_RELAY_CONTRACT_ADDRESS: ${RLN_RELAY_CONTRACT_ADDRESS:-0xfe7a9eabcE779a090FD702346Fd0bFAc02ce6Ac8}
|
||||
RLN_RELAY_CONTRACT_ADDRESS: ${RLN_RELAY_CONTRACT_ADDRESS:-0xB9cd878C90E49F797B4431fBF4fb333108CB90e6}
|
||||
RLN_RELAY_CRED_PATH: ${RLN_RELAY_CRED_PATH:-} # Optional: Add your RLN_RELAY_CRED_PATH after the "-"
|
||||
RLN_RELAY_CRED_PASSWORD: ${RLN_RELAY_CRED_PASSWORD:-} # Optional: Add your RLN_RELAY_CRED_PASSWORD after the "-"
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@ fi
|
||||
docker run -v $(pwd)/keystore:/keystore/:Z harbor.status.im/wakuorg/nwaku:v0.30.1 generateRlnKeystore \
|
||||
--rln-relay-eth-client-address=${RLN_RELAY_ETH_CLIENT_ADDRESS} \
|
||||
--rln-relay-eth-private-key=${ETH_TESTNET_KEY} \
|
||||
--rln-relay-eth-contract-address=0xfe7a9eabcE779a090FD702346Fd0bFAc02ce6Ac8 \
|
||||
--rln-relay-eth-contract-address=0xB9cd878C90E49F797B4431fBF4fb333108CB90e6 \
|
||||
--rln-relay-cred-path=/keystore/keystore.json \
|
||||
--rln-relay-cred-password="${RLN_RELAY_CRED_PASSWORD}" \
|
||||
--rln-relay-user-message-limit=20 \
|
||||
|
||||
@ -2,13 +2,14 @@
|
||||
|
||||
# Install Anvil
|
||||
|
||||
if ! command -v anvil &> /dev/null; then
|
||||
BASE_DIR="${XDG_CONFIG_HOME:-$HOME}"
|
||||
FOUNDRY_DIR="${FOUNDRY_DIR:-"$BASE_DIR/.foundry"}"
|
||||
FOUNDRY_BIN_DIR="$FOUNDRY_DIR/bin"
|
||||
|
||||
BASE_DIR="${XDG_CONFIG_HOME:-$HOME}"
|
||||
FOUNDRY_DIR="${FOUNDRY_DIR-"$BASE_DIR/.foundry"}"
|
||||
FOUNDRY_BIN_DIR="$FOUNDRY_DIR/bin"
|
||||
|
||||
curl -L https://foundry.paradigm.xyz | bash
|
||||
# Extract the source path from the download result
|
||||
echo "foundryup_path: $FOUNDRY_BIN_DIR"
|
||||
# run foundryup
|
||||
$FOUNDRY_BIN_DIR/foundryup
|
||||
curl -L https://foundry.paradigm.xyz | bash
|
||||
# Extract the source path from the download result
|
||||
echo "foundryup_path: $FOUNDRY_BIN_DIR"
|
||||
# run foundryup
|
||||
$FOUNDRY_BIN_DIR/foundryup
|
||||
fi
|
||||
8
scripts/install_pnpm.sh
Executable file
8
scripts/install_pnpm.sh
Executable file
@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Install pnpm
|
||||
if ! command -v pnpm &> /dev/null; then
|
||||
echo "pnpm is not installed, installing it now..."
|
||||
npm i pnpm --global
|
||||
fi
|
||||
|
||||
7
scripts/install_rln_tests_dependencies.sh
Executable file
7
scripts/install_rln_tests_dependencies.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Install Anvil
|
||||
./scripts/install_anvil.sh
|
||||
|
||||
#Install pnpm
|
||||
./scripts/install_pnpm.sh
|
||||
@ -3,7 +3,7 @@
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
std/[options, sequtils, deques, random],
|
||||
std/[options, sequtils, deques, random, locks],
|
||||
results,
|
||||
stew/byteutils,
|
||||
testutils/unittests,
|
||||
@ -28,58 +28,71 @@ import
|
||||
../testlib/wakucore,
|
||||
./utils_onchain
|
||||
|
||||
var testLock: Lock
|
||||
initLock(testLock)
|
||||
|
||||
suite "Onchain group manager":
|
||||
# We run Anvil
|
||||
let runAnvil {.used.} = runAnvil()
|
||||
setup:
|
||||
# Acquire lock to ensure tests run sequentially
|
||||
acquire(testLock)
|
||||
|
||||
var manager {.threadvar.}: OnchainGroupManager
|
||||
let runAnvil {.used.} = runAnvil()
|
||||
|
||||
asyncSetup:
|
||||
manager = await setupOnchainGroupManager()
|
||||
var manager {.threadvar.}: OnchainGroupManager
|
||||
manager = waitFor setupOnchainGroupManager()
|
||||
|
||||
asyncTeardown:
|
||||
await manager.stop()
|
||||
teardown:
|
||||
waitFor manager.stop()
|
||||
stopAnvil(runAnvil)
|
||||
# Release lock after test completes
|
||||
release(testLock)
|
||||
|
||||
asyncTest "should initialize successfully":
|
||||
(await manager.init()).isOkOr:
|
||||
test "should initialize successfully":
|
||||
(waitFor manager.init()).isOkOr:
|
||||
raiseAssert $error
|
||||
|
||||
check:
|
||||
manager.ethRpc.isSome()
|
||||
manager.wakuRlnContract.isSome()
|
||||
manager.initialized
|
||||
manager.rlnRelayMaxMessageLimit == 100
|
||||
manager.rlnRelayMaxMessageLimit == 600
|
||||
|
||||
asyncTest "should error on initialization when chainId does not match":
|
||||
test "should error on initialization when chainId does not match":
|
||||
manager.chainId = utils_onchain.CHAIN_ID + 1
|
||||
|
||||
(await manager.init()).isErrOr:
|
||||
(waitFor manager.init()).isErrOr:
|
||||
raiseAssert "Expected error when chainId does not match"
|
||||
|
||||
asyncTest "should initialize when chainId is set to 0":
|
||||
test "should initialize when chainId is set to 0":
|
||||
manager.chainId = 0x0'u256
|
||||
|
||||
(await manager.init()).isOkOr:
|
||||
(waitFor manager.init()).isOkOr:
|
||||
raiseAssert $error
|
||||
|
||||
asyncTest "should error on initialization when loaded metadata does not match":
|
||||
(await manager.init()).isOkOr:
|
||||
test "should error on initialization when loaded metadata does not match":
|
||||
(waitFor manager.init()).isOkOr:
|
||||
assert false, $error
|
||||
|
||||
let metadataSetRes = manager.setMetadata()
|
||||
assert metadataSetRes.isOk(), metadataSetRes.error
|
||||
let metadataOpt = manager.rlnInstance.getMetadata().valueOr:
|
||||
assert false, $error
|
||||
return
|
||||
|
||||
assert metadataOpt.isSome(), "metadata is not set"
|
||||
let metadata = metadataOpt.get()
|
||||
|
||||
assert metadata.chainId == 1337, "chainId is not equal to 1337"
|
||||
assert metadata.chainId == 1234, "chainId is not equal to 1234"
|
||||
assert metadata.contractAddress == manager.ethContractAddress,
|
||||
"contractAddress is not equal to " & manager.ethContractAddress
|
||||
|
||||
let differentContractAddress = await uploadRLNContract(manager.ethClientUrls[0])
|
||||
let web3 = manager.ethRpc.get()
|
||||
let accounts = waitFor web3.provider.eth_accounts()
|
||||
web3.defaultAccount = accounts[2]
|
||||
let (privateKey, acc) = createEthAccount(web3)
|
||||
let tokenAddress = (waitFor deployTestToken(privateKey, acc, web3)).valueOr:
|
||||
assert false, "Failed to deploy test token contract: " & $error
|
||||
return
|
||||
let differentContractAddress = (
|
||||
waitFor executeForgeContractDeployScripts(privateKey, acc, web3)
|
||||
).valueOr:
|
||||
assert false, "Failed to deploy RLN contract: " & $error
|
||||
return
|
||||
# simulating a change in the contractAddress
|
||||
let manager2 = OnchainGroupManager(
|
||||
ethClientUrls: @[EthClient],
|
||||
@ -89,52 +102,47 @@ suite "Onchain group manager":
|
||||
assert false, errStr
|
||||
,
|
||||
)
|
||||
let e = await manager2.init()
|
||||
let e = waitFor manager2.init()
|
||||
(e).isErrOr:
|
||||
assert false, "Expected error when contract address doesn't match"
|
||||
|
||||
asyncTest "should error if contract does not exist":
|
||||
test "should error if contract does not exist":
|
||||
manager.ethContractAddress = "0x0000000000000000000000000000000000000000"
|
||||
|
||||
var triggeredError = false
|
||||
try:
|
||||
discard await manager.init()
|
||||
except CatchableError:
|
||||
triggeredError = true
|
||||
(waitFor manager.init()).isErrOr:
|
||||
raiseAssert "Expected error when contract address doesn't exist"
|
||||
|
||||
check triggeredError
|
||||
|
||||
asyncTest "should error when keystore path and password are provided but file doesn't exist":
|
||||
test "should error when keystore path and password are provided but file doesn't exist":
|
||||
manager.keystorePath = some("/inexistent/file")
|
||||
manager.keystorePassword = some("password")
|
||||
|
||||
(await manager.init()).isErrOr:
|
||||
(waitFor manager.init()).isErrOr:
|
||||
raiseAssert "Expected error when keystore file doesn't exist"
|
||||
|
||||
asyncTest "trackRootChanges: start tracking roots":
|
||||
(await manager.init()).isOkOr:
|
||||
test "trackRootChanges: start tracking roots":
|
||||
(waitFor manager.init()).isOkOr:
|
||||
raiseAssert $error
|
||||
discard manager.trackRootChanges()
|
||||
|
||||
asyncTest "trackRootChanges: should guard against uninitialized state":
|
||||
test "trackRootChanges: should guard against uninitialized state":
|
||||
try:
|
||||
discard manager.trackRootChanges()
|
||||
except CatchableError:
|
||||
check getCurrentExceptionMsg().len == 38
|
||||
|
||||
asyncTest "trackRootChanges: should sync to the state of the group":
|
||||
test "trackRootChanges: should sync to the state of the group":
|
||||
let credentials = generateCredentials(manager.rlnInstance)
|
||||
(await manager.init()).isOkOr:
|
||||
(waitFor manager.init()).isOkOr:
|
||||
raiseAssert $error
|
||||
|
||||
let merkleRootBefore = manager.fetchMerkleRoot()
|
||||
|
||||
try:
|
||||
await manager.register(credentials, UserMessageLimit(1))
|
||||
waitFor manager.register(credentials, UserMessageLimit(20))
|
||||
except Exception, CatchableError:
|
||||
assert false, "exception raised: " & getCurrentExceptionMsg()
|
||||
|
||||
discard await withTimeout(trackRootChanges(manager), 15.seconds)
|
||||
discard waitFor withTimeout(trackRootChanges(manager), 15.seconds)
|
||||
|
||||
let merkleRootAfter = manager.fetchMerkleRoot()
|
||||
|
||||
@ -151,7 +159,7 @@ suite "Onchain group manager":
|
||||
metadata.validRoots == manager.validRoots.toSeq()
|
||||
merkleRootBefore != merkleRootAfter
|
||||
|
||||
asyncTest "trackRootChanges: should fetch history correctly":
|
||||
test "trackRootChanges: should fetch history correctly":
|
||||
# TODO: We can't use `trackRootChanges()` directly in this test because its current implementation
|
||||
# relies on a busy loop rather than event-based monitoring. As a result, some root changes
|
||||
# may be missed, leading to inconsistent test results (i.e., it may randomly return true or false).
|
||||
@ -159,15 +167,16 @@ suite "Onchain group manager":
|
||||
# after each registration.
|
||||
const credentialCount = 6
|
||||
let credentials = generateCredentials(manager.rlnInstance, credentialCount)
|
||||
(await manager.init()).isOkOr:
|
||||
(waitFor manager.init()).isOkOr:
|
||||
raiseAssert $error
|
||||
|
||||
let merkleRootBefore = manager.fetchMerkleRoot()
|
||||
|
||||
try:
|
||||
for i in 0 ..< credentials.len():
|
||||
await manager.register(credentials[i], UserMessageLimit(1))
|
||||
discard await manager.updateRoots()
|
||||
debug "Registering credential", index = i, credential = credentials[i]
|
||||
waitFor manager.register(credentials[i], UserMessageLimit(20))
|
||||
discard waitFor manager.updateRoots()
|
||||
except Exception, CatchableError:
|
||||
assert false, "exception raised: " & getCurrentExceptionMsg()
|
||||
|
||||
@ -177,13 +186,13 @@ suite "Onchain group manager":
|
||||
merkleRootBefore != merkleRootAfter
|
||||
manager.validRoots.len() == credentialCount
|
||||
|
||||
asyncTest "register: should guard against uninitialized state":
|
||||
test "register: should guard against uninitialized state":
|
||||
let dummyCommitment = default(IDCommitment)
|
||||
|
||||
try:
|
||||
await manager.register(
|
||||
waitFor manager.register(
|
||||
RateCommitment(
|
||||
idCommitment: dummyCommitment, userMessageLimit: UserMessageLimit(1)
|
||||
idCommitment: dummyCommitment, userMessageLimit: UserMessageLimit(20)
|
||||
)
|
||||
)
|
||||
except CatchableError:
|
||||
@ -191,18 +200,18 @@ suite "Onchain group manager":
|
||||
except Exception:
|
||||
assert false, "exception raised: " & getCurrentExceptionMsg()
|
||||
|
||||
asyncTest "register: should register successfully":
|
||||
test "register: should register successfully":
|
||||
# TODO :- similar to ```trackRootChanges: should fetch history correctly```
|
||||
(await manager.init()).isOkOr:
|
||||
(waitFor manager.init()).isOkOr:
|
||||
raiseAssert $error
|
||||
|
||||
let idCommitment = generateCredentials(manager.rlnInstance).idCommitment
|
||||
let merkleRootBefore = manager.fetchMerkleRoot()
|
||||
|
||||
try:
|
||||
await manager.register(
|
||||
waitFor manager.register(
|
||||
RateCommitment(
|
||||
idCommitment: idCommitment, userMessageLimit: UserMessageLimit(1)
|
||||
idCommitment: idCommitment, userMessageLimit: UserMessageLimit(20)
|
||||
)
|
||||
)
|
||||
except Exception, CatchableError:
|
||||
@ -215,47 +224,47 @@ suite "Onchain group manager":
|
||||
merkleRootAfter != merkleRootBefore
|
||||
manager.latestIndex == 1
|
||||
|
||||
asyncTest "register: callback is called":
|
||||
test "register: callback is called":
|
||||
let idCredentials = generateCredentials(manager.rlnInstance)
|
||||
let idCommitment = idCredentials.idCommitment
|
||||
|
||||
let fut = newFuture[void]()
|
||||
|
||||
proc callback(registrations: seq[Membership]): Future[void] {.async.} =
|
||||
let rateCommitment = getRateCommitment(idCredentials, UserMessageLimit(1)).get()
|
||||
let rateCommitment = getRateCommitment(idCredentials, UserMessageLimit(20)).get()
|
||||
check:
|
||||
registrations.len == 1
|
||||
registrations[0].rateCommitment == rateCommitment
|
||||
registrations[0].index == 0
|
||||
fut.complete()
|
||||
|
||||
(await manager.init()).isOkOr:
|
||||
(waitFor manager.init()).isOkOr:
|
||||
raiseAssert $error
|
||||
|
||||
manager.onRegister(callback)
|
||||
|
||||
try:
|
||||
await manager.register(
|
||||
waitFor manager.register(
|
||||
RateCommitment(
|
||||
idCommitment: idCommitment, userMessageLimit: UserMessageLimit(1)
|
||||
idCommitment: idCommitment, userMessageLimit: UserMessageLimit(20)
|
||||
)
|
||||
)
|
||||
except Exception, CatchableError:
|
||||
assert false, "exception raised: " & getCurrentExceptionMsg()
|
||||
|
||||
await fut
|
||||
waitFor fut
|
||||
|
||||
asyncTest "withdraw: should guard against uninitialized state":
|
||||
test "withdraw: should guard against uninitialized state":
|
||||
let idSecretHash = generateCredentials(manager.rlnInstance).idSecretHash
|
||||
|
||||
try:
|
||||
await manager.withdraw(idSecretHash)
|
||||
waitFor manager.withdraw(idSecretHash)
|
||||
except CatchableError:
|
||||
assert true
|
||||
except Exception:
|
||||
assert false, "exception raised: " & getCurrentExceptionMsg()
|
||||
|
||||
asyncTest "validateRoot: should validate good root":
|
||||
test "validateRoot: should validate good root":
|
||||
let idCredentials = generateCredentials(manager.rlnInstance)
|
||||
let idCommitment = idCredentials.idCommitment
|
||||
|
||||
@ -264,27 +273,27 @@ suite "Onchain group manager":
|
||||
proc callback(registrations: seq[Membership]): Future[void] {.async.} =
|
||||
if registrations.len == 1 and
|
||||
registrations[0].rateCommitment ==
|
||||
getRateCommitment(idCredentials, UserMessageLimit(1)).get() and
|
||||
getRateCommitment(idCredentials, UserMessageLimit(20)).get() and
|
||||
registrations[0].index == 0:
|
||||
manager.idCredentials = some(idCredentials)
|
||||
fut.complete()
|
||||
|
||||
manager.onRegister(callback)
|
||||
|
||||
(await manager.init()).isOkOr:
|
||||
(waitFor manager.init()).isOkOr:
|
||||
raiseAssert $error
|
||||
|
||||
try:
|
||||
await manager.register(idCredentials, UserMessageLimit(1))
|
||||
waitFor manager.register(idCredentials, UserMessageLimit(20))
|
||||
except Exception, CatchableError:
|
||||
assert false, "exception raised: " & getCurrentExceptionMsg()
|
||||
|
||||
await fut
|
||||
waitFor fut
|
||||
|
||||
let rootUpdated = await manager.updateRoots()
|
||||
let rootUpdated = waitFor manager.updateRoots()
|
||||
|
||||
if rootUpdated:
|
||||
let proofResult = await manager.fetchMerkleProofElements()
|
||||
let proofResult = waitFor manager.fetchMerkleProofElements()
|
||||
if proofResult.isErr():
|
||||
error "Failed to fetch Merkle proof", error = proofResult.error
|
||||
manager.merkleProofCache = proofResult.get()
|
||||
@ -306,14 +315,14 @@ suite "Onchain group manager":
|
||||
check:
|
||||
validated
|
||||
|
||||
asyncTest "validateRoot: should reject bad root":
|
||||
test "validateRoot: should reject bad root":
|
||||
let idCredentials = generateCredentials(manager.rlnInstance)
|
||||
let idCommitment = idCredentials.idCommitment
|
||||
|
||||
(await manager.init()).isOkOr:
|
||||
(waitFor manager.init()).isOkOr:
|
||||
raiseAssert $error
|
||||
|
||||
manager.userMessageLimit = some(UserMessageLimit(1))
|
||||
manager.userMessageLimit = some(UserMessageLimit(20))
|
||||
manager.membershipIndex = some(MembershipIndex(0))
|
||||
manager.idCredentials = some(idCredentials)
|
||||
|
||||
@ -339,9 +348,9 @@ suite "Onchain group manager":
|
||||
check:
|
||||
validated == false
|
||||
|
||||
asyncTest "verifyProof: should verify valid proof":
|
||||
test "verifyProof: should verify valid proof":
|
||||
let credentials = generateCredentials(manager.rlnInstance)
|
||||
(await manager.init()).isOkOr:
|
||||
(waitFor manager.init()).isOkOr:
|
||||
raiseAssert $error
|
||||
|
||||
let fut = newFuture[void]()
|
||||
@ -349,7 +358,7 @@ suite "Onchain group manager":
|
||||
proc callback(registrations: seq[Membership]): Future[void] {.async.} =
|
||||
if registrations.len == 1 and
|
||||
registrations[0].rateCommitment ==
|
||||
getRateCommitment(credentials, UserMessageLimit(1)).get() and
|
||||
getRateCommitment(credentials, UserMessageLimit(20)).get() and
|
||||
registrations[0].index == 0:
|
||||
manager.idCredentials = some(credentials)
|
||||
fut.complete()
|
||||
@ -357,15 +366,15 @@ suite "Onchain group manager":
|
||||
manager.onRegister(callback)
|
||||
|
||||
try:
|
||||
await manager.register(credentials, UserMessageLimit(1))
|
||||
waitFor manager.register(credentials, UserMessageLimit(20))
|
||||
except Exception, CatchableError:
|
||||
assert false, "exception raised: " & getCurrentExceptionMsg()
|
||||
await fut
|
||||
waitFor fut
|
||||
|
||||
let rootUpdated = await manager.updateRoots()
|
||||
let rootUpdated = waitFor manager.updateRoots()
|
||||
|
||||
if rootUpdated:
|
||||
let proofResult = await manager.fetchMerkleProofElements()
|
||||
let proofResult = waitFor manager.fetchMerkleProofElements()
|
||||
if proofResult.isErr():
|
||||
error "Failed to fetch Merkle proof", error = proofResult.error
|
||||
manager.merkleProofCache = proofResult.get()
|
||||
@ -388,21 +397,21 @@ suite "Onchain group manager":
|
||||
check:
|
||||
verified
|
||||
|
||||
asyncTest "verifyProof: should reject invalid proof":
|
||||
(await manager.init()).isOkOr:
|
||||
test "verifyProof: should reject invalid proof":
|
||||
(waitFor manager.init()).isOkOr:
|
||||
raiseAssert $error
|
||||
|
||||
let idCredential = generateCredentials(manager.rlnInstance)
|
||||
|
||||
try:
|
||||
await manager.register(idCredential, UserMessageLimit(1))
|
||||
waitFor manager.register(idCredential, UserMessageLimit(20))
|
||||
except Exception, CatchableError:
|
||||
assert false,
|
||||
"exception raised when calling startGroupSync: " & getCurrentExceptionMsg()
|
||||
|
||||
let messageBytes = "Hello".toBytes()
|
||||
|
||||
let rootUpdated = await manager.updateRoots()
|
||||
let rootUpdated = waitFor manager.updateRoots()
|
||||
|
||||
manager.merkleProofCache = newSeq[byte](640)
|
||||
for i in 0 ..< 640:
|
||||
@ -427,10 +436,10 @@ suite "Onchain group manager":
|
||||
check:
|
||||
verified == false
|
||||
|
||||
asyncTest "root queue should be updated correctly":
|
||||
test "root queue should be updated correctly":
|
||||
const credentialCount = 12
|
||||
let credentials = generateCredentials(manager.rlnInstance, credentialCount)
|
||||
(await manager.init()).isOkOr:
|
||||
(waitFor manager.init()).isOkOr:
|
||||
raiseAssert $error
|
||||
|
||||
type TestBackfillFuts = array[0 .. credentialCount - 1, Future[void]]
|
||||
@ -445,7 +454,7 @@ suite "Onchain group manager":
|
||||
proc callback(registrations: seq[Membership]): Future[void] {.async.} =
|
||||
if registrations.len == 1 and
|
||||
registrations[0].rateCommitment ==
|
||||
getRateCommitment(credentials[futureIndex], UserMessageLimit(1)).get() and
|
||||
getRateCommitment(credentials[futureIndex], UserMessageLimit(20)).get() and
|
||||
registrations[0].index == MembershipIndex(futureIndex):
|
||||
futs[futureIndex].complete()
|
||||
futureIndex += 1
|
||||
@ -456,47 +465,40 @@ suite "Onchain group manager":
|
||||
manager.onRegister(generateCallback(futures, credentials))
|
||||
|
||||
for i in 0 ..< credentials.len():
|
||||
await manager.register(credentials[i], UserMessageLimit(1))
|
||||
discard await manager.updateRoots()
|
||||
waitFor manager.register(credentials[i], UserMessageLimit(20))
|
||||
discard waitFor manager.updateRoots()
|
||||
except Exception, CatchableError:
|
||||
assert false, "exception raised: " & getCurrentExceptionMsg()
|
||||
|
||||
await allFutures(futures)
|
||||
waitFor allFutures(futures)
|
||||
|
||||
check:
|
||||
manager.validRoots.len() == credentialCount
|
||||
|
||||
asyncTest "isReady should return false if ethRpc is none":
|
||||
(await manager.init()).isOkOr:
|
||||
test "isReady should return false if ethRpc is none":
|
||||
(waitFor manager.init()).isOkOr:
|
||||
raiseAssert $error
|
||||
|
||||
manager.ethRpc = none(Web3)
|
||||
|
||||
var isReady = true
|
||||
try:
|
||||
isReady = await manager.isReady()
|
||||
isReady = waitFor manager.isReady()
|
||||
except Exception, CatchableError:
|
||||
assert false, "exception raised: " & getCurrentExceptionMsg()
|
||||
|
||||
check:
|
||||
isReady == false
|
||||
|
||||
asyncTest "isReady should return true if ethRpc is ready":
|
||||
(await manager.init()).isOkOr:
|
||||
test "isReady should return true if ethRpc is ready":
|
||||
(waitFor manager.init()).isOkOr:
|
||||
raiseAssert $error
|
||||
|
||||
var isReady = false
|
||||
try:
|
||||
isReady = await manager.isReady()
|
||||
isReady = waitFor manager.isReady()
|
||||
except Exception, CatchableError:
|
||||
assert false, "exception raised: " & getCurrentExceptionMsg()
|
||||
|
||||
check:
|
||||
isReady == true
|
||||
|
||||
################################
|
||||
## Terminating/removing Anvil
|
||||
################################
|
||||
|
||||
# We stop Anvil daemon
|
||||
stopAnvil(runAnvil)
|
||||
|
||||
@ -30,7 +30,7 @@ import
|
||||
../testlib/common,
|
||||
./utils
|
||||
|
||||
const CHAIN_ID* = 1337'u256
|
||||
const CHAIN_ID* = 1234'u256
|
||||
|
||||
template skip0xPrefix(hexStr: string): int =
|
||||
## Returns the index of the first meaningful char in `hexStr` by skipping
|
||||
@ -61,64 +61,347 @@ proc generateCredentials*(rlnInstance: ptr RLN, n: int): seq[IdentityCredential]
|
||||
credentials.add(generateCredentials(rlnInstance))
|
||||
return credentials
|
||||
|
||||
# a util function used for testing purposes
|
||||
# it deploys membership contract on Anvil (or any Eth client available on EthClient address)
|
||||
# must be edited if used for a different contract than membership contract
|
||||
# <the difference between this and rln-v1 is that there is no need to deploy the poseidon hasher contract>
|
||||
proc uploadRLNContract*(ethClientAddress: string): Future[Address] {.async.} =
|
||||
let web3 = await newWeb3(ethClientAddress)
|
||||
debug "web3 connected to", ethClientAddress
|
||||
proc getContractAddressFromDeployScriptOutput(output: string): Result[string, string] =
|
||||
const searchStr = "Return ==\n0: address "
|
||||
const addressLength = 42 # Length of an Ethereum address in hex format
|
||||
let idx = output.find(searchStr)
|
||||
if idx >= 0:
|
||||
let startPos = idx + searchStr.len
|
||||
let endPos = output.find('\n', startPos)
|
||||
if (endPos - startPos) >= addressLength:
|
||||
let address = output[startPos ..< endPos]
|
||||
return ok(address)
|
||||
return err("Unable to find contract address in deploy script output")
|
||||
|
||||
# fetch the list of registered accounts
|
||||
let accounts = await web3.provider.eth_accounts()
|
||||
web3.defaultAccount = accounts[1]
|
||||
let add = web3.defaultAccount
|
||||
debug "contract deployer account address ", add
|
||||
proc getForgePath(): string =
|
||||
var forgePath = ""
|
||||
if existsEnv("XDG_CONFIG_HOME"):
|
||||
forgePath = joinPath(forgePath, os.getEnv("XDG_CONFIG_HOME", ""))
|
||||
else:
|
||||
forgePath = joinPath(forgePath, os.getEnv("HOME", ""))
|
||||
forgePath = joinPath(forgePath, ".foundry/bin/forge")
|
||||
return $forgePath
|
||||
|
||||
let balance =
|
||||
await web3.provider.eth_getBalance(web3.defaultAccount, blockId("latest"))
|
||||
debug "Initial account balance: ", balance
|
||||
contract(ERC20Token):
|
||||
proc allowance(owner: Address, spender: Address): UInt256 {.view.}
|
||||
proc balanceOf(account: Address): UInt256 {.view.}
|
||||
|
||||
# deploy poseidon hasher bytecode
|
||||
let poseidonT3Receipt = await web3.deployContract(PoseidonT3)
|
||||
let poseidonT3Address = poseidonT3Receipt.contractAddress.get()
|
||||
let poseidonAddressStripped = strip0xPrefix($poseidonT3Address)
|
||||
proc getTokenBalance(
|
||||
web3: Web3, tokenAddress: Address, account: Address
|
||||
): Future[UInt256] {.async.} =
|
||||
let token = web3.contractSender(ERC20Token, tokenAddress)
|
||||
return await token.balanceOf(account).call()
|
||||
|
||||
# deploy lazy imt bytecode
|
||||
let lazyImtReceipt = await web3.deployContract(
|
||||
LazyIMT.replace("__$PoseidonT3$__", poseidonAddressStripped)
|
||||
)
|
||||
let lazyImtAddress = lazyImtReceipt.contractAddress.get()
|
||||
let lazyImtAddressStripped = strip0xPrefix($lazyImtAddress)
|
||||
proc ethToWei(eth: UInt256): UInt256 =
|
||||
eth * 1000000000000000000.u256
|
||||
|
||||
# deploy waku rlnv2 contract
|
||||
let wakuRlnContractReceipt = await web3.deployContract(
|
||||
WakuRlnV2Contract.replace("__$PoseidonT3$__", poseidonAddressStripped).replace(
|
||||
"__$LazyIMT$__", lazyImtAddressStripped
|
||||
proc sendMintCall(
|
||||
web3: Web3,
|
||||
accountFrom: Address,
|
||||
tokenAddress: Address,
|
||||
recipientAddress: Address,
|
||||
amountTokens: UInt256,
|
||||
recipientBalanceBeforeExpectedTokens: Option[UInt256] = none(UInt256),
|
||||
): Future[TxHash] {.async.} =
|
||||
let doBalanceAssert = recipientBalanceBeforeExpectedTokens.isSome()
|
||||
|
||||
if doBalanceAssert:
|
||||
let balanceBeforeMint = await getTokenBalance(web3, tokenAddress, recipientAddress)
|
||||
let balanceBeforeExpectedTokens = recipientBalanceBeforeExpectedTokens.get()
|
||||
assert balanceBeforeMint == balanceBeforeExpectedTokens,
|
||||
fmt"Balance is {balanceBeforeMint} before minting but expected {balanceBeforeExpectedTokens}"
|
||||
|
||||
# Create mint transaction
|
||||
# Method ID for mint(address,uint256) is 0x40c10f19 which is part of the openzeppelin ERC20 standard
|
||||
# The method ID for a deployed test token can be viewed here https://sepolia.lineascan.build/address/0x185A0015aC462a0aECb81beCc0497b649a64B9ea#writeContract
|
||||
let mintSelector = "0x40c10f19"
|
||||
let addressHex = recipientAddress.toHex()
|
||||
# Pad the address and amount to 32 bytes each
|
||||
let paddedAddress = addressHex.align(64, '0')
|
||||
|
||||
let amountHex = amountTokens.toHex()
|
||||
let amountWithout0x =
|
||||
if amountHex.toLower().startsWith("0x"):
|
||||
amountHex[2 .. ^1]
|
||||
else:
|
||||
amountHex
|
||||
let paddedAmount = amountWithout0x.align(64, '0')
|
||||
let mintCallData = mintSelector & paddedAddress & paddedAmount
|
||||
let gasPrice = int(await web3.provider.eth_gasPrice())
|
||||
|
||||
# Create the transaction
|
||||
var tx: TransactionArgs
|
||||
tx.`from` = Opt.some(accountFrom)
|
||||
tx.to = Opt.some(tokenAddress)
|
||||
tx.value = Opt.some(0.u256) # No ETH is sent for token operations
|
||||
tx.gasPrice = Opt.some(Quantity(gasPrice))
|
||||
tx.data = Opt.some(byteutils.hexToSeqByte(mintCallData))
|
||||
|
||||
trace "Sending mint call"
|
||||
let txHash = await web3.send(tx)
|
||||
|
||||
let balanceOfSelector = "0x70a08231"
|
||||
let balanceCallData = balanceOfSelector & paddedAddress
|
||||
|
||||
# Wait a bit for transaction to be mined
|
||||
await sleepAsync(500.milliseconds)
|
||||
|
||||
if doBalanceAssert:
|
||||
let balanceAfterMint = await getTokenBalance(web3, tokenAddress, recipientAddress)
|
||||
let balanceAfterExpectedTokens =
|
||||
recipientBalanceBeforeExpectedTokens.get() + amountTokens
|
||||
assert balanceAfterMint == balanceAfterExpectedTokens,
|
||||
fmt"Balance is {balanceAfterMint} after transfer but expected {balanceAfterExpectedTokens}"
|
||||
|
||||
return txHash
|
||||
|
||||
# Check how many tokens a spender (the RLN contract) is allowed to spend on behalf of the owner (account which wishes to register a membership)
|
||||
proc checkTokenAllowance(
|
||||
web3: Web3, tokenAddress: Address, owner: Address, spender: Address
|
||||
): Future[UInt256] {.async.} =
|
||||
let token = web3.contractSender(ERC20Token, tokenAddress)
|
||||
let allowance = await token.allowance(owner, spender).call()
|
||||
trace "Current allowance", owner = owner, spender = spender, allowance = allowance
|
||||
return allowance
|
||||
|
||||
proc setupContractDeployment(
|
||||
forgePath: string, submodulePath: string
|
||||
): Result[void, string] =
|
||||
trace "Contract deployer paths", forgePath = forgePath, submodulePath = submodulePath
|
||||
# Build the Foundry project
|
||||
try:
|
||||
let (forgeCleanOutput, forgeCleanExitCode) =
|
||||
execCmdEx(fmt"""cd {submodulePath} && {forgePath} clean""")
|
||||
trace "Executed forge clean command", output = forgeCleanOutput
|
||||
if forgeCleanExitCode != 0:
|
||||
return err("forge clean command failed")
|
||||
|
||||
let (forgeInstallOutput, forgeInstallExitCode) =
|
||||
execCmdEx(fmt"""cd {submodulePath} && {forgePath} install""")
|
||||
trace "Executed forge install command", output = forgeInstallOutput
|
||||
if forgeInstallExitCode != 0:
|
||||
return err("forge install command failed")
|
||||
|
||||
let (pnpmInstallOutput, pnpmInstallExitCode) =
|
||||
execCmdEx(fmt"""cd {submodulePath} && pnpm install""")
|
||||
trace "Executed pnpm install command", output = pnpmInstallOutput
|
||||
if pnpmInstallExitCode != 0:
|
||||
return err("pnpm install command failed" & pnpmInstallOutput)
|
||||
|
||||
let (forgeBuildOutput, forgeBuildExitCode) =
|
||||
execCmdEx(fmt"""cd {submodulePath} && {forgePath} build""")
|
||||
trace "Executed forge build command", output = forgeBuildOutput
|
||||
if forgeBuildExitCode != 0:
|
||||
return err("forge build command failed")
|
||||
|
||||
# Set the environment variable API keys to anything for local testnet deployment
|
||||
putEnv("API_KEY_CARDONA", "123")
|
||||
putEnv("API_KEY_LINEASCAN", "123")
|
||||
putEnv("API_KEY_ETHERSCAN", "123")
|
||||
except OSError, IOError:
|
||||
return err("Command execution failed: " & getCurrentExceptionMsg())
|
||||
return ok()
|
||||
|
||||
proc deployTestToken*(
|
||||
pk: keys.PrivateKey, acc: Address, web3: Web3
|
||||
): Future[Result[Address, string]] {.async.} =
|
||||
## Executes a Foundry forge script that deploys the a token contract (ERC-20) used for testing. This is a prerequisite to enable the contract deployment and this token contract address needs to be minted and approved for the accounts that need to register memberships with the contract
|
||||
## submodulePath: path to the submodule containing contract deploy scripts
|
||||
|
||||
# All RLN related tests should be run from the root directory of the project
|
||||
let submodulePath = absolutePath("./vendor/waku-rlnv2-contract")
|
||||
|
||||
# Verify submodule path exists
|
||||
if not dirExists(submodulePath):
|
||||
error "Submodule path does not exist", submodulePath = submodulePath
|
||||
return err("Submodule path does not exist: " & submodulePath)
|
||||
|
||||
let forgePath = getForgePath()
|
||||
|
||||
setupContractDeployment(forgePath, submodulePath).isOkOr:
|
||||
error "Failed to setup contract deployment", error = $error
|
||||
return err("Failed to setup contract deployment: " & $error)
|
||||
|
||||
# Deploy TestToken contract
|
||||
let forgeCmdTestToken =
|
||||
fmt"""cd {submodulePath} && {forgePath} script test/TestToken.sol --broadcast -vvv --rpc-url http://localhost:8540 --tc TestTokenFactory --private-key {pk} && rm -rf broadcast/*/*/run-1*.json && rm -rf cache/*/*/run-1*.json"""
|
||||
let (outputDeployTestToken, exitCodeDeployTestToken) = execCmdEx(forgeCmdTestToken)
|
||||
trace "Executed forge command to deploy TestToken contract",
|
||||
output = outputDeployTestToken
|
||||
if exitCodeDeployTestToken != 0:
|
||||
return error("Forge command to deploy TestToken contract failed")
|
||||
|
||||
# Parse the command output to find contract address
|
||||
let testTokenAddress = getContractAddressFromDeployScriptOutput(outputDeployTestToken).valueOr:
|
||||
error "Failed to get TestToken contract address from deploy script output",
|
||||
error = $error
|
||||
return err(
|
||||
"Failed to get TestToken contract address from deploy script output: " & $error
|
||||
)
|
||||
)
|
||||
let wakuRlnContractAddress = wakuRlnContractReceipt.contractAddress.get()
|
||||
let wakuRlnAddressStripped = strip0xPrefix($wakuRlnContractAddress)
|
||||
debug "Address of the TestToken contract", testTokenAddress
|
||||
|
||||
debug "Address of the deployed rlnv2 contract: ", wakuRlnContractAddress
|
||||
let testTokenAddressBytes = hexToByteArray[20](testTokenAddress)
|
||||
let testTokenAddressAddress = Address(testTokenAddressBytes)
|
||||
putEnv("TOKEN_ADDRESS", testTokenAddressAddress.toHex())
|
||||
|
||||
# need to send concat: impl & init_bytes
|
||||
let contractInput =
|
||||
byteutils.toHex(encode(wakuRlnContractAddress)) & Erc1967ProxyContractInput
|
||||
debug "contractInput", contractInput
|
||||
let proxyReceipt =
|
||||
await web3.deployContract(Erc1967Proxy, contractInput = contractInput)
|
||||
return ok(testTokenAddressAddress)
|
||||
|
||||
debug "proxy receipt", contractAddress = proxyReceipt.contractAddress.get()
|
||||
let proxyAddress = proxyReceipt.contractAddress.get()
|
||||
# Sends an ERC20 token approval call to allow a spender to spend a certain amount of tokens on behalf of the owner
|
||||
proc approveTokenAllowanceAndVerify*(
|
||||
web3: Web3,
|
||||
accountFrom: Address,
|
||||
privateKey: keys.PrivateKey,
|
||||
tokenAddress: Address,
|
||||
spender: Address,
|
||||
amountWei: UInt256,
|
||||
expectedAllowanceBefore: Option[UInt256] = none(UInt256),
|
||||
): Future[Result[TxHash, string]] {.async.} =
|
||||
var allowanceBefore: UInt256
|
||||
if expectedAllowanceBefore.isSome():
|
||||
allowanceBefore =
|
||||
await checkTokenAllowance(web3, tokenAddress, accountFrom, spender)
|
||||
let expected = expectedAllowanceBefore.get()
|
||||
if allowanceBefore != expected:
|
||||
return
|
||||
err(fmt"Allowance is {allowanceBefore} before approval but expected {expected}")
|
||||
|
||||
let newBalance = await web3.provider.eth_getBalance(web3.defaultAccount, "latest")
|
||||
debug "Account balance after the contract deployment: ", newBalance
|
||||
# Temporarily set the private key
|
||||
let oldPrivateKey = web3.privateKey
|
||||
web3.privateKey = Opt.some(privateKey)
|
||||
web3.lastKnownNonce = Opt.none(Quantity)
|
||||
|
||||
try:
|
||||
# ERC20 approve function signature: approve(address spender, uint256 amount)
|
||||
# Method ID for approve(address,uint256) is 0x095ea7b3
|
||||
const APPROVE_SELECTOR = "0x095ea7b3"
|
||||
let addressHex = spender.toHex().align(64, '0')
|
||||
let amountHex = amountWei.toHex().align(64, '0')
|
||||
let approveCallData = APPROVE_SELECTOR & addressHex & amountHex
|
||||
|
||||
let gasPrice = await web3.provider.eth_gasPrice()
|
||||
|
||||
var tx: TransactionArgs
|
||||
tx.`from` = Opt.some(accountFrom)
|
||||
tx.to = Opt.some(tokenAddress)
|
||||
tx.value = Opt.some(0.u256)
|
||||
tx.gasPrice = Opt.some(gasPrice)
|
||||
tx.gas = Opt.some(Quantity(100000))
|
||||
tx.data = Opt.some(byteutils.hexToSeqByte(approveCallData))
|
||||
tx.chainId = Opt.some(CHAIN_ID)
|
||||
|
||||
trace "Sending approve call", tx = tx
|
||||
let txHash = await web3.send(tx)
|
||||
let receipt = await web3.getMinedTransactionReceipt(txHash)
|
||||
|
||||
if receipt.status.isNone():
|
||||
return err("Approval transaction failed receipt is none")
|
||||
if receipt.status.get() != 1.Quantity:
|
||||
return err("Approval transaction failed status quantity not 1")
|
||||
|
||||
# Single verification check after mining (no extra sleep needed)
|
||||
let allowanceAfter =
|
||||
await checkTokenAllowance(web3, tokenAddress, accountFrom, spender)
|
||||
let expectedAfter =
|
||||
if expectedAllowanceBefore.isSome():
|
||||
expectedAllowanceBefore.get() + amountWei
|
||||
else:
|
||||
amountWei
|
||||
|
||||
if allowanceAfter < expectedAfter:
|
||||
return err(
|
||||
fmt"Allowance is {allowanceAfter} after approval but expected at least {expectedAfter}"
|
||||
)
|
||||
|
||||
return ok(txHash)
|
||||
except CatchableError as e:
|
||||
return err(fmt"Failed to send approve transaction: {e.msg}")
|
||||
finally:
|
||||
# Restore the old private key
|
||||
web3.privateKey = oldPrivateKey
|
||||
|
||||
proc executeForgeContractDeployScripts*(
|
||||
privateKey: keys.PrivateKey, acc: Address, web3: Web3
|
||||
): Future[Result[Address, string]] {.async, gcsafe.} =
|
||||
## Executes a set of foundry forge scripts required to deploy the RLN contract and returns the deployed proxy contract address
|
||||
## submodulePath: path to the submodule containing contract deploy scripts
|
||||
|
||||
# All RLN related tests should be run from the root directory of the project
|
||||
let submodulePath = "./vendor/waku-rlnv2-contract"
|
||||
|
||||
# Verify submodule path exists
|
||||
if not dirExists(submodulePath):
|
||||
error "Submodule path does not exist", submodulePath = submodulePath
|
||||
return err("Submodule path does not exist: " & submodulePath)
|
||||
|
||||
let forgePath = getForgePath()
|
||||
debug "Forge path", forgePath
|
||||
|
||||
# Verify forge executable exists
|
||||
if not fileExists(forgePath):
|
||||
error "Forge executable not found", forgePath = forgePath
|
||||
return err("Forge executable not found: " & forgePath)
|
||||
|
||||
trace "contract deployer account details", account = acc, privateKey = privateKey
|
||||
let setupContractEnv = setupContractDeployment(forgePath, submodulePath)
|
||||
if setupContractEnv.isErr():
|
||||
error "Failed to setup contract deployment"
|
||||
return err("Failed to setup contract deployment")
|
||||
|
||||
# Deploy LinearPriceCalculator contract
|
||||
let forgeCmdPriceCalculator =
|
||||
fmt"""cd {submodulePath} && {forgePath} script script/Deploy.s.sol --broadcast -vvvv --rpc-url http://localhost:8540 --tc DeployPriceCalculator --private-key {privateKey} && rm -rf broadcast/*/*/run-1*.json && rm -rf cache/*/*/run-1*.json"""
|
||||
let (outputDeployPriceCalculator, exitCodeDeployPriceCalculator) =
|
||||
execCmdEx(forgeCmdPriceCalculator)
|
||||
trace "Executed forge command to deploy LinearPriceCalculator contract",
|
||||
output = outputDeployPriceCalculator
|
||||
if exitCodeDeployPriceCalculator != 0:
|
||||
return error("Forge command to deploy LinearPriceCalculator contract failed")
|
||||
|
||||
# Parse the output to find contract address
|
||||
let priceCalculatorAddressRes =
|
||||
getContractAddressFromDeployScriptOutput(outputDeployPriceCalculator)
|
||||
if priceCalculatorAddressRes.isErr():
|
||||
error "Failed to get LinearPriceCalculator contract address from deploy script output"
|
||||
let priceCalculatorAddress = priceCalculatorAddressRes.get()
|
||||
debug "Address of the LinearPriceCalculator contract", priceCalculatorAddress
|
||||
putEnv("PRICE_CALCULATOR_ADDRESS", priceCalculatorAddress)
|
||||
|
||||
let forgeCmdWakuRln =
|
||||
fmt"""cd {submodulePath} && {forgePath} script script/Deploy.s.sol --broadcast -vvvv --rpc-url http://localhost:8540 --tc DeployWakuRlnV2 --private-key {privateKey} && rm -rf broadcast/*/*/run-1*.json && rm -rf cache/*/*/run-1*.json"""
|
||||
let (outputDeployWakuRln, exitCodeDeployWakuRln) = execCmdEx(forgeCmdWakuRln)
|
||||
trace "Executed forge command to deploy WakuRlnV2 contract",
|
||||
output = outputDeployWakuRln
|
||||
if exitCodeDeployWakuRln != 0:
|
||||
error "Forge command to deploy WakuRlnV2 contract failed",
|
||||
output = outputDeployWakuRln
|
||||
|
||||
# Parse the output to find contract address
|
||||
let wakuRlnV2AddressRes =
|
||||
getContractAddressFromDeployScriptOutput(outputDeployWakuRln)
|
||||
if wakuRlnV2AddressRes.isErr():
|
||||
error "Failed to get WakuRlnV2 contract address from deploy script output"
|
||||
##TODO: raise exception here?
|
||||
let wakuRlnV2Address = wakuRlnV2AddressRes.get()
|
||||
debug "Address of the WakuRlnV2 contract", wakuRlnV2Address
|
||||
putEnv("WAKURLNV2_ADDRESS", wakuRlnV2Address)
|
||||
|
||||
# Deploy Proxy contract
|
||||
let forgeCmdProxy =
|
||||
fmt"""cd {submodulePath} && {forgePath} script script/Deploy.s.sol --broadcast -vvvv --rpc-url http://localhost:8540 --tc DeployProxy --private-key {privateKey} && rm -rf broadcast/*/*/run-1*.json && rm -rf cache/*/*/run-1*.json"""
|
||||
let (outputDeployProxy, exitCodeDeployProxy) = execCmdEx(forgeCmdProxy)
|
||||
trace "Executed forge command to deploy proxy contract", output = outputDeployProxy
|
||||
if exitCodeDeployProxy != 0:
|
||||
error "Forge command to deploy Proxy failed", error = outputDeployProxy
|
||||
return err("Forge command to deploy Proxy failed")
|
||||
|
||||
let proxyAddress = getContractAddressFromDeployScriptOutput(outputDeployProxy)
|
||||
let proxyAddressBytes = hexToByteArray[20](proxyAddress.get())
|
||||
let proxyAddressAddress = Address(proxyAddressBytes)
|
||||
|
||||
info "Address of the Proxy contract", proxyAddressAddress
|
||||
|
||||
await web3.close()
|
||||
debug "disconnected from ", ethClientAddress
|
||||
|
||||
return proxyAddress
|
||||
return ok(proxyAddressAddress)
|
||||
|
||||
proc sendEthTransfer*(
|
||||
web3: Web3,
|
||||
@ -133,7 +416,7 @@ proc sendEthTransfer*(
|
||||
let balanceBeforeWei = await web3.provider.eth_getBalance(accountTo, "latest")
|
||||
let balanceBeforeExpectedWei = accountToBalanceBeforeExpectedWei.get()
|
||||
assert balanceBeforeWei == balanceBeforeExpectedWei,
|
||||
fmt"Balance is {balanceBeforeWei} but expected {balanceBeforeExpectedWei}"
|
||||
fmt"Balance is {balanceBeforeWei} before transfer but expected {balanceBeforeExpectedWei}"
|
||||
|
||||
let gasPrice = int(await web3.provider.eth_gasPrice())
|
||||
|
||||
@ -146,17 +429,17 @@ proc sendEthTransfer*(
|
||||
# TODO: handle the error if sending fails
|
||||
let txHash = await web3.send(tx)
|
||||
|
||||
# Wait a bit for transaction to be mined
|
||||
await sleepAsync(200.milliseconds)
|
||||
|
||||
if doBalanceAssert:
|
||||
let balanceAfterWei = await web3.provider.eth_getBalance(accountTo, "latest")
|
||||
let balanceAfterExpectedWei = accountToBalanceBeforeExpectedWei.get() + amountWei
|
||||
assert balanceAfterWei == balanceAfterExpectedWei,
|
||||
fmt"Balance is {balanceAfterWei} but expected {balanceAfterExpectedWei}"
|
||||
fmt"Balance is {balanceAfterWei} after transfer but expected {balanceAfterExpectedWei}"
|
||||
|
||||
return txHash
|
||||
|
||||
proc ethToWei(eth: UInt256): UInt256 =
|
||||
eth * 1000000000000000000.u256
|
||||
|
||||
proc createEthAccount*(
|
||||
ethAmount: UInt256 = 1000.u256
|
||||
): Future[(keys.PrivateKey, Address)] {.async.} =
|
||||
@ -198,7 +481,7 @@ proc getAnvilPath*(): string =
|
||||
return $anvilPath
|
||||
|
||||
# Runs Anvil daemon
|
||||
proc runAnvil*(port: int = 8540, chainId: string = "1337"): Process =
|
||||
proc runAnvil*(port: int = 8540, chainId: string = "1234"): Process =
|
||||
# Passed options are
|
||||
# --port Port to listen on.
|
||||
# --gas-limit Sets the block gas limit in WEI.
|
||||
@ -212,13 +495,13 @@ proc runAnvil*(port: int = 8540, chainId: string = "1337"): Process =
|
||||
anvilPath,
|
||||
args = [
|
||||
"--port",
|
||||
"8540",
|
||||
$port,
|
||||
"--gas-limit",
|
||||
"300000000000000",
|
||||
"--balance",
|
||||
"1000000000",
|
||||
"--chain-id",
|
||||
$CHAIN_ID,
|
||||
$chainId,
|
||||
],
|
||||
options = {poUsePath},
|
||||
)
|
||||
@ -242,14 +525,26 @@ proc runAnvil*(port: int = 8540, chainId: string = "1337"): Process =
|
||||
|
||||
# Stops Anvil daemon
|
||||
proc stopAnvil*(runAnvil: Process) {.used.} =
|
||||
if runAnvil.isNil:
|
||||
debug "stopAnvil called with nil Process"
|
||||
return
|
||||
|
||||
let anvilPID = runAnvil.processID
|
||||
# We wait the daemon to exit
|
||||
debug "Stopping Anvil daemon", anvilPID = anvilPID
|
||||
|
||||
try:
|
||||
# We terminate Anvil daemon by sending a SIGTERM signal to the runAnvil PID to trigger RPC server termination and clean-up
|
||||
kill(runAnvil)
|
||||
debug "Sent SIGTERM to Anvil", anvilPID = anvilPID
|
||||
except:
|
||||
error "Anvil daemon termination failed: ", err = getCurrentExceptionMsg()
|
||||
# Send termination signals
|
||||
when not defined(windows):
|
||||
discard execCmdEx(fmt"kill -TERM {anvilPID}")
|
||||
discard execCmdEx(fmt"kill -9 {anvilPID}")
|
||||
else:
|
||||
discard execCmdEx(fmt"taskkill /F /PID {anvilPID}")
|
||||
|
||||
# Close Process object to release resources
|
||||
close(runAnvil)
|
||||
debug "Anvil daemon stopped", anvilPID = anvilPID
|
||||
except Exception as e:
|
||||
debug "Error stopping Anvil daemon", anvilPID = anvilPID, error = e.msg
|
||||
|
||||
proc setupOnchainGroupManager*(
|
||||
ethClientUrl: string = EthClient, amountEth: UInt256 = 10.u256
|
||||
@ -261,12 +556,10 @@ proc setupOnchainGroupManager*(
|
||||
|
||||
let rlnInstance = rlnInstanceRes.get()
|
||||
|
||||
let contractAddress = await uploadRLNContract(ethClientUrl)
|
||||
# connect to the eth client
|
||||
let web3 = await newWeb3(ethClientUrl)
|
||||
|
||||
let accounts = await web3.provider.eth_accounts()
|
||||
web3.defaultAccount = accounts[0]
|
||||
web3.defaultAccount = accounts[1]
|
||||
|
||||
let (privateKey, acc) = createEthAccount(web3)
|
||||
|
||||
@ -276,6 +569,32 @@ proc setupOnchainGroupManager*(
|
||||
web3, web3.defaultAccount, acc, ethToWei(1000.u256), some(0.u256)
|
||||
)
|
||||
|
||||
let testTokenAddress = (await deployTestToken(privateKey, acc, web3)).valueOr:
|
||||
assert false, "Failed to deploy test token contract: " & $error
|
||||
return
|
||||
|
||||
# mint the token from the generated account
|
||||
discard await sendMintCall(
|
||||
web3, web3.defaultAccount, testTokenAddress, acc, ethToWei(1000.u256), some(0.u256)
|
||||
)
|
||||
|
||||
let contractAddress = (await executeForgeContractDeployScripts(privateKey, acc, web3)).valueOr:
|
||||
assert false, "Failed to deploy RLN contract: " & $error
|
||||
return
|
||||
|
||||
# If the generated account wishes to register a membership, it needs to approve the contract to spend its tokens
|
||||
let tokenApprovalResult = await approveTokenAllowanceAndVerify(
|
||||
web3,
|
||||
acc, # owner
|
||||
privateKey,
|
||||
testTokenAddress, # ERC20 token address
|
||||
contractAddress, # spender - the proxy contract that will spend the tokens
|
||||
ethToWei(200.u256),
|
||||
some(0.u256), # expected allowance before approval
|
||||
)
|
||||
|
||||
assert tokenApprovalResult.isOk, tokenApprovalResult.error()
|
||||
|
||||
let manager = OnchainGroupManager(
|
||||
ethClientUrls: @[ethClientUrl],
|
||||
ethContractAddress: $contractAddress,
|
||||
|
||||
@ -209,7 +209,7 @@ suite "Waku Sync – reconciliation":
|
||||
baseHash = hashLocal
|
||||
alteredHash = toDigest("msg" & $i & "_x")
|
||||
hashRemote = alteredHash
|
||||
|
||||
|
||||
remote.insert(SyncID(time: ts, hash: hashRemote)).isOkOr:
|
||||
assert false, "failed to insert hash: " & $error
|
||||
|
||||
|
||||
2
vendor/waku-rlnv2-contract
vendored
2
vendor/waku-rlnv2-contract
vendored
@ -1 +1 @@
|
||||
Subproject commit a576a8949ca20e310f2fbb4ec0bd05a57ac3045f
|
||||
Subproject commit b7e9a9b1bc69256a2a3076c1f099b50ce84e7eff
|
||||
@ -30,12 +30,12 @@ type ClusterConf* = object
|
||||
# Cluster configuration corresponding to The Waku Network. Note that it
|
||||
# overrides existing cli configuration
|
||||
proc TheWakuNetworkConf*(T: type ClusterConf): ClusterConf =
|
||||
const RelayChainId = 11155111'u256
|
||||
const RelayChainId = 59141'u256
|
||||
return ClusterConf(
|
||||
maxMessageSize: "150KiB",
|
||||
clusterId: 1,
|
||||
rlnRelay: true,
|
||||
rlnRelayEthContractAddress: "0xfe7a9eabcE779a090FD702346Fd0bFAc02ce6Ac8",
|
||||
rlnRelayEthContractAddress: "0xB9cd878C90E49F797B4431fBF4fb333108CB90e6",
|
||||
rlnRelayDynamic: true,
|
||||
rlnRelayChainId: RelayChainId,
|
||||
rlnEpochSizeSec: 600,
|
||||
|
||||
@ -30,23 +30,29 @@ logScope:
|
||||
# using the when predicate does not work within the contract macro, hence need to dupe
|
||||
contract(WakuRlnContract):
|
||||
# this serves as an entrypoint into the rln membership set
|
||||
proc register(idCommitment: UInt256, userMessageLimit: UInt32)
|
||||
proc register(
|
||||
idCommitment: UInt256, userMessageLimit: UInt32, idCommitmentsToErase: seq[UInt256]
|
||||
)
|
||||
|
||||
# Initializes the implementation contract (only used in unit tests)
|
||||
proc initialize(maxMessageLimit: UInt256)
|
||||
# this event is raised when a new member is registered
|
||||
proc MemberRegistered(rateCommitment: UInt256, index: UInt32) {.event.}
|
||||
# this event is emitted when a new member is registered
|
||||
proc MembershipRegistered(
|
||||
idCommitment: UInt256, membershipRateLimit: UInt256, index: UInt32
|
||||
) {.event.}
|
||||
|
||||
# this function denotes existence of a given user
|
||||
proc memberExists(idCommitment: UInt256): UInt256 {.view.}
|
||||
proc isInMembershipSet(idCommitment: Uint256): bool {.view.}
|
||||
# this constant describes the next index of a new member
|
||||
proc commitmentIndex(): UInt256 {.view.}
|
||||
proc nextFreeIndex(): UInt256 {.view.}
|
||||
# this constant describes the block number this contract was deployed on
|
||||
proc deployedBlockNumber(): UInt256 {.view.}
|
||||
# this constant describes max message limit of rln contract
|
||||
proc MAX_MESSAGE_LIMIT(): UInt256 {.view.}
|
||||
# this function returns the merkleProof for a given index
|
||||
# proc merkleProofElements(index: UInt40): seq[byte] {.view.}
|
||||
# this function returns the merkle root
|
||||
proc root(): UInt256 {.view.}
|
||||
proc maxMembershipRateLimit(): UInt256 {.view.}
|
||||
# this function returns the merkleProof for a given index
|
||||
# proc getMerkleProof(index: EthereumUInt40): seq[array[32, byte]] {.view.}
|
||||
# this function returns the Merkle root
|
||||
proc root(): Uint256 {.view.}
|
||||
|
||||
type
|
||||
WakuRlnContractWithSender = Sender[WakuRlnContract]
|
||||
@ -67,11 +73,7 @@ type
|
||||
proc setMetadata*(
|
||||
g: OnchainGroupManager, lastProcessedBlock = none(BlockNumber)
|
||||
): GroupManagerResult[void] =
|
||||
let normalizedBlock =
|
||||
if lastProcessedBlock.isSome():
|
||||
lastProcessedBlock.get()
|
||||
else:
|
||||
g.latestProcessedBlock
|
||||
let normalizedBlock = lastProcessedBlock.get(g.latestProcessedBlock)
|
||||
try:
|
||||
let metadataSetRes = g.rlnInstance.setMetadata(
|
||||
RlnMetadata(
|
||||
@ -87,14 +89,68 @@ proc setMetadata*(
|
||||
return err("failed to persist rln metadata: " & getCurrentExceptionMsg())
|
||||
return ok()
|
||||
|
||||
proc sendEthCallWithChainId(
|
||||
ethRpc: Web3,
|
||||
functionSignature: string,
|
||||
fromAddress: Address,
|
||||
toAddress: Address,
|
||||
chainId: UInt256,
|
||||
): Future[Result[UInt256, string]] {.async.} =
|
||||
## Workaround for web3 chainId=null issue on some networks (e.g., linea-sepolia)
|
||||
## Makes contract calls with explicit chainId for view functions with no parameters
|
||||
let functionHash =
|
||||
keccak256.digest(functionSignature.toOpenArrayByte(0, functionSignature.len - 1))
|
||||
let functionSelector = functionHash.data[0 .. 3]
|
||||
let dataSignature = "0x" & functionSelector.mapIt(it.toHex(2)).join("")
|
||||
|
||||
var tx: TransactionArgs
|
||||
tx.`from` = Opt.some(fromAddress)
|
||||
tx.to = Opt.some(toAddress)
|
||||
tx.value = Opt.some(0.u256)
|
||||
tx.data = Opt.some(byteutils.hexToSeqByte(dataSignature))
|
||||
tx.chainId = Opt.some(chainId)
|
||||
|
||||
let resultBytes = await ethRpc.provider.eth_call(tx, "latest")
|
||||
if resultBytes.len == 0:
|
||||
return err("No result returned for function call: " & functionSignature)
|
||||
return ok(UInt256.fromBytesBE(resultBytes))
|
||||
|
||||
proc sendEthCallWithParams(
|
||||
ethRpc: Web3,
|
||||
functionSignature: string,
|
||||
params: seq[byte],
|
||||
fromAddress: Address,
|
||||
toAddress: Address,
|
||||
chainId: UInt256,
|
||||
): Future[Result[seq[byte], string]] {.async.} =
|
||||
## Workaround for web3 chainId=null issue with parameterized contract calls
|
||||
let functionHash =
|
||||
keccak256.digest(functionSignature.toOpenArrayByte(0, functionSignature.len - 1))
|
||||
let functionSelector = functionHash.data[0 .. 3]
|
||||
let callData = functionSelector & params
|
||||
|
||||
var tx: TransactionArgs
|
||||
tx.`from` = Opt.some(fromAddress)
|
||||
tx.to = Opt.some(toAddress)
|
||||
tx.value = Opt.some(0.u256)
|
||||
tx.data = Opt.some(callData)
|
||||
tx.chainId = Opt.some(chainId)
|
||||
|
||||
let resultBytes = await ethRpc.provider.eth_call(tx, "latest")
|
||||
return ok(resultBytes)
|
||||
|
||||
proc fetchMerkleProofElements*(
|
||||
g: OnchainGroupManager
|
||||
): Future[Result[seq[byte], string]] {.async.} =
|
||||
try:
|
||||
# let merkleRootInvocation = g.wakuRlnContract.get().root()
|
||||
# let merkleRoot = await merkleRootInvocation.call()
|
||||
# The above code is not working with the latest web3 version due to chainId being null (specifically on linea-sepolia)
|
||||
# TODO: find better solution than this custom sendEthCallWithChainId call
|
||||
let membershipIndex = g.membershipIndex.get()
|
||||
let index40 = stuint(membershipIndex, 40)
|
||||
|
||||
let methodSig = "merkleProofElements(uint40)"
|
||||
let methodSig = "getMerkleProof(uint40)"
|
||||
let methodIdDigest = keccak.keccak256.digest(methodSig)
|
||||
let methodId = methodIdDigest.data[0 .. 3]
|
||||
|
||||
@ -111,6 +167,7 @@ proc fetchMerkleProofElements*(
|
||||
var tx: TransactionArgs
|
||||
tx.to = Opt.some(fromHex(Address, g.ethContractAddress))
|
||||
tx.data = Opt.some(callData)
|
||||
tx.chainId = Opt.some(g.chainId) # Explicitly set the chain ID
|
||||
|
||||
let responseBytes = await g.ethRpc.get().provider.eth_call(tx, "latest")
|
||||
|
||||
@ -123,8 +180,17 @@ proc fetchMerkleRoot*(
|
||||
g: OnchainGroupManager
|
||||
): Future[Result[UInt256, string]] {.async.} =
|
||||
try:
|
||||
let merkleRootInvocation = g.wakuRlnContract.get().root()
|
||||
let merkleRoot = await merkleRootInvocation.call()
|
||||
let merkleRoot = (
|
||||
await sendEthCallWithChainId(
|
||||
ethRpc = g.ethRpc.get(),
|
||||
functionSignature = "root()",
|
||||
fromAddress = g.ethRpc.get().defaultAccount,
|
||||
toAddress = fromHex(Address, g.ethContractAddress),
|
||||
chainId = g.chainId,
|
||||
)
|
||||
).valueOr:
|
||||
error "Failed to fetch Merkle root", error = $error
|
||||
return err("Failed to fetch merkle root: " & $error)
|
||||
return ok(merkleRoot)
|
||||
except CatchableError:
|
||||
error "Failed to fetch Merkle root", error = getCurrentExceptionMsg()
|
||||
@ -151,6 +217,7 @@ proc updateRoots*(g: OnchainGroupManager): Future[bool] {.async.} =
|
||||
return false
|
||||
|
||||
let merkleRoot = UInt256ToField(rootRes.get())
|
||||
|
||||
if g.validRoots.len == 0:
|
||||
g.validRoots.addLast(merkleRoot)
|
||||
return true
|
||||
@ -183,8 +250,26 @@ proc trackRootChanges*(g: OnchainGroupManager) {.async: (raises: [CatchableError
|
||||
error "Failed to fetch Merkle proof", error = proofResult.error
|
||||
g.merkleProofCache = proofResult.get()
|
||||
|
||||
# also need update registerd membership
|
||||
let memberCount = cast[int64](await wakuRlnContract.commitmentIndex().call())
|
||||
# also need to update registered membership
|
||||
# g.rlnRelayMaxMessageLimit =
|
||||
# cast[uint64](await wakuRlnContract.nextFreeIndex().call())
|
||||
# The above code is not working with the latest web3 version due to chainId being null (specifically on linea-sepolia)
|
||||
# TODO: find better solution than this custom sendEthCallWithChainId call
|
||||
let nextFreeIndex = await sendEthCallWithChainId(
|
||||
ethRpc = ethRpc,
|
||||
functionSignature = "nextFreeIndex()",
|
||||
fromAddress = ethRpc.defaultAccount,
|
||||
toAddress = fromHex(Address, g.ethContractAddress),
|
||||
chainId = g.chainId,
|
||||
)
|
||||
|
||||
if nextFreeIndex.isErr():
|
||||
error "Failed to fetch next free index", error = nextFreeIndex.error
|
||||
raise newException(
|
||||
CatchableError, "Failed to fetch next free index: " & nextFreeIndex.error
|
||||
)
|
||||
|
||||
let memberCount = cast[int64](nextFreeIndex.get())
|
||||
waku_rln_number_registered_memberships.set(float64(memberCount))
|
||||
|
||||
await sleepAsync(rpcDelay)
|
||||
@ -219,15 +304,19 @@ method register*(
|
||||
var gasPrice: int
|
||||
g.retryWrapper(gasPrice, "Failed to get gas price"):
|
||||
int(await ethRpc.provider.eth_gasPrice()) * 2
|
||||
let idCommitmentHex = identityCredential.idCommitment.inHex()
|
||||
debug "identityCredential idCommitmentHex", idCommitment = idCommitmentHex
|
||||
let idCommitment = identityCredential.idCommitment.toUInt256()
|
||||
|
||||
let idCommitmentsToErase: seq[UInt256] = @[]
|
||||
debug "registering the member",
|
||||
idCommitment = idCommitment, userMessageLimit = userMessageLimit
|
||||
idCommitment = idCommitment,
|
||||
userMessageLimit = userMessageLimit,
|
||||
idCommitmentsToErase = idCommitmentsToErase
|
||||
var txHash: TxHash
|
||||
g.retryWrapper(txHash, "Failed to register the member"):
|
||||
await wakuRlnContract.register(idCommitment, userMessageLimit.stuint(32)).send(
|
||||
gasPrice = gasPrice
|
||||
)
|
||||
await wakuRlnContract
|
||||
.register(idCommitment, userMessageLimit.stuint(32), idCommitmentsToErase)
|
||||
.send(gasPrice = gasPrice)
|
||||
|
||||
# wait for the transaction to be mined
|
||||
var tsReceipt: ReceiptObject
|
||||
@ -240,27 +329,29 @@ method register*(
|
||||
debug "ts receipt", receipt = tsReceipt[]
|
||||
|
||||
if tsReceipt.status.isNone():
|
||||
raise newException(ValueError, "register: transaction failed status is None")
|
||||
raise newException(ValueError, "Transaction failed: status is None")
|
||||
if tsReceipt.status.get() != 1.Quantity:
|
||||
raise newException(
|
||||
ValueError, "register: transaction failed status is: " & $tsReceipt.status.get()
|
||||
ValueError, "Transaction failed with status: " & $tsReceipt.status.get()
|
||||
)
|
||||
|
||||
let firstTopic = tsReceipt.logs[0].topics[0]
|
||||
# the hash of the signature of MemberRegistered(uint256,uint32) event is equal to the following hex value
|
||||
if firstTopic !=
|
||||
cast[FixedBytes[32]](keccak.keccak256.digest("MemberRegistered(uint256,uint32)").data):
|
||||
## Extract MembershipRegistered event from transaction logs (third event)
|
||||
let thirdTopic = tsReceipt.logs[2].topics[0]
|
||||
debug "third topic", thirdTopic = thirdTopic
|
||||
if thirdTopic !=
|
||||
cast[FixedBytes[32]](keccak.keccak256.digest(
|
||||
"MembershipRegistered(uint256,uint256,uint32)"
|
||||
).data):
|
||||
raise newException(ValueError, "register: unexpected event signature")
|
||||
|
||||
# the arguments of the raised event i.e., MemberRegistered are encoded inside the data field
|
||||
# data = rateCommitment encoded as 256 bits || index encoded as 32 bits
|
||||
let arguments = tsReceipt.logs[0].data
|
||||
## Parse MembershipRegistered event data: rateCommitment(256) || membershipRateLimit(256) || index(32)
|
||||
let arguments = tsReceipt.logs[2].data
|
||||
debug "tx log data", arguments = arguments
|
||||
let
|
||||
# In TX log data, uints are encoded in big endian
|
||||
membershipIndex = UInt256.fromBytesBE(arguments[32 ..^ 1])
|
||||
## Extract membership index from transaction log data (big endian)
|
||||
membershipIndex = UInt256.fromBytesBE(arguments[64 .. 95])
|
||||
|
||||
debug "parsed membershipIndex", membershipIndex
|
||||
trace "parsed membershipIndex", membershipIndex
|
||||
g.userMessageLimit = some(userMessageLimit)
|
||||
g.membershipIndex = some(membershipIndex.toMembershipIndex())
|
||||
g.idCredentials = some(identityCredential)
|
||||
@ -376,7 +467,7 @@ method generateProof*(
|
||||
var proofValue = cast[ptr array[320, byte]](output_witness_buffer.`ptr`)
|
||||
let proofBytes: array[320, byte] = proofValue[]
|
||||
|
||||
## parse the proof as [ proof<128> | root<32> | external_nullifier<32> | share_x<32> | share_y<32> | nullifier<32> ]
|
||||
## Parse the proof as [ proof<128> | root<32> | external_nullifier<32> | share_x<32> | share_y<32> | nullifier<32> ]
|
||||
let
|
||||
proofOffset = 128
|
||||
rootOffset = proofOffset + 32
|
||||
@ -418,9 +509,7 @@ method generateProof*(
|
||||
return ok(output)
|
||||
|
||||
method verifyProof*(
|
||||
g: OnchainGroupManager, # verifier context
|
||||
input: seq[byte], # raw message data (signal)
|
||||
proof: RateLimitProof, # proof received from the peer
|
||||
g: OnchainGroupManager, input: seq[byte], proof: RateLimitProof
|
||||
): GroupManagerResult[bool] {.gcsafe, raises: [].} =
|
||||
## -- Verifies an RLN rate-limit proof against the set of valid Merkle roots --
|
||||
|
||||
@ -543,11 +632,31 @@ method init*(g: OnchainGroupManager): Future[GroupManagerResult[void]] {.async.}
|
||||
g.membershipIndex = some(keystoreCred.treeIndex)
|
||||
g.userMessageLimit = some(keystoreCred.userMessageLimit)
|
||||
# now we check on the contract if the commitment actually has a membership
|
||||
let idCommitmentBytes = keystoreCred.identityCredential.idCommitment
|
||||
let idCommitmentUInt256 = keystoreCred.identityCredential.idCommitment.toUInt256()
|
||||
let idCommitmentHex = idCommitmentBytes.inHex()
|
||||
debug "Keystore idCommitment in bytes", idCommitmentBytes = idCommitmentBytes
|
||||
debug "Keystore idCommitment in UInt256 ", idCommitmentUInt256 = idCommitmentUInt256
|
||||
debug "Keystore idCommitment in hex ", idCommitmentHex = idCommitmentHex
|
||||
let idCommitment = idCommitmentUInt256
|
||||
try:
|
||||
let membershipExists = await wakuRlnContract
|
||||
.memberExists(keystoreCred.identityCredential.idCommitment.toUInt256())
|
||||
.call()
|
||||
if membershipExists == 0:
|
||||
let commitmentBytes = keystoreCred.identityCredential.idCommitment
|
||||
let params = commitmentBytes.reversed()
|
||||
let resultBytes = await sendEthCallWithParams(
|
||||
ethRpc = g.ethRpc.get(),
|
||||
functionSignature = "isInMembershipSet(uint256)",
|
||||
params = params,
|
||||
fromAddress = ethRpc.defaultAccount,
|
||||
toAddress = contractAddress,
|
||||
chainId = g.chainId,
|
||||
)
|
||||
if resultBytes.isErr():
|
||||
return err("Failed to check membership: " & resultBytes.error)
|
||||
let responseBytes = resultBytes.get()
|
||||
let membershipExists = responseBytes.len == 32 and responseBytes[^1] == 1'u8
|
||||
|
||||
debug "membershipExists", membershipExists = membershipExists
|
||||
if membershipExists == false:
|
||||
return err("the commitment does not have a membership")
|
||||
except CatchableError:
|
||||
return err("failed to check if the commitment has a membership")
|
||||
@ -564,8 +673,18 @@ method init*(g: OnchainGroupManager): Future[GroupManagerResult[void]] {.async.}
|
||||
if metadata.contractAddress != g.ethContractAddress.toLower():
|
||||
return err("persisted data: contract address mismatch")
|
||||
|
||||
g.rlnRelayMaxMessageLimit =
|
||||
cast[uint64](await wakuRlnContract.MAX_MESSAGE_LIMIT().call())
|
||||
let maxMembershipRateLimit = (
|
||||
await sendEthCallWithChainId(
|
||||
ethRpc = ethRpc,
|
||||
functionSignature = "maxMembershipRateLimit()",
|
||||
fromAddress = ethRpc.defaultAccount,
|
||||
toAddress = contractAddress,
|
||||
chainId = g.chainId,
|
||||
)
|
||||
).valueOr:
|
||||
return err("Failed to fetch max membership rate limit: " & $error)
|
||||
|
||||
g.rlnRelayMaxMessageLimit = cast[uint64](maxMembershipRateLimit)
|
||||
|
||||
proc onDisconnect() {.async.} =
|
||||
error "Ethereum client disconnected"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user