nwaku/tests/waku_rln_relay/utils_onchain.nim
Ivan FB f8a233381e
chore: bump dependencies for v0.35 (#3255)
Changes:
	modified:   .gitmodules
	modified:   tests/waku_discv5/utils.nim
	modified:   tests/waku_enr/utils.nim
	modified:   tests/waku_rln_relay/test_rln_group_manager_onchain.nim
	modified:   tests/waku_rln_relay/utils.nim
	modified:   tests/waku_rln_relay/utils_onchain.nim

        modified:   vendor/nim-chronicles
	modified:   vendor/nim-eth
	modified:   vendor/nim-http-utils
	modified:   vendor/nim-json-rpc
	modified:   vendor/nim-json-serialization
	modified:   vendor/nim-libp2p - 1.8.0!
	modified:   vendor/nim-metrics
	new file:   vendor/nim-minilru
	modified:   vendor/nim-nat-traversal
	modified:   vendor/nim-presto
	modified:   vendor/nim-secp256k1
	modified:   vendor/nim-serialization
	modified:   vendor/nim-stew
	modified:   vendor/nim-taskpools
	modified:   vendor/nim-testutils
	modified:   vendor/nim-toml-serialization
	modified:   vendor/nim-unicodedb
	modified:   vendor/nim-unittest2
	modified:   vendor/nim-web3 - from distinct branch that solves Ethereum ABI issue.
	modified:   vendor/nim-websock
	modified:   vendor/nim-zlib
	modified:   vendor/nimcrypto
	modified:   waku.nimble

        modified:   waku/common/enr/builder.nim
	modified:   waku/common/enr/typed_record.nim
	modified:   waku/common/utils/nat.nim
	modified:   waku/discovery/waku_discv5.nim
	modified:   waku/waku_rln_relay/conversion_utils.nim
	modified:   waku/waku_rln_relay/group_manager/on_chain/group_manager.nim
	modified:   waku/waku_rln_relay/rln/wrappers.nim
	modified:   waku/waku_rln_relay/rln_relay.nim

* Eliminate C compilation issue with chat2bridge due to an overcomplicating import from json_rpc instead of using std/json
* Adapt ENR Record handling to new interface of nim-eth
* Fix chrash in group_manager on_chain
* Fix signature of register and MemberRegister to UInt256, check transaction success in register
* Upgrade json-rpc and serialization
* Update to match latest enr and nat interface
* Using of extracted result of contract macro - with necessary adaption
* Bump nim-chornicles, nim-libp2p, nimcrypto
* Bump nim-web3, nim-eth and deps - on_chain/group_manager.nim adaption
* Added status-im/nim-minilru submodule required by latest nim-eth
Fixing tests.
* group_manager: adapt smart contract param types
* update web3 vendor
* bump vendors for v0.35.0
* protobuf.nim: fix compilation error after nim-libp2p bump
* changes to make it compile after rebase from master
---------

Co-authored-by: NagyZoltanPeter <113987313+NagyZoltanPeter@users.noreply.github.com>
2025-01-28 10:04:34 +01:00

290 lines
9.4 KiB
Nim

{.used.}
{.push raises: [].}
import
std/[options, os, osproc, sequtils, deques, streams, strutils, tempfiles, strformat],
stew/[results, byteutils],
testutils/unittests,
chronos,
chronicles,
stint,
web3,
web3/conversions,
web3/eth_api_types,
json,
libp2p/crypto/crypto,
eth/keys,
results
import
waku/[
waku_rln_relay,
waku_rln_relay/protocol_types,
waku_rln_relay/constants,
waku_rln_relay/contract,
waku_rln_relay/rln,
],
../testlib/common,
./utils
const CHAIN_ID* = 1337
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
proc generateCredentials*(rlnInstance: ptr RLN): IdentityCredential =
let credRes = membershipKeyGen(rlnInstance)
return credRes.get()
proc getRateCommitment*(
idCredential: IdentityCredential, userMessageLimit: UserMessageLimit
): RlnRelayResult[RawRateCommitment] =
return RateCommitment(
idCommitment: idCredential.idCommitment, userMessageLimit: userMessageLimit
).toLeaf()
proc generateCredentials*(rlnInstance: ptr RLN, n: int): seq[IdentityCredential] =
var credentials: seq[IdentityCredential]
for i in 0 ..< n:
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
# 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
let balance = await web3.provider.eth_getBalance(web3.defaultAccount, "latest")
debug "Initial account balance: ", balance
# deploy poseidon hasher bytecode
let poseidonT3Receipt = await web3.deployContract(PoseidonT3)
let poseidonT3Address = poseidonT3Receipt.contractAddress.get()
let poseidonAddressStripped = strip0xPrefix($poseidonT3Address)
# deploy lazy imt bytecode
let lazyImtReceipt = await web3.deployContract(
LazyIMT.replace("__$PoseidonT3$__", poseidonAddressStripped)
)
let lazyImtAddress = lazyImtReceipt.contractAddress.get()
let lazyImtAddressStripped = strip0xPrefix($lazyImtAddress)
# deploy waku rlnv2 contract
let wakuRlnContractReceipt = await web3.deployContract(
WakuRlnV2Contract.replace("__$PoseidonT3$__", poseidonAddressStripped).replace(
"__$LazyIMT$__", lazyImtAddressStripped
)
)
let wakuRlnContractAddress = wakuRlnContractReceipt.contractAddress.get()
let wakuRlnAddressStripped = strip0xPrefix($wakuRlnContractAddress)
debug "Address of the deployed rlnv2 contract: ", wakuRlnContractAddress
# 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)
debug "proxy receipt", contractAddress = proxyReceipt.contractAddress.get()
let proxyAddress = proxyReceipt.contractAddress.get()
let newBalance = await web3.provider.eth_getBalance(web3.defaultAccount, "latest")
debug "Account balance after the contract deployment: ", newBalance
await web3.close()
debug "disconnected from ", ethClientAddress
return proxyAddress
proc sendEthTransfer*(
web3: Web3,
accountFrom: Address,
accountTo: Address,
amountWei: UInt256,
accountToBalanceBeforeExpectedWei: Option[UInt256] = none(UInt256),
): Future[TxHash] {.async.} =
let doBalanceAssert = accountToBalanceBeforeExpectedWei.isSome()
if doBalanceAssert:
let balanceBeforeWei = await web3.provider.eth_getBalance(accountTo, "latest")
let balanceBeforeExpectedWei = accountToBalanceBeforeExpectedWei.get()
assert balanceBeforeWei == balanceBeforeExpectedWei,
fmt"Balance is {balanceBeforeWei} but expected {balanceBeforeExpectedWei}"
let gasPrice = int(await web3.provider.eth_gasPrice())
var tx: TransactionArgs
tx.`from` = Opt.some(accountFrom)
tx.to = Opt.some(accountTo)
tx.value = Opt.some(amountWei)
tx.gasPrice = Opt.some(Quantity(gasPrice))
# TODO: handle the error if sending fails
let txHash = await web3.send(tx)
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}"
return txHash
proc ethToWei(eth: UInt256): UInt256 =
eth * 1000000000000000000.u256
proc createEthAccount*(
ethAmount: UInt256 = 1000.u256
): Future[(keys.PrivateKey, Address)] {.async.} =
let web3 = await newWeb3(EthClient)
let accounts = await web3.provider.eth_accounts()
let gasPrice = Quantity(await web3.provider.eth_gasPrice())
web3.defaultAccount = accounts[0]
let pk = keys.PrivateKey.random(rng[])
let acc = Address(toCanonicalAddress(pk.toPublicKey()))
var tx: TransactionArgs
tx.`from` = Opt.some(accounts[0])
tx.value = Opt.some(ethToWei(ethAmount))
tx.to = Opt.some(acc)
tx.gasPrice = Opt.some(Quantity(gasPrice))
# Send ethAmount to acc
discard await web3.send(tx)
let balance = await web3.provider.eth_getBalance(acc, "latest")
assert balance == ethToWei(ethAmount),
fmt"Balance is {balance} but expected {ethToWei(ethAmount)}"
return (pk, acc)
proc createEthAccount*(web3: Web3): (keys.PrivateKey, Address) =
let pk = keys.PrivateKey.random(rng[])
let acc = Address(toCanonicalAddress(pk.toPublicKey()))
return (pk, acc)
proc getAnvilPath*(): string =
var anvilPath = ""
if existsEnv("XDG_CONFIG_HOME"):
anvilPath = joinPath(anvilPath, os.getEnv("XDG_CONFIG_HOME", ""))
else:
anvilPath = joinPath(anvilPath, os.getEnv("HOME", ""))
anvilPath = joinPath(anvilPath, ".foundry/bin/anvil")
return $anvilPath
# Runs Anvil daemon
proc runAnvil*(port: int = 8540, chainId: string = "1337"): 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.
# See anvil documentation https://book.getfoundry.sh/reference/anvil/ for more details
try:
let anvilPath = getAnvilPath()
debug "Anvil path", anvilPath
let runAnvil = startProcess(
anvilPath,
args = [
"--port",
"8540",
"--gas-limit",
"300000000000000",
"--balance",
"1000000000",
"--chain-id",
$CHAIN_ID,
],
options = {poUsePath},
)
let anvilPID = runAnvil.processID
# We read stdout from Anvil to see when daemon is ready
var anvilStartLog: string
var cmdline: string
while true:
try:
if runAnvil.outputstream.readLine(cmdline):
anvilStartLog.add(cmdline)
if cmdline.contains("Listening on 127.0.0.1:" & $port):
break
except Exception, CatchableError:
break
debug "Anvil daemon is running and ready", pid = anvilPID, startLog = anvilStartLog
return runAnvil
except: # TODO: Fix "BareExcept" warning
error "Anvil daemon run failed", err = getCurrentExceptionMsg()
# Stops Anvil daemon
proc stopAnvil*(runAnvil: Process) {.used.} =
let anvilPID = runAnvil.processID
# We wait the daemon to exit
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()
proc setupOnchainGroupManager*(
ethClientAddress: string = EthClient, amountEth: UInt256 = 10.u256
): Future[OnchainGroupManager] {.async.} =
let rlnInstanceRes =
createRlnInstance(tree_path = genTempPath("rln_tree", "group_manager_onchain"))
check:
rlnInstanceRes.isOk()
let rlnInstance = rlnInstanceRes.get()
let contractAddress = await uploadRLNContract(ethClientAddress)
# connect to the eth client
let web3 = await newWeb3(ethClientAddress)
let accounts = await web3.provider.eth_accounts()
web3.defaultAccount = accounts[0]
let (privateKey, acc) = createEthAccount(web3)
# 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)
)
let manager = OnchainGroupManager(
ethClientUrl: ethClientAddress,
ethContractAddress: $contractAddress,
chainId: CHAIN_ID,
ethPrivateKey: some($privateKey),
rlnInstance: rlnInstance,
onFatalErrorAction: proc(errStr: string) =
raiseAssert errStr
,
)
return manager
{.pop.}