diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d1f2309bce..8485b19cba1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -125,7 +125,7 @@ jobs: # (Note ARM uses just 2 tests as a smoketest) - name: Create list of non-bench end-to-end jobs id: e2e_list - run: echo "list=$(earthly ls ./yarn-project/end-to-end | grep -v '+base' | grep -v '+bench' | sed 's/+//' | jq -R . | jq -cs .)" >> $GITHUB_OUTPUT + run: echo "list=$(earthly ls ./yarn-project/end-to-end | grep -v '+base' | grep -v '+bench' | grep -v 'devnet' | sed 's/+//' | jq -R . | jq -cs .)" >> $GITHUB_OUTPUT - name: Create list of bench end-to-end jobs id: bench_list run: echo "list=$(earthly ls ./yarn-project/end-to-end | grep '+bench' | sed 's/+//' | jq -R . | jq -cs .)" >> $GITHUB_OUTPUT diff --git a/.github/workflows/devnet-deploys.yml b/.github/workflows/devnet-deploys.yml index eb574191a6b..a6b2f6997af 100644 --- a/.github/workflows/devnet-deploys.yml +++ b/.github/workflows/devnet-deploys.yml @@ -1,35 +1,75 @@ -name: Deploy to devnet +name: Deploy to network on: push: - branches: [devnet] + branches: [devnet, provernet, alphanet] concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +# We only reference secret variables here where we put them into the environment so as to not create confusion + +# Anvil Accounts. Anvil provides 10 pre-funded accounts for the mnemonic we have specified in FORK_MNEMONIC. We are using: +# 1. The first account (index 0) is used in SEQ_1_PUBLISHER_PRIVATE_KEY +# 2. The 9th account (index 8) is used in this workflow for deploying contracts etc +# 3. The 10th account (index 9) is used by the deployed faucet +# TODO: Convert all this so we take the provided mnemonic and derive the keys from the above indices env: DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} GIT_COMMIT: ${{ github.sha }} - DEPLOY_TAG: devnet + DEPLOY_TAG: none L1_CHAIN_ID: 677692 AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + API_KEY: ${{ secrets.DEVNET_API_KEY }} + FORK_MNEMONIC: ${{ secrets.FORK_MNEMONIC }} + CONTRACT_PUBLISHER_PRIVATE_KEY: ${{ secrets.CONTRACT_PUBLISHER_PRIVATE_KEY }} CONTRACT_S3_BUCKET: s3://static.aztec.network + # TF Vars TF_VAR_DOCKERHUB_ACCOUNT: aztecprotocol TF_VAR_L1_CHAIN_ID: 677692 - TF_VAR_BOOTNODE_1_PRIVATE_KEY: ${{ secrets.BOOTNODE_1_PRIVATE_KEY }} - TF_VAR_BOOTNODE_2_PRIVATE_KEY: ${{ secrets.BOOTNODE_2_PRIVATE_KEY }} + TF_VAR_DEPLOY_TAG: none + TF_VAR_IMAGE_TAG: ${{ github.sha }} + TF_VAR_API_KEY: ${{ secrets.DEVNET_API_KEY }} + + # Node / Sequencer + TF_VAR_BOOTSTRAP_NODES: "" + TF_VAR_P2P_ENABLED: "false" TF_VAR_SEQUENCER_PRIVATE_KEYS: '["${{ secrets.SEQ_1_PUBLISHER_PRIVATE_KEY }}"]' TF_VAR_NODE_P2P_PRIVATE_KEYS: '[""]' - TF_VAR_DEPLOY_TAG: devnet - TF_VAR_IMAGE_TAG: ${{ github.sha }} - TF_VAR_API_KEY: ${{ secrets.FORK_API_KEY }} + TF_VAR_SEQ_MAX_SECONDS_BETWEEN_BLOCKS: 0 # disable auto block building + TF_VAR_SEQ_MIN_TX_PER_BLOCK: 1 + TF_VAR_SEQ_MAX_TX_PER_BLOCK: 64 + TF_VAR_NODE_P2P_TCP_PORT: 40000 + TF_VAR_NODE_P2P_UDP_PORT: 45000 + TF_VAR_NODE_LB_RULE_PRIORITY: 500 + + # Anvil TF_VAR_FORK_MNEMONIC: ${{ secrets.FORK_MNEMONIC }} TF_VAR_INFURA_API_KEY: ${{ secrets.INFURA_API_KEY }} + + # Faucet TF_VAR_FAUCET_ACCOUNT_INDEX: 9 + TF_VAR_FAUCET_LB_RULE_PRIORITY: 600 + + # Prover + TF_VAR_AGENTS_PER_PROVER: 1 + TF_VAR_PROVING_ENABLED: false + + # Transaction Bot TF_VAR_BOT_API_KEY: ${{ secrets.BOT_API_KEY }} - TF_VAR_BOT_PRIVATE_KEY: ${{ secrets.BOT_PRIVATE_KEY }} + TF_VAR_BOT_PRIVATE_KEY: "" + TF_VAR_BOT_NO_START: true + TF_VAR_BOT_PRIVATE_TRANSFERS_PER_TX: 0 # no private transfers + TF_VAR_BOT_PUBLIC_TRANSFERS_PER_TX: 1 + TF_VAR_BOT_TX_MINED_WAIT_SECONDS: 2400 + TF_VAR_BOT_NO_WAIT_FOR_TRANSFERS: true + TF_VAR_BOT_TX_INTERVAL_SECONDS: 180 + TF_VAR_BOT_COUNT: 1 + + # PXE + TF_VAR_PXE_LB_RULE_PRIORITY: 4000 jobs: setup: @@ -38,9 +78,147 @@ jobs: username: master runner_type: builder-x86 secrets: inherit - build: + + # Set network specific variables as outputs from this job to be referenced in later jobs + # The only exception is the network api key which needs to be re-derived in every job as it is a secret + # Secrets can't be passed between jobs + set-network: needs: setup runs-on: ${{ github.actor }}-x86 + outputs: + deploy_tag: ${{ steps.set_network_vars.outputs.deploy_tag }} + branch_name: ${{ steps.set_network_vars.outputs.branch_name }} + network_api_key: ${{ steps.set_network_vars.outputs.network_api_key }} + agents_per_prover: ${{ steps.set_network_vars.outputs.agents_per_prover }} + bot_interval: ${{ steps.set_network_vars.outputs.bot_interval }} + node_tcp_range_start: ${{ steps.set_network_vars.outputs.node_tcp_range_start }} + node_udp_range_start: ${{ steps.set_network_vars.outputs.node_udp_range_start }} + node_lb_priority_range_start: ${{ steps.set_network_vars.outputs.node_lb_priority_range_start }} + pxe_lb_priority_range_start: ${{ steps.set_network_vars.outputs.pxe_lb_priority_range_start }} + faucet_lb_priority: ${{ steps.set_network_vars.outputs.faucet_lb_priority }} + bot_no_wait: ${{ steps.set_network_vars.outputs.bot_no_wait }} + steps: + - name: Set network vars + shell: bash + run: | + env + export BRANCH_NAME=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}} + if [ "$BRANCH_NAME" = "devnet" ] + then + echo "deploy_tag=devnet" >> $GITHUB_OUTPUT + echo "branch_name=devnet" >> $GITHUB_OUTPUT + echo "network_api_key=DEVNET_API_KEY" >> $GITHUB_OUTPUT + echo "agents_per_prover=4" >> $GITHUB_OUTPUT + echo "bot_interval=30" >> $GITHUB_OUTPUT + echo "node_tcp_range_start=40100" >> $GITHUB_OUTPUT + echo "node_udp_range_start=45100" >> $GITHUB_OUTPUT + echo "node_lb_priority_range_start=4100" >> $GITHUB_OUTPUT + echo "pxe_lb_priority_range_start=5100" >> $GITHUB_OUTPUT + echo "faucet_lb_priority=601" >> $GITHUB_OUTPUT + echo "bot_no_wait=false" >> $GITHUB_OUTPUT + elif [ "$BRANCH_NAME" = "provernet" ] + then + echo "deploy_tag=provernet" >> $GITHUB_OUTPUT + echo "branch_name=provernet" >> $GITHUB_OUTPUT + echo "network_api_key=PROVERNET_API_KEY" >> $GITHUB_OUTPUT + echo "agents_per_prover=2" >> $GITHUB_OUTPUT + echo "bot_interval=300" >> $GITHUB_OUTPUT + echo "node_tcp_range_start=40200" >> $GITHUB_OUTPUT + echo "node_udp_range_start=45200" >> $GITHUB_OUTPUT + echo "node_lb_priority_range_start=4200" >> $GITHUB_OUTPUT + echo "pxe_lb_priority_range_start=5200" >> $GITHUB_OUTPUT + echo "faucet_lb_priority=602" >> $GITHUB_OUTPUT + echo "bot_no_wait=true" >> $GITHUB_OUTPUT + elif [ "$BRANCH_NAME" = "alphanet" ] + then + echo "deploy_tag=alphanet" >> $GITHUB_OUTPUT + echo "branch_name=alphanet" >> $GITHUB_OUTPUT + echo "network_api_key=ALPHANET_API_KEY" >> $GITHUB_OUTPUT + echo "agents_per_prover=1" >> $GITHUB_OUTPUT + echo "bot_interval=30" >> $GITHUB_OUTPUT + echo "node_tcp_range_start=40000" >> $GITHUB_OUTPUT + echo "node_udp_range_start=45000" >> $GITHUB_OUTPUT + echo "node_lb_priority_range_start=4000" >> $GITHUB_OUTPUT + echo "pxe_lb_priority_range_start=5000" >> $GITHUB_OUTPUT + echo "faucet_lb_priority=600" >> $GITHUB_OUTPUT + echo "bot_no_wait=false" >> $GITHUB_OUTPUT + else + echo "Unrecognized Branch!!" + exit 1 + fi + id: set_network_vars + + build-mainnet-fork: + needs: set-network + env: + BRANCH_NAME: ${{ needs.set-network.outputs.branch_name }} + DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + TF_VAR_DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + TF_VAR_API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + API_KEY_NAME: ${{ needs.set-network.outputs.network_api_key }} + runs-on: ${{ github.actor }}-x86 + steps: + - uses: actions/checkout@v4 + with: + ref: "${{ env.GIT_COMMIT }}" + fetch-depth: 0 + - uses: ./.github/ci-setup-action + with: + concurrency_key: build-mainnet-fork-${{ github.actor }} + dockerhub_password: "${{ env.DOCKERHUB_PASSWORD }}" + + - name: Check if mainnet fork needs deployment + id: check_fork_changes + uses: actions/github-script@v7 + with: + script: | + const { execSync } = require('child_process'); + const changedFiles = execSync('git diff --name-only ${{ github.event.before }} ${{ github.sha }}').toString().split('\n'); + const fileChanged = changedFiles.some(file => file.startsWith('iac/mainnet-fork')); + return fileChanged + + - name: Build & push mainnet fork image + if: steps.check_fork_changes.outputs.result == 'true' + run: | + earthly-ci \ + --no-output --push ./iac/mainnet-fork+export-mainnet-fork --DIST_TAG=${{ env.DEPLOY_TAG }} + + build-aztec-nargo: + needs: set-network + env: + BRANCH_NAME: ${{ needs.set-network.outputs.branch_name }} + DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + TF_VAR_DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + TF_VAR_API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + API_KEY_NAME: ${{ needs.set-network.outputs.network_api_key }} + runs-on: ${{ github.actor }}-x86 + steps: + - uses: actions/checkout@v4 + with: + ref: "${{ env.GIT_COMMIT }}" + fetch-depth: 0 + - uses: ./.github/ci-setup-action + with: + concurrency_key: build-aztec-nargo-${{ github.actor }} + dockerhub_password: "${{ env.DOCKERHUB_PASSWORD }}" + + - name: Build & push aztec nargo image + run: | + earthly-ci --no-output --push ./aztec-nargo+export-aztec-nargo --DIST_TAG=${{ env.DEPLOY_TAG }} + earthly-ci --no-output --push ./aztec-nargo+export-aztec-nargo --DIST_TAG=${{ github.sha }} + + build-aztec: + needs: set-network + env: + BRANCH_NAME: ${{ needs.set-network.outputs.branch_name }} + DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + TF_VAR_DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + TF_VAR_API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + API_KEY_NAME: ${{ needs.set-network.outputs.network_api_key }} + runs-on: ${{ github.actor }}-x86 steps: - uses: actions/checkout@v4 with: @@ -48,8 +226,8 @@ jobs: fetch-depth: 0 - uses: ./.github/ci-setup-action with: - concurrency_key: build-release-artifacts-${{ github.actor }} - dockerhub_password: "${{ secrets.DOCKERHUB_PASSWORD }}" + concurrency_key: build-aztec-${{ github.actor }} + dockerhub_password: "${{ env.DOCKERHUB_PASSWORD }}" - name: Check if only workflow flows have changed id: check_only_workflow_changes uses: actions/github-script@v7 @@ -70,35 +248,171 @@ jobs: timeout-minutes: 40 if: steps.check_only_workflow_changes.outputs.result == 'false' run: | + env earthly-ci --no-output --push ./yarn-project+export-aztec-arch --DIST_TAG=${{ env.DEPLOY_TAG }} earthly-ci --no-output --push ./yarn-project+export-aztec-arch --DIST_TAG=${{ github.sha }} - earthly-ci --no-output --push ./yarn-project+export-cli-wallet --DIST_TAG=${{ env.DEPLOY_TAG }} - earthly-ci --no-output --push ./yarn-project+export-cli-wallet-arch --DIST_TAG=${{ github.sha }} - - name: Check if mainnet fork needs deployment - id: check_fork_changes + - name: "Re-tag Aztec image" + if: steps.check_only_workflow_changes.outputs.result == 'true' + run: | + env + docker pull aztecprotocol/aztec:${{ env.DEPLOY_TAG }} + + docker tag aztecprotocol/aztec:${{ env.DEPLOY_TAG }} aztecprotocol/aztec:${{ github.sha }} + + docker push aztecprotocol/aztec:${{ github.sha }} + + build-faucet: + needs: set-network + env: + BRANCH_NAME: ${{ needs.set-network.outputs.branch_name }} + DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + TF_VAR_DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + TF_VAR_API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + API_KEY_NAME: ${{ needs.set-network.outputs.network_api_key }} + runs-on: ${{ github.actor }}-x86 + steps: + - uses: actions/checkout@v4 + with: + ref: "${{ env.GIT_COMMIT }}" + fetch-depth: 0 + - uses: ./.github/ci-setup-action + with: + concurrency_key: build-faucet-${{ github.actor }} + dockerhub_password: "${{ env.DOCKERHUB_PASSWORD }}" + - name: Check if only workflow flows have changed + id: check_only_workflow_changes uses: actions/github-script@v7 with: script: | const { execSync } = require('child_process'); - const changedFiles = execSync('git diff --name-only ${{ github.event.before }} ${{ github.sha }}').toString().split('\n'); - const fileChanged = changedFiles.some(file => file.startsWith('iac/mainnet-fork')); - return fileChanged + const changedFiles = execSync('git diff --name-only ${{ github.event.before }} ${{ github.sha }}') + .toString() + .split('\n') + .filter(line => line); + const prefixesToIgnore = ['.github', 'iac']; + const suffixesToIgnore = ['.tf']; + return changedFiles.every(file => ( + prefixesToIgnore.some(prefix => file.startsWith(prefix)) || + suffixesToIgnore.some(suffix => file.endsWith(suffix)) + )); + - name: "Build & Push aztec images" + timeout-minutes: 40 + if: steps.check_only_workflow_changes.outputs.result == 'false' + run: | + env + earthly-ci --no-output --push ./yarn-project+export-aztec-faucet --DIST_TAG=${{ env.DEPLOY_TAG }} + earthly-ci --no-output --push ./yarn-project+export-aztec-faucet --DIST_TAG=${{ github.sha }} - - name: Build & push mainnet fork image - if: steps.check_fork_changes.outputs.result == 'true' + - name: "Re-tag Aztec image" + if: steps.check_only_workflow_changes.outputs.result == 'true' run: | - earthly-ci \ - --no-output --push ./iac/mainnet-fork+export-mainnet-fork --DIST_TAG=${{ env.DEPLOY_TAG }} + env + docker pull aztecprotocol/aztec-faucet:${{ env.DEPLOY_TAG }} + + docker tag aztecprotocol/aztec-faucet:${{ env.DEPLOY_TAG }} aztecprotocol/aztec-faucet:${{ github.sha }} + + docker push aztecprotocol/aztec-faucet:${{ github.sha }} + + # build-cli-wallet: + # needs: set-network + # env: + # BRANCH_NAME: ${{ needs.set-network.outputs.branch_name }} + # DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + # TF_VAR_DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + # API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + # TF_VAR_API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + # API_KEY_NAME: ${{ needs.set-network.outputs.network_api_key }} + # runs-on: ${{ github.actor }}-x86 + # steps: + # - uses: actions/checkout@v4 + # with: + # ref: "${{ env.GIT_COMMIT }}" + # fetch-depth: 0 + # - uses: ./.github/ci-setup-action + # with: + # concurrency_key: build-cli-wallet-${{ github.actor }} + # dockerhub_password: "${{ env.DOCKERHUB_PASSWORD }}" + # - name: Check if only workflow flows have changed + # id: check_only_workflow_changes + # uses: actions/github-script@v7 + # with: + # script: | + # const { execSync } = require('child_process'); + # const changedFiles = execSync('git diff --name-only ${{ github.event.before }} ${{ github.sha }}') + # .toString() + # .split('\n') + # .filter(line => line); + # const prefixesToIgnore = ['.github', 'iac']; + # const suffixesToIgnore = ['.tf']; + # return changedFiles.every(file => ( + # prefixesToIgnore.some(prefix => file.startsWith(prefix)) || + # suffixesToIgnore.some(suffix => file.endsWith(suffix)) + # )); + # - name: "Build & Push aztec images" + # timeout-minutes: 40 + # if: steps.check_only_workflow_changes.outputs.result == 'false' + # run: | + # env + # earthly-ci --no-output --push ./yarn-project+export-cli-wallet --DIST_TAG=${{ env.DEPLOY_TAG }} + # earthly-ci --no-output --push ./yarn-project+export-cli-wallet --DIST_TAG=${{ github.sha }} + + # - name: "Re-tag Aztec image" + # if: steps.check_only_workflow_changes.outputs.result == 'true' + # run: | + # env + # docker pull aztecprotocol/cli-wallet:${{ env.DEPLOY_TAG }} + + # docker tag aztecprotocol/cli-wallet:${{ env.DEPLOY_TAG }} aztecprotocol/cli-wallet:${{ github.sha }} + + # docker push aztecprotocol/cli-wallet:${{ github.sha }} + + build-end: + runs-on: ubuntu-latest + needs: [ + build-aztec, + build-faucet, + #build-cli-wallet, + build-mainnet-fork, + build-aztec-nargo, + set-network, + ] + env: + BRANCH_NAME: ${{ needs.set-network.outputs.branch_name }} + DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + TF_VAR_DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + TF_VAR_API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + steps: + - uses: actions/checkout@v4 + with: + ref: "${{ env.GIT_COMMIT }}" + - uses: ./.github/ci-setup-action + - uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.7.5 - terraform_deploy: + terraform-deploy: runs-on: ubuntu-latest - needs: build + needs: [build-end, set-network] + env: + BRANCH_NAME: ${{ needs.set-network.outputs.branch_name }} + DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + TF_VAR_DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + TF_VAR_API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + TF_VAR_AGENTS_PER_PROVER: ${{ needs.set-network.outputs.agents_per_prover }} + TF_VAR_BOT_TX_INTERVAL_SECONDS: ${{ needs.set-network.outputs.bot_interval }} + TF_VAR_NODE_P2P_TCP_PORT: ${{ needs.set-network.outputs.node_tcp_range_start }} + TF_VAR_NODE_P2P_UDP_PORT: ${{ needs.set-network.outputs.node_udp_range_start }} + TF_VAR_NODE_LB_RULE_PRIORITY: ${{ needs.set-network.outputs.node_lb_priority_range_start }} + TF_VAR_PXE_LB_RULE_PRIORITY: ${{ needs.set-network.outputs.pxe_lb_priority_range_start }} + TF_VAR_BOT_NO_WAIT_FOR_TRANSFERS: ${{ needs.set-network.outputs.bot_no_wait }} steps: - uses: actions/checkout@v4 with: ref: "${{ env.GIT_COMMIT }}" - fetch-depth: 0 - uses: ./.github/ci-setup-action - uses: hashicorp/setup-terraform@v3 with: @@ -107,19 +421,20 @@ jobs: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} aws-region: eu-west-2 - name: Deploy mainnet fork working-directory: ./iac/mainnet-fork/terraform run: | + env terraform init -input=false -backend-config="key=${{ env.DEPLOY_TAG }}/mainnet-fork" terraform apply -input=false -auto-approve -replace="aws_efs_file_system.aztec_mainnet_fork_data_store" - name: Wait for mainnet fork deployment run: | - ./.github/scripts/wait_for_infra.sh mainnet-fork ${{ env.DEPLOY_TAG }} ${{ secrets.FORK_API_KEY }} + ./.github/scripts/wait_for_infra.sh mainnet-fork ${{ env.DEPLOY_TAG }} ${{ env.API_KEY }} - name: Deploy L1 Contracts run: | @@ -128,8 +443,8 @@ jobs: docker pull aztecprotocol/aztec:${{ env.DEPLOY_TAG }} docker run aztecprotocol/aztec:${{ env.DEPLOY_TAG }} deploy-l1-contracts \ - --private-key ${{ secrets.CONTRACT_PUBLISHER_PRIVATE_KEY }} \ - --rpc-url https://${{ env.DEPLOY_TAG }}-mainnet-fork.aztec.network:8545/${{ secrets.FORK_API_KEY }} \ + --private-key ${{ env.CONTRACT_PUBLISHER_PRIVATE_KEY }} \ + --rpc-url https://${{ env.DEPLOY_TAG }}-mainnet-fork.aztec.network:8545/${{ env.API_KEY }} \ --l1-chain-id ${{ env.L1_CHAIN_ID }} \ --json | tee ./l1_contracts.json @@ -152,9 +467,16 @@ jobs: - name: Apply l1-contracts Terraform working-directory: ./l1-contracts/terraform run: | + env terraform init -input=false -backend-config="key=${{ env.DEPLOY_TAG }}/l1-contracts" terraform apply -input=false -auto-approve + - name: Disable transactions bot + working-directory: ./yarn-project/aztec/terraform/bot + run: | + terraform init -input=false -backend-config="key=${{ env.DEPLOY_TAG }}/bot" + terraform apply -input=false -auto-approve + - name: Init Aztec Node Terraform working-directory: ./yarn-project/aztec/terraform/node run: | @@ -175,35 +497,35 @@ jobs: working-directory: ./yarn-project/aztec/terraform/pxe run: | terraform init -input=false -backend-config="key=${{ env.DEPLOY_TAG }}/pxe" - terraform apply -input=false -auto-approve - - - name: Deploy P2P Bootstrap Nodes - working-directory: ./yarn-project/p2p-bootstrap/terraform - run: | - terraform init -input=false -backend-config="key=${{ env.DEPLOY_TAG }}/p2p-bootstrap" - terraform apply -input=false -auto-approve + terraform apply -input=false -auto-approve -replace="aws_efs_file_system.pxe_data_store" bootstrap: runs-on: ubuntu-latest - needs: terraform_deploy + needs: [terraform-deploy, set-network] + env: + BRANCH_NAME: ${{ needs.set-network.outputs.branch_name }} + DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + TF_VAR_DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + TF_VAR_API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} steps: - uses: actions/checkout@v4 with: ref: "${{ env.GIT_COMMIT }}" - fetch-depth: 0 - uses: ./.github/ci-setup-action - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} aws-region: eu-west-2 - name: Wait for PXE to be available run: | - ./.github/scripts/wait_for_infra.sh pxe ${{ env.DEPLOY_TAG }} ${{ secrets.FORK_API_KEY }} + env + ./.github/scripts/wait_for_infra.sh pxe ${{ env.DEPLOY_TAG }} ${{ env.API_KEY }} - name: Deploy protocol contracts run: | @@ -211,7 +533,7 @@ jobs: set -o pipefail docker pull aztecprotocol/aztec:${{ env.DEPLOY_TAG }} docker run aztecprotocol/aztec:${{ env.DEPLOY_TAG }} deploy-protocol-contracts \ - --rpc-url https://api.aztec.network/${{ env.DEPLOY_TAG }}/aztec-pxe/${{ secrets.FORK_API_KEY }} \ + --rpc-url https://api.aztec.network/${{ env.DEPLOY_TAG }}/aztec-pxe/${{ env.API_KEY }} \ --l1-chain-id ${{ env.L1_CHAIN_ID }} \ --json | tee ./protocol_contracts.json @@ -222,22 +544,28 @@ jobs: set -e set -o pipefail docker run aztecprotocol/aztec:${{ env.DEPLOY_TAG }} bootstrap-network \ - --rpc-url https://api.aztec.network/${{ env.DEPLOY_TAG }}/aztec-pxe/${{ secrets.FORK_API_KEY }} \ - --l1-rpc-url https://${{ env.DEPLOY_TAG }}-mainnet-fork.aztec.network:8545/${{ secrets.FORK_API_KEY }} \ + --rpc-url https://api.aztec.network/${{ env.DEPLOY_TAG }}/aztec-pxe/${{ env.API_KEY }} \ + --l1-rpc-url https://${{ env.DEPLOY_TAG }}-mainnet-fork.aztec.network:8545/${{ env.API_KEY }} \ --l1-chain-id ${{ env.L1_CHAIN_ID }} \ - --l1-private-key ${{ secrets.CONTRACT_PUBLISHER_PRIVATE_KEY }} \ + --l1-private-key ${{ env.CONTRACT_PUBLISHER_PRIVATE_KEY }} \ --json | tee ./basic_contracts.json aws s3 cp ./basic_contracts.json ${{ env.CONTRACT_S3_BUCKET }}/${{ env.DEPLOY_TAG }}/basic_contracts.json deploy-faucet: runs-on: ubuntu-latest - needs: [terraform_deploy, bootstrap] + needs: [terraform-deploy, bootstrap, set-network] + env: + BRANCH_NAME: ${{ needs.set-network.outputs.branch_name }} + DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + TF_VAR_DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + TF_VAR_API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + TF_VAR_FAUCET_LB_RULE_PRIORITY: ${{ needs.set-network.outputs.faucet_lb_priority }} steps: - uses: actions/checkout@v4 with: ref: "${{ env.GIT_COMMIT }}" - fetch-depth: 0 - uses: ./.github/ci-setup-action - uses: hashicorp/setup-terraform@v3 with: @@ -246,8 +574,8 @@ jobs: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} aws-region: eu-west-2 - name: Retrieve contract addresses @@ -260,19 +588,33 @@ jobs: echo "TF_VAR_DEV_COIN_CONTRACT_ADDRESS=$(jq -r '.devCoinL1' ./basic_contracts.json)" >>$GITHUB_ENV - name: Deploy Faucet - working-directory: ./yarn-project/aztec-faucet + working-directory: ./yarn-project/aztec-faucet/terraform run: | terraform init -input=false -backend-config="key=${{ env.DEPLOY_TAG }}/aztec-faucet" terraform apply -input=false -auto-approve - deploy-bot: + enable-proving: runs-on: ubuntu-latest - needs: [terraform_deploy, bootstrap] + needs: [deploy-faucet, set-network] + env: + BRANCH_NAME: ${{ needs.set-network.outputs.branch_name }} + DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + TF_VAR_DEPLOY_TAG: ${{ needs.set-network.outputs.deploy_tag }} + API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + TF_VAR_API_KEY: ${{ secrets[needs.set-network.outputs.network_api_key] }} + TF_VAR_AGENTS_PER_PROVER: ${{ needs.set-network.outputs.agents_per_prover }} + TF_VAR_BOT_TX_INTERVAL_SECONDS: ${{ needs.set-network.outputs.bot_interval }} + TF_VAR_NODE_P2P_TCP_PORT: ${{ needs.set-network.outputs.node_tcp_range_start }} + TF_VAR_NODE_P2P_UDP_PORT: ${{ needs.set-network.outputs.node_udp_range_start }} + TF_VAR_NODE_LB_RULE_PRIORITY: ${{ needs.set-network.outputs.node_lb_priority_range_start }} + TF_VAR_PXE_LB_RULE_PRIORITY: ${{ needs.set-network.outputs.pxe_lb_priority_range_start }} + TF_VAR_BOT_NO_WAIT_FOR_TRANSFERS: ${{ needs.set-network.outputs.bot_no_wait }} + TF_VAR_PROVING_ENABLED: true + TF_VAR_BOT_NO_START: false steps: - uses: actions/checkout@v4 with: ref: "${{ env.GIT_COMMIT }}" - fetch-depth: 0 - uses: ./.github/ci-setup-action - uses: hashicorp/setup-terraform@v3 with: @@ -281,11 +623,34 @@ jobs: - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} aws-region: eu-west-2 - - name: Deploy transactions bot + - name: Deploy PXE + working-directory: ./yarn-project/aztec/terraform/pxe + run: | + terraform init -input=false -backend-config="key=${{ env.DEPLOY_TAG }}/pxe" + terraform apply -input=false -auto-approve + + - name: Deploy Aztec Nodes + working-directory: ./yarn-project/aztec/terraform/node + run: | + env + terraform init -input=false -backend-config="key=${{ env.DEPLOY_TAG }}/aztec-node" + terraform apply -input=false -auto-approve + + - name: Deploy Provers + working-directory: ./yarn-project/aztec/terraform/prover + run: | + terraform init -input=false -backend-config="key=${{ env.DEPLOY_TAG }}/prover" + terraform apply -input=false -auto-approve + + - name: Wait for PXE to be available + run: | + ./.github/scripts/wait_for_infra.sh pxe ${{ env.DEPLOY_TAG }} ${{ env.API_KEY }} + + - name: Enable transactions bot working-directory: ./yarn-project/aztec/terraform/bot run: | terraform init -input=false -backend-config="key=${{ env.DEPLOY_TAG }}/bot" diff --git a/.github/workflows/devnet-smoke.yml b/.github/workflows/devnet-smoke.yml new file mode 100644 index 00000000000..335d0ca58a3 --- /dev/null +++ b/.github/workflows/devnet-smoke.yml @@ -0,0 +1,85 @@ +name: Run devnet smoke tests +on: + workflow_dispatch: + workflow_run: + workflows: + - Deploy to network + types: + # triggered even if the workflow fails + - completed + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }} + GIT_COMMIT: devnet + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AZTEC_NODE_URL: https://api.aztec.network/devnet/aztec-node-1/{{ secrets.DEVNET_API_KEY }} + FAUCET_URL: https://api.aztec.network/devnet/aztec-faucet/{{ secrets.DEVNET_API_KEY }} + ETHEREUM_HOST: https://devnet-mainnet-fork.aztec.network:8545/${{ secrets.DEVNET_API_KEY }} + +jobs: + setup: + uses: ./.github/workflows/setup-runner.yml + with: + username: ${{ github.event.pull_request.user.login || github.actor }} + runner_type: builder-x86 + secrets: inherit + if: ${{ github.event.workflow_run.conclusion == 'success' }} + + build: + needs: setup + runs-on: ${{ github.event.pull_request.user.login || github.actor }}-x86 + outputs: + e2e_list: ${{ steps.e2e_list.outputs.list }} + if: ${{ github.event.workflow_run.conclusion == 'success' }} + steps: + - uses: actions/checkout@v4 + with: + ref: "${{ env.GIT_COMMIT }}" + + - uses: ./.github/ci-setup-action + with: + concurrency_key: build-test-artifacts-${{ github.actor }} + + - name: "Build E2E Image" + timeout-minutes: 40 + run: | + earthly-ci ./yarn-project+export-e2e-test-images + + - name: Create list of devnet end-to-end jobs + id: e2e_list + run: echo "list=$(earthly ls ./yarn-project/end-to-end | grep 'devnet' | sed 's/+//' | jq -R . | jq -cs .)" >> $GITHUB_OUTPUT + + e2e: + needs: build + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + test: ${{ fromJson( needs.build.outputs.e2e_list )}} + if: ${{ github.event.workflow_run.conclusion == 'success' }} + steps: + - uses: actions/checkout@v4 + with: { ref: "${{ env.GIT_COMMIT }}" } + - uses: ./.github/ci-setup-action + - name: Setup and Test + timeout-minutes: 40 + uses: ./.github/ensure-tester-with-images + with: + # big machine since we're doing proving + runner_type: "64core-tester-x86" + builder_type: builder-x86 + # these are copied to the tester and expected by the earthly command below + # if they fail to copy, it will try to build them on the tester and fail + builder_images_to_copy: aztecprotocol/end-to-end:${{ env.GIT_COMMIT }} + # command to produce the images in case they don't exist + builder_command: scripts/earthly-ci ./yarn-project+export-e2e-test-images + run: | + set -eux + cd ./yarn-project/end-to-end/ + export FORCE_COLOR=1 + ../../scripts/earthly-ci -P --no-output +${{ matrix.test }} diff --git a/aztec-nargo/Earthfile b/aztec-nargo/Earthfile index c8aee15796e..f0615e9bd65 100644 --- a/aztec-nargo/Earthfile +++ b/aztec-nargo/Earthfile @@ -15,3 +15,9 @@ run: ENV PATH "/usr/bin:${PATH}" ENTRYPOINT ["/usr/bin/tini", "--", "/usr/bin/compile_then_transpile.sh"] SAVE IMAGE aztecprotocol/aztec-nargo + +export-aztec-nargo: + FROM +run + ARG DIST_TAG="latest" + ARG ARCH + SAVE IMAGE --push aztecprotocol/aztec-nargo:${DIST_TAG}${ARCH:+-$ARCH} diff --git a/cspell.json b/cspell.json index 9a41fb4eb43..dda4954485e 100644 --- a/cspell.json +++ b/cspell.json @@ -201,6 +201,7 @@ "protobuf", "protogalaxy", "proverless", + "PROVERNET", "proxied", "proxified", "proxify", diff --git a/docker-compose.yml b/docker-compose.yml index 737b2dc064d..09ad44729ad 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -185,6 +185,7 @@ configs: otlp: protocols: http: + endpoint: 0.0.0.0:4318 processors: batch: diff --git a/grafana_dashboards/aztec/aztec-dashboard-all-in-one.json b/grafana_dashboards/aztec/aztec-dashboard-all-in-one.json new file mode 100644 index 00000000000..cc8b3a93826 --- /dev/null +++ b/grafana_dashboards/aztec/aztec-dashboard-all-in-one.json @@ -0,0 +1,2310 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 2, + "links": [], + "liveNow": false, + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 1, + "panels": [], + "title": "Node", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "description": "Percentage of total CPU bused", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "fieldMinMax": false, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 1 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "sum by(exported_job) (system_cpu_utilization{system_cpu_state=\"user\", exported_job=\"$node\"}) / count by(exported_job) (system_cpu_utilization{system_cpu_state=\"user\", exported_job=\"$node\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "CPU utilization", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "description": "Percentage of total system memory being used", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 1 + }, + "id": 8, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "system_memory_utilization{exported_job=\"$node\", system_memory_state=\"used\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Memory utilization", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 10, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "increase(system_network_io_total{exported_job=\"$node\"}[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "{{exported_job}} - {{network_io_direction}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Network IO", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "false" + }, + "properties": [ + { + "id": "displayName", + "value": "Rejected" + }, + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byName", + "options": "true" + }, + "properties": [ + { + "id": "displayName", + "value": "Accepted" + }, + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 9 + }, + "id": 14, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["sum"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(aztec_node_receive_tx_count{exported_job=\"$node\"}[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{aztec_ok}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Transactions", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "percentage", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 9 + }, + "id": 120, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "aztec_mempool_tx_count{exported_job=\"$node\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Tx in mempool", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 15, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(aztec_node_receive_tx_duration_milliseconds_sum{exported_job=\"$node\"}[$__rate_interval]) / on(aztec_ok) rate(aztec_node_receive_tx_duration_milliseconds_count{exported_job=\"$node\"}[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "AVG", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (rate(aztec_node_receive_tx_duration_milliseconds_bucket{exported_job=\"$node\"}[$__rate_interval])))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "95th Perc", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.5, sum by(le) (rate(aztec_node_receive_tx_duration_milliseconds_bucket{exported_job=\"$node\"}[$__rate_interval])))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "50th Perc", + "range": true, + "refId": "C", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(1, sum by(le) (rate(aztec_node_receive_tx_duration_milliseconds_bucket{exported_job=\"$node\"}[$__rate_interval])))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "MAX", + "range": true, + "refId": "D", + "useBackend": false + } + ], + "title": "Tx process time", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 17 + }, + "id": 2, + "panels": [], + "title": "Sequencer", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "description": "Total blocks produced and accepted by the rollup", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 0, + "y": 18 + }, + "id": 17, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "aztec_sequencer_block_build_ok_count{exported_job=\"$node\", aztec_ok=\"true\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Blocks produced", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 7, + "x": 5, + "y": 18 + }, + "id": 16, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (rate(aztec_sequencer_block_build_duration_milliseconds_bucket{exported_job=\"$node\"}[$__rate_interval])))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "95th perc", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Block building duration", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 30, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "decimals": 0, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 12, + "y": 18 + }, + "id": 99, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "aztec-node-metrics" + }, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "increase(aztec_l1_publisher_tx_count{aztec_ok=\"true\"}[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "L1 Tx \"{{aztec_l1_tx_type}}\"", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "aztec-node-metrics" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "sum(increase(aztec_l1_publisher_tx_count{aztec_ok=\"false\"}[$__rate_interval]))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "Failed L1 txs", + "range": true, + "refId": "B", + "useBackend": false + } + ], + "title": "L1 transactions", + "type": "timeseries" + }, + { + "datasource": {}, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "noValue": "0", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 18, + "y": 18 + }, + "id": 98, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "aztec-node-metrics" + }, + "editorMode": "builder", + "expr": "rate(aztec_l1_publisher_gas_price_gwei_sum[$__rate_interval]) / rate(aztec_l1_publisher_gas_price_gwei_count[$__rate_interval])", + "instant": false, + "legendFormat": "Avg gas price gwei", + "range": true, + "refId": "A" + } + ], + "title": "Average gas cost gwei", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 26 + }, + "id": 96, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (rate(aztec_public_executor_simulation_duration_milliseconds_bucket{exported_job=\"$node\"}[$__rate_interval])))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "95th Perc", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(1, sum by(le) (rate(aztec_public_executor_simulation_duration_milliseconds_bucket{exported_job=\"$node\"}[$__rate_interval])))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "MAX", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.5, sum by(le) (rate(aztec_public_executor_simulation_duration_milliseconds_bucket{exported_job=\"$node\"}[$__rate_interval])))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "50th Perc", + "range": true, + "refId": "C", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(aztec_public_executor_simulation_duration_milliseconds_sum{exported_job=\"$node\"}[$__rate_interval])/rate(aztec_public_executor_simulation_duration_milliseconds_count{exported_job=\"$node\"}[$__rate_interval])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "AVG", + "range": true, + "refId": "D", + "useBackend": false + } + ], + "title": "Public function simuation", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 8, + "y": 26 + }, + "id": 58, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (rate(aztec_public_processor_tx_duration_milliseconds_bucket{exported_job=\"$node\"}[$__rate_interval])))", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "95th Perc", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.5, sum by(le) (rate(aztec_public_processor_tx_duration_milliseconds_bucket{exported_job=\"$node\"}[$__rate_interval])))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "50th Perc", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (rate(aztec_public_processor_tx_duration_milliseconds_bucket{exported_job=\"$node\"}[$__rate_interval])))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "MAX", + "range": true, + "refId": "C", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(aztec_public_processor_tx_duration_milliseconds_sum{exported_job=\"$node\"}[$__rate_interval]) / rate(aztec_public_processor_tx_duration_milliseconds_count{exported_job=\"$node\"}[$__rate_interval])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "AVG", + "range": true, + "refId": "E", + "useBackend": false + } + ], + "title": "Public processor duration", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 16, + "y": 26 + }, + "id": 57, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(aztec_public_processor_phase_duration_milliseconds_sum[$__rate_interval]) / rate(aztec_public_processor_phase_duration_milliseconds_count[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "Avg {{aztec_tx_phase_name}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Tx phase duration", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 34 + }, + "id": 56, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(aztec_public_executor_simulation_bytecode_size_bytes_sum[$__rate_interval]) / rate(aztec_public_executor_simulation_bytecode_size_bytes_count[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "Avg bytes", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Deployed bytecode ", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 34 + }, + "id": 97, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (rate(aztec_proving_orchestrator_base_rollup_inputs_duration_milliseconds_bucket{exported_job=\"$node\"}[$__rate_interval])))", + "fullMetaSearch": false, + "hide": true, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "95th Perc", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(1, sum by(le) (rate(aztec_proving_orchestrator_base_rollup_inputs_duration_milliseconds_bucket{exported_job=\"$node\"}[$__rate_interval])))", + "fullMetaSearch": false, + "hide": true, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "MAX", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.5, sum by(le) (rate(aztec_proving_orchestrator_base_rollup_inputs_duration_milliseconds_bucket{exported_job=\"$node\"}[$__rate_interval])))", + "fullMetaSearch": false, + "hide": true, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "50th Perc", + "range": true, + "refId": "C", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(aztec_proving_orchestrator_base_rollup_inputs_duration_milliseconds_sum{exported_job=\"$node\"}[$__rate_interval]) / rate(aztec_proving_orchestrator_base_rollup_inputs_duration_milliseconds_count{exported_job=\"$node\"}[$__rate_interval])", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "AVG", + "range": true, + "refId": "D", + "useBackend": false + } + ], + "title": "Merkle tree insertions before Base Rollup", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 42 + }, + "id": 3, + "panels": [], + "title": "Prover Agent", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "description": "CPU usage as a percentage of all cores", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 0, + "y": 43 + }, + "id": 6, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(exported_job) (system_cpu_utilization{system_cpu_state=\"user\", exported_job=\"$prover\"}) / count by(exported_job) (system_cpu_utilization{system_cpu_state=\"user\", exported_job=\"$prover\"})", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "CPU utilization", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "description": "Percentage of total system memory being used", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 6, + "x": 6, + "y": 43 + }, + "id": 11, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.1", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "system_memory_utilization{exported_job=\"$prover\", system_memory_state=\"used\"}", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "__auto", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Memory utilization", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 43 + }, + "id": 13, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "increase(system_network_io_total{exported_job=\"$prover\"}[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "{{exported_job}} - {{network_io_direction}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Network IO", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 51 + }, + "id": 22, + "panels": [], + "title": "Witness generation", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 52 + }, + "id": 23, + "maxPerRow": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "repeat": "protocol_circuit", + "repeatDirection": "h", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "code", + "expr": "rate(aztec_circuit_witness_generation_duration_milliseconds_sum{exported_job=\"$prover\", aztec_circuit_protocol_circuit_name=\"$protocol_circuit\"}[$__rate_interval]) / rate(aztec_circuit_witness_generation_duration_milliseconds_count{exported_job=\"$prover\"}[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "Avg", + "range": true, + "refId": "A", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.95, sum by(le) (rate(aztec_circuit_witness_generation_duration_milliseconds_bucket{exported_job=\"$prover\", aztec_circuit_protocol_circuit_name=\"$protocol_circuit\"}[$__rate_interval])))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "95th Perc", + "range": true, + "refId": "B", + "useBackend": false + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "histogram_quantile(0.5, sum by(le) (rate(aztec_circuit_witness_generation_duration_milliseconds_bucket{exported_job=\"$prover\", aztec_circuit_protocol_circuit_name=\"$protocol_circuit\"}[$__rate_interval])))", + "fullMetaSearch": false, + "hide": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "50th Perc", + "range": true, + "refId": "C", + "useBackend": false + } + ], + "title": "Witness generation - $protocol_circuit", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 84 + }, + "id": 30, + "panels": [], + "title": "Proving", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "ms" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 8, + "x": 0, + "y": 85 + }, + "id": 37, + "maxPerRow": 3, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "repeat": "protocol_circuit", + "repeatDirection": "h", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "rate(aztec_circuit_proving_duration_milliseconds_sum{exported_job=\"$prover\", aztec_circuit_protocol_circuit_name=\"$protocol_circuit\"}[$__rate_interval]) / rate(aztec_circuit_proving_duration_milliseconds_count{exported_job=\"$prover\"}[$__rate_interval])", + "fullMetaSearch": false, + "includeNullMetadata": false, + "instant": false, + "legendFormat": "Avg", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Circuit proving - $protocol_circuit", + "type": "timeseries" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "devnet", + "value": "devnet" + }, + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "definition": "label_values(exported_job)", + "description": "The network", + "hide": 0, + "includeAll": false, + "label": "Network", + "multi": false, + "name": "network", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(exported_job)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "^(?\\w+)-.+", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": { + "selected": true, + "text": ["devnet-aztec-node-1"], + "value": ["devnet-aztec-node-1"] + }, + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "definition": "label_values(exported_job)", + "hide": 2, + "includeAll": true, + "label": "Node", + "multi": true, + "name": "node", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(exported_job)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "$network-aztec-node-\\d+", + "skipUrlSync": false, + "sort": 7, + "type": "query" + }, + { + "current": { + "selected": true, + "text": ["All"], + "value": ["$__all"] + }, + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "definition": "label_values(exported_job)", + "description": "The prover agents in the network", + "hide": 2, + "includeAll": true, + "label": "Prover", + "multi": true, + "name": "prover", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(exported_job)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "$network-aztec-proving-agent-group-\\d+", + "skipUrlSync": false, + "sort": 7, + "type": "query" + }, + { + "allValue": "", + "current": { + "selected": false, + "text": "All", + "value": "$__all" + }, + "datasource": { + "type": "prometheus", + "uid": "-qqguYXIk" + }, + "definition": "label_values(aztec_circuit_protocol_circuit_name)", + "hide": 2, + "includeAll": true, + "label": "Protocol circuit", + "multi": false, + "name": "protocol_circuit", + "options": [], + "query": { + "qryType": 1, + "query": "label_values(aztec_circuit_protocol_circuit_name)", + "refId": "PrometheusVariableQueryEditor-VariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "New dashboard", + "uid": "bi4a3LXIz", + "version": 24, + "weekStart": "" +} diff --git a/grafana_dashboards/aztec/aztec-node-dashboard.json b/grafana_dashboards/aztec/aztec-node-dashboard.json deleted file mode 100644 index 863e0079d49..00000000000 --- a/grafana_dashboards/aztec/aztec-node-dashboard.json +++ /dev/null @@ -1,576 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "description": "Stats from the Aztec Node", - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "links": [], - "panels": [ - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 6, - "panels": [], - "title": "Node status", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 2, - "fieldMinMax": false, - "mappings": [], - "max": 1, - "min": 0, - "noValue": "0", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "percentunit" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 5, - "x": 0, - "y": 1 - }, - "id": 7, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "center", - "orientation": "auto", - "reduceOptions": { - "calcs": ["lastNotNull"], - "fields": "", - "values": false - }, - "showPercentChange": false, - "text": { - "valueSize": 64 - }, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.0.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "sum(process_cpu_utilization)", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "__auto", - "range": true, - "refId": "A", - "useBackend": false - } - ], - "title": "CPU utilization", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 15, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineStyle": { - "fill": "solid" - }, - "lineWidth": 1, - "pointSize": 1, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 7, - "x": 5, - "y": 1 - }, - "id": 8, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": false - }, - "tooltip": { - "maxHeight": 600, - "mode": "single", - "sort": "none" - } - }, - "pluginVersion": "11.0.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "system_memory_usage{system_memory_state=\"used\"}", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "__auto", - "range": true, - "refId": "A", - "useBackend": false - } - ], - "title": "Memory use", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 5, - "x": 12, - "y": 1 - }, - "id": 9, - "options": { - "colorMode": "value", - "graphMode": "none", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": ["lastNotNull"], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.0.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "builder", - "expr": "aztec_archiver_block_height", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "__auto", - "range": true, - "refId": "A", - "useBackend": false - } - ], - "title": "Current block height", - "type": "stat" - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "decimals": 0, - "displayName": "txs/block", - "mappings": [], - "min": 0, - "noValue": "0", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 6, - "w": 7, - "x": 17, - "y": 1 - }, - "id": 10, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": ["lastNotNull"], - "fields": "", - "values": false - }, - "showPercentChange": false, - "text": { - "titleSize": 12 - }, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.0.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "rate(aztec_archiver_block_size_sum[$__rate_interval]) / rate(aztec_archiver_block_size_count[$__rate_interval])", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "__auto", - "range": true, - "refId": "A", - "useBackend": false - } - ], - "title": "Average block size", - "type": "stat" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 7 - }, - "id": 3, - "panels": [], - "title": "Mempool", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "min": 0, - "noValue": "0", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "bytes" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 8 - }, - "id": 5, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "maxHeight": 600, - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "rate(aztec_mempool_tx_size_bytes_sum[$__rate_interval]) / rate(aztec_mempool_tx_size_bytes_count[$__rate_interval])", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Tx size", - "range": true, - "refId": "Avg tx size", - "useBackend": false - } - ], - "title": "Average transaction size ", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "thresholds" - }, - "mappings": [], - "min": 0, - "noValue": "0", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "none" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 8 - }, - "id": 2, - "options": { - "colorMode": "value", - "graphMode": "area", - "justifyMode": "auto", - "orientation": "auto", - "reduceOptions": { - "calcs": ["last"], - "fields": "", - "values": false - }, - "showPercentChange": false, - "textMode": "auto", - "wideLayout": true - }, - "pluginVersion": "11.0.0", - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "builder", - "expr": "aztec_mempool_tx_count", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "__auto", - "range": true, - "refId": "tx", - "useBackend": false - } - ], - "title": "Transactions in mempool", - "type": "stat" - } - ], - "refresh": "", - "schemaVersion": 39, - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-15m", - "to": "now" - }, - "timeRangeUpdatedDuringEditOrView": false, - "timepicker": {}, - "timezone": "browser", - "title": "Aztec Node", - "uid": "edp4qxqgjoav4e", - "version": 1, - "weekStart": "" -} diff --git a/grafana_dashboards/aztec/protocol-circuits-dashboard.json b/grafana_dashboards/aztec/protocol-circuits-dashboard.json deleted file mode 100644 index 1849485d30c..00000000000 --- a/grafana_dashboards/aztec/protocol-circuits-dashboard.json +++ /dev/null @@ -1,747 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": { - "type": "grafana", - "uid": "-- Grafana --" - }, - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "description": "Metrics relating to protocol circuits", - "editable": true, - "fiscalYearStartMonth": 0, - "graphTooltip": 0, - "links": [], - "panels": [ - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 3, - "panels": [], - "title": "Circuit proving", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "noValue": "0", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 1 - }, - "id": 4, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "maxHeight": 600, - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "aztec_circuit_proving_duration_seconds{aztec_circuit_protocol_circuit_name=\"base-parity\"}", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Base parity", - "range": true, - "refId": "A", - "useBackend": false - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "aztec_circuit_proving_duration_seconds{aztec_circuit_protocol_circuit_name=\"root-parity\"}", - "fullMetaSearch": false, - "hide": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Root parity", - "range": true, - "refId": "B", - "useBackend": false - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "aztec_circuit_proving_duration_seconds{aztec_circuit_protocol_circuit_name=\"base-rollup\"}", - "fullMetaSearch": false, - "hide": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Base rollup", - "range": true, - "refId": "C", - "useBackend": false - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "aztec_circuit_proving_duration_seconds{aztec_circuit_protocol_circuit_name=\"merge-rollup\"}", - "fullMetaSearch": false, - "hide": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Merge rollup", - "range": true, - "refId": "D", - "useBackend": false - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "aztec_circuit_proving_duration_seconds{aztec_circuit_protocol_circuit_name=\"root-rollup\"}", - "fullMetaSearch": false, - "hide": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Root rollup", - "range": true, - "refId": "E", - "useBackend": false - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "aztec_circuit_proving_duration_seconds{aztec_circuit_protocol_circuit_name=\"public-kernel-setup\"}", - "fullMetaSearch": false, - "hide": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Public Kernel - Setup", - "range": true, - "refId": "F", - "useBackend": false - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "aztec_circuit_proving_duration_seconds{aztec_circuit_protocol_circuit_name=\"public-kernel-app-logic\"}", - "fullMetaSearch": false, - "hide": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Public Kernel - App logic", - "range": true, - "refId": "G", - "useBackend": false - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "aztec_circuit_proving_duration_seconds{aztec_circuit_protocol_circuit_name=\"public-kernel-teardown\"}", - "fullMetaSearch": false, - "hide": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Public Kernel - Teardown", - "range": true, - "refId": "H", - "useBackend": false - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "aztec_circuit_proving_duration_seconds{aztec_circuit_protocol_circuit_name=\"public-kernel-tail\"}", - "fullMetaSearch": false, - "hide": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Public Kernel - Tail", - "range": true, - "refId": "I", - "useBackend": false - } - ], - "title": "Circuit proving", - "type": "timeseries" - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "noValue": "0", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 1 - }, - "id": 5, - "options": { - "legend": { - "calcs": [], - "displayMode": "list", - "placement": "bottom", - "showLegend": true - }, - "tooltip": { - "maxHeight": 600, - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "aztec_circuit_witness_generation_duration_seconds{aztec_circuit_protocol_circuit_name=\"base-parity\"}", - "fullMetaSearch": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Base parity", - "range": true, - "refId": "A", - "useBackend": false - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "aztec_circuit_witness_generation_duration_seconds{aztec_circuit_protocol_circuit_name=\"root-parity\"}", - "fullMetaSearch": false, - "hide": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Root parity", - "range": true, - "refId": "B", - "useBackend": false - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "aztec_circuit_witness_generation_duration_seconds{aztec_circuit_protocol_circuit_name=\"base-rollup\"}", - "fullMetaSearch": false, - "hide": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Base rollup", - "range": true, - "refId": "C", - "useBackend": false - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "aztec_circuit_witness_generation_duration_seconds{aztec_circuit_protocol_circuit_name=\"merge-rollup\"}", - "fullMetaSearch": false, - "hide": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Merge rollup", - "range": true, - "refId": "D", - "useBackend": false - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "aztec_circuit_witness_generation_duration_seconds{aztec_circuit_protocol_circuit_name=\"root-rollup\"}", - "fullMetaSearch": false, - "hide": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Root rollup", - "range": true, - "refId": "E", - "useBackend": false - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "aztec_circuit_witness_generation_duration_seconds{aztec_circuit_protocol_circuit_name=\"public-kernel-setup\"}", - "fullMetaSearch": false, - "hide": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Public Kernel - Setup", - "range": true, - "refId": "F", - "useBackend": false - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "aztec_circuit_witness_generation_duration_seconds{aztec_circuit_protocol_circuit_name=\"public-kernel-app-logic\"}", - "fullMetaSearch": false, - "hide": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Public Kernel - App logic", - "range": true, - "refId": "G", - "useBackend": false - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "aztec_circuit_witness_generation_duration_seconds{aztec_circuit_protocol_circuit_name=\"public-kernel-teardown\"}", - "fullMetaSearch": false, - "hide": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Public Kernel - Teardown", - "range": true, - "refId": "H", - "useBackend": false - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "disableTextWrap": false, - "editorMode": "code", - "expr": "aztec_circuit_witness_generation_duration_seconds{aztec_circuit_protocol_circuit_name=\"public-kernel-tail\"}", - "fullMetaSearch": false, - "hide": false, - "includeNullMetadata": true, - "instant": false, - "legendFormat": "Public Kernel - Tail", - "range": true, - "refId": "I", - "useBackend": false - } - ], - "title": "Circuit witness generation", - "type": "timeseries" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 9 - }, - "id": 2, - "panels": [], - "title": "Circuit simulation", - "type": "row" - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "fieldConfig": { - "defaults": { - "color": { - "mode": "palette-classic" - }, - "custom": { - "axisBorderShow": false, - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "insertNulls": false, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": true, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } - }, - "mappings": [], - "min": 0, - "noValue": "0", - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - }, - "unit": "s" - }, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 10 - }, - "id": 6, - "options": { - "legend": { - "calcs": [], - "displayMode": "table", - "placement": "right", - "showLegend": true - }, - "tooltip": { - "maxHeight": 600, - "mode": "single", - "sort": "none" - } - }, - "targets": [ - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "editorMode": "code", - "exemplar": false, - "expr": "rate(aztec_circuit_simulation_duration_seconds_sum{aztec_circuit_protocol_circuit_name=\"base-parity\"}[$__rate_interval]) / rate(aztec_circuit_simulation_duration_seconds_count{aztec_circuit_protocol_circuit_name=\"base-parity\"}[$__rate_interval])", - "instant": false, - "legendFormat": "Base paritiy", - "range": true, - "refId": "A" - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "editorMode": "code", - "expr": "rate(aztec_circuit_simulation_duration_seconds_sum{aztec_circuit_protocol_circuit_name=\"root-parity\"}[$__rate_interval]) / rate(aztec_circuit_simulation_duration_seconds_count{aztec_circuit_protocol_circuit_name=\"root-parity\"}[$__rate_interval])", - "hide": true, - "instant": false, - "legendFormat": "Root paritiy", - "range": true, - "refId": "B" - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "editorMode": "code", - "expr": "rate(aztec_circuit_simulation_duration_seconds_sum{aztec_circuit_protocol_circuit_name=\"base-rollup\"}[$__rate_interval]) / rate(aztec_circuit_simulation_duration_seconds_count{aztec_circuit_protocol_circuit_name=\"base-rollup\"}[$__rate_interval])", - "hide": true, - "instant": false, - "legendFormat": "Base rollup", - "range": true, - "refId": "C" - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "editorMode": "code", - "expr": "rate(aztec_circuit_simulation_duration_seconds_sum{aztec_circuit_protocol_circuit_name=\"merge-rollup\"}[$__rate_interval]) / rate(aztec_circuit_simulation_duration_seconds_count{aztec_circuit_protocol_circuit_name=\"merge-rollup\"}[$__rate_interval])", - "hide": false, - "instant": false, - "legendFormat": "Merge rollup", - "range": true, - "refId": "D" - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "editorMode": "code", - "expr": "rate(aztec_circuit_simulation_duration_seconds_sum{aztec_circuit_protocol_circuit_name=\"root-rollup\"}[$__rate_interval]) / rate(aztec_circuit_simulation_duration_seconds_count{aztec_circuit_protocol_circuit_name=\"root-rollup\"}[$__rate_interval])", - "hide": false, - "instant": false, - "legendFormat": "Root rollup", - "range": true, - "refId": "E" - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "editorMode": "code", - "expr": "rate(aztec_circuit_simulation_duration_seconds_sum{aztec_circuit_protocol_circuit_name=\"public-kernel-setup\"}[$__rate_interval]) / rate(aztec_circuit_simulation_duration_seconds_count{aztec_circuit_protocol_circuit_name=\"public-kernel-setup\"}[$__rate_interval])", - "hide": false, - "instant": false, - "legendFormat": "Public kernel - Setup", - "range": true, - "refId": "F" - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "editorMode": "code", - "expr": "rate(aztec_circuit_simulation_duration_seconds_sum{aztec_circuit_protocol_circuit_name=\"public-kernel-app-logic\"}[$__rate_interval]) / rate(aztec_circuit_simulation_duration_seconds_count{aztec_circuit_protocol_circuit_name=\"public-kernel-app-logic\"}[$__rate_interval])", - "hide": false, - "instant": false, - "legendFormat": "Public kernel - App logic", - "range": true, - "refId": "H" - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "editorMode": "code", - "expr": "rate(aztec_circuit_simulation_duration_seconds_sum{aztec_circuit_protocol_circuit_name=\"public-kernel-teardown\"}[$__rate_interval]) / rate(aztec_circuit_simulation_duration_seconds_count{aztec_circuit_protocol_circuit_name=\"public-kernel-teardown\"}[$__rate_interval])", - "hide": false, - "instant": false, - "legendFormat": "Public kernel - Teardown", - "range": true, - "refId": "I" - }, - { - "datasource": { - "type": "prometheus", - "uid": "aztec-node-metrics" - }, - "editorMode": "code", - "expr": "rate(aztec_circuit_simulation_duration_seconds_sum{aztec_circuit_protocol_circuit_name=\"public-kernel-tail\"}[$__rate_interval]) / rate(aztec_circuit_simulation_duration_seconds_count{aztec_circuit_protocol_circuit_name=\"public-kernel-tail\"}[$__rate_interval])", - "hide": false, - "instant": false, - "legendFormat": "Public kernel - Tail", - "range": true, - "refId": "G" - } - ], - "title": "Circuit simulation (only when faking proofs)", - "type": "timeseries" - } - ], - "schemaVersion": 39, - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-15m", - "to": "now" - }, - "timeRangeUpdatedDuringEditOrView": false, - "timepicker": {}, - "timezone": "browser", - "title": "Protocol circuits", - "uid": "ddp5sfpkscb9cf", - "version": 3, - "weekStart": "" -} diff --git a/iac/mainnet-fork/redeploy b/iac/mainnet-fork/redeploy new file mode 100644 index 00000000000..e440e5c8425 --- /dev/null +++ b/iac/mainnet-fork/redeploy @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/iac/mainnet-fork/scripts/run_nginx_anvil.sh b/iac/mainnet-fork/scripts/run_nginx_anvil.sh index 2ac3ecfd0a0..dda30668ce0 100755 --- a/iac/mainnet-fork/scripts/run_nginx_anvil.sh +++ b/iac/mainnet-fork/scripts/run_nginx_anvil.sh @@ -28,8 +28,8 @@ echo "Waiting for ethereum host at $ETHEREUM_HOST..." while ! curl -s $ETHEREUM_HOST >/dev/null; do sleep 1; done # Fix anvil's fork timestamp -./foundry/bin/cast rpc --rpc-url="$ETHEREUM_HOST" evm_setNextBlockTimestamp $(date +%s | xargs printf '0x%x') > /dev/null -./foundry/bin/cast rpc --rpc-url="$ETHEREUM_HOST" evm_mine > /dev/null +#.foundry/bin/cast rpc --rpc-url="$ETHEREUM_HOST" evm_setNextBlockTimestamp $(date +%s | xargs printf '0x%x') > /dev/null +#.foundry/bin/cast rpc --rpc-url="$ETHEREUM_HOST" evm_mine > /dev/null echo "Starting nginx..." nginx & diff --git a/metrics/grafana/Dockerfile b/metrics/grafana/Dockerfile index 22d366f9af1..f6fc8ff58c9 100644 --- a/metrics/grafana/Dockerfile +++ b/metrics/grafana/Dockerfile @@ -1,4 +1,4 @@ -FROM grafana/grafana:9.3.2 +FROM grafana/grafana:11.1.1 COPY grafana.ini /etc/grafana/grafana.ini USER root EXPOSE 80 \ No newline at end of file diff --git a/yarn-project/aztec-faucet/terraform/main.tf b/yarn-project/aztec-faucet/terraform/main.tf index e891229062c..ab2e8630665 100644 --- a/yarn-project/aztec-faucet/terraform/main.tf +++ b/yarn-project/aztec-faucet/terraform/main.tf @@ -71,7 +71,7 @@ resource "aws_service_discovery_service" "aztec-faucet" { # Terraform just fails if this resource changes and you have registered instances. provisioner "local-exec" { when = destroy - command = "${path.module}/../servicediscovery-drain.sh ${self.id}" + command = "${path.module}/servicediscovery-drain.sh ${self.id}" } } @@ -84,83 +84,83 @@ resource "aws_ecs_task_definition" "aztec-faucet" { memory = "4096" execution_role_arn = data.terraform_remote_state.setup_iac.outputs.ecs_task_execution_role_arn task_role_arn = data.terraform_remote_state.aztec2_iac.outputs.cloudwatch_logging_ecs_role_arn - container_definitions = < 0) { this.log.warn(`Rejecting tx ${tx.getTxHash()} because of validation errors`); + this.metrics.receivedTx(timer.ms(), false); return; } await this.p2pClient!.sendTx(tx); + this.metrics.receivedTx(timer.ms(), true); } public async getTxReceipt(txHash: TxHash): Promise { diff --git a/yarn-project/aztec/package.json b/yarn-project/aztec/package.json index 1a0755a61a6..879963d1ca2 100644 --- a/yarn-project/aztec/package.json +++ b/yarn-project/aztec/package.json @@ -54,6 +54,7 @@ "@aztec/pxe": "workspace:^", "@aztec/telemetry-client": "workspace:^", "@aztec/txe": "workspace:^", + "@aztec/types": "workspace:^", "abitype": "^0.8.11", "commander": "^12.1.0", "koa": "^2.14.2", diff --git a/yarn-project/aztec/src/bin/index.ts b/yarn-project/aztec/src/bin/index.ts index ed048fa8bc8..65d2e284239 100644 --- a/yarn-project/aztec/src/bin/index.ts +++ b/yarn-project/aztec/src/bin/index.ts @@ -1,3 +1,4 @@ +#!/usr/bin/env node import { fileURLToPath } from '@aztec/aztec.js'; import { injectCommands as injectBuilderCommands } from '@aztec/builder'; import { injectCommands as injectWalletCommands } from '@aztec/cli-wallet'; diff --git a/yarn-project/aztec/src/cli/cli.ts b/yarn-project/aztec/src/cli/cli.ts index 3e63e8e2a6c..d5b92f2b566 100644 --- a/yarn-project/aztec/src/cli/cli.ts +++ b/yarn-project/aztec/src/cli/cli.ts @@ -95,7 +95,7 @@ export function injectAztecCommands(program: Command, userLog: LogFn, debugLogge userLog(`Cannot run a standalone sequencer without a node`); process.exit(1); } else { - userLog(`No module specified to start`); + userLog(`No module specified to start ${JSON.stringify(options, null, 2)}`); process.exit(1); } } diff --git a/yarn-project/aztec/src/cli/cmds/start_bot.ts b/yarn-project/aztec/src/cli/cmds/start_bot.ts index 3d31392634b..0d33f0bf10c 100644 --- a/yarn-project/aztec/src/cli/cmds/start_bot.ts +++ b/yarn-project/aztec/src/cli/cmds/start_bot.ts @@ -21,7 +21,14 @@ export async function startBot( process.exit(1); } - await addBot(options, services, signalHandlers); + // Start a PXE client that is used by the bot if required + let pxe: PXE | undefined; + if (options.pxe) { + const { addPXE } = await import('./start_pxe.js'); + pxe = await addPXE(options, services, signalHandlers, userLog); + } + + await addBot(options, services, signalHandlers, { pxe }); return services; } diff --git a/yarn-project/aztec/src/cli/cmds/start_pxe.ts b/yarn-project/aztec/src/cli/cmds/start_pxe.ts index 799dadac612..c86864c0d20 100644 --- a/yarn-project/aztec/src/cli/cmds/start_pxe.ts +++ b/yarn-project/aztec/src/cli/cmds/start_pxe.ts @@ -1,16 +1,38 @@ +import { + type ContractArtifact, + type ContractInstanceWithAddress, + Fr, + getContractClassFromArtifact, +} from '@aztec/aztec.js'; import { type AztecNode, createAztecNodeClient } from '@aztec/circuit-types'; +import { getContractArtifact } from '@aztec/cli/cli-utils'; import { type ServerList } from '@aztec/foundation/json-rpc/server'; import { type LogFn } from '@aztec/foundation/log'; -import { type PXEServiceConfig, createPXERpcServer, createPXEService, getPXEServiceConfig } from '@aztec/pxe'; +import { AztecAddress, type CliPXEOptions, createPXERpcServer, createPXEService, getCliPXEOptions } from '@aztec/pxe'; +import { L2BasicContractsMap, Network } from '@aztec/types/network'; import { mergeEnvVarsAndCliOptions, parseModuleOptions } from '../util.js'; +const contractAddressesUrl = 'http://static.aztec.network'; + export async function startPXE(options: any, signalHandlers: (() => Promise)[], userLog: LogFn) { const services: ServerList = []; await addPXE(options, services, signalHandlers, userLog, {}); return services; } +function isValidNetwork(value: any): value is Network { + return Object.values(Network).includes(value); +} + +async function fetchBasicContractAddresses(url: string) { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed to fetch basic contract addresses from ${url}`); + } + return response.json(); +} + export async function addPXE( options: any, services: ServerList, @@ -19,9 +41,23 @@ export async function addPXE( deps: { node?: AztecNode } = {}, ) { const pxeCliOptions = parseModuleOptions(options.pxe); - const pxeConfig = mergeEnvVarsAndCliOptions(getPXEServiceConfig(), pxeCliOptions); - const nodeUrl = pxeCliOptions.nodeUrl ?? process.env.AZTEC_NODE_URL; - if (!nodeUrl && !deps.node) { + const pxeConfig = mergeEnvVarsAndCliOptions(getCliPXEOptions(), pxeCliOptions); + let nodeUrl; + if (pxeCliOptions.network) { + if (isValidNetwork(pxeCliOptions.network)) { + if (!pxeCliOptions.apiKey) { + userLog(`API Key is required to connect to ${pxeCliOptions.network}`); + process.exit(1); + } + nodeUrl = `https://api.aztec.network/${pxeCliOptions.network}/aztec-node-1/${pxeCliOptions.apiKey}`; + } else { + userLog(`Network ${pxeCliOptions.network} is not supported`); + process.exit(1); + } + } else { + nodeUrl = pxeCliOptions.nodeUrl ?? process.env.AZTEC_NODE_URL; + } + if (!nodeUrl && !deps.node && !pxeCliOptions.network) { userLog('Aztec Node URL (nodeUrl | AZTEC_NODE_URL) option is required to start PXE without --node option'); process.exit(1); } @@ -30,6 +66,41 @@ export async function addPXE( const pxe = await createPXEService(node, pxeConfig); const pxeServer = createPXERpcServer(pxe); + // register basic contracts + if (pxeCliOptions.network) { + userLog(`Registering basic contracts for ${pxeCliOptions.network}`); + const basicContractsInfo = await fetchBasicContractAddresses( + `${contractAddressesUrl}/${pxeCliOptions.network}/basic_contracts.json`, + ); + const l2Contracts: Record< + string, + { name: string; address: AztecAddress; initHash: Fr; salt: Fr; artifact: ContractArtifact } + > = {}; + for (const [key, artifactName] of Object.entries(L2BasicContractsMap[pxeCliOptions.network as Network])) { + l2Contracts[key] = { + name: key, + address: AztecAddress.fromString(basicContractsInfo[key].address), + initHash: Fr.fromString(basicContractsInfo[key].initHash), + salt: Fr.fromString(basicContractsInfo[key].salt), + artifact: await getContractArtifact(artifactName, userLog), + }; + } + + Object.values(l2Contracts).forEach(async ({ name, address, artifact, initHash, salt }) => { + const instance: ContractInstanceWithAddress = { + version: 1, + salt, + initializationHash: initHash, + address, + deployer: AztecAddress.ZERO, + contractClassId: getContractClassFromArtifact(artifact!).id, + publicKeysHash: Fr.ZERO, + }; + userLog(`Registering ${name} at ${address.toString()}`); + await pxe.registerContract({ artifact, instance }); + }); + } + // Add PXE to services list services.push({ pxe: pxeServer }); diff --git a/yarn-project/aztec/src/cli/texts.ts b/yarn-project/aztec/src/cli/texts.ts index 0b6464208c9..fe1903dcd8b 100644 --- a/yarn-project/aztec/src/cli/texts.ts +++ b/yarn-project/aztec/src/cli/texts.ts @@ -44,7 +44,9 @@ export const cliTexts = { 'port:PXE_PORT - number - The port on which the PXE should listen for connections. Default: 79\n' + 'l2BlockPollingIntervalMS:PXE_BLOCK_POLLING_INTERVAL_MS - number - The frequency in which to check for blocks in ms. Default: 1000\n' + 'l2StartingBlock:PXE_L2_STARTING_BLOCK - number - The block number from which to start polling. Default: 1\n' + - 'dataDirectory:PXE_DATA_DIRECTORY - string - Where to store PXE data. If not set, will store temporarily.\n', + 'dataDirectory:PXE_DATA_DIRECTORY - string - Where to store PXE data. If not set, will store temporarily.\n' + + 'network:NETWORK - string - The network to connect to, e.g. devnet\n' + + "apiKey:API_KEY - string - The API key to use when connecting to an Aztec network. Required when using 'network' option.\n", archiver: 'Starts an Archiver with options. If started additionally to --node, the Archiver will attach to that node.' + 'Available options are listed below as cliProperty:ENV_VARIABLE_NAME.\n' + diff --git a/yarn-project/aztec/src/cli/util.ts b/yarn-project/aztec/src/cli/util.ts index 8b14059e5f1..bb4442930a6 100644 --- a/yarn-project/aztec/src/cli/util.ts +++ b/yarn-project/aztec/src/cli/util.ts @@ -8,7 +8,7 @@ import { type ServerList } from '@aztec/foundation/json-rpc/server'; import { type LogFn, createConsoleLogger } from '@aztec/foundation/log'; import { type P2PConfig } from '@aztec/p2p'; import { type ProverNodeConfig } from '@aztec/prover-node'; -import { type PXEService, type PXEServiceConfig } from '@aztec/pxe'; +import type { CliPXEOptions, PXEService } from '@aztec/pxe'; export interface ServiceStarter { (options: T, signalHandlers: (() => Promise)[], logger: LogFn): Promise; @@ -69,9 +69,9 @@ export const parseModuleOptions = (options: string): Record => { }; export const mergeEnvVarsAndCliOptions = < - T extends AztecNodeConfig | PXEServiceConfig | P2PConfig | ArchiverConfig | BotConfig | ProverNodeConfig, + T extends AztecNodeConfig | CliPXEOptions | P2PConfig | ArchiverConfig | BotConfig | ProverNodeConfig, >( - envVars: AztecNodeConfig | PXEServiceConfig | P2PConfig | ArchiverConfig | BotConfig | ProverNodeConfig, + envVars: AztecNodeConfig | CliPXEOptions | P2PConfig | ArchiverConfig | BotConfig | ProverNodeConfig, cliOptions: Record, contractsRequired = false, userLog = createConsoleLogger(), diff --git a/yarn-project/aztec/src/sandbox.ts b/yarn-project/aztec/src/sandbox.ts index d2f544c4016..57c7a25f3e5 100644 --- a/yarn-project/aztec/src/sandbox.ts +++ b/yarn-project/aztec/src/sandbox.ts @@ -33,7 +33,10 @@ import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types'; import { GasTokenAddress } from '@aztec/protocol-contracts/gas-token'; import { type PXEServiceConfig, createPXEService, getPXEServiceConfig } from '@aztec/pxe'; import { type TelemetryClient } from '@aztec/telemetry-client'; -import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; +import { + createAndStartTelemetryClient, + getConfigEnvVars as getTelemetryClientConfig, +} from '@aztec/telemetry-client/start'; import { type HDAccount, type PrivateKeyAccount, createPublicClient, http as httpViemTransport } from 'viem'; import { mnemonicToAccount } from 'viem/accounts'; @@ -156,7 +159,8 @@ export async function createSandbox(config: Partial = {}) { await deployContractsToL1(aztecNodeConfig, hdAccount); } - const node = await createAztecNode(new NoopTelemetryClient(), aztecNodeConfig); + const client = createAndStartTelemetryClient(getTelemetryClientConfig()); + const node = await createAztecNode(client, aztecNodeConfig); const pxe = await createAztecPXE(node); await deployCanonicalKeyRegistry( diff --git a/yarn-project/aztec/terraform/bot/main.tf b/yarn-project/aztec/terraform/bot/main.tf index 4353e10a48a..ed2858ef0d5 100644 --- a/yarn-project/aztec/terraform/bot/main.tf +++ b/yarn-project/aztec/terraform/bot/main.tf @@ -69,6 +69,69 @@ resource "aws_service_discovery_service" "aztec-bot" { } } +# Create a fleet. +data "template_file" "user_data" { + template = <> /etc/ecs/ecs.config +echo 'ECS_INSTANCE_ATTRIBUTES={"group": "${var.DEPLOY_TAG}-bot"}' >> /etc/ecs/ecs.config +EOF +} + +resource "aws_launch_template" "bot_launch_template" { + name = "${var.DEPLOY_TAG}-launch-template" + image_id = "ami-0cd4858f2b923aa6b" + instance_type = "m4.4xlarge" + vpc_security_group_ids = [data.terraform_remote_state.setup_iac.outputs.security_group_private_id] + + iam_instance_profile { + name = data.terraform_remote_state.setup_iac.outputs.ecs_instance_profile_name + } + + key_name = data.terraform_remote_state.setup_iac.outputs.ecs_instance_key_pair_name + + user_data = base64encode(data.template_file.user_data.rendered) + + tag_specifications { + resource_type = "instance" + tags = { + Name = "${var.DEPLOY_TAG}-bot" + prometheus = "" + } + } +} + +resource "aws_ec2_fleet" "bot_fleet" { + launch_template_config { + launch_template_specification { + launch_template_id = aws_launch_template.bot_launch_template.id + version = aws_launch_template.bot_launch_template.latest_version + } + + override { + subnet_id = data.terraform_remote_state.setup_iac.outputs.subnet_az1_private_id + availability_zone = "eu-west-2a" + max_price = "0.4" + } + + override { + subnet_id = data.terraform_remote_state.setup_iac.outputs.subnet_az2_private_id + availability_zone = "eu-west-2b" + max_price = "0.4" + } + } + + target_capacity_specification { + default_target_capacity_type = "on-demand" + total_target_capacity = var.BOT_COUNT + spot_target_capacity = 0 + on_demand_target_capacity = var.BOT_COUNT + } + + terminate_instances = true + terminate_instances_with_expiration = true +} + locals { api_prefix = "/${var.DEPLOY_TAG}/aztec-bot/${var.BOT_API_KEY}" } @@ -76,18 +139,18 @@ locals { resource "aws_ecs_task_definition" "aztec-bot" { family = "${var.DEPLOY_TAG}-aztec-bot" network_mode = "awsvpc" - cpu = 2048 - memory = 4096 - requires_compatibilities = ["FARGATE"] + requires_compatibilities = ["EC2"] execution_role_arn = data.terraform_remote_state.setup_iac.outputs.ecs_task_execution_role_arn task_role_arn = data.terraform_remote_state.aztec2_iac.outputs.cloudwatch_logging_ecs_role_arn container_definitions = jsonencode([ { - name = "${var.DEPLOY_TAG}-aztec-bot" - image = "${var.DOCKERHUB_ACCOUNT}/aztec:${var.DEPLOY_TAG}" - command = ["start", "--bot"] - essential = true + name = "${var.DEPLOY_TAG}-aztec-bot" + image = "${var.DOCKERHUB_ACCOUNT}/aztec:${var.DEPLOY_TAG}" + command = ["start", "--bot", "--pxe"] + essential = true + cpu = 8192 + memoryReservation = 30720 portMappings = [ { containerPort = 80 @@ -96,12 +159,18 @@ resource "aws_ecs_task_definition" "aztec-bot" { ] environment = [ { name = "BOT_PRIVATE_KEY", value = var.BOT_PRIVATE_KEY }, - { name = "BOT_NO_START", value = "true" }, - { name = "BOT_PXE_URL", value = "http://${var.DEPLOY_TAG}-aztec-pxe-1.local/${var.DEPLOY_TAG}/aztec-pxe-1/${var.API_KEY}" }, - { name = "BOT_TX_INTERVAL_SECONDS", value = 300 }, + { name = "BOT_NO_START", value = var.BOT_NO_START }, + { name = "BOT_TX_INTERVAL_SECONDS", value = var.BOT_TX_INTERVAL_SECONDS }, { name = "LOG_LEVEL", value = var.LOG_LEVEL }, { name = "AZTEC_PORT", value = "80" }, { name = "API_PREFIX", value = local.api_prefix }, + { name = "BOT_PRIVATE_TRANSFERS_PER_TX", value = var.BOT_PRIVATE_TRANSFERS_PER_TX }, + { name = "BOT_PUBLIC_TRANSFERS_PER_TX", value = var.BOT_PUBLIC_TRANSFERS_PER_TX }, + { name = "BOT_TX_MINED_WAIT_SECONDS", value = var.BOT_TX_MINED_WAIT_SECONDS }, + { name = "BOT_NO_WAIT_FOR_TRANSFERS", value = var.BOT_NO_WAIT_FOR_TRANSFERS }, + { name = "AZTEC_NODE_URL", value = "http://${var.DEPLOY_TAG}-aztec-node-1.local/${var.DEPLOY_TAG}/aztec-node-1/${var.API_KEY}" }, + { name = "PXE_PROVER_ENABLED", value = tostring(var.PROVING_ENABLED) }, + { name = "NETWORK", value = var.DEPLOY_TAG } ] logConfiguration = { logDriver = "awslogs" @@ -118,11 +187,10 @@ resource "aws_ecs_task_definition" "aztec-bot" { resource "aws_ecs_service" "aztec-bot" { name = "${var.DEPLOY_TAG}-aztec-bot" cluster = data.terraform_remote_state.setup_iac.outputs.ecs_cluster_id - launch_type = "FARGATE" - desired_count = 1 + launch_type = "EC2" + desired_count = var.BOT_COUNT deployment_maximum_percent = 100 deployment_minimum_healthy_percent = 0 - platform_version = "1.4.0" force_new_deployment = true network_configuration { @@ -133,11 +201,11 @@ resource "aws_ecs_service" "aztec-bot" { security_groups = [data.terraform_remote_state.setup_iac.outputs.security_group_private_id] } - load_balancer { - target_group_arn = aws_alb_target_group.bot_http.arn - container_name = "${var.DEPLOY_TAG}-aztec-bot" - container_port = 80 - } + # load_balancer { + # target_group_arn = aws_alb_target_group.bot_http.arn + # container_name = "${var.DEPLOY_TAG}-aztec-bot" + # container_port = 80 + # } service_registries { registry_arn = aws_service_discovery_service.aztec-bot.arn @@ -145,43 +213,48 @@ resource "aws_ecs_service" "aztec-bot" { container_port = 80 } + placement_constraints { + type = "memberOf" + expression = "attribute:group == ${var.DEPLOY_TAG}-bot" + } + task_definition = aws_ecs_task_definition.aztec-bot.family } -resource "aws_alb_target_group" "bot_http" { - name = "${var.DEPLOY_TAG}-bot-http" - port = 80 - protocol = "HTTP" - target_type = "ip" - vpc_id = data.terraform_remote_state.setup_iac.outputs.vpc_id - deregistration_delay = 5 - - health_check { - path = "${local.api_prefix}/status" - matcher = 200 - interval = 10 - healthy_threshold = 2 - unhealthy_threshold = 5 - timeout = 5 - } +# resource "aws_alb_target_group" "bot_http" { +# name = "${var.DEPLOY_TAG}-bot-http" +# port = 80 +# protocol = "HTTP" +# target_type = "ip" +# vpc_id = data.terraform_remote_state.setup_iac.outputs.vpc_id +# deregistration_delay = 5 - tags = { - name = "${var.DEPLOY_TAG}-bot-http" - } -} +# health_check { +# path = "${local.api_prefix}/status" +# matcher = 200 +# interval = 10 +# healthy_threshold = 2 +# unhealthy_threshold = 5 +# timeout = 5 +# } -resource "aws_lb_listener_rule" "bot_api" { - listener_arn = data.terraform_remote_state.aztec2_iac.outputs.alb_listener_arn - priority = 700 +# tags = { +# name = "${var.DEPLOY_TAG}-bot-http" +# } +# } - action { - type = "forward" - target_group_arn = aws_alb_target_group.bot_http.arn - } +# resource "aws_lb_listener_rule" "bot_api" { +# listener_arn = data.terraform_remote_state.aztec2_iac.outputs.alb_listener_arn +# priority = 700 - condition { - path_pattern { - values = ["${local.api_prefix}*"] - } - } -} +# action { +# type = "forward" +# target_group_arn = aws_alb_target_group.bot_http.arn +# } + +# condition { +# path_pattern { +# values = ["${local.api_prefix}*"] +# } +# } +# } diff --git a/yarn-project/aztec/terraform/bot/variables.tf b/yarn-project/aztec/terraform/bot/variables.tf index a4720ad6ac4..e323540fde2 100644 --- a/yarn-project/aztec/terraform/bot/variables.tf +++ b/yarn-project/aztec/terraform/bot/variables.tf @@ -18,7 +18,42 @@ variable "BOT_PRIVATE_KEY" { type = string } +variable "BOT_NO_START" { + type = string +} + +variable "BOT_PRIVATE_TRANSFERS_PER_TX" { + type = string +} + +variable "BOT_PUBLIC_TRANSFERS_PER_TX" { + type = string +} variable "LOG_LEVEL" { type = string default = "verbose" } + +variable "BOT_TX_INTERVAL_SECONDS" { + type = string + default = "300" +} + +variable "BOT_TX_MINED_WAIT_SECONDS" { + type = string +} + +variable "BOT_NO_WAIT_FOR_TRANSFERS" { + type = string + default = true +} + +variable "PROVING_ENABLED" { + type = bool + default = false +} + +variable "BOT_COUNT" { + type = string + default = "1" +} diff --git a/yarn-project/aztec/terraform/node/main.tf b/yarn-project/aztec/terraform/node/main.tf index ea9a4b8454a..d4d8ffd881e 100644 --- a/yarn-project/aztec/terraform/node/main.tf +++ b/yarn-project/aztec/terraform/node/main.tf @@ -152,7 +152,7 @@ resource "aws_ecs_task_definition" "aztec-node" { name = "init-container" image = "amazonlinux:latest" essential = false - command = ["sh", "-c", "mkdir -p ${local.data_dir}/node_${count.index + 1}"] + command = ["sh", "-c", "mkdir -p ${local.data_dir}/node_${count.index + 1}/data ${local.data_dir}/node_${count.index + 1}/temp"] mountPoints = [ { containerPath = local.data_dir @@ -206,7 +206,7 @@ resource "aws_ecs_task_definition" "aztec-node" { }, { name = "DATA_DIRECTORY" - value = "${local.data_dir}_${count.index + 1}" + value = "${local.data_dir}/node_${count.index + 1}/data" }, { name = "ARCHIVER_POLLING_INTERVAL" @@ -294,7 +294,7 @@ resource "aws_ecs_task_definition" "aztec-node" { }, { name = "BOOTSTRAP_NODES" - value = "enr:-JO4QNvVz7yYHQ4nzZQ7JCng9LOQkDnFqeLntDEfrAAGOS_eMFWOE4ZlyjYKb3J-yCGu8xoXXEUnUqI8iTJj1K43KH0EjWF6dGVjX25ldHdvcmsBgmlkgnY0gmlwhA0pYm6Jc2VjcDI1NmsxoQLzGvsxdzM9VhPjrMnxLmMxvrEcvSg-QZq7PWXDnnIy1YN1ZHCCnjQ" + value = var.BOOTSTRAP_NODES }, { name = "P2P_ENABLED" @@ -343,6 +343,22 @@ resource "aws_ecs_task_definition" "aztec-node" { { name = "TEL_SERVICE_NAME" value = "${var.DEPLOY_TAG}-aztec-node-${count.index + 1}" + }, + { + name = "BB_WORKING_DIRECTORY" + value = "${local.data_dir}/node_${count.index + 1}/temp" + }, + { + name = "ACVM_WORKING_DIRECTORY" + value = "${local.data_dir}/node_${count.index + 1}/temp" + }, + { + name = "LOG_LEVEL" + value = "info" + }, + { + name = "TEL_NETWORK_ID", + value = "${var.DEPLOY_TAG}" } ] mountPoints = [ @@ -430,7 +446,7 @@ resource "aws_alb_target_group" "aztec-node-http" { resource "aws_lb_listener_rule" "api" { count = local.node_count listener_arn = data.terraform_remote_state.aztec2_iac.outputs.alb_listener_arn - priority = 500 + count.index + priority = var.NODE_LB_RULE_PRIORITY + count.index action { type = "forward" @@ -465,18 +481,20 @@ resource "aws_security_group_rule" "allow-node-tcp-out" { } resource "aws_security_group_rule" "allow-node-udp-in" { + count = local.node_count type = "ingress" from_port = var.NODE_P2P_UDP_PORT - to_port = var.NODE_P2P_UDP_PORT + 100 + to_port = var.NODE_P2P_UDP_PORT + count.index protocol = "udp" cidr_blocks = ["0.0.0.0/0"] security_group_id = data.terraform_remote_state.aztec-network_iac.outputs.p2p_security_group_id } resource "aws_security_group_rule" "allow-node-udp-out" { + count = local.node_count type = "egress" from_port = var.NODE_P2P_UDP_PORT - to_port = var.NODE_P2P_UDP_PORT + 100 + to_port = var.NODE_P2P_UDP_PORT + count.index protocol = "udp" cidr_blocks = ["0.0.0.0/0"] security_group_id = data.terraform_remote_state.aztec-network_iac.outputs.p2p_security_group_id diff --git a/yarn-project/aztec/terraform/node/variables.tf b/yarn-project/aztec/terraform/node/variables.tf index 620157e8c25..fe6b5c88ec8 100644 --- a/yarn-project/aztec/terraform/node/variables.tf +++ b/yarn-project/aztec/terraform/node/variables.tf @@ -3,7 +3,8 @@ variable "DEPLOY_TAG" { } variable "IMAGE_TAG" { - type = string + type = string + default = "latest" } variable "API_KEY" { @@ -25,12 +26,12 @@ variable "L1_CHAIN_ID" { variable "NODE_P2P_TCP_PORT" { type = number - default = 40400 + default = 40000 } variable "NODE_P2P_UDP_PORT" { type = number - default = 40300 + default = 45000 } variable "DOCKERHUB_ACCOUNT" { @@ -44,7 +45,7 @@ variable "SEQ_MAX_TX_PER_BLOCK" { variable "SEQ_MIN_TX_PER_BLOCK" { type = string - default = 0 + default = 2 } variable "SEQ_MAX_SECONDS_BETWEEN_BLOCKS" { @@ -69,7 +70,7 @@ variable "P2P_MAX_PEERS" { variable "P2P_ENABLED" { type = bool - default = true + default = false } variable "P2P_TX_POOL_KEEP_PROVEN_FOR" { @@ -81,3 +82,13 @@ variable "PROVING_ENABLED" { type = bool default = false } + +variable "BOOTSTRAP_NODES" { + type = string + default = "" +} + +variable "NODE_LB_RULE_PRIORITY" { + type = number + default = 4000 +} diff --git a/yarn-project/aztec/terraform/prover/main.tf b/yarn-project/aztec/terraform/prover/main.tf index 42f18016b56..c6e3ff8c206 100644 --- a/yarn-project/aztec/terraform/prover/main.tf +++ b/yarn-project/aztec/terraform/prover/main.tf @@ -53,8 +53,8 @@ data "terraform_remote_state" "aztec-network_node" { } locals { - node_count = data.terraform_remote_state.aztec-network_node.outputs.node_count - agents_per_sequencer = var.AGENTS_PER_SEQUENCER + node_count = data.terraform_remote_state.aztec-network_node.outputs.node_count + agents_per_prover = var.AGENTS_PER_PROVER } resource "aws_cloudwatch_log_group" "aztec-proving-agent-log-group" { @@ -89,14 +89,145 @@ resource "aws_service_discovery_service" "aztec-proving-agent" { } } +# Create an autoscaling group for every sequencer. For each group we want 1 permanent on-demand instance to ensure liveness. +# We will fill the remaining desired instances from spot capacity. +data "template_file" "user_data" { + count = local.node_count + template = <> /etc/ecs/ecs.config +echo 'ECS_INSTANCE_ATTRIBUTES={"group": "${var.DEPLOY_TAG}-proving-agent-group-${count.index + 1}"}' >> /etc/ecs/ecs.config +EOF +} + +# Launch template for our prover agents +# 32 cores and 128 GB memory +resource "aws_launch_template" "proving-agent-launch-template" { + count = local.node_count + name = "${var.DEPLOY_TAG}-proving-agent-launch-template-${count.index + 1}" + image_id = "ami-0cd4858f2b923aa6b" + instance_type = "m5.8xlarge" + vpc_security_group_ids = [data.terraform_remote_state.setup_iac.outputs.security_group_private_id] + + iam_instance_profile { + name = data.terraform_remote_state.setup_iac.outputs.ecs_instance_profile_name + } + + key_name = data.terraform_remote_state.setup_iac.outputs.ecs_instance_key_pair_name + + user_data = base64encode(data.template_file.user_data[count.index].rendered) + + tag_specifications { + resource_type = "instance" + tags = { + Name = "${var.DEPLOY_TAG}-proving-agent-group-${count.index + 1}" + prometheus = "" + } + } +} + +resource "aws_ec2_fleet" "aztec_proving_agent_fleet" { + count = local.node_count + launch_template_config { + launch_template_specification { + launch_template_id = aws_launch_template.proving-agent-launch-template[count.index].id + version = aws_launch_template.proving-agent-launch-template[count.index].latest_version + } + + override { + subnet_id = data.terraform_remote_state.setup_iac.outputs.subnet_az1_private_id + availability_zone = "eu-west-2a" + max_price = "0.7" + } + + override { + subnet_id = data.terraform_remote_state.setup_iac.outputs.subnet_az2_private_id + availability_zone = "eu-west-2b" + max_price = "0.7" + } + } + + target_capacity_specification { + default_target_capacity_type = "on-demand" + total_target_capacity = local.agents_per_prover + spot_target_capacity = 0 + on_demand_target_capacity = local.agents_per_prover + } + + terminate_instances = true + terminate_instances_with_expiration = true +} + +# Sets up the autoscaling groups +# resource "aws_autoscaling_group" "proving-agent-auto-scaling-group" { +# count = local.node_count +# min_size = 1 +# max_size = local.agents_per_prover +# desired_capacity = 1 +# vpc_zone_identifier = [data.terraform_remote_state.setup_iac.outputs.subnet_az1_private_id, data.terraform_remote_state.setup_iac.outputs.subnet_az2_private_id] + +# mixed_instances_policy { +# instances_distribution { +# on_demand_base_capacity = 1 +# on_demand_percentage_above_base_capacity = 100 +# spot_allocation_strategy = "lowest-price" +# spot_max_price = "0.7" # Current spot instance price for the m5.8xlarge instance type +# } + +# launch_template { +# launch_template_specification { +# launch_template_id = aws_launch_template.proving-agent-launch-template[count.index].id +# version = "$Latest" +# } +# } +# } + +# tag { +# key = "AmazonECSManaged" +# value = true +# propagate_at_launch = true +# } +# } + + +# # Capacity provider to manage the scaling of the EC2 instances +# resource "aws_ecs_capacity_provider" "proving-agent-capacity-provider" { +# count = local.node_count +# name = "${var.DEPLOY_TAG}-proving-agent-capacity-provider-${count.index + 1}" + + +# auto_scaling_group_provider { +# auto_scaling_group_arn = aws_autoscaling_group.proving-agent-auto-scaling-group[count.index].arn +# managed_termination_protection = "DISABLED" + +# managed_scaling { +# maximum_scaling_step_size = local.agents_per_prover +# minimum_scaling_step_size = 1 +# status = "ENABLED" +# target_capacity = 100 +# } +# } +# } + +# # Update the capacity providers on the cluster +# resource "aws_ecs_cluster_capacity_providers" "proving-agent-capacity-providers" { +# count = local.node_count +# cluster_name = data.terraform_remote_state.setup_iac.outputs.ecs_cluster_name + +# #capacity_providers = [aws_ecs_capacity_provider.proving-agent-capacity-provider[count.index].name] + +# capacity_providers = local.enable_ecs_cluster_auto_scaling == true ? aws_ecs_capacity_provider.asg[*].name : [] + +# capacity_providers = (contains(capacity_providers, aws_ecs_capacity_provider.proving-agent-capacity-provider[count.index].name) == false ? concat(capacity_providers, [aws_ecs_capacity_provider.proving-agent-capacity-provider[count.index].name]) : capacity_providers) +# } + + # Define task definitions for each node. resource "aws_ecs_task_definition" "aztec-proving-agent" { count = local.node_count family = "${var.DEPLOY_TAG}-aztec-proving-agent-group-${count.index + 1}" - requires_compatibilities = ["FARGATE"] + requires_compatibilities = ["EC2"] network_mode = "awsvpc" - cpu = "16384" - memory = "98304" execution_role_arn = data.terraform_remote_state.setup_iac.outputs.ecs_task_execution_role_arn task_role_arn = data.terraform_remote_state.aztec2_iac.outputs.cloudwatch_logging_ecs_role_arn container_definitions = < ~327s }, }); - this.witGenDuration = meter.createGauge(Metrics.CIRCUIT_WITNESS_GEN_DURATION, { + this.witGenDuration = meter.createHistogram(Metrics.CIRCUIT_WITNESS_GEN_DURATION, { description: 'Records how long it takes to generate the partial witness for a circuit', - unit: 's', - valueType: ValueType.DOUBLE, + unit: 'ms', + valueType: ValueType.INT, + advice: { + explicitBucketBoundaries: millisecondBuckets(1), + }, }); - // ideally this would be a histogram, but proving takes a long time on the server - // and they don't happen that often so Prometheus & Grafana have a hard time handling it - this.provingDuration = meter.createGauge(Metrics.CIRCUIT_PROVING_DURATION, { - unit: 's', + this.provingDuration = meter.createHistogram(Metrics.CIRCUIT_PROVING_DURATION, { + unit: 'ms', description: 'Records how long it takes to prove a circuit', - valueType: ValueType.DOUBLE, + valueType: ValueType.INT, + advice: { + explicitBucketBoundaries: millisecondBuckets(2), // 100ms -> 54 minutes + }, }); this.witGenInputSize = meter.createGauge(Metrics.CIRCUIT_WITNESS_GEN_INPUT_SIZE, { @@ -87,15 +92,15 @@ export class ProverInstrumentation { * Records the duration of a circuit operation. * @param metric - The metric to record * @param circuitName - The name of the circuit - * @param timerOrS - The duration + * @param timerOrMS - The duration */ recordDuration( metric: 'simulationDuration' | 'witGenDuration' | 'provingDuration', - circuitName: CircuitName, - timerOrS: Timer | number, + circuitName: CircuitName | 'tubeCircuit', + timerOrMS: Timer | number, ) { - const s = typeof timerOrS === 'number' ? timerOrS : timerOrS.s(); - this[metric].record(s, { + const ms = typeof timerOrMS === 'number' ? timerOrMS : timerOrMS.ms(); + this[metric].record(Math.ceil(ms), { [Attributes.PROTOCOL_CIRCUIT_NAME]: circuitName, [Attributes.PROTOCOL_CIRCUIT_TYPE]: 'server', }); @@ -105,11 +110,11 @@ export class ProverInstrumentation { * Records the duration of an AVM circuit operation. * @param metric - The metric to record * @param appCircuitName - The name of the function circuit (should be a `contract:function` string) - * @param timerOrS - The duration + * @param timerOrMS - The duration */ - recordAvmDuration(metric: 'witGenDuration' | 'provingDuration', appCircuitName: string, timerOrS: Timer | number) { - const s = typeof timerOrS === 'number' ? timerOrS : timerOrS.s(); - this[metric].record(s, { + recordAvmDuration(metric: 'witGenDuration' | 'provingDuration', appCircuitName: string, timerOrMS: Timer | number) { + const ms = typeof timerOrMS === 'number' ? timerOrMS : timerOrMS.s(); + this[metric].record(Math.ceil(ms), { [Attributes.APP_CIRCUIT_NAME]: appCircuitName, }); } @@ -122,7 +127,7 @@ export class ProverInstrumentation { */ recordSize( metric: 'witGenInputSize' | 'witGenOutputSize' | 'proofSize' | 'circuitSize' | 'circuitPublicInputCount', - circuitName: CircuitName, + circuitName: CircuitName | 'tubeCircuit', size: number, ) { this[metric].record(Math.ceil(size), { diff --git a/yarn-project/bb-prover/src/prover/bb_prover.ts b/yarn-project/bb-prover/src/prover/bb_prover.ts index 8c128bdf135..e303604f2b9 100644 --- a/yarn-project/bb-prover/src/prover/bb_prover.ts +++ b/yarn-project/bb-prover/src/prover/bb_prover.ts @@ -291,7 +291,6 @@ export class BBNativeRollupProver implements ServerCircuitProver { logger.debug(`kernel Data proof: ${baseRollupInput.kernelData.proof}`); logger.info(`in getBaseRollupProof`); logger.info(`Number of public inputs in baseRollupInput: ${baseRollupInput.kernelData.vk.numPublicInputs}`); - logger.info(`Number of public inputs ${baseRollupInput.kernelData.publicInputs}`); baseRollupInput.kernelData.proof = await this.ensureValidProof( baseRollupInput.kernelData.proof, 'BaseRollupArtifact', @@ -531,7 +530,7 @@ export class BBNativeRollupProver implements ServerCircuitProver { const proof = new Proof(rawProof, vkData.numPublicInputs); const circuitName = mapProtocolArtifactNameToCircuitName(circuitType); - this.instrumentation.recordDuration('provingDuration', circuitName, provingResult.durationMs / 1000); + this.instrumentation.recordDuration('provingDuration', circuitName, provingResult.durationMs); this.instrumentation.recordSize('proofSize', circuitName, proof.buffer.length); this.instrumentation.recordSize('circuitPublicInputCount', circuitName, vkData.numPublicInputs); this.instrumentation.recordSize('circuitSize', circuitName, vkData.circuitSize); @@ -634,6 +633,12 @@ export class BBNativeRollupProver implements ServerCircuitProver { // Read the proof as fields const tubeVK = await extractVkData(provingResult.vkPath!); const tubeProof = await this.readTubeProofAsFields(provingResult.proofPath!, tubeVK, TUBE_PROOF_LENGTH); + + this.instrumentation.recordDuration('provingDuration', 'tubeCircuit', provingResult.durationMs); + this.instrumentation.recordSize('proofSize', 'tubeCircuit', tubeProof.binaryProof.buffer.length); + this.instrumentation.recordSize('circuitPublicInputCount', 'tubeCircuit', tubeVK.numPublicInputs); + this.instrumentation.recordSize('circuitSize', 'tubeCircuit', tubeVK.circuitSize); + // Sanity check the tube proof (can be removed later) await this.verifyWithKey(tubeVK, tubeProof.binaryProof); @@ -681,7 +686,7 @@ export class BBNativeRollupProver implements ServerCircuitProver { const proof = await this.readProofAsFields(provingResult.proofPath!, circuitType, proofLength); const circuitName = mapProtocolArtifactNameToCircuitName(circuitType); - this.instrumentation.recordDuration('provingDuration', circuitName, provingResult.durationMs / 1000); + this.instrumentation.recordDuration('provingDuration', circuitName, provingResult.durationMs); this.instrumentation.recordSize('proofSize', circuitName, proof.binaryProof.buffer.length); this.instrumentation.recordSize('circuitPublicInputCount', circuitName, vkData.numPublicInputs); this.instrumentation.recordSize('circuitSize', circuitName, vkData.circuitSize); diff --git a/yarn-project/bot/src/bot.ts b/yarn-project/bot/src/bot.ts index 1fff1e12f51..ca0543ebccc 100644 --- a/yarn-project/bot/src/bot.ts +++ b/yarn-project/bot/src/bot.ts @@ -68,9 +68,15 @@ export class Bot { this.log.verbose(`Sending tx`, logCtx); const tx = batch.send(opts); - this.log.verbose(`Awaiting tx ${tx.getTxHash()} to be mined (timeout ${this.config.txMinedWaitSeconds}s)`, logCtx); - const receipt = await tx.wait({ timeout: this.config.txMinedWaitSeconds }); + const txHash = await tx.getTxHash(); + + if (this.config.noWaitForTransfers) { + this.log.info(`Transaction ${txHash} sent, not waiting for it to be mined`); + return; + } + this.log.verbose(`Awaiting tx ${txHash} to be mined (timeout ${this.config.txMinedWaitSeconds}s)`, logCtx); + const receipt = await tx.wait({ timeout: this.config.txMinedWaitSeconds }); this.log.info(`Tx ${receipt.txHash} mined in block ${receipt.blockNumber}`, logCtx); } diff --git a/yarn-project/bot/src/config.ts b/yarn-project/bot/src/config.ts index ee93af956c8..1199cdcce2f 100644 --- a/yarn-project/bot/src/config.ts +++ b/yarn-project/bot/src/config.ts @@ -22,6 +22,8 @@ export type BotConfig = { noStart: boolean; /** How long to wait for a tx to be mined before reporting an error. */ txMinedWaitSeconds: number; + /** Don't wait for transfer transactions. */ + noWaitForTransfers: boolean; }; export function getBotConfigFromEnv(): BotConfig { @@ -35,6 +37,7 @@ export function getBotConfigFromEnv(): BotConfig { BOT_PUBLIC_TRANSFERS_PER_TX, BOT_NO_START, BOT_TX_MINED_WAIT_SECONDS, + BOT_NO_WAIT_FOR_TRANSFERS, } = process.env; if (BOT_FEE_PAYMENT_METHOD && !['native', 'none'].includes(BOT_FEE_PAYMENT_METHOD)) { throw new Error(`Invalid bot fee payment method: ${BOT_FEE_PAYMENT_METHOD}`); @@ -53,6 +56,7 @@ export function getBotConfigFromEnv(): BotConfig { feePaymentMethod: BOT_FEE_PAYMENT_METHOD ? (BOT_FEE_PAYMENT_METHOD as 'native' | 'none') : undefined, noStart: BOT_NO_START ? ['1', 'true'].includes(BOT_NO_START) : undefined, txMinedWaitSeconds: BOT_TX_MINED_WAIT_SECONDS ? parseInt(BOT_TX_MINED_WAIT_SECONDS) : undefined, + noWaitForTransfers: BOT_NO_WAIT_FOR_TRANSFERS ? ['1', 'true'].includes(BOT_NO_WAIT_FOR_TRANSFERS) : undefined, }); } @@ -68,6 +72,7 @@ export function getBotDefaultConfig(overrides: Partial = {}): BotConf feePaymentMethod: 'none', noStart: false, txMinedWaitSeconds: 180, + noWaitForTransfers: false, ...compact(overrides), }; } diff --git a/yarn-project/bot/src/factory.ts b/yarn-project/bot/src/factory.ts index c12e4d52866..b1e2e56613b 100644 --- a/yarn-project/bot/src/factory.ts +++ b/yarn-project/bot/src/factory.ts @@ -19,7 +19,13 @@ export class BotFactory { throw new Error(`Either a PXE client or a PXE URL must be provided`); } - this.pxe = dependencies.pxe ?? createPXEClient(config.pxeUrl!); + if (dependencies.pxe) { + this.log.info(`Using local PXE`); + this.pxe = dependencies.pxe; + return; + } + this.log.info(`Using remote PXE at ${config.pxeUrl!}`); + this.pxe = createPXEClient(config.pxeUrl!); } /** @@ -48,7 +54,7 @@ export class BotFactory { return account.register(); } else { this.log.info(`Initializing account at ${account.getAddress().toString()}`); - return account.waitSetup(); + return account.waitSetup({ timeout: this.config.txMinedWaitSeconds }); } } @@ -74,7 +80,7 @@ export class BotFactory { return deploy.register(); } else { this.log.info(`Deploying token contract at ${address.toString()}`); - return deploy.send(deployOpts).deployed(); + return deploy.send(deployOpts).deployed({ timeout: this.config.txMinedWaitSeconds }); } } @@ -98,6 +104,6 @@ export class BotFactory { this.log.info(`Skipping minting as ${sender.toString()} has enough tokens`); return; } - await new BatchCall(token.wallet, calls).send().wait(); + await new BatchCall(token.wallet, calls).send().wait({ timeout: this.config.txMinedWaitSeconds }); } } diff --git a/yarn-project/circuits.js/src/structs/kernel/private_kernel_empty_inputs.test.ts b/yarn-project/circuits.js/src/structs/kernel/private_kernel_empty_inputs.test.ts new file mode 100644 index 00000000000..b62840aefcc --- /dev/null +++ b/yarn-project/circuits.js/src/structs/kernel/private_kernel_empty_inputs.test.ts @@ -0,0 +1,11 @@ +import { Fr } from '@aztec/foundation/fields'; + +import { makeHeader } from '../../tests/factories.js'; +import { PrivateKernelEmptyInputData } from './private_kernel_empty_inputs.js'; + +describe('PrivateKernelEmptyInputData', () => { + it('serializes and deserializes', () => { + const obj = new PrivateKernelEmptyInputData(makeHeader(), Fr.random(), Fr.random(), Fr.random()); + expect(PrivateKernelEmptyInputData.fromString(obj.toString())).toEqual(obj); + }); +}); diff --git a/yarn-project/circuits.js/src/structs/kernel/private_kernel_empty_inputs.ts b/yarn-project/circuits.js/src/structs/kernel/private_kernel_empty_inputs.ts index 01ea1fb6113..a52c30b2840 100644 --- a/yarn-project/circuits.js/src/structs/kernel/private_kernel_empty_inputs.ts +++ b/yarn-project/circuits.js/src/structs/kernel/private_kernel_empty_inputs.ts @@ -1,13 +1,46 @@ -import { type Fr } from '@aztec/foundation/fields'; -import { serializeToBuffer } from '@aztec/foundation/serialize'; +import { Fr } from '@aztec/foundation/fields'; +import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { type FieldsOf } from '@aztec/foundation/types'; import { type RECURSIVE_PROOF_LENGTH } from '../../constants.gen.js'; -import { type Header } from '../header.js'; +import { Header } from '../header.js'; import { type RecursiveProof } from '../recursive_proof.js'; import { type VerificationKeyAsFields } from '../verification_key.js'; -export type PrivateKernelEmptyInputData = Omit, 'emptyNested'>; +export class PrivateKernelEmptyInputData { + constructor( + public readonly header: Header, + public readonly chainId: Fr, + public readonly version: Fr, + public readonly vkTreeRoot: Fr, + ) {} + + toBuffer(): Buffer { + return serializeToBuffer(this.header, this.chainId, this.version, this.vkTreeRoot); + } + + toString(): string { + return this.toBuffer().toString('hex'); + } + + static fromBuffer(buf: Buffer) { + const reader = BufferReader.asReader(buf); + return new PrivateKernelEmptyInputData( + reader.readObject(Header), + reader.readObject(Fr), + reader.readObject(Fr), + reader.readObject(Fr), + ); + } + + static fromString(str: string): PrivateKernelEmptyInputData { + return PrivateKernelEmptyInputData.fromBuffer(Buffer.from(str, 'hex')); + } + + static from(fields: FieldsOf) { + return new PrivateKernelEmptyInputData(fields.header, fields.chainId, fields.version, fields.vkTreeRoot); + } +} export class PrivateKernelEmptyInputs { constructor( diff --git a/yarn-project/cli-wallet/src/cmds/create_account.ts b/yarn-project/cli-wallet/src/cmds/create_account.ts index 5076fa60d26..45b1ed5d942 100644 --- a/yarn-project/cli-wallet/src/cmds/create_account.ts +++ b/yarn-project/cli-wallet/src/cmds/create_account.ts @@ -1,6 +1,7 @@ import { getSchnorrAccount } from '@aztec/accounts/schnorr'; import { type DeployAccountOptions, createCompatibleClient } from '@aztec/aztec.js'; import { deriveSigningKey } from '@aztec/circuits.js'; +import { prettyPrintJSON } from '@aztec/cli/cli-utils'; import { Fr } from '@aztec/foundation/fields'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; @@ -15,6 +16,7 @@ export async function createAccount( skipInitialization: boolean, wait: boolean, feeOpts: IFeeOpts, + json: boolean, debugLogger: DebugLogger, log: LogFn, ) { @@ -27,16 +29,29 @@ export async function createAccount( const account = getSchnorrAccount(client, privateKey, deriveSigningKey(privateKey), salt); const { address, publicKeys, partialAddress } = account.getCompleteAddress(); - log(`\nNew account:\n`); - log(`Address: ${address.toString()}`); - log(`Public key: 0x${publicKeys.toString()}`); - if (printPK) { - log(`Private key: ${privateKey.toString()}`); + const out: Record = {}; + if (json) { + out.address = address; + out.publicKey = publicKeys; + if (printPK) { + out.privateKey = privateKey; + } + out.partialAddress = partialAddress; + out.salt = salt; + out.initHash = account.getInstance().initializationHash; + out.deployer = account.getInstance().deployer; + } else { + log(`\nNew account:\n`); + log(`Address: ${address.toString()}`); + log(`Public key: 0x${publicKeys.toString()}`); + if (printPK) { + log(`Private key: ${privateKey.toString()}`); + } + log(`Partial address: ${partialAddress.toString()}`); + log(`Salt: ${salt.toString()}`); + log(`Init hash: ${account.getInstance().initializationHash.toString()}`); + log(`Deployer: ${account.getInstance().deployer.toString()}`); } - log(`Partial address: ${partialAddress.toString()}`); - log(`Salt: ${salt.toString()}`); - log(`Init hash: ${account.getInstance().initializationHash.toString()}`); - log(`Deployer: ${account.getInstance().deployer.toString()}`); let tx; let txReceipt; @@ -52,23 +67,47 @@ export async function createAccount( }; if (feeOpts.estimateOnly) { const gas = await (await account.getDeployMethod()).estimateGas({ ...sendOpts }); - printGasEstimates(feeOpts, gas, log); + if (json) { + out.fee = { + gasLimits: { + da: gas.gasLimits.daGas, + l2: gas.gasLimits.l2Gas, + }, + teardownGasLimits: { + da: gas.teardownGasLimits.daGas, + l2: gas.teardownGasLimits, + }, + }; + } else { + printGasEstimates(feeOpts, gas, log); + } } else { tx = account.deploy({ ...sendOpts }); const txHash = await tx.getTxHash(); debugLogger.debug(`Account contract tx sent with hash ${txHash}`); + out.txHash = txHash; if (wait) { - log(`\nWaiting for account contract deployment...`); + if (!json) { + log(`\nWaiting for account contract deployment...`); + } txReceipt = await tx.wait(); + out.txReceipt = { + status: txReceipt.status, + transactionFee: txReceipt.transactionFee, + }; } } } - if (tx) { - log(`Deploy tx hash: ${await tx.getTxHash()}`); - } - if (txReceipt) { - log(`Deploy tx fee: ${txReceipt.transactionFee}`); + if (json) { + log(prettyPrintJSON(out)); + } else { + if (tx) { + log(`Deploy tx hash: ${await tx.getTxHash()}`); + } + if (txReceipt) { + log(`Deploy tx fee: ${txReceipt.transactionFee}`); + } } return { alias, address, privateKey, salt }; diff --git a/yarn-project/cli-wallet/src/cmds/index.ts b/yarn-project/cli-wallet/src/cmds/index.ts index e8754109955..d972c648141 100644 --- a/yarn-project/cli-wallet/src/cmds/index.ts +++ b/yarn-project/cli-wallet/src/cmds/index.ts @@ -42,22 +42,24 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: DebugL '--register-only', 'Just register the account on the PXE. Do not deploy or initialize the account contract.', ) + .option('--json', 'Emit output as json') // `options.wait` is default true. Passing `--no-wait` will set it to false. // https://github.com/tj/commander.js#other-option-types-negatable-boolean-and-booleanvalue .option('--no-wait', 'Skip waiting for the contract to be deployed. Print the hash of deployment transaction') .action(async (_options, command) => { const { createAccount } = await import('../cmds/create_account.js'); const options = command.optsWithGlobals(); - const { privateKey, wait, registerOnly, skipInitialization, publicDeploy, rpcUrl, alias } = options; + const { privateKey, wait, registerOnly, skipInitialization, publicDeploy, rpcUrl, alias, json } = options; const accountCreationResult = await createAccount( rpcUrl, privateKey, alias, registerOnly, - skipInitialization, publicDeploy, + skipInitialization, wait, FeeOpts.fromCli(options, log), + json, debugLogger, log, ); diff --git a/yarn-project/cli/package.json b/yarn-project/cli/package.json index 2d5ce54d615..b8662539708 100644 --- a/yarn-project/cli/package.json +++ b/yarn-project/cli/package.json @@ -8,6 +8,7 @@ "./infrastructure": "./dest/cmds/infrastructure/index.js", "./l1": "./dest/cmds/l1/index.js", "./pxe": "./dest/cmds/pxe/index.js", + "./cli-utils": "./dest/utils/index.js", "./misc": "./dest/cmds/misc/index.js", "./utils": "./dest/utils/index.js" }, diff --git a/yarn-project/cli/src/cmds/devnet/bootstrap_devnet.ts b/yarn-project/cli/src/cmds/devnet/bootstrap_network.ts similarity index 56% rename from yarn-project/cli/src/cmds/devnet/bootstrap_devnet.ts rename to yarn-project/cli/src/cmds/devnet/bootstrap_network.ts index b13d1d368e9..90c21da829d 100644 --- a/yarn-project/cli/src/cmds/devnet/bootstrap_devnet.ts +++ b/yarn-project/cli/src/cmds/devnet/bootstrap_network.ts @@ -9,12 +9,19 @@ import { deployL1Contract, } from '@aztec/ethereum'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; -import { PortalERC20Abi, PortalERC20Bytecode, TokenPortalAbi, TokenPortalBytecode } from '@aztec/l1-artifacts'; import { getContract } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; -export async function bootstrapDevnet( +import { FeeJuicePortalManager } from '../../portal_manager.js'; + +type ContractDeploymentInfo = { + address: AztecAddress; + initHash: Fr; + salt: Fr; +}; + +export async function bootstrapNetwork( pxeUrl: string, l1Url: string, l1ChainId: string, @@ -37,11 +44,16 @@ export async function bootstrapDevnet( ); const { erc20Address, portalAddress } = await deployERC20(l1Clients); - const { tokenAddress, bridgeAddress } = await deployToken(wallet, portalAddress); - await initPortal(pxe, l1Clients, erc20Address, portalAddress, bridgeAddress); - const fpcAddress = await deployFPC(wallet, tokenAddress); - const counterAddress = await deployCounter(wallet); + const { token, bridge } = await deployToken(wallet, portalAddress); + + await initPortal(pxe, l1Clients, erc20Address, portalAddress, bridge.address); + + const fpc = await deployFPC(wallet, token.address); + + const counter = await deployCounter(wallet); + + await fundFPC(counter.address, wallet, l1Clients, fpc.address, debugLog); if (json) { log( @@ -49,10 +61,26 @@ export async function bootstrapDevnet( { devCoinL1: erc20Address.toString(), devCoinPortalL1: portalAddress.toString(), - devCoinL2: tokenAddress.toString(), - devCoinBridgeL2: bridgeAddress.toString(), - devCoinFpcL2: fpcAddress.toString(), - counterL2: counterAddress.toString(), + devCoin: { + address: token.address.toString(), + initHash: token.initHash.toString(), + salt: token.salt.toString(), + }, + devCoinBridge: { + address: bridge.address.toString(), + initHash: bridge.initHash.toString(), + salt: bridge.salt.toString(), + }, + devCoinFpc: { + address: fpc.address.toString(), + initHash: fpc.initHash.toString(), + salt: fpc.salt.toString(), + }, + counter: { + address: counter.address.toString(), + initHash: counter.initHash.toString(), + salt: counter.salt.toString(), + }, }, null, 2, @@ -61,10 +89,10 @@ export async function bootstrapDevnet( } else { log(`DevCoin L1: ${erc20Address}`); log(`DevCoin L1 Portal: ${portalAddress}`); - log(`DevCoin L2: ${tokenAddress}`); - log(`DevCoin L2 Bridge: ${bridgeAddress}`); - log(`DevCoin FPC: ${fpcAddress}`); - log(`Counter: ${counterAddress}`); + log(`DevCoin L2: ${token.address}`); + log(`DevCoin L2 Bridge: ${bridge.address}`); + log(`DevCoin FPC: ${fpc.address}`); + log(`Counter: ${counter.address}`); } } @@ -72,6 +100,10 @@ export async function bootstrapDevnet( * Step 1. Deploy the L1 contracts, but don't initialize */ async function deployERC20({ walletClient, publicClient }: L1Clients) { + const { PortalERC20Abi, PortalERC20Bytecode, TokenPortalAbi, TokenPortalBytecode } = await import( + '@aztec/l1-artifacts' + ); + const erc20: ContractArtifacts = { contractAbi: PortalERC20Abi, contractBytecode: PortalERC20Bytecode, @@ -108,7 +140,7 @@ async function deployERC20({ walletClient, publicClient }: L1Clients) { async function deployToken( wallet: Wallet, l1Portal: EthAddress, -): Promise<{ tokenAddress: AztecAddress; bridgeAddress: AztecAddress }> { +): Promise<{ token: ContractDeploymentInfo; bridge: ContractDeploymentInfo }> { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - Importing noir-contracts.js even in devDeps results in a circular dependency error. Need to ignore because this line doesn't cause an error in a dev environment const { TokenContract, TokenBridgeContract } = await import('@aztec/noir-contracts.js'); @@ -123,8 +155,16 @@ async function deployToken( .wait(); return { - tokenAddress: devCoin.address, - bridgeAddress: bridge.address, + token: { + address: devCoin.address, + initHash: devCoin.instance.initializationHash, + salt: devCoin.instance.salt, + }, + bridge: { + address: bridge.address, + initHash: bridge.instance.initializationHash, + salt: bridge.instance.salt, + }, }; } @@ -138,6 +178,7 @@ async function initPortal( portal: EthAddress, bridge: AztecAddress, ) { + const { TokenPortalAbi } = await import('@aztec/l1-artifacts'); const { l1ContractAddresses: { registryAddress }, } = await pxe.getNodeInfo(); @@ -149,10 +190,11 @@ async function initPortal( }); const hash = await contract.write.initialize([registryAddress.toString(), erc20.toString(), bridge.toString()]); + await publicClient.waitForTransactionReceipt({ hash }); } -async function deployFPC(wallet: Wallet, tokenAddress: AztecAddress): Promise { +async function deployFPC(wallet: Wallet, tokenAddress: AztecAddress): Promise { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - Importing noir-contracts.js even in devDeps results in a circular dependency error. Need to ignore because this line doesn't cause an error in a dev environment const { FPCContract } = await import('@aztec/noir-contracts.js'); @@ -160,13 +202,59 @@ async function deployFPC(wallet: Wallet, tokenAddress: AztecAddress): Promise { +async function deployCounter(wallet: Wallet): Promise { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - Importing noir-contracts.js even in devDeps results in a circular dependency error. Need to ignore because this line doesn't cause an error in a dev environment const { CounterContract } = await import('@aztec/noir-contracts.js'); const counter = await CounterContract.deploy(wallet, 1, wallet.getAddress(), wallet.getAddress()).send().deployed(); - return counter.address; + const info: ContractDeploymentInfo = { + address: counter.address, + initHash: counter.instance.initializationHash, + salt: counter.instance.salt, + }; + return info; +} + +async function fundFPC( + counterAddress: AztecAddress, + wallet: Wallet, + l1Clients: L1Clients, + fpcAddress: AztecAddress, + debugLog: DebugLogger, +) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - Importing noir-contracts.js even in devDeps results in a circular dependency error. Need to ignore because this line doesn't cause an error in a dev environment + const { GasTokenContract, CounterContract } = await import('@aztec/noir-contracts.js'); + const { + protocolContractAddresses: { gasToken }, + } = await wallet.getPXEInfo(); + + const gasTokenContract = await GasTokenContract.at(gasToken, wallet); + + const feeJuicePortal = await FeeJuicePortalManager.create( + wallet, + l1Clients.publicClient, + l1Clients.walletClient, + debugLog, + ); + + const amount = 10n ** 21n; + const { secret } = await feeJuicePortal.prepareTokensOnL1(amount, amount, fpcAddress, true); + + const counter = await CounterContract.at(counterAddress, wallet); + + // TODO (alexg) remove this once sequencer builds blocks continuously + // advance the chain + await counter.methods.increment(wallet.getAddress(), wallet.getAddress()).send().wait(); + await counter.methods.increment(wallet.getAddress(), wallet.getAddress()).send().wait(); + + await gasTokenContract.methods.claim(fpcAddress, amount, secret).send().wait(); } diff --git a/yarn-project/cli/src/cmds/devnet/faucet.ts b/yarn-project/cli/src/cmds/devnet/faucet.ts index 291a9c97fc2..73e9d5c0642 100644 --- a/yarn-project/cli/src/cmds/devnet/faucet.ts +++ b/yarn-project/cli/src/cmds/devnet/faucet.ts @@ -1,15 +1,33 @@ import { type EthAddress } from '@aztec/circuits.js'; import { type LogFn } from '@aztec/foundation/log'; -export async function dripFaucet(faucetUrl: string, asset: string, account: EthAddress, log: LogFn): Promise { +import { prettyPrintJSON } from '../../utils/commands.js'; + +export async function dripFaucet( + faucetUrl: string, + asset: string, + account: EthAddress, + json: boolean, + log: LogFn, +): Promise { const url = new URL(`${faucetUrl}/drip/${account.toString()}`); url.searchParams.set('asset', asset); const res = await fetch(url); if (res.status === 200) { - log(`Dripped ${asset} for ${account.toString()}`); - } else if (res.status === 429) { - log(`Rate limited when dripping ${asset} for ${account.toString()}`); + if (json) { + log(prettyPrintJSON({ ok: true })); + } else { + log(`Dripped ${asset} for ${account.toString()}`); + } } else { - log(`Failed to drip ${asset} for ${account.toString()}`); + if (json) { + log(prettyPrintJSON({ ok: false })); + } else if (res.status === 429) { + log(`Rate limited when dripping ${asset} for ${account.toString()}`); + } else { + log(`Failed to drip ${asset} for ${account.toString()}`); + } + + process.exit(1); } } diff --git a/yarn-project/cli/src/cmds/devnet/index.ts b/yarn-project/cli/src/cmds/devnet/index.ts index 6562ec3bac3..3fdbe0121ec 100644 --- a/yarn-project/cli/src/cmds/devnet/index.ts +++ b/yarn-project/cli/src/cmds/devnet/index.ts @@ -23,8 +23,8 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: DebugL ) .option('--json', 'Output the result as JSON') .action(async options => { - const { bootstrapDevnet } = await import('./bootstrap_devnet.js'); - await bootstrapDevnet( + const { bootstrapNetwork } = await import('./bootstrap_network.js'); + await bootstrapNetwork( options[pxeOption.attributeName()], options.l1RpcUrl, options[l1ChainIdOption.attributeName()], @@ -42,9 +42,10 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: DebugL .requiredOption('-u, --faucet-url ', 'Url of the faucet', 'http://localhost:8082') .requiredOption('-t, --token ', 'The asset to drip', 'eth') .requiredOption('-a, --address ', 'The Ethereum address to drip to', parseEthereumAddress) + .option('--json', 'Output the result as JSON') .action(async options => { const { dripFaucet } = await import('./faucet.js'); - await dripFaucet(options.faucetUrl, options.token, options.address, log); + await dripFaucet(options.faucetUrl, options.token, options.address, options.json, log); }); return program; diff --git a/yarn-project/cli/src/cmds/l1/bridge_erc20.ts b/yarn-project/cli/src/cmds/l1/bridge_erc20.ts index a62c3e71439..c42d555cb10 100644 --- a/yarn-project/cli/src/cmds/l1/bridge_erc20.ts +++ b/yarn-project/cli/src/cmds/l1/bridge_erc20.ts @@ -3,6 +3,7 @@ import { createEthereumChain, createL1Clients } from '@aztec/ethereum'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { ERC20PortalManager } from '../../portal_manager.js'; +import { prettyPrintJSON } from '../../utils/commands.js'; export async function bridgeERC20( amount: bigint, @@ -14,6 +15,7 @@ export async function bridgeERC20( tokenAddress: EthAddress, portalAddress: EthAddress, mint: boolean, + json: boolean, log: LogFn, debugLogger: DebugLogger, ) { @@ -25,11 +27,20 @@ export async function bridgeERC20( const portal = await ERC20PortalManager.create(tokenAddress, portalAddress, publicClient, walletClient, debugLogger); const { secret } = await portal.prepareTokensOnL1(amount, amount, recipient, mint); - if (mint) { - log(`Minted ${amount} tokens on L1 and pushed to L2 portal`); + if (json) { + log( + prettyPrintJSON({ + claimAmount: amount, + claimSecret: secret, + }), + ); } else { - log(`Bridged ${amount} tokens to L2 portal`); + if (mint) { + log(`Minted ${amount} tokens on L1 and pushed to L2 portal`); + } else { + log(`Bridged ${amount} tokens to L2 portal`); + } + log(`claimAmount=${amount},claimSecret=${secret}\n`); + log(`Note: You need to wait for two L2 blocks before pulling them from the L2 side`); } - log(`claimAmount=${amount},claimSecret=${secret}\n`); - log(`Note: You need to wait for two L2 blocks before pulling them from the L2 side`); } diff --git a/yarn-project/cli/src/cmds/l1/bridge_fee_juice.ts b/yarn-project/cli/src/cmds/l1/bridge_fee_juice.ts index 47d72505217..010926fc7d7 100644 --- a/yarn-project/cli/src/cmds/l1/bridge_fee_juice.ts +++ b/yarn-project/cli/src/cmds/l1/bridge_fee_juice.ts @@ -4,6 +4,7 @@ import { createEthereumChain, createL1Clients } from '@aztec/ethereum'; import { type DebugLogger, type LogFn } from '@aztec/foundation/log'; import { FeeJuicePortalManager } from '../../portal_manager.js'; +import { prettyPrintJSON } from '../../utils/commands.js'; export async function bridgeL1Gas( amount: bigint, @@ -11,14 +12,16 @@ export async function bridgeL1Gas( rpcUrl: string, l1RpcUrl: string, chainId: number, + privateKey: string | undefined, mnemonic: string, mint: boolean, + json: boolean, log: LogFn, debugLogger: DebugLogger, ) { // Prepare L1 client const chain = createEthereumChain(l1RpcUrl, chainId); - const { publicClient, walletClient } = createL1Clients(chain.rpcUrl, mnemonic, chain.chainInfo); + const { publicClient, walletClient } = createL1Clients(chain.rpcUrl, privateKey ?? mnemonic, chain.chainInfo); // Prepare L2 client const client = await createCompatibleClient(rpcUrl, debugLogger); @@ -27,7 +30,19 @@ export async function bridgeL1Gas( const portal = await FeeJuicePortalManager.create(client, publicClient, walletClient, debugLogger); const { secret } = await portal.prepareTokensOnL1(amount, amount, recipient, mint); - log(`Minted ${amount} gas tokens on L1 and pushed to L2 portal`); - log(`claimAmount=${amount},claimSecret=${secret}\n`); - log(`Note: You need to wait for two L2 blocks before pulling them from the L2 side`); + if (json) { + const out = { + claimAmount: amount, + claimSecret: secret, + }; + log(prettyPrintJSON(out)); + } else { + if (mint) { + log(`Minted ${amount} fee juice on L1 and pushed to L2 portal`); + } else { + log(`Bridged ${amount} fee juice to L2 portal`); + } + log(`claimAmount=${amount},claimSecret=${secret}\n`); + log(`Note: You need to wait for two L2 blocks before pulling them from the L2 side`); + } } diff --git a/yarn-project/cli/src/cmds/l1/create_l1_account.ts b/yarn-project/cli/src/cmds/l1/create_l1_account.ts index 4e4b01fb5ed..81764ce5f02 100644 --- a/yarn-project/cli/src/cmds/l1/create_l1_account.ts +++ b/yarn-project/cli/src/cmds/l1/create_l1_account.ts @@ -2,10 +2,16 @@ import { type LogFn } from '@aztec/foundation/log'; import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'; -export function createL1Account(log: LogFn) { +import { prettyPrintJSON } from '../../utils/commands.js'; + +export function createL1Account(json: boolean, log: LogFn) { const privateKey = generatePrivateKey(); const account = privateKeyToAccount(privateKey); - log(`Private Key: ${privateKey}`); - log(`Address: ${account.address}`); + if (json) { + log(prettyPrintJSON({ privateKey, address: account.address })); + } else { + log(`Private Key: ${privateKey}`); + log(`Address: ${account.address}`); + } } 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 1ca93b6b312..43f75ae5cde 100644 --- a/yarn-project/cli/src/cmds/l1/get_l1_balance.ts +++ b/yarn-project/cli/src/cmds/l1/get_l1_balance.ts @@ -5,17 +5,37 @@ import { PortalERC20Abi } from '@aztec/l1-artifacts'; import { createPublicClient, getContract, http } from 'viem'; -export async function getL1Balance(who: EthAddress, token: EthAddress, l1RpcUrl: string, chainId: number, log: LogFn) { +import { prettyPrintJSON } from '../../utils/commands.js'; + +export async function getL1Balance( + who: EthAddress, + token: EthAddress | undefined, + l1RpcUrl: string, + chainId: number, + json: boolean, + log: LogFn, +) { const chain = createEthereumChain(l1RpcUrl, chainId); const publicClient = createPublicClient({ chain: chain.chainInfo, transport: http(chain.rpcUrl) }); - const gasL1 = getContract({ - address: token.toString(), - abi: PortalERC20Abi, - client: publicClient, - }); + let balance = 0n; + if (token) { + const gasL1 = getContract({ + address: token.toString(), + abi: PortalERC20Abi, + client: publicClient, + }); - const balance = await gasL1.read.balanceOf([who.toString()]); + balance = await gasL1.read.balanceOf([who.toString()]); + } else { + balance = await publicClient.getBalance({ + address: who.toString(), + }); + } - log(`L1 gas token balance of ${who.toString()} is ${balance.toString()}`); + if (json) { + log(prettyPrintJSON({ balance })); + } else { + log(`L1 balance of ${who.toString()} is ${balance.toString()}`); + } } diff --git a/yarn-project/cli/src/cmds/l1/index.ts b/yarn-project/cli/src/cmds/l1/index.ts index d199864714b..4607af02c20 100644 --- a/yarn-project/cli/src/cmds/l1/index.ts +++ b/yarn-project/cli/src/cmds/l1/index.ts @@ -101,8 +101,10 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: DebugL 'test test test test test test test test test test test junk', ) .option('--mint', 'Mint the tokens on L1', false) + .option('--l1-private-key ', 'The private key to the eth account bridging', PRIVATE_KEY) .addOption(pxeOption) .addOption(l1ChainIdOption) + .option('--json', 'Output the claim in JSON format') .action(async (amount, recipient, options) => { const { bridgeL1Gas } = await import('./bridge_fee_juice.js'); await bridgeL1Gas( @@ -111,8 +113,10 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: DebugL options.rpcUrl, options.l1RpcUrl, options.l1ChainId, + options.l1PrivateKey, options.mnemonic, options.mint, + options.json, log, debugLogger, ); @@ -137,7 +141,8 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: DebugL .addOption(l1ChainIdOption) .requiredOption('-t, --token ', 'The address of the token to bridge', parseEthereumAddress) .requiredOption('-p, --portal ', 'The address of the portal contract', parseEthereumAddress) - .option('-k, --private-key ', 'The private key to use for deployment', PRIVATE_KEY) + .option('--l1-private-key ', 'The private key to use for deployment', PRIVATE_KEY) + .option('--json', 'Output the claim in JSON format') .action(async (amount, recipient, options) => { const { bridgeERC20 } = await import('./bridge_erc20.js'); await bridgeERC20( @@ -145,20 +150,24 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: DebugL recipient, options.l1RpcUrl, options.l1ChainId, - options.privateKey, + options.l1PrivateKey, options.mnemonic, options.token, options.portal, options.mint, + options.json, log, debugLogger, ); }); - program.command('create-l1-account').action(async () => { - const { createL1Account } = await import('./create_l1_account.js'); - createL1Account(log); - }); + program + .command('create-l1-account') + .option('--json', 'Output the account in JSON format') + .action(async options => { + const { createL1Account } = await import('./create_l1_account.js'); + createL1Account(options.json, log); + }); program .command('get-l1-balance') @@ -169,11 +178,12 @@ export function injectCommands(program: Command, log: LogFn, debugLogger: DebugL 'Url of the ethereum host. Chain identifiers localhost and testnet can be used', ETHEREUM_HOST, ) - .requiredOption('-t, --token ', 'The address of the token to check the balance of', parseEthereumAddress) + .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, log); + await getL1Balance(who, options.token, options.l1RpcUrl, options.l1ChainId, options.json, log); }); return program; diff --git a/yarn-project/cli/src/utils/commands.ts b/yarn-project/cli/src/utils/commands.ts index b68916a755d..378bc2c3294 100644 --- a/yarn-project/cli/src/utils/commands.ts +++ b/yarn-project/cli/src/utils/commands.ts @@ -353,3 +353,24 @@ export function parseField(field: string): Fr { export function parseFields(fields: string[]): Fr[] { return fields.map(parseField); } + +/** + * Pretty prints an object as JSON + * @param data - The object to stringify + * @returns A JSON string + */ +export function prettyPrintJSON(data: Record): string { + return JSON.stringify( + data, + (_key, val) => { + if (typeof val === 'bigint') { + return String(val); + } else if (val && typeof val === 'object' && 'toBuffer' in val) { + return '0x' + val.toBuffer().toString('hex'); + } else { + return val; + } + }, + 2, + ); +} diff --git a/yarn-project/end-to-end/Earthfile b/yarn-project/end-to-end/Earthfile index d9dae7c94e1..4d91c75af46 100644 --- a/yarn-project/end-to-end/Earthfile +++ b/yarn-project/end-to-end/Earthfile @@ -250,3 +250,6 @@ bench-prover: ARG COMMIT_HASH DO +E2E_COMPOSE_TEST --test=bench_prover --debug="aztec:benchmarks:*,aztec:prover*,aztec:bb*,aztec:pxe*" --enable_gas=1 --compose_file=./scripts/docker-compose-no-sandbox.yml --hardware_concurrency=${HARDWARE_CONCURRENCY:-32} DO ../../+UPLOAD_LOGS --PULL_REQUEST=$PULL_REQUEST --BRANCH=$BRANCH --COMMIT_HASH=$COMMIT_HASH + +e2e-devnet-smoke: + DO +E2E_COMPOSE_TEST --test=devnet/e2e_smoke.test.ts --compose_file=scripts/docker-compose-devnet.yml diff --git a/yarn-project/end-to-end/scripts/docker-compose-devnet.yml b/yarn-project/end-to-end/scripts/docker-compose-devnet.yml new file mode 100644 index 00000000000..4f6efe441ad --- /dev/null +++ b/yarn-project/end-to-end/scripts/docker-compose-devnet.yml @@ -0,0 +1,29 @@ +version: '3' +services: + end-to-end: + image: aztecprotocol/end-to-end:${AZTEC_DOCKER_TAG:-latest} + secrets: + - ethereum-host + - aztec-node-url + - faucet-url + environment: + DEBUG: ${DEBUG:-'aztec:*'} + DEBUG_COLORS: 1 + ETHEREUM_HOST: + JOB_NAME: ${JOB_NAME:-''} + PXE_PROVER_ENABLED: ${PXE_PROVER_ENABLED:-1} + command: | + export ETHEREUM_HOST=$$(cat /var/run/secrets/ethereum-host) + 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} + volumes: + - ../log:/usr/src/yarn-project/end-to-end/log:rw + +secrets: + aztec-node-url: + environment: AZTEC_NODE_URL + ethereum-host: + environment: ETHEREUM_HOST + faucet-url: + environment: FAUCET_URL 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 bf4bd673a8d..87c5573176d 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 @@ -140,14 +140,17 @@ describe('L1Publisher integration', () => { await worldStateSynchronizer.start(); builder = await TxProver.new(config, worldStateSynchronizer, blockSource, new NoopTelemetryClient()); - publisher = getL1Publisher({ - rpcUrl: config.rpcUrl, - requiredConfirmations: 1, - l1Contracts: l1ContractAddresses, - publisherPrivateKey: sequencerPK, - l1PublishRetryIntervalMS: 100, - l1ChainId: 31337, - }); + publisher = getL1Publisher( + { + rpcUrl: config.rpcUrl, + requiredConfirmations: 1, + l1Contracts: l1ContractAddresses, + publisherPrivateKey: sequencerPK, + l1PublishRetryIntervalMS: 100, + l1ChainId: 31337, + }, + new NoopTelemetryClient(), + ); coinbase = config.coinbase || EthAddress.random(); feeRecipient = config.feeRecipient || AztecAddress.random(); 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 new file mode 100644 index 00000000000..a0f8cfba405 --- /dev/null +++ b/yarn-project/end-to-end/src/devnet/e2e_smoke.test.ts @@ -0,0 +1,308 @@ +import { getSchnorrAccount } from '@aztec/accounts/schnorr'; +import { + type EthAddress, + Fr, + NativeFeePaymentMethodWithClaim, + type PXE, + SignerlessWallet, + TxStatus, + type WaitOpts, + createAztecNodeClient, + createPXEClient, + fileURLToPath, + retryUntil, +} from '@aztec/aztec.js'; +import { DefaultMultiCallEntrypoint } from '@aztec/aztec.js/entrypoint'; +import { GasSettings, deriveSigningKey } from '@aztec/circuits.js'; +import { startHttpRpcServer } from '@aztec/foundation/json-rpc/server'; +import { type DebugLogger } from '@aztec/foundation/log'; +import { promiseWithResolvers } from '@aztec/foundation/promise'; +import { GasTokenContract, TestContract } from '@aztec/noir-contracts.js'; +import { createPXERpcServer } from '@aztec/pxe'; + +import getPort from 'get-port'; +import { exec } from 'node:child_process'; +import { lookup } from 'node:dns/promises'; +import { tmpdir } from 'node:os'; +import { resolve } from 'node:path'; + +import { getACVMConfig } from '../fixtures/get_acvm_config.js'; +import { getBBConfig } from '../fixtures/get_bb_config.js'; +import { getLogger, setupPXEService } from '../fixtures/utils.js'; + +const { + AZTEC_NODE_URL, + PXE_URL, + FAUCET_URL, + AZTEC_CLI = `node ${resolve(fileURLToPath(import.meta.url), '../../../../aztec/dest/bin/index.js')}`, + ETHEREUM_HOST, + PXE_PROVER_ENABLED = '0', + USE_EMPTY_BLOCKS = '0', +} = process.env; + +const waitOpts: WaitOpts = { timeout: 3600, interval: 1 }; + +const MIN_BLOCKS_FOR_BRIDGING = 2; + +/** + * If we can successfully resolve 'host.docker.internal', then we are running in a container, and we should treat + * localhost as being host.docker.internal. + */ +export const getLocalhost = () => + lookup('host.docker.internal') + .then(() => 'host.docker.internal') + .catch(() => 'localhost'); + +describe('End-to-end tests for devnet', () => { + // eslint-disable-next-line + let pxe: PXE; + let pxeUrl: string; // needed for the CLI + let logger: DebugLogger; + let l1ChainId: number; + let feeJuiceL1: EthAddress; + let teardown: () => void | Promise; + + beforeAll(async () => { + logger = getLogger(); + + if (!ETHEREUM_HOST) { + throw new Error('ETHEREUM_HOST must be set'); + } + + if (!AZTEC_CLI) { + throw new Error('AZTEC_CLI must be set'); + } + + if (!FAUCET_URL) { + throw new Error('FAUCET_URL must be set'); + } + + logger.info(`Using AZTEC_CLI: ${AZTEC_CLI}`); + + if (AZTEC_NODE_URL) { + logger.info(`Using AZTEC_NODE_URL: ${AZTEC_NODE_URL}`); + const node = createAztecNodeClient(AZTEC_NODE_URL); + const bbConfig = await getBBConfig(logger); + const acvmConfig = await getACVMConfig(logger); + const svc = await setupPXEService(node, { + ...bbConfig, + ...acvmConfig, + proverEnabled: ['1', 'true'].includes(PXE_PROVER_ENABLED!), + }); + pxe = svc.pxe; + + const nodeInfo = await pxe.getNodeInfo(); + const pxeInfo = await pxe.getPXEInfo(); + + expect(nodeInfo.protocolContractAddresses.classRegisterer).toEqual( + pxeInfo.protocolContractAddresses.classRegisterer, + ); + expect(nodeInfo.protocolContractAddresses.instanceDeployer).toEqual( + pxeInfo.protocolContractAddresses.instanceDeployer, + ); + expect(nodeInfo.protocolContractAddresses.gasToken).toEqual(pxeInfo.protocolContractAddresses.gasToken); + expect(nodeInfo.protocolContractAddresses.keyRegistry).toEqual(pxeInfo.protocolContractAddresses.keyRegistry); + expect(nodeInfo.protocolContractAddresses.multiCallEntrypoint).toEqual( + pxeInfo.protocolContractAddresses.multiCallEntrypoint, + ); + + const port = await getPort(); + const localhost = await getLocalhost(); + pxeUrl = `http://${localhost}:${port}`; + // start a server for the CLI to talk to + const server = startHttpRpcServer('pxe', pxe, createPXERpcServer, port); + + teardown = async () => { + const { promise, resolve, reject } = promiseWithResolvers(); + server.close(e => (e ? reject(e) : resolve())); + await promise; + + await svc.teardown(); + await bbConfig?.cleanup(); + await acvmConfig?.cleanup(); + }; + } else if (PXE_URL) { + logger.info(`Using PXE_URL: ${PXE_URL}`); + pxe = createPXEClient(PXE_URL); + pxeUrl = PXE_URL; + teardown = () => {}; + } else { + throw new Error('AZTEC_NODE_URL or PXE_URL must be set'); + } + + ({ + l1ChainId, + l1ContractAddresses: { gasTokenAddress: feeJuiceL1 }, + } = await pxe.getNodeInfo()); + logger.info(`PXE instance started`); + }); + + afterAll(async () => { + await teardown(); + }); + + it('deploys an account while paying with FeeJuice', async () => { + const privateKey = Fr.random(); + const l1Account = await cli<{ privateKey: string; address: string }>('create-l1-account'); + const l2Account = getSchnorrAccount(pxe, privateKey, deriveSigningKey(privateKey), Fr.ZERO); + + await expect(getL1Balance(l1Account.address)).resolves.toEqual(0n); + await expect(getL1Balance(l1Account.address, feeJuiceL1)).resolves.toEqual(0n); + + await cli('drip-faucet', { 'faucet-url': FAUCET_URL!, token: 'eth', address: l1Account.address }); + await expect(getL1Balance(l1Account.address)).resolves.toBeGreaterThan(0n); + + await cli('drip-faucet', { 'faucet-url': FAUCET_URL!, token: 'fee_juice', address: l1Account.address }); + await expect(getL1Balance(l1Account.address, feeJuiceL1)).resolves.toBeGreaterThan(0n); + + const amount = 1_000_000_000_000n; + const { claimAmount, claimSecret } = await cli<{ claimAmount: string; claimSecret: { value: string } }>( + 'bridge-fee-juice', + [amount, l2Account.getAddress()], + { + 'l1-rpc-url': ETHEREUM_HOST!, + 'l1-chain-id': l1ChainId.toString(), + 'l1-private-key': l1Account.privateKey, + 'rpc-url': pxeUrl, + mint: true, + }, + ); + + if (['1', 'true', 'yes'].includes(USE_EMPTY_BLOCKS)) { + await advanceChainWithEmptyBlocks(pxe); + } else { + await waitForL1MessageToArrive(); + } + + const txReceipt = await l2Account + .deploy({ + fee: { + gasSettings: GasSettings.default(), + paymentMethod: new NativeFeePaymentMethodWithClaim( + l2Account.getAddress(), + BigInt(claimAmount), + Fr.fromString(claimSecret.value), + ), + }, + }) + .wait(waitOpts); + + // disabled because the CLI process doesn't exit + // const { txHash, address } = await cli<{ txHash: string; address: { value: string } }>('create-account', { + // 'private-key': privateKey, + // payment: `method=native,claimSecret=${claimSecret.value},claimAmount=${claimAmount}`, + // wait: false, + // }); + // expect(address).toEqual(l2Account.getAddress().toString()); + // const txReceipt = await retryUntil( + // async () => { + // const receipt = await pxe.getTxReceipt(txHash); + // if (receipt.status === TxStatus.PENDING) { + // return undefined; + // } + // return receipt; + // }, + // 'wait_for_l2_account', + // waitOpts.timeout, + // waitOpts.interval, + // ); + + expect(txReceipt.status).toBe(TxStatus.SUCCESS); + const feeJuice = await GasTokenContract.at( + ( + await pxe.getNodeInfo() + ).protocolContractAddresses.gasToken, + await l2Account.getWallet(), + ); + const balance = await feeJuice.methods.balance_of_public(l2Account.getAddress()).simulate(); + expect(balance).toEqual(amount - txReceipt.transactionFee!); + }); + + type OptionValue = null | undefined | boolean | { toString(): string }; + type ArgumentValue = { toString(): string }; + + function cli(cliCommand: string): Promise; + function cli(cliCommand: string, args: ArgumentValue[]): Promise; + function cli(cliCommand: string, opts: Record): Promise; + function cli(cliCommand: string, args: ArgumentValue[], opts: Record): Promise; + function cli( + cliCommand: string, + _args?: ArgumentValue[] | Record, + _opts?: Record, + ): Promise { + const { promise, resolve, reject } = promiseWithResolvers(); + const args = Array.isArray(_args) ? _args : []; + const opts = _args && !Array.isArray(_args) ? _args : typeof _opts !== 'undefined' ? _opts : {}; + + const cmdArguments = args.map(x => x.toString()); + + opts.json = true; + const cmdOptions = Object.entries(opts) + .filter((entry): entry is [string, { toString(): string }] => entry[1] !== undefined && entry[1] !== null) + .map(([key, value]) => + typeof value === 'boolean' ? (value ? `--${key}` : `--no-${key}`) : `--${key} ${value.toString()}`, + ); + + const cmd = `${AZTEC_CLI} ${cliCommand} ${cmdArguments.join(' ')} ${cmdOptions.join(' ')}`; + logger.info(`Running aztec-cli: ${cmd}`); + const child = exec(cmd, { + cwd: tmpdir(), + env: { + NODE_OPTIONS: '--no-warnings', + }, + }); + + let err = ''; + child.stderr?.on('data', data => { + logger.error(data.toString()); + err += data.toString(); + }); + + let out = ''; + child.stdout?.on('data', data => { + out += data.toString(); + }); + + child.on('error', reject); + child.on('close', (code, signal) => { + if (code === 0) { + const res = JSON.parse(out); + logger.info(`aztec-cli JSON output: ${JSON.stringify(res)}`); + resolve(res); + } else { + reject(new Error(`aztec-cli ${cliCommand} non-zero exit: code=${code} signal=${signal} ${err}`)); + } + }); + + return promise; + } + + async function getL1Balance(address: string, token?: EthAddress): Promise { + const { balance } = await cli<{ balance: string }>('get-l1-balance', [address], { + 'l1-rpc-url': ETHEREUM_HOST!, + 'l1-chain-id': l1ChainId.toString(), + token, + }); + + return BigInt(balance); + } + + async function waitForL1MessageToArrive() { + const targetBlockNumber = (await pxe.getBlockNumber()) + MIN_BLOCKS_FOR_BRIDGING; + await retryUntil(async () => (await pxe.getBlockNumber()) >= targetBlockNumber, 'wait_for_l1_message', 0, 10); + } + + async function advanceChainWithEmptyBlocks(pxe: PXE) { + const { l1ChainId, protocolVersion } = await pxe.getNodeInfo(); + const test = await TestContract.deploy( + new SignerlessWallet(pxe, new DefaultMultiCallEntrypoint(l1ChainId, protocolVersion)), + ) + .send({ universalDeploy: true, skipClassRegistration: true }) + .deployed(); + + // start at 1 because deploying the contract has already mined a block + for (let i = 1; i < MIN_BLOCKS_FOR_BRIDGING; i++) { + await test.methods.get_this_address().send().wait(waitOpts); + } + } +}); diff --git a/yarn-project/ethereum/src/l1_contract_addresses.ts b/yarn-project/ethereum/src/l1_contract_addresses.ts index 9c98ee87bb9..ec2f1c0be40 100644 --- a/yarn-project/ethereum/src/l1_contract_addresses.ts +++ b/yarn-project/ethereum/src/l1_contract_addresses.ts @@ -1,4 +1,5 @@ import { EthAddress } from '@aztec/foundation/eth-address'; +import type { DebugLogger } from '@aztec/foundation/log'; /** * The names of the current L1 contract addresses. @@ -47,3 +48,32 @@ export function getL1ContractAddressesFromEnv() { : EthAddress.ZERO, }; } + +function convertToL1ContractAddresses(obj: any): L1ContractAddresses { + if (typeof obj !== 'object' || obj === null) { + throw new Error('Object is not valid'); + } + + const result: Partial = {}; + + for (const key of l1ContractsNames) { + const value = obj[key]; + result[key] = EthAddress.fromString(value); + } + + return result as L1ContractAddresses; +} + +export async function getL1ContractAddressesFromUrl(url: string, log: DebugLogger): Promise { + try { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`HTTP error when fetching L1 contracts from ${url}. Status: ${response.status}`); + } + const data = await response.json(); + return convertToL1ContractAddresses(data); + } catch (error) { + log.error(`Error fetching JSON from ${url}:`, error); + throw error; + } +} diff --git a/yarn-project/foundation/src/fields/fields.ts b/yarn-project/foundation/src/fields/fields.ts index 6b7b8001242..33050507e66 100644 --- a/yarn-project/foundation/src/fields/fields.ts +++ b/yarn-project/foundation/src/fields/fields.ts @@ -433,7 +433,7 @@ export function reduceFn(fn: (input: TInput) = } /** If we are in test mode, we register a special equality for fields. */ -if (process.env.NODE_ENV === 'test') { +if (process.env.NODE_ENV === 'test' && typeof expect !== 'undefined') { const areFieldsEqual = (a: unknown, b: unknown): boolean | undefined => { const isAField = a instanceof BaseField; const isBField = b instanceof BaseField; diff --git a/yarn-project/p2p/src/tx_pool/aztec_kv_tx_pool.ts b/yarn-project/p2p/src/tx_pool/aztec_kv_tx_pool.ts index 16fa4be870f..043869d3121 100644 --- a/yarn-project/p2p/src/tx_pool/aztec_kv_tx_pool.ts +++ b/yarn-project/p2p/src/tx_pool/aztec_kv_tx_pool.ts @@ -47,6 +47,8 @@ export class AztecKVTxPool implements TxPool { void this.#minedTxs.add(key); void this.#pendingTxs.delete(key); } + this.#metrics.recordRemovedTxs('pending', txHashes.length); + this.#metrics.recordAddedTxs('mined', txHashes.length); }); } @@ -87,6 +89,7 @@ export class AztecKVTxPool implements TxPool { public addTxs(txs: Tx[]): Promise { const txHashes = txs.map(tx => tx.getTxHash()); return this.#store.transaction(() => { + let pendingCount = 0; for (const [i, tx] of txs.entries()) { const txHash = txHashes[i]; this.#log.info(`Adding tx with id ${txHash.toString()}`, { @@ -97,12 +100,14 @@ export class AztecKVTxPool implements TxPool { const key = txHash.toString(); void this.#txs.set(key, tx.toBuffer()); if (!this.#minedTxs.has(key)) { + pendingCount++; // REFACTOR: Use an lmdb conditional write to avoid race conditions with this write tx void this.#pendingTxs.add(key); + this.#metrics.recordTxSize(tx); } } - this.#metrics.recordTxs(txs); + this.#metrics.recordAddedTxs('pending', pendingCount); }); } @@ -113,14 +118,24 @@ export class AztecKVTxPool implements TxPool { */ public deleteTxs(txHashes: TxHash[]): Promise { return this.#store.transaction(() => { + let pendingDeleted = 0; + let minedDeleted = 0; for (const hash of txHashes) { const key = hash.toString(); void this.#txs.delete(key); - void this.#pendingTxs.delete(key); - void this.#minedTxs.delete(key); + if (this.#pendingTxs.has(key)) { + pendingDeleted++; + void this.#pendingTxs.delete(key); + } + + if (this.#minedTxs.has(key)) { + minedDeleted++; + void this.#minedTxs.delete(key); + } } - this.#metrics.removeTxs(txHashes.length); + this.#metrics.recordRemovedTxs('pending', pendingDeleted); + this.#metrics.recordRemovedTxs('mined', minedDeleted); }); } diff --git a/yarn-project/p2p/src/tx_pool/instrumentation.ts b/yarn-project/p2p/src/tx_pool/instrumentation.ts index 099afe22522..941a2f46168 100644 --- a/yarn-project/p2p/src/tx_pool/instrumentation.ts +++ b/yarn-project/p2p/src/tx_pool/instrumentation.ts @@ -1,5 +1,7 @@ import { type Tx } from '@aztec/circuit-types'; -import { type Histogram, Metrics, type TelemetryClient, type UpDownCounter } from '@aztec/telemetry-client'; +import { Attributes, type Histogram, Metrics, type TelemetryClient, type UpDownCounter } from '@aztec/telemetry-client'; + +export type TxStatus = 'pending' | 'mined'; /** * Instrumentation class for the TxPool. @@ -33,26 +35,39 @@ export class TxPoolInstrumentation { }); } + public recordTxSize(tx: Tx) { + this.txSize.record(tx.getSize()); + } + /** * Updates the metrics with the new transactions. * @param txs - The transactions to record */ - public recordTxs(txs: Tx[]) { - for (const tx of txs) { - this.txSize.record(tx.getSize()); + public recordAddedTxs(status: string, count = 1) { + if (count < 0) { + throw new Error('Count must be positive'); } - - this.txInMempool.add(txs.length); + if (count === 0) { + return; + } + this.txInMempool.add(count, { + [Attributes.STATUS]: status, + }); } /** * Updates the metrics by removing transactions from the mempool. * @param count - The number of transactions to remove from the mempool */ - public removeTxs(count = 1) { + public recordRemovedTxs(status: string, count = 1) { if (count < 0) { throw new Error('Count must be positive'); } - this.txInMempool.add(-1 * count); + if (count === 0) { + return; + } + this.txInMempool.add(-1 * count, { + [Attributes.STATUS]: status, + }); } } diff --git a/yarn-project/p2p/src/tx_pool/memory_tx_pool.ts b/yarn-project/p2p/src/tx_pool/memory_tx_pool.ts index 30d3a23b31b..6fcf2c263c6 100644 --- a/yarn-project/p2p/src/tx_pool/memory_tx_pool.ts +++ b/yarn-project/p2p/src/tx_pool/memory_tx_pool.ts @@ -36,6 +36,8 @@ export class InMemoryTxPool implements TxPool { this.minedTxs.add(key); this.pendingTxs.delete(key); } + this.metrics.recordRemovedTxs('pending', txHashes.length); + this.metrics.recordAddedTxs('mined', txHashes.length); return Promise.resolve(); } @@ -74,7 +76,7 @@ export class InMemoryTxPool implements TxPool { * @returns Empty promise. */ public addTxs(txs: Tx[]): Promise { - this.metrics.recordTxs(txs); + let pending = 0; for (const tx of txs) { const txHash = tx.getTxHash(); this.log.debug(`Adding tx with id ${txHash.toString()}`, { @@ -85,9 +87,13 @@ export class InMemoryTxPool implements TxPool { const key = txHash.toBigInt(); this.txs.set(key, tx); if (!this.minedTxs.has(key)) { + pending++; + this.metrics.recordTxSize(tx); this.pendingTxs.add(key); } } + + this.metrics.recordAddedTxs('pending', pending); return Promise.resolve(); } @@ -97,13 +103,19 @@ export class InMemoryTxPool implements TxPool { * @returns The number of transactions that was deleted from the pool. */ public deleteTxs(txHashes: TxHash[]): Promise { - this.metrics.removeTxs(txHashes.length); + let deletedMined = 0; + let deletedPending = 0; + for (const txHash of txHashes) { const key = txHash.toBigInt(); this.txs.delete(key); - this.pendingTxs.delete(key); - this.minedTxs.delete(key); + deletedPending += this.pendingTxs.delete(key) ? 1 : 0; + deletedMined += this.minedTxs.delete(key) ? 1 : 0; } + + this.metrics.recordRemovedTxs('pending', deletedPending); + this.metrics.recordRemovedTxs('mined', deletedMined); + return Promise.resolve(); } diff --git a/yarn-project/prover-client/src/mocks/test_context.ts b/yarn-project/prover-client/src/mocks/test_context.ts index dbe28edd870..99f7738f049 100644 --- a/yarn-project/prover-client/src/mocks/test_context.ts +++ b/yarn-project/prover-client/src/mocks/test_context.ts @@ -122,7 +122,7 @@ export class TestContext { localProver = await createProver(bbConfig); } - const queue = new MemoryProvingQueue(); + const queue = new MemoryProvingQueue(telemetry); const orchestrator = new ProvingOrchestrator(actualDb, queue, telemetry); const agent = new ProverAgent(localProver, proverCount); diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator.ts b/yarn-project/prover-client/src/orchestrator/orchestrator.ts index e3ad5b357af..be0f21a8460 100644 --- a/yarn-project/prover-client/src/orchestrator/orchestrator.ts +++ b/yarn-project/prover-client/src/orchestrator/orchestrator.ts @@ -37,6 +37,7 @@ import { type NESTED_RECURSIVE_PROOF_LENGTH, NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP, NUM_BASE_PARITY_PER_ROOT_PARITY, + PrivateKernelEmptyInputData, type Proof, type PublicKernelCircuitPublicInputs, type RECURSIVE_PROOF_LENGTH, @@ -55,6 +56,7 @@ import { createDebugLogger } from '@aztec/foundation/log'; import { promiseWithResolvers } from '@aztec/foundation/promise'; import { BufferReader, type Tuple } from '@aztec/foundation/serialize'; import { pushTestData } from '@aztec/foundation/testing'; +import { elapsed } from '@aztec/foundation/timer'; import { ProtocolCircuitVks, getVKIndex, getVKSiblingPath, getVKTreeRoot } from '@aztec/noir-protocol-circuits-types'; import { Attributes, type TelemetryClient, type Tracer, trackSpan, wrapCallbackInSpan } from '@aztec/telemetry-client'; import { type MerkleTreeOperations } from '@aztec/world-state'; @@ -71,6 +73,7 @@ import { validateRootOutput, validateTx, } from './block-building-helpers.js'; +import { ProvingOrchestratorMetrics } from './orchestrator_metrics.js'; import { type MergeRollupInputData, ProvingState, type TreeSnapshots } from './proving-state.js'; import { TX_PROVING_CODE, TxProvingState } from './tx-proving-state.js'; @@ -95,7 +98,7 @@ export class ProvingOrchestrator { private pendingProvingJobs: AbortController[] = []; private paddingTx: PaddingProcessedTx | undefined = undefined; - public readonly tracer: Tracer; + private metrics: ProvingOrchestratorMetrics; constructor( private db: MerkleTreeOperations, @@ -103,7 +106,11 @@ export class ProvingOrchestrator { telemetryClient: TelemetryClient, public readonly proverId: Fr = Fr.ZERO, ) { - this.tracer = telemetryClient.getTracer('ProvingOrchestrator'); + this.metrics = new ProvingOrchestratorMetrics(telemetryClient, 'ProvingOrchestrator'); + } + + get tracer(): Tracer { + return this.metrics.tracer; } /** @@ -306,15 +313,15 @@ export class ProvingOrchestrator { }, signal => this.prover.getEmptyPrivateKernelProof( - { + new PrivateKernelEmptyInputData( + unprovenPaddingTx.data.constants.historicalHeader, // Chain id and version should not change even if the proving state does, so it's safe to use them for the padding tx // which gets cached across multiple runs of the orchestrator with different proving states. If they were to change, // we'd have to clear out the paddingTx here and regenerate it when they do. - chainId: unprovenPaddingTx.data.constants.txContext.chainId, - version: unprovenPaddingTx.data.constants.txContext.version, - header: unprovenPaddingTx.data.constants.historicalHeader, - vkTreeRoot: getVKTreeRoot(), - }, + unprovenPaddingTx.data.constants.txContext.chainId, + unprovenPaddingTx.data.constants.txContext.version, + getVKTreeRoot(), + ), signal, ), ), @@ -557,21 +564,30 @@ export class ProvingOrchestrator { } const getBaseInputsEmptyTx = async () => { - const inputs = { - header: this.db.getInitialHeader(), - chainId: tx.data.constants.globalVariables.chainId, - version: tx.data.constants.globalVariables.version, - vkTreeRoot: tx.data.constants.vkTreeRoot, - }; + const inputs = new PrivateKernelEmptyInputData( + this.db.getInitialHeader(), + tx.data.constants.globalVariables.chainId, + tx.data.constants.globalVariables.version, + tx.data.constants.vkTreeRoot, + ); const proof = await this.prover.getEmptyTubeProof(inputs); - return await buildBaseRollupInput(tx, proof.proof, provingState.globalVariables, this.db, proof.verificationKey); + return await elapsed( + buildBaseRollupInput(tx, proof.proof, provingState.globalVariables, this.db, proof.verificationKey), + ); }; const getBaseInputsNonEmptyTx = async () => { const proof = await this.prover.getTubeProof(new TubeInputs(tx.clientIvcProof)); - return await buildBaseRollupInput(tx, proof.tubeProof, provingState.globalVariables, this.db, proof.tubeVK); + return await elapsed( + buildBaseRollupInput(tx, proof.tubeProof, provingState.globalVariables, this.db, proof.tubeVK), + ); }; - const inputs = tx.isEmpty ? await getBaseInputsEmptyTx() : await getBaseInputsNonEmptyTx(); + const [ms, inputs] = tx.isEmpty ? await getBaseInputsEmptyTx() : await getBaseInputsNonEmptyTx(); + + if (!tx.isEmpty) { + this.metrics.recordBaseRollupInputs(ms); + } + const promises = [MerkleTreeId.NOTE_HASH_TREE, MerkleTreeId.NULLIFIER_TREE, MerkleTreeId.PUBLIC_DATA_TREE].map( async (id: MerkleTreeId) => { return { key: id, value: await getTreeSnapshot(id, this.db) }; diff --git a/yarn-project/prover-client/src/orchestrator/orchestrator_metrics.ts b/yarn-project/prover-client/src/orchestrator/orchestrator_metrics.ts new file mode 100644 index 00000000000..08811291d82 --- /dev/null +++ b/yarn-project/prover-client/src/orchestrator/orchestrator_metrics.ts @@ -0,0 +1,32 @@ +import { + type Histogram, + Metrics, + type TelemetryClient, + type Tracer, + ValueType, + millisecondBuckets, +} from '@aztec/telemetry-client'; + +export class ProvingOrchestratorMetrics { + public readonly tracer: Tracer; + + private baseRollupInputsDuration: Histogram; + + constructor(client: TelemetryClient, name = 'ProvingOrchestrator') { + this.tracer = client.getTracer(name); + const meter = client.getMeter(name); + + this.baseRollupInputsDuration = meter.createHistogram(Metrics.PROVING_ORCHESTRATOR_BASE_ROLLUP_INPUTS_DURATION, { + unit: 'ms', + description: 'Duration to build base rollup inputs', + valueType: ValueType.INT, + advice: { + explicitBucketBoundaries: millisecondBuckets(1), // 10ms -> ~327s + }, + }); + } + + recordBaseRollupInputs(durationMs: number) { + this.baseRollupInputsDuration.record(Math.ceil(durationMs)); + } +} diff --git a/yarn-project/prover-client/src/prover-agent/agent-queue-integration.test.ts b/yarn-project/prover-client/src/prover-agent/agent-queue-integration.test.ts index ca8b80573c4..381d0b8a018 100644 --- a/yarn-project/prover-client/src/prover-agent/agent-queue-integration.test.ts +++ b/yarn-project/prover-client/src/prover-agent/agent-queue-integration.test.ts @@ -4,6 +4,7 @@ import { makeBaseParityInputs, makeRootParityInput } from '@aztec/circuits.js/te import { AbortError } from '@aztec/foundation/error'; import { promiseWithResolvers } from '@aztec/foundation/promise'; import { sleep } from '@aztec/foundation/sleep'; +import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; import { type MockProxy, mock } from 'jest-mock-extended'; @@ -23,7 +24,7 @@ describe('Prover agent <-> queue integration', () => { queueJobTimeout = 100; queuePollInterval = 10; - queue = new MemoryProvingQueue(queueJobTimeout, queuePollInterval); + queue = new MemoryProvingQueue(new NoopTelemetryClient(), queueJobTimeout, queuePollInterval); agentPollInterval = 10; agent = new ProverAgent(prover, 1, agentPollInterval); diff --git a/yarn-project/prover-client/src/prover-agent/memory-proving-queue.test.ts b/yarn-project/prover-client/src/prover-agent/memory-proving-queue.test.ts index cde99db00b5..41eb09a9e54 100644 --- a/yarn-project/prover-client/src/prover-agent/memory-proving-queue.test.ts +++ b/yarn-project/prover-client/src/prover-agent/memory-proving-queue.test.ts @@ -11,6 +11,7 @@ import { makeBaseParityInputs, makeBaseRollupInputs, makeParityPublicInputs } fr import { makeTuple } from '@aztec/foundation/array'; import { AbortError } from '@aztec/foundation/error'; import { sleep } from '@aztec/foundation/sleep'; +import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; import { MemoryProvingQueue } from './memory-proving-queue.js'; @@ -22,7 +23,7 @@ describe('MemoryProvingQueue', () => { beforeEach(() => { jobTimeoutMs = 100; pollingIntervalMs = 10; - queue = new MemoryProvingQueue(jobTimeoutMs, pollingIntervalMs); + queue = new MemoryProvingQueue(new NoopTelemetryClient(), jobTimeoutMs, pollingIntervalMs); queue.start(); }); diff --git a/yarn-project/prover-client/src/prover-agent/memory-proving-queue.ts b/yarn-project/prover-client/src/prover-agent/memory-proving-queue.ts index dcba5452acd..6362a0546c3 100644 --- a/yarn-project/prover-client/src/prover-agent/memory-proving-queue.ts +++ b/yarn-project/prover-client/src/prover-agent/memory-proving-queue.ts @@ -34,6 +34,10 @@ import { AbortError, TimeoutError } from '@aztec/foundation/error'; import { MemoryFifo } from '@aztec/foundation/fifo'; import { createDebugLogger } from '@aztec/foundation/log'; import { type PromiseWithResolvers, RunningPromise, promiseWithResolvers } from '@aztec/foundation/promise'; +import { serializeToBuffer } from '@aztec/foundation/serialize'; +import { type TelemetryClient } from '@aztec/telemetry-client'; + +import { ProvingQueueMetrics } from './queue_metrics.js'; type ProvingJobWithResolvers = { id: string; @@ -59,7 +63,10 @@ export class MemoryProvingQueue implements ServerCircuitProver, ProvingJobSource private runningPromise: RunningPromise; + private metrics: ProvingQueueMetrics; + constructor( + client: TelemetryClient, /** Timeout the job if an agent doesn't report back in this time */ private jobTimeoutMs = 60 * 1000, /** How often to check for timed out jobs */ @@ -67,6 +74,7 @@ export class MemoryProvingQueue implements ServerCircuitProver, ProvingJobSource private generateId = defaultIdGenerator, private timeSource = defaultTimeSource, ) { + this.metrics = new ProvingQueueMetrics(client, 'MemoryProvingQueue'); this.runningPromise = new RunningPromise(this.poll, pollingIntervalMs); } @@ -190,6 +198,7 @@ export class MemoryProvingQueue implements ServerCircuitProver, ProvingJobSource private poll = () => { const now = this.timeSource(); + this.metrics.recordQueueSize(this.queue.length()); for (const job of this.jobsInProgress.values()) { if (job.signal?.aborted) { @@ -239,6 +248,9 @@ export class MemoryProvingQueue implements ServerCircuitProver, ProvingJobSource throw new Error(); } + const byteSize = serializeToBuffer(item.request.inputs).length; + this.metrics.recordNewJob(item.request.type, byteSize); + return promise; } diff --git a/yarn-project/prover-client/src/prover-agent/prover-agent.test.ts b/yarn-project/prover-client/src/prover-agent/prover-agent.test.ts index 8ebbabe34df..b892dff31d5 100644 --- a/yarn-project/prover-client/src/prover-agent/prover-agent.test.ts +++ b/yarn-project/prover-client/src/prover-agent/prover-agent.test.ts @@ -9,6 +9,7 @@ import { } from '@aztec/circuits.js'; import { makeBaseParityInputs, makeParityPublicInputs } from '@aztec/circuits.js/testing'; import { makeTuple } from '@aztec/foundation/array'; +import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; import { type MockProxy, mock } from 'jest-mock-extended'; @@ -22,7 +23,7 @@ describe('ProverAgent', () => { beforeEach(() => { prover = mock(); - queue = new MemoryProvingQueue(); + queue = new MemoryProvingQueue(new NoopTelemetryClient()); agent = new ProverAgent(prover); }); diff --git a/yarn-project/prover-client/src/prover-agent/queue_metrics.ts b/yarn-project/prover-client/src/prover-agent/queue_metrics.ts new file mode 100644 index 00000000000..542ea20b321 --- /dev/null +++ b/yarn-project/prover-client/src/prover-agent/queue_metrics.ts @@ -0,0 +1,29 @@ +import { ProvingRequestType } from '@aztec/circuit-types'; +import { Attributes, type Gauge, type Histogram, Metrics, type TelemetryClient } from '@aztec/telemetry-client'; + +export class ProvingQueueMetrics { + private jobSize: Histogram; + private queueSize: Gauge; + + constructor(client: TelemetryClient, name = 'ProvingQueueMetrics') { + const meter = client.getMeter(name); + this.jobSize = meter.createHistogram(Metrics.PROVING_QUEUE_JOB_SIZE, { + description: 'Size of proving job', + unit: 'by', + }); + + this.queueSize = meter.createGauge(Metrics.PROVING_QUEUE_SIZE, { + description: 'Size of proving queue', + }); + } + + recordNewJob(type: ProvingRequestType, size: number) { + this.jobSize.record(size, { + [Attributes.PROVING_JOB_TYPE]: ProvingRequestType[type], + }); + } + + recordQueueSize(size: number) { + this.queueSize.record(size); + } +} diff --git a/yarn-project/prover-client/src/prover-agent/rpc.ts b/yarn-project/prover-client/src/prover-agent/rpc.ts index 06c51f5e93d..d68b3718040 100644 --- a/yarn-project/prover-client/src/prover-agent/rpc.ts +++ b/yarn-project/prover-client/src/prover-agent/rpc.ts @@ -9,6 +9,7 @@ import { KernelCircuitPublicInputs, MergeRollupInputs, ParityPublicInputs, + PrivateKernelEmptyInputData, Proof, PublicKernelCircuitPrivateInputs, PublicKernelCircuitPublicInputs, @@ -41,6 +42,7 @@ export function createProvingJobSourceServer(queue: ProvingJobSource): JsonRpcSe ParityPublicInputs, Proof, ProvingError, + PrivateKernelEmptyInputData, PublicKernelCircuitPrivateInputs, PublicKernelCircuitPublicInputs, PublicKernelTailCircuitPrivateInputs, @@ -75,6 +77,7 @@ export function createProvingJobSourceClient( ParityPublicInputs, Proof, ProvingError, + PrivateKernelEmptyInputData, PublicKernelCircuitPrivateInputs, PublicKernelCircuitPublicInputs, PublicKernelTailCircuitPrivateInputs, diff --git a/yarn-project/prover-client/src/test/bb_prover_base_rollup.test.ts b/yarn-project/prover-client/src/test/bb_prover_base_rollup.test.ts index e2b11a1338e..ea572030820 100644 --- a/yarn-project/prover-client/src/test/bb_prover_base_rollup.test.ts +++ b/yarn-project/prover-client/src/test/bb_prover_base_rollup.test.ts @@ -1,6 +1,10 @@ import { BBNativeRollupProver, type BBProverConfig } from '@aztec/bb-prover'; import { makePaddingProcessedTxFromTubeProof } from '@aztec/circuit-types'; -import { NESTED_RECURSIVE_PROOF_LENGTH, makeEmptyRecursiveProof } from '@aztec/circuits.js'; +import { + NESTED_RECURSIVE_PROOF_LENGTH, + PrivateKernelEmptyInputData, + makeEmptyRecursiveProof, +} from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types'; import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; @@ -32,13 +36,7 @@ describe('prover/bb_prover/base-rollup', () => { const version = context.globalVariables.version; const vkTreeRoot = getVKTreeRoot(); - const inputs = { - header, - chainId, - version, - vkTreeRoot, - }; - + const inputs = new PrivateKernelEmptyInputData(header, chainId, version, vkTreeRoot); const paddingTxPublicInputsAndProof = await context.prover.getEmptyTubeProof(inputs); const tx = makePaddingProcessedTxFromTubeProof(paddingTxPublicInputsAndProof); diff --git a/yarn-project/prover-client/src/tx-prover/tx-prover.ts b/yarn-project/prover-client/src/tx-prover/tx-prover.ts index b6b78fc9835..f57d475d576 100644 --- a/yarn-project/prover-client/src/tx-prover/tx-prover.ts +++ b/yarn-project/prover-client/src/tx-prover/tx-prover.ts @@ -31,7 +31,7 @@ export class TxProver implements ProverClient { private telemetry: TelemetryClient, private agent?: ProverAgent, ) { - this.queue = new MemoryProvingQueue(config.proverJobTimeoutMs, config.proverJobPollIntervalMs); + this.queue = new MemoryProvingQueue(telemetry, config.proverJobTimeoutMs, config.proverJobPollIntervalMs); this.orchestrator = new ProvingOrchestrator( worldStateSynchronizer.getLatest(), this.queue, diff --git a/yarn-project/prover-node/src/factory.ts b/yarn-project/prover-node/src/factory.ts index d2e18610b14..26a8bd6cf69 100644 --- a/yarn-project/prover-node/src/factory.ts +++ b/yarn-project/prover-node/src/factory.ts @@ -43,7 +43,7 @@ export async function createProverNode( const prover = await createProverClient(config, worldStateSynchronizer, archiver); // REFACTOR: Move publisher out of sequencer package and into an L1-related package - const publisher = getL1Publisher(config); + const publisher = getL1Publisher(config, telemetry); const latestWorldState = worldStateSynchronizer.getLatest(); const publicProcessorFactory = new PublicProcessorFactory(latestWorldState, archiver, simulationProvider, telemetry); diff --git a/yarn-project/pxe/src/config/index.ts b/yarn-project/pxe/src/config/index.ts index b9caa044d73..5ee999957e0 100644 --- a/yarn-project/pxe/src/config/index.ts +++ b/yarn-project/pxe/src/config/index.ts @@ -1,4 +1,5 @@ import { INITIAL_L2_BLOCK_NUM } from '@aztec/circuits.js/constants'; +import { type Network } from '@aztec/types/network'; import { readFileSync } from 'fs'; import { dirname, resolve } from 'path'; @@ -33,6 +34,8 @@ export interface PXEConfig { export type PXEServiceConfig = PXEConfig & KernelProverConfig & BBProverConfig; +export type CliPXEOptions = PXEServiceConfig & { network?: Network; apiKey?: string; nodeUrl?: string }; + /** * Creates an instance of PXEServiceConfig out of environment variables using sensible defaults for integration testing if not set. */ @@ -56,6 +59,22 @@ export function getPXEServiceConfig(): PXEServiceConfig { }; } +/** + * Creates an instance of CliPxeOptions out of environment variables + */ +export function getCliPXEOptions(): CliPXEOptions { + const pxeServiceConfig = getPXEServiceConfig(); + const { NETWORK, AZTEC_NODE_URL, ΑPI_KEY } = process.env; + + return { + ...pxeServiceConfig, + network: NETWORK ? (NETWORK as Network) : undefined, + apiKey: ΑPI_KEY, + nodeUrl: AZTEC_NODE_URL, + proverEnabled: !!NETWORK, + }; +} + /** * Returns package name and version. */ diff --git a/yarn-project/sequencer-client/src/client/sequencer-client.ts b/yarn-project/sequencer-client/src/client/sequencer-client.ts index 5250d962aca..db9a3704978 100644 --- a/yarn-project/sequencer-client/src/client/sequencer-client.ts +++ b/yarn-project/sequencer-client/src/client/sequencer-client.ts @@ -41,7 +41,7 @@ export class SequencerClient { simulationProvider: SimulationProvider, telemetryClient: TelemetryClient, ) { - const publisher = getL1Publisher(config); + const publisher = getL1Publisher(config, telemetryClient); const globalsBuilder = getGlobalVariableBuilder(config); const merkleTreeDb = worldStateSynchronizer.getLatest(); diff --git a/yarn-project/sequencer-client/src/publisher/index.ts b/yarn-project/sequencer-client/src/publisher/index.ts index 8e5f8dbd60e..71a69336ae7 100644 --- a/yarn-project/sequencer-client/src/publisher/index.ts +++ b/yarn-project/sequencer-client/src/publisher/index.ts @@ -1,3 +1,5 @@ +import { type TelemetryClient } from '@aztec/telemetry-client'; + import { type PublisherConfig, type TxSenderConfig } from './config.js'; import { L1Publisher } from './l1-publisher.js'; import { ViemTxSender } from './viem-tx-sender.js'; @@ -9,6 +11,6 @@ export { PublisherConfig, TxSenderConfig, getTxSenderConfigFromEnv } from './con * Returns a new instance of the L1Publisher. * @param config - Configuration to initialize the new instance. */ -export function getL1Publisher(config: PublisherConfig & TxSenderConfig): L1Publisher { - return new L1Publisher(new ViemTxSender(config), config); +export function getL1Publisher(config: PublisherConfig & TxSenderConfig, client: TelemetryClient): L1Publisher { + return new L1Publisher(new ViemTxSender(config), client, config); } diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher-metrics.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher-metrics.ts new file mode 100644 index 00000000000..4a11fad1e54 --- /dev/null +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher-metrics.ts @@ -0,0 +1,108 @@ +import type { L1PublishBlockStats, L1PublishProofStats } from '@aztec/circuit-types/stats'; +import { + Attributes, + type Histogram, + Metrics, + type TelemetryClient, + type UpDownCounter, + ValueType, +} from '@aztec/telemetry-client'; + +import { formatEther } from 'viem/utils'; + +export type L1TxType = 'submitProof' | 'process'; + +export class L1PublisherMetrics { + private gasPrice: Histogram; + + private txCount: UpDownCounter; + private txDuration: Histogram; + private txGas: Histogram; + private txCalldataSize: Histogram; + private txCalldataGas: Histogram; + + constructor(client: TelemetryClient, name = 'L1Publisher') { + const meter = client.getMeter(name); + + this.gasPrice = meter.createHistogram(Metrics.L1_PUBLISHER_GAS_PRICE, { + description: 'The gas price used for transactions', + unit: 'gwei', + valueType: ValueType.DOUBLE, + }); + + this.txCount = meter.createUpDownCounter(Metrics.L1_PUBLISHER_TX_COUNT, { + description: 'The number of transactions processed', + }); + + this.txDuration = meter.createHistogram(Metrics.L1_PUBLISHER_TX_DURATION, { + description: 'The duration of transaction processing', + unit: 'ms', + valueType: ValueType.INT, + advice: { + explicitBucketBoundaries: [10, 50, 100, 200, 500, 1000, 2000, 5000, 10000], + }, + }); + + this.txGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_GAS, { + description: 'The gas consumed by transactions', + unit: 'gas', + valueType: ValueType.INT, + }); + + this.txCalldataSize = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_SIZE, { + description: 'The size of the calldata in transactions', + unit: 'bytes', + valueType: ValueType.INT, + advice: { + explicitBucketBoundaries: [0, 100, 200, 500, 1000, 2000, 5000, 10000], + }, + }); + + this.txCalldataGas = meter.createHistogram(Metrics.L1_PUBLISHER_TX_CALLDATA_GAS, { + description: 'The gas consumed by the calldata in transactions', + unit: 'gas', + valueType: ValueType.INT, + }); + } + + recordFailedTx(txType: L1TxType) { + this.txCount.add(1, { + [Attributes.L1_TX_TYPE]: txType, + [Attributes.OK]: false, + }); + } + + recordSubmitProof(durationMs: number, stats: L1PublishProofStats) { + this.recordTx('submitProof', durationMs, stats); + } + + recordProcessBlockTx(durationMs: number, stats: L1PublishBlockStats) { + this.recordTx('process', durationMs, stats); + } + + private recordTx(txType: L1TxType, durationMs: number, stats: Omit) { + const attributes = { + [Attributes.L1_TX_TYPE]: txType, + } as const; + + this.txCount.add(1, { + ...attributes, + [Attributes.OK]: true, + }); + + this.txDuration.record(Math.ceil(durationMs), attributes); + this.txGas.record( + // safe to downcast - total block limit is 30M gas which fits in a JS number + Number(stats.gasUsed), + attributes, + ); + this.txCalldataGas.record(stats.calldataGas, attributes); + this.txCalldataSize.record(stats.calldataSize, attributes); + + try { + this.gasPrice.record(parseInt(formatEther(stats.gasPrice, 'gwei'), 10)); + } catch (e) { + // ignore + } + } +} diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts index 6ceaa3d6f1b..32eaee2e7d5 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts @@ -1,5 +1,6 @@ import { L2Block } from '@aztec/circuit-types'; import { sleep } from '@aztec/foundation/sleep'; +import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; import { type MockProxy, mock } from 'jest-mock-extended'; @@ -47,7 +48,7 @@ describe('L1Publisher', () => { txSender.getTransactionReceipt.mockResolvedValueOnce(publishTxReceipt).mockResolvedValueOnce(processTxReceipt); txSender.getCurrentArchive.mockResolvedValue(l2Block.header.lastArchive.root.toBuffer()); - publisher = new L1Publisher(txSender, { l1PublishRetryIntervalMS: 1 }); + publisher = new L1Publisher(txSender, new NoopTelemetryClient(), { l1PublishRetryIntervalMS: 1 }); }); it('publishes l2 block to l1', async () => { diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts index 7ea4c48bd46..5c7894c31e4 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts @@ -5,11 +5,14 @@ import { type Fr } from '@aztec/foundation/fields'; import { createDebugLogger } from '@aztec/foundation/log'; import { serializeToBuffer } from '@aztec/foundation/serialize'; import { InterruptibleSleep } from '@aztec/foundation/sleep'; +import { Timer } from '@aztec/foundation/timer'; +import { type TelemetryClient } from '@aztec/telemetry-client'; import pick from 'lodash.pick'; import { type L2BlockReceiver } from '../receiver.js'; import { type PublisherConfig } from './config.js'; +import { L1PublisherMetrics } from './l1-publisher-metrics.js'; /** * Stats for a sent transaction. @@ -133,10 +136,12 @@ export class L1Publisher implements L2BlockReceiver { private interruptibleSleep = new InterruptibleSleep(); private sleepTimeMs: number; private interrupted = false; + private metrics: L1PublisherMetrics; private log = createDebugLogger('aztec:sequencer:publisher'); - constructor(private txSender: L1PublisherTxSender, config?: PublisherConfig) { + constructor(private txSender: L1PublisherTxSender, client: TelemetryClient, config?: PublisherConfig) { this.sleepTimeMs = config?.l1PublishRetryIntervalMS ?? 60_000; + this.metrics = new L1PublisherMetrics(client, 'L1Publisher'); } public async isItMyTurnToSubmit(): Promise { @@ -203,6 +208,7 @@ export class L1Publisher implements L2BlockReceiver { // Process block while (!this.interrupted) { + const timer = new Timer(); const txHash = await this.sendProcessTx(processTxArgs); if (!txHash) { break; @@ -223,9 +229,12 @@ export class L1Publisher implements L2BlockReceiver { eventName: 'rollup-published-to-l1', }; this.log.info(`Published L2 block to L1 rollup contract`, { ...stats, ...ctx }); + this.metrics.recordProcessBlockTx(timer.ms(), stats); return true; } + this.metrics.recordFailedTx('process'); + // Check if someone else incremented the block number if (!(await this.checkLastArchiveHash(lastArchive))) { this.log.warn('Publish failed. Detected different last archive hash.', ctx); @@ -259,6 +268,7 @@ export class L1Publisher implements L2BlockReceiver { // Process block while (!this.interrupted) { + const timer = new Timer(); const txHash = await this.sendSubmitProofTx(txArgs); if (!txHash) { break; @@ -278,9 +288,11 @@ export class L1Publisher implements L2BlockReceiver { eventName: 'proof-published-to-l1', }; this.log.info(`Published L2 block to L1 rollup contract`, { ...stats, ...ctx }); + this.metrics.recordSubmitProof(timer.ms(), stats); return true; } + this.metrics.recordFailedTx('submitProof'); this.log.error(`Rollup.submitProof tx status failed: ${receipt.transactionHash}`, ctx); await this.sleepOrInterrupted(); } diff --git a/yarn-project/sequencer-client/src/sequencer/metrics.ts b/yarn-project/sequencer-client/src/sequencer/metrics.ts new file mode 100644 index 00000000000..858c42455cb --- /dev/null +++ b/yarn-project/sequencer-client/src/sequencer/metrics.ts @@ -0,0 +1,61 @@ +import { + Attributes, + type Histogram, + Metrics, + type TelemetryClient, + type Tracer, + type UpDownCounter, + ValueType, + millisecondBuckets, +} from '@aztec/telemetry-client'; + +export class SequencerMetrics { + public readonly tracer: Tracer; + + private cancelledBlockCounter: UpDownCounter; + private blocksBuiltCounter: UpDownCounter; + private blockBuildDuration: Histogram; + private blockTxCount: Histogram; + + constructor(client: TelemetryClient, name = 'Sequencer') { + const meter = client.getMeter(name); + this.tracer = client.getTracer(name); + + this.cancelledBlockCounter = meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_BUILD_CANCELLED_COUNT); + this.blocksBuiltCounter = meter.createUpDownCounter(Metrics.SEQUENCER_BLOCK_BUILD_COUNT); + this.blockBuildDuration = meter.createHistogram(Metrics.SEQUENCER_BLOCK_BUILD_DURATION, { + unit: 'ms', + description: 'Duration to build a block', + valueType: ValueType.INT, + advice: { + explicitBucketBoundaries: millisecondBuckets(2), + }, + }); + + this.blockTxCount = meter.createHistogram(Metrics.SEQUENCER_BLOCK_BUILD_TX_COUNT, { + description: 'Number of transactions in a block', + valueType: ValueType.INT, + }); + } + + recordCancelledBlock() { + this.cancelledBlockCounter.add(1); + } + + recordPublishedBlock(buildDurationMs: number) { + this.blocksBuiltCounter.add(1, { + [Attributes.OK]: true, + }); + this.blockBuildDuration.record(Math.ceil(buildDurationMs)); + } + + recordFailedBlock() { + this.blocksBuiltCounter.add(1, { + [Attributes.OK]: false, + }); + } + + recordNewBlock(txCount: number) { + this.blockTxCount.record(txCount); + } +} diff --git a/yarn-project/sequencer-client/src/sequencer/sequencer.ts b/yarn-project/sequencer-client/src/sequencer/sequencer.ts index 8fbdff27634..2a08c56dae2 100644 --- a/yarn-project/sequencer-client/src/sequencer/sequencer.ts +++ b/yarn-project/sequencer-client/src/sequencer/sequencer.ts @@ -27,6 +27,7 @@ import { type GlobalVariableBuilder } from '../global_variable_builder/global_bu import { type L1Publisher } from '../publisher/l1-publisher.js'; import { type TxValidatorFactory } from '../tx_validator/tx_validator_factory.js'; import { type SequencerConfig } from './config.js'; +import { SequencerMetrics } from './metrics.js'; /** * Sequencer client @@ -53,8 +54,7 @@ export class Sequencer { private allowedInTeardown: AllowedElement[] = []; private maxBlockSizeInBytes: number = 1024 * 1024; private skipSubmitProofs: boolean = false; - - public readonly tracer: Tracer; + private metrics: SequencerMetrics; constructor( private publisher: L1Publisher, @@ -71,10 +71,14 @@ export class Sequencer { private log = createDebugLogger('aztec:sequencer'), ) { this.updateConfig(config); - this.tracer = telemetry.getTracer('Sequencer'); + this.metrics = new SequencerMetrics(telemetry, 'Sequencer'); this.log.verbose(`Initialized sequencer with ${this.minTxsPerBLock}-${this.maxTxsPerBlock} txs per block.`); } + get tracer(): Tracer { + return this.metrics.tracer; + } + /** * Updates sequencer config. * @param config - New parameters. @@ -280,6 +284,7 @@ export class Sequencer { historicalHeader: Header | undefined, elapsedSinceLastBlock: number, ): Promise { + this.metrics.recordNewBlock(validTxs.length); const workTimer = new Timer(); this.state = SequencerState.CREATING_BLOCK; this.log.info(`Building block ${newGlobalVariables.blockNumber.toNumber()} with ${validTxs.length} transactions`); @@ -287,6 +292,7 @@ export class Sequencer { const assertBlockHeight = async () => { const currentBlockNumber = await this.l2BlockSource.getBlockNumber(); if (currentBlockNumber + 1 !== newGlobalVariables.blockNumber.toNumber()) { + this.metrics.recordCancelledBlock(); throw new Error('New block was emitted while building block'); } @@ -350,16 +356,23 @@ export class Sequencer { await assertBlockHeight(); + const workDuration = workTimer.ms(); this.log.verbose(`Assembled block ${block.number}`, { eventName: 'l2-block-built', - duration: workTimer.ms(), + duration: workDuration, publicProcessDuration: publicProcessorDuration, rollupCircuitsDuration: blockBuildingTimer.ms(), ...block.getStats(), } satisfies L2BlockBuiltStats); - await this.publishL2Block(block); - this.log.info(`Submitted rollup block ${block.number} with ${processedTxs.length} transactions`); + try { + await this.publishL2Block(block); + this.metrics.recordPublishedBlock(workDuration); + this.log.info(`Submitted rollup block ${block.number} with ${processedTxs.length} transactions`); + } catch (err) { + this.metrics.recordFailedBlock(); + throw err; + } // Submit the proof if we have configured this sequencer to run with an actual prover. // This is temporary while we submit one proof per block, but will have to change once we diff --git a/yarn-project/simulator/src/public/executor.ts b/yarn-project/simulator/src/public/executor.ts index 1e96f97c956..f3a8a13ebce 100644 --- a/yarn-project/simulator/src/public/executor.ts +++ b/yarn-project/simulator/src/public/executor.ts @@ -3,6 +3,7 @@ import { type AvmSimulationStats } from '@aztec/circuit-types/stats'; import { Fr, Gas, type GlobalVariables, type Header, type Nullifier, type TxContext } from '@aztec/circuits.js'; import { createDebugLogger } from '@aztec/foundation/log'; import { Timer } from '@aztec/foundation/timer'; +import { type TelemetryClient } from '@aztec/telemetry-client'; import { AvmContext } from '../avm/avm_context.js'; import { AvmExecutionEnvironment } from '../avm/avm_execution_environment.js'; @@ -12,18 +13,24 @@ import { HostStorage } from '../avm/journal/host_storage.js'; import { AvmPersistableStateManager } from '../avm/journal/index.js'; import { type CommitmentsDB, type PublicContractsDB, type PublicStateDB } from './db_interfaces.js'; import { type PublicExecutionResult } from './execution.js'; +import { ExecutorMetrics } from './executor_metrics.js'; import { PublicSideEffectTrace } from './side_effect_trace.js'; /** * Handles execution of public functions. */ export class PublicExecutor { + metrics: ExecutorMetrics; + constructor( private readonly publicStorageDB: PublicStateDB, private readonly contractsDb: PublicContractsDB, private readonly commitmentsDb: CommitmentsDB, private readonly header: Header, - ) {} + client: TelemetryClient, + ) { + this.metrics = new ExecutorMetrics(client, 'PublicExecutor'); + } static readonly log = createDebugLogger('aztec:simulator:public_executor'); @@ -102,6 +109,12 @@ export class PublicExecutor { fnName, ); + if (publicExecutionResult.reverted) { + this.metrics.recordFunctionSimulationFailure(); + } else { + this.metrics.recordFunctionSimulation(bytecode.length, timer.ms()); + } + return publicExecutionResult; } } diff --git a/yarn-project/simulator/src/public/executor_metrics.ts b/yarn-project/simulator/src/public/executor_metrics.ts new file mode 100644 index 00000000000..1d4a05566a8 --- /dev/null +++ b/yarn-project/simulator/src/public/executor_metrics.ts @@ -0,0 +1,48 @@ +import { + Attributes, + type Histogram, + Metrics, + type TelemetryClient, + type UpDownCounter, + ValueType, +} from '@aztec/telemetry-client'; + +export class ExecutorMetrics { + private fnCount: UpDownCounter; + private fnDuration: Histogram; + private bytecodeSize: Histogram; + + constructor(client: TelemetryClient, name = 'PublicExecutor') { + const meter = client.getMeter(name); + + this.fnCount = meter.createUpDownCounter(Metrics.PUBLIC_EXECUTOR_SIMULATION_COUNT, { + description: 'Number of functions executed', + }); + + this.fnDuration = meter.createHistogram(Metrics.PUBLIC_EXECUTOR_SIMULATION_DURATION, { + description: 'How long it takes to execute a function', + unit: 'ms', + valueType: ValueType.INT, + }); + + this.bytecodeSize = meter.createHistogram(Metrics.PUBLIC_EXECUTION_SIMULATION_BYTECODE_SIZE, { + description: 'Size of the function bytecode', + unit: 'By', + valueType: ValueType.INT, + }); + } + + recordFunctionSimulation(bytecodeSize: number, durationMs: number) { + this.fnCount.add(1, { + [Attributes.OK]: true, + }); + this.bytecodeSize.record(bytecodeSize); + this.fnDuration.record(Math.ceil(durationMs)); + } + + recordFunctionSimulationFailure() { + this.fnCount.add(1, { + [Attributes.OK]: false, + }); + } +} diff --git a/yarn-project/simulator/src/public/public_processor.ts b/yarn-project/simulator/src/public/public_processor.ts index b47d347b464..e3392c67154 100644 --- a/yarn-project/simulator/src/public/public_processor.ts +++ b/yarn-project/simulator/src/public/public_processor.ts @@ -13,6 +13,7 @@ import { } from '@aztec/circuit-types'; import { AztecAddress, + ContractClassRegisteredEvent, GAS_TOKEN_ADDRESS, type GlobalVariables, type Header, @@ -23,7 +24,9 @@ import { } from '@aztec/circuits.js'; import { times } from '@aztec/foundation/collection'; import { createDebugLogger } from '@aztec/foundation/log'; +import { Timer } from '@aztec/foundation/timer'; import { type ProtocolArtifact } from '@aztec/noir-protocol-circuits-types'; +import { ClassRegistererAddress } from '@aztec/protocol-contracts/class-registerer'; import { PublicExecutor, type PublicStateDB, @@ -40,6 +43,7 @@ import { PhaseManagerFactory } from './phase_manager_factory.js'; import { ContractsDataSourcePublicDB, WorldStateDB, WorldStatePublicDB } from './public_db_sources.js'; import { RealPublicKernelCircuitSimulator } from './public_kernel.js'; import { type PublicKernelCircuitSimulator } from './public_kernel_circuit_simulator.js'; +import { PublicProcessorMetrics } from './public_processor_metrics.js'; /** * Creates new instances of PublicProcessor given the provided merkle tree db and contract data source. @@ -65,7 +69,13 @@ export class PublicProcessorFactory { const publicContractsDB = new ContractsDataSourcePublicDB(this.contractDataSource); const worldStatePublicDB = new WorldStatePublicDB(this.merkleTree); const worldStateDB = new WorldStateDB(this.merkleTree); - const publicExecutor = new PublicExecutor(worldStatePublicDB, publicContractsDB, worldStateDB, historicalHeader); + const publicExecutor = new PublicExecutor( + worldStatePublicDB, + publicContractsDB, + worldStateDB, + historicalHeader, + this.telemetryClient, + ); return new PublicProcessor( this.merkleTree, publicExecutor, @@ -84,7 +94,7 @@ export class PublicProcessorFactory { * any public function calls in them. Txs with private calls only are unaffected. */ export class PublicProcessor { - public readonly tracer: Tracer; + private metrics: PublicProcessorMetrics; constructor( protected db: MerkleTreeOperations, protected publicExecutor: PublicExecutor, @@ -96,7 +106,11 @@ export class PublicProcessor { telemetryClient: TelemetryClient, private log = createDebugLogger('aztec:sequencer:public-processor'), ) { - this.tracer = telemetryClient.getTracer('PublicProcessor'); + this.metrics = new PublicProcessorMetrics(telemetryClient, 'PublicProcessor'); + } + + get tracer(): Tracer { + return this.metrics.tracer; } /** @@ -222,6 +236,7 @@ export class PublicProcessor { [Attributes.TX_HASH]: tx.getTxHash().toString(), })) private async processTxWithPublicCalls(tx: Tx): Promise<[ProcessedTx, NestedProcessReturnValues[]]> { + const timer = new Timer(); let returnValues: NestedProcessReturnValues[] = []; const publicProvingRequests: PublicProvingRequest[] = []; let phase: AbstractPhaseManager | undefined = PhaseManagerFactory.phaseFromTx( @@ -240,8 +255,18 @@ export class PublicProcessor { let finalKernelOutput: KernelCircuitPublicInputs | undefined; let revertReason: SimulationError | undefined; const gasUsed: ProcessedTx['gasUsed'] = {}; + let phaseCount = 0; while (phase) { + phaseCount++; + const phaseTimer = new Timer(); const output = await phase.handle(tx, publicKernelPublicInput, lastKernelArtifact); + + if (output.revertReason) { + this.metrics.recordRevertedPhase(phase.phase); + } else { + this.metrics.recordPhaseDuration(phase.phase, phaseTimer.ms()); + } + gasUsed[phase.phase] = output.gasUsed; if (phase.phase === PublicKernelType.APP_LOGIC) { returnValues = output.returnValues; @@ -265,9 +290,15 @@ export class PublicProcessor { } if (!finalKernelOutput) { + this.metrics.recordFailedTx(); throw new Error('Final public kernel was not executed.'); } + this.metrics.recordClassRegistration( + ...ContractClassRegisteredEvent.fromLogs(tx.unencryptedLogs.unrollLogs(), ClassRegistererAddress), + ); + + this.metrics.recordTx(phaseCount, timer.ms()); const processedTx = makeProcessedTx(tx, finalKernelOutput, publicProvingRequests, revertReason, gasUsed); return [processedTx, returnValues]; } diff --git a/yarn-project/simulator/src/public/public_processor_metrics.ts b/yarn-project/simulator/src/public/public_processor_metrics.ts new file mode 100644 index 00000000000..e20b90f2f73 --- /dev/null +++ b/yarn-project/simulator/src/public/public_processor_metrics.ts @@ -0,0 +1,90 @@ +import { type PublicKernelType } from '@aztec/circuit-types'; +import { type ContractClassRegisteredEvent } from '@aztec/circuits.js'; +import { + Attributes, + type Histogram, + Metrics, + type TelemetryClient, + type Tracer, + type UpDownCounter, + ValueType, +} from '@aztec/telemetry-client'; + +export class PublicProcessorMetrics { + public readonly tracer: Tracer; + + private txDuration: Histogram; + private txCount: UpDownCounter; + private txPhaseCount: UpDownCounter; + + private phaseDuration: Histogram; + private phaseCount: UpDownCounter; + + private bytecodeDeployed: Histogram; + + constructor(client: TelemetryClient, name = 'PublicProcessor') { + this.tracer = client.getTracer(name); + const meter = client.getMeter(name); + + this.txDuration = meter.createHistogram(Metrics.PUBLIC_PROCESSOR_TX_DURATION, { + description: 'How long it takes to process a transaction', + unit: 'ms', + valueType: ValueType.INT, + }); + + this.txCount = meter.createUpDownCounter(Metrics.PUBLIC_PROCESSOR_TX_COUNT, { + description: 'Number of transactions processed', + }); + + this.txPhaseCount = meter.createUpDownCounter(Metrics.PUBLIC_PROCESSOR_TX_PHASE_COUNT, { + description: 'Number of phases processed', + }); + + this.phaseDuration = meter.createHistogram(Metrics.PUBLIC_PROCESSOR_PHASE_DURATION, { + description: 'How long it takes to process a phase', + unit: 'ms', + valueType: ValueType.INT, + }); + + this.phaseCount = meter.createUpDownCounter(Metrics.PUBLIC_PROCESSOR_PHASE_COUNT, { + description: 'Number of failed phases', + }); + + this.bytecodeDeployed = meter.createHistogram(Metrics.PUBLIC_PROCESSOR_DEPLOY_BYTECODE_SIZE, { + description: 'Size of deployed bytecode', + unit: 'By', + }); + } + + recordPhaseDuration(phaseName: PublicKernelType, durationMs: number) { + this.phaseCount.add(1, { [Attributes.TX_PHASE_NAME]: phaseName, [Attributes.OK]: true }); + this.phaseDuration.record(Math.ceil(durationMs), { [Attributes.TX_PHASE_NAME]: phaseName }); + } + + recordTx(phaseCount: number, durationMs: number) { + this.txPhaseCount.add(phaseCount); + this.txDuration.record(Math.ceil(durationMs)); + this.txCount.add(1, { + [Attributes.OK]: true, + }); + } + + recordFailedTx() { + this.txCount.add(1, { + [Attributes.OK]: false, + }); + } + + recordRevertedPhase(phaseName: PublicKernelType) { + this.phaseCount.add(1, { [Attributes.TX_PHASE_NAME]: phaseName, [Attributes.OK]: false }); + } + + recordClassRegistration(...events: ContractClassRegisteredEvent[]) { + let totalBytecode = 0; + for (const event of events) { + totalBytecode += event.packedPublicBytecode.length; + } + + this.bytecodeDeployed.record(totalBytecode); + } +} diff --git a/yarn-project/telemetry-client/src/attributes.ts b/yarn-project/telemetry-client/src/attributes.ts index d4df0253436..8a47a7d486e 100644 --- a/yarn-project/telemetry-client/src/attributes.ts +++ b/yarn-project/telemetry-client/src/attributes.ts @@ -14,6 +14,9 @@ * @see {@link https://opentelemetry.io/docs/specs/semconv/general/attribute-naming/} */ +/** The Aztec network identifier */ +export const NETWORK_ID = 'aztec.network.id'; + /** * The name of the protocol circuit being run (e.g. public-kernel-setup or base-rollup) * @see {@link @aztec/circuit-types/stats:CircuitName} @@ -47,3 +50,13 @@ export const BLOCK_TXS_COUNT = 'aztec.block.txs_count'; export const BLOCK_SIZE = 'aztec.block.size'; /** The tx hash */ export const TX_HASH = 'aztec.tx.hash'; +/** Generic attribute representing whether the action was successful or not */ +export const OK = 'aztec.ok'; +/** Generic status attribute */ +export const STATUS = 'aztec.status'; +/** The type of the transaction */ +export const L1_TX_TYPE = 'aztec.l1.tx_type'; +/** The phase of the transaction */ +export const TX_PHASE_NAME = 'aztec.tx.phase_name'; +/** The proving job type */ +export const PROVING_JOB_TYPE = 'aztec.proving.job_type'; diff --git a/yarn-project/telemetry-client/src/histogram_utils.test.ts b/yarn-project/telemetry-client/src/histogram_utils.test.ts new file mode 100644 index 00000000000..f23482f995b --- /dev/null +++ b/yarn-project/telemetry-client/src/histogram_utils.test.ts @@ -0,0 +1,47 @@ +import { exponentialBuckets, linearBuckets, millisecondBuckets } from './histogram_utils.js'; + +describe('linearBuckets', () => { + it.each([[10, 1_000, 5, [10, 208, 406, 604, 802, 1000]]] as const)( + 'should return the expected buckets for a given range', + (start, end, count, expected) => { + expect(linearBuckets(start, end, count)).toEqual(expected); + }, + ); +}); + +describe('exponentialBuckets', () => { + it.each([[2, 8, [1, 1.19, 1.41, 1.68, 2, 2.38, 2.83, 3.36, 4].map(x => expect.closeTo(x, 2))]] as const)( + 'should return the expected buckets for a given range', + (scale, count, expected) => { + expect(exponentialBuckets(scale, count)).toEqual(expected); + }, + ); +}); + +describe('millisecondBuckets', () => { + it('should throw an error if significantFractionalDigits is less than 1', () => { + expect(() => millisecondBuckets(0)).toThrow(); + }); + + it('should return the expected buckets for milliseconds', () => { + expect(millisecondBuckets(1, 16)).toEqual([ + 10, // 2^0 * 10 + 12, + 14, + 17, + 20, // 2^1 * 10 + 24, + 28, + 34, + 40, // 2^2 * 10 + 48, + 57, + 67, + 80, // 2^3 * 10 + 95, + 113, + 135, + 160, // 2^4 * 10 + ]); + }); +}); diff --git a/yarn-project/telemetry-client/src/histogram_utils.ts b/yarn-project/telemetry-client/src/histogram_utils.ts new file mode 100644 index 00000000000..0296bac486a --- /dev/null +++ b/yarn-project/telemetry-client/src/histogram_utils.ts @@ -0,0 +1,66 @@ +/** + * Creates a set of buckets that follow linear growth + * @param start - The start of the range + * @param end - The end of the range + * @param count - The number of buckets + * @returns - An array of bucket boundaries + */ +export function linearBuckets(start: number, end: number, count: number): number[] { + const buckets = []; + const step = (end - start) / count; + for (let i = 0; i <= count; i++) { + buckets.push(start + i * step); + } + return buckets; +} + +/** + * Creates an array of exponential buckets that follow the formulas at + * https://opentelemetry.io/docs/specs/otel/metrics/data-model/#exponential-buckets + * + * The formula followed is: bucket[i] = base ** i, where base = 2 ** (2 ** -scale). Naturally base will be very small when scale > 0. + * This ensures that between each power of 2, there are 2 ** scale intermediate buckets. + * + * @example + * scale = 2, count = 8 + * base = 2 ** (2 ** -2) = 1.189207115 + * |bucket index| value | + * |------------|-------| + * | 0 | 1 | + * | 1 | 1.18 | + * | 2 | 1.41 | + * | 3 | 1.68 | + * | 4 | 2 | + * | 5 | 2.37 | + * | 6 | 2.82 | + * | 7 | 3.36 | + * | 8 | 4 | + * + * @param scale - The "precision" of the buckets. The higher the scale, the more buckets will be created + * @param count - The total number of buckets + * @returns - An array of bucket boundaries + */ +export function exponentialBuckets(scale: number, count: number): number[] { + const buckets: number[] = []; + const base = 2 ** (2 ** -scale); + for (let i = 0; i <= count; i++) { + buckets.push(base ** i); + } + return buckets; +} + +/** + * Creates an array of exponential buckets optimized for milliseconds + * @param significantFractionalDigits - The number of significant digits to round to + * @param count - The number of buckets. Defaults to 60 + * @returns - An array of bucket boundaries + */ +export function millisecondBuckets(significantFractionalDigits: number, count = 60): number[] { + if (significantFractionalDigits < 1) { + // if significant digits is 1 then we end up having duplicate buckets + throw new Error('significantFractionalDigits must be >= 1'); + } + + const scale = 10 ** significantFractionalDigits; + return exponentialBuckets(2, count).map(x => Math.round(x * scale)); +} diff --git a/yarn-project/telemetry-client/src/index.ts b/yarn-project/telemetry-client/src/index.ts index f84f46bf75c..909f43f644c 100644 --- a/yarn-project/telemetry-client/src/index.ts +++ b/yarn-project/telemetry-client/src/index.ts @@ -1 +1,2 @@ export * from './telemetry.js'; +export * from './histogram_utils.js'; diff --git a/yarn-project/telemetry-client/src/metrics.ts b/yarn-project/telemetry-client/src/metrics.ts index e5487ef41b3..67095735d0e 100644 --- a/yarn-project/telemetry-client/src/metrics.ts +++ b/yarn-project/telemetry-client/src/metrics.ts @@ -28,3 +28,35 @@ export const MEMPOOL_TX_SIZE = 'aztec.mempool.tx_size'; export const ARCHIVER_BLOCK_HEIGHT = 'aztec.archiver.block_height'; export const ARCHIVER_BLOCK_SIZE = 'aztec.archiver.block_size'; + +export const NODE_RECEIVE_TX_DURATION = 'aztec.node.receive_tx.duration'; +export const NODE_RECEIVE_TX_COUNT = 'aztec.node.receive_tx.count'; + +export const SEQUENCER_BLOCK_BUILD_DURATION = 'aztec.sequencer.block_build.duration'; +export const SEQUENCER_BLOCK_BUILD_COUNT = 'aztec.sequencer.block_build.ok_count'; +export const SEQUENCER_BLOCK_BUILD_CANCELLED_COUNT = 'aztec.sequencer.block_build.cancelled_count'; +export const SEQUENCER_BLOCK_BUILD_TX_COUNT = 'aztec.sequencer.block_build.tx_count'; + +export const L1_PUBLISHER_GAS_PRICE = 'aztec.l1_publisher.gas_price'; +export const L1_PUBLISHER_TX_COUNT = 'aztec.l1_publisher.tx_count'; +export const L1_PUBLISHER_TX_DURATION = 'aztec.l1_publisher.tx_duration'; +export const L1_PUBLISHER_TX_GAS = 'aztec.l1_publisher.tx_gas'; +export const L1_PUBLISHER_TX_CALLDATA_SIZE = 'aztec.l1_publisher.tx_calldata_size'; +export const L1_PUBLISHER_TX_CALLDATA_GAS = 'aztec.l1_publisher.tx_calldata_gas'; + +export const PUBLIC_PROCESSOR_TX_DURATION = 'aztec.public_processor.tx_duration'; +export const PUBLIC_PROCESSOR_TX_COUNT = 'aztec.public_processor.tx_count'; +export const PUBLIC_PROCESSOR_TX_PHASE_COUNT = 'aztec.public_processor.tx_phase_count'; +export const PUBLIC_PROCESSOR_PHASE_DURATION = 'aztec.public_processor.phase_duration'; +export const PUBLIC_PROCESSOR_PHASE_COUNT = 'aztec.public_processor.phase_count'; +export const PUBLIC_PROCESSOR_DEPLOY_BYTECODE_SIZE = 'aztec.public_processor.deploy_bytecode_size'; + +export const PUBLIC_EXECUTOR_SIMULATION_COUNT = 'aztec.public_executor.simulation_count'; +export const PUBLIC_EXECUTOR_SIMULATION_DURATION = 'aztec.public_executor.simulation_duration'; +export const PUBLIC_EXECUTION_SIMULATION_BYTECODE_SIZE = 'aztec.public_executor.simulation_bytecode_size'; + +export const PROVING_ORCHESTRATOR_BASE_ROLLUP_INPUTS_DURATION = + 'aztec.proving_orchestrator.base_rollup.inputs_duration'; + +export const PROVING_QUEUE_JOB_SIZE = 'aztec.proving_queue.job_size'; +export const PROVING_QUEUE_SIZE = 'aztec.proving_queue.size'; diff --git a/yarn-project/telemetry-client/src/otel.ts b/yarn-project/telemetry-client/src/otel.ts index 6c2a7864c51..8d1e5be21a4 100644 --- a/yarn-project/telemetry-client/src/otel.ts +++ b/yarn-project/telemetry-client/src/otel.ts @@ -1,7 +1,13 @@ import { type DebugLogger } from '@aztec/foundation/log'; -import { type Meter, type Tracer, type TracerProvider } from '@opentelemetry/api'; -import { DiagConsoleLogger, DiagLogLevel, diag } from '@opentelemetry/api'; +import { + DiagConsoleLogger, + DiagLogLevel, + type Meter, + type Tracer, + type TracerProvider, + diag, +} from '@opentelemetry/api'; import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'; import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; import { HostMetrics } from '@opentelemetry/host-metrics'; @@ -10,10 +16,13 @@ import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk import { BatchSpanProcessor, NodeTracerProvider } from '@opentelemetry/sdk-trace-node'; import { SEMRESATTRS_SERVICE_NAME, SEMRESATTRS_SERVICE_VERSION } from '@opentelemetry/semantic-conventions'; -import { type TelemetryClient } from './telemetry.js'; +import { NETWORK_ID } from './attributes.js'; +import { type Gauge, type TelemetryClient } from './telemetry.js'; export class OpenTelemetryClient implements TelemetryClient { hostMetrics: HostMetrics | undefined; + targetInfo: Gauge | undefined; + protected constructor( private resource: Resource, private meterProvider: MeterProvider, @@ -38,6 +47,14 @@ export class OpenTelemetryClient implements TelemetryClient { meterProvider: this.meterProvider, }); + // See these two links for more information on providing target information: + // https://opentelemetry.io/docs/specs/otel/compatibility/prometheus_and_openmetrics/#resource-attributes + // https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#supporting-target-metadata-in-both-push-based-and-pull-based-systems + this.targetInfo = this.meterProvider.getMeter('target').createGauge('target_info', { + description: 'Target information', + }); + this.targetInfo.record(1, this.resource.attributes); + this.hostMetrics.start(); } @@ -48,12 +65,14 @@ export class OpenTelemetryClient implements TelemetryClient { public static createAndStart( name: string, version: string, + networkIdentifier: string, collectorBaseUrl: URL, log: DebugLogger, ): OpenTelemetryClient { const resource = new Resource({ [SEMRESATTRS_SERVICE_NAME]: name, [SEMRESATTRS_SERVICE_VERSION]: version, + [NETWORK_ID]: networkIdentifier, }); const tracerProvider = new NodeTracerProvider({ diff --git a/yarn-project/telemetry-client/src/start.ts b/yarn-project/telemetry-client/src/start.ts index e28c2ab9c5d..79994571968 100644 --- a/yarn-project/telemetry-client/src/start.ts +++ b/yarn-project/telemetry-client/src/start.ts @@ -8,13 +8,20 @@ export interface TelemetryClientConfig { collectorBaseUrl?: URL; serviceName: string; serviceVersion: string; + networkId: string; } export function createAndStartTelemetryClient(config: TelemetryClientConfig): TelemetryClient { const log = createDebugLogger('aztec:telemetry-client'); if (config.collectorBaseUrl) { log.info('Using OpenTelemetry client'); - return OpenTelemetryClient.createAndStart(config.serviceName, config.serviceVersion, config.collectorBaseUrl, log); + return OpenTelemetryClient.createAndStart( + config.serviceName, + config.serviceVersion, + config.networkId, + config.collectorBaseUrl, + log, + ); } else { log.info('Using NoopTelemetryClient'); return new NoopTelemetryClient(); @@ -22,11 +29,17 @@ export function createAndStartTelemetryClient(config: TelemetryClientConfig): Te } export function getConfigEnvVars(): TelemetryClientConfig { - const { TEL_COLLECTOR_BASE_URL, TEL_SERVICE_NAME = 'aztec', TEL_SERVICE_VERSION = '0.0.0' } = process.env; + const { + TEL_COLLECTOR_BASE_URL, + TEL_SERVICE_NAME = 'aztec', + TEL_SERVICE_VERSION = '0.0.0', + TEL_NETWORK_ID = 'local', + } = process.env; return { collectorBaseUrl: TEL_COLLECTOR_BASE_URL ? new URL(TEL_COLLECTOR_BASE_URL) : undefined, serviceName: TEL_SERVICE_NAME, serviceVersion: TEL_SERVICE_VERSION, + networkId: TEL_NETWORK_ID, }; } diff --git a/yarn-project/txe/package.json b/yarn-project/txe/package.json index d01e249591e..ca291a0e2db 100644 --- a/yarn-project/txe/package.json +++ b/yarn-project/txe/package.json @@ -67,6 +67,7 @@ "@aztec/kv-store": "workspace:^", "@aztec/pxe": "workspace:^", "@aztec/simulator": "workspace:^", + "@aztec/telemetry-client": "workspace:^", "@aztec/types": "workspace:^", "@aztec/world-state": "workspace:^" }, diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index e904db3165b..96ff0160f37 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -68,6 +68,7 @@ import { toACVMWitness, witnessMapToFields, } from '@aztec/simulator'; +import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; import { type ContractInstance, type ContractInstanceWithAddress } from '@aztec/types/contracts'; import { MerkleTreeSnapshotOperationsFacade, type MerkleTrees } from '@aztec/world-state'; @@ -714,6 +715,7 @@ export class TXE implements TypedOracle { new ContractsDataSourcePublicDB(new TXEPublicContractDataSource(this)), new WorldStateDB(this.trees.asLatest()), header, + new NoopTelemetryClient(), ); const execution = new PublicExecutionRequest(targetContractAddress, callContext, args); diff --git a/yarn-project/txe/tsconfig.json b/yarn-project/txe/tsconfig.json index ec9add87a8b..fcd88cf390a 100644 --- a/yarn-project/txe/tsconfig.json +++ b/yarn-project/txe/tsconfig.json @@ -36,6 +36,9 @@ { "path": "../simulator" }, + { + "path": "../telemetry-client" + }, { "path": "../types" }, diff --git a/yarn-project/types/package.json b/yarn-project/types/package.json index b750c105ca8..4fe9e6e145b 100644 --- a/yarn-project/types/package.json +++ b/yarn-project/types/package.json @@ -10,7 +10,8 @@ "./contracts": "./dest/contracts/index.js", "./interfaces": "./dest/interfaces/index.js", "./membership": "./dest/sibling-path/index.js", - "./noir": "./dest/noir/index.js" + "./noir": "./dest/noir/index.js", + "./network": "./dest/network/index.js" }, "scripts": { "build": "yarn clean && yarn generate && tsc -b", diff --git a/yarn-project/types/src/network/index.ts b/yarn-project/types/src/network/index.ts new file mode 100644 index 00000000000..ebbecfbce28 --- /dev/null +++ b/yarn-project/types/src/network/index.ts @@ -0,0 +1,25 @@ +/** + * Deployed aztec networks & their names. + */ +export enum Network { + DEVNET = 'devnet', + PROVERNET = 'provernet', +} + +/** + * Map of basic contracts deployed for each network. + */ +export const L2BasicContractsMap = { + [Network.DEVNET]: { + devCoin: 'TokenContract', + devCoinBridge: 'TokenBridgeContract', + devCoinFpc: 'FPCContract', + counter: 'CounterContract', + }, + [Network.PROVERNET]: { + devCoin: 'TokenContract', + devCoinBridge: 'TokenBridgeContract', + devCoinFpc: 'FPCContract', + counter: 'CounterContract', + }, +}; diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 9ad29d4931d..6646b5b600d 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -217,6 +217,7 @@ __metadata: "@aztec/pxe": "workspace:^" "@aztec/telemetry-client": "workspace:^" "@aztec/txe": "workspace:^" + "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 "@types/jest": ^29.5.0 "@types/koa": ^2.13.6 @@ -1053,6 +1054,7 @@ __metadata: "@aztec/kv-store": "workspace:^" "@aztec/pxe": "workspace:^" "@aztec/simulator": "workspace:^" + "@aztec/telemetry-client": "workspace:^" "@aztec/types": "workspace:^" "@aztec/world-state": "workspace:^" "@jest/globals": ^29.5.0