mirror of https://github.com/waku-org/nwaku.git
242 lines
7.7 KiB
Nim
242 lines
7.7 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
|
|
|
|
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 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(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 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 setup*(
|
|
ethClientAddress: string = EthClient, ethAmount: 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]
|
|
|
|
var pk = none(string)
|
|
let (privateKey, _) = await createEthAccount(ethAmount)
|
|
pk = some($privateKey)
|
|
|
|
let manager = OnchainGroupManager(
|
|
ethClientUrl: ethClientAddress,
|
|
ethContractAddress: $contractAddress,
|
|
chainId: CHAIN_ID,
|
|
ethPrivateKey: pk,
|
|
rlnInstance: rlnInstance,
|
|
onFatalErrorAction: proc(errStr: string) =
|
|
raiseAssert errStr
|
|
,
|
|
)
|
|
|
|
return manager
|
|
|
|
{.pop.}
|