diff --git a/.github/workflows/network-deploy.yml b/.github/workflows/network-deploy.yml index d521117338fd..49c0773a13e0 100644 --- a/.github/workflows/network-deploy.yml +++ b/.github/workflows/network-deploy.yml @@ -116,7 +116,7 @@ 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 }}" + 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 }}" steps: @@ -187,16 +187,15 @@ jobs: continue-on-error: true run: | if ${{ inputs.sepolia_deployment == 'true' }}; then + export L1_DEPLOYMENT_MNEMONIC="${{ secrets.SEPOLIA_ACCOUNTS_MNEMONIC }}" terraform destroy -auto-approve \ -var="RELEASE_NAME=${{ env.NAMESPACE }}" \ -var="VALUES_FILE=${{ env.VALUES_FILE }}" \ -var="GKE_CLUSTER_CONTEXT=${{ env.GKE_CLUSTER_CONTEXT }}" \ -var="AZTEC_DOCKER_IMAGE=${{ env.AZTEC_DOCKER_IMAGE }}" \ -var="L1_DEPLOYMENT_PRIVATE_KEY=${{ secrets.SEPOLIA_L1_DEPLOYMENT_PRIVATE_KEY }}" \ - -var="VALIDATOR_KEYS=${{ secrets.SEPOLIA_VALIDATOR_KEYS }}" \ - -var="BOOT_NODE_SEQ_PUBLISHER_PRIVATE_KEY=${{ secrets.SEPOLIA_BOOT_NODE_SEQ_PUBLISHER_PRIVATE_KEY }}" \ - -var="PROVER_PUBLISHER_PRIVATE_KEY=${{ secrets.SEPOLIA_PROVER_PUBLISHER_PRIVATE_KEY }}" \ - -var="EXTERNAL_ETHEREUM_HOST=${{ env.EXTERNAL_ETHEREUM_HOST }}?key=${{ secrets.SEPOLIA_API_KEY }}" \ + -var="L1_DEPLOYMENT_MNEMONIC=$L1_DEPLOYMENT_MNEMONIC" \ + -var="EXTERNAL_ETHEREUM_HOST=${{ env.EXTERNAL_ETHEREUM_HOST }}" \ -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_HEADER=${{ env.GCP_API_KEY_HEADER }}" \ @@ -215,17 +214,16 @@ jobs: working-directory: ./spartan/terraform/deploy-release run: | if ${{ inputs.sepolia_deployment == 'true' }}; then + export L1_DEPLOYMENT_MNEMONIC="${{ secrets.SEPOLIA_ACCOUNTS_MNEMONIC }}" terraform plan \ -var="RELEASE_NAME=${{ env.NAMESPACE }}" \ -var="VALUES_FILE=${{ env.VALUES_FILE }}" \ -var="GKE_CLUSTER_CONTEXT=${{ env.GKE_CLUSTER_CONTEXT }}" \ -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="L1_DEPLOYMENT_SALT=${DEPLOYMENT_SALT:-$RANDOM}" \ - -var="VALIDATOR_KEYS=${{ secrets.SEPOLIA_VALIDATOR_KEYS }}" \ - -var="BOOT_NODE_SEQ_PUBLISHER_PRIVATE_KEY=${{ secrets.SEPOLIA_BOOT_NODE_SEQ_PUBLISHER_PRIVATE_KEY }}" \ - -var="PROVER_PUBLISHER_PRIVATE_KEY=${{ secrets.SEPOLIA_PROVER_PUBLISHER_PRIVATE_KEY }}" \ - -var="EXTERNAL_ETHEREUM_HOST=${{ env.EXTERNAL_ETHEREUM_HOST }}?key=${{ secrets.SEPOLIA_API_KEY }}" \ + -var="EXTERNAL_ETHEREUM_HOST=${{ env.EXTERNAL_ETHEREUM_HOST }}" \ -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_HEADER=${{ env.GCP_API_KEY_HEADER }}" \ diff --git a/.github/workflows/nightly-kind-test.yml b/.github/workflows/nightly-kind-test.yml index 2895090256a0..d7b7fbd7f45a 100644 --- a/.github/workflows/nightly-kind-test.yml +++ b/.github/workflows/nightly-kind-test.yml @@ -19,6 +19,9 @@ 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 }}" + GCP_API_KEY_HEADER: "X-goog-api-key" jobs: setup: uses: ./.github/workflows/setup-runner.yml @@ -88,6 +91,56 @@ jobs: INSTALL_TIMEOUT=45m FORCE_COLOR=1 INSTALL_METRICS=false \ ./spartan/scripts/test_kind.sh ./src/spartan/proving.test.ts 1-validator-with-proving || true + kind-sepolia-test: + needs: [build] + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + config: + - test: 4epochs.test.ts + values: ci-sepolia.yaml + runner_type: 16core-tester-x86 + timeout: 40 + steps: + - uses: actions/checkout@v4 + with: { ref: "${{ env.GIT_COMMIT }}" } + - name: Setup and KIND Network Test against Sepolia + timeout-minutes: ${{ matrix.config.timeout }} + uses: ./.github/ensure-tester + with: + runner_type: ${{ matrix.config.runner_type }} + spot_strategy: None # use on-demand machines + ttl: ${{ matrix.config.timeout }} + run: | + until docker info &>/dev/null; do sleep 1; done + export CI=1 USE_CACHE=1 + artifact="kind-network-${{matrix.config.test}}-$(./spartan/bootstrap.sh hash)" + if ci3/test_should_run "$artifact"; then + docker pull aztecprotocol/aztec:${{ env.GIT_COMMIT }} + 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_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_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 }}" + SEPOLIA_RUN=true INSTALL_METRICS=false ./spartan/scripts/test_kind.sh "./src/spartan/${{ matrix.config.test }}" "${{ matrix.config.values }}" + ci3/cache_upload_flag "$artifact" + fi + - name: Copy Network Logs + if: always() + run: scripts/copy_from_tester spartan/scripts/logs/test_kind.log test_kind.log || true + + - name: Upload Network Logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: kind-network-test-${{ matrix.config.values }}-${{ matrix.config.test }}.log + path: test_kind.log + success-check: runs-on: ubuntu-20.04 needs: diff --git a/playground/yarn.lock b/playground/yarn.lock index c7fce48e13a9..f69d6eabdb99 100644 --- a/playground/yarn.lock +++ b/playground/yarn.lock @@ -2559,6 +2559,7 @@ __metadata: checksum: 10c0/d8680ee1e5fcd4c197e4ac33b2b4dce03c71f4d91717292785703db200f5c21f977c568d28061226f9b5900cbcd2c84463646134fd5337e7925e0942bc3f46d5 languageName: node linkType: hard + version: 0.0.0-use.local "get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6": version: 1.2.7 diff --git a/spartan/aztec-network/files/config/get-private-key.sh b/spartan/aztec-network/files/config/get-private-key.sh index c3cc36acd2d8..abf352f68b50 100755 --- a/spartan/aztec-network/files/config/get-private-key.sh +++ b/spartan/aztec-network/files/config/get-private-key.sh @@ -6,6 +6,11 @@ KEY_INDEX=$(echo $POD_NAME | awk -F'-' '{print $NF}') # Add the index to the start index to get the private key index PRIVATE_KEY_INDEX=$((KEY_INDEX_START + KEY_INDEX)) +echo "KEY_INDEX: $KEY_INDEX" +echo "KEY_INDEX_START: $KEY_INDEX_START" +echo "PRIVATE_KEY_INDEX: $PRIVATE_KEY_INDEX" +echo "MNEMONIC: $(echo $MNEMONIC | cut -d' ' -f1-2)..." + # Get the private key from the mnemonic private_key=$(cast wallet private-key "$MNEMONIC" --mnemonic-index $PRIVATE_KEY_INDEX) diff --git a/spartan/aztec-network/files/config/get-validator-addresses.sh b/spartan/aztec-network/files/config/get-validator-addresses.sh index 490642b8790f..90da37ba1db3 100755 --- a/spartan/aztec-network/files/config/get-validator-addresses.sh +++ b/spartan/aztec-network/files/config/get-validator-addresses.sh @@ -11,11 +11,16 @@ set -eu # - NUMBER_OF_VALIDATORS # source /scripts/get-validator-addresses.sh +echo "Getting validator addresses for $NUMBER_OF_VALIDATORS validators starting at index $KEY_INDEX_START" +# Echo first 2 words of mnemonic +first_two=$(echo "$MNEMONIC" | cut -d' ' -f1-2) +echo "First two words of mnemonic: $first_two" + # Initialize empty string for validator addresses VALIDATOR_ADDRESSES_LIST="" i=$KEY_INDEX_START -while [ $i -lt $NUMBER_OF_VALIDATORS ]; do +while [ $i -lt $((KEY_INDEX_START + NUMBER_OF_VALIDATORS)) ]; do # Get the private key from the mnemonic private_key=$(cast wallet private-key "$MNEMONIC" --mnemonic-index $i) address=$(cast wallet address "$private_key") diff --git a/spartan/aztec-network/templates/boot-node.yaml b/spartan/aztec-network/templates/boot-node.yaml index 75a81e07ddd3..307b38b75f90 100644 --- a/spartan/aztec-network/templates/boot-node.yaml +++ b/spartan/aztec-network/templates/boot-node.yaml @@ -115,6 +115,10 @@ spec: value: "{{ .Values.aztec.epochDuration }}" - name: AZTEC_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS value: "{{ .Values.aztec.epochProofClaimWindow }}" + - name: L1_GAS_PRICE_MAX + value: "{{ .Values.ethereum.l1GasPriceMax }}" + - name: L1_FIXED_PRIORITY_FEE_PER_GAS + value: "{{ .Values.ethereum.l1FixedPriorityFeePerGas }}" - name: K8S_POD_UID valueFrom: fieldRef: diff --git a/spartan/aztec-network/templates/prover-node.yaml b/spartan/aztec-network/templates/prover-node.yaml index 214dcab69776..eab5743fa822 100644 --- a/spartan/aztec-network/templates/prover-node.yaml +++ b/spartan/aztec-network/templates/prover-node.yaml @@ -191,6 +191,12 @@ spec: value: service.name={{ .Release.Name }},service.namespace={{ .Release.Namespace }},service.version={{ .Chart.AppVersion }},environment={{ .Values.environment | default "production" }} - name: L1_CHAIN_ID value: "{{ .Values.ethereum.chainId }}" + - name: L1_FIXED_PRIORITY_FEE_PER_GAS + value: {{ .Values.proverNode.l1FixedPriorityFeePerGas | quote }} + - name: L1_GAS_LIMIT_BUFFER_PERCENTAGE + value: {{ .Values.proverNode.l1GasLimitBufferPercentage | quote }} + - name: L1_GAS_PRICE_MAX + value: {{ .Values.proverNode.l1GasPriceMax | quote }} - name: P2P_ENABLED value: "{{ .Values.proverNode.p2pEnabled }}" - name: P2P_TCP_LISTEN_ADDR diff --git a/spartan/aztec-network/values.yaml b/spartan/aztec-network/values.yaml index a20a1c356315..e485434e875c 100644 --- a/spartan/aztec-network/values.yaml +++ b/spartan/aztec-network/values.yaml @@ -177,6 +177,9 @@ proverNode: intervalMs: 1000 maxParallelRequests: 100 failedProofStore: "gs://aztec-develop/spartan/failed-proofs" + l1GasPriceMax: 100 + l1FixedPriorityFeePerGas: "" + l1GasLimitBufferPercentage: "" pxe: enabled: true diff --git a/spartan/aztec-network/values/ci-sepolia.yaml b/spartan/aztec-network/values/ci-sepolia.yaml new file mode 100644 index 000000000000..614c9f01803a --- /dev/null +++ b/spartan/aztec-network/values/ci-sepolia.yaml @@ -0,0 +1,65 @@ +aztec: + slotDuration: 36 + epochDuration: 4 + realProofs: false + l1DeploymentMnemonic: "" + validatorKeyIndexStart: 49 + proverKeyIndexStart: 52 + +ethereum: + chainId: "11155111" + externalHost: "" + l1GasPriceMax: 500 + l1FixedPriorityFeePerGas: 3 + beacon: + externalHost: "" + apiKey: "" + apiKeyHeader: "" + +telemetry: + enabled: false + +validator: + l1FixedPriorityFeePerGas: 3 + l1GasLimitBufferPercentage: 15 + replicas: 3 + validatorKeys: + resources: + requests: + memory: "512Mi" + cpu: "200m" + validator: + disabled: false + sequencer: + enforceTimeTable: false + +bot: + followChain: "PENDING" + enabled: true + txIntervalSeconds: 1 + +bootNode: + validator: + disabled: true + resources: + requests: + memory: "2Gi" + cpu: "200m" + +proverAgent: + resources: + requests: + memory: "2Gi" + cpu: "200m" + +proverBroker: + resources: + requests: + memory: "2Gi" + cpu: "200m" + +proverNode: + resources: + requests: + memory: "2Gi" + cpu: "200m" diff --git a/spartan/aztec-network/values/zombienet-reth.yaml b/spartan/aztec-network/values/ignition-reth.yaml similarity index 100% rename from spartan/aztec-network/values/zombienet-reth.yaml rename to spartan/aztec-network/values/ignition-reth.yaml diff --git a/spartan/aztec-network/values/ignition-testnet.yaml b/spartan/aztec-network/values/ignition-testnet.yaml new file mode 100644 index 000000000000..b38231fdb90d --- /dev/null +++ b/spartan/aztec-network/values/ignition-testnet.yaml @@ -0,0 +1,93 @@ +telemetry: + enabled: true + +aztec: + slotDuration: 36 + epochDuration: 32 + realProofs: true + l1DeploymentMnemonic: "" + validatorKeyIndexStart: 0 + proverKeyIndexStart: 48 + +network: + setupL2Contracts: false + +bot: + enabled: false + +pxe: + enabled: false + +faucet: + enabled: false + +proverNode: + l1FixedPriorityFeePerGas: 3 + l1GasLimitBufferPercentage: 15 + l1GasPriceMax: 500 + +validator: + replicas: 3 + l1FixedPriorityFeePerGas: 3 + l1GasLimitBufferPercentage: 15 + sequencer: + minTxsPerBlock: 0 + maxTxsPerBlock: 0 + validator: + disabled: false + resources: + requests: + cpu: "1" + +bootNode: + validator: + disabled: true + resources: + requests: + cpu: "1" + +proverBroker: + resources: + requests: + memory: "2Gi" + cpu: "1" + +proverAgent: + replicas: 2 + bb: + hardwareConcurrency: 31 + gke: + spotEnabled: true + resources: + requests: + memory: "116Gi" + cpu: "31" + +ethereum: + chainId: "11155111" + l1GasPriceMax: 500 + l1FixedPriorityFeePerGas: 3 + deployL1ContractsPrivateKey: + externalHost: + execution: + resources: + requests: + memory: "1Gi" + cpu: "0.5" + beacon: + externalHost: + apiKey: "" + apiKeyHeader: "" + resources: + requests: + memory: "1Gi" + cpu: "0.5" + validator: + resources: + requests: + memory: "1Gi" + cpu: "0.5" + +jobs: + deployL1Verifier: + enable: true diff --git a/spartan/aztec-network/values/rc-2.yaml b/spartan/aztec-network/values/rc-2.yaml index b06a613ab3a2..edded02e9d38 100644 --- a/spartan/aztec-network/values/rc-2.yaml +++ b/spartan/aztec-network/values/rc-2.yaml @@ -5,6 +5,10 @@ aztec: slotDuration: 36 epochDuration: 32 realProofs: true + # TODO: Will need to change these as ignition will be using them + l1DeploymentMnemonic: "" + validatorKeyIndexStart: 4 + proverKeyIndexStart: 52 images: aztec: @@ -36,14 +40,10 @@ validator: disabled: false bootNode: - seqPublisherPrivateKey: validator: disabled: true storageSize: "100Gi" -proverNode: - proverPublisherPrivateKey: - proverAgent: replicas: 60 bb: 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 3819ade90f93..4f3c9c5d372e 100644 --- a/spartan/aztec-network/values/sepolia-3-validators-with-metrics.yaml +++ b/spartan/aztec-network/values/sepolia-3-validators-with-metrics.yaml @@ -1,34 +1,37 @@ telemetry: enabled: true +aztec: + slotDuration: 36 + epochDuration: 32 + realProofs: false + network: setupL2Contracts: false public: false ethereum: - execution: - externalHost: + l1DeploymentMnemonic: + proverKeyIndexStart: 0 + validatorKeyIndexStart: 1 + chainId: "11155111" + externalHost: beacon: externalHost: - chainId: "11155111" + apiKey: + apiKeyHeader: validator: + l1FixedPriorityFeePerGas: 2 + l1GasLimitBufferPercentage: 15 + l1GasPriceMax: 500 replicas: 3 - validatorKeys: - validatorAddresses: - - 0xB5221f3FA03acDEA5A68e355CcDed3f76847F375 - - 0x226E9D4c69525884b0A52C1E9E4C11054729223e - - 0xA33Fa6E2890C37C42CFC0875B86462E73885e02b validator: disabled: false bootNode: - seqPublisherPrivateKey: validator: disabled: true -proverNode: - proverPublisherPrivateKey: - bot: txIntervalSeconds: 20 diff --git a/spartan/scripts/deploy_kind.sh b/spartan/scripts/deploy_kind.sh index 4137ca115602..8dc8bdfba3ef 100755 --- a/spartan/scripts/deploy_kind.sh +++ b/spartan/scripts/deploy_kind.sh @@ -13,6 +13,7 @@ source $(git rev-parse --show-toplevel)/ci3/source # Positional parameters. namespace="$1" values_file="${2:-default.yaml}" +sepolia_deployment="${3:-false}" # Default values for environment variables chaos_values="${CHAOS_VALUES:-}" @@ -60,14 +61,45 @@ fi # and are used to generate the genesis.json file. # We need to read these values and pass them into the eth devnet create.sh script # so that it can generate the genesis.json and config.yaml file with the correct values. -./generate_devnet_config.sh "$values_file" +if [ "$sepolia_deployment" != "true" ]; then + echo "Generating devnet config..." + ./generate_devnet_config.sh "$values_file" +fi # Install the Helm chart +echo "Cleaning up any existing Helm releases..." +helm uninstall spartan -n "$namespace" 2>/dev/null || true +kubectl delete clusterrole spartan-aztec-network-node 2>/dev/null || true +kubectl delete clusterrolebinding spartan-aztec-network-node 2>/dev/null || true + +helm_set_args=( + --set images.aztec.image="aztecprotocol/aztec:$aztec_docker_tag" +) + +# If this is a sepolia run, we need to write some values +if [ "$sepolia_deployment" = "true" ]; then + helm_set_args+=( + --set ethereum.externalHost="$EXTERNAL_ETHEREUM_HOST" + --set ethereum.beacon.externalHost="$EXTERNAL_ETHEREUM_CONSENSUS_HOST" + --set aztec.l1DeploymentMnemonic="$L1_ACCOUNTS_MNEMONIC" + --set ethereum.deployL1ContractsPrivateKey="$L1_DEPLOYMENT_PRIVATE_KEY" + ) + + if [ -n "${EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY:-}" ]; then + helm_set_args+=(--set ethereum.beacon.apiKey="$EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY") + fi + + if [ -n "${EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY_HEADER:-}" ]; then + helm_set_args+=(--set ethereum.beacon.apiKeyHeader="$EXTERNAL_ETHEREUM_CONSENSUS_HOST_API_KEY_HEADER") + fi +fi + helm upgrade --install spartan ../aztec-network \ --namespace "$namespace" \ --create-namespace \ - --values "../aztec-network/values/$values_file" \ + "${helm_set_args[@]}" \ --set images.aztec.image="aztecprotocol/aztec:$aztec_docker_tag" \ + --values "../aztec-network/values/$values_file" \ --wait \ --wait-for-jobs=true \ --timeout="$install_timeout" @@ -79,4 +111,4 @@ if [ -n "$chaos_values" ]; then ../bootstrap.sh network-shaping "$chaos_values" else echo "Skipping network chaos configuration (CHAOS_VALUES not set)" -fi \ No newline at end of file +fi diff --git a/spartan/scripts/test_kind.sh b/spartan/scripts/test_kind.sh index 96c5b3b15da4..5602bc9f1513 100755 --- a/spartan/scripts/test_kind.sh +++ b/spartan/scripts/test_kind.sh @@ -32,6 +32,7 @@ cleanup_cluster=${CLEANUP_CLUSTER:-false} install_metrics=${INSTALL_METRICS:-true} # NOTE: slated for removal along with e2e image! use_docker=${USE_DOCKER:-true} +sepolia_run=${SEPOLIA_RUN:-false} # Ensure we have kind context ../bootstrap.sh kind @@ -70,7 +71,7 @@ trap cleanup SIGINT SIGTERM EXIT stern_pid="" function copy_stern_to_log() { - stern spartan -n $namespace > logs/test_kind.log & + stern spartan -n $namespace >logs/test_kind.log & stern_pid=$! } @@ -79,7 +80,7 @@ copy_stern_to_log # uses VALUES_FILE, CHAOS_VALUES, AZTEC_DOCKER_TAG and INSTALL_TIMEOUT optional env vars if [ "$fresh_install" != "no-deploy" ]; then - ./deploy_kind.sh $namespace $values_file + ./deploy_kind.sh $namespace $values_file $sepolia_run fi # Find 4 free ports between 9000 and 10000 @@ -105,6 +106,14 @@ aztec_slot_duration=$(./read_value.sh "aztec.slotDuration" $value_yamls) aztec_epoch_duration=$(./read_value.sh "aztec.epochDuration" $value_yamls) aztec_epoch_proof_claim_window_in_l2_slots=$(./read_value.sh "aztec.epochProofClaimWindow" $value_yamls) +env_args=() +if [ "$sepolia_run" = "true" ]; then + env_args+=( + -e ETHEREUM_HOST="$EXTERNAL_ETHEREUM_HOST" + -e SEPOLIA_RUN="true" + ) +fi + if [ "$use_docker" = "true" ]; then echo "RUNNING TEST: $test (docker)" # Run test in Docker. @@ -131,6 +140,7 @@ if [ "$use_docker" = "true" ]; then -e AZTEC_SLOT_DURATION=$aztec_slot_duration \ -e AZTEC_EPOCH_DURATION=$aztec_epoch_duration \ -e AZTEC_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS=$aztec_epoch_proof_claim_window_in_l2_slots \ + "${env_args[@]}" \ aztecprotocol/end-to-end:$aztec_docker_tag $test else echo "RUNNING TEST: $test" @@ -157,4 +167,4 @@ else export AZTEC_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS="$aztec_epoch_proof_claim_window_in_l2_slots" yarn --cwd ../../yarn-project/end-to-end test --forceExit "$test" -fi \ No newline at end of file +fi diff --git a/spartan/terraform/deploy-release/main.tf b/spartan/terraform/deploy-release/main.tf index bc7ae23f3ba7..fb9fd2870ec2 100644 --- a/spartan/terraform/deploy-release/main.tf +++ b/spartan/terraform/deploy-release/main.tf @@ -72,30 +72,6 @@ resource "helm_release" "aztec-gke-cluster" { } } - dynamic "set" { - for_each = var.BOOT_NODE_SEQ_PUBLISHER_PRIVATE_KEY != "" ? toset(["iterate"]) : toset([]) - content { - name = "bootNode.seqPublisherPrivateKey" - value = var.BOOT_NODE_SEQ_PUBLISHER_PRIVATE_KEY - } - } - - dynamic "set" { - for_each = var.PROVER_PUBLISHER_PRIVATE_KEY != "" ? toset(["iterate"]) : toset([]) - content { - name = "proverNode.proverPublisherPrivateKey" - value = var.PROVER_PUBLISHER_PRIVATE_KEY - } - } - - dynamic "set_list" { - for_each = length(try(var.VALIDATOR_KEYS, [])) > 0 ? toset(["iterate"]) : toset([]) - content { - name = "validator.validatorKeys" - value = var.VALIDATOR_KEYS - } - } - dynamic "set" { for_each = var.EXTERNAL_ETHEREUM_HOST != "" ? toset(["iterate"]) : toset([]) content { diff --git a/spartan/terraform/deploy-release/variables.tf b/spartan/terraform/deploy-release/variables.tf index b9fb6b595355..77642a2e4b4b 100644 --- a/spartan/terraform/deploy-release/variables.tf +++ b/spartan/terraform/deploy-release/variables.tf @@ -33,27 +33,6 @@ variable "L1_DEPLOYMENT_PRIVATE_KEY" { default = "" } -variable "VALIDATOR_KEYS" { - description = "List of private keys to use for the validators" - type = list(string) - sensitive = true - default = [] -} - -variable "BOOT_NODE_SEQ_PUBLISHER_PRIVATE_KEY" { - description = "Private key to use for the boot node" - type = string - sensitive = true - default = "" -} - -variable "PROVER_PUBLISHER_PRIVATE_KEY" { - description = "Private key to use for the prover" - type = string - sensitive = true - default = "" -} - variable "EXTERNAL_ETHEREUM_HOST" { description = "External host to use for the ethereum node" type = string diff --git a/yarn-project/archiver/src/archiver/config.ts b/yarn-project/archiver/src/archiver/config.ts index 2a28644ee2b8..04c892a4513b 100644 --- a/yarn-project/archiver/src/archiver/config.ts +++ b/yarn-project/archiver/src/archiver/config.ts @@ -1,3 +1,4 @@ +import { type BlobSinkConfig, blobSinkConfigMapping } from '@aztec/blob-sink/client'; import { type ChainConfig, chainConfigMappings } from '@aztec/circuit-types/config'; import { type L1ContractAddresses, @@ -41,9 +42,11 @@ export type ArchiverConfig = { maxLogs?: number; } & L1ReaderConfig & L1ContractsConfig & + BlobSinkConfig & ChainConfig; export const archiverConfigMappings: ConfigMappingsType = { + ...blobSinkConfigMapping, archiverUrl: { env: 'ARCHIVER_URL', description: diff --git a/yarn-project/aztec-node/src/aztec-node/config.ts b/yarn-project/aztec-node/src/aztec-node/config.ts index e1765829ec49..0489f18e9092 100644 --- a/yarn-project/aztec-node/src/aztec-node/config.ts +++ b/yarn-project/aztec-node/src/aztec-node/config.ts @@ -1,5 +1,4 @@ import { type ArchiverConfig, archiverConfigMappings } from '@aztec/archiver/config'; -import { type BlobSinkConfig, blobSinkConfigMapping } from '@aztec/blob-sink/client'; import { type ConfigMappingsType, booleanConfigHelper, getConfigFromMappings } from '@aztec/foundation/config'; import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config'; import { type P2PConfig, p2pConfigMappings } from '@aztec/p2p/config'; @@ -18,7 +17,6 @@ export { sequencerClientConfigMappings, SequencerClientConfig }; * The configuration the aztec node. */ export type AztecNodeConfig = ArchiverConfig & - BlobSinkConfig & SequencerClientConfig & ValidatorClientConfig & ProverClientConfig & @@ -37,7 +35,6 @@ export const aztecNodeConfigMappings: ConfigMappingsType = { ...worldStateConfigMappings, ...p2pConfigMappings, ...dataConfigMappings, - ...blobSinkConfigMapping, disableValidator: { env: 'VALIDATOR_DISABLED', description: 'Whether the validator is disabled for this node.', diff --git a/yarn-project/blob-sink/src/client/http.test.ts b/yarn-project/blob-sink/src/client/http.test.ts index 7cfbd1bf21d4..8ab70f801b0f 100644 --- a/yarn-project/blob-sink/src/client/http.test.ts +++ b/yarn-project/blob-sink/src/client/http.test.ts @@ -91,40 +91,46 @@ describe('HttpBlobSinkClient', () => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ data: { header: { message: { slot: MOCK_SLOT_NUMBER } } } })); } else if (req.url?.includes('/eth/v1/beacon/blob_sidecars/')) { - res.writeHead(200, { 'Content-Type': 'application/json' }); - res.end( - JSON.stringify({ - data: [ - // Correctly encoded blob - { - index: 0, - blob: `0x${Buffer.from(testEncodedBlob.data).toString('hex')}`, - // eslint-disable-next-line camelcase - kzg_commitment: `0x${testEncodedBlob.commitment.toString('hex')}`, - // eslint-disable-next-line camelcase - kzg_proof: `0x${testEncodedBlob.proof.toString('hex')}`, - }, - // Correctly encoded blob, but we do not ask for it in the client - { - index: 1, - blob: `0x${Buffer.from(testBlobIgnore.data).toString('hex')}`, - // eslint-disable-next-line camelcase - kzg_commitment: `0x${testBlobIgnore.commitment.toString('hex')}`, - // eslint-disable-next-line camelcase - kzg_proof: `0x${testBlobIgnore.proof.toString('hex')}`, - }, - // Incorrectly encoded blob - { - index: 2, - blob: `0x${Buffer.from(testNonEncodedBlob.data).toString('hex')}`, - // eslint-disable-next-line camelcase - kzg_commitment: `0x${testNonEncodedBlob.commitment.toString('hex')}`, - // eslint-disable-next-line camelcase - kzg_proof: `0x${testNonEncodedBlob.proof.toString('hex')}`, - }, - ], - }), - ); + if (req.url?.includes('33')) { + // test for L1 missed slot + res.writeHead(404, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ error: 'Not Found' })); + } else { + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end( + JSON.stringify({ + data: [ + // Correctly encoded blob + { + index: 0, + blob: `0x${Buffer.from(testEncodedBlob.data).toString('hex')}`, + // eslint-disable-next-line camelcase + kzg_commitment: `0x${testEncodedBlob.commitment.toString('hex')}`, + // eslint-disable-next-line camelcase + kzg_proof: `0x${testEncodedBlob.proof.toString('hex')}`, + }, + // Correctly encoded blob, but we do not ask for it in the client + { + index: 1, + blob: `0x${Buffer.from(testBlobIgnore.data).toString('hex')}`, + // eslint-disable-next-line camelcase + kzg_commitment: `0x${testBlobIgnore.commitment.toString('hex')}`, + // eslint-disable-next-line camelcase + kzg_proof: `0x${testBlobIgnore.proof.toString('hex')}`, + }, + // Incorrectly encoded blob + { + index: 2, + blob: `0x${Buffer.from(testNonEncodedBlob.data).toString('hex')}`, + // eslint-disable-next-line camelcase + kzg_commitment: `0x${testNonEncodedBlob.commitment.toString('hex')}`, + // eslint-disable-next-line camelcase + kzg_proof: `0x${testNonEncodedBlob.proof.toString('hex')}`, + }, + ], + }), + ); + } } else { res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Not Found' })); @@ -202,5 +208,34 @@ describe('HttpBlobSinkClient', () => { // We should only get the correctly encoded blob expect(retrievedBlobs).toEqual([testEncodedBlob]); }); + + it('should handle L1 missed slots', async () => { + await startExecutionHostServer(); + await startConsensusHostServer(); + + const client = new HttpBlobSinkClient({ + l1RpcUrl: `http://localhost:${executionHostPort}`, + l1ConsensusHostUrl: `http://localhost:${consensusHostPort}`, + }); + + // Add spy on the fetch method + const fetchSpy = jest.spyOn(client as any, 'fetch'); + + const retrievedBlobs = await client.getBlobSidecarFrom(`http://localhost:${consensusHostPort}`, 33, [ + testEncodedBlobHash, + ]); + + expect(retrievedBlobs).toEqual([testEncodedBlob]); + + // Verify we hit the 404 for slot 33 before trying slot 34 + expect(fetchSpy).toHaveBeenCalledWith( + expect.stringContaining('/eth/v1/beacon/blob_sidecars/33'), + expect.any(Object), + ); + expect(fetchSpy).toHaveBeenCalledWith( + expect.stringContaining('/eth/v1/beacon/blob_sidecars/34'), + expect.any(Object), + ); + }); }); }); diff --git a/yarn-project/blob-sink/src/client/http.ts b/yarn-project/blob-sink/src/client/http.ts index 4984ac81c0c6..967b01cabed0 100644 --- a/yarn-project/blob-sink/src/client/http.ts +++ b/yarn-project/blob-sink/src/client/http.ts @@ -117,6 +117,7 @@ export class HttpBlobSinkClient implements BlobSinkClientInterface { blockHashOrSlot: string | number, blobHashes: Buffer[], indices?: number[], + maxRetries = 10, ): Promise { try { let baseUrl = `${hostUrl}/eth/v1/beacon/blob_sidecars/${blockHashOrSlot}`; @@ -132,36 +133,15 @@ export class HttpBlobSinkClient implements BlobSinkClientInterface { if (res.ok) { const body = await res.json(); - const preFilteredBlobsPromise = body.data - // Filter out blobs that did not come from our rollup - .filter((b: BlobJson) => { - const committment = Buffer.from(b.kzg_commitment.slice(2), 'hex'); - const blobHash = Blob.getEthVersionedBlobHash(committment); - return blobHashes.some(hash => hash.equals(blobHash)); - }) - // Attempt to deserialise the blob - // If we cannot decode it, then it is malicious and we should not use it - .map(async (b: BlobJson): Promise => { - try { - return await Blob.fromJson(b); - } catch (err) { - if (err instanceof BlobDeserializationError) { - this.log.warn(`Failed to deserialise blob`, { commitment: b.kzg_commitment }); - return undefined; - } - throw err; - } - }); - - // Second map is async, so we need to await it - const preFilteredBlobs = await Promise.all(preFilteredBlobsPromise); - - // Filter out blobs that did not deserialise - const filteredBlobs = preFilteredBlobs.filter((b: Blob | undefined) => { - return b !== undefined; - }); - - return filteredBlobs; + const blobs = await getRelevantBlobs(body.data, blobHashes, this.log); + return blobs; + } else if (res.status === 404) { + // L1 slot may have been missed, try next few + if (!isNaN(Number(blockHashOrSlot)) && maxRetries > 0) { + const nextSlot = Number(blockHashOrSlot) + 1; + this.log.debug(`L1 slot ${blockHashOrSlot} not found, trying next slot ${nextSlot}`); + return this.getBlobSidecarFrom(hostUrl, nextSlot, blobHashes, indices, maxRetries - 1); + } } this.log.debug(`Unable to get blob sidecar`, res.status); @@ -248,6 +228,39 @@ export class HttpBlobSinkClient implements BlobSinkClientInterface { } } +async function getRelevantBlobs(data: any, blobHashes: Buffer[], logger: Logger): Promise { + const preFilteredBlobsPromise = data + // Filter out blobs that did not come from our rollup + .filter((b: BlobJson) => { + const commitment = Buffer.from(b.kzg_commitment.slice(2), 'hex'); + const blobHash = Blob.getEthVersionedBlobHash(commitment); + return blobHashes.some(hash => hash.equals(blobHash)); + }) + // Attempt to deserialise the blob + // If we cannot decode it, then it is malicious and we should not use it + .map(async (b: BlobJson): Promise => { + try { + return await Blob.fromJson(b); + } catch (err) { + if (err instanceof BlobDeserializationError) { + logger.warn(`Failed to deserialise blob`, { commitment: b.kzg_commitment }); + return undefined; + } + throw err; + } + }); + + // Second map is async, so we need to await it + const preFilteredBlobs = await Promise.all(preFilteredBlobsPromise); + + // Filter out blobs that did not deserialise + const filteredBlobs = preFilteredBlobs.filter((b: Blob | undefined) => { + return b !== undefined; + }); + + return filteredBlobs; +} + function getBeaconNodeFetchOptions(url: string, config: BlobSinkConfig) { let formattedUrl = url; if (config.l1ConsensusHostApiKey && !config.l1ConsensusHostApiKeyHeader) { diff --git a/yarn-project/cli/src/utils/aztec.ts b/yarn-project/cli/src/utils/aztec.ts index 44ce022bb5a2..c6143ed82a7c 100644 --- a/yarn-project/cli/src/utils/aztec.ts +++ b/yarn-project/cli/src/utils/aztec.ts @@ -65,14 +65,21 @@ export async function deployAztecContracts( const { getVKTreeRoot } = await import('@aztec/noir-protocol-circuits-types/vks'); - return await deployL1Contracts(chain.rpcUrl, account, chain.chainInfo, debugLogger, { - l2FeeJuiceAddress: ProtocolContractAddress.FeeJuice, - vkTreeRoot: getVKTreeRoot(), - protocolContractTreeRoot, - salt, - initialValidators, - ...config, - }); + return await deployL1Contracts( + chain.rpcUrl, + account, + chain.chainInfo, + debugLogger, + { + l2FeeJuiceAddress: ProtocolContractAddress.FeeJuice, + vkTreeRoot: getVKTreeRoot(), + protocolContractTreeRoot, + salt, + initialValidators, + ...config, + }, + config, + ); } /** Sets the assumed proven block number on the rollup contract on L1 */ 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 92de2f5cf39a..4413bfaa744e 100644 --- a/yarn-project/end-to-end/src/spartan/4epochs.test.ts +++ b/yarn-project/end-to-end/src/spartan/4epochs.test.ts @@ -36,14 +36,21 @@ describe('token transfer test', () => { containerPort: config.CONTAINER_PXE_PORT, hostPort: config.HOST_PXE_PORT, }); - await startPortForward({ - resource: `svc/${config.INSTANCE_NAME}-aztec-network-eth-execution`, - namespace: config.NAMESPACE, - containerPort: config.CONTAINER_ETHEREUM_PORT, - hostPort: config.HOST_ETHEREUM_PORT, - }); PXE_URL = `http://127.0.0.1:${config.HOST_PXE_PORT}`; - ETHEREUM_HOST = `http://127.0.0.1:${config.HOST_ETHEREUM_PORT}`; + if (config.SEPOLIA_RUN !== 'true') { + await startPortForward({ + resource: `svc/${config.INSTANCE_NAME}-aztec-network-eth-execution`, + namespace: config.NAMESPACE, + containerPort: config.CONTAINER_ETHEREUM_PORT, + hostPort: config.HOST_ETHEREUM_PORT, + }); + ETHEREUM_HOST = `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'); + } + ETHEREUM_HOST = config.ETHEREUM_HOST; + } } else { PXE_URL = config.PXE_URL; ETHEREUM_HOST = config.ETHEREUM_HOST; @@ -51,20 +58,21 @@ describe('token transfer test', () => { testWallets = await setupTestWalletsWithTokens(PXE_URL, MINT_AMOUNT, logger); expect(ROUNDS).toBeLessThanOrEqual(MINT_AMOUNT); + logger.info(`Tested wallets setup: ${ROUNDS} < ${MINT_AMOUNT}`); }); it('can get info', async () => { const name = readFieldCompressedString(await testWallets.tokenAdminWallet.methods.private_get_name().simulate()); expect(name).toBe(testWallets.tokenName); + logger.info(`Token name verified: ${name}`); }); it('transfer tokens for 4 epochs', async () => { const ethCheatCodes = new EthCheatCodesWithState(ETHEREUM_HOST); + const l1ContractAddresses = await testWallets.pxe.getNodeInfo().then(n => n.l1ContractAddresses); // Get 4 epochs - const rollupCheatCodes = new RollupCheatCodes( - ethCheatCodes, - await testWallets.pxe.getNodeInfo().then(n => n.l1ContractAddresses), - ); + const rollupCheatCodes = new RollupCheatCodes(ethCheatCodes, l1ContractAddresses); + logger.info(`Deployed L1 contract addresses: ${JSON.stringify(l1ContractAddresses)}`); const recipient = testWallets.recipientWallet.getAddress(); const transferAmount = 1n; @@ -72,6 +80,8 @@ describe('token transfer test', () => { expect(MINT_AMOUNT).toBe(await testWallets.tokenAdminWallet.methods.balance_of_public(w.getAddress()).simulate()); } + logger.info('Minted tokens'); + expect(0n).toBe(await testWallets.tokenAdminWallet.methods.balance_of_public(recipient).simulate()); // For each round, make both private and public transfers @@ -85,8 +95,12 @@ describe('token transfer test', () => { ), ]); + logger.info(`Created interactions ${interactions.length} for round ${i} of ${ROUNDS}`); + const txs = await Promise.all(interactions.map(async i => await i.prove())); + logger.info(`Proved ${txs.length} in round ${i} of ${ROUNDS}`); + await Promise.all(txs.map(t => t.send().wait({ timeout: 600 }))); const currentSlot = await rollupCheatCodes.getSlot(); expect(currentSlot).toBeLessThanOrEqual(startSlot + i + MAX_MISSED_SLOTS); diff --git a/yarn-project/end-to-end/src/spartan/utils.ts b/yarn-project/end-to-end/src/spartan/utils.ts index 758e52e26963..43f1c5b4d9b5 100644 --- a/yarn-project/end-to-end/src/spartan/utils.ts +++ b/yarn-project/end-to-end/src/spartan/utils.ts @@ -28,6 +28,8 @@ 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(), + SEPOLIA_RUN: z.string().default('false'), K8S: z.literal('local'), }); diff --git a/yarn-project/ethereum/src/config.ts b/yarn-project/ethereum/src/config.ts index 2a2bff7e4fa3..7b411b1938aa 100644 --- a/yarn-project/ethereum/src/config.ts +++ b/yarn-project/ethereum/src/config.ts @@ -5,6 +5,8 @@ import { numberConfigHelper, } from '@aztec/foundation/config'; +import { type L1TxUtilsConfig, l1TxUtilsConfigMappings } from './l1_tx_utils.js'; + export type L1ContractsConfig = { /** How many seconds an L1 slot lasts. */ ethereumSlotDuration: number; @@ -26,7 +28,7 @@ export type L1ContractsConfig = { governanceProposerQuorum: number; /** Governance proposing round size */ governanceProposerRoundSize: number; -}; +} & L1TxUtilsConfig; export const DefaultL1ContractsConfig = { ethereumSlotDuration: 12, @@ -92,6 +94,7 @@ export const l1ContractsConfigMappings: ConfigMappingsType = description: 'The governance proposing round size', ...numberConfigHelper(DefaultL1ContractsConfig.governanceProposerRoundSize), }, + ...l1TxUtilsConfigMappings, }; export function getL1ContractsConfigEnvVars(): L1ContractsConfig { diff --git a/yarn-project/ethereum/src/deploy_l1_contracts.ts b/yarn-project/ethereum/src/deploy_l1_contracts.ts index ab3a18ba3adf..dbfdd45e05fa 100644 --- a/yarn-project/ethereum/src/deploy_l1_contracts.ts +++ b/yarn-project/ethereum/src/deploy_l1_contracts.ts @@ -67,7 +67,7 @@ import { foundry } from 'viem/chains'; import { isAnvilTestChain } from './chain.js'; import { type L1ContractsConfig } from './config.js'; import { type L1ContractAddresses } from './l1_contract_addresses.js'; -import { L1TxUtils } from './l1_tx_utils.js'; +import { L1TxUtils, type L1TxUtilsConfig, defaultL1TxUtilsConfig } from './l1_tx_utils.js'; export const DEPLOYER_ADDRESS: Hex = '0x4e59b44847b379578588920cA78FbF26c0B4956C'; @@ -270,6 +270,7 @@ export const deployL1Contracts = async ( chain: Chain, logger: Logger, args: DeployL1ContractsArgs, + txUtilsConfig: L1TxUtilsConfig = defaultL1TxUtilsConfig, ): 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 @@ -296,7 +297,7 @@ export const deployL1Contracts = async ( const walletClient = createWalletClient({ account, chain, transport: http(rpcUrl) }); const publicClient = createPublicClient({ chain, transport: http(rpcUrl) }); // Governance stuff - const govDeployer = new L1Deployer(walletClient, publicClient, args.salt, logger); + const govDeployer = new L1Deployer(walletClient, publicClient, args.salt, logger, txUtilsConfig); const registryAddress = await govDeployer.deploy(l1Artifacts.registry, [account.address.toString()]); logger.verbose(`Deployed Registry at ${registryAddress}`); @@ -579,13 +580,16 @@ export const deployL1Contracts = async ( class L1Deployer { private salt: Hex | undefined; private txHashes: Hex[] = []; + private l1TxUtils: L1TxUtils; constructor( private walletClient: WalletClient, private publicClient: PublicClient, maybeSalt: number | undefined, private logger: Logger, + private txUtilsConfig?: L1TxUtilsConfig, ) { this.salt = maybeSalt ? padHex(numberToHex(maybeSalt), { size: 32 }) : undefined; + this.l1TxUtils = new L1TxUtils(this.publicClient, this.walletClient, this.logger, this.txUtilsConfig); } async deploy(params: ContractArtifacts, args: readonly unknown[] = []): Promise { @@ -598,6 +602,7 @@ class L1Deployer { this.salt, params.libraries, this.logger, + this.l1TxUtils, ); if (txHash) { this.txHashes.push(txHash); @@ -630,11 +635,15 @@ export async function deployL1Contract( maybeSalt?: Hex, libraries?: Libraries, logger?: Logger, + _l1TxUtils?: L1TxUtils, ): Promise<{ address: EthAddress; txHash: Hex | undefined }> { let txHash: Hex | undefined = undefined; let resultingAddress: Hex | null | undefined = undefined; + let l1TxUtils: L1TxUtils | undefined = _l1TxUtils; - const l1TxUtils = new L1TxUtils(publicClient, walletClient, logger); + if (!l1TxUtils) { + l1TxUtils = new L1TxUtils(publicClient, walletClient, logger); + } if (libraries) { // Note that this does NOT work well for linked libraries having linked libraries. @@ -662,6 +671,7 @@ export async function deployL1Contract( maybeSalt, undefined, logger, + l1TxUtils, ); for (const linkRef in libraries.linkReferences) { diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index ca7db2059006..73f6fa7ddb2f 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -215,6 +215,7 @@ export class L1TxUtils { ...defaultL1TxUtilsConfig, ...(config || {}), }; + this.logger?.debug('Initializing L1 TX utils with config', { config: this.config }); } public interrupt() { @@ -505,12 +506,14 @@ export class L1TxUtils { // Get blob base fee if available let blobBaseFee = 0n; - try { - const blobBaseFeeHex = await this.publicClient.request({ method: 'eth_blobBaseFee' }); - blobBaseFee = BigInt(blobBaseFeeHex); - this.logger?.debug('L1 Blob base fee:', { blobBaseFee: formatGwei(blobBaseFee) }); - } catch { - this.logger?.warn('Failed to get L1 blob base fee', attempt); + if (isBlobTx) { + try { + const blobBaseFeeHex = await this.publicClient.request({ method: 'eth_blobBaseFee' }); + blobBaseFee = BigInt(blobBaseFeeHex); + this.logger?.debug('L1 Blob base fee:', { blobBaseFee: formatGwei(blobBaseFee) }); + } catch { + this.logger?.warn('Failed to get L1 blob base fee', attempt); + } } let priorityFee: bigint; diff --git a/yarn-project/foundation/src/config/index.ts b/yarn-project/foundation/src/config/index.ts index 297c357ec9ec..5b1d23860ac4 100644 --- a/yarn-project/foundation/src/config/index.ts +++ b/yarn-project/foundation/src/config/index.ts @@ -72,7 +72,12 @@ export function numberConfigHelper(defaultVal: number): Pick { return { - parseEnv: (val: string) => BigInt(val), + parseEnv: (val: string) => { + if (val === '') { + return defaultVal; + } + return BigInt(val); + }, defaultValue: defaultVal, }; } diff --git a/yarn-project/sequencer-client/src/publisher/config.ts b/yarn-project/sequencer-client/src/publisher/config.ts index a6afc7634bd8..747b939e4771 100644 --- a/yarn-project/sequencer-client/src/publisher/config.ts +++ b/yarn-project/sequencer-client/src/publisher/config.ts @@ -1,3 +1,4 @@ +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 { EthAddress } from '@aztec/foundation/eth-address'; @@ -25,17 +26,13 @@ export type TxSenderConfig = L1ReaderConfig & { /** * Configuration of the L1Publisher. */ -export type PublisherConfig = L1TxUtilsConfig & { - /** - * The interval to wait between publish retries. - */ - l1PublishRetryIntervalMS: number; - - /** - * The URL of the blob sink. - */ - blobSinkUrl?: string; -}; +export type PublisherConfig = L1TxUtilsConfig & + BlobSinkConfig & { + /** + * The interval to wait between publish retries. + */ + l1PublishRetryIntervalMS: number; + }; export const getTxSenderConfigMappings: ( scope: 'PROVER' | 'SEQ', @@ -89,11 +86,7 @@ export const getPublisherConfigMappings: ( description: 'The interval to wait between publish retries.', }, ...l1TxUtilsConfigMappings, - blobSinkUrl: { - env: 'BLOB_SINK_URL', - description: 'The URL of the blob sink.', - parseEnv: (val?: string) => val, - }, + ...blobSinkConfigMapping, }); export function getPublisherConfigFromEnv(scope: 'PROVER' | 'SEQ'): PublisherConfig {