mirror of
https://github.com/logos-messaging/logos-messaging-nim.git
synced 2026-01-02 14:03:06 +00:00
Chore: bump waku-rlnv2-contract-repo commit (#3651)
* Bump commit for vendor wakurlnv2contract * Update RLN registration proc for contract updates * add option to runAnvil for state dump or load with optional contract deployment on setup * Code clean up * Upodate rln relay tests to use cached anvil state * Minor updates to utils and new test for anvil state dump * stopAnvil needs to wait for graceful shutdown * configure runAnvil to use load state in other tests * reduce ci timeout * Allow for RunAnvil load state file to be compressed * Fix linting * Change return type of sendMintCall to Futre[void] * Update naming of ci path for interop tests
This commit is contained in:
parent
a8590a0a7d
commit
2cf4fe559a
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -94,7 +94,7 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-22.04, macos-15]
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 90
|
||||
timeout-minutes: 45
|
||||
|
||||
name: test-${{ matrix.os }}
|
||||
steps:
|
||||
@ -137,7 +137,7 @@ jobs:
|
||||
|
||||
nwaku-nwaku-interop-tests:
|
||||
needs: build-docker-image
|
||||
uses: logos-messaging/waku-interop-tests/.github/workflows/nim_waku_PR.yml@SMOKE_TEST_0.0.1
|
||||
uses: logos-messaging/logos-messaging-interop-tests/.github/workflows/nim_waku_PR.yml@SMOKE_TEST_0.0.1
|
||||
with:
|
||||
node_nwaku: ${{ needs.build-docker-image.outputs.image }}
|
||||
|
||||
|
||||
@ -135,8 +135,8 @@ suite "RLN Proofs as a Lightpush Service":
|
||||
server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0))
|
||||
client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0))
|
||||
|
||||
anvilProc = runAnvil()
|
||||
manager = waitFor setupOnchainGroupManager()
|
||||
anvilProc = runAnvil(stateFile = some(DEFAULT_ANVIL_STATE_PATH))
|
||||
manager = waitFor setupOnchainGroupManager(deployContracts = false)
|
||||
|
||||
# mount rln-relay
|
||||
let wakuRlnConfig = getWakuRlnConfig(manager = manager, index = MembershipIndex(1))
|
||||
|
||||
@ -135,8 +135,8 @@ suite "RLN Proofs as a Lightpush Service":
|
||||
server = newTestWakuNode(serverKey, parseIpAddress("0.0.0.0"), Port(0))
|
||||
client = newTestWakuNode(clientKey, parseIpAddress("0.0.0.0"), Port(0))
|
||||
|
||||
anvilProc = runAnvil()
|
||||
manager = waitFor setupOnchainGroupManager()
|
||||
anvilProc = runAnvil(stateFile = some(DEFAULT_ANVIL_STATE_PATH))
|
||||
manager = waitFor setupOnchainGroupManager(deployContracts = false)
|
||||
|
||||
# mount rln-relay
|
||||
let wakuRlnConfig = getWakuRlnConfig(manager = manager, index = MembershipIndex(1))
|
||||
|
||||
Binary file not shown.
29
tests/waku_rln_relay/test_rln_contract_deployment.nim
Normal file
29
tests/waku_rln_relay/test_rln_contract_deployment.nim
Normal file
@ -0,0 +1,29 @@
|
||||
{.used.}
|
||||
|
||||
{.push raises: [].}
|
||||
|
||||
import std/[options, os], results, testutils/unittests, chronos, web3
|
||||
|
||||
import
|
||||
waku/[
|
||||
waku_rln_relay,
|
||||
waku_rln_relay/conversion_utils,
|
||||
waku_rln_relay/group_manager/on_chain/group_manager,
|
||||
],
|
||||
./utils_onchain
|
||||
|
||||
suite "Token and RLN Contract Deployment":
|
||||
test "anvil should dump state to file on exit":
|
||||
# git will ignore this file, if the contract has been updated and the state file needs to be regenerated then this file can be renamed to replace the one in the repo (tests/waku_rln_relay/anvil_state/tests/waku_rln_relay/anvil_state/state-deployed-contracts-mint-and-approved.json)
|
||||
let testStateFile = some("tests/waku_rln_relay/anvil_state/anvil_state.ignore.json")
|
||||
let anvilProc = runAnvil(stateFile = testStateFile, dumpStateOnExit = true)
|
||||
let manager = waitFor setupOnchainGroupManager(deployContracts = true)
|
||||
|
||||
stopAnvil(anvilProc)
|
||||
|
||||
check:
|
||||
fileExists(testStateFile.get())
|
||||
|
||||
#The test should still pass even if thie compression fails
|
||||
compressGzipFile(testStateFile.get(), testStateFile.get() & ".gz").isOkOr:
|
||||
error "Failed to compress state file", error = error
|
||||
@ -33,8 +33,8 @@ suite "Onchain group manager":
|
||||
var manager {.threadVar.}: OnchainGroupManager
|
||||
|
||||
setup:
|
||||
anvilProc = runAnvil()
|
||||
manager = waitFor setupOnchainGroupManager()
|
||||
anvilProc = runAnvil(stateFile = some(DEFAULT_ANVIL_STATE_PATH))
|
||||
manager = waitFor setupOnchainGroupManager(deployContracts = false)
|
||||
|
||||
teardown:
|
||||
stopAnvil(anvilProc)
|
||||
|
||||
@ -27,8 +27,8 @@ suite "Waku rln relay":
|
||||
var manager {.threadVar.}: OnchainGroupManager
|
||||
|
||||
setup:
|
||||
anvilProc = runAnvil()
|
||||
manager = waitFor setupOnchainGroupManager()
|
||||
anvilProc = runAnvil(stateFile = some(DEFAULT_ANVIL_STATE_PATH))
|
||||
manager = waitFor setupOnchainGroupManager(deployContracts = false)
|
||||
|
||||
teardown:
|
||||
stopAnvil(anvilProc)
|
||||
|
||||
@ -30,8 +30,8 @@ procSuite "WakuNode - RLN relay":
|
||||
var manager {.threadVar.}: OnchainGroupManager
|
||||
|
||||
setup:
|
||||
anvilProc = runAnvil()
|
||||
manager = waitFor setupOnchainGroupManager()
|
||||
anvilProc = runAnvil(stateFile = some(DEFAULT_ANVIL_STATE_PATH))
|
||||
manager = waitFor setupOnchainGroupManager(deployContracts = false)
|
||||
|
||||
teardown:
|
||||
stopAnvil(anvilProc)
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
{.push raises: [].}
|
||||
|
||||
import
|
||||
std/[options, os, osproc, deques, streams, strutils, tempfiles, strformat],
|
||||
std/[options, os, osproc, streams, strutils, strformat],
|
||||
results,
|
||||
stew/byteutils,
|
||||
testutils/unittests,
|
||||
@ -14,7 +14,6 @@ import
|
||||
web3/conversions,
|
||||
web3/eth_api_types,
|
||||
json_rpc/rpcclient,
|
||||
json,
|
||||
libp2p/crypto/crypto,
|
||||
eth/keys,
|
||||
results
|
||||
@ -24,25 +23,19 @@ import
|
||||
waku_rln_relay,
|
||||
waku_rln_relay/protocol_types,
|
||||
waku_rln_relay/constants,
|
||||
waku_rln_relay/contract,
|
||||
waku_rln_relay/rln,
|
||||
],
|
||||
../testlib/common,
|
||||
./utils
|
||||
../testlib/common
|
||||
|
||||
const CHAIN_ID* = 1234'u256
|
||||
|
||||
template skip0xPrefix(hexStr: string): int =
|
||||
## Returns the index of the first meaningful char in `hexStr` by skipping
|
||||
## "0x" prefix
|
||||
if hexStr.len > 1 and hexStr[0] == '0' and hexStr[1] in {'x', 'X'}: 2 else: 0
|
||||
|
||||
func strip0xPrefix(s: string): string =
|
||||
let prefixLen = skip0xPrefix(s)
|
||||
if prefixLen != 0:
|
||||
s[prefixLen .. ^1]
|
||||
else:
|
||||
s
|
||||
# Path to the file which Anvil loads at startup to initialize the chain with pre-deployed contracts, an account funded with tokens and approved for spending
|
||||
const DEFAULT_ANVIL_STATE_PATH* =
|
||||
"tests/waku_rln_relay/anvil_state/state-deployed-contracts-mint-and-approved.json.gz"
|
||||
# The contract address of the TestStableToken used for the RLN Membership registration fee
|
||||
const TOKEN_ADDRESS* = "0x5FbDB2315678afecb367f032d93F642f64180aa3"
|
||||
# The contract address used ti interact with the WakuRLNV2 contract via the proxy
|
||||
const WAKU_RLNV2_PROXY_ADDRESS* = "0x5fc8d32690cc91d4c39d9d3abcbd16989f875707"
|
||||
|
||||
proc generateCredentials*(): IdentityCredential =
|
||||
let credRes = membershipKeyGen()
|
||||
@ -106,7 +99,7 @@ proc sendMintCall(
|
||||
recipientAddress: Address,
|
||||
amountTokens: UInt256,
|
||||
recipientBalanceBeforeExpectedTokens: Option[UInt256] = none(UInt256),
|
||||
): Future[TxHash] {.async.} =
|
||||
): Future[void] {.async.} =
|
||||
let doBalanceAssert = recipientBalanceBeforeExpectedTokens.isSome()
|
||||
|
||||
if doBalanceAssert:
|
||||
@ -142,7 +135,7 @@ proc sendMintCall(
|
||||
tx.data = Opt.some(byteutils.hexToSeqByte(mintCallData))
|
||||
|
||||
trace "Sending mint call"
|
||||
let txHash = await web3.send(tx)
|
||||
discard await web3.send(tx)
|
||||
|
||||
let balanceOfSelector = "0x70a08231"
|
||||
let balanceCallData = balanceOfSelector & paddedAddress
|
||||
@ -157,8 +150,6 @@ proc sendMintCall(
|
||||
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
|
||||
@ -487,20 +478,64 @@ proc getAnvilPath*(): string =
|
||||
anvilPath = joinPath(anvilPath, ".foundry/bin/anvil")
|
||||
return $anvilPath
|
||||
|
||||
proc decompressGzipFile*(
|
||||
compressedPath: string, targetPath: string
|
||||
): Result[void, string] =
|
||||
## Decompress a gzipped file using the gunzip command-line utility
|
||||
let cmd = fmt"gunzip -c {compressedPath} > {targetPath}"
|
||||
|
||||
try:
|
||||
let (output, exitCode) = execCmdEx(cmd)
|
||||
if exitCode != 0:
|
||||
return err(
|
||||
"Failed to decompress '" & compressedPath & "' to '" & targetPath & "': " &
|
||||
output
|
||||
)
|
||||
except OSError as e:
|
||||
return err("Failed to execute gunzip command: " & e.msg)
|
||||
except IOError as e:
|
||||
return err("Failed to execute gunzip command: " & e.msg)
|
||||
|
||||
ok()
|
||||
|
||||
proc compressGzipFile*(sourcePath: string, targetPath: string): Result[void, string] =
|
||||
## Compress a file with gzip using the gzip command-line utility
|
||||
let cmd = fmt"gzip -c {sourcePath} > {targetPath}"
|
||||
|
||||
try:
|
||||
let (output, exitCode) = execCmdEx(cmd)
|
||||
if exitCode != 0:
|
||||
return err(
|
||||
"Failed to compress '" & sourcePath & "' to '" & targetPath & "': " & output
|
||||
)
|
||||
except OSError as e:
|
||||
return err("Failed to execute gzip command: " & e.msg)
|
||||
except IOError as e:
|
||||
return err("Failed to execute gzip command: " & e.msg)
|
||||
|
||||
ok()
|
||||
|
||||
# Runs Anvil daemon
|
||||
proc runAnvil*(port: int = 8540, chainId: string = "1234"): Process =
|
||||
proc runAnvil*(
|
||||
port: int = 8540,
|
||||
chainId: string = "1234",
|
||||
stateFile: Option[string] = none(string),
|
||||
dumpStateOnExit: bool = false,
|
||||
): Process =
|
||||
# Passed options are
|
||||
# --port Port to listen on.
|
||||
# --gas-limit Sets the block gas limit in WEI.
|
||||
# --balance The default account balance, specified in ether.
|
||||
# --chain-id Chain ID of the network.
|
||||
# --load-state Initialize the chain from a previously saved state snapshot (read-only)
|
||||
# --dump-state Dump the state on exit to the given file (write-only)
|
||||
# See anvil documentation https://book.getfoundry.sh/reference/anvil/ for more details
|
||||
try:
|
||||
let anvilPath = getAnvilPath()
|
||||
info "Anvil path", anvilPath
|
||||
let runAnvil = startProcess(
|
||||
anvilPath,
|
||||
args = [
|
||||
|
||||
var args =
|
||||
@[
|
||||
"--port",
|
||||
$port,
|
||||
"--gas-limit",
|
||||
@ -509,9 +544,54 @@ proc runAnvil*(port: int = 8540, chainId: string = "1234"): Process =
|
||||
"1000000000",
|
||||
"--chain-id",
|
||||
$chainId,
|
||||
],
|
||||
options = {poUsePath, poStdErrToStdOut},
|
||||
)
|
||||
]
|
||||
|
||||
# Add state file argument if provided
|
||||
if stateFile.isSome():
|
||||
var statePath = stateFile.get()
|
||||
info "State file parameter provided",
|
||||
statePath = statePath,
|
||||
dumpStateOnExit = dumpStateOnExit,
|
||||
absolutePath = absolutePath(statePath)
|
||||
|
||||
# Check if the file is gzip compressed and handle decompression
|
||||
if statePath.endsWith(".gz"):
|
||||
let decompressedPath = statePath[0 .. ^4] # Remove .gz extension
|
||||
debug "Gzip compressed state file detected",
|
||||
compressedPath = statePath, decompressedPath = decompressedPath
|
||||
|
||||
if not fileExists(decompressedPath):
|
||||
decompressGzipFile(statePath, decompressedPath).isOkOr:
|
||||
error "Failed to decompress state file", error = error
|
||||
return nil
|
||||
|
||||
statePath = decompressedPath
|
||||
|
||||
if dumpStateOnExit:
|
||||
# Ensure the directory exists
|
||||
let stateDir = parentDir(statePath)
|
||||
if not dirExists(stateDir):
|
||||
createDir(stateDir)
|
||||
# Fresh deployment: start clean and dump state on exit
|
||||
args.add("--dump-state")
|
||||
args.add(statePath)
|
||||
debug "Anvil configured to dump state on exit", path = statePath
|
||||
else:
|
||||
# Using cache: only load state, don't overwrite it (preserves clean cached state)
|
||||
if fileExists(statePath):
|
||||
args.add("--load-state")
|
||||
args.add(statePath)
|
||||
debug "Anvil configured to load state file (read-only)", path = statePath
|
||||
else:
|
||||
warn "State file does not exist, anvil will start fresh",
|
||||
path = statePath, absolutePath = absolutePath(statePath)
|
||||
else:
|
||||
info "No state file provided, anvil will start fresh without state persistence"
|
||||
|
||||
info "Starting anvil with arguments", args = args.join(" ")
|
||||
|
||||
let runAnvil =
|
||||
startProcess(anvilPath, args = args, options = {poUsePath, poStdErrToStdOut})
|
||||
let anvilPID = runAnvil.processID
|
||||
|
||||
# We read stdout from Anvil to see when daemon is ready
|
||||
@ -549,7 +629,14 @@ proc stopAnvil*(runAnvil: Process) {.used.} =
|
||||
# Send termination signals
|
||||
when not defined(windows):
|
||||
discard execCmdEx(fmt"kill -TERM {anvilPID}")
|
||||
discard execCmdEx(fmt"kill -9 {anvilPID}")
|
||||
# Wait for graceful shutdown to allow state dumping
|
||||
sleep(200)
|
||||
# Only force kill if process is still running
|
||||
let checkResult = execCmdEx(fmt"kill -0 {anvilPID} 2>/dev/null")
|
||||
if checkResult.exitCode == 0:
|
||||
info "Anvil process still running after TERM signal, sending KILL",
|
||||
anvilPID = anvilPID
|
||||
discard execCmdEx(fmt"kill -9 {anvilPID}")
|
||||
else:
|
||||
discard execCmdEx(fmt"taskkill /F /PID {anvilPID}")
|
||||
|
||||
@ -560,52 +647,100 @@ proc stopAnvil*(runAnvil: Process) {.used.} =
|
||||
info "Error stopping Anvil daemon", anvilPID = anvilPID, error = e.msg
|
||||
|
||||
proc setupOnchainGroupManager*(
|
||||
ethClientUrl: string = EthClient, amountEth: UInt256 = 10.u256
|
||||
ethClientUrl: string = EthClient,
|
||||
amountEth: UInt256 = 10.u256,
|
||||
deployContracts: bool = true,
|
||||
): Future[OnchainGroupManager] {.async.} =
|
||||
## Setup an onchain group manager for testing
|
||||
## If deployContracts is false, it will assume that the Anvil testnet already has the required contracts deployed, this significantly speeds up test runs.
|
||||
## To run Anvil with a cached state file containing pre-deployed contracts, see runAnvil documentation.
|
||||
##
|
||||
## To generate/update the cached state file:
|
||||
## 1. Call runAnvil with stateFile and dumpStateOnExit=true
|
||||
## 2. Run setupOnchainGroupManager with deployContracts=true to deploy contracts
|
||||
## 3. The state will be saved to the specified file when anvil exits
|
||||
## 4. Commit this file to git
|
||||
##
|
||||
## To use cached state:
|
||||
## 1. Call runAnvil with stateFile and dumpStateOnExit=false
|
||||
## 2. Anvil loads state in read-only mode (won't overwrite the cached file)
|
||||
## 3. Call setupOnchainGroupManager with deployContracts=false
|
||||
## 4. Tests run fast using pre-deployed contracts
|
||||
let rlnInstanceRes = createRlnInstance()
|
||||
check:
|
||||
rlnInstanceRes.isOk()
|
||||
|
||||
let rlnInstance = rlnInstanceRes.get()
|
||||
|
||||
# connect to the eth client
|
||||
let web3 = await newWeb3(ethClientUrl)
|
||||
let accounts = await web3.provider.eth_accounts()
|
||||
web3.defaultAccount = accounts[1]
|
||||
|
||||
let (privateKey, acc) = createEthAccount(web3)
|
||||
var privateKey: keys.PrivateKey
|
||||
var acc: Address
|
||||
var testTokenAddress: Address
|
||||
var contractAddress: Address
|
||||
|
||||
# we just need to fund the default account
|
||||
# the send procedure returns a tx hash that we don't use, hence discard
|
||||
discard await sendEthTransfer(
|
||||
web3, web3.defaultAccount, acc, ethToWei(1000.u256), some(0.u256)
|
||||
)
|
||||
if not deployContracts:
|
||||
info "Using contract addresses from constants"
|
||||
|
||||
let testTokenAddress = (await deployTestToken(privateKey, acc, web3)).valueOr:
|
||||
assert false, "Failed to deploy test token contract: " & $error
|
||||
return
|
||||
testTokenAddress = Address(hexToByteArray[20](TOKEN_ADDRESS))
|
||||
contractAddress = Address(hexToByteArray[20](WAKU_RLNV2_PROXY_ADDRESS))
|
||||
|
||||
# mint the token from the generated account
|
||||
discard await sendMintCall(
|
||||
web3, web3.defaultAccount, testTokenAddress, acc, ethToWei(1000.u256), some(0.u256)
|
||||
)
|
||||
(privateKey, acc) = createEthAccount(web3)
|
||||
|
||||
let contractAddress = (await executeForgeContractDeployScripts(privateKey, acc, web3)).valueOr:
|
||||
assert false, "Failed to deploy RLN contract: " & $error
|
||||
return
|
||||
# Fund the test account
|
||||
discard await sendEthTransfer(web3, web3.defaultAccount, acc, ethToWei(1000.u256))
|
||||
|
||||
# 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,
|
||||
privateKey,
|
||||
testTokenAddress,
|
||||
contractAddress,
|
||||
ethToWei(200.u256),
|
||||
some(0.u256),
|
||||
)
|
||||
# Mint tokens to the test account
|
||||
await sendMintCall(
|
||||
web3, web3.defaultAccount, testTokenAddress, acc, ethToWei(1000.u256)
|
||||
)
|
||||
|
||||
assert tokenApprovalResult.isOk, tokenApprovalResult.error()
|
||||
# Approve the contract to spend tokens
|
||||
let tokenApprovalResult = await approveTokenAllowanceAndVerify(
|
||||
web3, acc, privateKey, testTokenAddress, contractAddress, ethToWei(200.u256)
|
||||
)
|
||||
assert tokenApprovalResult.isOk(), tokenApprovalResult.error
|
||||
else:
|
||||
info "Performing Token and RLN contracts deployment"
|
||||
(privateKey, acc) = createEthAccount(web3)
|
||||
|
||||
# fund the default account
|
||||
discard await sendEthTransfer(
|
||||
web3, web3.defaultAccount, acc, ethToWei(1000.u256), some(0.u256)
|
||||
)
|
||||
|
||||
testTokenAddress = (await deployTestToken(privateKey, acc, web3)).valueOr:
|
||||
assert false, "Failed to deploy test token contract: " & $error
|
||||
return
|
||||
|
||||
# mint the token from the generated account
|
||||
await sendMintCall(
|
||||
web3,
|
||||
web3.defaultAccount,
|
||||
testTokenAddress,
|
||||
acc,
|
||||
ethToWei(1000.u256),
|
||||
some(0.u256),
|
||||
)
|
||||
|
||||
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,
|
||||
privateKey,
|
||||
testTokenAddress,
|
||||
contractAddress,
|
||||
ethToWei(200.u256),
|
||||
some(0.u256),
|
||||
)
|
||||
|
||||
assert tokenApprovalResult.isOk(), tokenApprovalResult.error
|
||||
|
||||
let manager = OnchainGroupManager(
|
||||
ethClientUrls: @[ethClientUrl],
|
||||
|
||||
@ -41,8 +41,8 @@ suite "Waku v2 REST API - health":
|
||||
var manager {.threadVar.}: OnchainGroupManager
|
||||
|
||||
setup:
|
||||
anvilProc = runAnvil()
|
||||
manager = waitFor setupOnchainGroupManager()
|
||||
anvilProc = runAnvil(stateFile = some(DEFAULT_ANVIL_STATE_PATH))
|
||||
manager = waitFor setupOnchainGroupManager(deployContracts = false)
|
||||
|
||||
teardown:
|
||||
stopAnvil(anvilProc)
|
||||
|
||||
2
vendor/waku-rlnv2-contract
vendored
2
vendor/waku-rlnv2-contract
vendored
@ -1 +1 @@
|
||||
Subproject commit 900d4f95e0e618bdeb4c241f7a4b6347df6bb950
|
||||
Subproject commit 8a338f354481e8a3f3d64a72e38fad4c62e32dcd
|
||||
@ -242,7 +242,7 @@ method register*(
|
||||
fetchedGasPrice = fetchedGasPrice, gasPrice = calculatedGasPrice
|
||||
calculatedGasPrice
|
||||
let idCommitmentHex = identityCredential.idCommitment.inHex()
|
||||
info "identityCredential idCommitmentHex", idCommitment = idCommitmentHex
|
||||
debug "identityCredential idCommitmentHex", idCommitment = idCommitmentHex
|
||||
let idCommitment = identityCredential.idCommitment.toUInt256()
|
||||
let idCommitmentsToErase: seq[UInt256] = @[]
|
||||
info "registering the member",
|
||||
@ -259,11 +259,10 @@ method register*(
|
||||
var tsReceipt: ReceiptObject
|
||||
g.retryWrapper(tsReceipt, "Failed to get the transaction receipt"):
|
||||
await ethRpc.getMinedTransactionReceipt(txHash)
|
||||
info "registration transaction mined", txHash = txHash
|
||||
debug "registration transaction mined", txHash = txHash
|
||||
g.registrationTxHash = some(txHash)
|
||||
# the receipt topic holds the hash of signature of the raised events
|
||||
# TODO: make this robust. search within the event list for the event
|
||||
info "ts receipt", receipt = tsReceipt[]
|
||||
debug "ts receipt", receipt = tsReceipt[]
|
||||
|
||||
if tsReceipt.status.isNone():
|
||||
raise newException(ValueError, "Transaction failed: status is None")
|
||||
@ -272,18 +271,27 @@ method register*(
|
||||
ValueError, "Transaction failed with status: " & $tsReceipt.status.get()
|
||||
)
|
||||
|
||||
## Extract MembershipRegistered event from transaction logs (third event)
|
||||
let thirdTopic = tsReceipt.logs[2].topics[0]
|
||||
info "third topic", thirdTopic = thirdTopic
|
||||
if thirdTopic !=
|
||||
cast[FixedBytes[32]](keccak.keccak256.digest(
|
||||
"MembershipRegistered(uint256,uint256,uint32)"
|
||||
).data):
|
||||
raise newException(ValueError, "register: unexpected event signature")
|
||||
## Search through all transaction logs to find the MembershipRegistered event
|
||||
let expectedEventSignature = cast[FixedBytes[32]](keccak.keccak256.digest(
|
||||
"MembershipRegistered(uint256,uint256,uint32)"
|
||||
).data)
|
||||
|
||||
## Parse MembershipRegistered event data: rateCommitment(256) || membershipRateLimit(256) || index(32)
|
||||
let arguments = tsReceipt.logs[2].data
|
||||
info "tx log data", arguments = arguments
|
||||
var membershipRegisteredLog: Option[LogObject]
|
||||
for log in tsReceipt.logs:
|
||||
if log.topics.len > 0 and log.topics[0] == expectedEventSignature:
|
||||
membershipRegisteredLog = some(log)
|
||||
break
|
||||
|
||||
if membershipRegisteredLog.isNone():
|
||||
raise newException(
|
||||
ValueError, "register: MembershipRegistered event not found in transaction logs"
|
||||
)
|
||||
|
||||
let registrationLog = membershipRegisteredLog.get()
|
||||
|
||||
## Parse MembershipRegistered event data: idCommitment(256) || membershipRateLimit(256) || index(32)
|
||||
let arguments = registrationLog.data
|
||||
trace "registration transaction log data", arguments = arguments
|
||||
let
|
||||
## Extract membership index from transaction log data (big endian)
|
||||
membershipIndex = UInt256.fromBytesBE(arguments[64 .. 95])
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user