diff --git a/.github/scripts/wait_for_infra.sh b/.github/scripts/wait_for_infra.sh index d202e6cbcdd2..7f44911f43dc 100755 --- a/.github/scripts/wait_for_infra.sh +++ b/.github/scripts/wait_for_infra.sh @@ -11,12 +11,12 @@ API_KEY=$3 # We retry every 20 seconds, and wait for a total of 5 minutes (15 times) if [ "$INFRA" == "mainnet-fork" ]; then - export ETHEREUM_HOST="https://$DEPLOY_TAG-mainnet-fork.aztec.network:8545/$API_KEY" + export ETHEREUM_HOSTS="https://$DEPLOY_TAG-mainnet-fork.aztec.network:8545/$API_KEY" curl -H "Content-Type: application/json" -X POST --data '{"method":"eth_chainId","params":[],"id":49,"jsonrpc":"2.0"}' \ --connect-timeout 30 \ --retry 15 \ --retry-delay 20 \ - $ETHEREUM_HOST + $ETHEREUM_HOSTS elif [ "$INFRA" == "pxe" ]; then export PXE_URL="https://api.aztec.network/$DEPLOY_TAG/aztec-pxe/$API_KEY/status" curl \ diff --git a/.github/workflows/devnet-deploy.yml b/.github/workflows/devnet-deploy.yml index a42ca670fffa..63bd400da0c0 100644 --- a/.github/workflows/devnet-deploy.yml +++ b/.github/workflows/devnet-deploy.yml @@ -152,7 +152,7 @@ jobs: docker run --rm --network host $AZTEC_DOCKER_IMAGE bootstrap-network \ --rpc-url http://127.0.0.1:$PXE_PORT \ - --l1-rpc-url http://127.0.0.1:$ETHEREUM_PORT \ + --l1-rpc-urls http://127.0.0.1:$ETHEREUM_PORT \ --l1-chain-id "$L1_CHAIN_ID" \ --mnemonic "$MNEMONIC" \ --address-index "$ADDRESS_INDEX" \ diff --git a/.github/workflows/network-deploy.yml b/.github/workflows/network-deploy.yml index d584af135858..2e12f030b8e1 100644 --- a/.github/workflows/network-deploy.yml +++ b/.github/workflows/network-deploy.yml @@ -116,8 +116,8 @@ jobs: TF_STATE_BUCKET: aztec-terraform GKE_CLUSTER_CONTEXT: "gke_testnet-440309_us-west1-a_${{ inputs.cluster }}" GCP_API_KEY_HEADER: "X-goog-api-key" - EXTERNAL_ETHEREUM_HOST: "https://json-rpc.${{ secrets.SEPOLIA_EXTERNAL_HOST }}?key=${{ secrets.SEPOLIA_API_KEY }}" - EXTERNAL_ETHEREUM_CONSENSUS_HOST: "https://beacon.${{ secrets.SEPOLIA_EXTERNAL_HOST }}" + EXTERNAL_ETHEREUM_HOSTS: "https://json-rpc.${{ secrets.GCP_SEPOLIA_URL }}?key=${{ secrets.GCP_SEPOLIA_API_KEY }},${{ secrets.INFURA_SEPOLIA_URL }}" + EXTERNAL_ETHEREUM_CONSENSUS_HOST: "https://beacon.${{ secrets.GCP_SEPOLIA_URL }}" steps: - name: Checkout code @@ -195,9 +195,9 @@ jobs: -var="AZTEC_DOCKER_IMAGE=${{ env.AZTEC_DOCKER_IMAGE }}" \ -var="L1_DEPLOYMENT_PRIVATE_KEY=${{ secrets.SEPOLIA_L1_DEPLOYMENT_PRIVATE_KEY }}" \ -var="L1_DEPLOYMENT_MNEMONIC=$L1_DEPLOYMENT_MNEMONIC" \ - -var="EXTERNAL_ETHEREUM_HOST=${{ env.EXTERNAL_ETHEREUM_HOST }}" \ + -var="EXTERNAL_ETHEREUM_HOSTS=${{ env.EXTERNAL_ETHEREUM_HOSTS }}" \ -var="EXTERNAL_ETHEREUM_CONSENSUS_HOST=${{ env.EXTERNAL_ETHEREUM_CONSENSUS_HOST }}" \ - -var="EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY=${{ secrets.SEPOLIA_API_KEY }}" \ + -var="EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY=${{ secrets.GCP_SEPOLIA_API_KEY }}" \ -var="EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY_HEADER=${{ env.GCP_API_KEY_HEADER }}" \ -lock=${{ inputs.respect_tf_lock }} else @@ -223,9 +223,9 @@ jobs: -var="L1_DEPLOYMENT_PRIVATE_KEY=${{ secrets.SEPOLIA_L1_DEPLOYMENT_PRIVATE_KEY }}" \ -var="L1_DEPLOYMENT_MNEMONIC=$L1_DEPLOYMENT_MNEMONIC" \ -var="L1_DEPLOYMENT_SALT=${DEPLOYMENT_SALT:-$RANDOM}" \ - -var="EXTERNAL_ETHEREUM_HOST=${{ env.EXTERNAL_ETHEREUM_HOST }}" \ + -var="EXTERNAL_ETHEREUM_HOSTS=${{ env.EXTERNAL_ETHEREUM_HOSTS }}" \ -var="EXTERNAL_ETHEREUM_CONSENSUS_HOST=${{ env.EXTERNAL_ETHEREUM_CONSENSUS_HOST }}" \ - -var="EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY=${{ secrets.SEPOLIA_API_KEY }}" \ + -var="EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY=${{ secrets.GCP_SEPOLIA_API_KEY }}" \ -var="EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY_HEADER=${{ env.GCP_API_KEY_HEADER }}" \ ${{ contains(env.VALUES_FILE, 'devnet') && '-var="EXPOSE_HTTPS_BOOTNODE=true"' || '' }} \ -out=tfplan \ diff --git a/.github/workflows/nightly-kind-test.yml b/.github/workflows/nightly-kind-test.yml index 0d61e6f35921..84b1762f3002 100644 --- a/.github/workflows/nightly-kind-test.yml +++ b/.github/workflows/nightly-kind-test.yml @@ -19,8 +19,8 @@ env: AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} BUILD_INSTANCE_SSH_KEY: ${{ secrets.BUILD_INSTANCE_SSH_KEY }} GIT_COMMIT: ${{ github.event.pull_request.head.sha || github.sha }} - EXTERNAL_ETHEREUM_HOST: "https://json-rpc.${{ secrets.SEPOLIA_EXTERNAL_HOST }}?key=${{ secrets.SEPOLIA_API_KEY }}" - EXTERNAL_ETHEREUM_CONSENSUS_HOST: "https://beacon.${{ secrets.SEPOLIA_EXTERNAL_HOST }}" + EXTERNAL_ETHEREUM_HOSTS: "https://json-rpc.${{ secrets.GCP_SEPOLIA_URL }}?key=${{ secrets.GCP_SEPOLIA_API_KEY }}" + EXTERNAL_ETHEREUM_CONSENSUS_HOST: "https://beacon.${{ secrets.GCP_SEPOLIA_URL }}" GCP_API_KEY_HEADER: "X-goog-api-key" jobs: setup: @@ -121,9 +121,9 @@ jobs: docker pull aztecprotocol/end-to-end:${{ env.GIT_COMMIT }} # Set the sepolia run variables - export EXTERNAL_ETHEREUM_HOST=${{ env.EXTERNAL_ETHEREUM_HOST }} + export EXTERNAL_ETHEREUM_HOSTS=${{ env.EXTERNAL_ETHEREUM_HOSTS }} export EXTERNAL_ETHEREUM_CONSENSUS_HOST=${{ env.EXTERNAL_ETHEREUM_CONSENSUS_HOST }} - export EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY=${{ secrets.SEPOLIA_API_KEY }} + export EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY=${{ secrets.GCP_SEPOLIA_API_KEY }} export EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY_HEADER=${{ env.GCP_API_KEY_HEADER }} export L1_DEPLOYMENT_PRIVATE_KEY=${{ secrets.SEPOLIA_L1_DEPLOYMENT_PRIVATE_KEY }} export L1_ACCOUNTS_MNEMONIC="${{ secrets.SEPOLIA_ACCOUNTS_MNEMONIC }}" diff --git a/boxes/docker-compose.yml b/boxes/docker-compose.yml index 37dfb68222eb..98779ac52be2 100644 --- a/boxes/docker-compose.yml +++ b/boxes/docker-compose.yml @@ -15,8 +15,8 @@ services: working_dir: /root/aztec-packages/yarn-project/aztec command: "node ./dest/bin start --sandbox" environment: + ETHEREUM_HOSTS: http://ethereum:8545 HARDWARE_CONCURRENCY: 4 - ETHEREUM_HOST: http://ethereum:8545 L1_CHAIN_ID: 31337 FORCE_COLOR: ${FORCE_COLOR:-1} ARCHIVER_POLLING_INTERVAL_MS: 50 @@ -45,7 +45,7 @@ services: HARDWARE_CONCURRENCY: 4 DEBUG: "aztec:*" DEBUG_COLORS: "true" - ETHEREUM_HOST: http://ethereum:8545 + ETHEREUM_HOSTS: http://ethereum:8545 L1_CHAIN_ID: 31337 FORCE_COLOR: ${FORCE_COLOR:-1} PXE_URL: http://aztec:8080 diff --git a/ci3/tmux_split b/ci3/tmux_split index 28b0d68dc769..8d9488fd48db 100755 --- a/ci3/tmux_split +++ b/ci3/tmux_split @@ -6,8 +6,8 @@ set -eu # Check if at least two commands are provided (otherwise what is the point) if [ "$#" -lt 2 ]; then - echo "Usage: $0 ..." - exit 1 + echo "Usage: $0 ..." + exit 1 fi # Launches tmux with 1 window that has as many panes as commands @@ -23,7 +23,7 @@ tmux new-session -d -s "$session_name" -e LOG_LEVEL=${LOG_LEVEL:-"debug"} \ -e OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=${OTEL_EXPORTER_OTLP_METRICS_ENDPOINT:-} \ -e OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=${OTEL_EXPORTER_OTLP_TRACES_ENDPOINT:-} \ -e L1_CONSENSUS_HOST_URL=${L1_CONSENSUS_HOST_URL:-} \ - -e ETHEREUM_HOST=${ETHEREUM_HOST:-} \ + -e ETHEREUM_HOSTS=${ETHEREUM_HOSTS:-} \ -e LOG_JSON=${LOG_JSON:-} shift 1 @@ -39,7 +39,7 @@ echo "Using tmux base_index=$base_index" # Create the necessary number of panes and set titles num_commands=${#commands[@]} -for ((i=0; i 'while true; do node --no-warnings /usr/src/yarn-project/aztec/dest/bin/index.js block-number -u http://aztec-bot | head -n2; sleep 10; done' restart: on-failure:5 @@ -194,7 +193,7 @@ services: aztec-cli: image: "aztecprotocol/${IMAGE:-aztec:master}" environment: - ETHEREUM_HOST: http://ethereum:8545 + ETHEREUM_HOSTS: http://ethereum:8545 PXE_URL: http://aztec-bot L1_CHAIN_ID: 31337 stdin_open: true diff --git a/docker-compose.yml b/docker-compose.yml index c0928538e49f..449a1830ab78 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -48,7 +48,7 @@ services: OUTBOX_CONTRACT_ADDRESS: "0x3ec4b6c68a8c2ce4c78cdd465b3019b11a568d1d" FEE_JUICE_CONTRACT_ADDRESS: "0x73c43b919973711e096bfc04c9d4b3be511ffc0b" FEE_JUICE_PORTAL_CONTRACT_ADDRESS: "0xdf25b0a34dbee9f25518f7a4d63bab8b3bb3e496" - ETHEREUM_HOST: + ETHEREUM_HOSTS: P2P_TCP_LISTEN_ADDR: "0.0.0.0:9000" P2P_UDP_LISTEN_ADDR: "0.0.0.0:9001" P2P_TCP_ANNOUNCE_ADDR: ":9000" @@ -60,11 +60,11 @@ services: OTEL_EXPORTER_OTLP_METRICS_ENDPOINT: ${OTEL_EXPORTER_OTLP_METRICS_ENDPOINT:-http://otel-collector:4318/v1/metrics} OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: ${OTEL_EXPORTER_OTLP_TRACES_ENDPOINT:-http://otel-collector:4318/v1/traces} secrets: - - ethereum-host + - ethereum-hosts - p2p-boot-node entrypoint: | /bin/sh -c ' - export ETHEREUM_HOST=$$(cat /var/run/secrets/ethereum-host) + export ETHEREUM_HOSTS=$$(cat /var/run/secrets/ethereum-hosts) export BOOTSTRAP_NODES=$$(cat /var/run/secrets/p2p-boot-node) test -z "$$PEER_ID_PRIVATE_KEY" -a ! -f /var/lib/aztec/p2p-private-key && node /usr/src/yarn-project/cli/dest/bin/index.js generate-p2p-private-key | head -1 | cut -d" " -f 3 | tee /var/lib/aztec/p2p-private-key || echo "Re-using existing P2P private key" @@ -105,7 +105,7 @@ services: PXE_URL: http://pxe:8080 NODE_NO_WARNINGS: 1 SECRET_KEY: - ETHEREUM_HOST: + ETHEREUM_HOSTS: profiles: - cli @@ -121,7 +121,7 @@ services: secrets: aztec-node-url: environment: AZTEC_NODE_URL - ethereum-host: - environment: ETHEREUM_HOST + ethereum-hosts: + environment: ETHEREUM_HOSTS p2p-boot-node: environment: BOOTSTRAP_NODES diff --git a/docs/docs/developers/reference/environment_reference/cli_reference.md b/docs/docs/developers/reference/environment_reference/cli_reference.md index a2c31796ae33..5b8ee8dca8c5 100644 --- a/docs/docs/developers/reference/environment_reference/cli_reference.md +++ b/docs/docs/developers/reference/environment_reference/cli_reference.md @@ -285,7 +285,7 @@ aztec deploy-l1-contracts [options] ``` Required options: -- `-u, --rpc-url `: URL of the Ethereum host. +- `-u, --rpc-urls `: List of URLs of Ethereum nodes (comma separated). - `-pk, --private-key `: The private key to use for deployment. ### deploy-l1-verifier @@ -296,7 +296,7 @@ aztec deploy-l1-verifier [options] ``` Required options: -- `--eth-rpc-url `: URL of the Ethereum host. +- `--eth-rpc-urls `: List of URLs of Ethereum nodes (comma separated). - `-pk, --private-key `: The private key to use for deployment. - `--verifier `: Either 'mock' or 'real'. @@ -308,7 +308,7 @@ aztec bridge-fee-juice [options] ``` Required option: -- `--l1-rpc-url `: URL of the Ethereum host. +- `--l1-rpc-urls `: List of URLs of Ethereum nodes (comma separated). ### get-l1-balance Gets the balance of ETH or an ERC20 token on L1 for a given Ethereum address. @@ -318,7 +318,7 @@ aztec get-l1-balance [options] ``` Required option: -- `--l1-rpc-url `: URL of the Ethereum host. +- `--l1-rpc-urls `: List of URLs of Ethereum nodes (comma separated). ## Utility Commands @@ -370,6 +370,6 @@ aztec sequencers [who] [options] Commands: list, add, remove, who-next Required option: -- `--l1-rpc-url `: URL of the Ethereum host. +- `--l1-rpc-urls `: List of URLs of Ethereum nodes (comma separated). Note: Most commands accept a `--rpc-url` option to specify the Aztec node URL, and many accept fee-related options for gas limit and price configuration. diff --git a/docs/docs/developers/reference/environment_reference/sandbox-reference.md b/docs/docs/developers/reference/environment_reference/sandbox-reference.md index 671bc41cab8f..71aaa9087cfa 100644 --- a/docs/docs/developers/reference/environment_reference/sandbox-reference.md +++ b/docs/docs/developers/reference/environment_reference/sandbox-reference.md @@ -21,7 +21,7 @@ To change them, you can open `~/.aztec/docker-compose.sandbox.yml` and edit them ```sh LOG_LEVEL=debug # Options are 'fatal', 'error', 'warn', 'info', 'verbose', 'debug', 'trace' HOST_WORKDIR='${PWD}' # The location to store log outputs. Will use ~/.aztec where the docker-compose.yml file is stored by default. -ETHEREUM_HOST=http://ethereum:8545 # The Ethereum JSON RPC URL. We use an anvil instance that runs in parallel to the sandbox on docker by default. +ETHEREUM_HOSTS=http://ethereum:8545 # List of Ethereum JSON RPC URLs. We use an anvil instance that runs in parallel to the sandbox on docker by default. L1_CHAIN_ID=31337 # The Chain ID that the Ethereum host is using. TEST_ACCOUNTS='true' # Option to deploy 3 test account when sandbox starts. (default: true) MODE='sandbox' # Option to start the sandbox or a standalone part of the system. (default: sandbox) diff --git a/docs/docs/developers/tutorials/codealong/js_tutorials/simple_dapp/4_testing.md b/docs/docs/developers/tutorials/codealong/js_tutorials/simple_dapp/4_testing.md index 1c0b037fd9de..643c05995b8b 100644 --- a/docs/docs/developers/tutorials/codealong/js_tutorials/simple_dapp/4_testing.md +++ b/docs/docs/developers/tutorials/codealong/js_tutorials/simple_dapp/4_testing.md @@ -35,7 +35,7 @@ import { TokenContract } from "@aztec/noir-contracts.js/Token"; const { PXE_URL = "http://localhost:8080", - ETHEREUM_HOST = "http://localhost:8545", + ETHEREUM_HOSTS = "http://localhost:8545", } = process.env; describe("token contract", () => { diff --git a/docs/docs/run_node/guides/run_nodes/how_to_run_prover_draft.md b/docs/docs/run_node/guides/run_nodes/how_to_run_prover_draft.md index 8c1ea44feac5..03019363e385 100644 --- a/docs/docs/run_node/guides/run_nodes/how_to_run_prover_draft.md +++ b/docs/docs/run_node/guides/run_nodes/how_to_run_prover_draft.md @@ -67,7 +67,7 @@ To run a prover agent, either run `aztec start --prover`, or add the `--prover` The Aztec client is configured via environment variables, the following ones being relevant for the prover node: -- **ETHEREUM_HOST**: URL to an Ethereum node. +- **ETHEREUM_HOSTS**: List of URLs of Ethereum nodes (comma separated). - **L1_CHAIN_ID**: Chain ID for the L1 Ethereum chain. - **DATA_DIRECTORY**: Local folder where archive and world state data is stored. - **AZTEC_PORT**: Port where the JSON-RPC APIs will be served. @@ -93,4 +93,3 @@ Both the prover node and agent also rely on the following: - **LOG_LEVEL**: One of `debug`, `verbose`, `info`, `warn`, or `error`. - **LOG_JSON**: Set to `true` to output logs in JSON format (unreleased). - **OTEL_EXPORTER_OTLP_METRICS_ENDPOINT**: Optional URL for pushing telemetry data to a remote OpenTelemetry data collector. - diff --git a/docs/docs/run_node/guides/run_nodes/how_to_run_sequencer_draft.md b/docs/docs/run_node/guides/run_nodes/how_to_run_sequencer_draft.md index 4d45c281fb69..cdfa59eb531d 100644 --- a/docs/docs/run_node/guides/run_nodes/how_to_run_sequencer_draft.md +++ b/docs/docs/run_node/guides/run_nodes/how_to_run_sequencer_draft.md @@ -58,7 +58,7 @@ The `aztec-spartan.sh` script will set the following required variables on your | Variable | Description | | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -| ETHEREUM_HOST | URL to the Ethereum node your validator will connect to. For as long as we're on private networks, please use the value in `aztec-spartan.sh` | +| ETHEREUM_HOSTS | List of URLs of Ethereum nodes (comma separated). For as long as we're on private networks, please use the value in `aztec-spartan.sh` | | BOOTNODE_URL | URL to a bootnode that supplies L1 contract addresses and the ENR of the bootstrap nodes. | | IMAGE | The docker image to run | @@ -74,7 +74,7 @@ The Archiver's primary functions are data storage and retrieval (i.e. L1->L2 mes | Variable | Description | | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | -| ETHEREUM_HOST | This is the URL to the L1 node your validator will connect to. For as long as we're on private networks, please use the value in `aztec-spartan.sh` | +| ETHEREUM_HOSTS | List of L1 node URLs (comma separated) your validator will connect to. For as long as we're on private networks, please use the value in `aztec-spartan.sh` | | L1_CHAIN_ID | Chain ID of the L1 | | DATA_DIRECTORY | Optional dir to store archiver and world state data. If omitted will store in memory | | ARCHIVER_POLLING_INTERVAL_MS | The polling interval in ms for retrieving new L2 blocks and encrypted logs | diff --git a/iac/mainnet-fork/scripts/run_nginx_anvil.sh b/iac/mainnet-fork/scripts/run_nginx_anvil.sh index b4fce9dbfc5f..d06edd74ce8c 100755 --- a/iac/mainnet-fork/scripts/run_nginx_anvil.sh +++ b/iac/mainnet-fork/scripts/run_nginx_anvil.sh @@ -17,7 +17,7 @@ trap 'kill $(jobs -p)' SIGTERM # Anvil defaults - Nginx assumes these values to be as they are HOST="0.0.0.0" PORT=8544 -ETHEREUM_HOST=$HOST:$PORT +ETHEREUM_HOSTS=$HOST:$PORT # Stripping double quotations from the mnemonic seed phrase echo "stripping double quotations from the mnemonic seed phrase: ${MNEMONIC:0:10}..." MNEMONIC_STRIPPED=${MNEMONIC//\"/} @@ -32,12 +32,12 @@ mkdir -p /var/log/anvil/ # Run anvil logging to stdout .foundry/bin/anvil --block-time 12 --host $HOST -p $PORT -m "$MNEMONIC_STRIPPED" -f=https://mainnet.infura.io/v3/$INFURA_API_KEY --chain-id=$L1_CHAIN_ID --fork-block-number=15918000 --block-base-fee-per-gas=10 -s=$SNAPSHOT_FREQUENCY --state=./data/state --balance=1000000000000000000 & -echo "Waiting for ethereum host at $ETHEREUM_HOST..." -while ! curl -s $ETHEREUM_HOST >/dev/null; do sleep 1; done +echo "Waiting for ethereum host at $ETHEREUM_HOSTS..." +while ! curl -s $ETHEREUM_HOSTS >/dev/null; do sleep 1; done # Fix anvil's fork timestamp -curl -s -H "Content-Type: application/json" -XPOST -d"{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"evm_setNextBlockTimestamp\",\"params\":[\"$(date +%s | xargs printf '0x%x')\"]}" $ETHEREUM_HOST > /dev/null -curl -s -H "Content-Type: application/json" -XPOST -d"{\"id\":2,\"jsonrpc\":\"2.0\",\"method\":\"evm_mine\",\"params\":[]}" $ETHEREUM_HOST > /dev/null +curl -s -H "Content-Type: application/json" -XPOST -d"{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"evm_setNextBlockTimestamp\",\"params\":[\"$(date +%s | xargs printf '0x%x')\"]}" $ETHEREUM_HOSTS >/dev/null +curl -s -H "Content-Type: application/json" -XPOST -d"{\"id\":2,\"jsonrpc\":\"2.0\",\"method\":\"evm_mine\",\"params\":[]}" $ETHEREUM_HOSTS >/dev/null echo "Starting nginx..." nginx & diff --git a/iac/mainnet-fork/scripts/wait_for_fork b/iac/mainnet-fork/scripts/wait_for_fork index ddafc00cc2eb..9a48d41f2473 100755 --- a/iac/mainnet-fork/scripts/wait_for_fork +++ b/iac/mainnet-fork/scripts/wait_for_fork @@ -6,10 +6,10 @@ set -e # This script waits on a healthy status from the fork - a valid response to the chainid request # We retry every 20 seconds, and wait for a total of 5 minutes (15 times) -export ETHEREUM_HOST="https://$DEPLOY_TAG-mainnet-fork.aztec.network:8545/$API_KEY" +export ETHEREUM_HOSTS="https://$DEPLOY_TAG-mainnet-fork.aztec.network:8545/$API_KEY" curl -H "Content-Type: application/json" -X POST --data '{"method":"eth_chainId","params":[],"id":33,"jsonrpc":"2.0"}' \ --connect-timeout 30 \ --retry 15 \ --retry-delay 20 \ - $ETHEREUM_HOST + $ETHEREUM_HOSTS diff --git a/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh b/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh index b227f82914e2..67bc1a3b0443 100755 --- a/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh +++ b/noir/noir-repo/examples/codegen_verifier/codegen_verifier.sh @@ -25,15 +25,15 @@ HEX_PUBLIC_INPUTS=$(head -c $PUBLIC_INPUT_BYTES $PROOF_PATH | od -An -v -t x1 | HEX_PROOF=$(tail -c +$(($PUBLIC_INPUT_BYTES + 1)) $PROOF_PATH | od -An -v -t x1 | tr -d $' \n') # Split public inputs into strings where each string represents a `bytes32`. -SPLIT_HEX_PUBLIC_INPUTS=$(sed -e 's/.\{64\}/0x&,/g' <<< $HEX_PUBLIC_INPUTS) +SPLIT_HEX_PUBLIC_INPUTS=$(sed -e 's/.\{64\}/0x&,/g' <<<$HEX_PUBLIC_INPUTS) # Spin up an anvil node to deploy the contract to anvil & DEPLOY_INFO=$(forge create UltraVerifier \ - --rpc-url "127.0.0.1:8545" \ - --private-key "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" \ - --json) + --rpc-urls "127.0.0.1:8545" \ + --private-key "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" \ + --json) VERIFIER_ADDRESS=$(echo $DEPLOY_INFO | jq -r '.deployedTo') # Call the verifier contract with our proof. diff --git a/scripts/run_native_testnet.sh b/scripts/run_native_testnet.sh index efc63d2e7693..8757013cc293 100755 --- a/scripts/run_native_testnet.sh +++ b/scripts/run_native_testnet.sh @@ -34,93 +34,93 @@ INTERLEAVED=false METRICS=false DISABLE_BLOB_SINK=false LOG_LEVEL="info" -ETHEREUM_HOST= +ETHEREUM_HOSTS= L1_CONSENSUS_HOST_URL= OTEL_COLLECTOR_ENDPOINT=${OTEL_COLLECTOR_ENDPOINT:-"http://localhost:4318"} # Function to display help message function display_help { - echo "Usage: $0 [options]" - echo - echo "Options:" - echo " -h Display this help message" - echo " -t Specify the test file (default: $TEST_SCRIPT)" - echo " -p Specify the prover command (default: $PROVER_SCRIPT)" - echo " -val Specify the number of validators (default: $NUM_VALIDATORS)" - echo " -v Set logging level to verbose" - echo " -vv Set logging level to debug" - echo " -i Run interleaved (default: $INTERLEAVED)" - echo " -m Run with metrics (default: $METRICS) will use $OTEL_COLLECTOR_ENDPOINT as default otel endpoint" - echo " -c Specify the otel collector endpoint (default: $OTEL_COLLECTOR_ENDPOINT)" - echo " -b Disable the blob sink (default: false)" - echo " -e Specify the ethereum host url (default: $ETHEREUM_HOST)" - echo " -cl Specify the l1 consensus host url (default: $L1_CONSENSUS_HOST_URL)" - echo - echo "Example:" - echo " $0 -t ./test-4epochs.sh -val 5 -v" + echo "Usage: $0 [options]" + echo + echo "Options:" + echo " -h Display this help message" + echo " -t Specify the test file (default: $TEST_SCRIPT)" + echo " -p Specify the prover command (default: $PROVER_SCRIPT)" + echo " -val Specify the number of validators (default: $NUM_VALIDATORS)" + echo " -v Set logging level to verbose" + echo " -vv Set logging level to debug" + echo " -i Run interleaved (default: $INTERLEAVED)" + echo " -m Run with metrics (default: $METRICS) will use $OTEL_COLLECTOR_ENDPOINT as default otel endpoint" + echo " -c Specify the otel collector endpoint (default: $OTEL_COLLECTOR_ENDPOINT)" + echo " -b Disable the blob sink (default: false)" + echo " -e Specify the ethereum host url (default: $ETHEREUM_HOSTS)" + echo " -cl Specify the l1 consensus host url (default: $L1_CONSENSUS_HOST_URL)" + echo + echo "Example:" + echo " $0 -t ./test-4epochs.sh -val 5 -v" } # Parse command line arguments while [[ $# -gt 0 ]]; do case $1 in - -h) - display_help - exit 0 - ;; - -t) - TEST_SCRIPT="$2" - shift 2 - ;; - -p) - PROVER_SCRIPT="$2" - shift 2 - ;; - -val) - NUM_VALIDATORS="$2" - shift 2 - ;; - -v) - if [[ $LOG_LEVEL == "info" ]]; then - LOG_LEVEL="verbose" - elif [[ $LOG_LEVEL == "verbose" ]]; then - LOG_LEVEL="debug" - fi - shift - ;; - -i) - INTERLEAVED=true - shift - ;; - -vv) + -h) + display_help + exit 0 + ;; + -t) + TEST_SCRIPT="$2" + shift 2 + ;; + -p) + PROVER_SCRIPT="$2" + shift 2 + ;; + -val) + NUM_VALIDATORS="$2" + shift 2 + ;; + -v) + if [[ $LOG_LEVEL == "info" ]]; then + LOG_LEVEL="verbose" + elif [[ $LOG_LEVEL == "verbose" ]]; then LOG_LEVEL="debug" - shift - ;; - -m) - METRICS=true - shift - ;; - -c) - OTEL_COLLECTOR_ENDPOINT="$2" - shift 2 - ;; - -e) - ETHEREUM_HOST="$2" - shift 2 - ;; - -cl) - L1_CONSENSUS_HOST_URL="$2" - shift 2 - ;; - -b) - DISABLE_BLOB_SINK=true - shift - ;; - *) - echo "Invalid option: $1" >&2 - display_help - exit 1 - ;; + fi + shift + ;; + -i) + INTERLEAVED=true + shift + ;; + -vv) + LOG_LEVEL="debug" + shift + ;; + -m) + METRICS=true + shift + ;; + -c) + OTEL_COLLECTOR_ENDPOINT="$2" + shift 2 + ;; + -e) + ETHEREUM_HOSTS="$2" + shift 2 + ;; + -cl) + L1_CONSENSUS_HOST_URL="$2" + shift 2 + ;; + -b) + DISABLE_BLOB_SINK=true + shift + ;; + *) + echo "Invalid option: $1" >&2 + display_help + exit 1 + ;; esac done @@ -135,15 +135,15 @@ if $METRICS; then fi # If an ethereum rpc url is provided, use it -if [ -n "$ETHEREUM_HOST" ]; then - export ETHEREUM_HOST +if [ -n "$ETHEREUM_HOSTS" ]; then + export ETHEREUM_HOSTS fi if [ -n "$L1_CONSENSUS_HOST_URL" ]; then export L1_CONSENSUS_HOST_URL fi # If an ethereum url has been provided, do not run the ethereum.sh script -if [ -n "$ETHEREUM_HOST" ]; then +if [ -n "$ETHEREUM_HOSTS" ]; then ETHEREUM_SCRIPT="" else ETHEREUM_SCRIPT="./ethereum.sh" diff --git a/spartan/aztec-network/files/config/setup-service-addresses.sh b/spartan/aztec-network/files/config/setup-service-addresses.sh index bac3eedf08b5..949831cefdba 100644 --- a/spartan/aztec-network/files/config/setup-service-addresses.sh +++ b/spartan/aztec-network/files/config/setup-service-addresses.sh @@ -4,146 +4,145 @@ set -ex # Get load balancer IP for service function get_load_balancer_ip() { - local SERVICE_LABEL=$1 - local PORT=$2 - local MAX_RETRIES=30 - local RETRY_INTERVAL=2 - local attempt=1 - - # Check load balancer exists for service - while [ $attempt -le $MAX_RETRIES ]; do - LOAD_BALANCER_IP=$(kubectl get svc -n ${NAMESPACE} -l app=${SERVICE_LABEL} -o jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}') - if [ -n "$LOAD_BALANCER_IP" ]; then - break - fi - echo "Attempt $attempt: Waiting for ${SERVICE_LABEL} load balancer to be available..." >&2 - sleep $RETRY_INTERVAL - attempt=$((attempt + 1)) - done - - if [ -z "$LOAD_BALANCER_IP" ]; then - echo "Error: Failed to get load balancer IP after $MAX_RETRIES attempts" >&2 - return 1 + local SERVICE_LABEL=$1 + local PORT=$2 + local MAX_RETRIES=30 + local RETRY_INTERVAL=2 + local attempt=1 + + # Check load balancer exists for service + while [ $attempt -le $MAX_RETRIES ]; do + LOAD_BALANCER_IP=$(kubectl get svc -n ${NAMESPACE} -l app=${SERVICE_LABEL} -o jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}') + if [ -n "$LOAD_BALANCER_IP" ]; then + break fi - - echo "Load balancer IP: ${LOAD_BALANCER_IP}" >&2 - echo "http://${LOAD_BALANCER_IP}:${PORT}" + echo "Attempt $attempt: Waiting for ${SERVICE_LABEL} load balancer to be available..." >&2 + sleep $RETRY_INTERVAL + attempt=$((attempt + 1)) + done + + if [ -z "$LOAD_BALANCER_IP" ]; then + echo "Error: Failed to get load balancer IP after $MAX_RETRIES attempts" >&2 + return 1 + fi + + echo "Load balancer IP: ${LOAD_BALANCER_IP}" >&2 + echo "http://${LOAD_BALANCER_IP}:${PORT}" } # Function to get pod and node details function get_service_address() { - local SERVICE_LABEL=$1 - local PORT=$2 - local MAX_RETRIES=30 - local RETRY_INTERVAL=2 - local attempt=1 - - # Get pod name - while [ $attempt -le $MAX_RETRIES ]; do - POD_NAME=$(kubectl get pods -n ${NAMESPACE} -l app=${SERVICE_LABEL} -o jsonpath='{.items[0].metadata.name}') - if [ -n "$POD_NAME" ]; then - break - fi - echo "Attempt $attempt: Waiting for ${SERVICE_LABEL} pod to be available..." >&2 - sleep $RETRY_INTERVAL - attempt=$((attempt + 1)) - done - - if [ -z "$POD_NAME" ]; then - echo "Error: Failed to get ${SERVICE_LABEL} pod name after $MAX_RETRIES attempts" >&2 - return 1 + local SERVICE_LABEL=$1 + local PORT=$2 + local MAX_RETRIES=30 + local RETRY_INTERVAL=2 + local attempt=1 + + # Get pod name + while [ $attempt -le $MAX_RETRIES ]; do + POD_NAME=$(kubectl get pods -n ${NAMESPACE} -l app=${SERVICE_LABEL} -o jsonpath='{.items[0].metadata.name}') + if [ -n "$POD_NAME" ]; then + break fi - echo "Pod name: [${POD_NAME}]" >&2 - - # Get node name - attempt=1 - NODE_NAME="" - while [ $attempt -le $MAX_RETRIES ]; do - NODE_NAME=$(kubectl get pod ${POD_NAME} -n ${NAMESPACE} -o jsonpath='{.spec.nodeName}') - if [ -n "$NODE_NAME" ]; then - break - fi - echo "Attempt $attempt: Waiting for node name to be available..." >&2 - sleep $RETRY_INTERVAL - attempt=$((attempt + 1)) - done - - if [ -z "$NODE_NAME" ]; then - echo "Error: Failed to get node name after $MAX_RETRIES attempts" >&2 - return 1 + echo "Attempt $attempt: Waiting for ${SERVICE_LABEL} pod to be available..." >&2 + sleep $RETRY_INTERVAL + attempt=$((attempt + 1)) + done + + if [ -z "$POD_NAME" ]; then + echo "Error: Failed to get ${SERVICE_LABEL} pod name after $MAX_RETRIES attempts" >&2 + return 1 + fi + echo "Pod name: [${POD_NAME}]" >&2 + + # Get node name + attempt=1 + NODE_NAME="" + while [ $attempt -le $MAX_RETRIES ]; do + NODE_NAME=$(kubectl get pod ${POD_NAME} -n ${NAMESPACE} -o jsonpath='{.spec.nodeName}') + if [ -n "$NODE_NAME" ]; then + break fi - echo "Node name: ${NODE_NAME}" >&2 - - # Get the node's external IP - NODE_IP=$(kubectl get node ${NODE_NAME} -o jsonpath='{.status.addresses[?(@.type=="ExternalIP")].address}') - echo "Node IP: ${NODE_IP}" >&2 - echo "http://${NODE_IP}:${PORT}" + echo "Attempt $attempt: Waiting for node name to be available..." >&2 + sleep $RETRY_INTERVAL + attempt=$((attempt + 1)) + done + + if [ -z "$NODE_NAME" ]; then + echo "Error: Failed to get node name after $MAX_RETRIES attempts" >&2 + return 1 + fi + echo "Node name: ${NODE_NAME}" >&2 + + # Get the node's external IP + NODE_IP=$(kubectl get node ${NODE_NAME} -o jsonpath='{.status.addresses[?(@.type=="ExternalIP")].address}') + echo "Node IP: ${NODE_IP}" >&2 + echo "http://${NODE_IP}:${PORT}" } # Configure Ethereum execution client address -if [ "${EXTERNAL_ETHEREUM_HOST}" != "" ]; then - ETHEREUM_ADDR="${EXTERNAL_ETHEREUM_HOST}" +if [ "${EXTERNAL_ETHEREUM_HOSTS}" != "" ]; then + ETHEREUM_ADDR="${EXTERNAL_ETHEREUM_HOSTS}" elif [ "${NETWORK_PUBLIC}" = "true" ]; then - ETHEREUM_ADDR=$(get_load_balancer_ip "eth-execution" "${ETHEREUM_PORT}") + ETHEREUM_ADDR=$(get_load_balancer_ip "eth-execution" "${ETHEREUM_PORT}") else - ETHEREUM_ADDR="http://${SERVICE_NAME}-eth-execution.${NAMESPACE}:${ETHEREUM_PORT}" + ETHEREUM_ADDR="http://${SERVICE_NAME}-eth-execution.${NAMESPACE}:${ETHEREUM_PORT}" fi # Configure Ethereum Consensus address if [ "${EXTERNAL_ETHEREUM_CONSENSUS_HOST}" != "" ]; then - ETHEREUM_CONSENSUS_ADDR="${EXTERNAL_ETHEREUM_CONSENSUS_HOST}" + ETHEREUM_CONSENSUS_ADDR="${EXTERNAL_ETHEREUM_CONSENSUS_HOST}" elif [ "${NETWORK_PUBLIC}" = "true" ]; then - ETHEREUM_CONSENSUS_ADDR=$(get_load_balancer_ip "eth-beacon" "${ETHEREUM_CONSENSUS_PORT}") + ETHEREUM_CONSENSUS_ADDR=$(get_load_balancer_ip "eth-beacon" "${ETHEREUM_CONSENSUS_PORT}") else - ETHEREUM_CONSENSUS_ADDR="http://${SERVICE_NAME}-eth-beacon.${NAMESPACE}:${ETHEREUM_CONSENSUS_PORT}" + ETHEREUM_CONSENSUS_ADDR="http://${SERVICE_NAME}-eth-beacon.${NAMESPACE}:${ETHEREUM_CONSENSUS_PORT}" fi # Configure Boot Node address if [ "${BOOT_NODE_EXTERNAL_HOST}" != "" ]; then - BOOT_NODE_ADDR="${BOOT_NODE_EXTERNAL_HOST}" + BOOT_NODE_ADDR="${BOOT_NODE_EXTERNAL_HOST}" elif [ "${NETWORK_PUBLIC}" = "true" ]; then - BOOT_NODE_ADDR=$(get_service_address "boot-node" "${BOOT_NODE_PORT}") + BOOT_NODE_ADDR=$(get_service_address "boot-node" "${BOOT_NODE_PORT}") else - BOOT_NODE_ADDR="http://${SERVICE_NAME}-boot-node.${NAMESPACE}:${BOOT_NODE_PORT}" + BOOT_NODE_ADDR="http://${SERVICE_NAME}-boot-node.${NAMESPACE}:${BOOT_NODE_PORT}" fi # Configure Prover Node address if [ "${PROVER_NODE_EXTERNAL_HOST}" != "" ]; then - PROVER_NODE_ADDR="${PROVER_NODE_EXTERNAL_HOST}" + PROVER_NODE_ADDR="${PROVER_NODE_EXTERNAL_HOST}" elif [ "${NETWORK_PUBLIC}" = "true" ]; then - PROVER_NODE_ADDR=$(get_service_address "prover-node" "${PROVER_NODE_PORT}") + PROVER_NODE_ADDR=$(get_service_address "prover-node" "${PROVER_NODE_PORT}") else - PROVER_NODE_ADDR="http://${SERVICE_NAME}-prover-node.${NAMESPACE}:${PROVER_NODE_PORT}" + PROVER_NODE_ADDR="http://${SERVICE_NAME}-prover-node.${NAMESPACE}:${PROVER_NODE_PORT}" fi if [ "${PROVER_BROKER_EXTERNAL_HOST}" != "" ]; then - PROVER_BROKER_ADDR="${PROVER_BROKER_EXTERNAL_HOST}" + PROVER_BROKER_ADDR="${PROVER_BROKER_EXTERNAL_HOST}" else - PROVER_BROKER_ADDR="http://${SERVICE_NAME}-prover-broker.${NAMESPACE}:${PROVER_BROKER_PORT}" + PROVER_BROKER_ADDR="http://${SERVICE_NAME}-prover-broker.${NAMESPACE}:${PROVER_BROKER_PORT}" fi # Configure OTEL_COLLECTOR_ENDPOINT if not set in values file if [ "${TELEMETRY:-false}" = "true" ] && [ "${OTEL_COLLECTOR_ENDPOINT}" = "" ]; then - OTEL_COLLECTOR_PORT=${OTEL_COLLECTOR_PORT:-4318} - OTEL_COLLECTOR_ENDPOINT="http://metrics-opentelemetry-collector.metrics:$OTEL_COLLECTOR_PORT" + OTEL_COLLECTOR_PORT=${OTEL_COLLECTOR_PORT:-4318} + OTEL_COLLECTOR_ENDPOINT="http://metrics-opentelemetry-collector.metrics:$OTEL_COLLECTOR_PORT" fi # Write addresses to file for sourcing -echo "export ETHEREUM_HOST=${ETHEREUM_ADDR}" >> /shared/config/service-addresses -echo "export L1_CONSENSUS_HOST_URL=${ETHEREUM_CONSENSUS_ADDR}" >> /shared/config/service-addresses -echo "export L1_CONSENSUS_HOST_API_KEY=${EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY}" >> /shared/config/service-addresses -echo "export L1_CONSENSUS_HOST_API_KEY_HEADER=${EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY_HEADER}" >> /shared/config/service-addresses -echo "export BOOT_NODE_HOST=${BOOT_NODE_ADDR}" >> /shared/config/service-addresses -echo "export PROVER_NODE_HOST=${PROVER_NODE_ADDR}" >> /shared/config/service-addresses -echo "export PROVER_BROKER_HOST=${PROVER_BROKER_ADDR}" >> /shared/config/service-addresses +echo "export ETHEREUM_HOSTS=${ETHEREUM_ADDR}" >>/shared/config/service-addresses +echo "export L1_CONSENSUS_HOST_URL=${ETHEREUM_CONSENSUS_ADDR}" >>/shared/config/service-addresses +echo "export L1_CONSENSUS_HOST_API_KEY=${EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY}" >>/shared/config/service-addresses +echo "export L1_CONSENSUS_HOST_API_KEY_HEADER=${EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY_HEADER}" >>/shared/config/service-addresses +echo "export BOOT_NODE_HOST=${BOOT_NODE_ADDR}" >>/shared/config/service-addresses +echo "export PROVER_NODE_HOST=${PROVER_NODE_ADDR}" >>/shared/config/service-addresses +echo "export PROVER_BROKER_HOST=${PROVER_BROKER_ADDR}" >>/shared/config/service-addresses if [ "${OTEL_COLLECTOR_ENDPOINT}" != "" ]; then - echo "export OTEL_COLLECTOR_ENDPOINT=$OTEL_COLLECTOR_ENDPOINT" >> /shared/config/service-addresses - echo "export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=$OTEL_COLLECTOR_ENDPOINT/v1/logs" >> /shared/config/service-addresses - echo "export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=$OTEL_COLLECTOR_ENDPOINT/v1/metrics" >> /shared/config/service-addresses - echo "export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=$OTEL_COLLECTOR_ENDPOINT/v1/traces" >> /shared/config/service-addresses + echo "export OTEL_COLLECTOR_ENDPOINT=$OTEL_COLLECTOR_ENDPOINT" >>/shared/config/service-addresses + echo "export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=$OTEL_COLLECTOR_ENDPOINT/v1/logs" >>/shared/config/service-addresses + echo "export OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=$OTEL_COLLECTOR_ENDPOINT/v1/metrics" >>/shared/config/service-addresses + echo "export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=$OTEL_COLLECTOR_ENDPOINT/v1/traces" >>/shared/config/service-addresses fi - echo "Addresses configured:" cat /shared/config/service-addresses diff --git a/spartan/aztec-network/templates/_helpers.tpl b/spartan/aztec-network/templates/_helpers.tpl index c6a67022adeb..60d35f812f34 100644 --- a/spartan/aztec-network/templates/_helpers.tpl +++ b/spartan/aztec-network/templates/_helpers.tpl @@ -145,8 +145,8 @@ Service Address Setup Container value: "{{ .Values.telemetry.enabled }}" - name: OTEL_COLLECTOR_ENDPOINT value: "{{ .Values.telemetry.otelCollectorEndpoint }}" - - name: EXTERNAL_ETHEREUM_HOST - value: "{{ .Values.ethereum.execution.externalHost }}" + - name: EXTERNAL_ETHEREUM_HOSTS + value: "{{ .Values.ethereum.execution.externalHosts }}" - name: ETHEREUM_PORT value: "{{ .Values.ethereum.execution.service.port }}" - name: EXTERNAL_ETHEREUM_CONSENSUS_HOST @@ -204,15 +204,20 @@ nodeSelector: {{- end -}} {{- define "aztec-network.waitForEthereum" -}} -if [ -n "${EXTERNAL_ETHEREUM_HOST}" ]; then - export ETHEREUM_HOST="${EXTERNAL_ETHEREUM_HOST}" +if [ -n "${EXTERNAL_ETHEREUM_HOSTS}" ]; then + export ETHEREUM_HOSTS="${EXTERNAL_ETHEREUM_HOSTS}" fi -echo "Awaiting ethereum node at ${ETHEREUM_HOST}" -until curl -s -X POST -H 'Content-Type: application/json' \ - -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":67}' \ - ${ETHEREUM_HOST} | grep 0x; do - echo "Waiting for Ethereum node ${ETHEREUM_HOST}..." +echo "Awaiting any ethereum node from: ${ETHEREUM_HOSTS}" +while true; do + for HOST in $(echo "${ETHEREUM_HOSTS}" | tr ',' '\n'); do + if curl -s -X POST -H 'Content-Type: application/json' \ + -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":67}' \ + "${HOST}" | grep -q 0x; then + echo "Ethereum node ${HOST} is ready!" + break 2 + fi + echo "Waiting for Ethereum node ${HOST}..." + done sleep 5 done -echo "Ethereum node is ready!" {{- end -}} diff --git a/spartan/aztec-network/templates/deploy-l1-verifier.yaml b/spartan/aztec-network/templates/deploy-l1-verifier.yaml index 735763ac3907..7cbe04bf828c 100644 --- a/spartan/aztec-network/templates/deploy-l1-verifier.yaml +++ b/spartan/aztec-network/templates/deploy-l1-verifier.yaml @@ -100,8 +100,8 @@ spec: value: "{{ .Values.network.public }}" - name: NAMESPACE value: {{ .Release.Namespace }} - - name: EXTERNAL_ETHEREUM_HOST - value: "{{ .Values.ethereum.execution.externalHost }}" + - name: EXTERNAL_ETHEREUM_HOSTS + value: "{{ .Values.ethereum.execution.externalHosts }}" - name: ETHEREUM_PORT value: "{{ .Values.ethereum.execution.service.port }}" - name: EXTERNAL_BOOT_NODE_HOST diff --git a/spartan/aztec-network/templates/eth/eth-execution.yaml b/spartan/aztec-network/templates/eth/eth-execution.yaml index 9d0b844771ad..c37ebacc2ef0 100644 --- a/spartan/aztec-network/templates/eth/eth-execution.yaml +++ b/spartan/aztec-network/templates/eth/eth-execution.yaml @@ -1,4 +1,4 @@ -{{- if not .Values.ethereum.execution.externalHost }} +{{- if not .Values.ethereum.execution.externalHosts }} apiVersion: apps/v1 kind: Deployment metadata: diff --git a/spartan/aztec-network/templates/faucet.yaml b/spartan/aztec-network/templates/faucet.yaml index 313e8fe5689d..7a0652613e41 100644 --- a/spartan/aztec-network/templates/faucet.yaml +++ b/spartan/aztec-network/templates/faucet.yaml @@ -1,4 +1,4 @@ -{{- if and (not .Values.ethereum.execution.externalHost) .Values.pxe.enabled }} +{{- if and (not .Values.ethereum.execution.externalHosts) .Values.pxe.enabled }} apiVersion: apps/v1 kind: Deployment metadata: diff --git a/spartan/aztec-network/templates/setup-l2-contracts.yaml b/spartan/aztec-network/templates/setup-l2-contracts.yaml index effc968d2126..c08e53296ef8 100644 --- a/spartan/aztec-network/templates/setup-l2-contracts.yaml +++ b/spartan/aztec-network/templates/setup-l2-contracts.yaml @@ -88,8 +88,8 @@ spec: value: "{{ .Values.network.public }}" - name: NAMESPACE value: {{ .Release.Namespace }} - - name: EXTERNAL_ETHEREUM_HOST - value: "{{ .Values.ethereum.execution.externalHost }}" + - name: EXTERNAL_ETHEREUM_HOSTS + value: "{{ .Values.ethereum.execution.externalHosts }}" - name: ETHEREUM_PORT value: "{{ .Values.ethereum.execution.service.port }}" - name: EXTERNAL_BOOT_NODE_HOST diff --git a/spartan/aztec-network/values.yaml b/spartan/aztec-network/values.yaml index 0d03a74716f5..f692c552b07f 100644 --- a/spartan/aztec-network/values.yaml +++ b/spartan/aztec-network/values.yaml @@ -241,7 +241,7 @@ ethereum: maxTxInputSizeBytes: "1310720" args: "" execution: - externalHost: "" + externalHosts: "" service: port: 8545 targetPort: 8545 diff --git a/spartan/aztec-network/values/ci-sepolia.yaml b/spartan/aztec-network/values/ci-sepolia.yaml index 8290c4159a0a..90c8923b6adb 100644 --- a/spartan/aztec-network/values/ci-sepolia.yaml +++ b/spartan/aztec-network/values/ci-sepolia.yaml @@ -11,7 +11,7 @@ ethereum: l1GasPriceMax: 500 l1FixedPriorityFeePerGas: 3 execution: - externalHost: "" + externalHosts: "" beacon: externalHost: "" apiKey: "" diff --git a/spartan/aztec-network/values/ignition-testnet.yaml b/spartan/aztec-network/values/ignition-testnet.yaml index 4c045b88a27e..bf13dbdc11f4 100644 --- a/spartan/aztec-network/values/ignition-testnet.yaml +++ b/spartan/aztec-network/values/ignition-testnet.yaml @@ -28,6 +28,7 @@ validator: replicas: 3 l1FixedPriorityFeePerGas: 3 l1GasLimitBufferPercentage: 15 + l1GasPriceMax: 500 sequencer: minTxsPerBlock: 0 maxTxsPerBlock: 0 @@ -67,7 +68,7 @@ ethereum: l1FixedPriorityFeePerGas: 3 deployL1ContractsPrivateKey: execution: - externalHost: + externalHosts: resources: requests: memory: "1Gi" diff --git a/spartan/aztec-network/values/rc-2.yaml b/spartan/aztec-network/values/rc-2.yaml index 6ae96570c7a7..2f24ad43aabe 100644 --- a/spartan/aztec-network/values/rc-2.yaml +++ b/spartan/aztec-network/values/rc-2.yaml @@ -20,7 +20,7 @@ ethereum: chainId: "11155111" deployL1ContractsPrivateKey: execution: - externalHost: + externalHosts: beacon: externalHost: apiKey: "" diff --git a/spartan/aztec-network/values/sepolia-3-validators-with-metrics.yaml b/spartan/aztec-network/values/sepolia-3-validators-with-metrics.yaml index eaf8a23978d1..454d735e9087 100644 --- a/spartan/aztec-network/values/sepolia-3-validators-with-metrics.yaml +++ b/spartan/aztec-network/values/sepolia-3-validators-with-metrics.yaml @@ -14,7 +14,7 @@ ethereum: validatorKeyIndexStart: 1 chainId: "11155111" execution: - externalHost: + externalHosts: beacon: externalHost: apiKey: diff --git a/spartan/aztec-network/values/sepolia-48-validators-with-metrics.yaml b/spartan/aztec-network/values/sepolia-48-validators-with-metrics.yaml index 7a7728fda959..fa9a4bcd1172 100644 --- a/spartan/aztec-network/values/sepolia-48-validators-with-metrics.yaml +++ b/spartan/aztec-network/values/sepolia-48-validators-with-metrics.yaml @@ -9,7 +9,7 @@ ethereum: chainId: "11155111" deployL1ContractsPrivateKey: execution: - externalHost: + externalHosts: beacon: externalHost: diff --git a/spartan/aztec-network/values/sepolia-48-validators-with-proving-and-metrics.yaml b/spartan/aztec-network/values/sepolia-48-validators-with-proving-and-metrics.yaml index 9f28f252e8f9..812dce55ab4a 100644 --- a/spartan/aztec-network/values/sepolia-48-validators-with-proving-and-metrics.yaml +++ b/spartan/aztec-network/values/sepolia-48-validators-with-proving-and-metrics.yaml @@ -16,7 +16,7 @@ ethereum: chainId: "11155111" deployL1ContractsPrivateKey: execution: - externalHost: + externalHosts: beacon: externalHost: apiKey: "" diff --git a/spartan/releases/README.md b/spartan/releases/README.md index e9da25a6247a..c206cfec6ba3 100644 --- a/spartan/releases/README.md +++ b/spartan/releases/README.md @@ -50,11 +50,11 @@ To spare you a few keystrokes, you can use `npx aztec-spartan [start/stop/logs/u The `aztec-spartan.sh` script will set the following required variables on your behalf. You can ofcourse override the variables set by the script by simply changing the `.env` file directly and re-running `./aztec-spartan.sh` -| Variable | Description | -| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -| ETHEREUM_HOST | URL to the Ethereum node your validator will connect to. For as long as we're on private networks, please use the value in `aztec-spartan.sh` | -| BOOTNODE_URL | URL to a bootnode that supplies L1 contract addresses and the ENR of the bootstrap nodes. | -| IMAGE | The docker image to run | +| Variable | Description | +| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | +| ETHEREUM_HOSTS | URL to the Ethereum node your validator will connect to. For as long as we're on private networks, please use the value in `aztec-spartan.sh` | +| BOOTNODE_URL | URL to a bootnode that supplies L1 contract addresses and the ENR of the bootstrap nodes. | +| IMAGE | The docker image to run | In addition, the user is prompted to enter 1) an IP Address and a P2P port to be used for the TCP and UDP addresses (defaults to 40400) 2) A port for your node (8080) 3) an Ethereum private key 4) `COINBASE` which is the Ethereum address associated with the private key and 5) a path to a local directory to store node data if you don't opt for a named volume. @@ -68,7 +68,7 @@ The Archiver's primary functions are data storage and retrieval (i.e. L1->L2 mes | Variable | Description | | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | -| ETHEREUM_HOST | This is the URL to the L1 node your validator will connect to. For as long as we're on private networks, please use the value in `aztec-spartan.sh` | +| ETHEREUM_HOSTS | This is the URL to the L1 node your validator will connect to. For as long as we're on private networks, please use the value in `aztec-spartan.sh` | | L1_CHAIN_ID | Chain ID of the L1 | | DATA_DIRECTORY | Optional dir to store archiver and world state data. If omitted will store in memory | | ARCHIVER_POLLING_INTERVAL_MS | The polling interval in ms for retrieving new L2 blocks and encrypted logs | diff --git a/spartan/releases/testnet/aztec-spartan.sh b/spartan/releases/testnet/aztec-spartan.sh index 6fb5ab0d589f..d3193abab347 100755 --- a/spartan/releases/testnet/aztec-spartan.sh +++ b/spartan/releases/testnet/aztec-spartan.sh @@ -18,262 +18,259 @@ DEFAULT_IP=$(curl -s --connect-timeout 5 https://api.ipify.org?format=json | gre DEFAULT_BIND_MOUNT_DIR="$HOME/aztec-data" # unset these to avoid conflicts with the host's environment -ETHEREUM_HOST= +ETHEREUM_HOSTS= IMAGE= BOOTNODE_URL= - # Parse command line arguments parse_args() { - while [[ $# -gt 0 ]]; do - case $1 in - -b|--bootnode-url) - BOOTNODE_URL="$2" - shift 2 - ;; - -n|--network) - NETWORK="$2" - shift 2 - ;; - -i|--image) - IMAGE="$2" - shift 2 - ;; - -e|--ethereum-host) - ETHEREUM_HOST="$2" - shift 2 - ;; - -p|--port) - CLI_PORT="$2" - shift 2 - ;; - -p2p|--p2p-port) - CLI_P2P_PORT="$2" - shift 2 - ;; - -ip|--ip) - CLI_IP="$2" - shift 2 - ;; - -k|--key) - CLI_KEY="$2" - shift 2 - ;; - -d|--data-dir) - BIND_MOUNT_DIR="$2" - shift 2 - ;; - -pk|--p2p-id-private-key) - PEER_ID_PRIVATE_KEY="$2" - shift 2 - ;; - *) - shift - ;; - esac - done + while [[ $# -gt 0 ]]; do + case $1 in + -b | --bootnode-url) + BOOTNODE_URL="$2" + shift 2 + ;; + -n | --network) + NETWORK="$2" + shift 2 + ;; + -i | --image) + IMAGE="$2" + shift 2 + ;; + -e | --ethereum-hosts) + ETHEREUM_HOSTS="$2" + shift 2 + ;; + -p | --port) + CLI_PORT="$2" + shift 2 + ;; + -p2p | --p2p-port) + CLI_P2P_PORT="$2" + shift 2 + ;; + -ip | --ip) + CLI_IP="$2" + shift 2 + ;; + -k | --key) + CLI_KEY="$2" + shift 2 + ;; + -d | --data-dir) + BIND_MOUNT_DIR="$2" + shift 2 + ;; + -pk | --p2p-id-private-key) + PEER_ID_PRIVATE_KEY="$2" + shift 2 + ;; + *) + shift + ;; + esac + done } # Show banner function show_banner() { - echo -e "${BLUE}" - echo " _ ____ _____ _____ _____ _____ _____ ____ _____ _ _ _____ _____ " - echo " / \ |_ /|_ _| _____| __/|_ _| ____/ ___|_ _| \ | | ____|_ _|" - echo " / _ \ / / | | | _| | | | | | _| \___ \ | | | \| | _| | | " - echo " / ___ \/ /_ | | | |___ | |__ | | | |___ ___) || | | |\ | |___ | | " - echo "/_/ \_\___| |_| |______|____\ |_| |_____|____/ |_| |_| \_|_____| |_| " - echo -e "${NC}" + echo -e "${BLUE}" + echo " _ ____ _____ _____ _____ _____ _____ ____ _____ _ _ _____ _____ " + echo " / \ |_ /|_ _| _____| __/|_ _| ____/ ___|_ _| \ | | ____|_ _|" + echo " / _ \ / / | | | _| | | | | | _| \___ \ | | | \| | _| | | " + echo " / ___ \/ /_ | | | |___ | |__ | | | |___ ___) || | | |\ | |___ | | " + echo "/_/ \_\___| |_| |______|____\ |_| |_____|____/ |_| |_| \_|_____| |_| " + echo -e "${NC}" } # Check if Docker is installed check_docker() { - echo -e "${BLUE}Checking Docker installation...${NC}" - if command -v docker >/dev/null 2>&1 && command -v docker compose >/dev/null 2>&1; then - echo -e "${GREEN}Docker and Docker Compose are installed${NC}" - return 0 + echo -e "${BLUE}Checking Docker installation...${NC}" + if command -v docker >/dev/null 2>&1 && command -v docker compose >/dev/null 2>&1; then + echo -e "${GREEN}Docker and Docker Compose are installed${NC}" + return 0 + else + echo -e "${RED}Docker or Docker Compose not found${NC}" + # If macOS + if [[ "$(uname -s)" == "Darwin" ]]; then + echo -e "${YELLOW}macOS detected. Please install Docker Desktop for Mac:${NC}" + echo "https://www.docker.com/products/docker-desktop" + return 1 else - echo -e "${RED}Docker or Docker Compose not found${NC}" - # If macOS - if [[ "$(uname -s)" == "Darwin" ]]; then - echo -e "${YELLOW}macOS detected. Please install Docker Desktop for Mac:${NC}" - echo "https://www.docker.com/products/docker-desktop" - return 1 - else - read -p "Would you like to install Docker? [Y/n] " -n 1 -r - echo - if [[ $REPLY =~ ^[Yy]$ ]] || [[ -z $REPLY ]]; then - install_docker - return $? - fi - return 1 - fi + read -p "Would you like to install Docker? [Y/n] " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]] || [[ -z $REPLY ]]; then + install_docker + return $? + fi + return 1 fi + fi } # Install Docker install_docker() { - echo -e "${BLUE}Installing Docker...${NC}" - if curl -fsSL https://get.docker.com | sh; then - sudo usermod -aG docker $USER - echo -e "${GREEN}Docker installed successfully${NC}" - echo -e "${YELLOW}Please log out and back in for group changes to take effect${NC}" - return 0 - else - echo -e "${RED}Failed to install Docker${NC}" - return 1 - fi + echo -e "${BLUE}Installing Docker...${NC}" + if curl -fsSL https://get.docker.com | sh; then + sudo usermod -aG docker $USER + echo -e "${GREEN}Docker installed successfully${NC}" + echo -e "${YELLOW}Please log out and back in for group changes to take effect${NC}" + return 0 + else + echo -e "${RED}Failed to install Docker${NC}" + return 1 + fi } # Get public IP get_public_ip() { - echo -e "${BLUE}Fetching public IP...${NC}" - PUBLIC_IP=$(curl -s https://api.ipify.org?format=json | grep -o '"ip":"[^"]*' | cut -d'"' -f4) - if [ -n "$PUBLIC_IP" ]; then - echo -e "${GREEN}Public IP: $PUBLIC_IP${NC}" - return 0 - else - echo -e "${YELLOW}Failed to get public IP${NC}" - return 1 - fi + echo -e "${BLUE}Fetching public IP...${NC}" + PUBLIC_IP=$(curl -s https://api.ipify.org?format=json | grep -o '"ip":"[^"]*' | cut -d'"' -f4) + if [ -n "$PUBLIC_IP" ]; then + echo -e "${GREEN}Public IP: $PUBLIC_IP${NC}" + return 0 + else + echo -e "${YELLOW}Failed to get public IP${NC}" + return 1 + fi } get_node_info() { - echo -e "${BLUE}Fetching node info...${NC}" - CMD="get-node-info --node-url ${BOOTNODE_URL} --json" - # TODO: use the correct (corresponding) image - # Can't do it today because `release/unhinged-unicorn` doesn't support --json flag - NODE_INFO=$(curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","method":"node_getNodeInfo","params":[],"id":1}' -s ${BOOTNODE_URL}) - - # Extract the relevant fields - result=$(echo $NODE_INFO | jq -r '.result') - L1_CHAIN_ID=$(echo $result | jq -r '.l1ChainId') - BOOTSTRAP_NODES=$(echo $result | jq -r '.enr') - REGISTRY_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.registryAddress') - GOVERNANCE_PROPOSER_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.governanceProposerAddress') - FEE_JUICE_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.feeJuiceAddress') - ROLLUP_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.rollupAddress') - REWARD_DISTRIBUTOR_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.rewardDistributorAddress') - GOVERNANCE_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.governanceAddress') - COIN_ISSUER_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.coinIssuerAddress') - FEE_JUICE_PORTAL_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.feeJuicePortalAddress') - INBOX_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.inboxAddress') - OUTBOX_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.outboxAddress') - - echo -e "${GREEN}Node info fetched successfully${NC}" - return 0 + echo -e "${BLUE}Fetching node info...${NC}" + CMD="get-node-info --node-url ${BOOTNODE_URL} --json" + # TODO: use the correct (corresponding) image + # Can't do it today because `release/unhinged-unicorn` doesn't support --json flag + NODE_INFO=$(curl -X POST -H 'Content-Type: application/json' -d '{"jsonrpc":"2.0","method":"node_getNodeInfo","params":[],"id":1}' -s ${BOOTNODE_URL}) + + # Extract the relevant fields + result=$(echo $NODE_INFO | jq -r '.result') + L1_CHAIN_ID=$(echo $result | jq -r '.l1ChainId') + BOOTSTRAP_NODES=$(echo $result | jq -r '.enr') + REGISTRY_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.registryAddress') + GOVERNANCE_PROPOSER_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.governanceProposerAddress') + FEE_JUICE_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.feeJuiceAddress') + ROLLUP_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.rollupAddress') + REWARD_DISTRIBUTOR_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.rewardDistributorAddress') + GOVERNANCE_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.governanceAddress') + COIN_ISSUER_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.coinIssuerAddress') + FEE_JUICE_PORTAL_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.feeJuicePortalAddress') + INBOX_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.inboxAddress') + OUTBOX_CONTRACT_ADDRESS=$(echo $result | jq -r '.l1ContractAddresses.outboxAddress') + + echo -e "${GREEN}Node info fetched successfully${NC}" + return 0 } # Configure environment configure_environment() { - local args=("$@") - parse_args "${args[@]}" - - echo -e "${BLUE}Configuring environment...${NC}" - if [ -n "$NETWORK" ]; then - NETWORK="$NETWORK" - else - read -p "Network [unhinged-unicorn]: " NETWORK - NETWORK=${NETWORK:-unhinged-unicorn} - fi - - # if the network is `unhinged-unicorn` - if [ "$NETWORK" = "unhinged-unicorn" ]; then - BOOTNODE_URL="${BOOTNODE_URL:-http://34.169.19.201:8080}" - ETHEREUM_HOST="${ETHEREUM_HOST:-http://34.82.214.254:8545}" - IMAGE="${IMAGE:-aztecprotocol/aztec:unhinged-unicorn}" - else - # unknown network - echo -e "${RED}Unknown network: $NETWORK${NC}" - fi - - # Check that bootnode, ethereum host, and image are set - if [ -z "$BOOTNODE_URL" ] || [ -z "$ETHEREUM_HOST" ] || [ -z "$IMAGE" ]; then - echo -e "${RED}Bootnode, Ethereum host, and image are required${NC}" - exit 1 - fi - - # get the node info - get_node_info - - - if [ -n "$CLI_P2P_PORT" ]; then - P2P_PORT="$CLI_P2P_PORT" - else - read -p "P2P Port [$DEFAULT_P2P_PORT]: " P2P_PORT - P2P_PORT=${P2P_PORT:-$DEFAULT_P2P_PORT} - fi - - if [ -n "$CLI_PORT" ]; then - PORT="$CLI_PORT" - else - read -p "Node Port [$DEFAULT_PORT]: " PORT - PORT=${PORT:-$DEFAULT_PORT} - fi - - if [ -n "$CLI_KEY" ]; then - KEY="$CLI_KEY" - else - while true; do - read -p "Validator Private Key: " KEY - if [ -z "$KEY" ]; then - echo -e "${RED}Error: Validator Private Key is required${NC}" - else - break - fi - done - fi + local args=("$@") + parse_args "${args[@]}" + + echo -e "${BLUE}Configuring environment...${NC}" + if [ -n "$NETWORK" ]; then + NETWORK="$NETWORK" + else + read -p "Network [unhinged-unicorn]: " NETWORK + NETWORK=${NETWORK:-unhinged-unicorn} + fi + + # if the network is `unhinged-unicorn` + if [ "$NETWORK" = "unhinged-unicorn" ]; then + BOOTNODE_URL="${BOOTNODE_URL:-http://34.169.19.201:8080}" + ETHEREUM_HOSTS="${ETHEREUM_HOSTS:-http://34.82.214.254:8545}" + IMAGE="${IMAGE:-aztecprotocol/aztec:unhinged-unicorn}" + else + # unknown network + echo -e "${RED}Unknown network: $NETWORK${NC}" + fi + + # Check that bootnode, ethereum host, and image are set + if [ -z "$BOOTNODE_URL" ] || [ -z "$ETHEREUM_HOSTS" ] || [ -z "$IMAGE" ]; then + echo -e "${RED}Bootnode, Ethereum host, and image are required${NC}" + exit 1 + fi + + # get the node info + get_node_info + + if [ -n "$CLI_P2P_PORT" ]; then + P2P_PORT="$CLI_P2P_PORT" + else + read -p "P2P Port [$DEFAULT_P2P_PORT]: " P2P_PORT + P2P_PORT=${P2P_PORT:-$DEFAULT_P2P_PORT} + fi + + if [ -n "$CLI_PORT" ]; then + PORT="$CLI_PORT" + else + read -p "Node Port [$DEFAULT_PORT]: " PORT + PORT=${PORT:-$DEFAULT_PORT} + fi + + if [ -n "$CLI_KEY" ]; then + KEY="$CLI_KEY" + else + while true; do + read -p "Validator Private Key: " KEY + if [ -z "$KEY" ]; then + echo -e "${RED}Error: Validator Private Key is required${NC}" + else + break + fi + done + fi - if [ -n "$CLI_COINBASE" ]; then + if [ -n "$CLI_COINBASE" ]; then COINBASE="$CLI_COINBASE" - else - while true; do - read -p "Validator Address (Coinbase): " COINBASE - - if [ -z "$COINBASE" ]; then - echo -e "${RED}Error: Validator Address (Coinbase) is required${NC}" - else - if [[ "$COINBASE" =~ ^0x[a-fA-F0-9]{40}$ ]]; then - break - else - echo -e "${RED}Error: Invalid COINBASE address. Please enter a valid Ethereum address.${NC}" - fi - fi - - done - fi - - if [ -n "$CLI_IP" ]; then - IP="$CLI_IP" - else - if [ -z "$DEFAULT_IP" ]; then - while true; do - read -p "Public IP: " IP - if [ -z "$IP" ]; then - echo -e "${RED}Error: Public IP is required${NC}" - else - break - fi - done + else + while true; do + read -p "Validator Address (Coinbase): " COINBASE + + if [ -z "$COINBASE" ]; then + echo -e "${RED}Error: Validator Address (Coinbase) is required${NC}" + else + if [[ "$COINBASE" =~ ^0x[a-fA-F0-9]{40}$ ]]; then + break else - read -p "Public IP [$DEFAULT_IP]: " IP - IP=${IP:-$DEFAULT_IP} + echo -e "${RED}Error: Invalid COINBASE address. Please enter a valid Ethereum address.${NC}" fi - fi + fi - if [ -n "$BIND_MOUNT_DIR" ]; then - BIND_MOUNT_DIR="$BIND_MOUNT_DIR" - else - read -p "Use docker volume for data directory? [Y/n] " -n 1 -r - echo - if [[ $REPLY =~ ^[Nn]$ ]]; then - read -p "Relative path for data directory [${DEFAULT_BIND_MOUNT_DIR}]: " BIND_MOUNT_DIR - BIND_MOUNT_DIR=${BIND_MOUNT_DIR:-$DEFAULT_BIND_MOUNT_DIR} + done + fi + + if [ -n "$CLI_IP" ]; then + IP="$CLI_IP" + else + if [ -z "$DEFAULT_IP" ]; then + while true; do + read -p "Public IP: " IP + if [ -z "$IP" ]; then + echo -e "${RED}Error: Public IP is required${NC}" + else + break fi + done + else + read -p "Public IP [$DEFAULT_IP]: " IP + IP=${IP:-$DEFAULT_IP} fi + fi + + if [ -n "$BIND_MOUNT_DIR" ]; then + BIND_MOUNT_DIR="$BIND_MOUNT_DIR" + else + read -p "Use docker volume for data directory? [Y/n] " -n 1 -r + echo + if [[ $REPLY =~ ^[Nn]$ ]]; then + read -p "Relative path for data directory [${DEFAULT_BIND_MOUNT_DIR}]: " BIND_MOUNT_DIR + BIND_MOUNT_DIR=${BIND_MOUNT_DIR:-$DEFAULT_BIND_MOUNT_DIR} + fi + fi - - # Generate .env file - cat > .env << EOF + # Generate .env file + cat >.env < docker-compose.yml << EOF + # Generate docker-compose.yml + cat >docker-compose.yml <> docker-compose.yml << EOF + # Add volume configuration based on user choice + if [ -n "$BIND_MOUNT_DIR" ]; then + cat >>docker-compose.yml <> docker-compose.yml << EOF + else + cat >>docker-compose.yml < "logs/kind-$namespace.log" &>/dev/null & + stern spartan -n "$namespace" >"logs/kind-$namespace.log" &>/dev/null & stern_pid=$! } diff --git a/spartan/scripts/test_spartan.sh b/spartan/scripts/test_spartan.sh index 7154aeb01def..03588ac86865 100755 --- a/spartan/scripts/test_spartan.sh +++ b/spartan/scripts/test_spartan.sh @@ -26,11 +26,11 @@ function get_load_balancer_url() { # Fetch the service URLs based on the namespace for injection in the test-transfer.sh export BOOTNODE_URL=http://$(get_load_balancer_url $NAMESPACE "$NAMESPACE-aztec-network-boot-node-lb-tcp"):8080 export PXE_URL=http://$(get_load_balancer_url $NAMESPACE "$NAMESPACE-aztec-network-pxe-lb"):8080 -export ETHEREUM_HOST=http://$(get_load_balancer_url $NAMESPACE "$NAMESPACE-aztec-network-ethereum-lb"):8545 +export ETHEREUM_HOSTS=http://$(get_load_balancer_url $NAMESPACE "$NAMESPACE-aztec-network-ethereum-lb"):8545 echo "BOOTNODE_URL: $BOOTNODE_URL" echo "PXE_URL: $PXE_URL" -echo "ETHEREUM_HOST: $ETHEREUM_HOST" +echo "ETHEREUM_HOSTS: $ETHEREUM_HOSTS" # hack to ensure L2 contracts are considered deployed touch $SCRIPT_DIR/../../yarn-project/end-to-end/scripts/native-network/state/l2-contracts.env diff --git a/spartan/terraform/deploy-release/main.tf b/spartan/terraform/deploy-release/main.tf index 9e3362f57bde..77a8a0b6e312 100644 --- a/spartan/terraform/deploy-release/main.tf +++ b/spartan/terraform/deploy-release/main.tf @@ -81,10 +81,10 @@ resource "helm_release" "aztec-gke-cluster" { } dynamic "set" { - for_each = var.EXTERNAL_ETHEREUM_HOST != "" ? toset(["iterate"]) : toset([]) + for_each = var.EXTERNAL_ETHEREUM_HOSTS != "" ? toset(["iterate"]) : toset([]) content { - name = "ethereum.execution.externalHost" - value = var.EXTERNAL_ETHEREUM_HOST + name = "ethereum.execution.externalHosts" + value = var.EXTERNAL_ETHEREUM_HOSTS } } diff --git a/spartan/terraform/deploy-release/variables.tf b/spartan/terraform/deploy-release/variables.tf index 616835e478b5..e6854b4a2f0e 100644 --- a/spartan/terraform/deploy-release/variables.tf +++ b/spartan/terraform/deploy-release/variables.tf @@ -33,7 +33,7 @@ variable "L1_DEPLOYMENT_PRIVATE_KEY" { default = "" } -variable "EXTERNAL_ETHEREUM_HOST" { +variable "EXTERNAL_ETHEREUM_HOSTS" { description = "External host to use for the ethereum node" type = string default = "" diff --git a/spartan/terraform/multicloud-deploy/main.tf b/spartan/terraform/multicloud-deploy/main.tf index 340a20a00941..56d4909f1c54 100644 --- a/spartan/terraform/multicloud-deploy/main.tf +++ b/spartan/terraform/multicloud-deploy/main.tf @@ -172,7 +172,7 @@ resource "helm_release" "aztec-gke-cluster" { # pointing Google Cloud provers to nodes in AWS set { - name = "ethereum.execution.externalHost" + name = "ethereum.execution.externalHosts" value = data.kubernetes_service.lb_ethereum_tcp.status.0.load_balancer.0.ingress.0.hostname } diff --git a/spartan/testnet-runbook.md b/spartan/testnet-runbook.md index f9b7dbc45582..ab9314c570ad 100644 --- a/spartan/testnet-runbook.md +++ b/spartan/testnet-runbook.md @@ -53,11 +53,13 @@ Deployments are initiated from CI by manually running the (_name pending_) workf After public testnet deployment, perform these sanity checks (these items can also be script automated): 1. Monitor for crashes and network-level health: + - Review the testnet dashboard at `https://grafana.aztec.network/` to confirm node uptime and block production - Verify overall TPS performance - Create Github issues for new crash scenarios 2. Spot check pod logs for component health: + - Tx gossiping (Bot: `Generated IVC proof`) - Peer discovery (Validator (failure case): `Failed FINDNODE request`) - Block proposal (Validator: `Can propose block`) @@ -72,7 +74,7 @@ After public testnet deployment, perform these sanity checks (these items can al After a successful sanity check, share the following network connection information in the `#team-alpha` slack channel: 1. AZTEC_IMAGE (`aztecprotocol/aztec:latest`) -2. ETHEREUM_HOST (Kubernetes: `kubectl get services -n | (head -1; grep ethereum)`) +2. ETHEREUM_HOSTS (Kubernetes: `kubectl get services -n | (head -1; grep ethereum)`) - ethereum-lb: `:8545` 3. BOOT_NODE_URL (Kubernetes: `kubectl get services -n | (head -3; grep boot)`) - boot-node-lb-tcp: `:40400` @@ -88,16 +90,16 @@ The following items are a shortlist of support items that may be required either ### Issue Resolution Matrix -| Event | Action | Criticality | Owner(s) | -|-------|---------|------------|-----------| -| Build failure | Rerun CI or revert problematic changes | Blocker | | -| Deployment issues | Reference deployment `README` or escalate to Delta Team | Blocker | Delta Team | -| Network instability* | Create detailed issue report for Alpha team | Blocker | Alpha Team | -| Challenge completion errors | Document issue and assess challenge viability | Major | Product Team | -| Minor operational issues | Create tracking issue | Minor | Delta Team | -| Hotfix deployment | Update public testnet and verify fix | Major | Delta Team | +| Event | Action | Criticality | Owner(s) | +| --------------------------- | ------------------------------------------------------- | ----------- | ------------ | +| Build failure | Rerun CI or revert problematic changes | Blocker | | +| Deployment issues | Reference deployment `README` or escalate to Delta Team | Blocker | Delta Team | +| Network instability\* | Create detailed issue report for Alpha team | Blocker | Alpha Team | +| Challenge completion errors | Document issue and assess challenge viability | Major | Product Team | +| Minor operational issues | Create tracking issue | Minor | Delta Team | +| Hotfix deployment | Update public testnet and verify fix | Major | Delta Team | -_*Defining Network Instability:_ +_\*Defining Network Instability:_ A public testnet is considered unstable if experiencing any of the following: @@ -111,9 +113,9 @@ A public testnet is considered unstable if experiencing any of the following: ### Release Support Matrix -| Event | Action | Criticality | Owner(s) | -|-------|---------|------------|-----------| -| Challenge completion issues | Provide guidance or create issue | Minor | DevRel Team | -| Node stability issues | Collect logs and create issue | Major | Delta Team | -| Network-wide problems | Escalate to Delta team | Critical | Alpha/Delta Teams | -| Bridge/Contract issues | Investigate and escalate if needed | Critical | Alpha Team | +| Event | Action | Criticality | Owner(s) | +| --------------------------- | ---------------------------------- | ----------- | ----------------- | +| Challenge completion issues | Provide guidance or create issue | Minor | DevRel Team | +| Node stability issues | Collect logs and create issue | Major | Delta Team | +| Network-wide problems | Escalate to Delta team | Critical | Alpha/Delta Teams | +| Bridge/Contract issues | Investigate and escalate if needed | Critical | Alpha Team | diff --git a/yarn-project/archiver/README.md b/yarn-project/archiver/README.md index 86f67911346f..aec8d95bc0d7 100644 --- a/yarn-project/archiver/README.md +++ b/yarn-project/archiver/README.md @@ -13,4 +13,4 @@ The interfaces defining how the data can be consumed from the archiver are `L2Bl To install dependencies and build the package run `yarn install` followed by `yarn build`. To run test execute `yarn test`. -To start the service export `ETHEREUM_HOST` (defaults to `http://127.0.0.1:8545/`), `ARCHIVER_POLLING_INTERVAL_MS` (defaults to `1000 ms`), `ROLLUP_CONTRACT_ADDRESS`, `INBOX_CONTRACT_ADDRESS` environmental variables and start the service with `yarn start`. +To start the service export `ETHEREUM_HOSTS` (defaults to `http://127.0.0.1:8545/`), `ARCHIVER_POLLING_INTERVAL_MS` (defaults to `1000 ms`), `ROLLUP_CONTRACT_ADDRESS`, `INBOX_CONTRACT_ADDRESS` environmental variables and start the service with `yarn start`. diff --git a/yarn-project/archiver/src/archiver/archiver.test.ts b/yarn-project/archiver/src/archiver/archiver.test.ts index 80f069ea7ad0..3794d0a727c2 100644 --- a/yarn-project/archiver/src/archiver/archiver.test.ts +++ b/yarn-project/archiver/src/archiver/archiver.test.ts @@ -1,7 +1,7 @@ import { Blob } from '@aztec/blob-lib'; import type { BlobSinkClientInterface } from '@aztec/blob-sink/client'; import { GENESIS_ARCHIVE_ROOT } from '@aztec/constants'; -import { DefaultL1ContractsConfig } from '@aztec/ethereum'; +import { DefaultL1ContractsConfig, type ViemPublicClient } from '@aztec/ethereum'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { type Logger, createLogger } from '@aztec/foundation/log'; @@ -15,15 +15,7 @@ import { getTelemetryClient } from '@aztec/telemetry-client'; import { jest } from '@jest/globals'; import { type MockProxy, mock } from 'jest-mock-extended'; -import { - type Chain, - type HttpTransport, - type Log, - type PublicClient, - type Transaction, - encodeFunctionData, - toHex, -} from 'viem'; +import { type Log, type Transaction, encodeFunctionData, toHex } from 'viem'; import { Archiver } from './archiver.js'; import type { ArchiverDataStore } from './archiver_store.js'; @@ -69,7 +61,7 @@ describe('Archiver', () => { .map((_, i) => getNumPrivateLogsForTx(i, blockNumber)) .reduce((accum, num) => accum + num, 0); - let publicClient: MockProxy>; + let publicClient: MockProxy; let instrumentation: MockProxy; let blobSinkClient: MockProxy; let archiverStore: ArchiverDataStore; @@ -102,7 +94,7 @@ describe('Archiver', () => { beforeEach(async () => { logger = createLogger('archiver:test'); now = +new Date(); - publicClient = mock>({ + publicClient = mock({ // Return a block with a reasonable timestamp getBlock: ((args: any) => ({ timestamp: args.blockNumber * BigInt(DefaultL1ContractsConfig.ethereumSlotDuration) + BigInt(now), diff --git a/yarn-project/archiver/src/archiver/archiver.ts b/yarn-project/archiver/src/archiver/archiver.ts index 3cb22057affb..f3e5c3c681b4 100644 --- a/yarn-project/archiver/src/archiver/archiver.ts +++ b/yarn-project/archiver/src/archiver/archiver.ts @@ -1,5 +1,5 @@ import type { BlobSinkClientInterface } from '@aztec/blob-sink/client'; -import { createEthereumChain } from '@aztec/ethereum'; +import { type ViemPublicClient, createEthereumChain } from '@aztec/ethereum'; import type { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { type Logger, createLogger } from '@aztec/foundation/log'; @@ -45,15 +45,7 @@ import { type BlockHeader, TxEffect, TxHash, TxReceipt } from '@aztec/stdlib/tx' import { Attributes, type TelemetryClient, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client'; import groupBy from 'lodash.groupby'; -import { - type Chain, - type GetContractReturnType, - type HttpTransport, - type PublicClient, - createPublicClient, - getContract, - http, -} from 'viem'; +import { type GetContractReturnType, createPublicClient, fallback, getContract, http } from 'viem'; import type { ArchiverDataStore, ArchiverL1SynchPoint } from './archiver_store.js'; import type { ArchiverConfig } from './config.js'; @@ -83,8 +75,8 @@ export class Archiver implements ArchiveSource, Traceable { */ private runningPromise?: RunningPromise; - private rollup: GetContractReturnType>; - private inbox: GetContractReturnType>; + private rollup: GetContractReturnType; + private inbox: GetContractReturnType; private store: ArchiverStoreHelper; @@ -104,7 +96,7 @@ export class Archiver implements ArchiveSource, Traceable { * @param log - A logger. */ constructor( - private readonly publicClient: PublicClient, + private readonly publicClient: ViemPublicClient, private readonly l1Addresses: { rollupAddress: EthAddress; inboxAddress: EthAddress; registryAddress: EthAddress }, readonly dataStore: ArchiverDataStore, private readonly config: { pollingIntervalMs: number; batchSize: number }, @@ -142,10 +134,10 @@ export class Archiver implements ArchiveSource, Traceable { deps: { telemetry: TelemetryClient; blobSinkClient: BlobSinkClientInterface }, blockUntilSynced = true, ): Promise { - const chain = createEthereumChain(config.l1RpcUrl, config.l1ChainId); + const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId); const publicClient = createPublicClient({ chain: chain.chainInfo, - transport: http(chain.rpcUrl), + transport: fallback(config.l1RpcUrls.map(url => http(url))), pollingInterval: config.viemPollingIntervalMS, }); diff --git a/yarn-project/archiver/src/archiver/data_retrieval.ts b/yarn-project/archiver/src/archiver/data_retrieval.ts index 52c4bb243467..ef01234607b4 100644 --- a/yarn-project/archiver/src/archiver/data_retrieval.ts +++ b/yarn-project/archiver/src/archiver/data_retrieval.ts @@ -1,5 +1,6 @@ import { Blob, BlobDeserializationError } from '@aztec/blob-lib'; import type { BlobSinkClientInterface } from '@aztec/blob-sink/client'; +import type { ViemPublicClient } from '@aztec/ethereum'; import { asyncPool } from '@aztec/foundation/async-pool'; import type { EthAddress } from '@aztec/foundation/eth-address'; import type { ViemSignature } from '@aztec/foundation/eth-signature'; @@ -14,12 +15,9 @@ import { AppendOnlyTreeSnapshot } from '@aztec/stdlib/trees'; import { BlockHeader } from '@aztec/stdlib/tx'; import { - type Chain, type GetContractEventsReturnType, type GetContractReturnType, type Hex, - type HttpTransport, - type PublicClient, decodeFunctionData, getAbiItem, hexToBytes, @@ -39,8 +37,8 @@ import type { L1Published, L1PublishedData } from './structs/published.js'; * @returns An array of block; as well as the next eth block to search from. */ export async function retrieveBlocksFromRollup( - rollup: GetContractReturnType>, - publicClient: PublicClient, + rollup: GetContractReturnType, + publicClient: ViemPublicClient, blobSinkClient: BlobSinkClientInterface, searchStartBlock: bigint, searchEndBlock: bigint, @@ -93,8 +91,8 @@ export async function retrieveBlocksFromRollup( * @returns - An array blocks. */ export async function processL2BlockProposedLogs( - rollup: GetContractReturnType>, - publicClient: PublicClient, + rollup: GetContractReturnType, + publicClient: ViemPublicClient, blobSinkClient: BlobSinkClientInterface, logs: GetContractEventsReturnType, logger: Logger, @@ -136,7 +134,7 @@ export async function processL2BlockProposedLogs( return retrievedBlocks; } -export async function getL1BlockTime(publicClient: PublicClient, blockNumber: bigint): Promise { +export async function getL1BlockTime(publicClient: ViemPublicClient, blockNumber: bigint): Promise { const block = await publicClient.getBlock({ blockNumber, includeTransactions: false }); return block.timestamp; } @@ -202,7 +200,7 @@ function extractRollupProposeCalldata(forwarderData: Hex, rollupAddress: Hex): H * @returns L2 block from the calldata, deserialized */ async function getBlockFromRollupTx( - publicClient: PublicClient, + publicClient: ViemPublicClient, blobSinkClient: BlobSinkClientInterface, txHash: `0x${string}`, blobHashes: Buffer[], // WORKTODO(md): buffer32? @@ -311,7 +309,7 @@ async function getBlockFromRollupTx( * @returns An array of InboxLeaf and next eth block to search from. */ export async function retrieveL1ToL2Messages( - inbox: GetContractReturnType>, + inbox: GetContractReturnType, searchStartBlock: bigint, searchEndBlock: bigint, ): Promise> { @@ -348,7 +346,7 @@ export async function retrieveL1ToL2Messages( /** Retrieves L2ProofVerified events from the rollup contract. */ export async function retrieveL2ProofVerifiedEvents( - publicClient: PublicClient, + publicClient: ViemPublicClient, rollupAddress: EthAddress, searchStartBlock: bigint, searchEndBlock?: bigint, @@ -371,7 +369,7 @@ export async function retrieveL2ProofVerifiedEvents( /** Retrieve submitted proofs from the rollup contract */ export async function retrieveL2ProofsFromRollup( - publicClient: PublicClient, + publicClient: ViemPublicClient, rollupAddress: EthAddress, searchStartBlock: bigint, searchEndBlock?: bigint, @@ -407,7 +405,7 @@ export type SubmitBlockProof = { * @returns L2 block metadata (header and archive) from the calldata, deserialized */ export async function getProofFromSubmitProofTx( - publicClient: PublicClient, + publicClient: ViemPublicClient, txHash: `0x${string}`, expectedProverId: Fr, ): Promise { diff --git a/yarn-project/aztec-faucet/src/faucet.ts b/yarn-project/aztec-faucet/src/faucet.ts index d762f739ec1b..f4c9eaa8843a 100644 --- a/yarn-project/aztec-faucet/src/faucet.ts +++ b/yarn-project/aztec-faucet/src/faucet.ts @@ -1,4 +1,4 @@ -import { createEthereumChain } from '@aztec/ethereum'; +import { type ViemPublicClient, type ViemWalletClient, createEthereumChain } from '@aztec/ethereum'; import type { EthAddress } from '@aztec/foundation/eth-address'; import { createLogger } from '@aztec/foundation/log'; import { TestERC20Abi } from '@aztec/l1-artifacts'; @@ -9,13 +9,13 @@ import { type GetContractReturnType, type HttpTransport, type LocalAccount, - type PublicClient, - http as ViemHttp, type WalletClient, createPublicClient, createWalletClient, + fallback, getContract, parseEther, + http as viemHttp, } from 'viem'; import { mnemonicToAccount } from 'viem/accounts'; @@ -27,8 +27,8 @@ type L1Asset = { }; export class Faucet { - private walletClient: WalletClient; - private publicClient: PublicClient; + private walletClient: ViemWalletClient; + private publicClient: ViemPublicClient; private dripHistory = new Map>(); private l1Assets = new Map(); @@ -39,17 +39,17 @@ export class Faucet { private timeFn: () => number = Date.now, private log = createLogger('aztec:faucet'), ) { - const chain = createEthereumChain(config.l1RpcUrl, config.l1ChainId); + const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId); this.walletClient = createWalletClient({ account: this.account, chain: chain.chainInfo, - transport: ViemHttp(chain.rpcUrl), + transport: fallback(chain.rpcUrls.map(url => viemHttp(url))), }); this.publicClient = createPublicClient({ chain: chain.chainInfo, - transport: ViemHttp(chain.rpcUrl), + transport: fallback(chain.rpcUrls.map(url => viemHttp(url))), }); } diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 571b70da45ed..f2a9e02b9a53 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -147,7 +147,7 @@ export class AztecNodeService implements AztecNode, Traceable { const log = deps.logger ?? createLogger('node'); const dateProvider = deps.dateProvider ?? new DateProvider(); const blobSinkClient = deps.blobSinkClient ?? createBlobSinkClient(config); - const ethereumChain = createEthereumChain(config.l1RpcUrl, config.l1ChainId); + const ethereumChain = createEthereumChain(config.l1RpcUrls, config.l1ChainId); //validate that the actual chain id matches that specified in configuration if (config.l1ChainId !== ethereumChain.chainInfo.id) { throw new Error( diff --git a/yarn-project/aztec.js/src/api/cheat_codes.ts b/yarn-project/aztec.js/src/api/cheat_codes.ts index 0354c077032e..eecb51dfba42 100644 --- a/yarn-project/aztec.js/src/api/cheat_codes.ts +++ b/yarn-project/aztec.js/src/api/cheat_codes.ts @@ -18,8 +18,8 @@ export class CheatCodes { public rollup: RollupCheatCodes, ) {} - static async create(rpcUrl: string, pxe: PXE): Promise { - const ethCheatCodes = new EthCheatCodes(rpcUrl); + static async create(rpcUrls: string[], pxe: PXE): Promise { + const ethCheatCodes = new EthCheatCodes(rpcUrls); const aztecCheatCodes = new AztecCheatCodes(pxe); const rollupCheatCodes = new RollupCheatCodes( ethCheatCodes, @@ -28,8 +28,8 @@ export class CheatCodes { return new CheatCodes(ethCheatCodes, aztecCheatCodes, rollupCheatCodes); } - static createRollup(rpcUrl: string, addresses: Pick): RollupCheatCodes { - const ethCheatCodes = new EthCheatCodes(rpcUrl); + static createRollup(rpcUrls: string[], addresses: Pick): RollupCheatCodes { + const ethCheatCodes = new EthCheatCodes(rpcUrls); return new RollupCheatCodes(ethCheatCodes, addresses); } } diff --git a/yarn-project/aztec.js/src/api/ethereum/anvil_test_watcher.ts b/yarn-project/aztec.js/src/api/ethereum/anvil_test_watcher.ts index 76bbf4a3ca98..5b06b45c1cff 100644 --- a/yarn-project/aztec.js/src/api/ethereum/anvil_test_watcher.ts +++ b/yarn-project/aztec.js/src/api/ethereum/anvil_test_watcher.ts @@ -1,3 +1,4 @@ +import type { ViemPublicClient } from '@aztec/ethereum'; import type { EthCheatCodes } from '@aztec/ethereum/eth-cheatcodes'; import type { EthAddress } from '@aztec/foundation/eth-address'; import { type Logger, createLogger } from '@aztec/foundation/log'; @@ -5,8 +6,7 @@ import { RunningPromise } from '@aztec/foundation/running-promise'; import type { TestDateProvider } from '@aztec/foundation/timer'; import { RollupAbi } from '@aztec/l1-artifacts'; -import { type GetContractReturnType, type HttpTransport, type PublicClient, getAddress, getContract } from 'viem'; -import type * as chains from 'viem/chains'; +import { type GetContractReturnType, getAddress, getContract } from 'viem'; import { RollupCheatCodes } from './cheat_codes.js'; @@ -20,7 +20,7 @@ import { RollupCheatCodes } from './cheat_codes.js'; export class AnvilTestWatcher { private isSandbox: boolean = false; - private rollup: GetContractReturnType>; + private rollup: GetContractReturnType; private rollupCheatCodes: RollupCheatCodes; private filledRunningPromise?: RunningPromise; @@ -34,7 +34,7 @@ export class AnvilTestWatcher { constructor( private cheatcodes: EthCheatCodes, rollupAddress: EthAddress, - publicClient: PublicClient, + publicClient: ViemPublicClient, private dateProvider?: TestDateProvider, ) { this.rollup = getContract({ diff --git a/yarn-project/aztec.js/src/api/ethereum/cheat_codes.ts b/yarn-project/aztec.js/src/api/ethereum/cheat_codes.ts index 1bb16e11ff01..fec9b4729fee 100644 --- a/yarn-project/aztec.js/src/api/ethereum/cheat_codes.ts +++ b/yarn-project/aztec.js/src/api/ethereum/cheat_codes.ts @@ -1,33 +1,27 @@ +import type { ViemPublicClient } from '@aztec/ethereum'; import { EthCheatCodes } from '@aztec/ethereum/eth-cheatcodes'; import type { L1ContractAddresses } from '@aztec/ethereum/l1-contract-addresses'; import { EthAddress } from '@aztec/foundation/eth-address'; import { createLogger } from '@aztec/foundation/log'; import { RollupAbi } from '@aztec/l1-artifacts'; -import { - type GetContractReturnType, - type Hex, - type PublicClient, - type WalletClient, - createWalletClient, - getContract, - http, - keccak256, - publicActions, -} from 'viem'; +import { type GetContractReturnType, type Hex, createPublicClient, fallback, getContract, http, keccak256 } from 'viem'; import { foundry } from 'viem/chains'; export { EthCheatCodes }; /** Cheat codes for the L1 rollup contract. */ export class RollupCheatCodes { - private client: WalletClient & PublicClient; - private rollup: GetContractReturnType; + private client: ViemPublicClient; + private rollup: GetContractReturnType; private logger = createLogger('aztecjs:cheat_codes'); constructor(private ethCheatCodes: EthCheatCodes, addresses: Pick) { - this.client = createWalletClient({ chain: foundry, transport: http(ethCheatCodes.rpcUrl) }).extend(publicActions); + this.client = createPublicClient({ + chain: foundry, + transport: fallback(ethCheatCodes.rpcUrls.map(url => http(url))), + }); this.rollup = getContract({ abi: RollupAbi, address: addresses.rollupAddress.toString(), @@ -149,7 +143,7 @@ export class RollupCheatCodes { * @param action - The action to execute */ public async asOwner( - action: (owner: Hex, rollup: GetContractReturnType) => Promise, + action: (owner: Hex, rollup: GetContractReturnType) => Promise, ) { const owner = await this.rollup.read.owner(); await this.ethCheatCodes.startImpersonating(owner); diff --git a/yarn-project/aztec.js/src/api/ethereum/index.ts b/yarn-project/aztec.js/src/api/ethereum/index.ts index 34b7d88fa0f8..f99e90e89197 100644 --- a/yarn-project/aztec.js/src/api/ethereum/index.ts +++ b/yarn-project/aztec.js/src/api/ethereum/index.ts @@ -12,4 +12,8 @@ export { getL1ContractAddresses } from './l1_contracts.js'; export { RollupCheatCodes, EthCheatCodes } from './cheat_codes.js'; export { ChainMonitor } from './chain_monitor.js'; export { AnvilTestWatcher } from './anvil_test_watcher.js'; -export { deployL1Contract, deployL1Contracts, type DeployL1Contracts } from '@aztec/ethereum/deploy-l1-contracts'; +export { + deployL1Contract, + deployL1Contracts, + type DeployL1ContractsReturnType, +} from '@aztec/ethereum/deploy-l1-contracts'; diff --git a/yarn-project/aztec.js/src/api/ethereum/portal_manager.ts b/yarn-project/aztec.js/src/api/ethereum/portal_manager.ts index 1f516525f28c..368c7411165e 100644 --- a/yarn-project/aztec.js/src/api/ethereum/portal_manager.ts +++ b/yarn-project/aztec.js/src/api/ethereum/portal_manager.ts @@ -1,3 +1,4 @@ +import type { ViemPublicClient, ViemWalletClient } from '@aztec/ethereum'; import { extractEvent } from '@aztec/ethereum/utils'; import { sha256ToField } from '@aztec/foundation/crypto'; import { EthAddress } from '@aztec/foundation/eth-address'; @@ -9,17 +10,7 @@ import type { AztecAddress } from '@aztec/stdlib/aztec-address'; import { computeSecretHash } from '@aztec/stdlib/hash'; import type { PXE } from '@aztec/stdlib/interfaces/client'; -import { - type Account, - type Chain, - type GetContractReturnType, - type Hex, - type HttpTransport, - type PublicClient, - type WalletClient, - getContract, - toFunctionSelector, -} from 'viem'; +import { type GetContractReturnType, type Hex, getContract, toFunctionSelector } from 'viem'; // docs:start:claim_type // docs:start:claim_type_amount @@ -60,13 +51,13 @@ export async function generateClaimSecret(logger?: Logger): Promise<[Fr, Fr]> { /** Helper for managing an ERC20 on L1. */ export class L1TokenManager { - private contract: GetContractReturnType>; + private contract: GetContractReturnType; public constructor( /** Address of the ERC20 contract. */ public readonly address: EthAddress, - private publicClient: PublicClient, - private walletClient: WalletClient, + private publicClient: ViemPublicClient, + private walletClient: ViemWalletClient, private logger: Logger, ) { this.contract = getContract({ @@ -114,16 +105,13 @@ export class L1TokenManager { /** Helper for interacting with the FeeJuicePortal on L1. */ export class L1FeeJuicePortalManager { private readonly tokenManager: L1TokenManager; - private readonly contract: GetContractReturnType< - typeof FeeJuicePortalAbi, - WalletClient - >; + private readonly contract: GetContractReturnType; constructor( portalAddress: EthAddress, tokenAddress: EthAddress, - private readonly publicClient: PublicClient, - private readonly walletClient: WalletClient, + private readonly publicClient: ViemPublicClient, + private readonly walletClient: ViemWalletClient, private readonly logger: Logger, ) { this.tokenManager = new L1TokenManager(tokenAddress, publicClient, walletClient, logger); @@ -194,8 +182,8 @@ export class L1FeeJuicePortalManager { */ public static async new( pxe: PXE, - publicClient: PublicClient, - walletClient: WalletClient, + publicClient: ViemPublicClient, + walletClient: ViemWalletClient, logger: Logger, ): Promise { const { @@ -212,14 +200,14 @@ export class L1FeeJuicePortalManager { /** Helper for interacting with a test TokenPortal on L1 for sending tokens to L2. */ export class L1ToL2TokenPortalManager { - protected readonly portal: GetContractReturnType>; + protected readonly portal: GetContractReturnType; protected readonly tokenManager: L1TokenManager; constructor( portalAddress: EthAddress, tokenAddress: EthAddress, - protected publicClient: PublicClient, - protected walletClient: WalletClient, + protected publicClient: ViemPublicClient, + protected walletClient: ViemWalletClient, protected logger: Logger, ) { this.tokenManager = new L1TokenManager(tokenAddress, publicClient, walletClient, logger); @@ -330,14 +318,14 @@ export class L1ToL2TokenPortalManager { /** Helper for interacting with a test TokenPortal on L1 for both withdrawing from and bridging to L2. */ export class L1TokenPortalManager extends L1ToL2TokenPortalManager { - private readonly outbox: GetContractReturnType>; + private readonly outbox: GetContractReturnType; constructor( portalAddress: EthAddress, tokenAddress: EthAddress, outboxAddress: EthAddress, - publicClient: PublicClient, - walletClient: WalletClient, + publicClient: ViemPublicClient, + walletClient: ViemWalletClient, logger: Logger, ) { super(portalAddress, tokenAddress, publicClient, walletClient, logger); diff --git a/yarn-project/aztec/README.md b/yarn-project/aztec/README.md index 2c01123c7b4c..eb6ab3968ba7 100644 --- a/yarn-project/aztec/README.md +++ b/yarn-project/aztec/README.md @@ -19,7 +19,7 @@ You can also run it as a standalone node server with: yarn start ``` -It will look for a local Ethereum RPC to talk to but you can change this with the `ETHEREUM_HOST` environment variable. +It will look for a local Ethereum RPC to talk to but you can change this with the `ETHEREUM_HOSTS` environment variable. ## Examples diff --git a/yarn-project/aztec/docker-compose.yml b/yarn-project/aztec/docker-compose.yml index 086ad2a8d4e4..f0745a1e1533 100644 --- a/yarn-project/aztec/docker-compose.yml +++ b/yarn-project/aztec/docker-compose.yml @@ -23,7 +23,7 @@ services: environment: LOG_LEVEL: # Loaded from the user shell if explicitly set HOST_WORKDIR: '${PWD}' # Loaded from the user shell to show log files absolute path in host - ETHEREUM_HOST: http://ethereum:8545 + ETHEREUM_HOSTS: http://ethereum:8545 L1_CHAIN_ID: 31337 FORCE_COLOR: ${FORCE_COLOR:-1} ARCHIVER_POLLING_INTERVAL_MS: 50 diff --git a/yarn-project/aztec/package.json b/yarn-project/aztec/package.json index 3a6ae2fcb332..b079e7cb09fc 100644 --- a/yarn-project/aztec/package.json +++ b/yarn-project/aztec/package.json @@ -17,7 +17,7 @@ "build": "yarn clean && tsc -b", "start": "node --no-warnings ./dest/bin", "start:debug": "node --inspect=0.0.0.0:9221 --no-warnings ./dest/bin", - "start:sandbox": "ETHEREUM_HOST=http://0.0.0.0:8545/ && yarn start start --sandbox", + "start:sandbox": "ETHEREUM_HOSTS=http://0.0.0.0:8545/ && yarn start start --sandbox", "clean": "rm -rf ./dest .tsbuildinfo", "formatting": "run -T prettier --check ./src && run -T eslint ./src", "formatting:fix": "run -T eslint --fix ./src && run -T prettier -w ./src", diff --git a/yarn-project/aztec/src/cli/aztec_start_action.ts b/yarn-project/aztec/src/cli/aztec_start_action.ts index a09c70aaab68..cd185fad0bad 100644 --- a/yarn-project/aztec/src/cli/aztec_start_action.ts +++ b/yarn-project/aztec/src/cli/aztec_start_action.ts @@ -36,7 +36,7 @@ export async function aztecStart(options: any, userLog: LogFn, debugLogger: Logg const { node, pxe, stop } = await createSandbox( { l1Mnemonic: options.l1Mnemonic, - l1RpcUrl: options.l1RpcUrl, + l1RpcUrls: options.l1RpcUrls, l1Salt: nodeOptions.deployAztecContractsSalt, noPXE: sandboxOptions.noPXE, testAccounts: sandboxOptions.testAccounts, diff --git a/yarn-project/aztec/src/cli/aztec_start_options.ts b/yarn-project/aztec/src/cli/aztec_start_options.ts index 03378c9cc45f..a04a0d2d5402 100644 --- a/yarn-project/aztec/src/cli/aztec_start_options.ts +++ b/yarn-project/aztec/src/cli/aztec_start_options.ts @@ -54,7 +54,7 @@ export const getOptions = (namespace: string, configMappings: Record', - description: 'URL of the Ethereum RPC node that services will connect to', - defaultValue: 'http://localhost:8545', - envVar: 'ETHEREUM_HOST', + flag: '--l1-rpc-urls ', + description: 'List of URLs of the Ethereum RPC nodes that services will connect to (comma separated)', + defaultValue: ['http://localhost:8545'], + envVar: 'ETHEREUM_HOSTS', + parseVal: (val: string) => val.split(',').map(url => url.trim()), }, { flag: '--l1-chain-id ', diff --git a/yarn-project/aztec/src/cli/validation.test.ts b/yarn-project/aztec/src/cli/validation.test.ts index fd30a6b90401..5b5a29fcfcd7 100644 --- a/yarn-project/aztec/src/cli/validation.test.ts +++ b/yarn-project/aztec/src/cli/validation.test.ts @@ -20,7 +20,7 @@ describe('validation', () => { beforeAll(async () => { ({ anvil, rpcUrl: l1RpcUrl } = await startAnvil()); - nodeConfig = { ...getDefaultConfig(aztecNodeConfigMappings), l1RpcUrl }; + nodeConfig = { ...getDefaultConfig(aztecNodeConfigMappings), l1RpcUrls: [l1RpcUrl] }; nodeConfig.aztecSlotDuration = 72; // Tweak config so we don't have just defaults const account = mnemonicToAccount(DefaultMnemonic); const deployed = await deployContractsToL1(nodeConfig, account, undefined, { salt: 1 }); diff --git a/yarn-project/aztec/src/cli/validation.ts b/yarn-project/aztec/src/cli/validation.ts index c400ecab0cdb..070dabd89a99 100644 --- a/yarn-project/aztec/src/cli/validation.ts +++ b/yarn-project/aztec/src/cli/validation.ts @@ -11,7 +11,7 @@ import { * contract. For each key, compares it against the provided config (if it is not empty) and throws on mismatches. */ export async function validateL1Config( - config: L1ContractsConfig & { l1Contracts: L1ContractAddresses } & { l1ChainId: number; l1RpcUrl: string }, + config: L1ContractsConfig & { l1Contracts: L1ContractAddresses } & { l1ChainId: number; l1RpcUrls: string[] }, ) { const publicClient = getPublicClient(config); const actualAddresses = await getL1ContractsAddresses(publicClient, config.l1Contracts.governanceAddress); diff --git a/yarn-project/aztec/src/examples/util.ts b/yarn-project/aztec/src/examples/util.ts index 81da0305a9b4..5de1fdc32690 100644 --- a/yarn-project/aztec/src/examples/util.ts +++ b/yarn-project/aztec/src/examples/util.ts @@ -1,8 +1,9 @@ import { EthAddress } from '@aztec/aztec.js'; +import type { ViemPublicClient, ViemWalletClient } from '@aztec/ethereum'; import { jsonStringify } from '@aztec/foundation/json-rpc'; import type { Abi, Narrow } from 'abitype'; -import type { Account, Chain, Hex, HttpTransport, PublicClient, WalletClient } from 'viem'; +import type { Hex } from 'viem'; /** * Helper function to deploy ETH contracts. @@ -14,8 +15,8 @@ import type { Account, Chain, Hex, HttpTransport, PublicClient, WalletClient } f * @returns The ETH address the contract was deployed to. */ export async function deployL1Contract( - walletClient: WalletClient, - publicClient: PublicClient, + walletClient: ViemWalletClient, + publicClient: ViemPublicClient, abi: Narrow, bytecode: Hex, args: readonly unknown[] = [], diff --git a/yarn-project/aztec/src/sandbox.ts b/yarn-project/aztec/src/sandbox.ts index 71291a888f72..4fc391d0c6bc 100644 --- a/yarn-project/aztec/src/sandbox.ts +++ b/yarn-project/aztec/src/sandbox.ts @@ -31,7 +31,7 @@ import { } from '@aztec/telemetry-client'; import { getGenesisValues } from '@aztec/world-state/testing'; -import { type HDAccount, type PrivateKeyAccount, createPublicClient, http as httpViemTransport } from 'viem'; +import { type HDAccount, type PrivateKeyAccount, createPublicClient, fallback, http as httpViemTransport } from 'viem'; import { mnemonicToAccount } from 'viem/accounts'; import { foundry } from 'viem/chains'; @@ -53,14 +53,15 @@ export async function deployContractsToL1( contractDeployLogger = logger, opts: { assumeProvenThroughBlockNumber?: number; salt?: number; genesisArchiveRoot?: Fr; genesisBlockHash?: Fr } = {}, ) { - const chain = aztecNodeConfig.l1RpcUrl - ? createEthereumChain(aztecNodeConfig.l1RpcUrl, aztecNodeConfig.l1ChainId) - : { chainInfo: localAnvil }; + const chain = + aztecNodeConfig.l1RpcUrls.length > 0 + ? createEthereumChain(aztecNodeConfig.l1RpcUrls, aztecNodeConfig.l1ChainId) + : { chainInfo: localAnvil }; await waitForPublicClient(aztecNodeConfig); const l1Contracts = await deployL1Contracts( - aztecNodeConfig.l1RpcUrl, + aztecNodeConfig.l1RpcUrls, hdAccount, chain.chainInfo, contractDeployLogger, @@ -157,6 +158,14 @@ export type SandboxConfig = AztecNodeConfig & { * @param config - Optional Sandbox settings. */ export async function createSandbox(config: Partial = {}, userLog: LogFn) { + // sandbox is meant for test envs. We should only need one l1RpcUrl + const l1RpcUrl = config.l1RpcUrls?.[0]; + if (!l1RpcUrl) { + throw new Error('An L1 RPC URL is required'); + } + if ((config.l1RpcUrls?.length || 0) > 1) { + logger.warn(`Multiple L1 RPC URLs provided. Sandbox will only use the first one: ${l1RpcUrl}`); + } const aztecNodeConfig: AztecNodeConfig = { ...getConfigEnvVars(), ...config }; const hdAccount = mnemonicToAccount(config.l1Mnemonic || DefaultMnemonic); if (!aztecNodeConfig.publisherPrivateKey || aztecNodeConfig.publisherPrivateKey === NULL_KEY) { @@ -196,20 +205,17 @@ export async function createSandbox(config: Partial = {}, userLog salt: config.l1Salt ? parseInt(config.l1Salt) : undefined, }); - const chain = aztecNodeConfig.l1RpcUrl - ? createEthereumChain(aztecNodeConfig.l1RpcUrl, aztecNodeConfig.l1ChainId) - : { chainInfo: localAnvil }; + const chain = + aztecNodeConfig.l1RpcUrls.length > 0 + ? createEthereumChain([l1RpcUrl], aztecNodeConfig.l1ChainId) + : { chainInfo: localAnvil }; const publicClient = createPublicClient({ chain: chain.chainInfo, - transport: httpViemTransport(aztecNodeConfig.l1RpcUrl), + transport: fallback([httpViemTransport(l1RpcUrl)]) as any, }); - watcher = new AnvilTestWatcher( - new EthCheatCodes(aztecNodeConfig.l1RpcUrl), - l1ContractAddresses.rollupAddress, - publicClient, - ); + watcher = new AnvilTestWatcher(new EthCheatCodes([l1RpcUrl]), l1ContractAddresses.rollupAddress, publicClient); watcher.setIsSandbox(true); await watcher.start(); } diff --git a/yarn-project/aztec/terraform/node/main.tf b/yarn-project/aztec/terraform/node/main.tf index 57057faf0ce7..73c11051419a 100644 --- a/yarn-project/aztec/terraform/node/main.tf +++ b/yarn-project/aztec/terraform/node/main.tf @@ -58,7 +58,7 @@ locals { node_p2p_private_keys = var.NODE_P2P_PRIVATE_KEYS node_count = length(local.sequencer_private_keys) data_dir = "/usr/src/yarn-project/aztec" - eth_host = var.ETHEREUM_HOST != "" ? var.ETHEREUM_HOST : "https://${var.DEPLOY_TAG}-mainnet-fork.aztec.network:8545/admin-${var.FORK_ADMIN_API_KEY}" + eth_host = var.ETHEREUM_HOSTS != "" ? var.ETHEREUM_HOSTS : "https://${var.DEPLOY_TAG}-mainnet-fork.aztec.network:8545/admin-${var.FORK_ADMIN_API_KEY}" } output "node_count" { @@ -253,7 +253,7 @@ resource "aws_ecs_task_definition" "aztec-node" { value = "80" }, { - name = "ETHEREUM_HOST" + name = "ETHEREUM_HOSTS" value = "${local.eth_host}" }, { diff --git a/yarn-project/aztec/terraform/node/variables.tf b/yarn-project/aztec/terraform/node/variables.tf index e6597e5dbf35..2d28863dd403 100644 --- a/yarn-project/aztec/terraform/node/variables.tf +++ b/yarn-project/aztec/terraform/node/variables.tf @@ -16,7 +16,7 @@ variable "FORK_ADMIN_API_KEY" { default = "" } -variable "ETHEREUM_HOST" { +variable "ETHEREUM_HOSTS" { type = string default = "" } diff --git a/yarn-project/aztec/terraform/proof-verifier/main.tf b/yarn-project/aztec/terraform/proof-verifier/main.tf index 970bcc2f9fee..4d32f2a7ce5d 100644 --- a/yarn-project/aztec/terraform/proof-verifier/main.tf +++ b/yarn-project/aztec/terraform/proof-verifier/main.tf @@ -155,7 +155,7 @@ resource "aws_ecs_task_definition" "aztec-proof-verifier" { environment = [ { name = "PROOF_VERIFIER_L1_START_BLOCK", value = "15918000" }, { name = "PROOF_VERIFIER_POLL_INTERVAL_MS", value = tostring(var.PROOF_VERIFIER_POLL_INTERVAL_MS) }, - { name = "ETHEREUM_HOST", value = var.ETHEREUM_HOST }, + { name = "ETHEREUM_HOSTS", value = var.ETHEREUM_HOSTS }, { name = "L1_CHAIN_ID", value = tostring(var.L1_CHAIN_ID) }, { name = "ROLLUP_CONTRACT_ADDRESS", value = var.ROLLUP_CONTRACT_ADDRESS }, { diff --git a/yarn-project/aztec/terraform/proof-verifier/variables.tf b/yarn-project/aztec/terraform/proof-verifier/variables.tf index 69aac96df1e1..4038aaf7f249 100644 --- a/yarn-project/aztec/terraform/proof-verifier/variables.tf +++ b/yarn-project/aztec/terraform/proof-verifier/variables.tf @@ -11,7 +11,7 @@ variable "LOG_LEVEL" { default = "verbose" } -variable "ETHEREUM_HOST" { +variable "ETHEREUM_HOSTS" { type = string } diff --git a/yarn-project/aztec/terraform/prover-node/main.tf b/yarn-project/aztec/terraform/prover-node/main.tf index 05be9ea631b9..d64e045c58ef 100644 --- a/yarn-project/aztec/terraform/prover-node/main.tf +++ b/yarn-project/aztec/terraform/prover-node/main.tf @@ -58,7 +58,7 @@ locals { node_p2p_private_keys = var.NODE_P2P_PRIVATE_KEYS node_count = length(local.prover_private_keys) data_dir = "/usr/src/yarn-project/aztec" - eth_host = var.ETHEREUM_HOST != "" ? var.ETHEREUM_HOST : "https://${var.DEPLOY_TAG}-mainnet-fork.aztec.network:8545/admin-${var.API_KEY}" + eth_host = var.ETHEREUM_HOSTS != "" ? var.ETHEREUM_HOSTS : "https://${var.DEPLOY_TAG}-mainnet-fork.aztec.network:8545/admin-${var.API_KEY}" } output "node_count" { @@ -237,7 +237,7 @@ resource "aws_ecs_task_definition" "aztec-prover-node" { { name = "LOG_JSON", value = "1" }, { name = "DEPLOY_TAG", value = var.DEPLOY_TAG }, { name = "NETWORK_NAME", value = "${var.DEPLOY_TAG}" }, - { name = "ETHEREUM_HOST", value = "${local.eth_host}" }, + { name = "ETHEREUM_HOSTS", value = "${local.eth_host}" }, { name = "L1_CHAIN_ID", value = var.L1_CHAIN_ID }, { name = "DATA_DIRECTORY", value = "${local.data_dir}/prover_node_${count.index + 1}/data" }, { name = "DEPLOY_AZTEC_CONTRACTS", value = "false" }, diff --git a/yarn-project/aztec/terraform/prover-node/variables.tf b/yarn-project/aztec/terraform/prover-node/variables.tf index 255978116000..834ea295cdf8 100644 --- a/yarn-project/aztec/terraform/prover-node/variables.tf +++ b/yarn-project/aztec/terraform/prover-node/variables.tf @@ -7,7 +7,7 @@ variable "IMAGE_TAG" { default = "latest" } -variable "ETHEREUM_HOST" { +variable "ETHEREUM_HOSTS" { type = string default = "" } diff --git a/yarn-project/blob-sink/package.json b/yarn-project/blob-sink/package.json index eccd17ae8240..b619b98237ce 100644 --- a/yarn-project/blob-sink/package.json +++ b/yarn-project/blob-sink/package.json @@ -61,6 +61,7 @@ "snappy": "^7.2.2", "source-map-support": "^0.5.21", "tslib": "^2.4.0", + "viem": "2.22.8", "zod": "^3.23.8" }, "devDependencies": { diff --git a/yarn-project/blob-sink/src/client/config.ts b/yarn-project/blob-sink/src/client/config.ts index 5270397a2b51..e07c7cb35e75 100644 --- a/yarn-project/blob-sink/src/client/config.ts +++ b/yarn-project/blob-sink/src/client/config.ts @@ -10,9 +10,9 @@ export interface BlobSinkConfig { blobSinkUrl?: string; /** - * The URL of the L1 RPC Execution client + * List of URLs for L1 RPC Execution clients */ - l1RpcUrl?: string; + l1RpcUrls?: string[]; /** * The URL of the L1 consensus client @@ -35,9 +35,10 @@ export const blobSinkConfigMapping: ConfigMappingsType = { env: 'BLOB_SINK_URL', description: 'The URL of the blob sink', }, - l1RpcUrl: { - env: 'ETHEREUM_HOST', - description: 'The URL of the L1 RPC Execution client', + l1RpcUrls: { + env: 'ETHEREUM_HOSTS', + description: 'List of URLs for L1 RPC Execution clients', + parseEnv: (val: string) => val.split(',').map(url => url.trim()), }, l1ConsensusHostUrl: { env: 'L1_CONSENSUS_HOST_URL', diff --git a/yarn-project/blob-sink/src/client/http.test.ts b/yarn-project/blob-sink/src/client/http.test.ts index 554a23d9f8e7..c6309318218d 100644 --- a/yarn-project/blob-sink/src/client/http.test.ts +++ b/yarn-project/blob-sink/src/client/http.test.ts @@ -168,7 +168,7 @@ describe('HttpBlobSinkClient', () => { const client = new HttpBlobSinkClient({ blobSinkUrl: `http://localhost:${blobSinkServer.port}`, - l1RpcUrl: `http://localhost:${executionHostPort}`, + l1RpcUrls: [`http://localhost:${executionHostPort}`], }); const success = await client.sendBlobsToBlobSink('0x1234', [testEncodedBlob]); @@ -188,7 +188,7 @@ describe('HttpBlobSinkClient', () => { await startConsensusHostServer(); const client = new HttpBlobSinkClient({ - l1RpcUrl: `http://localhost:${executionHostPort}`, + l1RpcUrls: [`http://localhost:${executionHostPort}`], l1ConsensusHostUrl: `http://localhost:${consensusHostPort}`, }); @@ -201,7 +201,7 @@ describe('HttpBlobSinkClient', () => { await startConsensusHostServer(); const client = new HttpBlobSinkClient({ - l1RpcUrl: `http://localhost:${executionHostPort}`, + l1RpcUrls: [`http://localhost:${executionHostPort}`], l1ConsensusHostUrl: `http://localhost:${consensusHostPort}`, }); @@ -215,7 +215,7 @@ describe('HttpBlobSinkClient', () => { await startConsensusHostServer(); const client = new HttpBlobSinkClient({ - l1RpcUrl: `http://localhost:${executionHostPort}`, + l1RpcUrls: [`http://localhost:${executionHostPort}`], l1ConsensusHostUrl: `http://localhost:${consensusHostPort}`, }); diff --git a/yarn-project/blob-sink/src/client/http.ts b/yarn-project/blob-sink/src/client/http.ts index 9e6bb9459ad6..67851a2c9369 100644 --- a/yarn-project/blob-sink/src/client/http.ts +++ b/yarn-project/blob-sink/src/client/http.ts @@ -2,6 +2,8 @@ import { Blob, BlobDeserializationError, type BlobJson } from '@aztec/blob-lib'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { makeBackoff, retry } from '@aztec/foundation/retry'; +import { type RpcBlock, createPublicClient, fallback, http } from 'viem'; + import { outboundTransform } from '../encoding/index.js'; import { type BlobSinkConfig, getBlobSinkConfigFromEnv } from './config.js'; import type { BlobSinkClientInterface } from './interface.js'; @@ -79,7 +81,7 @@ export class HttpBlobSinkClient implements BlobSinkClientInterface { * @param indices - The indices of the blobs to get * @returns The blobs */ - public async getBlobSidecar(blockHash: string, blobHashes: Buffer[], indices?: number[]): Promise { + public async getBlobSidecar(blockHash: `0x${string}`, blobHashes: Buffer[], indices?: number[]): Promise { let blobs: Blob[] = []; if (this.config.blobSinkUrl) { @@ -165,36 +167,30 @@ export class HttpBlobSinkClient implements BlobSinkClientInterface { * @param blockHash - The block hash * @returns The slot number */ - private async getSlotNumber(blockHash: string): Promise { + private async getSlotNumber(blockHash: `0x${string}`): Promise { if (!this.config.l1ConsensusHostUrl) { this.log.debug('No consensus host url configured'); return undefined; } - if (!this.config.l1RpcUrl) { + if (!this.config.l1RpcUrls) { this.log.debug('No execution host url configured'); return undefined; } // Ping execution node to get the parentBeaconBlockRoot for this block let parentBeaconBlockRoot: string | undefined; + const client = createPublicClient({ + transport: fallback(this.config.l1RpcUrls.map(url => http(url))), + }); try { - const res = await this.fetch(`${this.config.l1RpcUrl}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - jsonrpc: '2.0', - method: 'eth_getBlockByHash', - params: [blockHash, /*tx flag*/ false], - id: 1, - }), + const res: RpcBlock = await client.request({ + method: 'eth_getBlockByHash', + params: [blockHash, /*tx flag*/ false], }); - if (res.ok) { - const body = await res.json(); - parentBeaconBlockRoot = body.result.parentBeaconBlockRoot; + if (res.parentBeaconBlockRoot) { + parentBeaconBlockRoot = res.parentBeaconBlockRoot; } } catch (err) { this.log.error(`Error getting parent beacon block root`, err); diff --git a/yarn-project/bot/src/config.ts b/yarn-project/bot/src/config.ts index 2cd88730f607..92aca1f6a195 100644 --- a/yarn-project/bot/src/config.ts +++ b/yarn-project/bot/src/config.ts @@ -28,7 +28,7 @@ export type BotConfig = { /** URL to the PXE for sending txs, or undefined if an in-proc PXE is used. */ pxeUrl: string | undefined; /** Url of the ethereum host. */ - l1RpcUrl: string | undefined; + l1RpcUrls: string[] | undefined; /** The mnemonic for the account to bridge fee juice from L1. */ l1Mnemonic: string | undefined; /** The private key for the account to bridge fee juice from L1. */ @@ -75,7 +75,7 @@ export const BotConfigSchema = z .object({ nodeUrl: z.string().optional(), pxeUrl: z.string().optional(), - l1RpcUrl: z.string().optional(), + l1RpcUrls: z.array(z.string()).optional(), l1Mnemonic: z.string().optional(), l1PrivateKey: z.string().optional(), senderPrivateKey: schemas.Fr.optional(), @@ -100,7 +100,7 @@ export const BotConfigSchema = z .transform(config => ({ nodeUrl: undefined, pxeUrl: undefined, - l1RpcUrl: undefined, + l1RpcUrls: undefined, l1Mnemonic: undefined, l1PrivateKey: undefined, senderPrivateKey: undefined, @@ -118,9 +118,10 @@ export const botConfigMappings: ConfigMappingsType = { env: 'BOT_PXE_URL', description: 'URL to the PXE for sending txs, or undefined if an in-proc PXE is used.', }, - l1RpcUrl: { - env: 'ETHEREUM_HOST', + l1RpcUrls: { + env: 'ETHEREUM_HOSTS', description: 'URL of the ethereum host.', + parseEnv: (val: string) => val.split(',').map(url => url.trim()), }, l1Mnemonic: { env: 'BOT_L1_MNEMONIC', diff --git a/yarn-project/bot/src/factory.ts b/yarn-project/bot/src/factory.ts index 61c4dc18d48e..a2699d42e8a4 100644 --- a/yarn-project/bot/src/factory.ts +++ b/yarn-project/bot/src/factory.ts @@ -209,8 +209,8 @@ export class BotFactory { } private async bridgeL1FeeJuice(recipient: AztecAddress, amount: bigint) { - const l1RpcUrl = this.config.l1RpcUrl; - if (!l1RpcUrl) { + const l1RpcUrls = this.config.l1RpcUrls; + if (!l1RpcUrls?.length) { throw new Error('L1 Rpc url is required to bridge the fee juice to fund the deployment of the account.'); } const mnemonicOrPrivateKey = this.config.l1PrivateKey || this.config.l1Mnemonic; @@ -221,8 +221,8 @@ export class BotFactory { } const { l1ChainId } = await this.pxe.getNodeInfo(); - const chain = createEthereumChain(l1RpcUrl, l1ChainId); - const { publicClient, walletClient } = createL1Clients(chain.rpcUrl, mnemonicOrPrivateKey, chain.chainInfo); + const chain = createEthereumChain(l1RpcUrls, l1ChainId); + const { publicClient, walletClient } = createL1Clients(chain.rpcUrls, mnemonicOrPrivateKey, chain.chainInfo); const portal = await L1FeeJuicePortalManager.new(this.pxe, publicClient, walletClient, this.log); const claim = await portal.bridgeTokensPublic(recipient, amount, true /* mint */); diff --git a/yarn-project/cli-wallet/src/cmds/bridge_fee_juice.ts b/yarn-project/cli-wallet/src/cmds/bridge_fee_juice.ts index f90cdef8aac3..7e8723b88e53 100644 --- a/yarn-project/cli-wallet/src/cmds/bridge_fee_juice.ts +++ b/yarn-project/cli-wallet/src/cmds/bridge_fee_juice.ts @@ -9,7 +9,7 @@ export async function bridgeL1FeeJuice( amount: bigint, recipient: AztecAddress, pxe: PXE, - l1RpcUrl: string, + l1RpcUrls: string[], chainId: number, privateKey: string | undefined, mnemonic: string, @@ -21,8 +21,8 @@ export async function bridgeL1FeeJuice( debugLogger: Logger, ) { // Prepare L1 client - const chain = createEthereumChain(l1RpcUrl, chainId); - const { publicClient, walletClient } = createL1Clients(chain.rpcUrl, privateKey ?? mnemonic, chain.chainInfo); + const chain = createEthereumChain(l1RpcUrls, chainId); + const { publicClient, walletClient } = createL1Clients(chain.rpcUrls, privateKey ?? mnemonic, chain.chainInfo); const { protocolContractAddresses: { feeJuice: feeJuiceAddress }, diff --git a/yarn-project/cli-wallet/src/cmds/index.ts b/yarn-project/cli-wallet/src/cmds/index.ts index 45fea5c0eb81..09b214f80620 100644 --- a/yarn-project/cli-wallet/src/cmds/index.ts +++ b/yarn-project/cli-wallet/src/cmds/index.ts @@ -2,7 +2,7 @@ import { getIdentities } from '@aztec/accounts/utils'; import { createCompatibleClient } from '@aztec/aztec.js/rpc'; import { TxHash } from '@aztec/aztec.js/tx_hash'; import { - ETHEREUM_HOST, + ETHEREUM_HOSTS, PRIVATE_KEY, addOptions, createSecretKeyOption, @@ -337,10 +337,11 @@ export function injectCommands( .argument('', 'Aztec address of the recipient.', address => aliasedAddressParser('accounts', address, db), ) - .requiredOption( - '--l1-rpc-url ', - 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', - ETHEREUM_HOST, + .requiredOption( + '--l1-rpc-urls ', + 'List of Ethereum host URLs. Chain identifiers localhost and testnet can be used (comma separated)', + (arg: string) => arg.split(','), + [ETHEREUM_HOSTS], ) .option( '-m, --mnemonic ', @@ -362,14 +363,14 @@ export function injectCommands( ) .action(async (amount, recipient, options) => { const { bridgeL1FeeJuice } = await import('./bridge_fee_juice.js'); - const { rpcUrl, l1RpcUrl, l1ChainId, l1PrivateKey, mnemonic, mint, json, wait, interval: intervalS } = options; + const { rpcUrl, l1ChainId, l1RpcUrls, l1PrivateKey, mnemonic, mint, json, wait, interval: intervalS } = options; const client = pxeWrapper?.getPXE() ?? (await createCompatibleClient(rpcUrl, debugLogger)); const [secret, messageLeafIndex] = await bridgeL1FeeJuice( amount, recipient, client, - l1RpcUrl, + l1RpcUrls, l1ChainId, l1PrivateKey, mnemonic, diff --git a/yarn-project/cli/README.md b/yarn-project/cli/README.md index 76a90f506b21..49cae0ec75f7 100644 --- a/yarn-project/cli/README.md +++ b/yarn-project/cli/README.md @@ -38,7 +38,7 @@ These options are: - `SECRET_KEY` -> `-sk, --secret-key` for all commands that require an Aztec secret key. - `PUBLIC_KEY` -> `-k, --public-key` for all commands that require a public key. - `PXE_URL` -> `-u, --rpc-url` for commands that require a PXE -- `ETHEREUM_RPC_HOST` -> `-u, --rpc-url` for `deploy-l1-contracts`. +- `ETHEREUM_HOSTS` -> `-u, --rpc-urls` or `--l1-rpc-urls` for `deploy-l1-contracts`. So if for example you are running your Private eXecution Environment (PXE) remotely you can do: diff --git a/yarn-project/cli/src/cmds/devnet/bootstrap_network.ts b/yarn-project/cli/src/cmds/devnet/bootstrap_network.ts index a8c2d2f22b52..1c0e77c5ce42 100644 --- a/yarn-project/cli/src/cmds/devnet/bootstrap_network.ts +++ b/yarn-project/cli/src/cmds/devnet/bootstrap_network.ts @@ -38,7 +38,7 @@ const waitOpts: WaitOpts = { export async function bootstrapNetwork( pxeUrl: string, - l1Url: string, + l1Urls: string[], l1ChainId: string, l1PrivateKey: `0x${string}` | undefined, l1Mnemonic: string, @@ -52,13 +52,13 @@ export async function bootstrapNetwork( const [wallet] = await getDeployedTestAccountsWallets(pxe); const l1Clients = createL1Clients( - l1Url, + l1Urls, l1PrivateKey ? privateKeyToAccount(l1PrivateKey) : // We need to use a different account that the main "deployer" account because the "deployer" account creates transactions that send blobs. // Note that this account needs to be funded on L1 ! mnemonicToAccount(l1Mnemonic, { addressIndex }), - createEthereumChain(l1Url, +l1ChainId).chainInfo, + createEthereumChain(l1Urls, +l1ChainId).chainInfo, ); const { erc20Address, portalAddress } = await deployERC20(l1Clients); diff --git a/yarn-project/cli/src/cmds/devnet/index.ts b/yarn-project/cli/src/cmds/devnet/index.ts index 840d482435fe..ba0b82dc1d57 100644 --- a/yarn-project/cli/src/cmds/devnet/index.ts +++ b/yarn-project/cli/src/cmds/devnet/index.ts @@ -2,7 +2,7 @@ import type { LogFn, Logger } from '@aztec/foundation/log'; import type { Command } from 'commander'; -import { ETHEREUM_HOST, l1ChainIdOption, parseEthereumAddress, pxeOption } from '../../utils/commands.js'; +import { ETHEREUM_HOSTS, l1ChainIdOption, parseEthereumAddress, pxeOption } from '../../utils/commands.js'; export function injectCommands(program: Command, log: LogFn, debugLogger: Logger) { program @@ -10,10 +10,11 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger .description('Bootstrap a new network') .addOption(pxeOption) .addOption(l1ChainIdOption) - .requiredOption( - '--l1-rpc-url ', - 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', - ETHEREUM_HOST, + .requiredOption( + '--l1-rpc-urls ', + 'List of Ethereum host URLs. Chain identifiers localhost and testnet can be used (comma separated)', + (arg: string) => arg.split(','), + [ETHEREUM_HOSTS], ) .option('--l1-private-key ', 'The private key to use for deployment', process.env.PRIVATE_KEY) .option( @@ -32,7 +33,7 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger const { bootstrapNetwork } = await import('./bootstrap_network.js'); await bootstrapNetwork( options[pxeOption.attributeName()], - options.l1RpcUrl, + options.l1RpcUrls, options[l1ChainIdOption.attributeName()], options.l1PrivateKey, options.mnemonic, diff --git a/yarn-project/cli/src/cmds/infrastructure/index.ts b/yarn-project/cli/src/cmds/infrastructure/index.ts index 0df893606df8..c2386a55fc60 100644 --- a/yarn-project/cli/src/cmds/infrastructure/index.ts +++ b/yarn-project/cli/src/cmds/infrastructure/index.ts @@ -2,7 +2,7 @@ import type { LogFn, Logger } from '@aztec/foundation/log'; import type { Command } from 'commander'; -import { ETHEREUM_HOST, l1ChainIdOption, parseOptionalInteger, pxeOption } from '../../utils/commands.js'; +import { ETHEREUM_HOSTS, l1ChainIdOption, parseOptionalInteger, pxeOption } from '../../utils/commands.js'; export function injectCommands(program: Command, log: LogFn, debugLogger: Logger) { program @@ -22,10 +22,11 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger .argument('', 'Command to run: list, add, remove, who-next') .argument('[who]', 'Who to add/remove') .description('Manages or queries registered sequencers on the L1 rollup contract.') - .requiredOption( - '--l1-rpc-url ', - 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', - ETHEREUM_HOST, + .requiredOption( + '--l1-rpc-urls ', + 'List of Ethereum host URLs. Chain identifiers localhost and testnet can be used (comma separated)', + (arg: string) => arg.split(','), + [ETHEREUM_HOSTS], ) .option( '-m, --mnemonic ', @@ -42,7 +43,7 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger who, mnemonic: options.mnemonic, rpcUrl: options.rpcUrl, - l1RpcUrl: options.l1RpcUrl, + l1RpcUrls: options.l1RpcUrls.split(','), chainId: options.l1ChainId, blockNumber: options.blockNumber, log, diff --git a/yarn-project/cli/src/cmds/infrastructure/sequencers.ts b/yarn-project/cli/src/cmds/infrastructure/sequencers.ts index b4c6e3a88f51..f789d9589dc0 100644 --- a/yarn-project/cli/src/cmds/infrastructure/sequencers.ts +++ b/yarn-project/cli/src/cmds/infrastructure/sequencers.ts @@ -3,7 +3,7 @@ import { createEthereumChain, getL1ContractsConfigEnvVars } from '@aztec/ethereu import type { LogFn, Logger } from '@aztec/foundation/log'; import { RollupAbi, TestERC20Abi } from '@aztec/l1-artifacts'; -import { createPublicClient, createWalletClient, getContract, http } from 'viem'; +import { createPublicClient, createWalletClient, fallback, getContract, http } from 'viem'; import { mnemonicToAccount } from 'viem/accounts'; export async function sequencers(opts: { @@ -11,24 +11,27 @@ export async function sequencers(opts: { who?: string; mnemonic?: string; rpcUrl: string; - l1RpcUrl: string; + l1RpcUrls: string[]; chainId: number; blockNumber?: number; log: LogFn; debugLogger: Logger; }) { - const { command, who: maybeWho, mnemonic, rpcUrl, l1RpcUrl, chainId, log, debugLogger } = opts; + const { command, who: maybeWho, mnemonic, rpcUrl, l1RpcUrls, chainId, log, debugLogger } = opts; const client = await createCompatibleClient(rpcUrl, debugLogger); const { l1ContractAddresses } = await client.getNodeInfo(); - const chain = createEthereumChain(l1RpcUrl, chainId); - const publicClient = createPublicClient({ chain: chain.chainInfo, transport: http(chain.rpcUrl) }); + const chain = createEthereumChain(l1RpcUrls, chainId); + const publicClient = createPublicClient({ + chain: chain.chainInfo, + transport: fallback(l1RpcUrls.map(url => http(url))), + }); const walletClient = mnemonic ? createWalletClient({ account: mnemonicToAccount(mnemonic), chain: chain.chainInfo, - transport: http(chain.rpcUrl), + transport: fallback(l1RpcUrls.map(url => http(url))), }) : undefined; diff --git a/yarn-project/cli/src/cmds/l1/advance_epoch.ts b/yarn-project/cli/src/cmds/l1/advance_epoch.ts index 04391995dad3..4fc5fbbc9c2f 100644 --- a/yarn-project/cli/src/cmds/l1/advance_epoch.ts +++ b/yarn-project/cli/src/cmds/l1/advance_epoch.ts @@ -1,11 +1,11 @@ import { CheatCodes, createPXEClient, makeFetch } from '@aztec/aztec.js'; import type { LogFn } from '@aztec/foundation/log'; -export async function advanceEpoch(l1RpcUrl: string, rpcUrl: string, log: LogFn) { +export async function advanceEpoch(l1RpcUrls: string[], rpcUrl: string, log: LogFn) { const pxe = createPXEClient(rpcUrl, {}, makeFetch([], true)); const rollupAddress = await pxe.getNodeInfo().then(i => i.l1ContractAddresses.rollupAddress); - const cheat = CheatCodes.createRollup(l1RpcUrl, { rollupAddress }); + const cheat = CheatCodes.createRollup(l1RpcUrls, { rollupAddress }); await cheat.advanceToNextEpoch(); log(`Warped time to advance to next epoch`); diff --git a/yarn-project/cli/src/cmds/l1/assume_proven_through.ts b/yarn-project/cli/src/cmds/l1/assume_proven_through.ts index 4aa3429318a8..6217b05179ab 100644 --- a/yarn-project/cli/src/cmds/l1/assume_proven_through.ts +++ b/yarn-project/cli/src/cmds/l1/assume_proven_through.ts @@ -3,7 +3,7 @@ import type { LogFn } from '@aztec/foundation/log'; export async function assumeProvenThrough( blockNumberOrLatest: number | undefined, - l1RpcUrl: string, + l1RpcUrls: string[], rpcUrl: string, log: LogFn, ) { @@ -11,7 +11,7 @@ export async function assumeProvenThrough( const rollupAddress = await pxe.getNodeInfo().then(i => i.l1ContractAddresses.rollupAddress); const blockNumber = blockNumberOrLatest ?? (await pxe.getBlockNumber()); - const ethCheatCode = new EthCheatCodes(l1RpcUrl); + const ethCheatCode = new EthCheatCodes(l1RpcUrls); const rollupCheatCodes = new RollupCheatCodes(ethCheatCode, { rollupAddress }); await rollupCheatCodes.markAsProven(blockNumber); diff --git a/yarn-project/cli/src/cmds/l1/bridge_erc20.ts b/yarn-project/cli/src/cmds/l1/bridge_erc20.ts index 20197757d87d..6f676b241b29 100644 --- a/yarn-project/cli/src/cmds/l1/bridge_erc20.ts +++ b/yarn-project/cli/src/cmds/l1/bridge_erc20.ts @@ -7,7 +7,7 @@ import { prettyPrintJSON } from '../../utils/commands.js'; export async function bridgeERC20( amount: bigint, recipient: AztecAddress, - l1RpcUrl: string, + l1RpcUrls: string[], chainId: number, privateKey: string | undefined, mnemonic: string, @@ -20,8 +20,8 @@ export async function bridgeERC20( debugLogger: Logger, ) { // Prepare L1 client - const chain = createEthereumChain(l1RpcUrl, chainId); - const { publicClient, walletClient } = createL1Clients(chain.rpcUrl, privateKey ?? mnemonic, chain.chainInfo); + const chain = createEthereumChain(l1RpcUrls, chainId); + const { publicClient, walletClient } = createL1Clients(chain.rpcUrls, privateKey ?? mnemonic, chain.chainInfo); // Setup portal manager const manager = new L1ToL2TokenPortalManager(portalAddress, tokenAddress, publicClient, walletClient, debugLogger); diff --git a/yarn-project/cli/src/cmds/l1/deploy_l1_contracts.ts b/yarn-project/cli/src/cmds/l1/deploy_l1_contracts.ts index eeb07d431e09..6ffd0fa1a0da 100644 --- a/yarn-project/cli/src/cmds/l1/deploy_l1_contracts.ts +++ b/yarn-project/cli/src/cmds/l1/deploy_l1_contracts.ts @@ -7,7 +7,7 @@ import { getGenesisValues } from '@aztec/world-state/testing'; import { deployAztecContracts } from '../../utils/aztec.js'; export async function deployL1Contracts( - rpcUrl: string, + rpcUrls: string[], chainId: number, privateKey: string | undefined, mnemonic: string, @@ -25,7 +25,7 @@ export async function deployL1Contracts( const { genesisBlockHash, genesisArchiveRoot } = await getGenesisValues(initialFundedAccounts.map(a => a.address)); const { l1ContractAddresses } = await deployAztecContracts( - rpcUrl, + rpcUrls, chainId, privateKey, mnemonic, diff --git a/yarn-project/cli/src/cmds/l1/deploy_l1_verifier.ts b/yarn-project/cli/src/cmds/l1/deploy_l1_verifier.ts index c0120a1b1d0c..bed0d52904e4 100644 --- a/yarn-project/cli/src/cmds/l1/deploy_l1_verifier.ts +++ b/yarn-project/cli/src/cmds/l1/deploy_l1_verifier.ts @@ -8,7 +8,7 @@ import { type Hex, getContract } from 'viem'; export async function deployUltraHonkVerifier( rollupAddress: Hex | undefined, - ethRpcUrl: string, + ethRpcUrls: string[], l1ChainId: string, privateKey: string | undefined, mnemonic: string, @@ -23,9 +23,9 @@ export async function deployUltraHonkVerifier( } const { publicClient, walletClient } = createL1Clients( - ethRpcUrl, + ethRpcUrls, privateKey ?? mnemonic, - createEthereumChain(ethRpcUrl, l1ChainId).chainInfo, + createEthereumChain(ethRpcUrls, l1ChainId).chainInfo, ); if (!rollupAddress && pxeRpcUrl) { @@ -61,7 +61,7 @@ export async function deployUltraHonkVerifier( export async function deployMockVerifier( rollupAddress: Hex | undefined, - ethRpcUrl: string, + ethRpcUrls: string[], l1ChainId: string, privateKey: string | undefined, mnemonic: string, @@ -70,9 +70,9 @@ export async function deployMockVerifier( debugLogger: Logger, ) { const { publicClient, walletClient } = createL1Clients( - ethRpcUrl, + ethRpcUrls, privateKey ?? mnemonic, - createEthereumChain(ethRpcUrl, l1ChainId).chainInfo, + createEthereumChain(ethRpcUrls, l1ChainId).chainInfo, ); const { MockVerifierAbi, MockVerifierBytecode, RollupAbi } = await import('@aztec/l1-artifacts'); diff --git a/yarn-project/cli/src/cmds/l1/get_l1_balance.ts b/yarn-project/cli/src/cmds/l1/get_l1_balance.ts index 9572703f42f1..48b7138b0136 100644 --- a/yarn-project/cli/src/cmds/l1/get_l1_balance.ts +++ b/yarn-project/cli/src/cmds/l1/get_l1_balance.ts @@ -3,20 +3,23 @@ import type { EthAddress } from '@aztec/foundation/eth-address'; import type { LogFn } from '@aztec/foundation/log'; import { TestERC20Abi } from '@aztec/l1-artifacts'; -import { createPublicClient, getContract, http } from 'viem'; +import { createPublicClient, fallback, getContract, http } from 'viem'; import { prettyPrintJSON } from '../../utils/commands.js'; export async function getL1Balance( who: EthAddress, token: EthAddress | undefined, - l1RpcUrl: string, + l1RpcUrls: string[], chainId: number, json: boolean, log: LogFn, ) { - const chain = createEthereumChain(l1RpcUrl, chainId); - const publicClient = createPublicClient({ chain: chain.chainInfo, transport: http(chain.rpcUrl) }); + const chain = createEthereumChain(l1RpcUrls, chainId); + const publicClient = createPublicClient({ + chain: chain.chainInfo, + transport: fallback(l1RpcUrls.map(url => http(url))), + }); let balance = 0n; if (token) { diff --git a/yarn-project/cli/src/cmds/l1/index.ts b/yarn-project/cli/src/cmds/l1/index.ts index 72971562697e..90208eec6352 100644 --- a/yarn-project/cli/src/cmds/l1/index.ts +++ b/yarn-project/cli/src/cmds/l1/index.ts @@ -4,7 +4,7 @@ import type { LogFn, Logger } from '@aztec/foundation/log'; import { type Command, Option } from 'commander'; import { - ETHEREUM_HOST, + ETHEREUM_HOSTS, PRIVATE_KEY, l1ChainIdOption, makePxeOption, @@ -14,17 +14,22 @@ import { pxeOption, } from '../../utils/commands.js'; +const l1RpcUrlsOption = new Option( + '--l1-rpc-urls ', + 'List of Ethereum host URLs. Chain identifiers localhost and testnet can be used (comma separated)', +) + .env('ETHEREUM_HOSTS') + .default([ETHEREUM_HOSTS]) + .makeOptionMandatory(true) + .argParser((arg: string) => arg.split(',').map(url => url.trim())); + export function injectCommands(program: Command, log: LogFn, debugLogger: Logger) { const { BB_BINARY_PATH, BB_WORKING_DIRECTORY } = process.env; program .command('deploy-l1-contracts') .description('Deploys all necessary Ethereum contracts for Aztec.') - .requiredOption( - '-u, --rpc-url ', - 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', - ETHEREUM_HOST, - ) + .addOption(l1RpcUrlsOption) .option('-pk, --private-key ', 'The private key to use for deployment', PRIVATE_KEY) .option('--validators ', 'Comma separated list of validators') .option( @@ -43,7 +48,7 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger const initialValidators = options.validators?.split(',').map((validator: string) => EthAddress.fromString(validator)) || []; await deployL1Contracts( - options.rpcUrl, + options.l1RpcUrls, options.l1ChainId, options.privateKey, options.mnemonic, @@ -70,11 +75,7 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger program .command('add-l1-validator') .description('Adds a validator to the L1 rollup contract.') - .requiredOption( - '-u, --rpc-url ', - 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', - ETHEREUM_HOST, - ) + .addOption(l1RpcUrlsOption) .option('-pk, --private-key ', 'The private key to use for deployment', PRIVATE_KEY) .option( '-m, --mnemonic ', @@ -88,7 +89,7 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger .action(async options => { const { addL1Validator } = await import('./update_l1_validators.js'); await addL1Validator({ - rpcUrl: options.rpcUrl, + rpcUrls: options.l1RpcUrls, chainId: options.l1ChainId, privateKey: options.privateKey, mnemonic: options.mnemonic, @@ -103,11 +104,7 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger program .command('remove-l1-validator') .description('Removes a validator to the L1 rollup contract.') - .requiredOption( - '-u, --rpc-url ', - 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', - ETHEREUM_HOST, - ) + .addOption(l1RpcUrlsOption) .option('-pk, --private-key ', 'The private key to use for deployment', PRIVATE_KEY) .option( '-m, --mnemonic ', @@ -120,7 +117,7 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger .action(async options => { const { removeL1Validator } = await import('./update_l1_validators.js'); await removeL1Validator({ - rpcUrl: options.rpcUrl, + rpcUrls: options.l1RpcUrls, chainId: options.l1ChainId, privateKey: options.privateKey, mnemonic: options.mnemonic, @@ -134,18 +131,14 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger program .command('fast-forward-epochs') .description('Fast forwards the epoch of the L1 rollup contract.') - .requiredOption( - '-u, --rpc-url ', - 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', - ETHEREUM_HOST, - ) + .addOption(l1RpcUrlsOption) .addOption(l1ChainIdOption) .option('--rollup
', 'ethereum address of the rollup contract', parseEthereumAddress) .option('--count ', 'The number of epochs to fast forward', arg => BigInt(parseInt(arg)), 1n) .action(async options => { const { fastForwardEpochs } = await import('./update_l1_validators.js'); await fastForwardEpochs({ - rpcUrl: options.rpcUrl, + rpcUrls: options.l1RpcUrls, chainId: options.l1ChainId, rollupAddress: options.rollup, numEpochs: options.count, @@ -157,17 +150,13 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger program .command('debug-rollup') .description('Debugs the rollup contract.') - .requiredOption( - '-u, --rpc-url ', - 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', - ETHEREUM_HOST, - ) + .addOption(l1RpcUrlsOption) .addOption(l1ChainIdOption) .option('--rollup
', 'ethereum address of the rollup contract', parseEthereumAddress) .action(async options => { const { debugRollup } = await import('./update_l1_validators.js'); await debugRollup({ - rpcUrl: options.rpcUrl, + rpcUrls: options.l1RpcUrls, chainId: options.l1ChainId, privateKey: options.privateKey, mnemonic: options.mnemonic, @@ -180,11 +169,7 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger program .command('prune-rollup') .description('Prunes the pending chain on the rollup contract.') - .requiredOption( - '-u, --rpc-url ', - 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', - ETHEREUM_HOST, - ) + .addOption(l1RpcUrlsOption) .option('-pk, --private-key ', 'The private key to use for deployment', PRIVATE_KEY) .option( '-m, --mnemonic ', @@ -196,7 +181,7 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger .action(async options => { const { pruneRollup } = await import('./update_l1_validators.js'); await pruneRollup({ - rpcUrl: options.rpcUrl, + rpcUrls: options.rpcUrls, chainId: options.l1ChainId, privateKey: options.privateKey, mnemonic: options.mnemonic, @@ -209,17 +194,8 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger program .command('deploy-l1-verifier') .description('Deploys the rollup verifier contract') - .requiredOption( - '--l1-rpc-url ', - 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', - ETHEREUM_HOST, - ) - .addOption( - new Option('--l1-chain-id ', 'The chain id of the L1 network') - .env('L1_CHAIN_ID') - .default('31337') - .makeOptionMandatory(true), - ) + .addOption(l1RpcUrlsOption) + .addOption(l1ChainIdOption) .addOption(makePxeOption(false).conflicts('rollup-address')) .addOption( new Option('--rollup-address ', 'The address of the rollup contract') @@ -241,7 +217,7 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger if (options.verifier === 'mock') { await deployMockVerifier( options.rollupAddress?.toString(), - options.l1RpcUrl, + options.l1RpcUrls, options.l1ChainId, options.l1PrivateKey, options.mnemonic, @@ -252,7 +228,7 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger } else { await deployUltraHonkVerifier( options.rollupAddress?.toString(), - options.l1RpcUrl, + options.l1RpcUrls, options.l1ChainId, options.l1PrivateKey, options.mnemonic, @@ -270,11 +246,7 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger .description('Bridges ERC20 tokens to L2.') .argument('', 'The amount of Fee Juice to mint and bridge.', parseBigint) .argument('', 'Aztec address of the recipient.', parseAztecAddress) - .requiredOption( - '--l1-rpc-url ', - 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', - ETHEREUM_HOST, - ) + .addOption(l1RpcUrlsOption) .option( '-m, --mnemonic ', 'The mnemonic to use for deriving the Ethereum address that will mint and bridge', @@ -292,7 +264,7 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger await bridgeERC20( amount, recipient, - options.l1RpcUrl, + options.l1RpcUrls, options.l1ChainId, options.l1PrivateKey, options.mnemonic, @@ -318,17 +290,13 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger .command('get-l1-balance') .description('Gets the balance of an ERC token in L1 for the given Ethereum address.') .argument('', 'Ethereum address to check.', parseEthereumAddress) - .requiredOption( - '--l1-rpc-url ', - 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', - ETHEREUM_HOST, - ) + .addOption(l1RpcUrlsOption) .option('-t, --token ', 'The address of the token to check the balance of', parseEthereumAddress) .addOption(l1ChainIdOption) .option('--json', 'Output the balance in JSON format') .action(async (who, options) => { const { getL1Balance } = await import('./get_l1_balance.js'); - await getL1Balance(who, options.token, options.l1RpcUrl, options.l1ChainId, options.json, log); + await getL1Balance(who, options.token, options.l1RpcUrls, options.l1ChainId, options.json, log); }); program @@ -337,38 +305,26 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger 'Instructs the L1 rollup contract to assume all blocks until the given number are automatically proven.', ) .argument('[blockNumber]', 'The target block number, defaults to the latest pending block number.', parseBigint) - .requiredOption( - '--l1-rpc-url ', - 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', - ETHEREUM_HOST, - ) + .addOption(l1RpcUrlsOption) .addOption(pxeOption) .action(async (blockNumber, options) => { const { assumeProvenThrough } = await import('./assume_proven_through.js'); - await assumeProvenThrough(blockNumber, options.l1RpcUrl, options.rpcUrl, log); + await assumeProvenThrough(blockNumber, options.l1RpcUrls, options.rpcUrl, log); }); program .command('advance-epoch') .description('Use L1 cheat codes to warp time until the next epoch.') - .requiredOption( - '--l1-rpc-url ', - 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', - ETHEREUM_HOST, - ) + .addOption(l1RpcUrlsOption) .addOption(pxeOption) .action(async options => { const { advanceEpoch } = await import('./advance_epoch.js'); - await advanceEpoch(options.l1RpcUrl, options.rpcUrl, log); + await advanceEpoch(options.l1RpcUrls, options.rpcUrl, log); }); program .command('prover-stats', { hidden: true }) - .requiredOption( - '--l1-rpc-url ', - 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', - ETHEREUM_HOST, - ) + .addOption(l1RpcUrlsOption) .addOption(l1ChainIdOption) .option('--start-block ', 'The L1 block number to start from', parseBigint, 1n) .option('--end-block ', 'The last L1 block number to query', parseBigint) @@ -382,10 +338,10 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: Logger .option('--raw-logs', 'Output raw logs instead of aggregated stats') .action(async options => { const { proverStats } = await import('./prover_stats.js'); - const { l1RpcUrl, chainId, l1RollupAddress, startBlock, endBlock, batchSize, nodeUrl, provingTimeout, rawLogs } = + const { l1RpcUrls, chainId, l1RollupAddress, startBlock, endBlock, batchSize, nodeUrl, provingTimeout, rawLogs } = options; await proverStats({ - l1RpcUrl, + l1RpcUrls, chainId, l1RollupAddress, startBlock, diff --git a/yarn-project/cli/src/cmds/l1/prover_stats.ts b/yarn-project/cli/src/cmds/l1/prover_stats.ts index 38daccfbff6b..5b84ac8c85cf 100644 --- a/yarn-project/cli/src/cmds/l1/prover_stats.ts +++ b/yarn-project/cli/src/cmds/l1/prover_stats.ts @@ -1,5 +1,5 @@ import { retrieveL2ProofVerifiedEvents } from '@aztec/archiver'; -import { createEthereumChain } from '@aztec/ethereum'; +import { type ViemPublicClient, createEthereumChain } from '@aztec/ethereum'; import { compactArray, mapValues, unique } from '@aztec/foundation/collection'; import { EthAddress } from '@aztec/foundation/eth-address'; import { type LogFn, type Logger, createLogger } from '@aztec/foundation/log'; @@ -8,10 +8,10 @@ import { createAztecNodeClient } from '@aztec/stdlib/interfaces/client'; import chunk from 'lodash.chunk'; import groupBy from 'lodash.groupby'; -import { type PublicClient, createPublicClient, getAbiItem, getAddress, http } from 'viem'; +import { createPublicClient, fallback, getAbiItem, getAddress, http } from 'viem'; export async function proverStats(opts: { - l1RpcUrl: string; + l1RpcUrls: string[]; chainId: number; l1RollupAddress: string | undefined; nodeUrl: string | undefined; @@ -23,8 +23,18 @@ export async function proverStats(opts: { rawLogs: boolean; }) { const debugLog = createLogger('cli:prover_stats'); - const { startBlock, chainId, l1RpcUrl, l1RollupAddress, batchSize, nodeUrl, provingTimeout, endBlock, rawLogs, log } = - opts; + const { + startBlock, + chainId, + l1RpcUrls, + l1RollupAddress, + batchSize, + nodeUrl, + provingTimeout, + endBlock, + rawLogs, + log, + } = opts; if (!l1RollupAddress && !nodeUrl) { throw new Error('Either L1 rollup address or node URL must be set'); } @@ -35,8 +45,8 @@ export async function proverStats(opts: { .getL1ContractAddresses() .then(a => a.rollupAddress); - const chain = createEthereumChain(l1RpcUrl, chainId).chainInfo; - const publicClient = createPublicClient({ chain, transport: http(l1RpcUrl) }); + const chain = createEthereumChain(l1RpcUrls, chainId).chainInfo; + const publicClient = createPublicClient({ chain, transport: fallback(l1RpcUrls.map(url => http(url))) }); const lastBlockNum = endBlock ?? (await publicClient.getBlockNumber()); debugLog.verbose(`Querying events on rollup at ${rollup.toString()} from ${startBlock} up to ${lastBlockNum}`); @@ -146,7 +156,7 @@ async function getL2ProofVerifiedEvents( lastBlockNum: bigint, batchSize: bigint, debugLog: Logger, - publicClient: PublicClient, + publicClient: ViemPublicClient, rollup: EthAddress, ) { let blockNum = startBlock; @@ -166,7 +176,7 @@ async function getL2BlockEvents( lastBlockNum: bigint, batchSize: bigint, debugLog: Logger, - publicClient: PublicClient, + publicClient: ViemPublicClient, rollup: EthAddress, ) { let blockNum = startBlock; diff --git a/yarn-project/cli/src/cmds/l1/update_l1_validators.ts b/yarn-project/cli/src/cmds/l1/update_l1_validators.ts index bafb2ef5e0d9..ad1852a26d60 100644 --- a/yarn-project/cli/src/cmds/l1/update_l1_validators.ts +++ b/yarn-project/cli/src/cmds/l1/update_l1_validators.ts @@ -9,11 +9,11 @@ import type { EthAddress } from '@aztec/foundation/eth-address'; import type { LogFn, Logger } from '@aztec/foundation/log'; import { ForwarderAbi, ForwarderBytecode, RollupAbi, TestERC20Abi } from '@aztec/l1-artifacts'; -import { createPublicClient, createWalletClient, getContract, http } from 'viem'; +import { createPublicClient, createWalletClient, fallback, getContract, http } from 'viem'; import { generatePrivateKey, mnemonicToAccount, privateKeyToAccount } from 'viem/accounts'; export interface RollupCommandArgs { - rpcUrl: string; + rpcUrls: string[]; chainId: number; privateKey?: string; mnemonic?: string; @@ -37,7 +37,7 @@ export function generateL1Account() { } export async function addL1Validator({ - rpcUrl, + rpcUrls, chainId, privateKey, mnemonic, @@ -49,8 +49,8 @@ export async function addL1Validator({ }: RollupCommandArgs & LoggerArgs & { validatorAddress: EthAddress }) { const config = getL1ContractsConfigEnvVars(); const dualLog = makeDualLog(log, debugLogger); - const publicClient = getPublicClient(rpcUrl, chainId); - const walletClient = getWalletClient(rpcUrl, chainId, privateKey, mnemonic); + const publicClient = getPublicClient(rpcUrls, chainId); + const walletClient = getWalletClient(rpcUrls, chainId, privateKey, mnemonic); const rollup = getContract({ address: rollupAddress.toString(), abi: RollupAbi, @@ -83,7 +83,7 @@ export async function addL1Validator({ await publicClient.waitForTransactionReceipt({ hash: txHash }); if (isAnvilTestChain(chainId)) { dualLog(`Funding validator on L1`); - const cheatCodes = new EthCheatCodes(rpcUrl, debugLogger); + const cheatCodes = new EthCheatCodes(rpcUrls, debugLogger); await cheatCodes.setBalance(validatorAddress, 10n ** 20n); } else { const balance = await publicClient.getBalance({ address: validatorAddress.toString() }); @@ -96,7 +96,7 @@ export async function addL1Validator({ } export async function removeL1Validator({ - rpcUrl, + rpcUrls, chainId, privateKey, mnemonic, @@ -106,8 +106,8 @@ export async function removeL1Validator({ debugLogger, }: RollupCommandArgs & LoggerArgs & { validatorAddress: EthAddress }) { const dualLog = makeDualLog(log, debugLogger); - const publicClient = getPublicClient(rpcUrl, chainId); - const walletClient = getWalletClient(rpcUrl, chainId, privateKey, mnemonic); + const publicClient = getPublicClient(rpcUrls, chainId); + const walletClient = getWalletClient(rpcUrls, chainId, privateKey, mnemonic); const rollup = getContract({ address: rollupAddress.toString(), abi: RollupAbi, @@ -121,7 +121,7 @@ export async function removeL1Validator({ } export async function pruneRollup({ - rpcUrl, + rpcUrls, chainId, privateKey, mnemonic, @@ -130,8 +130,8 @@ export async function pruneRollup({ debugLogger, }: RollupCommandArgs & LoggerArgs) { const dualLog = makeDualLog(log, debugLogger); - const publicClient = getPublicClient(rpcUrl, chainId); - const walletClient = getWalletClient(rpcUrl, chainId, privateKey, mnemonic); + const publicClient = getPublicClient(rpcUrls, chainId); + const walletClient = getWalletClient(rpcUrls, chainId, privateKey, mnemonic); const rollup = getContract({ address: rollupAddress.toString(), abi: RollupAbi, @@ -145,7 +145,7 @@ export async function pruneRollup({ } export async function fastForwardEpochs({ - rpcUrl, + rpcUrls, chainId, rollupAddress, numEpochs, @@ -153,14 +153,14 @@ export async function fastForwardEpochs({ debugLogger, }: RollupCommandArgs & LoggerArgs & { numEpochs: bigint }) { const dualLog = makeDualLog(log, debugLogger); - const publicClient = getPublicClient(rpcUrl, chainId); + const publicClient = getPublicClient(rpcUrls, chainId); const rollup = getContract({ address: rollupAddress.toString(), abi: RollupAbi, client: publicClient, }); - const cheatCodes = new EthCheatCodes(rpcUrl, debugLogger); + const cheatCodes = new EthCheatCodes(rpcUrls, debugLogger); const currentSlot = await rollup.read.getCurrentSlot(); const l2SlotsInEpoch = await rollup.read.getEpochDuration(); const timestamp = await rollup.read.getTimestampForSlot([currentSlot + l2SlotsInEpoch * numEpochs]); @@ -178,9 +178,9 @@ export async function fastForwardEpochs({ } } -export async function debugRollup({ rpcUrl, chainId, rollupAddress, log }: RollupCommandArgs & LoggerArgs) { +export async function debugRollup({ rpcUrls, chainId, rollupAddress, log }: RollupCommandArgs & LoggerArgs) { const config = getL1ContractsConfigEnvVars(); - const publicClient = getPublicClient(rpcUrl, chainId); + const publicClient = getPublicClient(rpcUrls, chainId); const rollup = getContract({ address: rollupAddress.toString(), abi: RollupAbi, @@ -215,13 +215,13 @@ function makeDualLog(log: LogFn, debugLogger: Logger) { }; } -function getPublicClient(rpcUrl: string, chainId: number) { - const chain = createEthereumChain(rpcUrl, chainId); - return createPublicClient({ chain: chain.chainInfo, transport: http(rpcUrl) }); +function getPublicClient(rpcUrls: string[], chainId: number) { + const chain = createEthereumChain(rpcUrls, chainId); + return createPublicClient({ chain: chain.chainInfo, transport: fallback(rpcUrls.map(url => http(url))) }); } function getWalletClient( - rpcUrl: string, + rpcUrls: string[], chainId: number, privateKey: string | undefined, mnemonic: string | undefined, @@ -230,9 +230,9 @@ function getWalletClient( throw new Error('Either privateKey or mnemonic must be provided to create a wallet client'); } - const chain = createEthereumChain(rpcUrl, chainId); + const chain = createEthereumChain(rpcUrls, chainId); const account = !privateKey ? mnemonicToAccount(mnemonic!) : privateKeyToAccount(`${privateKey.startsWith('0x') ? '' : '0x'}${privateKey}` as `0x${string}`); - return createWalletClient({ account, chain: chain.chainInfo, transport: http(rpcUrl) }); + return createWalletClient({ account, chain: chain.chainInfo, transport: fallback(rpcUrls.map(url => http(url))) }); } diff --git a/yarn-project/cli/src/utils/aztec.ts b/yarn-project/cli/src/utils/aztec.ts index 144452470712..f7146de88f8a 100644 --- a/yarn-project/cli/src/utils/aztec.ts +++ b/yarn-project/cli/src/utils/aztec.ts @@ -1,5 +1,5 @@ import { type ContractArtifact, type FunctionArtifact, loadContractArtifact } from '@aztec/aztec.js/abi'; -import type { DeployL1Contracts, L1ContractsConfig } from '@aztec/ethereum'; +import type { DeployL1ContractsReturnType, L1ContractsConfig } from '@aztec/ethereum'; import type { EthAddress } from '@aztec/foundation/eth-address'; import type { Fr } from '@aztec/foundation/fields'; import type { LogFn, Logger } from '@aztec/foundation/log'; @@ -30,13 +30,13 @@ export function getFunctionArtifact(artifact: ContractArtifact, fnName: string): /** * Function to execute the 'deployRollupContracts' command. - * @param rpcUrl - The RPC URL of the ethereum node. + * @param rpcUrls - The RPC URL of the ethereum node. * @param chainId - The chain ID of the L1 host. * @param privateKey - The private key to be used in contract deployment. * @param mnemonic - The mnemonic to be used in contract deployment. */ export async function deployAztecContracts( - rpcUrl: string, + rpcUrls: string[], chainId: number, privateKey: string | undefined, mnemonic: string, @@ -47,19 +47,19 @@ export async function deployAztecContracts( genesisBlockHash: Fr, config: L1ContractsConfig, debugLogger: Logger, -): Promise { +): Promise { const { createEthereumChain, deployL1Contracts } = await import('@aztec/ethereum'); const { mnemonicToAccount, privateKeyToAccount } = await import('viem/accounts'); const account = !privateKey ? mnemonicToAccount(mnemonic!, { addressIndex: mnemonicIndex }) : privateKeyToAccount(`${privateKey.startsWith('0x') ? '' : '0x'}${privateKey}` as `0x${string}`); - const chain = createEthereumChain(rpcUrl, chainId); + const chain = createEthereumChain(rpcUrls, chainId); const { getVKTreeRoot } = await import('@aztec/noir-protocol-circuits-types/vks'); return await deployL1Contracts( - chain.rpcUrl, + chain.rpcUrls, account, chain.chainInfo, debugLogger, diff --git a/yarn-project/cli/src/utils/commands.ts b/yarn-project/cli/src/utils/commands.ts index 3cac57ef7557..ebbe95895539 100644 --- a/yarn-project/cli/src/utils/commands.ts +++ b/yarn-project/cli/src/utils/commands.ts @@ -22,7 +22,7 @@ export const getLocalhost = () => .catch(() => 'localhost'); export const LOCALHOST = await getLocalhost(); -export const { ETHEREUM_HOST = `http://${LOCALHOST}:8545`, PRIVATE_KEY, API_KEY, CLI_VERSION } = process.env; +export const { ETHEREUM_HOSTS = `http://${LOCALHOST}:8545`, PRIVATE_KEY, API_KEY, CLI_VERSION } = process.env; export function addOptions(program: Command, options: Option[]) { options.forEach(option => program.addOption(option)); diff --git a/yarn-project/end-to-end/scripts/docker-compose-devnet.yml b/yarn-project/end-to-end/scripts/docker-compose-devnet.yml index 20c6f61b7548..e85f4b5c3354 100644 --- a/yarn-project/end-to-end/scripts/docker-compose-devnet.yml +++ b/yarn-project/end-to-end/scripts/docker-compose-devnet.yml @@ -3,16 +3,16 @@ services: end-to-end: image: aztecprotocol/end-to-end:${AZTEC_DOCKER_TAG:-latest} secrets: - - ethereum-host + - ethereum-hosts - aztec-node-url - faucet-url environment: LOG_LEVEL: ${LOG_LEVEL:-verbose} - ETHEREUM_HOST: + ETHEREUM_HOSTS: JOB_NAME: ${JOB_NAME:-''} PXE_PROVER_ENABLED: ${PXE_PROVER_ENABLED:-1} command: | - export ETHEREUM_HOST=$$(cat /var/run/secrets/ethereum-host) + export ETHEREUM_HOSTS=$$(cat /var/run/secrets/ethereum-hosts) export FAUCET_URL=$$(cat /var/run/secrets/faucet-url) export AZTEC_NODE_URL=$$(cat /var/run/secrets/aztec-node-url) ${TEST:-./src/devnet/e2e_smoke.test.ts} @@ -22,7 +22,7 @@ services: secrets: aztec-node-url: environment: AZTEC_NODE_URL - ethereum-host: - environment: ETHEREUM_HOST + ethereum-hosts: + environment: ETHEREUM_HOSTS faucet-url: environment: FAUCET_URL diff --git a/yarn-project/end-to-end/scripts/docker-compose-images.yml b/yarn-project/end-to-end/scripts/docker-compose-images.yml index e1611ab215cd..58aab0800527 100644 --- a/yarn-project/end-to-end/scripts/docker-compose-images.yml +++ b/yarn-project/end-to-end/scripts/docker-compose-images.yml @@ -12,7 +12,7 @@ services: command: 'start --sandbox' environment: LOG_LEVEL: ${LOG_LEVEL:-'verbose'} - ETHEREUM_HOST: http://fork:8545 + ETHEREUM_HOSTS: http://fork:8545 L1_CHAIN_ID: 31337 FORCE_COLOR: ${FORCE_COLOR:-1} ARCHIVER_POLLING_INTERVAL_MS: 500 @@ -29,7 +29,7 @@ services: environment: BENCHMARK: 'true' LOG_LEVEL: ${LOG_LEVEL:-'verbose'} - ETHEREUM_HOST: http://fork:8545 + ETHEREUM_HOSTS: http://fork:8545 L1_CHAIN_ID: 31337 FORCE_COLOR: ${FORCE_COLOR:-1} PXE_URL: http://sandbox:8080 diff --git a/yarn-project/end-to-end/scripts/docker-compose-no-sandbox.yml b/yarn-project/end-to-end/scripts/docker-compose-no-sandbox.yml index 349236cba398..c26326b8af37 100644 --- a/yarn-project/end-to-end/scripts/docker-compose-no-sandbox.yml +++ b/yarn-project/end-to-end/scripts/docker-compose-no-sandbox.yml @@ -12,7 +12,7 @@ services: environment: BENCHMARK: 'true' LOG_LEVEL: ${LOG_LEVEL:-verbose} - ETHEREUM_HOST: http://fork:8545 + ETHEREUM_HOSTS: http://fork:8545 L1_CHAIN_ID: 31337 FORCE_COLOR: ${FORCE_COLOR:-1} ARCHIVER_POLLING_INTERVAL_MS: 50 diff --git a/yarn-project/end-to-end/scripts/docker-compose-p2p.yml b/yarn-project/end-to-end/scripts/docker-compose-p2p.yml index d49b4fa0d387..e697ec3d7a8d 100644 --- a/yarn-project/end-to-end/scripts/docker-compose-p2p.yml +++ b/yarn-project/end-to-end/scripts/docker-compose-p2p.yml @@ -27,7 +27,7 @@ services: BENCHMARK: true LOG_LEVEL: ${LOG_LEVEL:-verbose} DEBUG: ${DEBUG:-""} - ETHEREUM_HOST: http://fork:8545 + ETHEREUM_HOSTS: http://fork:8545 L1_CHAIN_ID: 31337 FORCE_COLOR: ${FORCE_COLOR:-1} ARCHIVER_POLLING_INTERVAL_MS: 500 diff --git a/yarn-project/end-to-end/scripts/docker-compose-wallet.yml b/yarn-project/end-to-end/scripts/docker-compose-wallet.yml index 2f5bdbc2a9d2..19e49feb5b79 100644 --- a/yarn-project/end-to-end/scripts/docker-compose-wallet.yml +++ b/yarn-project/end-to-end/scripts/docker-compose-wallet.yml @@ -12,7 +12,7 @@ services: command: 'start --sandbox' environment: LOG_LEVEL: ${LOG_LEVEL:-verbose} - ETHEREUM_HOST: http://fork:8545 + ETHEREUM_HOSTS: http://fork:8545 L1_CHAIN_ID: 31337 FORCE_COLOR: ${FORCE_COLOR:-1} ARCHIVER_POLLING_INTERVAL_MS: 50 @@ -28,7 +28,7 @@ services: image: aztecprotocol/end-to-end:${AZTEC_DOCKER_TAG:-latest} environment: LOG_LEVEL: ${LOG_LEVEL:-verbose} - ETHEREUM_HOST: http://fork:8545 + ETHEREUM_HOSTS: http://fork:8545 L1_CHAIN_ID: 31337 FORCE_COLOR: ${FORCE_COLOR:-1} PXE_URL: http://sandbox:8080 diff --git a/yarn-project/end-to-end/scripts/docker-compose.yml b/yarn-project/end-to-end/scripts/docker-compose.yml index 52def77ebea8..66981af22877 100644 --- a/yarn-project/end-to-end/scripts/docker-compose.yml +++ b/yarn-project/end-to-end/scripts/docker-compose.yml @@ -16,7 +16,7 @@ services: command: 'node ./dest/bin start --sandbox' environment: LOG_LEVEL: ${LOG_LEVEL:-verbose} - ETHEREUM_HOST: http://fork:8545 + ETHEREUM_HOSTS: http://fork:8545 L1_CHAIN_ID: 31337 FORCE_COLOR: ${FORCE_COLOR:-1} ARCHIVER_POLLING_INTERVAL_MS: 500 @@ -26,7 +26,6 @@ services: ARCHIVER_VIEM_POLLING_INTERVAL_MS: 500 HARDWARE_CONCURRENCY: ${HARDWARE_CONCURRENCY:-} - end-to-end: image: aztecprotocol/build:3.0 cpus: 4 @@ -42,7 +41,7 @@ services: JEST_CACHE_DIR: /tmp-jest BENCHMARK: 'true' LOG_LEVEL: ${LOG_LEVEL:-verbose} - ETHEREUM_HOST: http://fork:8545 + ETHEREUM_HOSTS: http://fork:8545 L1_CHAIN_ID: 31337 FORCE_COLOR: ${FORCE_COLOR:-1} PXE_URL: http://sandbox:8080 diff --git a/yarn-project/end-to-end/scripts/e2e_test_public_testnet.sh b/yarn-project/end-to-end/scripts/e2e_test_public_testnet.sh index 73c95dd5ef8b..d06b065cd3ad 100755 --- a/yarn-project/end-to-end/scripts/e2e_test_public_testnet.sh +++ b/yarn-project/end-to-end/scripts/e2e_test_public_testnet.sh @@ -4,7 +4,7 @@ # Optional environment variables: # SEQ_PUBLISHER_PRIVATE_KEY # PROVER_PUBLISHER_PRIVATE_KEY -# ETHEREUM_HOST +# ETHEREUM_HOSTS # HARDWARE_CONCURRENCY (default: "") # ALLOW_FAIL (default: false) # L1_CHAIN_ID (default: "31337") @@ -30,7 +30,7 @@ fi # Run the test docker run \ -e L1_CHAIN_ID="$L1_CHAIN_ID" \ - -e ETHEREUM_HOST="${ETHEREUM_HOST:-}" \ + -e ETHEREUM_HOSTS="${ETHEREUM_HOSTS:-}" \ -e FORCE_COLOR="${FORCE_COLOR:-1}" \ -e SEQ_PUBLISHER_PRIVATE_KEY="${SEQ_PUBLISHER_PRIVATE_KEY:-}" \ -e PROVER_PUBLISHER_PRIVATE_KEY="${PROVER_PUBLISHER_PRIVATE_KEY:-}" \ diff --git a/yarn-project/end-to-end/scripts/native-network/boot-node.sh b/yarn-project/end-to-end/scripts/native-network/boot-node.sh index 3f20e30172d4..0336db694b5f 100755 --- a/yarn-project/end-to-end/scripts/native-network/boot-node.sh +++ b/yarn-project/end-to-end/scripts/native-network/boot-node.sh @@ -9,14 +9,13 @@ SCRIPT_NAME=$(basename "$0" .sh) # Redirect stdout and stderr to .log while also printing to the console exec > >(tee -a "$(dirname $0)/logs/${SCRIPT_NAME}.log") 2> >(tee -a "$(dirname $0)/logs/${SCRIPT_NAME}.log" >&2) - # Starts the Boot Node # Set environment variables export PORT=${PORT:-"8080"} export DEBUG=${DEBUG:-""} export LOG_LEVEL=${LOG_LEVEL:-"verbose"} -export ETHEREUM_HOST=${ETHEREUM_HOST:-"http://127.0.0.1:8545"} +export ETHEREUM_HOSTS=${ETHEREUM_HOSTS:-"http://127.0.0.1:8545"} export L1_CONSENSUS_HOST_URL=${L1_CONSENSUS_HOST_URL:-} export P2P_ENABLED="true" export VALIDATOR_DISABLED="true" @@ -34,7 +33,6 @@ export OTEL_RESOURCE_ATTRIBUTES="service.name=boot-node" export VALIDATOR_PRIVATE_KEY=${VALIDATOR_PRIVATE_KEY:-"0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a"} export TEST_ACCOUNTS="true" - echo "Waiting for l1 contracts to be deployed..." until [ -f "$REPO"/yarn-project/end-to-end/scripts/native-network/state/l1-contracts.env ]; do sleep 1 diff --git a/yarn-project/end-to-end/scripts/native-network/deploy-l1-contracts.sh b/yarn-project/end-to-end/scripts/native-network/deploy-l1-contracts.sh index 03f9b7d7353e..7a19fb90858c 100755 --- a/yarn-project/end-to-end/scripts/native-network/deploy-l1-contracts.sh +++ b/yarn-project/end-to-end/scripts/native-network/deploy-l1-contracts.sh @@ -21,26 +21,34 @@ else INIT_VALIDATORS="false" fi -export ETHEREUM_HOST=${ETHEREUM_HOST:-"http://127.0.0.1:8545"} +export ETHEREUM_HOSTS=${ETHEREUM_HOSTS:-"http://127.0.0.1:8545"} # Remove hardcoded L1_CHAIN_ID and fetch it from the node export PRIVATE_KEY=${PRIVATE_KEY:-""} export SALT=${SALT:-"1337"} echo "Waiting for Ethereum node to be up..." -until curl -s -X POST -H 'Content-Type: application/json' \ - --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \ - $ETHEREUM_HOST 2>/dev/null | grep -q 'result'; do - sleep 1 +while true; do + for HOST in $(echo "${ETHEREUM_HOSTS}" | tr ',' '\n'); do + if curl -s -X POST -H 'Content-Type: application/json' \ + --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \ + "$HOST" 2>/dev/null | grep -q 'result'; then + echo "Node $HOST is ready" + break 2 + fi + echo "Waiting for Ethereum node ${HOST}..." + done + sleep 5 done echo "Done waiting." +echo "Fetching chain ID from the Ethereum node" # Fetch chain ID from the Ethereum node source "$REPO"/yarn-project/end-to-end/scripts/native-network/utils/get-chain-id.sh # Construct base command COMMAND="node --no-warnings $(git rev-parse --show-toplevel)/yarn-project/aztec/dest/bin/index.js \ deploy-l1-contracts \ - --rpc-url $ETHEREUM_HOST \ + --l1-rpc-urls $ETHEREUM_HOSTS \ --l1-chain-id $L1_CHAIN_ID \ --salt $SALT \ --test-accounts" diff --git a/yarn-project/end-to-end/scripts/native-network/deploy-l2-contracts.sh b/yarn-project/end-to-end/scripts/native-network/deploy-l2-contracts.sh index d2e65e592a73..cbeba7f8fa8c 100755 --- a/yarn-project/end-to-end/scripts/native-network/deploy-l2-contracts.sh +++ b/yarn-project/end-to-end/scripts/native-network/deploy-l2-contracts.sh @@ -20,6 +20,11 @@ until curl -s -X POST -H 'content-type: application/json' \ done echo "Done waiting." +# Get the chain ID from the Ethereum node +export ETHEREUM_HOSTS=${ETHEREUM_HOSTS:-"http://127.0.0.1:8545"} +source "$REPO"/yarn-project/end-to-end/scripts/native-network/utils/get-chain-id.sh +export L1_CHAIN_ID=${L1_CHAIN_ID:-31337} + # TODO(AD): Add option for prover-enabled mode ARGS="--skipProofWait --testAccounts" @@ -29,5 +34,5 @@ export PXE_URL="http://127.0.0.1:8079" node --no-warnings "$REPO"/yarn-project/aztec/dest/bin/index.js setup-protocol-contracts $ARGS echo "Deployed L2 contracts" # Use file just as done signal -echo "" > state/l2-contracts.env +echo "" >state/l2-contracts.env echo "Wrote to state/l2-contracts.env to signal completion" diff --git a/yarn-project/end-to-end/scripts/native-network/prover-node.sh b/yarn-project/end-to-end/scripts/native-network/prover-node.sh index 999511df5acb..f987ab4dfaef 100755 --- a/yarn-project/end-to-end/scripts/native-network/prover-node.sh +++ b/yarn-project/end-to-end/scripts/native-network/prover-node.sh @@ -19,7 +19,7 @@ until [ -f "$REPO"/yarn-project/end-to-end/scripts/native-network/state/l1-contr done # Get the chain ID from the Ethereum node -export ETHEREUM_HOST=${ETHEREUM_HOST:-"http://127.0.0.1:8545"} +export ETHEREUM_HOSTS=${ETHEREUM_HOSTS:-"http://127.0.0.1:8545"} source "$REPO"/yarn-project/end-to-end/scripts/native-network/utils/get-chain-id.sh echo "Waiting for Aztec Node..." diff --git a/yarn-project/end-to-end/scripts/native-network/pxe.sh b/yarn-project/end-to-end/scripts/native-network/pxe.sh index ce4992a91c07..17b77409ed02 100755 --- a/yarn-project/end-to-end/scripts/native-network/pxe.sh +++ b/yarn-project/end-to-end/scripts/native-network/pxe.sh @@ -10,7 +10,7 @@ exec > >(tee -a "$(dirname $0)/logs/${SCRIPT_NAME}.log") 2> >(tee -a "$(dirname # Starts the PXE (Private eXecution Environment) service # Set environment variables -export ETHEREUM_HOST=${ETHEREUM_HOST:-"http://127.0.0.1:8545"} +export ETHEREUM_HOSTS=${ETHEREUM_HOSTS:-"http://127.0.0.1:8545"} export L1_CONSENSUS_HOST_URL=${L1_CONSENSUS_HOST_URL:-} export AZTEC_NODE_URL=${AZTEC_NODE_URL:-"http://127.0.0.1:8080"} export VALIDATOR_NODE_URL=${VALIDATOR_NODE_URL:-"http://127.0.0.1:8081"} diff --git a/yarn-project/end-to-end/scripts/native-network/test-4epochs.sh b/yarn-project/end-to-end/scripts/native-network/test-4epochs.sh index 3e00718517c3..954159565c2e 100755 --- a/yarn-project/end-to-end/scripts/native-network/test-4epochs.sh +++ b/yarn-project/end-to-end/scripts/native-network/test-4epochs.sh @@ -10,13 +10,13 @@ exec > >(tee -a "$(dirname $0)/logs/${SCRIPT_NAME}.log") 2> >(tee -a "$(dirname export BOOTNODE_URL=${BOOTNODE_URL:-http://127.0.0.1:8080} export PXE_URL=${PXE_URL:-http://127.0.0.1:8079} -export ETHEREUM_HOST=${ETHEREUM_HOST:-http://127.0.0.1:8545} +export ETHEREUM_HOSTS=${ETHEREUM_HOSTS:-http://127.0.0.1:8545} REPO=$(git rev-parse --show-toplevel) # Run our test assuming the port in pxe.sh # Wait for the Aztec Node to be ready echo "Waiting for Aztec Node..." -until curl -s $BOOTNODE_URL/status >/dev/null ; do +until curl -s $BOOTNODE_URL/status >/dev/null; do sleep 1 done echo "Waiting for PXE service..." @@ -26,11 +26,11 @@ until curl -s -X POST -H 'content-type: application/json' \ sleep 1 done echo "Waiting for l2 contracts to be deployed..." -until [ -f "$REPO"/yarn-project/end-to-end/scripts/native-network/state/l2-contracts.env ] ; do +until [ -f "$REPO"/yarn-project/end-to-end/scripts/native-network/state/l2-contracts.env ]; do sleep 1 done echo "Done waiting." export LOG_LEVEL=${LOG_LEVEL:-"verbose"} cd $(git rev-parse --show-toplevel)/yarn-project/end-to-end -yarn test src/spartan/4epochs.test.ts \ No newline at end of file +yarn test src/spartan/4epochs.test.ts diff --git a/yarn-project/end-to-end/scripts/native-network/test-transfer.sh b/yarn-project/end-to-end/scripts/native-network/test-transfer.sh index 1b91130664b4..87a0193cec41 100755 --- a/yarn-project/end-to-end/scripts/native-network/test-transfer.sh +++ b/yarn-project/end-to-end/scripts/native-network/test-transfer.sh @@ -11,14 +11,14 @@ exec > >(tee -a "$(dirname $0)/logs/${SCRIPT_NAME}.log") 2> >(tee -a "$(dirname export BOOTNODE_URL=${BOOTNODE_URL:-http://127.0.0.1:8080} export NODE_URL=${NODE_URL:-${BOOTNODE_URL:-http://127.0.0.1:8080}} export PXE_URL=${PXE_URL:-http://127.0.0.1:8079} -export ETHEREUM_HOST=${ETHEREUM_HOST:-http://127.0.0.1:8545} +export ETHEREUM_HOSTS=${ETHEREUM_HOSTS:-http://127.0.0.1:8545} export K8S=${K8S:-false} REPO=$(git rev-parse --show-toplevel) # Run our test assuming the port in pxe.sh # Wait for the Aztec Node to be ready echo "Waiting for Aztec Node..." -until curl -s $BOOTNODE_URL/status >/dev/null ; do +until curl -s $BOOTNODE_URL/status >/dev/null; do sleep 1 done echo "Waiting for PXE service..." @@ -28,11 +28,11 @@ until curl -s -X POST -H 'content-type: application/json' \ sleep 1 done echo "Waiting for l2 contracts to be deployed..." -until [ -f "$REPO"/yarn-project/end-to-end/scripts/native-network/state/l2-contracts.env ] ; do +until [ -f "$REPO"/yarn-project/end-to-end/scripts/native-network/state/l2-contracts.env ]; do sleep 1 done echo "Done waiting." export LOG_LEVEL=${LOG_LEVEL:-"verbose"} cd $(git rev-parse --show-toplevel)/yarn-project/end-to-end -yarn test src/spartan/transfer.test.ts \ No newline at end of file +yarn test src/spartan/transfer.test.ts diff --git a/yarn-project/end-to-end/scripts/native-network/transaction-bot.sh b/yarn-project/end-to-end/scripts/native-network/transaction-bot.sh index 5f40ffd193bd..4163c464ce7d 100755 --- a/yarn-project/end-to-end/scripts/native-network/transaction-bot.sh +++ b/yarn-project/end-to-end/scripts/native-network/transaction-bot.sh @@ -31,7 +31,7 @@ done echo "Done waiting." # Set environment variables -export ETHEREUM_HOST=${ETHEREUM_HOST:-"http://127.0.0.1:8545"} +export ETHEREUM_HOSTS=${ETHEREUM_HOSTS:-"http://127.0.0.1:8545"} export AZTEC_NODE_URL=${AZTEC_NODE_URL:-"http://127.0.0.1:8080"} export LOG_LEVEL=${LOG_LEVEL:-"verbose"} export BOT_TX_INTERVAL_SECONDS="5" diff --git a/yarn-project/end-to-end/scripts/native-network/utils/get-chain-id.sh b/yarn-project/end-to-end/scripts/native-network/utils/get-chain-id.sh index 13ee498f5212..b5e7f198190d 100755 --- a/yarn-project/end-to-end/scripts/native-network/utils/get-chain-id.sh +++ b/yarn-project/end-to-end/scripts/native-network/utils/get-chain-id.sh @@ -1,6 +1,13 @@ +for HOST in $(echo "${ETHEREUM_HOSTS}" | tr ',' '\n'); do + RESULT=$(curl -s -X POST -H "Content-Type: application/json" \ + --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' \ + "$HOST" 2>/dev/null) || continue + if echo "$RESULT" | grep -q '"result":"0x'; then + export L1_CHAIN_ID=$(echo "$RESULT" | grep -o '"result":"0x[^"]*"' | cut -d'"' -f4 | xargs printf "%d\n") + echo "Using L1 chain ID: $L1_CHAIN_ID from $HOST" + return 0 + fi +done -export L1_CHAIN_ID=$(curl -s -X POST -H "Content-Type: application/json" \ - --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' \ - $ETHEREUM_HOST | grep -o '"result":"0x[^"]*"' | cut -d'"' -f4 | xargs printf "%d\n") - -echo "Using L1 chain ID: $L1_CHAIN_ID" \ No newline at end of file +echo "Error: Could not get chain ID from any host in: $ETHEREUM_HOSTS" +return 1 diff --git a/yarn-project/end-to-end/scripts/native-network/validator.sh b/yarn-project/end-to-end/scripts/native-network/validator.sh index adb98ab5931f..a23c78d96347 100755 --- a/yarn-project/end-to-end/scripts/native-network/validator.sh +++ b/yarn-project/end-to-end/scripts/native-network/validator.sh @@ -30,7 +30,7 @@ done echo "Done waiting." # Ethereum host required for the chain id script -export ETHEREUM_HOST=${ETHEREUM_HOST:-"http://127.0.0.1:8545"} +export ETHEREUM_HOSTS=${ETHEREUM_HOSTS:-"http://127.0.0.1:8545"} # Get the chain ID from the Ethereum node source "$REPO"/yarn-project/end-to-end/scripts/native-network/utils/get-chain-id.sh @@ -59,16 +59,6 @@ export SEQ_PUBLISHER_PRIVATE_KEY=$VALIDATOR_PRIVATE_KEY export DEBUG=${DEBUG:-""} export LOG_LEVEL=${LOG_LEVEL:-"verbose"} export L1_CONSENSUS_HOST_URL=${L1_CONSENSUS_HOST_URL:-} - - - -# Automatically detect if we're using Anvil -if curl -s -H "Content-Type: application/json" -X POST --data '{"method":"web3_clientVersion","params":[],"id":49,"jsonrpc":"2.0"}' $ETHEREUM_HOST | jq .result | grep -q anvil; then - IS_ANVIL="true" -else - IS_ANVIL="false" -fi - export P2P_ENABLED="true" export VALIDATOR_DISABLED="false" export SEQ_MAX_SECONDS_BETWEEN_BLOCKS="0" diff --git a/yarn-project/end-to-end/src/composed/e2e_persistence.test.ts b/yarn-project/end-to-end/src/composed/e2e_persistence.test.ts index cbe98f55a297..1f98c24eae29 100644 --- a/yarn-project/end-to-end/src/composed/e2e_persistence.test.ts +++ b/yarn-project/end-to-end/src/composed/e2e_persistence.test.ts @@ -2,7 +2,7 @@ import { getSchnorrAccount, getSchnorrWallet } from '@aztec/accounts/schnorr'; import { type InitialAccountData, deployFundedSchnorrAccount } from '@aztec/accounts/testing'; import { type AccountWallet, type ContractInstanceWithAddress, type TxHash, computeSecretHash } from '@aztec/aztec.js'; import { MAX_NOTE_HASHES_PER_TX } from '@aztec/constants'; -import type { DeployL1Contracts } from '@aztec/ethereum'; +import type { DeployL1ContractsReturnType } from '@aztec/ethereum'; import { Fr } from '@aztec/foundation/fields'; // We use TokenBlacklist because we want to test the persistence of manually added notes and standard token no longer // implements TransparentNote shield flow. @@ -46,7 +46,7 @@ describe('Aztec persistence', () => { let dataDirectory: string; // state that is persisted between tests - let deployL1ContractsValues: DeployL1Contracts; + let deployL1ContractsValues: DeployL1ContractsReturnType; let context: EndToEndContext; diff --git a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts index 5100d5c3e96f..d2185c4124fd 100644 --- a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts +++ b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts @@ -9,6 +9,8 @@ import { type L1ContractAddresses, RollupContract, SlashingProposerContract, + type ViemPublicClient, + type ViemWalletClient, createEthereumChain, createL1Clients, } from '@aztec/ethereum'; @@ -41,13 +43,8 @@ import { beforeEach, describe, expect, it, jest } from '@jest/globals'; import { writeFile } from 'fs/promises'; import { type MockProxy, mock } from 'jest-mock-extended'; import { - type Account, type Address, - type Chain, type GetContractReturnType, - type HttpTransport, - type PublicClient, - type WalletClient, encodeFunctionData, getAbiItem, getAddress, @@ -66,7 +63,7 @@ const deployerPK = '0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092 const logger = createLogger('integration_l1_publisher'); const config = getConfigEnvVars(); -config.l1RpcUrl = config.l1RpcUrl || 'http://127.0.0.1:8545'; +config.l1RpcUrls = config.l1RpcUrls || ['http://127.0.0.1:8545']; const numberOfConsecutiveBlocks = 2; @@ -76,16 +73,16 @@ const BLOB_SINK_URL = `http://localhost:${BLOB_SINK_PORT}`; jest.setTimeout(1000000); describe('L1Publisher integration', () => { - let publicClient: PublicClient; - let walletClient: WalletClient; + let publicClient: ViemPublicClient; + let walletClient: ViemWalletClient; let l1ContractAddresses: L1ContractAddresses; let deployerAccount: PrivateKeyAccount; let rollupAddress: Address; let outboxAddress: Address; - let rollup: GetContractReturnType>; - let outbox: GetContractReturnType>; + let rollup: GetContractReturnType; + let outbox: GetContractReturnType; let publisher: SequencerPublisher; @@ -99,7 +96,7 @@ describe('L1Publisher integration', () => { let blockSource: MockProxy; let blocks: L2Block[] = []; - const chainId = createEthereumChain(config.l1RpcUrl, config.l1ChainId).chainInfo.id; + const chainId = createEthereumChain(config.l1RpcUrls, config.l1ChainId).chainInfo.id; let coinbase: EthAddress; let feeRecipient: AztecAddress; @@ -109,7 +106,7 @@ describe('L1Publisher integration', () => { // To update the test data, run "export AZTEC_GENERATE_TEST_DATA=1" in shell and run the tests again // If you have issues with RPC_URL, it is likely that you need to set the RPC_URL in the shell as well - // If running ANVIL locally, you can use ETHEREUM_HOST="http://0.0.0.0:8545" + // If running ANVIL locally, you can use ETHEREUM_HOSTS="http://0.0.0.0:8545" const AZTEC_GENERATE_TEST_DATA = !!process.env.AZTEC_GENERATE_TEST_DATA; const progressTimeBySlot = async (slotsToJump = 1n) => { @@ -124,13 +121,13 @@ describe('L1Publisher integration', () => { beforeEach(async () => { deployerAccount = privateKeyToAccount(deployerPK); ({ l1ContractAddresses, publicClient, walletClient } = await setupL1Contracts( - config.l1RpcUrl, + config.l1RpcUrls, deployerAccount, logger, {}, )); - ethCheatCodes = new EthCheatCodesWithState(config.l1RpcUrl); + ethCheatCodes = new EthCheatCodesWithState(config.l1RpcUrls); rollupAddress = getAddress(l1ContractAddresses.rollupAddress.toString()); outboxAddress = getAddress(l1ContractAddresses.outboxAddress.toString()); @@ -177,7 +174,7 @@ describe('L1Publisher integration', () => { await worldStateSynchronizer.start(); const { walletClient: sequencerWalletClient, publicClient: sequencerPublicClient } = createL1Clients( - config.l1RpcUrl, + config.l1RpcUrls, sequencerPK, foundry, ); @@ -202,7 +199,7 @@ describe('L1Publisher integration', () => { }); publisher = new SequencerPublisher( { - l1RpcUrl: config.l1RpcUrl, + l1RpcUrls: config.l1RpcUrls, requiredConfirmations: 1, l1Contracts: l1ContractAddresses, publisherPrivateKey: sequencerPK, diff --git a/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts b/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts index e22ae0df2983..0459b9a660bf 100644 --- a/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts +++ b/yarn-project/end-to-end/src/composed/integration_proof_verification.test.ts @@ -1,6 +1,6 @@ import { deployL1Contract, fileURLToPath } from '@aztec/aztec.js'; import { BBCircuitVerifier } from '@aztec/bb-prover'; -import { createL1Clients } from '@aztec/ethereum'; +import { type ViemPublicClient, type ViemWalletClient, createL1Clients } from '@aztec/ethereum'; import type { Logger } from '@aztec/foundation/log'; import { HonkVerifierAbi, HonkVerifierBytecode, IVerifierAbi } from '@aztec/l1-artifacts'; import { Proof } from '@aztec/stdlib/proofs'; @@ -9,16 +9,7 @@ import { RootRollupPublicInputs } from '@aztec/stdlib/rollup'; import type { Anvil } from '@viem/anvil'; import { readFile } from 'fs/promises'; import { join } from 'path'; -import { - type Account, - type Chain, - type GetContractReturnType, - type Hex, - type HttpTransport, - type PublicClient, - type WalletClient, - getContract, -} from 'viem'; +import { type GetContractReturnType, type Hex, getContract } from 'viem'; import { mnemonicToAccount } from 'viem/accounts'; import { MNEMONIC } from '../fixtures/fixtures.js'; @@ -34,8 +25,8 @@ describe('proof_verification', () => { let proof: Proof; let publicInputs: RootRollupPublicInputs; let anvil: Anvil | undefined; - let walletClient: WalletClient; - let publicClient: PublicClient; + let walletClient: ViemWalletClient; + let publicClient: ViemPublicClient; let logger: Logger; let circuitVerifier: BBCircuitVerifier; let bbTeardown: () => Promise; @@ -44,9 +35,11 @@ describe('proof_verification', () => { beforeAll(async () => { logger = getLogger(); - let rpcUrl = process.env.ETHEREUM_HOST; + let rpcUrlList = process.env.ETHEREUM_HOSTS?.split(','); + let rpcUrl = rpcUrlList?.[0]; if (!rpcUrl) { ({ anvil, rpcUrl } = await startAnvil()); + rpcUrlList = [rpcUrl]; } logger.info('Anvil started'); @@ -59,7 +52,7 @@ describe('proof_verification', () => { acvmTeardown = acvm!.cleanup; logger.info('BB and ACVM initialized'); - ({ publicClient, walletClient } = createL1Clients(rpcUrl, mnemonicToAccount(MNEMONIC))); + ({ publicClient, walletClient } = createL1Clients(rpcUrlList!, mnemonicToAccount(MNEMONIC))); const { address: verifierAddress } = await deployL1Contract( walletClient, diff --git a/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts b/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts index 730c263f6d60..1f7c7051d204 100644 --- a/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts +++ b/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts @@ -36,7 +36,7 @@ const { PXE_URL, FAUCET_URL, AZTEC_CLI = `node ${resolve(fileURLToPath(import.meta.url), '../../../../aztec/dest/bin/index.js')}`, - ETHEREUM_HOST, + ETHEREUM_HOSTS, PXE_PROVER_ENABLED = '0', USE_EMPTY_BLOCKS = '0', } = process.env; @@ -66,8 +66,8 @@ describe('End-to-end tests for devnet', () => { beforeAll(async () => { logger = getLogger(); - if (!ETHEREUM_HOST) { - throw new Error('ETHEREUM_HOST must be set'); + if (!ETHEREUM_HOSTS) { + throw new Error('ETHEREUM_HOSTS must be set'); } if (!AZTEC_CLI) { @@ -162,7 +162,7 @@ describe('End-to-end tests for devnet', () => { claimSecret: { value: string }; messageLeafIndex: string; }>('bridge-fee-juice', [amount, l2Account.getAddress()], { - 'l1-rpc-url': ETHEREUM_HOST!, + 'l1-rpc-urls': ETHEREUM_HOSTS!, 'l1-chain-id': l1ChainId.toString(), 'l1-private-key': l1Account.privateKey, 'rpc-url': pxeUrl, @@ -279,7 +279,7 @@ describe('End-to-end tests for devnet', () => { async function getL1Balance(address: string, token?: EthAddress): Promise { const { balance } = await cli<{ balance: string }>('get-l1-balance', [address], { - 'l1-rpc-url': ETHEREUM_HOST!, + 'l1-rpc-urls': ETHEREUM_HOSTS!, 'l1-chain-id': l1ChainId.toString(), token, }); diff --git a/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts b/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts index 46c59a0754c7..fcd2603818eb 100644 --- a/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts +++ b/yarn-project/end-to-end/src/e2e_cheat_codes.test.ts @@ -1,8 +1,9 @@ import { AnvilTestWatcher, type AztecAddress, type CheatCodes, EthAddress, Fr, type Wallet } from '@aztec/aztec.js'; +import type { ViemPublicClient, ViemWalletClient } from '@aztec/ethereum'; import { RollupContract } from '@aztec/ethereum/contracts'; import { TokenContract } from '@aztec/noir-contracts.js/Token'; -import { type Account, type Chain, type HttpTransport, type PublicClient, type WalletClient, parseEther } from 'viem'; +import { parseEther } from 'viem'; import { mintTokensToPrivate } from './fixtures/token_utils.js'; import { setup } from './fixtures/utils.js'; @@ -13,8 +14,8 @@ describe('e2e_cheat_codes', () => { let cc: CheatCodes; let teardown: () => Promise; - let walletClient: WalletClient; - let publicClient: PublicClient; + let walletClient: ViemWalletClient; + let publicClient: ViemPublicClient; let token: TokenContract; let rollup: RollupContract; let watcher: AnvilTestWatcher | undefined; @@ -71,7 +72,7 @@ describe('e2e_cheat_codes', () => { const timestamp = await cc.eth.timestamp(); const pastTimestamp = timestamp - 1000; await expect(async () => await cc.eth.setNextBlockTimestamp(pastTimestamp)).rejects.toThrow( - `Error setting next block timestamp: Timestamp error: ${pastTimestamp} is lower than or equal to previous block's timestamp`, + new RegExp(`Details: Timestamp error: ${pastTimestamp} is lower than or equal to previous block's timestamp`), ); }); diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts index 42e85927ed1e..f2c4443fc18a 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/cross_chain_messaging_test.ts @@ -11,12 +11,12 @@ import { type PXE, createLogger, } from '@aztec/aztec.js'; -import { createL1Clients } from '@aztec/ethereum'; +import { type ViemPublicClient, createL1Clients } from '@aztec/ethereum'; import { InboxAbi, OutboxAbi, RollupAbi } from '@aztec/l1-artifacts'; import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { TokenBridgeContract } from '@aztec/noir-contracts.js/TokenBridge'; -import { type Chain, type HttpTransport, type PublicClient, getContract } from 'viem'; +import { getContract } from 'viem'; import { MNEMONIC } from '../fixtures/fixtures.js'; import { @@ -39,7 +39,7 @@ export class CrossChainMessagingTest { pxe!: PXE; aztecNodeConfig!: AztecNodeConfig; - publicClient!: PublicClient | undefined; + publicClient!: ViemPublicClient | undefined; user1Wallet!: AccountWallet; user2Wallet!: AccountWallet; @@ -68,7 +68,7 @@ export class CrossChainMessagingTest { this.aztecNode = aztecNode; this.pxe = pxe; this.aztecNodeConfig = aztecNodeConfig; - this.cheatcodes = await CheatCodes.create(this.aztecNodeConfig.l1RpcUrl, this.pxe); + this.cheatcodes = await CheatCodes.create(this.aztecNodeConfig.l1RpcUrls, this.pxe); } snapshot = ( @@ -116,7 +116,7 @@ export class CrossChainMessagingTest { this.logger.verbose(`Public deploy accounts...`); await publicDeployAccounts(this.wallets[0], this.accounts.slice(0, 3)); - const { publicClient, walletClient } = createL1Clients(this.aztecNodeConfig.l1RpcUrl, MNEMONIC); + const { publicClient, walletClient } = createL1Clients(this.aztecNodeConfig.l1RpcUrls, MNEMONIC); this.logger.verbose(`Setting up cross chain harness...`); this.crossChainTestHarness = await CrossChainTestHarness.new( @@ -141,7 +141,7 @@ export class CrossChainMessagingTest { this.ownerAddress = AztecAddress.fromString(crossChainContext.ownerAddress.toString()); const tokenPortalAddress = EthAddress.fromString(crossChainContext.tokenPortal.toString()); - const { publicClient, walletClient } = createL1Clients(this.aztecNodeConfig.l1RpcUrl, MNEMONIC); + const { publicClient, walletClient } = createL1Clients(this.aztecNodeConfig.l1RpcUrls, MNEMONIC); const inbox = getContract({ address: this.aztecNodeConfig.l1Contracts.inboxAddress.toString(), diff --git a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_private.test.ts b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_private.test.ts index 31d4cc7988d4..db03704e637f 100644 --- a/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_private.test.ts +++ b/yarn-project/end-to-end/src/e2e_cross_chain_messaging/token_bridge_private.test.ts @@ -40,7 +40,7 @@ describe('e2e_cross_chain_messaging token_bridge_private', () => { client: crossChainTestHarness.walletClient, }); - cheatCodes = await CheatCodes.create(t.aztecNodeConfig.l1RpcUrl, t.pxe); + cheatCodes = await CheatCodes.create(t.aztecNodeConfig.l1RpcUrls, t.pxe); }, 300_000); afterEach(async () => { diff --git a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts index a594618be8d6..64fd9e615507 100644 --- a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts @@ -159,7 +159,7 @@ export class FeesTest { this.pxe = pxe; this.aztecNode = aztecNode; this.gasSettings = GasSettings.default({ maxFeesPerGas: (await this.aztecNode.getCurrentBaseFees()).mul(2) }); - this.cheatCodes = await CheatCodes.create(aztecNodeConfig.l1RpcUrl, pxe); + this.cheatCodes = await CheatCodes.create(aztecNodeConfig.l1RpcUrls, pxe); this.wallets = await Promise.all(deployedAccounts.map(a => getSchnorrWallet(pxe, a.address, a.signingKey))); this.wallets.forEach((w, i) => this.logger.verbose(`Wallet ${i} address: ${w.getAddress()}`)); [this.aliceWallet, this.bobWallet] = this.wallets.slice(0, 2); @@ -176,7 +176,7 @@ export class FeesTest { } this.coinbase = EthAddress.random(); - const { publicClient, walletClient } = createL1Clients(aztecNodeConfig.l1RpcUrl, MNEMONIC); + const { publicClient, walletClient } = createL1Clients(aztecNodeConfig.l1RpcUrls, MNEMONIC); this.feeJuiceBridgeTestHarness = await FeeJuicePortalTestingHarnessFactory.create({ aztecNode: aztecNode, pxeService: pxe, @@ -208,7 +208,7 @@ export class FeesTest { this.getGasBalanceFn = getBalancesFn('⛽', this.feeJuiceContract.methods.balance_of_public, this.logger); - const { publicClient, walletClient } = createL1Clients(context.aztecNodeConfig.l1RpcUrl, MNEMONIC); + const { publicClient, walletClient } = createL1Clients(context.aztecNodeConfig.l1RpcUrls, MNEMONIC); this.feeJuiceBridgeTestHarness = await FeeJuicePortalTestingHarnessFactory.create({ aztecNode: context.aztecNode, pxeService: context.pxe, @@ -273,7 +273,7 @@ export class FeesTest { ); this.getCoinbaseBalance = async () => { - const { walletClient } = createL1Clients(context.aztecNodeConfig.l1RpcUrl, MNEMONIC); + const { walletClient } = createL1Clients(context.aztecNodeConfig.l1RpcUrls, MNEMONIC); const gasL1 = getContract({ address: data.l1FeeJuiceAddress.toString(), abi: TestERC20Abi, @@ -283,7 +283,7 @@ export class FeesTest { }; this.getCoinbaseSequencerRewards = async () => { - const { walletClient } = createL1Clients(context.aztecNodeConfig.l1RpcUrl, MNEMONIC); + const { walletClient } = createL1Clients(context.aztecNodeConfig.l1RpcUrls, MNEMONIC); const rollup = getContract({ address: data.rollupAddress.toString(), abi: RollupAbi, @@ -294,7 +294,7 @@ export class FeesTest { }; this.getProverFee = async (blockNumber: number) => { - const { walletClient } = createL1Clients(context.aztecNodeConfig.l1RpcUrl, MNEMONIC); + const { walletClient } = createL1Clients(context.aztecNodeConfig.l1RpcUrls, MNEMONIC); const rollup = getContract({ address: data.rollupAddress.toString(), abi: RollupAbi, diff --git a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts index 017723e17a4a..9c5fdffbc31b 100644 --- a/yarn-project/end-to-end/src/e2e_lending_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_lending_contract.test.ts @@ -1,4 +1,10 @@ -import { type AccountWallet, type CheatCodes, type DeployL1Contracts, Fr, type Logger } from '@aztec/aztec.js'; +import { + type AccountWallet, + type CheatCodes, + type DeployL1ContractsReturnType, + Fr, + type Logger, +} from '@aztec/aztec.js'; import type { TestDateProvider } from '@aztec/foundation/timer'; import { RollupAbi } from '@aztec/l1-artifacts'; import { LendingContract } from '@aztec/noir-contracts.js/Lending'; @@ -15,7 +21,7 @@ import { LendingAccount, LendingSimulator, TokenSimulator } from './simulators/i describe('e2e_lending_contract', () => { jest.setTimeout(100_000); let wallet: AccountWallet; - let deployL1ContractsValues: DeployL1Contracts; + let deployL1ContractsValues: DeployL1ContractsReturnType; let logger: Logger; let teardown: () => Promise; diff --git a/yarn-project/end-to-end/src/e2e_outbox.test.ts b/yarn-project/end-to-end/src/e2e_outbox.test.ts index c72db9fd5d46..e08004df4765 100644 --- a/yarn-project/end-to-end/src/e2e_outbox.test.ts +++ b/yarn-project/end-to-end/src/e2e_outbox.test.ts @@ -3,7 +3,7 @@ import { type AztecNode, BatchCall, type CheatCodes, - type DeployL1Contracts, + type DeployL1ContractsReturnType, EthAddress, Fr, type SiblingPath, @@ -25,7 +25,7 @@ describe('E2E Outbox Tests', () => { const merkleSha256 = new SHA256(); let contract: TestContract; let wallets: AccountWalletWithSecretKey[]; - let deployL1ContractsValues: DeployL1Contracts; + let deployL1ContractsValues: DeployL1ContractsReturnType; let outbox: any; let cheatCodes: CheatCodes; diff --git a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts index ed2c38adb9a2..7afa5daf8640 100644 --- a/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts +++ b/yarn-project/end-to-end/src/e2e_p2p/p2p_network.ts @@ -242,7 +242,7 @@ export class P2PNetworkTest { const slotsInEpoch = await rollup.read.getEpochDuration(); const timestamp = await rollup.read.getTimestampForSlot([slotsInEpoch]); - const cheatCodes = new EthCheatCodesWithState(aztecNodeConfig.l1RpcUrl); + const cheatCodes = new EthCheatCodesWithState(aztecNodeConfig.l1RpcUrls); try { await cheatCodes.warp(Number(timestamp)); } catch (err) { diff --git a/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts b/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts index 87995961d4b6..a7811c7a9e2f 100644 --- a/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts +++ b/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts @@ -10,7 +10,7 @@ import { type AztecNode, type CheatCodes, type CompleteAddress, - type DeployL1Contracts, + type DeployL1ContractsReturnType, EthAddress, type Logger, type PXE, @@ -79,7 +79,7 @@ export class FullProverTest { private context!: SubsystemsContext; private proverNode!: ProverNode; private simulatedProverNode!: ProverNode; - public l1Contracts!: DeployL1Contracts; + public l1Contracts!: DeployL1ContractsReturnType; public proverAddress!: EthAddress; constructor( diff --git a/yarn-project/end-to-end/src/e2e_sequencer/gov_proposal.test.ts b/yarn-project/end-to-end/src/e2e_sequencer/gov_proposal.test.ts index 7caebd71c21d..dd29b0d5fc76 100644 --- a/yarn-project/end-to-end/src/e2e_sequencer/gov_proposal.test.ts +++ b/yarn-project/end-to-end/src/e2e_sequencer/gov_proposal.test.ts @@ -1,6 +1,6 @@ import type { AztecNode, CheatCodes, Logger, PXE, Wallet } from '@aztec/aztec.js'; import { - type DeployL1Contracts, + type DeployL1ContractsReturnType, GovernanceProposerContract, RollupContract, deployL1Contract, @@ -22,7 +22,7 @@ describe('e2e_gov_proposal', () => { let wallet: Wallet; let pxe: PXE; let aztecNode: AztecNode; - let deployL1ContractsValues: DeployL1Contracts; + let deployL1ContractsValues: DeployL1ContractsReturnType; let aztecSlotDuration: number; let cheatCodes: CheatCodes; beforeEach(async () => { diff --git a/yarn-project/end-to-end/src/e2e_synching.test.ts b/yarn-project/end-to-end/src/e2e_synching.test.ts index b7fe396fac73..9a39d7e8a142 100644 --- a/yarn-project/end-to-end/src/e2e_synching.test.ts +++ b/yarn-project/end-to-end/src/e2e_synching.test.ts @@ -423,7 +423,7 @@ describe('e2e_synching', () => { }); const publisher = new SequencerPublisher( { - l1RpcUrl: config.l1RpcUrl, + l1RpcUrls: config.l1RpcUrls, requiredConfirmations: 1, l1Contracts: deployL1ContractsValues.l1ContractAddresses, publisherPrivateKey: sequencerPK, diff --git a/yarn-project/end-to-end/src/e2e_token_bridge_tutorial_test.test.ts b/yarn-project/end-to-end/src/e2e_token_bridge_tutorial_test.test.ts index cf187c0621ac..b34f0143d92f 100644 --- a/yarn-project/end-to-end/src/e2e_token_bridge_tutorial_test.test.ts +++ b/yarn-project/end-to-end/src/e2e_token_bridge_tutorial_test.test.ts @@ -20,9 +20,9 @@ import { getContract } from 'viem'; // docs:end:imports // docs:start:utils const MNEMONIC = 'test test test test test test test test test test test junk'; -const { ETHEREUM_HOST = 'http://localhost:8545' } = process.env; +const { ETHEREUM_HOSTS = 'http://localhost:8545' } = process.env; -const { walletClient, publicClient } = createL1Clients(ETHEREUM_HOST, MNEMONIC); +const { walletClient, publicClient } = createL1Clients(ETHEREUM_HOSTS.split(','), MNEMONIC); const ownerEthAddress = walletClient.account.address; const setupSandbox = async () => { diff --git a/yarn-project/end-to-end/src/fixtures/l1_to_l2_messaging.ts b/yarn-project/end-to-end/src/fixtures/l1_to_l2_messaging.ts index bf8963eaa0f8..bf99cd3710b0 100644 --- a/yarn-project/end-to-end/src/fixtures/l1_to_l2_messaging.ts +++ b/yarn-project/end-to-end/src/fixtures/l1_to_l2_messaging.ts @@ -1,24 +1,16 @@ -import type { L1ContractAddresses } from '@aztec/ethereum'; +import type { L1ContractAddresses, ViemPublicClient, ViemWalletClient } from '@aztec/ethereum'; import { Fr } from '@aztec/foundation/fields'; import { InboxAbi } from '@aztec/l1-artifacts'; import type { AztecAddress } from '@aztec/stdlib/aztec-address'; import { expect } from '@jest/globals'; -import { - type Account, - type Chain, - type HttpTransport, - type PublicClient, - type WalletClient, - decodeEventLog, - getContract, -} from 'viem'; +import { decodeEventLog, getContract } from 'viem'; export async function sendL1ToL2Message( message: { recipient: AztecAddress; content: Fr; secretHash: Fr }, ctx: { - walletClient: WalletClient; - publicClient: PublicClient; + walletClient: ViemWalletClient; + publicClient: ViemPublicClient; l1ContractAddresses: Pick; }, ) { diff --git a/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts b/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts index 7874ef13924a..554db61903ca 100644 --- a/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts +++ b/yarn-project/end-to-end/src/fixtures/setup_l1_contracts.ts @@ -15,7 +15,7 @@ export const setupL1Contracts = async ( args: Pick & L1ContractsConfig, ) => { - const l1Data = await deployL1Contracts(l1RpcUrl, account, foundry, logger, { + const l1Data = await deployL1Contracts([l1RpcUrl], account, foundry, logger, { l2FeeJuiceAddress: ProtocolContractAddress.FeeJuice.toField(), vkTreeRoot: getVKTreeRoot(), protocolContractTreeRoot, diff --git a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts index deed14dda57b..5e56093da067 100644 --- a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts +++ b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts @@ -9,7 +9,7 @@ import { CheatCodes, type CompleteAddress, type ContractFunctionInteraction, - type DeployL1Contracts, + type DeployL1ContractsReturnType, type FunctionCall, type Logger, type PXE, @@ -54,7 +54,7 @@ export type SubsystemsContext = { aztecNode: AztecNodeService; aztecNodeConfig: AztecNodeConfig; pxe: PXEService; - deployL1ContractsValues: DeployL1Contracts; + deployL1ContractsValues: DeployL1ContractsReturnType; proverNode?: ProverNode; watcher: AnvilTestWatcher; cheatCodes: CheatCodes; @@ -188,7 +188,7 @@ class SnapshotManager implements ISnapshotManager { await restore(snapshotData, context); // Save the snapshot data. - const ethCheatCodes = new EthCheatCodesWithState(context.aztecNodeConfig.l1RpcUrl); + const ethCheatCodes = new EthCheatCodesWithState(context.aztecNodeConfig.l1RpcUrls); const anvilStateFile = `${this.livePath}/anvil.dat`; await ethCheatCodes.dumpChainState(anvilStateFile); writeFileSync(`${this.livePath}/${name}.json`, JSON.stringify(snapshotData || {}, resolver)); @@ -320,7 +320,7 @@ async function setupFromFresh( logger.verbose('Starting anvil...'); const res = await startAnvil({ l1BlockTime: opts.ethereumSlotDuration }); const anvil = res.anvil; - aztecNodeConfig.l1RpcUrl = res.rpcUrl; + aztecNodeConfig.l1RpcUrls = [res.rpcUrl]; // Deploy our L1 contracts. logger.verbose('Deploying L1 contracts...'); @@ -334,7 +334,7 @@ async function setupFromFresh( aztecNodeConfig.publisherPrivateKey = `0x${publisherPrivKey!.toString('hex')}`; aztecNodeConfig.validatorPrivateKey = `0x${validatorPrivKey!.toString('hex')}`; - const ethCheatCodes = new EthCheatCodesWithState(aztecNodeConfig.l1RpcUrl); + const ethCheatCodes = new EthCheatCodesWithState(aztecNodeConfig.l1RpcUrls); if (opts.l1StartTime) { await ethCheatCodes.warp(opts.l1StartTime); @@ -346,7 +346,7 @@ async function setupFromFresh( opts.initialAccountFeeJuice, ); - const deployL1ContractsValues = await setupL1Contracts(aztecNodeConfig.l1RpcUrl, hdAccount, logger, { + const deployL1ContractsValues = await setupL1Contracts(aztecNodeConfig.l1RpcUrls[0], hdAccount, logger, { ...getL1ContractsConfigEnvVars(), genesisArchiveRoot, genesisBlockHash, @@ -381,7 +381,7 @@ async function setupFromFresh( } const watcher = new AnvilTestWatcher( - new EthCheatCodesWithState(aztecNodeConfig.l1RpcUrl), + new EthCheatCodesWithState(aztecNodeConfig.l1RpcUrls), deployL1ContractsValues.l1ContractAddresses.rollupAddress, deployL1ContractsValues.publicClient, ); @@ -426,7 +426,7 @@ async function setupFromFresh( pxeConfig.dataDirectory = statePath ?? path.join(directoryToCleanup, randomBytes(8).toString('hex')); const pxe = await createPXEService(aztecNode, pxeConfig); - const cheatCodes = await CheatCodes.create(aztecNodeConfig.l1RpcUrl, pxe); + const cheatCodes = await CheatCodes.create(aztecNodeConfig.l1RpcUrls, pxe); if (statePath) { writeFileSync(`${statePath}/aztec_node_config.json`, JSON.stringify(aztecNodeConfig, resolver)); @@ -486,10 +486,10 @@ async function setupFromState(statePath: string, logger: Logger): Promise { }; export const setupL1Contracts = async ( - l1RpcUrl: string, + l1RpcUrls: string[], account: HDAccount | PrivateKeyAccount, logger: Logger, args: Partial = {}, chain: Chain = foundry, ) => { - const l1Data = await deployL1Contracts(l1RpcUrl, account, chain, logger, { + const l1Data = await deployL1Contracts(l1RpcUrls, account, chain, logger, { l2FeeJuiceAddress: ProtocolContractAddress.FeeJuice.toField(), vkTreeRoot: getVKTreeRoot(), protocolContractTreeRoot, @@ -225,21 +225,21 @@ async function setupWithRemoteEnvironment( logger.verbose(`Retrieving contract addresses from ${PXE_URL}`); const l1Contracts = (await pxeClient.getNodeInfo()).l1ContractAddresses; - const walletClient = createWalletClient({ + const walletClient = createWalletClient({ account, chain: foundry, - transport: http(config.l1RpcUrl), + transport: fallback(config.l1RpcUrls.map(url => http(url))), }); const publicClient = createPublicClient({ chain: foundry, - transport: http(config.l1RpcUrl), + transport: fallback(config.l1RpcUrls.map(url => http(url))), }); - const deployL1ContractsValues: DeployL1Contracts = { + const deployL1ContractsValues: DeployL1ContractsReturnType = { l1ContractAddresses: l1Contracts, walletClient, publicClient, }; - const cheatCodes = await CheatCodes.create(config.l1RpcUrl, pxeClient!); + const cheatCodes = await CheatCodes.create(config.l1RpcUrls, pxeClient!); const teardown = () => Promise.resolve(); await setupCanonicalFeeJuice(pxeClient); @@ -281,7 +281,7 @@ export type SetupOptions = { /** Whether to enable metrics collection, if undefined, metrics collection is disabled */ metricsPort?: number | undefined; /** Previously deployed contracts on L1 */ - deployL1ContractsValues?: DeployL1Contracts; + deployL1ContractsValues?: DeployL1ContractsReturnType; /** Whether to skip deployment of protocol contracts (auth registry, etc) */ skipProtocolContracts?: boolean; /** Initial fee juice for default accounts */ @@ -319,7 +319,7 @@ export type EndToEndContext = { /** The Private eXecution Environment (PXE). */ pxe: PXE; /** Return values from deployL1Contracts function. */ - deployL1ContractsValues: DeployL1Contracts; + deployL1ContractsValues: DeployL1ContractsReturnType; /** The Aztec Node configuration. */ config: AztecNodeConfig; /** The data for the initial funded accounts. */ @@ -372,19 +372,19 @@ export async function setup( let anvil: Anvil | undefined; - if (!config.l1RpcUrl) { + if (!config.l1RpcUrls?.length) { if (!isAnvilTestChain(chain.id)) { - throw new Error(`No ETHEREUM_HOST set but non anvil chain requested`); + throw new Error(`No ETHEREUM_HOSTS set but non anvil chain requested`); } if (PXE_URL) { throw new Error( - `PXE_URL provided but no ETHEREUM_HOST set. Refusing to run, please set both variables so tests can deploy L1 contracts to the same Anvil instance`, + `PXE_URL provided but no ETHEREUM_HOSTS set. Refusing to run, please set both variables so tests can deploy L1 contracts to the same Anvil instance`, ); } const res = await startAnvil({ l1BlockTime: opts.ethereumSlotDuration }); anvil = res.anvil; - config.l1RpcUrl = res.rpcUrl; + config.l1RpcUrls = [res.rpcUrl]; } // Enable logging metrics to a local file named after the test suite @@ -394,7 +394,7 @@ export async function setup( setupMetricsLogger(filename); } - const ethCheatCodes = new EthCheatCodesWithState(config.l1RpcUrl); + const ethCheatCodes = new EthCheatCodesWithState(config.l1RpcUrls); if (opts.stateLoad) { await ethCheatCodes.loadChainState(opts.stateLoad); @@ -444,7 +444,7 @@ export async function setup( const deployL1ContractsValues = opts.deployL1ContractsValues ?? (await setupL1Contracts( - config.l1RpcUrl, + config.l1RpcUrls, publisherHdAccount!, logger, { ...opts, genesisArchiveRoot, genesisBlockHash }, @@ -485,7 +485,7 @@ export async function setup( const dateProvider = new TestDateProvider(); const watcher = new AnvilTestWatcher( - new EthCheatCodesWithState(config.l1RpcUrl), + new EthCheatCodesWithState(config.l1RpcUrls), deployL1ContractsValues.l1ContractAddresses.rollupAddress, deployL1ContractsValues.publicClient, dateProvider, @@ -556,7 +556,7 @@ export async function setup( ); } - const cheatCodes = await CheatCodes.create(config.l1RpcUrl, pxe!); + const cheatCodes = await CheatCodes.create(config.l1RpcUrls, pxe!); const teardown = async () => { await pxeTeardown(); @@ -816,7 +816,7 @@ export async function createAndSyncProverNode( } function createDelayedL1TxUtils(aztecNodeConfig: AztecNodeConfig, privateKey: `0x${string}`, logName: string) { - const { publicClient, walletClient } = createL1Clients(aztecNodeConfig.l1RpcUrl, privateKey, foundry); + const { publicClient, walletClient } = createL1Clients(aztecNodeConfig.l1RpcUrls, privateKey, foundry); const log = createLogger(logName); const l1TxUtils = new DelayedTxUtils(publicClient, walletClient, log, aztecNodeConfig); @@ -829,7 +829,7 @@ export async function createForwarderContract( privateKey: `0x${string}`, rollupAddress: Hex, ) { - const { walletClient, publicClient } = createL1Clients(aztecNodeConfig.l1RpcUrl, privateKey, foundry); + const { walletClient, publicClient } = createL1Clients(aztecNodeConfig.l1RpcUrls, privateKey, foundry); const forwarderContract = await ForwarderContract.create( walletClient.account.address, walletClient, diff --git a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts index 4189b7bc24e5..039fa607b46d 100644 --- a/yarn-project/end-to-end/src/guides/dapp_testing.test.ts +++ b/yarn-project/end-to-end/src/guides/dapp_testing.test.ts @@ -10,7 +10,7 @@ import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { U128_UNDERFLOW_ERROR } from '../fixtures/fixtures.js'; import { mintTokensToPrivate } from '../fixtures/token_utils.js'; -const { PXE_URL = 'http://localhost:8080', ETHEREUM_HOST = 'http://localhost:8545' } = process.env; +const { PXE_URL = 'http://localhost:8080', ETHEREUM_HOSTS = 'http://localhost:8545' } = process.env; describe('guides/dapp/testing', () => { describe('on local sandbox', () => { @@ -71,7 +71,7 @@ describe('guides/dapp/testing', () => { await mintTokensToPrivate(token, owner, ownerAddress, mintAmount); // docs:start:calc-slot - cheats = await CheatCodes.create(ETHEREUM_HOST, pxe); + cheats = await CheatCodes.create(ETHEREUM_HOSTS.split(','), pxe); // The balances mapping is indexed by user address ownerSlot = await cheats.aztec.computeSlotInMap(TokenContract.storage.balances.slot, ownerAddress); // docs:end:calc-slot diff --git a/yarn-project/end-to-end/src/public-testnet/e2e_public_testnet_transfer.test.ts b/yarn-project/end-to-end/src/public-testnet/e2e_public_testnet_transfer.test.ts index 44cf0b907731..86fe0904e6d7 100644 --- a/yarn-project/end-to-end/src/public-testnet/e2e_public_testnet_transfer.test.ts +++ b/yarn-project/end-to-end/src/public-testnet/e2e_public_testnet_transfer.test.ts @@ -8,7 +8,7 @@ import { setup } from '../fixtures/utils.js'; // process.env.SEQ_PUBLISHER_PRIVATE_KEY = ''; // process.env.PROVER_PUBLISHER_PRIVATE_KEY = ''; -// process.env.ETHEREUM_HOST= 'https://sepolia.infura.io/v3/'; +// process.env.ETHEREUM_HOSTS= 'https://sepolia.infura.io/v3/'; // process.env.L1_CHAIN_ID = '11155111'; describe(`deploys and transfers a private only token`, () => { diff --git a/yarn-project/end-to-end/src/sample-dapp/index.test.mjs b/yarn-project/end-to-end/src/sample-dapp/index.test.mjs index 0c28cad7eb6b..9ab94cf281d6 100644 --- a/yarn-project/end-to-end/src/sample-dapp/index.test.mjs +++ b/yarn-project/end-to-end/src/sample-dapp/index.test.mjs @@ -3,7 +3,7 @@ import { createLogger, createPXEClient, waitForPXE } from '@aztec/aztec.js'; import { deployToken } from '../fixtures/token_utils'; -const { PXE_URL = 'http://localhost:8080', ETHEREUM_HOST = 'http://localhost:8545' } = process.env; +const { PXE_URL = 'http://localhost:8080', ETHEREUM_HOSTS = 'http://localhost:8545' } = process.env; // Note: To run this test you need to spin up Aztec sandbox. Build the aztec image (or pull it with aztec-up if on // master) and then run this test as usual (yarn test src/sample-dapp/index.test.mjs). diff --git a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts index f5ebbf71cdb1..9201bafab680 100644 --- a/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts +++ b/yarn-project/end-to-end/src/shared/cross_chain_test_harness.ts @@ -18,20 +18,12 @@ import { deployL1Contract, retryUntil, } from '@aztec/aztec.js'; -import type { L1ContractAddresses } from '@aztec/ethereum'; +import type { L1ContractAddresses, ViemPublicClient, ViemWalletClient } from '@aztec/ethereum'; import { TestERC20Abi, TestERC20Bytecode, TokenPortalAbi, TokenPortalBytecode } from '@aztec/l1-artifacts'; import { TokenContract } from '@aztec/noir-contracts.js/Token'; import { TokenBridgeContract } from '@aztec/noir-contracts.js/TokenBridge'; -import { - type Account, - type Chain, - type Hex, - type HttpTransport, - type PublicClient, - type WalletClient, - getContract, -} from 'viem'; +import { type Hex, getContract } from 'viem'; import { mintTokensToPrivate } from '../fixtures/token_utils.js'; @@ -48,8 +40,8 @@ import { mintTokensToPrivate } from '../fixtures/token_utils.js'; */ export async function deployAndInitializeTokenAndBridgeContracts( wallet: Wallet, - walletClient: WalletClient, - publicClient: PublicClient, + walletClient: ViemWalletClient, + publicClient: ViemPublicClient, rollupRegistryAddress: EthAddress, owner: AztecAddress, underlyingERC20Address?: EthAddress, @@ -142,8 +134,8 @@ export class CrossChainTestHarness { static async new( aztecNode: AztecNode, pxeService: PXE, - publicClient: PublicClient, - walletClient: WalletClient, + publicClient: ViemPublicClient, + walletClient: ViemWalletClient, wallet: AccountWallet, logger: Logger, underlyingERC20Address?: EthAddress, @@ -205,9 +197,9 @@ export class CrossChainTestHarness { /** Underlying token for portal tests. */ public underlyingERC20Address: EthAddress, /** Viem Public client instance. */ - public publicClient: PublicClient, + public publicClient: ViemPublicClient, /** Viem Wallet Client instance. */ - public walletClient: WalletClient, + public walletClient: ViemWalletClient, /** Deployment addresses for all L1 contracts */ public readonly l1ContractAddresses: L1ContractAddresses, diff --git a/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts b/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts index 25a6f1c86fe3..104543157ef8 100644 --- a/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts +++ b/yarn-project/end-to-end/src/shared/gas_portal_test_harness.ts @@ -10,11 +10,10 @@ import { type Wallet, retryUntil, } from '@aztec/aztec.js'; +import type { ViemPublicClient, ViemWalletClient } from '@aztec/ethereum'; import { FeeJuiceContract } from '@aztec/noir-contracts.js/FeeJuice'; import { ProtocolContractAddress } from '@aztec/protocol-contracts'; -import type { Account, Chain, HttpTransport, PublicClient, WalletClient } from 'viem'; - export interface IGasBridgingTestHarness { getL1FeeJuiceBalance(address: EthAddress): Promise; prepareTokensOnL1(bridgeAmount: bigint, owner: AztecAddress): Promise; @@ -26,8 +25,8 @@ export interface IGasBridgingTestHarness { export interface FeeJuicePortalTestingHarnessFactoryConfig { aztecNode: AztecNode; pxeService: PXE; - publicClient: PublicClient; - walletClient: WalletClient; + publicClient: ViemPublicClient; + walletClient: ViemWalletClient; wallet: Wallet; logger: Logger; mockL1?: boolean; @@ -97,9 +96,9 @@ export class GasBridgingTestHarness implements IGasBridgingTestHarness { /** Underlying token for portal tests. */ public l1FeeJuiceAddress: EthAddress, /** Viem Public client instance. */ - public publicClient: PublicClient, + public publicClient: ViemPublicClient, /** Viem Wallet Client instance. */ - public walletClient: WalletClient, + public walletClient: ViemWalletClient, ) { this.feeJuicePortalManager = new L1FeeJuicePortalManager( this.feeJuicePortalAddress, diff --git a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts index 7996e1ab9f6c..e9019a2dedf1 100644 --- a/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts +++ b/yarn-project/end-to-end/src/shared/uniswap_l1_l2.ts @@ -10,24 +10,19 @@ import { computeAuthWitMessageHash, generateClaimSecret, } from '@aztec/aztec.js'; -import { type DeployL1Contracts, deployL1Contract, extractEvent } from '@aztec/ethereum'; +import { + type DeployL1ContractsReturnType, + type ViemPublicClient, + type ViemWalletClient, + deployL1Contract, + extractEvent, +} from '@aztec/ethereum'; import { sha256ToField } from '@aztec/foundation/crypto'; import { InboxAbi, RollupAbi, UniswapPortalAbi, UniswapPortalBytecode } from '@aztec/l1-artifacts'; import { UniswapContract } from '@aztec/noir-contracts.js/Uniswap'; import { jest } from '@jest/globals'; -import { - type Account, - type Chain, - type GetContractReturnType, - type HttpTransport, - type PublicClient, - type WalletClient, - getContract, - parseEther, - toFunctionSelector, -} from 'viem'; -import type * as chains from 'viem/chains'; +import { type GetContractReturnType, getContract, parseEther, toFunctionSelector } from 'viem'; import { ensureAccountsPubliclyDeployed } from '../fixtures/utils.js'; import { CrossChainTestHarness } from './cross_chain_test_harness.js'; @@ -51,15 +46,15 @@ export type UniswapSetupContext = { /** Logger instance named as the current test. */ logger: Logger; /** Viem Public client instance. */ - publicClient: PublicClient; + publicClient: ViemPublicClient; /** Viem Wallet Client instance. */ - walletClient: WalletClient; + walletClient: ViemWalletClient; /** The owner wallet. */ ownerWallet: AccountWallet; /** The sponsor wallet. */ sponsorWallet: AccountWallet; /** */ - deployL1ContractsValues: DeployL1Contracts; + deployL1ContractsValues: DeployL1ContractsReturnType; /** Cheat codes instance. */ cheatCodes: CheatCodes; }; @@ -81,8 +76,8 @@ export const uniswapL1L2TestSuite = ( let pxe: PXE; let logger: Logger; - let walletClient: WalletClient; - let publicClient: PublicClient; + let walletClient: ViemWalletClient; + let publicClient: ViemPublicClient; let ownerWallet: AccountWallet; let ownerAddress: AztecAddress; @@ -94,9 +89,9 @@ export const uniswapL1L2TestSuite = ( let daiCrossChainHarness: CrossChainTestHarness; let wethCrossChainHarness: CrossChainTestHarness; - let deployL1ContractsValues: DeployL1Contracts; - let rollup: GetContractReturnType>; - let uniswapPortal: GetContractReturnType>; + let deployL1ContractsValues: DeployL1ContractsReturnType; + let rollup: GetContractReturnType; + let uniswapPortal: GetContractReturnType; let uniswapPortalAddress: EthAddress; let uniswapL2Contract: UniswapContract; diff --git a/yarn-project/end-to-end/src/spartan/4epochs.test.ts b/yarn-project/end-to-end/src/spartan/4epochs.test.ts index 0d60ab859da1..705b017bf3af 100644 --- a/yarn-project/end-to-end/src/spartan/4epochs.test.ts +++ b/yarn-project/end-to-end/src/spartan/4epochs.test.ts @@ -26,7 +26,7 @@ describe('token transfer test', () => { let testWallets: TestWallets; let PXE_URL: string; - let ETHEREUM_HOST: string; + let ETHEREUM_HOSTS: string; beforeAll(async () => { if (isK8sConfig(config)) { @@ -45,12 +45,12 @@ describe('token transfer test', () => { containerPort: config.CONTAINER_ETHEREUM_PORT, hostPort: config.HOST_ETHEREUM_PORT, }); - ETHEREUM_HOST = `http://127.0.0.1:${config.HOST_ETHEREUM_PORT}`; + ETHEREUM_HOSTS = `http://127.0.0.1:${config.HOST_ETHEREUM_PORT}`; } else { - if (!config.ETHEREUM_HOST) { - throw new Error('ETHEREUM_HOST must be set for sepolia runs'); + if (!config.ETHEREUM_HOSTS) { + throw new Error('ETHEREUM_HOSTS must be set for sepolia runs'); } - ETHEREUM_HOST = config.ETHEREUM_HOST; + ETHEREUM_HOSTS = config.ETHEREUM_HOSTS; } await startPortForward({ @@ -66,14 +66,14 @@ describe('token transfer test', () => { testWallets = await deployTestWalletWithTokens( PXE_URL, NODE_URL, - ETHEREUM_HOST, + ETHEREUM_HOSTS, L1_ACCOUNT_MNEMONIC, MINT_AMOUNT, logger, ); } else { PXE_URL = config.PXE_URL; - ETHEREUM_HOST = config.ETHEREUM_HOST; + ETHEREUM_HOSTS = config.ETHEREUM_HOSTS; testWallets = await setupTestWalletsWithTokens(PXE_URL, MINT_AMOUNT, logger); } @@ -88,7 +88,7 @@ describe('token transfer test', () => { }); it('transfer tokens for 4 epochs', async () => { - const ethCheatCodes = new EthCheatCodesWithState(ETHEREUM_HOST); + const ethCheatCodes = new EthCheatCodesWithState([ETHEREUM_HOSTS]); const l1ContractAddresses = await testWallets.pxe.getNodeInfo().then(n => n.l1ContractAddresses); // Get 4 epochs const rollupCheatCodes = new RollupCheatCodes(ethCheatCodes, l1ContractAddresses); diff --git a/yarn-project/end-to-end/src/spartan/gating-passive.test.ts b/yarn-project/end-to-end/src/spartan/gating-passive.test.ts index bd40c7079717..efb3c13b0098 100644 --- a/yarn-project/end-to-end/src/spartan/gating-passive.test.ts +++ b/yarn-project/end-to-end/src/spartan/gating-passive.test.ts @@ -83,7 +83,7 @@ describe('a test that passively observes the network in the presence of network }); const client = await createCompatibleClient(PXE_URL, debugLogger); - const ethCheatCodes = new EthCheatCodesWithState(ETHEREUM_HOST); + const ethCheatCodes = new EthCheatCodesWithState([ETHEREUM_HOST]); const rollupCheatCodes = new RollupCheatCodes( ethCheatCodes, await client.getNodeInfo().then(n => n.l1ContractAddresses), diff --git a/yarn-project/end-to-end/src/spartan/reorg.test.ts b/yarn-project/end-to-end/src/spartan/reorg.test.ts index 6021af8f034f..56b1bd4dee69 100644 --- a/yarn-project/end-to-end/src/spartan/reorg.test.ts +++ b/yarn-project/end-to-end/src/spartan/reorg.test.ts @@ -41,7 +41,7 @@ describe('reorg test', () => { const MINT_AMOUNT = 2_000_000n; const SETUP_EPOCHS = 2; const TRANSFER_AMOUNT = 1n; - const ETHEREUM_HOST = `http://127.0.0.1:${HOST_ETHEREUM_PORT}`; + const ETHEREUM_HOSTS = `http://127.0.0.1:${HOST_ETHEREUM_PORT}`; const PXE_URL = `http://127.0.0.1:${HOST_PXE_PORT}`; let testWallets: TestWallets; @@ -61,7 +61,7 @@ describe('reorg test', () => { }); testWallets = await setupTestWalletsWithTokens(PXE_URL, MINT_AMOUNT, debugLogger); const rollupCheatCodes = new RollupCheatCodes( - new EthCheatCodesWithState(ETHEREUM_HOST), + new EthCheatCodesWithState([ETHEREUM_HOSTS]), await testWallets.pxe.getNodeInfo().then(n => n.l1ContractAddresses), ); const { epochDuration, slotDuration } = await rollupCheatCodes.getConfig(); diff --git a/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts b/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts index 65ff2badd030..efdca31f0668 100644 --- a/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts +++ b/yarn-project/end-to-end/src/spartan/setup_test_wallets.ts @@ -103,8 +103,8 @@ async function bridgeL1FeeJuice( log: Logger, ) { const { l1ChainId } = await pxe.getNodeInfo(); - const chain = createEthereumChain(l1RpcUrl, l1ChainId); - const { publicClient, walletClient } = createL1Clients(chain.rpcUrl, mnemonicOrPrivateKey, chain.chainInfo); + const chain = createEthereumChain([l1RpcUrl], l1ChainId); + const { publicClient, walletClient } = createL1Clients(chain.rpcUrls, mnemonicOrPrivateKey, chain.chainInfo); const portal = await L1FeeJuicePortalManager.new(pxe, publicClient, walletClient, log); const claim = await portal.bridgeTokensPublic(recipient, amount, true /* mint */); diff --git a/yarn-project/end-to-end/src/spartan/transfer.test.ts b/yarn-project/end-to-end/src/spartan/transfer.test.ts index 63d1cdf23e82..b836bda0ca4b 100644 --- a/yarn-project/end-to-end/src/spartan/transfer.test.ts +++ b/yarn-project/end-to-end/src/spartan/transfer.test.ts @@ -36,7 +36,7 @@ describe('token transfer test', () => { containerPort: config.CONTAINER_ETHEREUM_PORT, hostPort: config.HOST_ETHEREUM_PORT, }); - const ETHEREUM_HOST = `http://127.0.0.1:${config.HOST_ETHEREUM_PORT}`; + const ETHEREUM_HOSTS = `http://127.0.0.1:${config.HOST_ETHEREUM_PORT}`; await startPortForward({ resource: `svc/${config.INSTANCE_NAME}-aztec-network-validator`, @@ -51,7 +51,7 @@ describe('token transfer test', () => { testWallets = await deployTestWalletWithTokens( PXE_URL, NODE_URL, - ETHEREUM_HOST, + ETHEREUM_HOSTS, L1_ACCOUNT_MNEMONIC, MINT_AMOUNT, logger, diff --git a/yarn-project/end-to-end/src/spartan/upgrade_governance_proposer.test.ts b/yarn-project/end-to-end/src/spartan/upgrade_governance_proposer.test.ts index 7fed811cb211..1d4ea7e788f7 100644 --- a/yarn-project/end-to-end/src/spartan/upgrade_governance_proposer.test.ts +++ b/yarn-project/end-to-end/src/spartan/upgrade_governance_proposer.test.ts @@ -30,7 +30,7 @@ const debugLogger = createLogger('e2e:spartan-test:upgrade_governance_proposer') describe('spartan_upgrade_governance_proposer', () => { let pxe: PXE; let nodeInfo: NodeInfo; - let ETHEREUM_HOST: string; + let ETHEREUM_HOSTS: string[]; beforeAll(async () => { await startPortForward({ resource: `svc/${config.INSTANCE_NAME}-aztec-network-pxe`, @@ -44,7 +44,7 @@ describe('spartan_upgrade_governance_proposer', () => { containerPort: config.CONTAINER_ETHEREUM_PORT, hostPort: config.HOST_ETHEREUM_PORT, }); - ETHEREUM_HOST = `http://127.0.0.1:${config.HOST_ETHEREUM_PORT}`; + ETHEREUM_HOSTS = [`http://127.0.0.1:${config.HOST_ETHEREUM_PORT}`]; const PXE_URL = `http://127.0.0.1:${config.HOST_PXE_PORT}`; pxe = await createCompatibleClient(PXE_URL, debugLogger); @@ -55,8 +55,8 @@ describe('spartan_upgrade_governance_proposer', () => { // because the underlying validators are currently producing blob transactions // and you can't submit blob and non-blob transactions from the same account const setupDeployerAccount = async () => { - const chain = createEthereumChain(ETHEREUM_HOST, 1337); - const { walletClient: validatorWalletClient } = createL1Clients(ETHEREUM_HOST, MNEMONIC, chain.chainInfo); + const chain = createEthereumChain(ETHEREUM_HOSTS, 1337); + const { walletClient: validatorWalletClient } = createL1Clients(ETHEREUM_HOSTS, MNEMONIC, chain.chainInfo); // const privateKey = generatePrivateKey(); const privateKey = deployerPrivateKey; debugLogger.info(`deployer privateKey: ${privateKey}`); @@ -74,7 +74,7 @@ describe('spartan_upgrade_governance_proposer', () => { const receipt = await validatorWalletClient.waitForTransactionReceipt({ hash: tx }); debugLogger.info(`receipt: ${stringify(receipt)}`); } - return createL1Clients(ETHEREUM_HOST, account, chain.chainInfo); + return createL1Clients(ETHEREUM_HOSTS, account, chain.chainInfo); }; it( diff --git a/yarn-project/end-to-end/src/spartan/utils.ts b/yarn-project/end-to-end/src/spartan/utils.ts index 32258fe9b088..59adb58800d1 100644 --- a/yarn-project/end-to-end/src/spartan/utils.ts +++ b/yarn-project/end-to-end/src/spartan/utils.ts @@ -14,6 +14,19 @@ const execAsync = promisify(exec); const logger = createLogger('e2e:k8s-utils'); +const ethereumHostsSchema = z.string().refine( + str => + str.split(',').every(url => { + try { + new URL(url.trim()); + return true; + } catch { + return false; + } + }), + 'ETHEREUM_HOSTS must be a comma-separated list of valid URLs', +); + const k8sLocalConfigSchema = z.object({ INSTANCE_NAME: z.string().min(1, 'INSTANCE_NAME env variable must be set'), NAMESPACE: z.string().min(1, 'NAMESPACE env variable must be set'), @@ -32,7 +45,7 @@ const k8sLocalConfigSchema = z.object({ GRAFANA_PASSWORD: z.string().optional(), METRICS_API_PATH: z.string().default('/api/datasources/proxy/uid/spartan-metrics-prometheus/api/v1'), SPARTAN_DIR: z.string().min(1, 'SPARTAN_DIR env variable must be set'), - ETHEREUM_HOST: z.string().url('ETHEREUM_HOST must be a valid URL').optional(), + ETHEREUM_HOSTS: ethereumHostsSchema.optional(), L1_ACCOUNT_MNEMONIC: z.string().default('test test test test test test test test test test test junk'), SEPOLIA_RUN: z.string().default('false'), K8S: z.literal('local'), @@ -47,7 +60,7 @@ const k8sGCloudConfigSchema = k8sLocalConfigSchema.extend({ const directConfigSchema = z.object({ PXE_URL: z.string().url('PXE_URL must be a valid URL'), NODE_URL: z.string().url('NODE_URL must be a valid URL'), - ETHEREUM_HOST: z.string().url('ETHEREUM_HOST must be a valid URL'), + ETHEREUM_HOSTS: ethereumHostsSchema, K8S: z.literal('false'), }); diff --git a/yarn-project/epoch-cache/src/config.ts b/yarn-project/epoch-cache/src/config.ts index 3cf1c705a7dd..6a9716527a39 100644 --- a/yarn-project/epoch-cache/src/config.ts +++ b/yarn-project/epoch-cache/src/config.ts @@ -7,7 +7,7 @@ import { export type EpochCacheConfig = Pick< L1ReaderConfig & L1ContractsConfig, - | 'l1RpcUrl' + | 'l1RpcUrls' | 'l1ChainId' | 'viemPollingIntervalMS' | 'aztecSlotDuration' diff --git a/yarn-project/epoch-cache/src/epoch_cache.ts b/yarn-project/epoch-cache/src/epoch_cache.ts index 58c5aa7a8ede..0db6112deea0 100644 --- a/yarn-project/epoch-cache/src/epoch_cache.ts +++ b/yarn-project/epoch-cache/src/epoch_cache.ts @@ -10,7 +10,7 @@ import { } from '@aztec/stdlib/epoch-helpers'; import { EventEmitter } from 'node:events'; -import { createPublicClient, encodeAbiParameters, http, keccak256 } from 'viem'; +import { createPublicClient, encodeAbiParameters, fallback, http, keccak256 } from 'viem'; import { type EpochCacheConfig, getEpochCacheConfigEnvVars } from './config.js'; @@ -76,10 +76,10 @@ export class EpochCache ) { config = config ?? getEpochCacheConfigEnvVars(); - const chain = createEthereumChain(config.l1RpcUrl, config.l1ChainId); + const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId); const publicClient = createPublicClient({ chain: chain.chainInfo, - transport: http(chain.rpcUrl), + transport: fallback(config.l1RpcUrls.map(url => http(url))), pollingInterval: config.viemPollingIntervalMS, }); diff --git a/yarn-project/ethereum/src/chain.ts b/yarn-project/ethereum/src/chain.ts index 72f3a54c8ec3..1edae58ff7ae 100644 --- a/yarn-project/ethereum/src/chain.ts +++ b/yarn-project/ethereum/src/chain.ts @@ -13,17 +13,17 @@ export interface EthereumChain { chainInfo: Chain; /** - * The actual url to be used. + * The list of actual urls to be used. */ - rpcUrl: string; + rpcUrls: string[]; } /** * Helper function to create an instance of Aztec Chain from an rpc url and api key. - * @param rpcUrl - The rpc url of the chain or a chain identifier (e.g. 'testnet') - * @param apiKey - An optional API key for the chain client. + * @param rpcUrls - List of RPC URLs of the chain or chain identifiers (e.g. 'testnet') + * @param _chainId - An optional chain identifier (e.g. 'testnet') */ -export function createEthereumChain(rpcUrl: string, _chainId: number | string): EthereumChain { +export function createEthereumChain(rpcUrls: string[], _chainId: number | string): EthereumChain { let chainId: number; if (typeof _chainId === 'string') { chainId = +_chainId; @@ -37,7 +37,7 @@ export function createEthereumChain(rpcUrl: string, _chainId: number | string): name: 'Ethereum', rpcUrls: { default: { - http: [rpcUrl], + http: rpcUrls, }, }, nativeCurrency: { @@ -46,12 +46,12 @@ export function createEthereumChain(rpcUrl: string, _chainId: number | string): symbol: 'ETH', }, }, - rpcUrl, + rpcUrls, }; } else { return { chainInfo: foundry, - rpcUrl, + rpcUrls, }; } } diff --git a/yarn-project/ethereum/src/client.ts b/yarn-project/ethereum/src/client.ts index 62528913b937..1b03e6d8a78b 100644 --- a/yarn-project/ethereum/src/client.ts +++ b/yarn-project/ethereum/src/client.ts @@ -1,14 +1,14 @@ import type { Logger } from '@aztec/foundation/log'; import { retryUntil } from '@aztec/foundation/retry'; -import { createPublicClient, http } from 'viem'; +import { createPublicClient, fallback, http } from 'viem'; import { createEthereumChain } from './chain.js'; import type { ViemPublicClient } from './types.js'; type Config = { /** The RPC Url of the ethereum host. */ - l1RpcUrl: string; + l1RpcUrls: string[]; /** The chain ID of the ethereum host. */ l1ChainId: number; /** The polling interval viem uses in ms */ @@ -19,10 +19,10 @@ type Config = { /** Returns a viem public client given the L1 config. */ export function getPublicClient(config: Config): ViemPublicClient { - const chain = createEthereumChain(config.l1RpcUrl, config.l1ChainId); + const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId); return createPublicClient({ chain: chain.chainInfo, - transport: http(chain.rpcUrl), + transport: fallback(config.l1RpcUrls.map(url => http(url))), pollingInterval: config.viemPollingIntervalMS, }); } @@ -41,16 +41,18 @@ async function waitForRpc(client: ViemPublicClient, config: Config, logger?: Log try { chainId = await client.getChainId(); } catch (err) { - logger?.warn(`Failed to connect to Ethereum node at ${config.l1RpcUrl}. Retrying...`); + logger?.warn(`Failed to connect to Ethereum node at ${config.l1RpcUrls.join(', ')}. Retrying...`); } return chainId; }, - `L1 RPC url at ${config.l1RpcUrl}`, + `L1 RPC url at ${config.l1RpcUrls.join(', ')}`, 600, 1, ); if (l1ChainId !== config.l1ChainId) { - throw new Error(`Ethereum node at ${config.l1RpcUrl} has chain ID ${l1ChainId} but expected ${config.l1ChainId}`); + throw new Error( + `Ethereum node at ${config.l1RpcUrls.join(', ')} has chain ID ${l1ChainId} but expected ${config.l1ChainId}`, + ); } } diff --git a/yarn-project/ethereum/src/contracts/forwarder.test.ts b/yarn-project/ethereum/src/contracts/forwarder.test.ts index f46557bffc5b..8ab31e63b923 100644 --- a/yarn-project/ethereum/src/contracts/forwarder.test.ts +++ b/yarn-project/ethereum/src/contracts/forwarder.test.ts @@ -6,14 +6,7 @@ import { TestERC20Abi } from '@aztec/l1-artifacts/TestERC20Abi'; import { TestERC20Bytecode } from '@aztec/l1-artifacts/TestERC20Bytecode'; import type { Anvil } from '@viem/anvil'; -import { - type Chain, - type GetContractReturnType, - type HttpTransport, - type PublicClient, - encodeFunctionData, - getContract, -} from 'viem'; +import { type GetContractReturnType, encodeFunctionData, getContract } from 'viem'; import { type PrivateKeyAccount, privateKeyToAccount } from 'viem/accounts'; import { foundry } from 'viem/chains'; @@ -21,7 +14,7 @@ import { DefaultL1ContractsConfig } from '../config.js'; import { createL1Clients, deployL1Contract, deployL1Contracts } from '../deploy_l1_contracts.js'; import { L1TxUtils } from '../l1_tx_utils.js'; import { startAnvil } from '../test/start_anvil.js'; -import type { L1Clients } from '../types.js'; +import type { ViemPublicClient, ViemWalletClient } from '../types.js'; import { FormattedViemError } from '../utils.js'; import { ForwarderContract } from './forwarder.js'; @@ -34,13 +27,13 @@ describe('Forwarder', () => { let vkTreeRoot: Fr; let protocolContractTreeRoot: Fr; let l2FeeJuiceAddress: Fr; - let walletClient: L1Clients['walletClient']; - let publicClient: L1Clients['publicClient']; + let walletClient: ViemWalletClient; + let publicClient: ViemPublicClient; let forwarder: ForwarderContract; let l1TxUtils: L1TxUtils; let govProposerAddress: EthAddress; let tokenAddress: EthAddress; - let tokenContract: GetContractReturnType>; + let tokenContract: GetContractReturnType; beforeAll(async () => { logger = createLogger('ethereum:test:forwarder'); // this is the 6th address that gets funded by the junk mnemonic @@ -52,9 +45,9 @@ describe('Forwarder', () => { ({ anvil, rpcUrl } = await startAnvil()); - ({ walletClient, publicClient } = createL1Clients(rpcUrl, privateKey)); + ({ walletClient, publicClient } = createL1Clients([rpcUrl], privateKey)); - const deployed = await deployL1Contracts(rpcUrl, privateKey, foundry, logger, { + const deployed = await deployL1Contracts([rpcUrl], privateKey, foundry, logger, { ...DefaultL1ContractsConfig, salt: undefined, vkTreeRoot, diff --git a/yarn-project/ethereum/src/contracts/forwarder.ts b/yarn-project/ethereum/src/contracts/forwarder.ts index 705401f359c7..ff7e300e5520 100644 --- a/yarn-project/ethereum/src/contracts/forwarder.ts +++ b/yarn-project/ethereum/src/contracts/forwarder.ts @@ -3,25 +3,20 @@ import type { Logger } from '@aztec/foundation/log'; import { ForwarderAbi, ForwarderBytecode } from '@aztec/l1-artifacts'; import { - type Account, - type Chain, type EncodeFunctionDataParameters, type GetContractReturnType, type Hex, - type HttpTransport, - type PublicClient, - type WalletClient, encodeFunctionData, getContract, } from 'viem'; import { deployL1Contract } from '../deploy_l1_contracts.js'; import type { L1BlobInputs, L1GasConfig, L1TxRequest, L1TxUtils } from '../l1_tx_utils.js'; -import type { L1Clients } from '../types.js'; +import type { L1Clients, ViemPublicClient, ViemWalletClient } from '../types.js'; import { RollupContract } from './rollup.js'; export class ForwarderContract { - private readonly forwarder: GetContractReturnType>; + private readonly forwarder: GetContractReturnType; constructor(public readonly client: L1Clients['publicClient'], address: Hex, public readonly rollupAddress: Hex) { this.forwarder = getContract({ address, abi: ForwarderAbi, client }); @@ -29,8 +24,8 @@ export class ForwarderContract { static async create( owner: Hex, - walletClient: WalletClient, - publicClient: PublicClient, + walletClient: ViemWalletClient, + publicClient: ViemPublicClient, logger: Logger, rollupAddress: Hex, ) { diff --git a/yarn-project/ethereum/src/contracts/governance.ts b/yarn-project/ethereum/src/contracts/governance.ts index 7d97f9bbe3a7..16cf9742f058 100644 --- a/yarn-project/ethereum/src/contracts/governance.ts +++ b/yarn-project/ethereum/src/contracts/governance.ts @@ -1,16 +1,10 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import { GovernanceAbi } from '@aztec/l1-artifacts'; -import { - type Chain, - type GetContractReturnType, - type Hex, - type HttpTransport, - type PublicClient, - getContract, -} from 'viem'; +import { type GetContractReturnType, type Hex, getContract } from 'viem'; import type { L1ContractAddresses } from '../l1_contract_addresses.js'; +import type { ViemPublicClient } from '../types.js'; import { GovernanceProposerContract } from './governance_proposer.js'; export type L1GovernanceContractAddresses = Pick< @@ -19,9 +13,9 @@ export type L1GovernanceContractAddresses = Pick< >; export class GovernanceContract { - private readonly governance: GetContractReturnType>; + private readonly governance: GetContractReturnType; - constructor(public readonly client: PublicClient, address: Hex) { + constructor(public readonly client: ViemPublicClient, address: Hex) { this.governance = getContract({ address, abi: GovernanceAbi, client }); } diff --git a/yarn-project/ethereum/src/contracts/governance_proposer.ts b/yarn-project/ethereum/src/contracts/governance_proposer.ts index f3b3704637b8..83d7578365c0 100644 --- a/yarn-project/ethereum/src/contracts/governance_proposer.ts +++ b/yarn-project/ethereum/src/contracts/governance_proposer.ts @@ -2,25 +2,16 @@ import { memoize } from '@aztec/foundation/decorators'; import { EthAddress } from '@aztec/foundation/eth-address'; import { GovernanceProposerAbi } from '@aztec/l1-artifacts'; -import { - type Chain, - type GetContractReturnType, - type Hex, - type HttpTransport, - type PublicClient, - type TransactionReceipt, - encodeFunctionData, - getContract, -} from 'viem'; +import { type GetContractReturnType, type Hex, type TransactionReceipt, encodeFunctionData, getContract } from 'viem'; import type { GasPrice, L1TxRequest, L1TxUtils } from '../l1_tx_utils.js'; -import type { L1Clients } from '../types.js'; +import type { ViemPublicClient } from '../types.js'; import { type IEmpireBase, encodeVote } from './empire_base.js'; export class GovernanceProposerContract implements IEmpireBase { - private readonly proposer: GetContractReturnType>; + private readonly proposer: GetContractReturnType; - constructor(public readonly client: L1Clients['publicClient'], address: Hex) { + constructor(public readonly client: ViemPublicClient, address: Hex) { this.proposer = getContract({ address, abi: GovernanceProposerAbi, client }); } diff --git a/yarn-project/ethereum/src/contracts/registry.test.ts b/yarn-project/ethereum/src/contracts/registry.test.ts index 5c50347f09d2..c925e3b746d0 100644 --- a/yarn-project/ethereum/src/contracts/registry.test.ts +++ b/yarn-project/ethereum/src/contracts/registry.test.ts @@ -43,9 +43,9 @@ describe('Registry', () => { ({ anvil, rpcUrl } = await startAnvil()); - ({ publicClient, walletClient } = createL1Clients(rpcUrl, privateKey)); + ({ publicClient, walletClient } = createL1Clients([rpcUrl], privateKey)); - const deployed = await deployL1Contracts(rpcUrl, privateKey, foundry, logger, { + const deployed = await deployL1Contracts([rpcUrl], privateKey, foundry, logger, { ...DefaultL1ContractsConfig, salt: originalVersionSalt, vkTreeRoot, @@ -109,7 +109,7 @@ describe('Registry', () => { const newVersionSalt = originalVersionSalt + 1; const { rollup: newRollup, payloadAddress } = await deployRollupAndPeriphery( - rpcUrl, + [rpcUrl], foundry, privateKey, { @@ -186,7 +186,7 @@ async function executeGovernanceProposal( }); }; - const cheatCodes = new EthCheatCodes(rpcUrl, logger); + const cheatCodes = new EthCheatCodes([rpcUrl], logger); const timeToActive = proposal.creation + proposal.config.votingDelay; logger.info(`Warping to ${timeToActive + 1n}`); diff --git a/yarn-project/ethereum/src/contracts/rollup.ts b/yarn-project/ethereum/src/contracts/rollup.ts index 17cd89ffb7df..4dd66ee9b104 100644 --- a/yarn-project/ethereum/src/contracts/rollup.ts +++ b/yarn-project/ethereum/src/contracts/rollup.ts @@ -3,23 +3,13 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import type { ViemSignature } from '@aztec/foundation/eth-signature'; import { RollupAbi, RollupStorage, SlasherAbi } from '@aztec/l1-artifacts'; -import { - type Account, - type Chain, - type GetContractReturnType, - type Hex, - type HttpTransport, - type PublicClient, - createPublicClient, - getAddress, - getContract, - http, -} from 'viem'; - -import { createEthereumChain } from '../chain.js'; -import type { DeployL1Contracts } from '../deploy_l1_contracts.js'; +import { type Account, type GetContractReturnType, type Hex, getAddress, getContract } from 'viem'; + +import { getPublicClient } from '../client.js'; +import type { DeployL1ContractsReturnType } from '../deploy_l1_contracts.js'; import type { L1ContractAddresses } from '../l1_contract_addresses.js'; import type { L1ReaderConfig } from '../l1_reader.js'; +import type { ViemPublicClient } from '../types.js'; import { formatViemError } from '../utils.js'; import { SlashingProposerContract } from './slashing_proposer.js'; @@ -35,7 +25,7 @@ export type L1RollupContractAddresses = Pick< >; export class RollupContract { - private readonly rollup: GetContractReturnType>; + private readonly rollup: GetContractReturnType; static get checkBlobStorageSlot(): bigint { const asString = RollupStorage.find(storage => storage.label === 'checkBlob')?.slot; @@ -45,7 +35,7 @@ export class RollupContract { return BigInt(asString); } - static getFromL1ContractsValues(deployL1ContractsValues: DeployL1Contracts) { + static getFromL1ContractsValues(deployL1ContractsValues: DeployL1ContractsReturnType) { const { publicClient, l1ContractAddresses: { rollupAddress }, @@ -54,15 +44,12 @@ export class RollupContract { } static getFromConfig(config: L1ReaderConfig) { - const client = createPublicClient({ - transport: http(config.l1RpcUrl), - chain: createEthereumChain(config.l1RpcUrl, config.l1ChainId).chainInfo, - }); + const client = getPublicClient(config); const address = config.l1Contracts.rollupAddress.toString(); return new RollupContract(client, address); } - constructor(public readonly client: PublicClient, address: Hex | EthAddress) { + constructor(public readonly client: ViemPublicClient, address: Hex | EthAddress) { if (address instanceof EthAddress) { address = address.toString(); } diff --git a/yarn-project/ethereum/src/contracts/slashing_proposer.ts b/yarn-project/ethereum/src/contracts/slashing_proposer.ts index b0322e56225e..47d36efd84aa 100644 --- a/yarn-project/ethereum/src/contracts/slashing_proposer.ts +++ b/yarn-project/ethereum/src/contracts/slashing_proposer.ts @@ -1,23 +1,16 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import { SlashingProposerAbi } from '@aztec/l1-artifacts'; -import { - type Chain, - type GetContractReturnType, - type Hex, - type HttpTransport, - type PublicClient, - getContract, -} from 'viem'; +import { type GetContractReturnType, type Hex, getContract } from 'viem'; import type { L1TxRequest } from '../l1_tx_utils.js'; -import type { L1Clients } from '../types.js'; +import type { ViemPublicClient } from '../types.js'; import { type IEmpireBase, encodeVote } from './empire_base.js'; export class SlashingProposerContract implements IEmpireBase { - private readonly proposer: GetContractReturnType>; + private readonly proposer: GetContractReturnType; - constructor(public readonly client: L1Clients['publicClient'], address: Hex) { + constructor(public readonly client: ViemPublicClient, address: Hex) { this.proposer = getContract({ address, abi: SlashingProposerAbi, client }); } diff --git a/yarn-project/ethereum/src/deploy_l1_contracts.test.ts b/yarn-project/ethereum/src/deploy_l1_contracts.test.ts index cfd59e928adb..1d5a46063453 100644 --- a/yarn-project/ethereum/src/deploy_l1_contracts.test.ts +++ b/yarn-project/ethereum/src/deploy_l1_contracts.test.ts @@ -57,7 +57,7 @@ describe('deploy_l1_contracts', () => { }); const deploy = (args: Partial = {}) => - deployL1Contracts(rpcUrl!, privateKey, createEthereumChain(rpcUrl!, chainId).chainInfo, logger, { + deployL1Contracts([rpcUrl!], privateKey, createEthereumChain([rpcUrl!], chainId).chainInfo, logger, { ...DefaultL1ContractsConfig, salt: undefined, vkTreeRoot, diff --git a/yarn-project/ethereum/src/deploy_l1_contracts.ts b/yarn-project/ethereum/src/deploy_l1_contracts.ts index 3cdd77fbc552..062fc50a60d3 100644 --- a/yarn-project/ethereum/src/deploy_l1_contracts.ts +++ b/yarn-project/ethereum/src/deploy_l1_contracts.ts @@ -37,16 +37,13 @@ import { import type { Abi, Narrow } from 'abitype'; import { - type Account, type Chain, type Hex, - type HttpTransport, - type PublicClient, - type WalletClient, concatHex, createPublicClient, createWalletClient, encodeDeployData, + fallback, getAddress, getContract, getContractAddress, @@ -64,22 +61,22 @@ import { RegistryContract } from './contracts/registry.js'; import { RollupContract } from './contracts/rollup.js'; import type { L1ContractAddresses } from './l1_contract_addresses.js'; import { L1TxUtils, type L1TxUtilsConfig, defaultL1TxUtilsConfig } from './l1_tx_utils.js'; -import type { L1Clients } from './types.js'; +import type { L1Clients, ViemPublicClient, ViemWalletClient } from './types.js'; export const DEPLOYER_ADDRESS: Hex = '0x4e59b44847b379578588920cA78FbF26c0B4956C'; /** * Return type of the deployL1Contract function. */ -export type DeployL1Contracts = { +export type DeployL1ContractsReturnType = { /** * Wallet Client Type. */ - walletClient: WalletClient; + walletClient: ViemWalletClient; /** * Public Client Type. */ - publicClient: PublicClient; + publicClient: ViemPublicClient; /** * The currently deployed l1 contract addresses */ @@ -211,13 +208,13 @@ export interface DeployL1ContractsArgs extends L1ContractsConfig { /** * Creates a wallet and a public viem client for interacting with L1. - * @param rpcUrl - RPC URL to connect to L1. + * @param rpcUrls - List of RPC URLs to connect to L1. * @param mnemonicOrPrivateKeyOrHdAccount - Mnemonic or account for the wallet client. * @param chain - Optional chain spec (defaults to local foundry). * @returns - A wallet and a public client. */ export function createL1Clients( - rpcUrl: string, + rpcUrls: string[], mnemonicOrPrivateKeyOrHdAccount: string | `0x${string}` | HDAccount | PrivateKeyAccount, chain: Chain = foundry, ): L1Clients { @@ -238,11 +235,11 @@ export function createL1Clients( const walletClient = createWalletClient({ account: hdAccount, chain, - transport: http(rpcUrl), + transport: fallback(rpcUrls.map(url => http(url))), }).extend(publicActions); const publicClient = createPublicClient({ chain, - transport: http(rpcUrl), + transport: fallback(rpcUrls.map(url => http(url))), pollingInterval: 100, }); @@ -250,7 +247,7 @@ export function createL1Clients( } export const deployRollupAndPeriphery = async ( - rpcUrl: string, + rpcUrls: string[], chain: Chain, account: HDAccount | PrivateKeyAccount, args: DeployL1ContractsArgs, @@ -261,7 +258,7 @@ export const deployRollupAndPeriphery = async ( logger: Logger, txUtilsConfig: L1TxUtilsConfig, ) => { - const { walletClient, publicClient } = createL1Clients(rpcUrl, account, chain); + const { walletClient, publicClient } = createL1Clients(rpcUrls, account, chain); const deployer = new L1Deployer(walletClient, publicClient, args.salt, logger, txUtilsConfig); const rollup = await deployRollup(walletClient, publicClient, deployer, args, addresses, logger); @@ -389,7 +386,7 @@ export const deployRollup = async ( /** * Deploys the aztec L1 contracts; Rollup & (optionally) Decoder Helper. - * @param rpcUrl - URL of the ETH RPC to use for deployment. + * @param rpcUrls - List of URLs of the ETH RPC to use for deployment. * @param account - Private Key or HD Account that will deploy the contracts. * @param chain - The chain instance to deploy to. * @param logger - A logger object. @@ -397,36 +394,36 @@ export const deployRollup = async ( * @returns A list of ETH addresses of the deployed contracts. */ export const deployL1Contracts = async ( - rpcUrl: string, + rpcUrls: string[], account: HDAccount | PrivateKeyAccount, chain: Chain, logger: Logger, args: DeployL1ContractsArgs, txUtilsConfig: L1TxUtilsConfig = defaultL1TxUtilsConfig, -): Promise => { +): Promise => { // We are assuming that you are running this on a local anvil node which have 1s block times // To align better with actual deployment, we update the block interval to 12s - // The code is same as `setBlockInterval` in `cheat_codes.ts` + const { walletClient, publicClient } = createL1Clients(rpcUrls, account, chain); + const rpcCall = async (method: string, params: any[]) => { - const paramsString = JSON.stringify(params); - const content = { - body: `{"jsonrpc":"2.0", "method": "${method}", "params": ${paramsString}, "id": 1}`, - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - }; - return await (await fetch(rpcUrl, content)).json(); + logger.info(`Calling ${method} with params: ${JSON.stringify(params)}`); + return (await publicClient.transport.request({ + method, + params, + })) as any; }; + if (isAnvilTestChain(chain.id)) { - const res = await rpcCall('anvil_setBlockTimestampInterval', [args.ethereumSlotDuration]); - if (res.error) { - throw new Error(`Error setting block interval: ${res.error.message}`); + try { + await rpcCall('anvil_setBlockTimestampInterval', [args.ethereumSlotDuration]); + logger.warn(`Set block interval to ${args.ethereumSlotDuration}`); + } catch (e) { + logger.error(`Error setting block interval: ${e}`); } - logger.warn(`Set block interval to ${args.ethereumSlotDuration}`); } logger.verbose(`Deploying contracts from ${account.address.toString()}`); - const { walletClient, publicClient } = createL1Clients(rpcUrl, account, chain); // Governance stuff const deployer = new L1Deployer(walletClient, publicClient, args.salt, logger, txUtilsConfig); @@ -623,8 +620,8 @@ class L1Deployer { private txHashes: Hex[] = []; private l1TxUtils: L1TxUtils; constructor( - private walletClient: WalletClient, - private publicClient: PublicClient, + private walletClient: ViemWalletClient, + private publicClient: ViemPublicClient, maybeSalt: number | undefined, private logger: Logger, private txUtilsConfig?: L1TxUtilsConfig, @@ -668,8 +665,8 @@ class L1Deployer { * @returns The ETH address the contract was deployed to. */ export async function deployL1Contract( - walletClient: WalletClient, - publicClient: PublicClient, + walletClient: ViemWalletClient, + publicClient: ViemPublicClient, abi: Narrow, bytecode: Hex, args: readonly unknown[] = [], diff --git a/yarn-project/ethereum/src/eth_cheat_codes.ts b/yarn-project/ethereum/src/eth_cheat_codes.ts index 4999c584df39..6f12343c66bb 100644 --- a/yarn-project/ethereum/src/eth_cheat_codes.ts +++ b/yarn-project/ethereum/src/eth_cheat_codes.ts @@ -3,32 +3,37 @@ import { keccak256 } from '@aztec/foundation/crypto'; import type { EthAddress } from '@aztec/foundation/eth-address'; import { createLogger } from '@aztec/foundation/log'; -import type { Hex } from 'viem'; +import { type Hex, createPublicClient, fallback, http } from 'viem'; + +import type { ViemPublicClient } from './types.js'; /** * A class that provides utility functions for interacting with ethereum (L1). */ export class EthCheatCodes { + private publicClient: ViemPublicClient; constructor( /** * The RPC URL to use for interacting with the chain */ - public rpcUrl: string, + public rpcUrls: string[], /** * The logger to use for the eth cheatcodes */ public logger = createLogger('ethereum:cheat_codes'), - ) {} + ) { + this.publicClient = createPublicClient({ + transport: fallback(this.rpcUrls.map(url => http(url))), + }); + } async rpcCall(method: string, params: any[]) { const paramsString = JSON.stringify(params); - const content = { - body: `{"jsonrpc":"2.0", "method": "${method}", "params": ${paramsString}, "id": 1}`, - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - }; - this.logger.info(`Calling ${method} with params: ${paramsString} on ${this.rpcUrl}`); - return await (await fetch(this.rpcUrl, content)).json(); + this.logger.info(`Calling ${method} with params: ${paramsString} on ${this.rpcUrls.join(', ')}`); + return (await this.publicClient.transport.request({ + method, + params, + })) as any; } /** @@ -38,7 +43,7 @@ export class EthCheatCodes { public async isAutoMining(): Promise { try { const res = await this.rpcCall('anvil_getAutomine', []); - return res.result; + return res; } catch (err) { this.logger.error(`Calling "anvil_getAutomine" failed with:`, err); } @@ -51,7 +56,7 @@ export class EthCheatCodes { */ public async blockNumber(): Promise { const res = await this.rpcCall('eth_blockNumber', []); - return parseInt(res.result, 16); + return parseInt(res, 16); } /** @@ -60,7 +65,7 @@ export class EthCheatCodes { */ public async chainId(): Promise { const res = await this.rpcCall('eth_chainId', []); - return parseInt(res.result, 16); + return parseInt(res, 16); } /** @@ -69,7 +74,7 @@ export class EthCheatCodes { */ public async timestamp(): Promise { const res = await this.rpcCall('eth_getBlockByNumber', ['latest', true]); - return parseInt(res.result.timestamp, 16); + return parseInt(res.timestamp, 16); } /** @@ -82,9 +87,10 @@ export class EthCheatCodes { } private async doMine(numberOfBlocks = 1): Promise { - const res = await this.rpcCall('hardhat_mine', [numberOfBlocks]); - if (res.error) { - throw new Error(`Error mining: ${res.error.message}`); + try { + await this.rpcCall('hardhat_mine', [numberOfBlocks]); + } catch (err) { + throw new Error(`Error mining: ${err}`); } } @@ -92,9 +98,10 @@ export class EthCheatCodes { * Mines a single block with evm_mine */ public async evmMine(): Promise { - const res = await this.rpcCall('evm_mine', []); - if (res.error) { - throw new Error(`Error mining: ${res.error.message}`); + try { + await this.rpcCall('evm_mine', []); + } catch (err) { + throw new Error(`Error mining: ${err}`); } } @@ -104,9 +111,10 @@ export class EthCheatCodes { * @param balance - The balance to set */ public async setBalance(account: EthAddress, balance: bigint): Promise { - const res = await this.rpcCall('anvil_setBalance', [account.toString(), toHex(balance)]); - if (res.error) { - throw new Error(`Error setting balance for ${account}: ${res.error.message}`); + try { + await this.rpcCall('anvil_setBalance', [account.toString(), toHex(balance)]); + } catch (err) { + throw new Error(`Error setting balance for ${account}: ${err}`); } this.logger.warn(`Set balance for ${account} to ${balance}`); } @@ -116,9 +124,10 @@ export class EthCheatCodes { * @param interval - The interval to use between blocks */ public async setBlockInterval(interval: number): Promise { - const res = await this.rpcCall('anvil_setBlockTimestampInterval', [interval]); - if (res.error) { - throw new Error(`Error setting block interval: ${res.error.message}`); + try { + await this.rpcCall('anvil_setBlockTimestampInterval', [interval]); + } catch (err) { + throw new Error(`Error setting block interval: ${err}`); } this.logger.warn(`Set L1 block interval to ${interval}`); } @@ -128,9 +137,10 @@ export class EthCheatCodes { * @param baseFee - The base fee to set */ public async setNextBlockBaseFeePerGas(baseFee: bigint | number): Promise { - const res = await this.rpcCall('anvil_setNextBlockBaseFeePerGas', [baseFee.toString()]); - if (res.error) { - throw new Error(`Error setting next block base fee per gas: ${res.error.message}`); + try { + await this.rpcCall('anvil_setNextBlockBaseFeePerGas', [baseFee.toString()]); + } catch (err) { + throw new Error(`Error setting next block base fee per gas: ${err}`); } this.logger.warn(`Set L1 next block base fee per gas to ${baseFee}`); } @@ -140,9 +150,10 @@ export class EthCheatCodes { * @param seconds - The interval to use between blocks */ public async setIntervalMining(seconds: number): Promise { - const res = await this.rpcCall('anvil_setIntervalMining', [seconds]); - if (res.error) { - throw new Error(`Error setting interval mining: ${res.error.message}`); + try { + await this.rpcCall('anvil_setIntervalMining', [seconds]); + } catch (err) { + throw new Error(`Error setting interval mining: ${err}`); } this.logger.warn(`Set L1 interval mining to ${seconds} seconds`); } @@ -152,9 +163,10 @@ export class EthCheatCodes { * @param automine - The automine status to set */ public async setAutomine(automine: boolean): Promise { - const res = await this.rpcCall('anvil_setAutomine', [automine]); - if (res.error) { - throw new Error(`Error setting automine: ${res.error.message}`); + try { + await this.rpcCall('anvil_setAutomine', [automine]); + } catch (err) { + throw new Error(`Error setting automine: ${err}`); } this.logger.warn(`Set L1 automine to ${automine}`); } @@ -164,9 +176,10 @@ export class EthCheatCodes { * @param txHash - The transaction hash */ public async dropTransaction(txHash: Hex): Promise { - const res = await this.rpcCall('anvil_dropTransaction', [txHash]); - if (res.error) { - throw new Error(`Error dropping transaction: ${res.error.message}`); + try { + await this.rpcCall('anvil_dropTransaction', [txHash]); + } catch (err) { + throw new Error(`Error dropping transaction: ${err}`); } this.logger.warn(`Dropped transaction ${txHash}`); } @@ -176,9 +189,10 @@ export class EthCheatCodes { * @param timestamp - The timestamp to set the next block to */ public async setNextBlockTimestamp(timestamp: number): Promise { - const res = await this.rpcCall('evm_setNextBlockTimestamp', [timestamp]); - if (res.error) { - throw new Error(`Error setting next block timestamp: ${res.error.message}`); + try { + await this.rpcCall('evm_setNextBlockTimestamp', [timestamp]); + } catch (err: any) { + throw new Error(`Error setting next block timestamp: ${err.message}`); } this.logger.warn(`Set L1 next block timestamp to ${timestamp}`); } @@ -188,9 +202,10 @@ export class EthCheatCodes { * @param timestamp - The timestamp to set the next block to */ public async warp(timestamp: number | bigint, silent = false): Promise { - const res = await this.rpcCall('evm_setNextBlockTimestamp', [Number(timestamp)]); - if (res.error) { - throw new Error(`Error warping: ${res.error.message}`); + try { + await this.rpcCall('evm_setNextBlockTimestamp', [Number(timestamp)]); + } catch (err) { + throw new Error(`Error warping: ${err}`); } await this.doMine(); if (!silent) { @@ -206,7 +221,7 @@ export class EthCheatCodes { */ public async load(contract: EthAddress, slot: bigint): Promise { const res = await this.rpcCall('eth_getStorageAt', [contract.toString(), toHex(slot), 'latest']); - return BigInt(res.result); + return BigInt(res); } /** @@ -217,9 +232,10 @@ export class EthCheatCodes { */ public async store(contract: EthAddress, slot: bigint, value: bigint): Promise { // for the rpc call, we need to change value to be a 32 byte hex string. - const res = await this.rpcCall('hardhat_setStorageAt', [contract.toString(), toHex(slot), toHex(value, true)]); - if (res.error) { - throw new Error(`Error setting storage for contract ${contract} at ${slot}: ${res.error.message}`); + try { + await this.rpcCall('hardhat_setStorageAt', [contract.toString(), toHex(slot), toHex(value, true)]); + } catch (err) { + throw new Error(`Error setting storage for contract ${contract} at ${slot}: ${err}`); } this.logger.warn(`Set L1 storage for contract ${contract} at ${slot} to ${value}`); } @@ -241,9 +257,10 @@ export class EthCheatCodes { * @param who - The address to impersonate */ public async startImpersonating(who: EthAddress | Hex): Promise { - const res = await this.rpcCall('hardhat_impersonateAccount', [who.toString()]); - if (res.error) { - throw new Error(`Error impersonating ${who}: ${res.error.message}`); + try { + await this.rpcCall('hardhat_impersonateAccount', [who.toString()]); + } catch (err) { + throw new Error(`Error impersonating ${who}: ${err}`); } this.logger.warn(`Impersonating ${who}`); } @@ -253,9 +270,10 @@ export class EthCheatCodes { * @param who - The address to stop impersonating */ public async stopImpersonating(who: EthAddress | Hex): Promise { - const res = await this.rpcCall('hardhat_stopImpersonatingAccount', [who.toString()]); - if (res.error) { - throw new Error(`Error when stopping the impersonation of ${who}: ${res.error.message}`); + try { + await this.rpcCall('hardhat_stopImpersonatingAccount', [who.toString()]); + } catch (err) { + throw new Error(`Error when stopping the impersonation of ${who}: ${err}`); } this.logger.warn(`Stopped impersonating ${who}`); } @@ -266,9 +284,10 @@ export class EthCheatCodes { * @param bytecode - The bytecode to set */ public async etch(contract: EthAddress, bytecode: `0x${string}`): Promise { - const res = await this.rpcCall('hardhat_setCode', [contract.toString(), bytecode]); - if (res.error) { - throw new Error(`Error setting bytecode for ${contract}: ${res.error.message}`); + try { + await this.rpcCall('hardhat_setCode', [contract.toString(), bytecode]); + } catch (err) { + throw new Error(`Error setting bytecode for ${contract}: ${err}`); } this.logger.warn(`Set bytecode for ${contract} to ${bytecode}`); } @@ -280,7 +299,7 @@ export class EthCheatCodes { */ public async getBytecode(contract: EthAddress): Promise<`0x${string}`> { const res = await this.rpcCall('eth_getCode', [contract.toString(), 'latest']); - return res.result; + return res; } /** @@ -290,6 +309,6 @@ export class EthCheatCodes { */ public async getRawTransaction(txHash: Hex): Promise<`0x${string}`> { const res = await this.rpcCall('debug_getRawTransaction', [txHash]); - return res.result; + return res; } } diff --git a/yarn-project/ethereum/src/l1_reader.ts b/yarn-project/ethereum/src/l1_reader.ts index b9b9f75438c6..c35540b46933 100644 --- a/yarn-project/ethereum/src/l1_reader.ts +++ b/yarn-project/ethereum/src/l1_reader.ts @@ -5,7 +5,7 @@ import { type L1ContractAddresses, l1ContractAddressesMapping } from './l1_contr /** Configuration of the L1GlobalReader. */ export interface L1ReaderConfig { /** The RPC Url of the ethereum host. */ - l1RpcUrl: string; + l1RpcUrls: string[]; /** The chain ID of the ethereum host. */ l1ChainId: number; /** The deployed l1 contract addresses */ @@ -15,9 +15,10 @@ export interface L1ReaderConfig { } export const l1ReaderConfigMappings: ConfigMappingsType = { - l1RpcUrl: { - env: 'ETHEREUM_HOST', + l1RpcUrls: { + env: 'ETHEREUM_HOSTS', description: 'The RPC Url of the ethereum host.', + parseEnv: (val: string) => val.split(',').map(url => url.trim()), }, l1ChainId: { env: 'L1_CHAIN_ID', diff --git a/yarn-project/ethereum/src/l1_tx_utils.test.ts b/yarn-project/ethereum/src/l1_tx_utils.test.ts index 97c364e21eb0..403a0f8748b4 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.test.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.test.ts @@ -4,17 +4,7 @@ import { createLogger } from '@aztec/foundation/log'; import { sleep } from '@aztec/foundation/sleep'; import type { Anvil } from '@viem/anvil'; -import { - type Abi, - type Account, - type Chain, - type HttpTransport, - type PublicClient, - type WalletClient, - createPublicClient, - createWalletClient, - http, -} from 'viem'; +import { type Abi, createPublicClient, createWalletClient, fallback, http } from 'viem'; import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts'; import { foundry } from 'viem/chains'; @@ -22,6 +12,7 @@ import { EthCheatCodes } from './eth_cheat_codes.js'; import { defaultL1TxUtilsConfig } from './l1_tx_utils.js'; import { L1TxUtilsWithBlobs } from './l1_tx_utils_with_blobs.js'; import { startAnvil } from './test/start_anvil.js'; +import type { SimpleViemWalletClient, ViemPublicClient } from './types.js'; import { formatViemError } from './utils.js'; const MNEMONIC = 'test test test test test test test test test test test junk'; @@ -37,8 +28,8 @@ export type PendingTransaction = { describe('GasUtils', () => { let gasUtils: L1TxUtilsWithBlobs; - let walletClient: WalletClient; - let publicClient: PublicClient; + let walletClient: SimpleViemWalletClient; + let publicClient: ViemPublicClient; let anvil: Anvil; let cheatCodes: EthCheatCodes; const initialBaseFee = WEI_CONST; // 1 gwei @@ -47,7 +38,7 @@ describe('GasUtils', () => { beforeAll(async () => { const { anvil: anvilInstance, rpcUrl } = await startAnvil({ l1BlockTime: 1 }); anvil = anvilInstance; - cheatCodes = new EthCheatCodes(rpcUrl); + cheatCodes = new EthCheatCodes([rpcUrl]); const hdAccount = mnemonicToAccount(MNEMONIC, { addressIndex: 0 }); const privKeyRaw = hdAccount.getHdKey().privateKey; if (!privKeyRaw) { @@ -57,12 +48,12 @@ describe('GasUtils', () => { const account = privateKeyToAccount(`0x${privKey}`); publicClient = createPublicClient({ - transport: http(rpcUrl), + transport: fallback([http(rpcUrl)]), chain: foundry, }); walletClient = createWalletClient({ - transport: http(rpcUrl), + transport: fallback([http(rpcUrl)]), chain: foundry, account, }); diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index c7b1c075c44f..614379d18341 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -15,22 +15,19 @@ import { type Address, type BaseError, type BlockOverrides, - type Chain, type ContractFunctionExecutionError, type GetTransactionReturnType, type Hex, - type HttpTransport, MethodNotFoundRpcError, MethodNotSupportedRpcError, type StateOverride, type TransactionReceipt, - type WalletClient, formatGwei, getContractError, hexToBytes, } from 'viem'; -import type { L1Clients } from './types.js'; +import type { ViemPublicClient, ViemWalletClient } from './types.js'; import { formatViemError } from './utils.js'; // 1_000_000_000 Gwei = 1 ETH @@ -206,8 +203,8 @@ export class L1TxUtils { private interrupted = false; constructor( - public publicClient: L1Clients['publicClient'], - public walletClient: WalletClient, + public publicClient: ViemPublicClient, + public walletClient: ViemWalletClient, protected readonly logger?: Logger, config?: Partial, ) { diff --git a/yarn-project/ethereum/src/queries.ts b/yarn-project/ethereum/src/queries.ts index 36fde9203c8e..a8676d57d3d2 100644 --- a/yarn-project/ethereum/src/queries.ts +++ b/yarn-project/ethereum/src/queries.ts @@ -1,15 +1,14 @@ import type { EthAddress } from '@aztec/foundation/eth-address'; -import type { Chain, HttpTransport, PublicClient } from 'viem'; - import type { L1ContractsConfig } from './config.js'; import { GovernanceContract } from './contracts/governance.js'; import { RollupContract } from './contracts/rollup.js'; import type { L1ContractAddresses } from './l1_contract_addresses.js'; +import type { ViemPublicClient } from './types.js'; /** Given the Governance contract address, reads the addresses from all other contracts from L1. */ export async function getL1ContractsAddresses( - publicClient: PublicClient, + publicClient: ViemPublicClient, governanceAddress: EthAddress, ): Promise> { const governance = new GovernanceContract(publicClient, governanceAddress.toString()); @@ -26,7 +25,7 @@ export async function getL1ContractsAddresses( /** Reads the L1ContractsConfig from L1 contracts. */ export async function getL1ContractsConfig( - publicClient: PublicClient, + publicClient: ViemPublicClient, addresses: { governanceAddress: EthAddress; rollupAddress?: EthAddress }, ): Promise & { l1StartBlock: bigint; l1GenesisTime: bigint }> { const governance = new GovernanceContract(publicClient, addresses.governanceAddress.toString()); diff --git a/yarn-project/ethereum/src/test/eth_cheat_codes_with_state.ts b/yarn-project/ethereum/src/test/eth_cheat_codes_with_state.ts index 8f28d8961b08..7679aa87f2c8 100644 --- a/yarn-project/ethereum/src/test/eth_cheat_codes_with_state.ts +++ b/yarn-project/ethereum/src/test/eth_cheat_codes_with_state.ts @@ -12,12 +12,13 @@ export class EthCheatCodesWithState extends EthCheatCodes { * @param fileName - The file name to dump state into */ public async dumpChainState(fileName: string): Promise { - const res = await this.rpcCall('hardhat_dumpState', []); - if (res.error) { - throw new Error(`Error dumping state: ${res.error.message}`); + let res: any; + try { + res = await this.rpcCall('hardhat_dumpState', []); + } catch (e) { + throw new Error(`Error dumping state: ${e}`); } - const jsonContent = JSON.stringify(res.result); - fs.writeFileSync(`${fileName}.json`, jsonContent, 'utf8'); + fs.writeFileSync(`${fileName}.json`, res, 'utf8'); this.logger.verbose(`Dumped state to ${fileName}`); } @@ -27,9 +28,10 @@ export class EthCheatCodesWithState extends EthCheatCodes { */ public async loadChainState(fileName: string): Promise { const data = JSON.parse(fs.readFileSync(`${fileName}.json`, 'utf8')); - const res = await this.rpcCall('hardhat_loadState', [data]); - if (res.error) { - throw new Error(`Error loading state: ${res.error.message}`); + try { + await this.rpcCall('hardhat_loadState', [data]); + } catch (e) { + throw new Error(`Error loading state: ${e}`); } this.logger.verbose(`Loaded state from ${fileName}`); } diff --git a/yarn-project/ethereum/src/test/tx_delayer.test.ts b/yarn-project/ethereum/src/test/tx_delayer.test.ts index 39fc43b98f40..a7c767e15bf0 100644 --- a/yarn-project/ethereum/src/test/tx_delayer.test.ts +++ b/yarn-project/ethereum/src/test/tx_delayer.test.ts @@ -2,11 +2,11 @@ import { type Logger, createLogger } from '@aztec/foundation/log'; import { TestERC20Abi, TestERC20Bytecode } from '@aztec/l1-artifacts'; import type { Anvil } from '@viem/anvil'; -import { type PrivateKeyAccount, createWalletClient, getContract, http, publicActions } from 'viem'; +import { type PrivateKeyAccount, createWalletClient, fallback, getContract, http, publicActions } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { foundry } from 'viem/chains'; -import type { ViemClient } from '../types.js'; +import type { ExtendedViemWalletClient } from '../types.js'; import { startAnvil } from './start_anvil.js'; import { type Delayer, withDelayer } from './tx_delayer.js'; @@ -15,7 +15,7 @@ describe('tx_delayer', () => { let rpcUrl: string; let logger: Logger; let account: PrivateKeyAccount; - let client: ViemClient; + let client: ExtendedViemWalletClient; let delayer: Delayer; const ETHEREUM_SLOT_DURATION = 2; @@ -26,12 +26,13 @@ describe('tx_delayer', () => { }); beforeEach(() => { - const transport = http(rpcUrl); account = privateKeyToAccount('0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'); - ({ client, delayer } = withDelayer( - createWalletClient({ transport, chain: foundry, account }).extend(publicActions), - { ethereumSlotDuration: ETHEREUM_SLOT_DURATION }, - )); + const _client = createWalletClient({ + transport: fallback([http(rpcUrl)]), + chain: foundry, + account, + }).extend(publicActions); + ({ client, delayer } = withDelayer(_client, { ethereumSlotDuration: ETHEREUM_SLOT_DURATION })); }); const receiptNotFound = expect.objectContaining({ name: 'TransactionReceiptNotFoundError' }); diff --git a/yarn-project/ethereum/src/test/tx_delayer.ts b/yarn-project/ethereum/src/test/tx_delayer.ts index c2ecd25fbcd7..64f4ed31df78 100644 --- a/yarn-project/ethereum/src/test/tx_delayer.ts +++ b/yarn-project/ethereum/src/test/tx_delayer.ts @@ -7,13 +7,14 @@ import { type Client, type Hex, type PublicClient, - type WalletClient, keccak256, parseTransaction, publicActions, walletActions, } from 'viem'; +import type { ViemWalletClient } from '../types.js'; + export function waitUntilBlock(client: T, blockNumber: number | bigint, logger?: Logger) { const publicClient = 'getBlockNumber' in client && typeof client.getBlockNumber === 'function' @@ -93,7 +94,7 @@ class DelayerImpl implements Delayer { * The delayer can be used to hold off the next tx to be sent until a given block number. * TODO(#10824): This doesn't play along well with blob txs for some reason. */ -export function withDelayer( +export function withDelayer( client: T, opts: { ethereumSlotDuration: bigint | number }, ): { client: T; delayer: Delayer } { diff --git a/yarn-project/ethereum/src/types.ts b/yarn-project/ethereum/src/types.ts index 3669fb245e32..5c7c895ee1b3 100644 --- a/yarn-project/ethereum/src/types.ts +++ b/yarn-project/ethereum/src/types.ts @@ -1,32 +1,33 @@ import type { + Account, Chain, Client, + FallbackTransport, HttpTransport, - PrivateKeyAccount, PublicActions, PublicClient, PublicRpcSchema, WalletActions, + WalletClient, WalletRpcSchema, } from 'viem'; -/** - * Type for a viem wallet and public client using a local private key. - * Created as: `createWalletClient({ account: privateKeyToAccount(key), transport: http(url), chain }).extend(publicActions)` - */ -export type ViemClient = Client< - HttpTransport, +/** Type for a viem public client */ +export type ViemPublicClient = PublicClient, Chain>; + +export type SimpleViemWalletClient = WalletClient, Chain, Account>; + +export type ExtendedViemWalletClient = Client< + FallbackTransport, Chain, - PrivateKeyAccount, + Account, [...PublicRpcSchema, ...WalletRpcSchema], - PublicActions & WalletActions + PublicActions, Chain> & WalletActions >; -/** Type for a viem public client */ -export type ViemPublicClient = PublicClient; +export type ViemWalletClient = SimpleViemWalletClient | ExtendedViemWalletClient; -/** Both L1 clients */ export type L1Clients = { publicClient: ViemPublicClient; - walletClient: ViemClient; + walletClient: ExtendedViemWalletClient; }; diff --git a/yarn-project/foundation/src/config/env_var.ts b/yarn-project/foundation/src/config/env_var.ts index f7f9e1153f29..4617444d5d27 100644 --- a/yarn-project/foundation/src/config/env_var.ts +++ b/yarn-project/foundation/src/config/env_var.ts @@ -47,7 +47,8 @@ export type EnvVar = | 'DEBUG_P2P_DISABLE_COLOCATION_PENALTY' | 'DEPLOY_AZTEC_CONTRACTS_SALT' | 'DEPLOY_AZTEC_CONTRACTS' - | 'ETHEREUM_HOST' + | 'ENFORCE_FEES' + | 'ETHEREUM_HOSTS' | 'FEE_JUICE_CONTRACT_ADDRESS' | 'FEE_JUICE_PORTAL_CONTRACT_ADDRESS' | 'FEE_RECIPIENT' diff --git a/yarn-project/proof-verifier/src/config.ts b/yarn-project/proof-verifier/src/config.ts index 9edd368779a8..e9eb22fff044 100644 --- a/yarn-project/proof-verifier/src/config.ts +++ b/yarn-project/proof-verifier/src/config.ts @@ -8,8 +8,8 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import { type TelemetryClientConfig, telemetryClientConfigMappings } from '@aztec/telemetry-client'; export type ProofVerifierConfig = { - /** The URL to an L1 node */ - l1Url: string; + /** The URLs to an L1 node */ + l1Urls: string[]; /** The L1 chain ID */ l1ChainId: number; /** Start block number */ @@ -30,9 +30,10 @@ export type ProofVerifierConfig = { export const proofVerifierConfigMappings: ConfigMappingsType = { ...telemetryClientConfigMappings, - l1Url: { - env: 'ETHEREUM_HOST', + l1Urls: { + env: 'ETHEREUM_HOSTS', description: 'The URL to an L1 node', + parseEnv: (val: string) => val.split(',').map(url => url.trim()), }, l1ChainId: { env: 'L1_CHAIN_ID', diff --git a/yarn-project/proof-verifier/src/proof_verifier.ts b/yarn-project/proof-verifier/src/proof_verifier.ts index e64a432401ed..da0d03de9217 100644 --- a/yarn-project/proof-verifier/src/proof_verifier.ts +++ b/yarn-project/proof-verifier/src/proof_verifier.ts @@ -1,6 +1,6 @@ import { retrieveL2ProofsFromRollup } from '@aztec/archiver/data-retrieval'; import { BBCircuitVerifier } from '@aztec/bb-prover'; -import { createEthereumChain } from '@aztec/ethereum'; +import { type ViemPublicClient, createEthereumChain } from '@aztec/ethereum'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { RunningPromise } from '@aztec/foundation/running-promise'; import { @@ -14,7 +14,7 @@ import { trackSpan, } from '@aztec/telemetry-client'; -import { type PublicClient, createPublicClient, http } from 'viem'; +import { createPublicClient, fallback, http } from 'viem'; import type { ProofVerifierConfig } from './config.js'; @@ -30,7 +30,7 @@ export class ProofVerifier implements Traceable { constructor( private config: ProofVerifierConfig, - private client: PublicClient, + private client: ViemPublicClient, private verifier: BBCircuitVerifier, telemetryClient: TelemetryClient, private logger: Logger, @@ -48,8 +48,8 @@ export class ProofVerifier implements Traceable { const logger = createLogger('proof-verifier:block-verifier-bot'); const verifier = await BBCircuitVerifier.new(config, logger); const client = createPublicClient({ - chain: createEthereumChain(config.l1Url, config.l1ChainId).chainInfo, - transport: http(config.l1Url), + chain: createEthereumChain(config.l1Urls, config.l1ChainId).chainInfo, + transport: fallback(config.l1Urls.map(url => http(url))), pollingInterval: config.viemPollingIntervalMS, }); diff --git a/yarn-project/prover-client/src/proving_broker/proving_broker_database/broker_persisted_database.test.ts b/yarn-project/prover-client/src/proving_broker/proving_broker_database/broker_persisted_database.test.ts index 92b9f356722d..e53f1b9548ca 100644 --- a/yarn-project/prover-client/src/proving_broker/proving_broker_database/broker_persisted_database.test.ts +++ b/yarn-project/prover-client/src/proving_broker/proving_broker_database/broker_persisted_database.test.ts @@ -280,7 +280,7 @@ describe('ProvingBrokerPersistedDatabase', () => { batchSize = 5; config = { - dataStoreMapSizeKB: 1024 * 1024 * 1024, // 1GB + dataStoreMapSizeKB: 1024 * 1024, // 1GB dataDirectory: directory, proverBrokerJobMaxRetries: 1, proverBrokerJobTimeoutMs: 1000, diff --git a/yarn-project/prover-node/src/factory.ts b/yarn-project/prover-node/src/factory.ts index c6052c505ec5..992dc2091a13 100644 --- a/yarn-project/prover-node/src/factory.ts +++ b/yarn-project/prover-node/src/factory.ts @@ -52,9 +52,9 @@ export async function createProverNode( const broker = deps.broker ?? (await createAndStartProvingBroker(config, telemetry)); const prover = await createProverClient(config, worldStateSynchronizer, broker, telemetry); - const { l1RpcUrl: rpcUrl, l1ChainId: chainId, publisherPrivateKey } = config; - const chain = createEthereumChain(rpcUrl, chainId); - const { publicClient, walletClient } = createL1Clients(rpcUrl, publisherPrivateKey, chain.chainInfo); + const { l1RpcUrls: rpcUrls, l1ChainId: chainId, publisherPrivateKey } = config; + const chain = createEthereumChain(rpcUrls, chainId); + const { publicClient, walletClient } = createL1Clients(rpcUrls, publisherPrivateKey, chain.chainInfo); const rollupContract = new RollupContract(publicClient, config.l1Contracts.rollupAddress.toString()); diff --git a/yarn-project/sequencer-client/src/client/sequencer-client.ts b/yarn-project/sequencer-client/src/client/sequencer-client.ts index 8538f9784b22..b97c776092ac 100644 --- a/yarn-project/sequencer-client/src/client/sequencer-client.ts +++ b/yarn-project/sequencer-client/src/client/sequencer-client.ts @@ -77,10 +77,10 @@ export class SequencerClient { l1ToL2MessageSource, telemetry: telemetryClient, } = deps; - const { l1RpcUrl: rpcUrl, l1ChainId: chainId, publisherPrivateKey } = config; - const chain = createEthereumChain(rpcUrl, chainId); + const { l1RpcUrls: rpcUrls, l1ChainId: chainId, publisherPrivateKey } = config; + const chain = createEthereumChain(rpcUrls, chainId); const log = createLogger('sequencer-client'); - const { publicClient, walletClient } = createL1Clients(rpcUrl, publisherPrivateKey, chain.chainInfo); + const { publicClient, walletClient } = createL1Clients(rpcUrls, publisherPrivateKey, chain.chainInfo); const l1TxUtils = deps.l1TxUtils ?? new L1TxUtilsWithBlobs(publicClient, walletClient, log, config); const rollupContract = new RollupContract(publicClient, config.l1Contracts.rollupAddress.toString()); const [l1GenesisTime, slotDuration] = await Promise.all([ @@ -113,7 +113,7 @@ export class SequencerClient { (await EpochCache.create( config.l1Contracts.rollupAddress, { - l1RpcUrl: rpcUrl, + l1RpcUrls: rpcUrls, l1ChainId: chainId, viemPollingIntervalMS: config.viemPollingIntervalMS, aztecSlotDuration: config.aztecSlotDuration, diff --git a/yarn-project/sequencer-client/src/global_variable_builder/global_builder.ts b/yarn-project/sequencer-client/src/global_variable_builder/global_builder.ts index 7a1036fbd8e3..9f5ff12d7c9d 100644 --- a/yarn-project/sequencer-client/src/global_variable_builder/global_builder.ts +++ b/yarn-project/sequencer-client/src/global_variable_builder/global_builder.ts @@ -1,4 +1,5 @@ import { type L1ContractsConfig, type L1ReaderConfig, createEthereumChain } from '@aztec/ethereum'; +import type { ViemPublicClient } from '@aztec/ethereum'; import type { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; import { createLogger } from '@aztec/foundation/log'; @@ -8,16 +9,7 @@ import { GasFees } from '@aztec/stdlib/gas'; import type { GlobalVariableBuilder as GlobalVariableBuilderInterface } from '@aztec/stdlib/tx'; import { GlobalVariables } from '@aztec/stdlib/tx'; -import { - type GetContractReturnType, - type HttpTransport, - type PublicClient, - createPublicClient, - getAddress, - getContract, - http, -} from 'viem'; -import type * as chains from 'viem/chains'; +import { type GetContractReturnType, createPublicClient, fallback, getAddress, getContract, http } from 'viem'; /** * Simple global variables builder. @@ -25,20 +17,20 @@ import type * as chains from 'viem/chains'; export class GlobalVariableBuilder implements GlobalVariableBuilderInterface { private log = createLogger('sequencer:global_variable_builder'); - private rollupContract: GetContractReturnType>; - private publicClient: PublicClient; + private rollupContract: GetContractReturnType; + private publicClient: ViemPublicClient; private ethereumSlotDuration: number; constructor(config: L1ReaderConfig & Pick) { - const { l1RpcUrl, l1ChainId: chainId, l1Contracts } = config; + const { l1RpcUrls, l1ChainId: chainId, l1Contracts } = config; - const chain = createEthereumChain(l1RpcUrl, chainId); + const chain = createEthereumChain(l1RpcUrls, chainId); this.ethereumSlotDuration = config.ethereumSlotDuration; this.publicClient = createPublicClient({ chain: chain.chainInfo, - transport: http(chain.rpcUrl), + transport: fallback(chain.rpcUrls.map(url => http(url))), pollingInterval: config.viemPollingIntervalMS, }); diff --git a/yarn-project/sequencer-client/src/publisher/config.ts b/yarn-project/sequencer-client/src/publisher/config.ts index 747b939e4771..715db7cb76a1 100644 --- a/yarn-project/sequencer-client/src/publisher/config.ts +++ b/yarn-project/sequencer-client/src/publisher/config.ts @@ -1,6 +1,12 @@ import { type BlobSinkConfig, blobSinkConfigMapping } from '@aztec/blob-sink/client'; -import { type L1ReaderConfig, type L1TxUtilsConfig, NULL_KEY, l1TxUtilsConfigMappings } from '@aztec/ethereum'; -import { type ConfigMappingsType, getConfigFromMappings, numberConfigHelper } from '@aztec/foundation/config'; +import { + type L1ReaderConfig, + type L1TxUtilsConfig, + NULL_KEY, + l1ReaderConfigMappings, + l1TxUtilsConfigMappings, +} from '@aztec/ethereum'; +import { type ConfigMappingsType, getConfigFromMappings } from '@aztec/foundation/config'; import { EthAddress } from '@aztec/foundation/eth-address'; /** @@ -37,16 +43,7 @@ export type PublisherConfig = L1TxUtilsConfig & export const getTxSenderConfigMappings: ( scope: 'PROVER' | 'SEQ', ) => ConfigMappingsType> = (scope: 'PROVER' | 'SEQ') => ({ - l1RpcUrl: { - env: 'ETHEREUM_HOST', - description: 'The RPC Url of the ethereum host.', - }, - l1ChainId: { - env: 'L1_CHAIN_ID', - parseEnv: (val: string) => +val, - defaultValue: 31337, - description: 'The chain ID of the ethereum host.', - }, + ...l1ReaderConfigMappings, customForwarderContractAddress: { env: `CUSTOM_FORWARDER_CONTRACT_ADDRESS`, parseEnv: (val: string) => EthAddress.fromString(val), @@ -65,11 +62,6 @@ export const getTxSenderConfigMappings: ( defaultValue: 1, description: 'The number of confirmations required.', }, - viemPollingIntervalMS: { - env: `${scope}_VIEM_POLLING_INTERVAL_MS`, - description: 'The polling interval viem uses in ms', - ...numberConfigHelper(1_000), - }, }); export function getTxSenderConfigFromEnv(scope: 'PROVER' | 'SEQ'): Omit { diff --git a/yarn-project/sequencer-client/src/publisher/sequencer-publisher.test.ts b/yarn-project/sequencer-client/src/publisher/sequencer-publisher.test.ts index c690500a46df..2510e053423b 100644 --- a/yarn-project/sequencer-client/src/publisher/sequencer-publisher.test.ts +++ b/yarn-project/sequencer-client/src/publisher/sequencer-publisher.test.ts @@ -82,7 +82,7 @@ describe('SequencerPublisher', () => { l1TxUtils.getBlockNumber.mockResolvedValue(1n); const config = { blobSinkUrl: BLOB_SINK_URL, - l1RpcUrl: `http://127.0.0.1:8545`, + l1RpcUrls: [`http://127.0.0.1:8545`], l1ChainId: 1, publisherPrivateKey: `0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80`, l1Contracts: { diff --git a/yarn-project/sequencer-client/src/slasher/slasher_client.test.ts b/yarn-project/sequencer-client/src/slasher/slasher_client.test.ts index da37f4376204..f6e7b99fde9f 100644 --- a/yarn-project/sequencer-client/src/slasher/slasher_client.test.ts +++ b/yarn-project/sequencer-client/src/slasher/slasher_client.test.ts @@ -39,7 +39,7 @@ describe('In-Memory Slasher Client', () => { l1Contracts: { slashFactoryAddress: EthAddress.ZERO, } as unknown as L1ContractAddresses, - l1RpcUrl: 'http://127.0.0.1:8545', + l1RpcUrls: ['http://127.0.0.1:8545'], l1ChainId: 1, viemPollingIntervalMS: 1000, }; diff --git a/yarn-project/sequencer-client/src/slasher/slasher_client.ts b/yarn-project/sequencer-client/src/slasher/slasher_client.ts index 9ea74f1aa94b..847e8e335151 100644 --- a/yarn-project/sequencer-client/src/slasher/slasher_client.ts +++ b/yarn-project/sequencer-client/src/slasher/slasher_client.ts @@ -1,5 +1,10 @@ import { INITIAL_L2_BLOCK_NUM } from '@aztec/constants'; -import { type L1ContractsConfig, type L1ReaderConfig, createEthereumChain } from '@aztec/ethereum'; +import { + type L1ContractsConfig, + type L1ReaderConfig, + type ViemPublicClient, + createEthereumChain, +} from '@aztec/ethereum'; import { EthAddress } from '@aztec/foundation/eth-address'; import { createLogger } from '@aztec/foundation/log'; import type { AztecAsyncKVStore, AztecAsyncMap, AztecAsyncSingleton } from '@aztec/kv-store'; @@ -14,16 +19,7 @@ import { } from '@aztec/stdlib/block'; import { type TelemetryClient, WithTracer, getTelemetryClient } from '@aztec/telemetry-client'; -import { - type Chain, - type GetContractReturnType, - type HttpTransport, - type PublicClient, - createPublicClient, - getAddress, - getContract, - http, -} from 'viem'; +import { type GetContractReturnType, createPublicClient, fallback, getAddress, getContract, http } from 'viem'; /** * Enum defining the possible states of the Slasher client. @@ -100,8 +96,7 @@ export class SlasherClient extends WithTracer { private slashEvents: SlashEvent[] = []; - protected slashFactoryContract?: GetContractReturnType> = - undefined; + protected slashFactoryContract?: GetContractReturnType = undefined; // The amount to slash for a prune. // Note that we set it to 0, such that no actual slashing will happen, but the event will be fired, @@ -127,10 +122,10 @@ export class SlasherClient extends WithTracer { this.synchedProvenBlockNumber = store.openSingleton('slasher_last_proven_l2_block'); if (config.l1Contracts.slashFactoryAddress && config.l1Contracts.slashFactoryAddress !== EthAddress.ZERO) { - const chain = createEthereumChain(config.l1RpcUrl, config.l1ChainId); + const chain = createEthereumChain(config.l1RpcUrls, config.l1ChainId); const publicClient = createPublicClient({ chain: chain.chainInfo, - transport: http(chain.rpcUrl), + transport: fallback(chain.rpcUrls.map(url => http(url))), pollingInterval: config.viemPollingIntervalMS, }); diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 293884092d7e..d4e90922e55b 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -404,6 +404,7 @@ __metadata: ts-node: "npm:^10.9.1" tslib: "npm:^2.4.0" typescript: "npm:^5.0.4" + viem: "npm:2.22.8" zod: "npm:^3.23.8" languageName: unknown linkType: soft