From 2aa30eecb515c071cd19bd36c361b466815066ea Mon Sep 17 00:00:00 2001 From: EmilLuta Date: Mon, 6 Jan 2025 15:57:00 +0100 Subject: [PATCH 01/97] chore: Pin dependencies (#3424) During FFLONK commit, dependencies got unpinned. This PR repins the dependencies to declared versions. --- Cargo.toml | 14 +++++++------- prover/Cargo.toml | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fb98b0624795..21a1a342f1ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -228,18 +228,18 @@ tokio-stream = "0.1.16" # We *always* pin the latest version of protocol to disallow accidental changes in the execution logic. # However, for the historical version of protocol crates, we have lax requirements. Otherwise, # Bumping a crypto dependency like `boojum` would require us to republish all the historical packages. -circuit_encodings = "0.150.19" -circuit_sequencer_api = "0.150.19" -circuit_definitions = "0.150.19" -crypto_codegen = { package = "zksync_solidity_vk_codegen",version = "0.30.12" } -kzg = { package = "zksync_kzg", version = "0.150.19" } +circuit_encodings = "=0.150.19" +circuit_sequencer_api = "=0.150.19" +circuit_definitions = "=0.150.19" +crypto_codegen = { package = "zksync_solidity_vk_codegen",version = "=0.30.12" } +kzg = { package = "zksync_kzg", version = "=0.150.19" } zk_evm = { version = "=0.133.0" } zk_evm_1_3_1 = { package = "zk_evm", version = "0.131.0-rc.2" } zk_evm_1_3_3 = { package = "zk_evm", version = "0.133" } zk_evm_1_4_0 = { package = "zk_evm", version = "0.140" } zk_evm_1_4_1 = { package = "zk_evm", version = "0.141" } -zk_evm_1_5_0 = { package = "zk_evm", version = "0.150.19" } -fflonk = "0.30.12" +zk_evm_1_5_0 = { package = "zk_evm", version = "=0.150.19" } +fflonk = "=0.30.12" # New VM; pinned to a specific commit because of instability zksync_vm2 = { git = "https://github.com/matter-labs/vm2.git", rev = "457d8a7eea9093af9440662e33e598c13ba41633" } diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 7e571db3f025..aed8cbfdc723 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -73,8 +73,8 @@ circuit_sequencer_api = "=0.150.19" zkevm_test_harness = "=0.150.19" proof-compression-gpu = { package = "proof-compression", version = "=0.152.10"} fflonk-gpu = { package = "fflonk-cuda", version = "=0.152.10"} -fflonk = "0.30.12" -franklin-crypto = "0.30.12" +fflonk = "=0.30.12" +franklin-crypto = "=0.30.12" # GPU proving dependencies wrapper_prover = { package = "zksync-wrapper-prover", version = "=0.152.10"} From 36559cd93f78d89b6272b0dcec89d13a281b3ff9 Mon Sep 17 00:00:00 2001 From: D025 Date: Mon, 6 Jan 2025 17:28:08 +0200 Subject: [PATCH 02/97] chore: remove unused workflows (#3427) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Remove deprecated workflows ## Why ❔ Increase usability ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- .../build-contract-verifier-template.yml | 205 +++++++++---- .github/workflows/build-core-template.yml | 215 +++++++++----- .github/workflows/build-docker-from-tag.yml | 8 +- .github/workflows/build-prover-template.yml | 218 ++++++++------ .../build-witness-generator-template.yml | 179 +++++------ .github/workflows/ci.yml | 12 +- .../new-build-contract-verifier-template.yml | 270 ----------------- .github/workflows/new-build-core-template.yml | 281 ------------------ .../workflows/new-build-prover-template.yml | 225 -------------- .../new-build-witness-generator-template.yml | 138 --------- .github/workflows/release-test-stage.yml | 8 +- 11 files changed, 499 insertions(+), 1260 deletions(-) delete mode 100644 .github/workflows/new-build-contract-verifier-template.yml delete mode 100644 .github/workflows/new-build-core-template.yml delete mode 100644 .github/workflows/new-build-prover-template.yml delete mode 100644 .github/workflows/new-build-witness-generator-template.yml diff --git a/.github/workflows/build-contract-verifier-template.yml b/.github/workflows/build-contract-verifier-template.yml index 1481e542de57..7d75f81fb73c 100644 --- a/.github/workflows/build-contract-verifier-template.yml +++ b/.github/workflows/build-contract-verifier-template.yml @@ -13,44 +13,37 @@ on: description: "Optional suffix to override tag name generation" type: string required: false - action: - description: "Action with docker image" - type: string - default: "push" - required: false compilers: description: 'JSON of required compilers and their versions' type: string required: false default: '[{ "zksolc": ["1.3.14", "1.3.16", "1.3.17", "1.3.1", "1.3.7", "1.3.18", "1.3.19", "1.3.21"] } , { "zkvyper": ["1.3.13"] }]' -jobs: - build-images: - name: Build and Push Docker Images - env: - IMAGE_TAG_SUFFIX: ${{ inputs.image_tag_suffix }} - runs-on: ${{ fromJSON('["matterlabs-ci-runner-high-performance", "matterlabs-ci-runner-arm"]')[contains(matrix.platforms, 'arm')] }} - strategy: - matrix: - components: - - contract-verifier - - verified-sources-fetcher - platforms: - - linux/amd64 + action: + type: string + default: non-push + required: false +jobs: + prepare-contracts: + name: Prepare contracts + runs-on: matterlabs-ci-runner-high-performance steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: "recursive" - - name: setup-env + - name: Prepare ENV + shell: bash run: | echo ZKSYNC_HOME=$(pwd) >> $GITHUB_ENV echo CI=1 >> $GITHUB_ENV echo $(pwd)/bin >> $GITHUB_PATH + echo $HOME/.local/bin >> $GITHUB_PATH echo CI=1 >> .env echo IN_DOCKER=1 >> .env - name: Download contracts + shell: bash run: | commit_sha=$(git submodule status contracts | awk '{print $1}' | tr -d '-') page=1 @@ -80,8 +73,41 @@ jobs: tar -C ./contracts -zxf l2-contracts.tar.gz tar -C ./contracts -zxf system-contracts.tar.gz - - name: pre-download compilers + - name: Install Apt dependencies if: env.BUILD_CONTRACTS == 'true' + shell: bash + run: | + sudo apt-get update && sudo apt-get install -y libssl-dev pkg-config + + - name: Install Node + if: env.BUILD_CONTRACTS == 'true' + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + with: + node-version: 20 + cache: 'npm' + + - name: Install Yarn + if: env.BUILD_CONTRACTS == 'true' + run: npm install -g yarn + + - name: Setup rust + if: env.BUILD_CONTRACTS == 'true' + uses: actions-rust-lang/setup-rust-toolchain@1fbea72663f6d4c03efaab13560c8a24cfd2a7cc # v1.9.0 + with: + toolchain: nightly-2024-08-01 + + - name: Install foundry-zksync + if: env.BUILD_CONTRACTS == 'true' + run: | + mkdir ./foundry-zksync + curl -LO https://github.com/matter-labs/foundry-zksync/releases/download/nightly-15bec2f861b3b4c71e58f85e2b2c9dd722585aa8/foundry_nightly_linux_amd64.tar.gz + tar zxf foundry_nightly_linux_amd64.tar.gz -C ./foundry-zksync + chmod +x ./foundry-zksync/forge ./foundry-zksync/cast + echo "$PWD/foundry-zksync" >> $GITHUB_PATH + + - name: Pre-download compilers + if: env.BUILD_CONTRACTS == 'true' + shell: bash run: | # Download needed versions of vyper compiler # Not sanitized due to unconventional path and tags @@ -98,59 +124,116 @@ jobs: chmod +x "./hardhat-nodejs/compilers-v2/$compiler/${compiler}-v${version}" done - - name: start-services + - name: Install zkstack + if: env.BUILD_CONTRACTS == 'true' + run: | + ./zkstack_cli/zkstackup/install --path ./zkstack_cli/zkstackup/zkstackup + zkstackup --local || true + + - name: build contracts + if: env.BUILD_CONTRACTS == 'true' + shell: bash run: | - echo "IMAGE_TAG_SUFFIX=${{ env.IMAGE_TAG_SUFFIX }}" >> .env - run_retried docker compose pull zk postgres - docker compose up -d zk postgres - ci_run pre_download_compilers.sh - ci_run sccache --start-server + cp etc/tokens/{test,localhost}.json + zkstack dev contracts + + - name: Upload contracts + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + with: + name: contacts-verifier + path: | + ./contracts + + build-images: + name: Build and Push Docker Images + needs: prepare-contracts + runs-on: ${{ fromJSON('["matterlabs-ci-runner-high-performance", "matterlabs-ci-runner-arm"]')[contains(matrix.platforms, 'arm')] }} + strategy: + matrix: + components: + - contract-verifier + - verified-sources-fetcher + platforms: + - linux/amd64 + + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + submodules: "recursive" - - name: init + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 + + - name: Setup env + shell: bash run: | - ci_run git config --global --add safe.directory /usr/src/zksync - ci_run git config --global --add safe.directory /usr/src/zksync/sdk/binaryen - ci_run git config --global --add safe.directory /usr/src/zksync/contracts/system-contracts - ci_run git config --global --add safe.directory /usr/src/zksync/contracts - ci_run ./bin/zk || true - ci_run run_retried curl -LO https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2\^26.key - - - name: install zkstack + echo ZKSYNC_HOME=$(pwd) >> $GITHUB_ENV + echo CI=1 >> $GITHUB_ENV + echo $(pwd)/bin >> $GITHUB_PATH + echo CI=1 >> .env + echo IN_DOCKER=1 >> .env + + - name: Download setup key + shell: bash run: | - ci_run ./zkstack_cli/zkstackup/install -g --path ./zkstack_cli/zkstackup/zkstackup || true - ci_run zkstackup -g --local + if [ -f "/setup_2^26.key" ]; then + cp '/setup_2^26.key' './setup_2^26.key' + else + run_retried curl -LO https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2\^26.key + fi - - name: build contracts - if: env.BUILD_CONTRACTS == 'true' + - name: Set env vars + shell: bash run: | - ci_run cp etc/tokens/{test,localhost}.json - ci_run zkstack dev contracts + echo PLATFORM=$(echo ${{ matrix.platforms }} | tr '/' '-') >> $GITHUB_ENV + echo IMAGE_TAG_SHA=$(git rev-parse --short HEAD) >> $GITHUB_ENV + # Support for custom tag suffix + if [ -n "${{ inputs.image_tag_suffix }}" ]; then + echo IMAGE_TAG_SHA_TS="${{ inputs.image_tag_suffix }}" >> $GITHUB_ENV + else + echo IMAGE_TAG_SHA_TS=$(git rev-parse --short HEAD)-$(date +%s) >> $GITHUB_ENV + fi + + - name: Download contracts + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + with: + name: contacts-verifier + path: | + ./contracts - - name: Login to Docker registries + - name: login to Docker registries if: ${{ inputs.action == 'push' }} + shell: bash run: | - ci_run docker login -u ${{ secrets.DOCKERHUB_USER }} -p ${{ secrets.DOCKERHUB_TOKEN }} - ci_run gcloud auth configure-docker us-docker.pkg.dev -q - - - name: update-images - env: - DOCKER_ACTION: ${{ inputs.action }} - COMPONENT: ${{ matrix.components }} - PLATFORM: ${{ matrix.platforms }} - run: | - ci_run run_retried rustup default nightly-2024-05-07 - platform=$(echo $PLATFORM | tr '/' '-') - ci_run zk docker $DOCKER_ACTION --custom-tag=${IMAGE_TAG_SUFFIX} --platform=${PLATFORM} $COMPONENT - - name: Show sccache stats - if: always() + docker login -u ${{ secrets.DOCKERHUB_USER }} -p ${{ secrets.DOCKERHUB_TOKEN }} + gcloud auth configure-docker us-docker.pkg.dev -q + + - name: Build and push + uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 + with: + context: . + load: true + platforms: ${{ matrix.platforms }} + file: docker/${{ matrix.components }}/Dockerfile + build-args: | + SCCACHE_GCS_BUCKET=matterlabs-infra-sccache-storage + SCCACHE_GCS_SERVICE_ACCOUNT=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com + SCCACHE_GCS_RW_MODE=READ_WRITE + RUSTC_WRAPPER=sccache + tags: | + us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} + matterlabs/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} + + - name: Push docker image + if: ${{ inputs.action == 'push' }} run: | - ci_run sccache --show-stats || true - ci_run cat /tmp/sccache_log.txt || true + docker push us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} + docker push matterlabs/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} + create_manifest: name: Create release manifest - # TODO: After migraton switch to CI - runs-on: matterlabs-default-infra-runners + runs-on: matterlabs-ci-runner needs: build-images if: ${{ inputs.action == 'push' }} strategy: diff --git a/.github/workflows/build-core-template.yml b/.github/workflows/build-core-template.yml index 15d4432191dd..557d8455a31d 100644 --- a/.github/workflows/build-core-template.yml +++ b/.github/workflows/build-core-template.yml @@ -13,11 +13,6 @@ on: description: "Optional suffix to override tag name generation" type: string required: false - action: - description: "Action with docker image" - type: string - default: "push" - required: false compilers: description: 'JSON of required compilers and their versions' type: string @@ -28,38 +23,32 @@ on: type: boolean required: false default: false -jobs: - build-images: - name: Build and Push Docker Images - env: - IMAGE_TAG_SUFFIX: ${{ inputs.image_tag_suffix }}${{ (inputs.en_alpha_release && matrix.components == 'external-node') && '-alpha' || '' }} - runs-on: ${{ fromJSON('["matterlabs-ci-runner-high-performance", "matterlabs-ci-runner-arm"]')[contains(matrix.platforms, 'arm')] }} - strategy: - matrix: - components: - - server-v2 - - external-node - - snapshots-creator - platforms: - - linux/amd64 - include: - - components: external-node - platforms: linux/arm64 + action: + type: string + required: false + default: "do nothing" +jobs: + prepare-contracts: + name: Prepare contracts + runs-on: matterlabs-ci-runner-high-performance steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: "recursive" - - name: setup-env + - name: Prepare ENV + shell: bash run: | echo ZKSYNC_HOME=$(pwd) >> $GITHUB_ENV echo CI=1 >> $GITHUB_ENV echo $(pwd)/bin >> $GITHUB_PATH + echo $HOME/.local/bin >> $GITHUB_PATH echo CI=1 >> .env echo IN_DOCKER=1 >> .env - name: Download contracts + shell: bash run: | commit_sha=$(git submodule status contracts | awk '{print $1}' | tr -d '-') page=1 @@ -71,15 +60,11 @@ jobs: if [ $(jq length <<<"$tags") -eq 0 ]; then echo "No tag found on all pages." echo "BUILD_CONTRACTS=true" >> "$GITHUB_ENV" - # TODO Remove it when we migrate to foundry inside contracts repository - mkdir -p contracts/l1-contracts/artifacts/ exit 0 fi filtered_tag=$(jq -r --arg commit_sha "$commit_sha" 'map(select(.commit.sha == $commit_sha)) | .[].name' <<<"$tags") if [[ ! -z "$filtered_tag" ]]; then echo "BUILD_CONTRACTS=false" >> "$GITHUB_ENV" - # TODO Remove it when we migrate to foundry inside contracts repository - mkdir -p contracts/l1-contracts/out break fi ((page++)) @@ -93,8 +78,41 @@ jobs: tar -C ./contracts -zxf l2-contracts.tar.gz tar -C ./contracts -zxf system-contracts.tar.gz - - name: pre-download compilers + - name: Install Apt dependencies + if: env.BUILD_CONTRACTS == 'true' + shell: bash + run: | + sudo apt-get update && sudo apt-get install -y libssl-dev pkg-config + + - name: Install Node + if: env.BUILD_CONTRACTS == 'true' + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + with: + node-version: 20 + cache: 'npm' + + - name: Install Yarn + if: env.BUILD_CONTRACTS == 'true' + run: npm install -g yarn + + - name: Setup rust + if: env.BUILD_CONTRACTS == 'true' + uses: actions-rust-lang/setup-rust-toolchain@1fbea72663f6d4c03efaab13560c8a24cfd2a7cc # v1.9.0 + with: + toolchain: nightly-2024-08-01 + + - name: Install foundry-zksync + if: env.BUILD_CONTRACTS == 'true' + run: | + mkdir ./foundry-zksync + curl -LO https://github.com/matter-labs/foundry-zksync/releases/download/nightly-15bec2f861b3b4c71e58f85e2b2c9dd722585aa8/foundry_nightly_linux_amd64.tar.gz + tar zxf foundry_nightly_linux_amd64.tar.gz -C ./foundry-zksync + chmod +x ./foundry-zksync/forge ./foundry-zksync/cast + echo "$PWD/foundry-zksync" >> $GITHUB_PATH + + - name: Pre-download compilers if: env.BUILD_CONTRACTS == 'true' + shell: bash run: | # Download needed versions of vyper compiler # Not sanitized due to unconventional path and tags @@ -111,60 +129,119 @@ jobs: chmod +x "./hardhat-nodejs/compilers-v2/$compiler/${compiler}-v${version}" done - - name: start-services - run: | - echo "IMAGE_TAG_SUFFIX=${{ env.IMAGE_TAG_SUFFIX }}" >> .env - run_retried docker compose pull zk postgres - docker compose up -d zk postgres - ci_run pre_download_compilers.sh - ci_run sccache --start-server - - - name: init - run: | - ci_run git config --global --add safe.directory /usr/src/zksync - ci_run git config --global --add safe.directory /usr/src/zksync/sdk/binaryen - ci_run git config --global --add safe.directory /usr/src/zksync/contracts/system-contracts - ci_run git config --global --add safe.directory /usr/src/zksync/contracts - ci_run ./bin/zk || true - ci_run run_retried curl -LO https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2\^26.key - - name: Install zkstack if: env.BUILD_CONTRACTS == 'true' run: | - ci_run ./zkstack_cli/zkstackup/install -g --path ./zkstack_cli/zkstackup/zkstackup || true - ci_run zkstackup -g --local + ./zkstack_cli/zkstackup/install --path ./zkstack_cli/zkstackup/zkstackup + zkstackup --local || true - name: build contracts if: env.BUILD_CONTRACTS == 'true' + shell: bash run: | - ci_run cp etc/tokens/{test,localhost}.json - ci_run zkstack dev contracts --system-contracts --l1-contracts --l2-contracts + cp etc/tokens/{test,localhost}.json + zkstack dev contracts - - name: Login to Docker registries - if: ${{ inputs.action == 'push' }} + - name: Upload contracts + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + with: + name: contacts + path: | + ./contracts + + build-images: + name: Build and Push Docker Images + needs: prepare-contracts + env: + IMAGE_TAG_SUFFIX: ${{ inputs.image_tag_suffix }}${{ (inputs.en_alpha_release && matrix.components == 'external-node') && '-alpha' || '' }} + runs-on: ${{ fromJSON('["matterlabs-ci-runner-high-performance", "matterlabs-ci-runner-arm"]')[contains(matrix.platforms, 'arm')] }} + strategy: + matrix: + components: + - server-v2 + - external-node + - snapshots-creator + platforms: + - linux/amd64 + include: + - components: external-node + platforms: linux/arm64 + + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + submodules: "recursive" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 + + - name: Setup env + shell: bash run: | - ci_run docker login -u ${{ secrets.DOCKERHUB_USER }} -p ${{ secrets.DOCKERHUB_TOKEN }} - ci_run gcloud auth configure-docker us-docker.pkg.dev -q - - - name: update-images - env: - DOCKER_ACTION: ${{ inputs.action }} - COMPONENT: ${{ matrix.components }} - PLATFORM: ${{ matrix.platforms }} + echo ZKSYNC_HOME=$(pwd) >> $GITHUB_ENV + echo CI=1 >> $GITHUB_ENV + echo $(pwd)/bin >> $GITHUB_PATH + echo CI=1 >> .env + echo IN_DOCKER=1 >> .env + + - name: Download setup key + shell: bash + run: | + if [ -f "/setup_2^26.key" ]; then + cp '/setup_2^26.key' './setup_2^26.key' + else + run_retried curl -LO https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2\^26.key + fi + + - name: Set env vars + shell: bash + run: | + echo PLATFORM=$(echo ${{ matrix.platforms }} | tr '/' '-') >> $GITHUB_ENV + if [ -n "${{ inputs.image_tag_suffix }}" ]; then + echo IMAGE_TAG_SHA_TS="${{ env.IMAGE_TAG_SUFFIX }}" >> $GITHUB_ENV + else + echo IMAGE_TAG_SHA_TS=$(git rev-parse --short HEAD)-$(date +%s) >> $GITHUB_ENV + fi + + - name: Download contracts + uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + with: + name: contacts + path: | + ./contracts + + - name: login to Docker registries + if: ${{ inputs.action == 'push' }} + shell: bash run: | - ci_run run_retried rustup default nightly-2024-05-07 - platform=$(echo $PLATFORM | tr '/' '-') - ci_run zk docker $DOCKER_ACTION --custom-tag=${IMAGE_TAG_SUFFIX} --platform=${PLATFORM} $COMPONENT - - name: Show sccache stats - if: always() + docker login -u ${{ secrets.DOCKERHUB_USER }} -p ${{ secrets.DOCKERHUB_TOKEN }} + gcloud auth configure-docker us-docker.pkg.dev -q + + - name: Build docker image + uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 + with: + context: . + load: true + platforms: ${{ matrix.platforms }} + file: docker/${{ matrix.components }}/Dockerfile + build-args: | + SCCACHE_GCS_BUCKET=matterlabs-infra-sccache-storage + SCCACHE_GCS_SERVICE_ACCOUNT=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com + SCCACHE_GCS_RW_MODE=READ_WRITE + RUSTC_WRAPPER=sccache + tags: | + us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} + matterlabs/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} + + - name: Push docker image + if: ${{ inputs.action == 'push' }} run: | - ci_run sccache --show-stats || true - ci_run cat /tmp/sccache_log.txt || true + docker push us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} + docker push matterlabs/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} create_manifest: name: Create release manifest - # TODO: After migraton switch to CI - runs-on: matterlabs-default-infra-runners + runs-on: matterlabs-ci-runner needs: build-images if: ${{ inputs.action == 'push' }} strategy: diff --git a/.github/workflows/build-docker-from-tag.yml b/.github/workflows/build-docker-from-tag.yml index e48539c90738..53522dd9b678 100644 --- a/.github/workflows/build-docker-from-tag.yml +++ b/.github/workflows/build-docker-from-tag.yml @@ -49,7 +49,7 @@ jobs: build-push-core-images: name: Build and push image needs: [setup] - uses: ./.github/workflows/new-build-core-template.yml + uses: ./.github/workflows/build-core-template.yml if: contains(github.ref_name, 'core') secrets: DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }} @@ -74,7 +74,7 @@ jobs: build-push-contract-verifier: name: Build and push image needs: [setup] - uses: ./.github/workflows/new-build-contract-verifier-template.yml + uses: ./.github/workflows/build-contract-verifier-template.yml if: contains(github.ref_name, 'contract_verifier') secrets: DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }} @@ -86,7 +86,7 @@ jobs: build-push-prover-images: name: Build and push image needs: [setup] - uses: ./.github/workflows/new-build-prover-template.yml + uses: ./.github/workflows/build-prover-template.yml if: contains(github.ref_name, 'prover') with: image_tag_suffix: ${{ needs.setup.outputs.image_tag_suffix }} @@ -99,7 +99,7 @@ jobs: build-push-witness-generator-image-avx512: name: Build and push image needs: [setup] - uses: ./.github/workflows/new-build-witness-generator-template.yml + uses: ./.github/workflows/build-witness-generator-template.yml if: contains(github.ref_name, 'prover') with: image_tag_suffix: ${{ needs.setup.outputs.image_tag_suffix }}-avx512 diff --git a/.github/workflows/build-prover-template.yml b/.github/workflows/build-prover-template.yml index 762ec496943c..3a721e4425a8 100644 --- a/.github/workflows/build-prover-template.yml +++ b/.github/workflows/build-prover-template.yml @@ -32,38 +32,84 @@ on: type: string default: "75;80;89" required: false + # Details: https://arnon.dk/matching-sm-architectures-arch-and-gencode-for-various-nvidia-cards/ + # L4: 89 + # T4: 75 + # A100: 80 outputs: protocol_version: description: "Protocol version of the binary" - value: ${{ jobs.build-images.outputs.protocol_version }} + value: ${{ jobs.get-protocol-version.outputs.protocol_version }} jobs: + get-protocol-version: + name: Get protocol version + runs-on: [matterlabs-ci-runner-high-performance] + outputs: + protocol_version: ${{ steps.protocolversion.outputs.protocol_version }} + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + submodules: "recursive" + + - name: setup-env + run: | + echo ZKSYNC_HOME=$(pwd) >> $GITHUB_ENV + echo CI=1 >> $GITHUB_ENV + echo $(pwd)/bin >> $GITHUB_PATH + echo CI=1 >> .env + echo IN_DOCKER=1 >> .env + + - name: setup rust + uses: actions-rust-lang/setup-rust-toolchain@1fbea72663f6d4c03efaab13560c8a24cfd2a7cc # v1.9.0 + with: + toolchain: nightly-2024-08-01 + + - name: Prepare sccache-cache env vars + shell: bash + run: | + echo SCCACHE_GCS_BUCKET=matterlabs-infra-sccache-storage >> $GITHUB_ENV + echo SCCACHE_GCS_SERVICE_ACCOUNT=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com >> $GITHUB_ENV + echo SCCACHE_ERROR_LOG=/tmp/sccache_log.txt >> $GITHUB_ENV + echo SCCACHE_GCS_RW_MODE=READ_WRITE >> $GITHUB_ENV + echo RUSTC_WRAPPER=sccache >> $GITHUB_ENV + + - name: protocol-version + id: protocolversion + # TODO: use -C flag, when it will become stable. + shell: bash + run: | + cd prover + cargo build --release --bin prover_version + PPV=$(target/release/prover_version) + echo Protocol version is ${PPV} + echo "protocol_version=${PPV}" >> $GITHUB_OUTPUT + build-images: name: Build and Push Docker Images + needs: get-protocol-version env: - IMAGE_TAG_SUFFIX: ${{ inputs.image_tag_suffix }} - RUNNER_COMPOSE_FILE: "docker-compose-runner-nightly.yml" - ERA_BELLMAN_CUDA_RELEASE: ${{ inputs.ERA_BELLMAN_CUDA_RELEASE }} - CUDA_ARCH: ${{ inputs.CUDA_ARCH }} + PROTOCOL_VERSION: ${{ needs.get-protocol-version.outputs.protocol_version }} runs-on: [matterlabs-ci-runner-high-performance] strategy: matrix: - component: + components: - witness-generator - prover-gpu-fri - witness-vector-generator - - circuit-prover-gpu - prover-fri-gateway - prover-job-monitor - proof-fri-gpu-compressor - prover-autoscaler - outputs: - protocol_version: ${{ steps.protocolversion.outputs.protocol_version }} + - circuit-prover-gpu steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: "recursive" + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 + - name: setup-env run: | echo ZKSYNC_HOME=$(pwd) >> $GITHUB_ENV @@ -72,107 +118,79 @@ jobs: echo CI=1 >> .env echo IN_DOCKER=1 >> .env - - name: start-services - run: | - echo "IMAGE_TAG_SUFFIX=${{ env.IMAGE_TAG_SUFFIX }}" >> .env - run_retried docker compose pull zk postgres - docker compose up -d zk postgres - ci_run sccache --start-server - - - name: init + - name: Set env vars + shell: bash run: | - ci_run git config --global --add safe.directory /usr/src/zksync - ci_run git config --global --add safe.directory /usr/src/zksync/contracts/system-contracts - ci_run git config --global --add safe.directory /usr/src/zksync/contracts - ci_run zk + # Support for custom tag suffix + if [ -n "${{ inputs.image_tag_suffix }}" ]; then + echo IMAGE_TAG_SHA_TS="${{ inputs.image_tag_suffix }}" >> $GITHUB_ENV + else + echo IMAGE_TAG_SHA_TS=$(git rev-parse --short HEAD)-$(date +%s) >> $GITHUB_ENV + fi - name: download CRS for GPU compressor - if: matrix.component == 'proof-fri-gpu-compressor' - run: | - ci_run run_retried curl -LO https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2\^24.key - - - name: login to Docker registries - if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) + if: matrix.components == 'proof-fri-gpu-compressor' run: | - ci_run docker login -u ${{ secrets.DOCKERHUB_USER }} -p ${{ secrets.DOCKERHUB_TOKEN }} - ci_run gcloud auth configure-docker us-docker.pkg.dev -q - + run_retried curl -LO https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2\^24.key # We need to run this only when ERA_BELLMAN_CUDA_RELEASE is not available # In our case it happens only when PR is created from fork - name: Wait for runner IP to be not rate-limited against GH API - if: inputs.is_pr_from_fork == true - run: | - api_endpoint="https://api.github.com/users/zksync-era-bot" - wait_time=60 - max_retries=60 - retry_count=0 - - while [[ $retry_count -lt $max_retries ]]; do - response=$(run_retried curl -s -w "%{http_code}" -o temp.json "$api_endpoint") - http_code=$(echo "$response" | tail -n1) - - if [[ "$http_code" == "200" ]]; then - echo "Request successful. Not rate-limited." - cat temp.json - rm temp.json - exit 0 - elif [[ "$http_code" == "403" ]]; then - rate_limit_exceeded=$(jq -r '.message' temp.json | grep -i "API rate limit exceeded") - if [[ -n "$rate_limit_exceeded" ]]; then - retry_count=$((retry_count+1)) - echo "API rate limit exceeded. Retry $retry_count of $max_retries. Retrying in $wait_time seconds..." - sleep $wait_time - else - echo "Request failed with HTTP status $http_code." - cat temp.json - rm temp.json - exit 1 - fi - else - echo "Request failed with HTTP status $http_code." - cat temp.json - rm temp.json - exit 1 - fi - done - - echo "Reached the maximum number of retries ($max_retries). Exiting." - rm temp.json - exit 1 + if: ( inputs.is_pr_from_fork == true && matrix.components == 'proof-fri-gpu-compressor' ) + run: ./.github/scripts/rate_limit_check.sh - - name: protocol-version - id: protocolversion - # TODO: use -C flag, when it will become stable. + - name: Hack to set env vars inside docker container shell: bash run: | - ci_run bash -c "cd prover && cargo build --release --bin prover_version" - PPV=$(ci_run prover/target/release/prover_version) - echo Protocol version is ${PPV} - echo "protocol_version=${PPV}" >> $GITHUB_OUTPUT - echo "PROTOCOL_VERSION=${PPV}" >> $GITHUB_ENV + sed -i '/^FROM matterlabs\/zksync-build-base:latest as builder/a ENV SCCACHE_GCS_BUCKET=matterlabs-infra-sccache-storage\nENV SCCACHE_GCS_SERVICE_ACCOUNT=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com\nENV SCCACHE_GCS_RW_MODE=READ_WRITE\nENV RUSTC_WRAPPER=sccache' ./docker/${{ matrix.components }}/Dockerfile + #TODO: remove AS version =) + sed -i '/^FROM matterlabs\/zksync-build-base:latest AS builder/a ENV SCCACHE_GCS_BUCKET=matterlabs-infra-sccache-storage\nENV SCCACHE_GCS_SERVICE_ACCOUNT=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com\nENV SCCACHE_GCS_RW_MODE=READ_WRITE\nENV RUSTC_WRAPPER=sccache' ./docker/${{ matrix.components }}/Dockerfile + cat ./docker/${{ matrix.components }}/Dockerfile - - name: update-images - env: - DOCKER_ACTION: ${{ inputs.action }} - COMPONENT: ${{ matrix.component }} + - name: login to Docker registries + if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) + shell: bash run: | - PASSED_ENV_VARS="ERA_BELLMAN_CUDA_RELEASE,CUDA_ARCH,PROTOCOL_VERSION" \ - ci_run zk docker $DOCKER_ACTION $COMPONENT + docker login -u ${{ secrets.DOCKERHUB_USER }} -p ${{ secrets.DOCKERHUB_TOKEN }} + gcloud auth configure-docker us-docker.pkg.dev -q - - name: Show sccache stats - if: always() + - name: Build and push + uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 + with: + context: . + load: true + build-args: | + CUDA_ARCH=${{ inputs.CUDA_ARCH }} + SCCACHE_GCS_BUCKET=matterlabs-infra-sccache-storage + SCCACHE_GCS_SERVICE_ACCOUNT=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com + SCCACHE_GCS_RW_MODE=READ_WRITE + RUSTC_WRAPPER=sccache + PROTOCOL_VERSION=${{ env.PROTOCOL_VERSION }} + ERA_BELLMAN_CUDA_RELEASE=${{ inputs.ERA_BELLMAN_CUDA_RELEASE }} + file: docker/${{ matrix.components }}/Dockerfile + tags: | + us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} + matterlabs/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} + us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} + matterlabs/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} + us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:latest + matterlabs/${{ matrix.components }}:latest + + - name: Push docker image + if: ${{ inputs.action == 'push' }} run: | - ci_run sccache --show-stats || true - ci_run cat /tmp/sccache_log.txt || true + docker push us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} + docker push matterlabs/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} + docker push us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} + docker push matterlabs/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} + docker push us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:latest + docker push matterlabs/${{ matrix.components }}:latest copy-images: name: Copy images between docker registries - needs: build-images + needs: [build-images, get-protocol-version] env: - IMAGE_TAG_SUFFIX: ${{ inputs.image_tag_suffix }} - PROTOCOL_VERSION: ${{ needs.build-images.outputs.protocol_version }} - # TODO: After migraton switch to CI - runs-on: matterlabs-default-infra-runners + PROTOCOL_VERSION: ${{ needs.get-protocol-version.outputs.protocol_version }} + runs-on: matterlabs-ci-runner if: ${{ inputs.action == 'push' }} strategy: matrix: @@ -190,12 +208,18 @@ jobs: run: | gcloud auth print-access-token --lifetime=7200 --impersonate-service-account=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com | docker login -u oauth2accesstoken --password-stdin https://asia-docker.pkg.dev docker buildx imagetools create \ - --tag asia-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ needs.build-images.outputs.protocol_version }}-${{ inputs.image_tag_suffix }} \ - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ needs.build-images.outputs.protocol_version }}-${{ inputs.image_tag_suffix }} + --tag asia-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ inputs.image_tag_suffix }} \ + us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ inputs.image_tag_suffix }} + docker buildx imagetools create \ + --tag asia-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ inputs.image_tag_suffix }} \ + us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ inputs.image_tag_suffix }} - name: Login and push to Europe GAR run: | gcloud auth print-access-token --lifetime=7200 --impersonate-service-account=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com | docker login -u oauth2accesstoken --password-stdin https://europe-docker.pkg.dev docker buildx imagetools create \ - --tag europe-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ needs.build-images.outputs.protocol_version }}-${{ inputs.image_tag_suffix }} \ - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ needs.build-images.outputs.protocol_version }}-${{ inputs.image_tag_suffix }} + --tag europe-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ inputs.image_tag_suffix }} \ + us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ inputs.image_tag_suffix }} + docker buildx imagetools create \ + --tag europe-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ inputs.image_tag_suffix }} \ + us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ inputs.image_tag_suffix }} diff --git a/.github/workflows/build-witness-generator-template.yml b/.github/workflows/build-witness-generator-template.yml index 95053b89d3d8..a96d217da832 100644 --- a/.github/workflows/build-witness-generator-template.yml +++ b/.github/workflows/build-witness-generator-template.yml @@ -18,14 +18,8 @@ on: type: string required: false action: - description: "Action with docker image" type: string - default: "push" - required: false - is_pr_from_fork: - description: "Indicates whether the workflow is invoked from a PR created from fork" - type: boolean - default: false + default: non-push required: false WITNESS_GENERATOR_RUST_FLAGS: description: "Rust flags for witness_generator compilation" @@ -35,26 +29,16 @@ on: outputs: protocol_version: description: "Protocol version of the binary" - value: ${{ jobs.build-images.outputs.protocol_version }} + value: ${{ jobs.get-protocol-version.outputs.protocol_version }} jobs: - build-images: - name: Build and Push Docker Images - env: - IMAGE_TAG_SUFFIX: ${{ inputs.image_tag_suffix }} - RUNNER_COMPOSE_FILE: "docker-compose-runner-nightly.yml" - ERA_BELLMAN_CUDA_RELEASE: ${{ inputs.ERA_BELLMAN_CUDA_RELEASE }} - WITNESS_GENERATOR_RUST_FLAGS: ${{ inputs.WITNESS_GENERATOR_RUST_FLAGS }} - ZKSYNC_USE_CUDA_STUBS: true - runs-on: [matterlabs-ci-runner-c3d] - strategy: - matrix: - component: - - witness-generator + get-protocol-version: + name: Get protocol version + runs-on: [matterlabs-ci-runner-high-performance] outputs: protocol_version: ${{ steps.protocolversion.outputs.protocol_version }} steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4 + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: submodules: "recursive" @@ -66,100 +50,89 @@ jobs: echo CI=1 >> .env echo IN_DOCKER=1 >> .env - - name: start-services - run: | - echo "IMAGE_TAG_SUFFIX=${{ env.IMAGE_TAG_SUFFIX }}" >> .env - run_retried docker compose pull zk postgres - docker compose up -d zk postgres - ci_run sccache --start-server - - - name: init - run: | - ci_run git config --global --add safe.directory /usr/src/zksync - ci_run git config --global --add safe.directory /usr/src/zksync/contracts/system-contracts - ci_run git config --global --add safe.directory /usr/src/zksync/contracts - ci_run zk - - - name: download CRS for GPU compressor - if: matrix.component == 'proof-fri-gpu-compressor' - run: | - ci_run run_retried curl -LO https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2\^24.key - - - name: login to Docker registries - if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) - run: | - ci_run docker login -u ${{ secrets.DOCKERHUB_USER }} -p ${{ secrets.DOCKERHUB_TOKEN }} - ci_run gcloud auth configure-docker us-docker.pkg.dev -q + - name: setup rust + uses: actions-rust-lang/setup-rust-toolchain@1fbea72663f6d4c03efaab13560c8a24cfd2a7cc # v1.9.0 + with: + toolchain: nightly-2024-08-01 - # We need to run this only when ERA_BELLMAN_CUDA_RELEASE is not available - # In our case it happens only when PR is created from fork - - name: Wait for runner IP to be not rate-limited against GH API - if: inputs.is_pr_from_fork == true + - name: Prepare sccache-cache env vars + shell: bash run: | - api_endpoint="https://api.github.com/users/zksync-era-bot" - wait_time=60 - max_retries=60 - retry_count=0 - - while [[ $retry_count -lt $max_retries ]]; do - response=$(run_retried curl -s -w "%{http_code}" -o temp.json "$api_endpoint") - http_code=$(echo "$response" | tail -n1) - - if [[ "$http_code" == "200" ]]; then - echo "Request successful. Not rate-limited." - cat temp.json - rm temp.json - exit 0 - elif [[ "$http_code" == "403" ]]; then - rate_limit_exceeded=$(jq -r '.message' temp.json | grep -i "API rate limit exceeded") - if [[ -n "$rate_limit_exceeded" ]]; then - retry_count=$((retry_count+1)) - echo "API rate limit exceeded. Retry $retry_count of $max_retries. Retrying in $wait_time seconds..." - sleep $wait_time - else - echo "Request failed with HTTP status $http_code." - cat temp.json - rm temp.json - exit 1 - fi - else - echo "Request failed with HTTP status $http_code." - cat temp.json - rm temp.json - exit 1 - fi - done - - echo "Reached the maximum number of retries ($max_retries). Exiting." - rm temp.json - exit 1 + echo SCCACHE_GCS_BUCKET=matterlabs-infra-sccache-storage >> $GITHUB_ENV + echo SCCACHE_GCS_SERVICE_ACCOUNT=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com >> $GITHUB_ENV + echo SCCACHE_ERROR_LOG=/tmp/sccache_log.txt >> $GITHUB_ENV + echo SCCACHE_GCS_RW_MODE=READ_WRITE >> $GITHUB_ENV + echo RUSTC_WRAPPER=sccache >> $GITHUB_ENV - name: protocol-version id: protocolversion # TODO: use -C flag, when it will become stable. shell: bash run: | - ci_run bash -c "cd prover && cargo build --release --bin prover_version" - PPV=$(ci_run prover/target/release/prover_version) + cd prover + cargo build --release --bin prover_version + PPV=$(target/release/prover_version) echo Protocol version is ${PPV} echo "protocol_version=${PPV}" >> $GITHUB_OUTPUT - echo "PROTOCOL_VERSION=${PPV}" >> $GITHUB_ENV - - name: setup-rust-flags-env - if: matrix.component == 'witness-generator' + build-images: + name: Build and Push Docker Images + needs: get-protocol-version + env: + PROTOCOL_VERSION: ${{ needs.get-protocol-version.outputs.protocol_version }} + runs-on: [matterlabs-ci-runner-c3d] + strategy: + matrix: + components: + - witness-generator + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + with: + submodules: "recursive" + + - name: setup-env run: | - echo RUST_FLAGS="${{ env.WITNESS_GENERATOR_RUST_FLAGS }}" >> $GITHUB_ENV + echo ZKSYNC_HOME=$(pwd) >> $GITHUB_ENV + echo CI=1 >> $GITHUB_ENV + echo $(pwd)/bin >> $GITHUB_PATH + echo CI=1 >> .env + echo IN_DOCKER=1 >> .env - - name: update-images - env: - DOCKER_ACTION: ${{ inputs.action }} - COMPONENT: ${{ matrix.component }} + - name: Set env vars + shell: bash run: | - PASSED_ENV_VARS="ERA_BELLMAN_CUDA_RELEASE,PROTOCOL_VERSION,RUST_FLAGS" \ - ci_run zk docker $DOCKER_ACTION $COMPONENT + # Support for custom tag suffix + if [ -n "${{ inputs.image_tag_suffix }}" ]; then + echo IMAGE_TAG_SHA_TS="${{ inputs.image_tag_suffix }}" >> $GITHUB_ENV + else + echo IMAGE_TAG_SHA_TS=$(git rev-parse --short HEAD)-$(date +%s) >> $GITHUB_ENV + fi - - name: Show sccache stats - if: always() + - name: login to Docker registries + if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) + shell: bash run: | - ci_run sccache --show-stats || true - ci_run cat /tmp/sccache_log.txt || true + docker login -u ${{ secrets.DOCKERHUB_USER }} -p ${{ secrets.DOCKERHUB_TOKEN }} + gcloud auth configure-docker us-docker.pkg.dev -q + + - name: Build and push + uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 + with: + context: . + push: ${{ inputs.action == 'push' }} + build-args: | + SCCACHE_GCS_BUCKET=matterlabs-infra-sccache-storage + SCCACHE_GCS_SERVICE_ACCOUNT=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com + SCCACHE_GCS_RW_MODE=READ_WRITE + RUSTC_WRAPPER=sccache + PROTOCOL_VERSION=${{ env.PROTOCOL_VERSION }} + ERA_BELLMAN_CUDA_RELEASE=${{ inputs.ERA_BELLMAN_CUDA_RELEASE }} + RUST_FLAGS=${{ inputs.WITNESS_GENERATOR_RUST_FLAGS }} + file: docker/${{ matrix.components }}/Dockerfile + tags: | + us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} + matterlabs/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} + us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} + matterlabs/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} + us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:latest + matterlabs/${{ matrix.components }}:latest diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 849fccc2e22c..c502a5f0c205 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,9 +41,7 @@ jobs: - '!prover/extract-setup-data-keys.sh' - 'docker/prover*/**' - '.github/workflows/build-prover-template.yml' - - '.github/workflows/new-build-prover-template.yml' - '.github/workflows/build-witness-generator-template.yml' - - '.github/workflows/new-build-witness-generator-template.yml' - '.github/workflows/ci-prover-reusable.yml' - 'docker-compose-runner-nightly.yml' - '!**/*.md' @@ -55,9 +53,7 @@ jobs: - 'docker/external-node/**' - 'docker/server/**' - '.github/workflows/build-core-template.yml' - - '.github/workflows/new-build-core-template.yml' - '.github/workflows/build-contract-verifier-template.yml' - - '.github/workflows/new-build-contract-verifier-template.yml' - '.github/workflows/ci-core-reusable.yml' - '.github/workflows/ci-core-lint-reusable.yml' - 'Cargo.toml' @@ -121,7 +117,7 @@ jobs: name: Build core images needs: changed_files if: ${{ (needs.changed_files.outputs.core == 'true' || needs.changed_files.outputs.all == 'true') && !contains(github.ref_name, 'release-please--branches') }} - uses: ./.github/workflows/new-build-core-template.yml + uses: ./.github/workflows/build-core-template.yml with: image_tag_suffix: ${{ needs.setup.outputs.image_tag_suffix }} action: "build" @@ -146,7 +142,7 @@ jobs: name: Build contract verifier needs: changed_files if: ${{ (needs.changed_files.outputs.core == 'true' || needs.changed_files.outputs.all == 'true') && !contains(github.ref_name, 'release-please--branches') }} - uses: ./.github/workflows/new-build-contract-verifier-template.yml + uses: ./.github/workflows/build-contract-verifier-template.yml with: image_tag_suffix: ${{ needs.setup.outputs.image_tag_suffix }} action: "build" @@ -158,7 +154,7 @@ jobs: name: Build prover images needs: changed_files if: ${{ (needs.changed_files.outputs.prover == 'true' || needs.changed_files.outputs.all == 'true') && !contains(github.ref_name, 'release-please--branches') }} - uses: ./.github/workflows/new-build-prover-template.yml + uses: ./.github/workflows/build-prover-template.yml with: image_tag_suffix: ${{ needs.setup.outputs.image_tag_suffix }} action: "build" @@ -172,7 +168,7 @@ jobs: name: Build prover images with avx512 instructions needs: changed_files if: ${{ (needs.changed_files.outputs.prover == 'true' || needs.changed_files.outputs.all == 'true') && !contains(github.ref_name, 'release-please--branches') }} - uses: ./.github/workflows/new-build-witness-generator-template.yml + uses: ./.github/workflows/build-witness-generator-template.yml with: image_tag_suffix: ${{ needs.setup.outputs.image_tag_suffix }}-avx512 action: "build" diff --git a/.github/workflows/new-build-contract-verifier-template.yml b/.github/workflows/new-build-contract-verifier-template.yml deleted file mode 100644 index 7d75f81fb73c..000000000000 --- a/.github/workflows/new-build-contract-verifier-template.yml +++ /dev/null @@ -1,270 +0,0 @@ -name: Build contract verifier -on: - workflow_call: - secrets: - DOCKERHUB_USER: - description: "DOCKERHUB_USER" - required: true - DOCKERHUB_TOKEN: - description: "DOCKERHUB_TOKEN" - required: true - inputs: - image_tag_suffix: - description: "Optional suffix to override tag name generation" - type: string - required: false - compilers: - description: 'JSON of required compilers and their versions' - type: string - required: false - default: '[{ "zksolc": ["1.3.14", "1.3.16", "1.3.17", "1.3.1", "1.3.7", "1.3.18", "1.3.19", "1.3.21"] } , { "zkvyper": ["1.3.13"] }]' - action: - type: string - default: non-push - required: false - -jobs: - prepare-contracts: - name: Prepare contracts - runs-on: matterlabs-ci-runner-high-performance - steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - with: - submodules: "recursive" - - - name: Prepare ENV - shell: bash - run: | - echo ZKSYNC_HOME=$(pwd) >> $GITHUB_ENV - echo CI=1 >> $GITHUB_ENV - echo $(pwd)/bin >> $GITHUB_PATH - echo $HOME/.local/bin >> $GITHUB_PATH - echo CI=1 >> .env - echo IN_DOCKER=1 >> .env - - - name: Download contracts - shell: bash - run: | - commit_sha=$(git submodule status contracts | awk '{print $1}' | tr -d '-') - page=1 - filtered_tag="" - while [ true ]; do - echo "Page: $page" - tags=$(run_retried curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -H "Accept: application/vnd.github+json" \ - "https://api.github.com/repos/matter-labs/era-contracts/tags?per_page=100&page=${page}" | jq .) - if [ $(jq length <<<"$tags") -eq 0 ]; then - echo "No tag found on all pages." - echo "BUILD_CONTRACTS=true" >> "$GITHUB_ENV" - exit 0 - fi - filtered_tag=$(jq -r --arg commit_sha "$commit_sha" 'map(select(.commit.sha == $commit_sha)) | .[].name' <<<"$tags") - if [[ ! -z "$filtered_tag" ]]; then - echo "BUILD_CONTRACTS=false" >> "$GITHUB_ENV" - break - fi - ((page++)) - done - echo "Contracts tag is: ${filtered_tag}" - mkdir -p ./contracts - run_retried curl -s -LO https://github.com/matter-labs/era-contracts/releases/download/${filtered_tag}/l1-contracts.tar.gz - run_retried curl -s -LO https://github.com/matter-labs/era-contracts/releases/download/${filtered_tag}/l2-contracts.tar.gz - run_retried curl -s -LO https://github.com/matter-labs/era-contracts/releases/download/${filtered_tag}/system-contracts.tar.gz - tar -C ./contracts -zxf l1-contracts.tar.gz - tar -C ./contracts -zxf l2-contracts.tar.gz - tar -C ./contracts -zxf system-contracts.tar.gz - - - name: Install Apt dependencies - if: env.BUILD_CONTRACTS == 'true' - shell: bash - run: | - sudo apt-get update && sudo apt-get install -y libssl-dev pkg-config - - - name: Install Node - if: env.BUILD_CONTRACTS == 'true' - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 - with: - node-version: 20 - cache: 'npm' - - - name: Install Yarn - if: env.BUILD_CONTRACTS == 'true' - run: npm install -g yarn - - - name: Setup rust - if: env.BUILD_CONTRACTS == 'true' - uses: actions-rust-lang/setup-rust-toolchain@1fbea72663f6d4c03efaab13560c8a24cfd2a7cc # v1.9.0 - with: - toolchain: nightly-2024-08-01 - - - name: Install foundry-zksync - if: env.BUILD_CONTRACTS == 'true' - run: | - mkdir ./foundry-zksync - curl -LO https://github.com/matter-labs/foundry-zksync/releases/download/nightly-15bec2f861b3b4c71e58f85e2b2c9dd722585aa8/foundry_nightly_linux_amd64.tar.gz - tar zxf foundry_nightly_linux_amd64.tar.gz -C ./foundry-zksync - chmod +x ./foundry-zksync/forge ./foundry-zksync/cast - echo "$PWD/foundry-zksync" >> $GITHUB_PATH - - - name: Pre-download compilers - if: env.BUILD_CONTRACTS == 'true' - shell: bash - run: | - # Download needed versions of vyper compiler - # Not sanitized due to unconventional path and tags - mkdir -p ./hardhat-nodejs/compilers-v2/vyper/linux - wget -nv -O ./hardhat-nodejs/compilers-v2/vyper/linux/0.3.10 https://github.com/vyperlang/vyper/releases/download/v0.3.10/vyper.0.3.10+commit.91361694.linux - wget -nv -O ./hardhat-nodejs/compilers-v2/vyper/linux/0.3.3 https://github.com/vyperlang/vyper/releases/download/v0.3.3/vyper.0.3.3+commit.48e326f0.linux - chmod +x ./hardhat-nodejs/compilers-v2/vyper/linux/0.3.10 - chmod +x ./hardhat-nodejs/compilers-v2/vyper/linux/0.3.3 - - COMPILERS_JSON='${{ inputs.compilers }}' - echo "$COMPILERS_JSON" | jq -r '.[] | to_entries[] | .key as $compiler | .value[] | "\(.),\($compiler)"' | while IFS=, read -r version compiler; do - mkdir -p "./hardhat-nodejs/compilers-v2/$compiler" - wget -nv -O "./hardhat-nodejs/compilers-v2/$compiler/${compiler}-v${version}" "https://github.com/matter-labs/${compiler}-bin/releases/download/v${version}/${compiler}-linux-amd64-musl-v${version}" - chmod +x "./hardhat-nodejs/compilers-v2/$compiler/${compiler}-v${version}" - done - - - name: Install zkstack - if: env.BUILD_CONTRACTS == 'true' - run: | - ./zkstack_cli/zkstackup/install --path ./zkstack_cli/zkstackup/zkstackup - zkstackup --local || true - - - name: build contracts - if: env.BUILD_CONTRACTS == 'true' - shell: bash - run: | - cp etc/tokens/{test,localhost}.json - zkstack dev contracts - - - name: Upload contracts - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 - with: - name: contacts-verifier - path: | - ./contracts - - build-images: - name: Build and Push Docker Images - needs: prepare-contracts - runs-on: ${{ fromJSON('["matterlabs-ci-runner-high-performance", "matterlabs-ci-runner-arm"]')[contains(matrix.platforms, 'arm')] }} - strategy: - matrix: - components: - - contract-verifier - - verified-sources-fetcher - platforms: - - linux/amd64 - - steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - with: - submodules: "recursive" - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 - - - name: Setup env - shell: bash - run: | - echo ZKSYNC_HOME=$(pwd) >> $GITHUB_ENV - echo CI=1 >> $GITHUB_ENV - echo $(pwd)/bin >> $GITHUB_PATH - echo CI=1 >> .env - echo IN_DOCKER=1 >> .env - - - name: Download setup key - shell: bash - run: | - if [ -f "/setup_2^26.key" ]; then - cp '/setup_2^26.key' './setup_2^26.key' - else - run_retried curl -LO https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2\^26.key - fi - - - name: Set env vars - shell: bash - run: | - echo PLATFORM=$(echo ${{ matrix.platforms }} | tr '/' '-') >> $GITHUB_ENV - echo IMAGE_TAG_SHA=$(git rev-parse --short HEAD) >> $GITHUB_ENV - # Support for custom tag suffix - if [ -n "${{ inputs.image_tag_suffix }}" ]; then - echo IMAGE_TAG_SHA_TS="${{ inputs.image_tag_suffix }}" >> $GITHUB_ENV - else - echo IMAGE_TAG_SHA_TS=$(git rev-parse --short HEAD)-$(date +%s) >> $GITHUB_ENV - fi - - - name: Download contracts - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 - with: - name: contacts-verifier - path: | - ./contracts - - - name: login to Docker registries - if: ${{ inputs.action == 'push' }} - shell: bash - run: | - docker login -u ${{ secrets.DOCKERHUB_USER }} -p ${{ secrets.DOCKERHUB_TOKEN }} - gcloud auth configure-docker us-docker.pkg.dev -q - - - name: Build and push - uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 - with: - context: . - load: true - platforms: ${{ matrix.platforms }} - file: docker/${{ matrix.components }}/Dockerfile - build-args: | - SCCACHE_GCS_BUCKET=matterlabs-infra-sccache-storage - SCCACHE_GCS_SERVICE_ACCOUNT=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com - SCCACHE_GCS_RW_MODE=READ_WRITE - RUSTC_WRAPPER=sccache - tags: | - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} - matterlabs/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} - - - name: Push docker image - if: ${{ inputs.action == 'push' }} - run: | - docker push us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} - docker push matterlabs/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} - - - create_manifest: - name: Create release manifest - runs-on: matterlabs-ci-runner - needs: build-images - if: ${{ inputs.action == 'push' }} - strategy: - matrix: - component: - - name: contract-verifier - platform: linux/amd64 - - name: verified-sources-fetcher - platform: linux/amd64 - env: - IMAGE_TAG_SUFFIX: ${{ inputs.image_tag_suffix }} - steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4 - - - name: login to Docker registries - run: | - docker login -u ${{ secrets.DOCKERHUB_USER }} -p ${{ secrets.DOCKERHUB_TOKEN }} - gcloud auth configure-docker us-docker.pkg.dev -q - - - name: Create Docker manifest - run: | - docker_repositories=("matterlabs/${{ matrix.component.name }}" "us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component.name }}") - platforms=${{ matrix.component.platform }} - for repo in "${docker_repositories[@]}"; do - platform_tags="" - for platform in ${platforms//,/ }; do - platform=$(echo $platform | tr '/' '-') - platform_tags+=" --amend ${repo}:${IMAGE_TAG_SUFFIX}-${platform}" - done - for manifest in "${repo}:${IMAGE_TAG_SUFFIX}" "${repo}:2.0-${IMAGE_TAG_SUFFIX}" "${repo}:latest" "${repo}:latest2.0"; do - docker manifest create ${manifest} ${platform_tags} - docker manifest push ${manifest} - done - done diff --git a/.github/workflows/new-build-core-template.yml b/.github/workflows/new-build-core-template.yml deleted file mode 100644 index 557d8455a31d..000000000000 --- a/.github/workflows/new-build-core-template.yml +++ /dev/null @@ -1,281 +0,0 @@ -name: Build Core images -on: - workflow_call: - secrets: - DOCKERHUB_USER: - description: "DOCKERHUB_USER" - required: true - DOCKERHUB_TOKEN: - description: "DOCKERHUB_TOKEN" - required: true - inputs: - image_tag_suffix: - description: "Optional suffix to override tag name generation" - type: string - required: false - compilers: - description: 'JSON of required compilers and their versions' - type: string - required: false - default: '[{ "zksolc": ["1.3.14", "1.3.16", "1.3.17", "1.3.1", "1.3.7", "1.3.18", "1.3.19", "1.3.21"] } , { "zkvyper": ["1.3.13"] }]' - en_alpha_release: - description: 'Flag that determins if EN release should be marked as alpha' - type: boolean - required: false - default: false - action: - type: string - required: false - default: "do nothing" - -jobs: - prepare-contracts: - name: Prepare contracts - runs-on: matterlabs-ci-runner-high-performance - steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - with: - submodules: "recursive" - - - name: Prepare ENV - shell: bash - run: | - echo ZKSYNC_HOME=$(pwd) >> $GITHUB_ENV - echo CI=1 >> $GITHUB_ENV - echo $(pwd)/bin >> $GITHUB_PATH - echo $HOME/.local/bin >> $GITHUB_PATH - echo CI=1 >> .env - echo IN_DOCKER=1 >> .env - - - name: Download contracts - shell: bash - run: | - commit_sha=$(git submodule status contracts | awk '{print $1}' | tr -d '-') - page=1 - filtered_tag="" - while [ true ]; do - echo "Page: $page" - tags=$(run_retried curl -s -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" -H "Accept: application/vnd.github+json" \ - "https://api.github.com/repos/matter-labs/era-contracts/tags?per_page=100&page=${page}" | jq .) - if [ $(jq length <<<"$tags") -eq 0 ]; then - echo "No tag found on all pages." - echo "BUILD_CONTRACTS=true" >> "$GITHUB_ENV" - exit 0 - fi - filtered_tag=$(jq -r --arg commit_sha "$commit_sha" 'map(select(.commit.sha == $commit_sha)) | .[].name' <<<"$tags") - if [[ ! -z "$filtered_tag" ]]; then - echo "BUILD_CONTRACTS=false" >> "$GITHUB_ENV" - break - fi - ((page++)) - done - echo "Contracts tag is: ${filtered_tag}" - mkdir -p ./contracts - run_retried curl -s -LO https://github.com/matter-labs/era-contracts/releases/download/${filtered_tag}/l1-contracts.tar.gz - run_retried curl -s -LO https://github.com/matter-labs/era-contracts/releases/download/${filtered_tag}/l2-contracts.tar.gz - run_retried curl -s -LO https://github.com/matter-labs/era-contracts/releases/download/${filtered_tag}/system-contracts.tar.gz - tar -C ./contracts -zxf l1-contracts.tar.gz - tar -C ./contracts -zxf l2-contracts.tar.gz - tar -C ./contracts -zxf system-contracts.tar.gz - - - name: Install Apt dependencies - if: env.BUILD_CONTRACTS == 'true' - shell: bash - run: | - sudo apt-get update && sudo apt-get install -y libssl-dev pkg-config - - - name: Install Node - if: env.BUILD_CONTRACTS == 'true' - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 - with: - node-version: 20 - cache: 'npm' - - - name: Install Yarn - if: env.BUILD_CONTRACTS == 'true' - run: npm install -g yarn - - - name: Setup rust - if: env.BUILD_CONTRACTS == 'true' - uses: actions-rust-lang/setup-rust-toolchain@1fbea72663f6d4c03efaab13560c8a24cfd2a7cc # v1.9.0 - with: - toolchain: nightly-2024-08-01 - - - name: Install foundry-zksync - if: env.BUILD_CONTRACTS == 'true' - run: | - mkdir ./foundry-zksync - curl -LO https://github.com/matter-labs/foundry-zksync/releases/download/nightly-15bec2f861b3b4c71e58f85e2b2c9dd722585aa8/foundry_nightly_linux_amd64.tar.gz - tar zxf foundry_nightly_linux_amd64.tar.gz -C ./foundry-zksync - chmod +x ./foundry-zksync/forge ./foundry-zksync/cast - echo "$PWD/foundry-zksync" >> $GITHUB_PATH - - - name: Pre-download compilers - if: env.BUILD_CONTRACTS == 'true' - shell: bash - run: | - # Download needed versions of vyper compiler - # Not sanitized due to unconventional path and tags - mkdir -p ./hardhat-nodejs/compilers-v2/vyper/linux - wget -nv -O ./hardhat-nodejs/compilers-v2/vyper/linux/0.3.10 https://github.com/vyperlang/vyper/releases/download/v0.3.10/vyper.0.3.10+commit.91361694.linux - wget -nv -O ./hardhat-nodejs/compilers-v2/vyper/linux/0.3.3 https://github.com/vyperlang/vyper/releases/download/v0.3.3/vyper.0.3.3+commit.48e326f0.linux - chmod +x ./hardhat-nodejs/compilers-v2/vyper/linux/0.3.10 - chmod +x ./hardhat-nodejs/compilers-v2/vyper/linux/0.3.3 - - COMPILERS_JSON='${{ inputs.compilers }}' - echo "$COMPILERS_JSON" | jq -r '.[] | to_entries[] | .key as $compiler | .value[] | "\(.),\($compiler)"' | while IFS=, read -r version compiler; do - mkdir -p "./hardhat-nodejs/compilers-v2/$compiler" - wget -nv -O "./hardhat-nodejs/compilers-v2/$compiler/${compiler}-v${version}" "https://github.com/matter-labs/${compiler}-bin/releases/download/v${version}/${compiler}-linux-amd64-musl-v${version}" - chmod +x "./hardhat-nodejs/compilers-v2/$compiler/${compiler}-v${version}" - done - - - name: Install zkstack - if: env.BUILD_CONTRACTS == 'true' - run: | - ./zkstack_cli/zkstackup/install --path ./zkstack_cli/zkstackup/zkstackup - zkstackup --local || true - - - name: build contracts - if: env.BUILD_CONTRACTS == 'true' - shell: bash - run: | - cp etc/tokens/{test,localhost}.json - zkstack dev contracts - - - name: Upload contracts - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 - with: - name: contacts - path: | - ./contracts - - build-images: - name: Build and Push Docker Images - needs: prepare-contracts - env: - IMAGE_TAG_SUFFIX: ${{ inputs.image_tag_suffix }}${{ (inputs.en_alpha_release && matrix.components == 'external-node') && '-alpha' || '' }} - runs-on: ${{ fromJSON('["matterlabs-ci-runner-high-performance", "matterlabs-ci-runner-arm"]')[contains(matrix.platforms, 'arm')] }} - strategy: - matrix: - components: - - server-v2 - - external-node - - snapshots-creator - platforms: - - linux/amd64 - include: - - components: external-node - platforms: linux/arm64 - - steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - with: - submodules: "recursive" - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 - - - name: Setup env - shell: bash - run: | - echo ZKSYNC_HOME=$(pwd) >> $GITHUB_ENV - echo CI=1 >> $GITHUB_ENV - echo $(pwd)/bin >> $GITHUB_PATH - echo CI=1 >> .env - echo IN_DOCKER=1 >> .env - - - name: Download setup key - shell: bash - run: | - if [ -f "/setup_2^26.key" ]; then - cp '/setup_2^26.key' './setup_2^26.key' - else - run_retried curl -LO https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2\^26.key - fi - - - name: Set env vars - shell: bash - run: | - echo PLATFORM=$(echo ${{ matrix.platforms }} | tr '/' '-') >> $GITHUB_ENV - if [ -n "${{ inputs.image_tag_suffix }}" ]; then - echo IMAGE_TAG_SHA_TS="${{ env.IMAGE_TAG_SUFFIX }}" >> $GITHUB_ENV - else - echo IMAGE_TAG_SHA_TS=$(git rev-parse --short HEAD)-$(date +%s) >> $GITHUB_ENV - fi - - - name: Download contracts - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 - with: - name: contacts - path: | - ./contracts - - - name: login to Docker registries - if: ${{ inputs.action == 'push' }} - shell: bash - run: | - docker login -u ${{ secrets.DOCKERHUB_USER }} -p ${{ secrets.DOCKERHUB_TOKEN }} - gcloud auth configure-docker us-docker.pkg.dev -q - - - name: Build docker image - uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 - with: - context: . - load: true - platforms: ${{ matrix.platforms }} - file: docker/${{ matrix.components }}/Dockerfile - build-args: | - SCCACHE_GCS_BUCKET=matterlabs-infra-sccache-storage - SCCACHE_GCS_SERVICE_ACCOUNT=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com - SCCACHE_GCS_RW_MODE=READ_WRITE - RUSTC_WRAPPER=sccache - tags: | - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} - matterlabs/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} - - - name: Push docker image - if: ${{ inputs.action == 'push' }} - run: | - docker push us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} - docker push matterlabs/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} - - create_manifest: - name: Create release manifest - runs-on: matterlabs-ci-runner - needs: build-images - if: ${{ inputs.action == 'push' }} - strategy: - matrix: - component: - - name: server-v2 - platform: linux/amd64 - - name: external-node - platform: linux/amd64,linux/arm64 - - name: snapshots-creator - platform: linux/amd64 - - env: - IMAGE_TAG_SUFFIX: ${{ inputs.image_tag_suffix }}${{ (inputs.en_alpha_release && matrix.component.name == 'external-node') && '-alpha' || '' }} - steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4 - - - name: login to Docker registries - run: | - docker login -u ${{ secrets.DOCKERHUB_USER }} -p ${{ secrets.DOCKERHUB_TOKEN }} - gcloud auth configure-docker us-docker.pkg.dev -q - - - name: Create Docker manifest - run: | - docker_repositories=("matterlabs/${{ matrix.component.name }}" "us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component.name }}") - platforms=${{ matrix.component.platform }} - for repo in "${docker_repositories[@]}"; do - platform_tags="" - for platform in ${platforms//,/ }; do - platform=$(echo $platform | tr '/' '-') - platform_tags+=" --amend ${repo}:${IMAGE_TAG_SUFFIX}-${platform}" - done - for manifest in "${repo}:${IMAGE_TAG_SUFFIX}" "${repo}:2.0-${IMAGE_TAG_SUFFIX}" "${repo}:latest" "${repo}:latest2.0"; do - docker manifest create ${manifest} ${platform_tags} - docker manifest push ${manifest} - done - done diff --git a/.github/workflows/new-build-prover-template.yml b/.github/workflows/new-build-prover-template.yml deleted file mode 100644 index 3a721e4425a8..000000000000 --- a/.github/workflows/new-build-prover-template.yml +++ /dev/null @@ -1,225 +0,0 @@ -name: Build Prover images -on: - workflow_call: - secrets: - DOCKERHUB_USER: - description: "DOCKERHUB_USER" - required: true - DOCKERHUB_TOKEN: - description: "DOCKERHUB_TOKEN" - required: true - inputs: - ERA_BELLMAN_CUDA_RELEASE: - description: "ERA_BELLMAN_CUDA_RELEASE" - type: string - required: true - image_tag_suffix: - description: "Optional suffix to override tag name generation" - type: string - required: false - action: - description: "Action with docker image" - type: string - default: "push" - required: false - is_pr_from_fork: - description: "Indicates whether the workflow is invoked from a PR created from fork" - type: boolean - default: false - required: false - CUDA_ARCH: - description: "CUDA Arch to build" - type: string - default: "75;80;89" - required: false - # Details: https://arnon.dk/matching-sm-architectures-arch-and-gencode-for-various-nvidia-cards/ - # L4: 89 - # T4: 75 - # A100: 80 - outputs: - protocol_version: - description: "Protocol version of the binary" - value: ${{ jobs.get-protocol-version.outputs.protocol_version }} - -jobs: - get-protocol-version: - name: Get protocol version - runs-on: [matterlabs-ci-runner-high-performance] - outputs: - protocol_version: ${{ steps.protocolversion.outputs.protocol_version }} - steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - with: - submodules: "recursive" - - - name: setup-env - run: | - echo ZKSYNC_HOME=$(pwd) >> $GITHUB_ENV - echo CI=1 >> $GITHUB_ENV - echo $(pwd)/bin >> $GITHUB_PATH - echo CI=1 >> .env - echo IN_DOCKER=1 >> .env - - - name: setup rust - uses: actions-rust-lang/setup-rust-toolchain@1fbea72663f6d4c03efaab13560c8a24cfd2a7cc # v1.9.0 - with: - toolchain: nightly-2024-08-01 - - - name: Prepare sccache-cache env vars - shell: bash - run: | - echo SCCACHE_GCS_BUCKET=matterlabs-infra-sccache-storage >> $GITHUB_ENV - echo SCCACHE_GCS_SERVICE_ACCOUNT=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com >> $GITHUB_ENV - echo SCCACHE_ERROR_LOG=/tmp/sccache_log.txt >> $GITHUB_ENV - echo SCCACHE_GCS_RW_MODE=READ_WRITE >> $GITHUB_ENV - echo RUSTC_WRAPPER=sccache >> $GITHUB_ENV - - - name: protocol-version - id: protocolversion - # TODO: use -C flag, when it will become stable. - shell: bash - run: | - cd prover - cargo build --release --bin prover_version - PPV=$(target/release/prover_version) - echo Protocol version is ${PPV} - echo "protocol_version=${PPV}" >> $GITHUB_OUTPUT - - build-images: - name: Build and Push Docker Images - needs: get-protocol-version - env: - PROTOCOL_VERSION: ${{ needs.get-protocol-version.outputs.protocol_version }} - runs-on: [matterlabs-ci-runner-high-performance] - strategy: - matrix: - components: - - witness-generator - - prover-gpu-fri - - witness-vector-generator - - prover-fri-gateway - - prover-job-monitor - - proof-fri-gpu-compressor - - prover-autoscaler - - circuit-prover-gpu - steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - with: - submodules: "recursive" - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 - - - name: setup-env - run: | - echo ZKSYNC_HOME=$(pwd) >> $GITHUB_ENV - echo CI=1 >> $GITHUB_ENV - echo $(pwd)/bin >> $GITHUB_PATH - echo CI=1 >> .env - echo IN_DOCKER=1 >> .env - - - name: Set env vars - shell: bash - run: | - # Support for custom tag suffix - if [ -n "${{ inputs.image_tag_suffix }}" ]; then - echo IMAGE_TAG_SHA_TS="${{ inputs.image_tag_suffix }}" >> $GITHUB_ENV - else - echo IMAGE_TAG_SHA_TS=$(git rev-parse --short HEAD)-$(date +%s) >> $GITHUB_ENV - fi - - - name: download CRS for GPU compressor - if: matrix.components == 'proof-fri-gpu-compressor' - run: | - run_retried curl -LO https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2\^24.key - # We need to run this only when ERA_BELLMAN_CUDA_RELEASE is not available - # In our case it happens only when PR is created from fork - - name: Wait for runner IP to be not rate-limited against GH API - if: ( inputs.is_pr_from_fork == true && matrix.components == 'proof-fri-gpu-compressor' ) - run: ./.github/scripts/rate_limit_check.sh - - - name: Hack to set env vars inside docker container - shell: bash - run: | - sed -i '/^FROM matterlabs\/zksync-build-base:latest as builder/a ENV SCCACHE_GCS_BUCKET=matterlabs-infra-sccache-storage\nENV SCCACHE_GCS_SERVICE_ACCOUNT=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com\nENV SCCACHE_GCS_RW_MODE=READ_WRITE\nENV RUSTC_WRAPPER=sccache' ./docker/${{ matrix.components }}/Dockerfile - #TODO: remove AS version =) - sed -i '/^FROM matterlabs\/zksync-build-base:latest AS builder/a ENV SCCACHE_GCS_BUCKET=matterlabs-infra-sccache-storage\nENV SCCACHE_GCS_SERVICE_ACCOUNT=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com\nENV SCCACHE_GCS_RW_MODE=READ_WRITE\nENV RUSTC_WRAPPER=sccache' ./docker/${{ matrix.components }}/Dockerfile - cat ./docker/${{ matrix.components }}/Dockerfile - - - name: login to Docker registries - if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) - shell: bash - run: | - docker login -u ${{ secrets.DOCKERHUB_USER }} -p ${{ secrets.DOCKERHUB_TOKEN }} - gcloud auth configure-docker us-docker.pkg.dev -q - - - name: Build and push - uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 - with: - context: . - load: true - build-args: | - CUDA_ARCH=${{ inputs.CUDA_ARCH }} - SCCACHE_GCS_BUCKET=matterlabs-infra-sccache-storage - SCCACHE_GCS_SERVICE_ACCOUNT=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com - SCCACHE_GCS_RW_MODE=READ_WRITE - RUSTC_WRAPPER=sccache - PROTOCOL_VERSION=${{ env.PROTOCOL_VERSION }} - ERA_BELLMAN_CUDA_RELEASE=${{ inputs.ERA_BELLMAN_CUDA_RELEASE }} - file: docker/${{ matrix.components }}/Dockerfile - tags: | - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} - matterlabs/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} - matterlabs/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:latest - matterlabs/${{ matrix.components }}:latest - - - name: Push docker image - if: ${{ inputs.action == 'push' }} - run: | - docker push us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} - docker push matterlabs/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} - docker push us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} - docker push matterlabs/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} - docker push us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:latest - docker push matterlabs/${{ matrix.components }}:latest - - copy-images: - name: Copy images between docker registries - needs: [build-images, get-protocol-version] - env: - PROTOCOL_VERSION: ${{ needs.get-protocol-version.outputs.protocol_version }} - runs-on: matterlabs-ci-runner - if: ${{ inputs.action == 'push' }} - strategy: - matrix: - component: - - witness-vector-generator - steps: - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 - - - name: Login to us-central1 GAR - run: | - gcloud auth print-access-token --lifetime=7200 --impersonate-service-account=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com | docker login -u oauth2accesstoken --password-stdin https://us-docker.pkg.dev - - - name: Login and push to Asia GAR - run: | - gcloud auth print-access-token --lifetime=7200 --impersonate-service-account=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com | docker login -u oauth2accesstoken --password-stdin https://asia-docker.pkg.dev - docker buildx imagetools create \ - --tag asia-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ inputs.image_tag_suffix }} \ - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ inputs.image_tag_suffix }} - docker buildx imagetools create \ - --tag asia-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ inputs.image_tag_suffix }} \ - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ inputs.image_tag_suffix }} - - - name: Login and push to Europe GAR - run: | - gcloud auth print-access-token --lifetime=7200 --impersonate-service-account=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com | docker login -u oauth2accesstoken --password-stdin https://europe-docker.pkg.dev - docker buildx imagetools create \ - --tag europe-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ inputs.image_tag_suffix }} \ - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ inputs.image_tag_suffix }} - docker buildx imagetools create \ - --tag europe-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ inputs.image_tag_suffix }} \ - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ inputs.image_tag_suffix }} diff --git a/.github/workflows/new-build-witness-generator-template.yml b/.github/workflows/new-build-witness-generator-template.yml deleted file mode 100644 index a96d217da832..000000000000 --- a/.github/workflows/new-build-witness-generator-template.yml +++ /dev/null @@ -1,138 +0,0 @@ -name: Build witness generator image with custom compiler flags -on: - workflow_call: - secrets: - DOCKERHUB_USER: - description: "DOCKERHUB_USER" - required: true - DOCKERHUB_TOKEN: - description: "DOCKERHUB_TOKEN" - required: true - inputs: - ERA_BELLMAN_CUDA_RELEASE: - description: "ERA_BELLMAN_CUDA_RELEASE" - type: string - required: true - image_tag_suffix: - description: "Optional suffix to override tag name generation" - type: string - required: false - action: - type: string - default: non-push - required: false - WITNESS_GENERATOR_RUST_FLAGS: - description: "Rust flags for witness_generator compilation" - type: string - default: "" - required: false - outputs: - protocol_version: - description: "Protocol version of the binary" - value: ${{ jobs.get-protocol-version.outputs.protocol_version }} - -jobs: - get-protocol-version: - name: Get protocol version - runs-on: [matterlabs-ci-runner-high-performance] - outputs: - protocol_version: ${{ steps.protocolversion.outputs.protocol_version }} - steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - with: - submodules: "recursive" - - - name: setup-env - run: | - echo ZKSYNC_HOME=$(pwd) >> $GITHUB_ENV - echo CI=1 >> $GITHUB_ENV - echo $(pwd)/bin >> $GITHUB_PATH - echo CI=1 >> .env - echo IN_DOCKER=1 >> .env - - - name: setup rust - uses: actions-rust-lang/setup-rust-toolchain@1fbea72663f6d4c03efaab13560c8a24cfd2a7cc # v1.9.0 - with: - toolchain: nightly-2024-08-01 - - - name: Prepare sccache-cache env vars - shell: bash - run: | - echo SCCACHE_GCS_BUCKET=matterlabs-infra-sccache-storage >> $GITHUB_ENV - echo SCCACHE_GCS_SERVICE_ACCOUNT=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com >> $GITHUB_ENV - echo SCCACHE_ERROR_LOG=/tmp/sccache_log.txt >> $GITHUB_ENV - echo SCCACHE_GCS_RW_MODE=READ_WRITE >> $GITHUB_ENV - echo RUSTC_WRAPPER=sccache >> $GITHUB_ENV - - - name: protocol-version - id: protocolversion - # TODO: use -C flag, when it will become stable. - shell: bash - run: | - cd prover - cargo build --release --bin prover_version - PPV=$(target/release/prover_version) - echo Protocol version is ${PPV} - echo "protocol_version=${PPV}" >> $GITHUB_OUTPUT - - build-images: - name: Build and Push Docker Images - needs: get-protocol-version - env: - PROTOCOL_VERSION: ${{ needs.get-protocol-version.outputs.protocol_version }} - runs-on: [matterlabs-ci-runner-c3d] - strategy: - matrix: - components: - - witness-generator - steps: - - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - with: - submodules: "recursive" - - - name: setup-env - run: | - echo ZKSYNC_HOME=$(pwd) >> $GITHUB_ENV - echo CI=1 >> $GITHUB_ENV - echo $(pwd)/bin >> $GITHUB_PATH - echo CI=1 >> .env - echo IN_DOCKER=1 >> .env - - - name: Set env vars - shell: bash - run: | - # Support for custom tag suffix - if [ -n "${{ inputs.image_tag_suffix }}" ]; then - echo IMAGE_TAG_SHA_TS="${{ inputs.image_tag_suffix }}" >> $GITHUB_ENV - else - echo IMAGE_TAG_SHA_TS=$(git rev-parse --short HEAD)-$(date +%s) >> $GITHUB_ENV - fi - - - name: login to Docker registries - if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) - shell: bash - run: | - docker login -u ${{ secrets.DOCKERHUB_USER }} -p ${{ secrets.DOCKERHUB_TOKEN }} - gcloud auth configure-docker us-docker.pkg.dev -q - - - name: Build and push - uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 - with: - context: . - push: ${{ inputs.action == 'push' }} - build-args: | - SCCACHE_GCS_BUCKET=matterlabs-infra-sccache-storage - SCCACHE_GCS_SERVICE_ACCOUNT=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com - SCCACHE_GCS_RW_MODE=READ_WRITE - RUSTC_WRAPPER=sccache - PROTOCOL_VERSION=${{ env.PROTOCOL_VERSION }} - ERA_BELLMAN_CUDA_RELEASE=${{ inputs.ERA_BELLMAN_CUDA_RELEASE }} - RUST_FLAGS=${{ inputs.WITNESS_GENERATOR_RUST_FLAGS }} - file: docker/${{ matrix.components }}/Dockerfile - tags: | - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} - matterlabs/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} - matterlabs/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:latest - matterlabs/${{ matrix.components }}:latest diff --git a/.github/workflows/release-test-stage.yml b/.github/workflows/release-test-stage.yml index 2e6c7882aa98..d8b39829c1b7 100644 --- a/.github/workflows/release-test-stage.yml +++ b/.github/workflows/release-test-stage.yml @@ -61,7 +61,7 @@ jobs: build-push-core-images: name: Build and push images needs: [setup, changed_files] - uses: ./.github/workflows/new-build-core-template.yml + uses: ./.github/workflows/build-core-template.yml if: needs.changed_files.outputs.core == 'true' || needs.changed_files.outputs.all == 'true' with: image_tag_suffix: ${{ needs.setup.outputs.image_tag_suffix }} @@ -85,7 +85,7 @@ jobs: build-push-contract-verifier: name: Build and push images needs: [setup, changed_files] - uses: ./.github/workflows/new-build-contract-verifier-template.yml + uses: ./.github/workflows/build-contract-verifier-template.yml if: needs.changed_files.outputs.core == 'true' || needs.changed_files.outputs.all == 'true' with: image_tag_suffix: ${{ needs.setup.outputs.image_tag_suffix }} @@ -97,7 +97,7 @@ jobs: build-push-prover-images: name: Build and push images needs: [setup, changed_files] - uses: ./.github/workflows/new-build-prover-template.yml + uses: ./.github/workflows/build-prover-template.yml if: needs.changed_files.outputs.prover == 'true' || needs.changed_files.outputs.all == 'true' with: image_tag_suffix: ${{ needs.setup.outputs.image_tag_suffix }} @@ -110,7 +110,7 @@ jobs: build-push-witness-generator-image-avx512: name: Build and push prover images with avx512 instructions needs: [setup, changed_files] - uses: ./.github/workflows/new-build-witness-generator-template.yml + uses: ./.github/workflows/build-witness-generator-template.yml if: needs.changed_files.outputs.prover == 'true' || needs.changed_files.outputs.all == 'true' with: image_tag_suffix: ${{ needs.setup.outputs.image_tag_suffix }}-avx512 From 3037ee6aa976744a09882b5830d6242ad8336717 Mon Sep 17 00:00:00 2001 From: Stanislav Bezkorovainyi Date: Mon, 6 Jan 2025 16:58:21 +0100 Subject: [PATCH 03/97] feat: Features for an easier upgrade (#3422) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Sending `execute` operations is stopped when gateway upgrade is ongoing Also, validator timelock is fetched from state transition manager Also, l1 shared bridge is updated from bridgehub ## Why ❔ ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- core/lib/contracts/src/lib.rs | 2 +- core/node/api_server/src/web3/state.rs | 4 + .../eth_sender/src/aggregated_operations.rs | 4 + core/node/eth_sender/src/aggregator.rs | 85 +++++- core/node/eth_sender/src/eth_tx_aggregator.rs | 247 +++++++++++++----- core/node/eth_sender/src/tester.rs | 3 + core/node/eth_sender/src/tests.rs | 61 ++++- core/node/eth_sender/src/zksync_functions.rs | 9 +- .../layers/eth_sender/aggregator.rs | 46 ++-- .../web3_api/server/bridge_addresses.rs | 82 +++++- .../layers/web3_api/server/mod.rs | 38 ++- 11 files changed, 456 insertions(+), 125 deletions(-) diff --git a/core/lib/contracts/src/lib.rs b/core/lib/contracts/src/lib.rs index ba22ba8d1b95..9ca679fef899 100644 --- a/core/lib/contracts/src/lib.rs +++ b/core/lib/contracts/src/lib.rs @@ -37,7 +37,7 @@ const FORGE_PATH_PREFIX: &str = "contracts/l1-contracts/out"; const BRIDGEHUB_CONTRACT_FILE: (&str, &str) = ("bridgehub", "IBridgehub.sol/IBridgehub.json"); const STATE_TRANSITION_CONTRACT_FILE: (&str, &str) = ( "state-transition", - "IStateTransitionManager.sol/IStateTransitionManager.json", + "StateTransitionManager.sol/StateTransitionManager.json", ); const ZKSYNC_HYPERCHAIN_CONTRACT_FILE: (&str, &str) = ( "state-transition/chain-interfaces", diff --git a/core/node/api_server/src/web3/state.rs b/core/node/api_server/src/web3/state.rs index bdefd79b6dd6..a50b9d062321 100644 --- a/core/node/api_server/src/web3/state.rs +++ b/core/node/api_server/src/web3/state.rs @@ -232,6 +232,10 @@ impl BridgeAddressesHandle { *self.0.write().await = bridge_addresses; } + pub async fn update_l1_shared_bridge(&self, l1_shared_bridge: Address) { + self.0.write().await.l1_shared_default_bridge = Some(l1_shared_bridge); + } + pub async fn read(&self) -> api::BridgeAddresses { self.0.read().await.clone() } diff --git a/core/node/eth_sender/src/aggregated_operations.rs b/core/node/eth_sender/src/aggregated_operations.rs index 5271d42d3b75..35cb1648116d 100644 --- a/core/node/eth_sender/src/aggregated_operations.rs +++ b/core/node/eth_sender/src/aggregated_operations.rs @@ -62,4 +62,8 @@ impl AggregatedOperation { self.get_action_type() == AggregatedActionType::PublishProofOnchain || self.get_action_type() == AggregatedActionType::Execute } + + pub fn is_execute(&self) -> bool { + self.get_action_type() == AggregatedActionType::Execute + } } diff --git a/core/node/eth_sender/src/aggregator.rs b/core/node/eth_sender/src/aggregator.rs index 3a318f44bcea..33b9500b2d18 100644 --- a/core/node/eth_sender/src/aggregator.rs +++ b/core/node/eth_sender/src/aggregator.rs @@ -51,6 +51,61 @@ pub struct Aggregator { priority_tree_start_index: Option, } +/// Denotes whether there are any restrictions on sending either +/// commit, prove or execute operations. If there is one, the reason for it +/// is stored to be logged. +#[derive(Debug, Default)] +pub(crate) struct OperationSkippingRestrictions { + pub(crate) commit_restriction: Option<&'static str>, + pub(crate) prove_restriction: Option<&'static str>, + pub(crate) execute_restriction: Option<&'static str>, +} + +impl OperationSkippingRestrictions { + fn check_for_continuation( + &self, + agg_op: &AggregatedOperation, + reason: Option<&'static str>, + ) -> bool { + if let Some(reason) = reason { + tracing::info!( + "Skipping sending commit operation of type {} for batches {}-{} \ + since {}", + agg_op.get_action_type(), + agg_op.l1_batch_range().start(), + agg_op.l1_batch_range().end(), + reason + ); + false + } else { + true + } + } + + // Unlike other funcitons `filter_commit_op` accepts an already prepared `AggregatedOperation` for + // easier compatibility with other interfaces in the file. + fn filter_commit_op( + &self, + commit_op: Option, + ) -> Option { + let commit_op = commit_op?; + self.check_for_continuation(&commit_op, self.commit_restriction) + .then_some(commit_op) + } + + fn filter_prove_op(&self, prove_op: Option) -> Option { + let op = AggregatedOperation::PublishProofOnchain(prove_op?); + self.check_for_continuation(&op, self.commit_restriction) + .then_some(op) + } + + fn filter_execute_op(&self, execute_op: Option) -> Option { + let op = AggregatedOperation::Execute(execute_op?); + self.check_for_continuation(&op, self.commit_restriction) + .then_some(op) + } +} + impl Aggregator { pub async fn new( config: SenderConfig, @@ -153,12 +208,13 @@ impl Aggregator { }) } - pub async fn get_next_ready_operation( + pub(crate) async fn get_next_ready_operation( &mut self, storage: &mut Connection<'_, Core>, base_system_contracts_hashes: BaseSystemContractsHashes, protocol_version_id: ProtocolVersionId, l1_verifier_config: L1VerifierConfig, + restrictions: OperationSkippingRestrictions, ) -> Result, EthSenderError> { let Some(last_sealed_l1_batch_number) = storage .blocks_dal() @@ -169,30 +225,31 @@ impl Aggregator { return Ok(None); // No L1 batches in Postgres; no operations are ready yet }; - if let Some(op) = self - .get_execute_operations( + if let Some(op) = restrictions.filter_execute_op( + self.get_execute_operations( storage, self.config.max_aggregated_blocks_to_execute as usize, last_sealed_l1_batch_number, ) - .await? - { - Ok(Some(AggregatedOperation::Execute(op))) - } else if let Some(op) = self - .get_proof_operation(storage, last_sealed_l1_batch_number, l1_verifier_config) - .await - { - Ok(Some(AggregatedOperation::PublishProofOnchain(op))) + .await?, + ) { + Ok(Some(op)) + } else if let Some(op) = restrictions.filter_prove_op( + self.get_proof_operation(storage, last_sealed_l1_batch_number, l1_verifier_config) + .await, + ) { + Ok(Some(op)) } else { - Ok(self - .get_commit_operation( + Ok(restrictions.filter_commit_op( + self.get_commit_operation( storage, self.config.max_aggregated_blocks_to_commit as usize, last_sealed_l1_batch_number, base_system_contracts_hashes, protocol_version_id, ) - .await) + .await, + )) } } diff --git a/core/node/eth_sender/src/eth_tx_aggregator.rs b/core/node/eth_sender/src/eth_tx_aggregator.rs index 0b176d6cc7f3..8a829ed00faa 100644 --- a/core/node/eth_sender/src/eth_tx_aggregator.rs +++ b/core/node/eth_sender/src/eth_tx_aggregator.rs @@ -28,6 +28,7 @@ use zksync_types::{ use super::aggregated_operations::AggregatedOperation; use crate::{ + aggregator::OperationSkippingRestrictions, health::{EthTxAggregatorHealthDetails, EthTxDetails}, metrics::{PubdataKind, METRICS}, publish_criterion::L1GasCriterion, @@ -41,7 +42,13 @@ use crate::{ pub struct MulticallData { pub base_system_contracts_hashes: BaseSystemContractsHashes, pub verifier_address: Address, - pub protocol_version_id: ProtocolVersionId, + pub chain_protocol_version_id: ProtocolVersionId, + /// The latest validator timelock that is stored on the StateTransitionManager (ChainTypeManager). + /// For a smoother upgrade process, if the `stm_protocol_version_id` is the same as `chain_protocol_version_id`, + /// we will use the validator timelock from the CTM. This removes the need to immediately set the correct + /// validator timelock in the config. However, it is expected that it will be done eventually. + pub stm_validator_timelock_address: Address, + pub stm_protocol_version_id: ProtocolVersionId, } /// The component is responsible for aggregating l1 batches into eth_txs: @@ -52,9 +59,15 @@ pub struct EthTxAggregator { aggregator: Aggregator, eth_client: Box, config: SenderConfig, - timelock_contract_address: Address, + // The validator timelock address provided in the config. + // If the contracts have the same protocol version as the state transition manager, the validator timelock + // from the state transition manager will be used. + // The address provided from the config is only used when there is a discrepancy between the two. + // TODO(EVM-932): always fetch the validator timelock from L1, but it requires a protocol change. + config_timelock_contract_address: Address, l1_multicall3_address: Address, pub(super) state_transition_chain_contract: Address, + state_transition_manager_address: Address, functions: ZkSyncFunctions, base_nonce: u64, base_nonce_custom_commit_sender: Option, @@ -84,7 +97,8 @@ impl EthTxAggregator { config: SenderConfig, aggregator: Aggregator, eth_client: Box, - timelock_contract_address: Address, + config_timelock_contract_address: Address, + state_transition_manager_address: Address, l1_multicall3_address: Address, state_transition_chain_contract: Address, rollup_chain_id: L2ChainId, @@ -113,7 +127,8 @@ impl EthTxAggregator { config, aggregator, eth_client, - timelock_contract_address, + config_timelock_contract_address, + state_transition_manager_address, l1_multicall3_address, state_transition_chain_contract, functions, @@ -229,12 +244,40 @@ impl EthTxAggregator { calldata: get_protocol_version_input, }; + let get_stm_protocol_version_input = self + .functions + .state_transition_manager_contract + .function("protocolVersion") + .unwrap() + .encode_input(&[]) + .unwrap(); + let get_stm_protocol_version_call = Multicall3Call { + target: self.state_transition_manager_address, + allow_failure: ALLOW_FAILURE, + calldata: get_stm_protocol_version_input, + }; + + let get_stm_validator_timelock_input = self + .functions + .state_transition_manager_contract + .function("validatorTimelock") + .unwrap() + .encode_input(&[]) + .unwrap(); + let get_stm_validator_timelock_call = Multicall3Call { + target: self.state_transition_manager_address, + allow_failure: ALLOW_FAILURE, + calldata: get_stm_validator_timelock_input, + }; + let mut token_vec = vec![ get_bootloader_hash_call.into_token(), get_default_aa_hash_call.into_token(), get_verifier_params_call.into_token(), get_verifier_call.into_token(), get_protocol_version_call.into_token(), + get_stm_protocol_version_call.into_token(), + get_stm_validator_timelock_call.into_token(), ]; let mut evm_emulator_hash_requested = false; @@ -270,8 +313,8 @@ impl EthTxAggregator { }; if let Token::Array(call_results) = token { - let number_of_calls = if evm_emulator_hash_requested { 6 } else { 5 }; - // 5 or 6 calls are aggregated in multicall + let number_of_calls = if evm_emulator_hash_requested { 8 } else { 7 }; + // 7 or 8 calls are aggregated in multicall if call_results.len() != number_of_calls { return parse_error(&call_results); } @@ -327,47 +370,86 @@ impl EthTxAggregator { call_results_iterator.next().unwrap(); // FIXME: why is this value requested? - let multicall3_verifier_address = - Multicall3Result::from_token(call_results_iterator.next().unwrap())?.return_data; - if multicall3_verifier_address.len() != 32 { - return Err(EthSenderError::Parse(Web3ContractError::InvalidOutputType( - format!( - "multicall3 verifier address data is not of the len of 32: {:?}", - multicall3_verifier_address - ), - ))); - } - let verifier_address = Address::from_slice(&multicall3_verifier_address[12..]); - - let multicall3_protocol_version = - Multicall3Result::from_token(call_results_iterator.next().unwrap())?.return_data; - if multicall3_protocol_version.len() != 32 { - return Err(EthSenderError::Parse(Web3ContractError::InvalidOutputType( - format!( - "multicall3 protocol version data is not of the len of 32: {:?}", - multicall3_protocol_version - ), - ))); - } - - let protocol_version = U256::from_big_endian(&multicall3_protocol_version); - // In case the protocol version is smaller than `PACKED_SEMVER_MINOR_MASK`, it will mean that it is - // equal to the `protocol_version_id` value, since it the interface from before the semver was supported. - let protocol_version_id = if protocol_version < U256::from(PACKED_SEMVER_MINOR_MASK) { - ProtocolVersionId::try_from(protocol_version.as_u32() as u16).unwrap() - } else { - ProtocolVersionId::try_from_packed_semver(protocol_version).unwrap() - }; + let verifier_address = + Self::parse_address(call_results_iterator.next().unwrap(), "verifier address")?; + + let chain_protocol_version_id = Self::parse_protocol_version( + call_results_iterator.next().unwrap(), + "contract protocol version", + )?; + let stm_protocol_version_id = Self::parse_protocol_version( + call_results_iterator.next().unwrap(), + "STM protocol version", + )?; + let stm_validator_timelock_address = Self::parse_address( + call_results_iterator.next().unwrap(), + "STM validator timelock address", + )?; return Ok(MulticallData { base_system_contracts_hashes, verifier_address, - protocol_version_id, + chain_protocol_version_id, + stm_protocol_version_id, + stm_validator_timelock_address, }); } parse_error(&[token]) } + fn parse_protocol_version( + data: Token, + name: &'static str, + ) -> Result { + let multicall_data = Multicall3Result::from_token(data)?.return_data; + if multicall_data.len() != 32 { + return Err(EthSenderError::Parse(Web3ContractError::InvalidOutputType( + format!( + "multicall3 {name} data is not of the len of 32: {:?}", + multicall_data + ), + ))); + } + + let protocol_version = U256::from_big_endian(&multicall_data); + // In case the protocol version is smaller than `PACKED_SEMVER_MINOR_MASK`, it will mean that it is + // equal to the `protocol_version_id` value, since it the interface from before the semver was supported. + let protocol_version_id = if protocol_version < U256::from(PACKED_SEMVER_MINOR_MASK) { + ProtocolVersionId::try_from(protocol_version.as_u32() as u16).unwrap() + } else { + ProtocolVersionId::try_from_packed_semver(protocol_version).unwrap() + }; + + Ok(protocol_version_id) + } + + fn parse_address(data: Token, name: &'static str) -> Result { + let multicall_data = Multicall3Result::from_token(data)?.return_data; + if multicall_data.len() != 32 { + return Err(EthSenderError::Parse(Web3ContractError::InvalidOutputType( + format!( + "multicall3 {name} data is not of the len of 32: {:?}", + multicall_data + ), + ))); + } + + Ok(Address::from_slice(&multicall_data[12..])) + } + + fn timelock_contract_address( + &self, + chain_protocol_version_id: ProtocolVersionId, + stm_protocol_version_id: ProtocolVersionId, + stm_validator_timelock_address: Address, + ) -> Address { + if chain_protocol_version_id == stm_protocol_version_id { + stm_validator_timelock_address + } else { + self.config_timelock_contract_address + } + } + /// Loads current verifier config on L1 async fn get_snark_wrapper_vk_hash( &mut self, @@ -382,6 +464,32 @@ impl EthTxAggregator { Ok(vk_hash) } + /// Returns whether there is a pending gateway upgrade. + /// During gateway upgrade, the signature of the `executeBatches` function on `ValidatorTimelock` will change. + /// This means that transactions that were created before the upgrade but were sent right after it + /// will fail, which we want to avoid. + async fn is_pending_gateway_upgrade( + storage: &mut Connection<'_, Core>, + chain_protocol_version: ProtocolVersionId, + ) -> bool { + // If the gateway protocol version is present in the DB, and its timestamp is larger than `now`, it means that + // the upgrade process on the server has begun. + // However, if the protocol version on the contract is lower than the `gateway_upgrade`, it means that the upgrade has + // not yet completed. + + if storage + .blocks_dal() + .pending_protocol_version() + .await + .unwrap() + < ProtocolVersionId::gateway_upgrade() + { + return false; + } + + chain_protocol_version < ProtocolVersionId::gateway_upgrade() + } + async fn get_fflonk_snark_wrapper_vk_hash( &mut self, verifier_address: Address, @@ -417,7 +525,9 @@ impl EthTxAggregator { let MulticallData { base_system_contracts_hashes, verifier_address, - protocol_version_id, + chain_protocol_version_id, + stm_protocol_version_id, + stm_validator_timelock_address, } = self.get_multicall_data().await.map_err(|err| { tracing::error!("Failed to get multicall data {err:?}"); err @@ -442,37 +552,51 @@ impl EthTxAggregator { snark_wrapper_vk_hash, fflonk_snark_wrapper_vk_hash, }; + + let mut op_restrictions = OperationSkippingRestrictions { + commit_restriction: self + .config + .tx_aggregation_only_prove_and_execute + .then_some("tx_aggregation_only_prove_and_execute=true"), + prove_restriction: None, + execute_restriction: Self::is_pending_gateway_upgrade( + storage, + chain_protocol_version_id, + ) + .await + .then_some("there is a pending gateway upgrade"), + }; + if self.config.tx_aggregation_paused { + let reason = Some("tx aggregation is paused"); + op_restrictions.commit_restriction = reason; + op_restrictions.prove_restriction = reason; + op_restrictions.execute_restriction = reason; + } + if let Some(agg_op) = self .aggregator .get_next_ready_operation( storage, base_system_contracts_hashes, - protocol_version_id, + chain_protocol_version_id, l1_verifier_config, + op_restrictions, ) .await? { - if self.config.tx_aggregation_paused { - tracing::info!( - "Skipping sending operation of type {} for batches {}-{} \ - as tx_aggregation_paused=true", - agg_op.get_action_type(), - agg_op.l1_batch_range().start(), - agg_op.l1_batch_range().end() - ); - return Ok(()); - } - if self.config.tx_aggregation_only_prove_and_execute && !agg_op.is_prove_or_execute() { - tracing::info!( - "Skipping sending commit operation for batches {}-{} \ - as tx_aggregation_only_prove_and_execute=true", - agg_op.l1_batch_range().start(), - agg_op.l1_batch_range().end() - ); - return Ok(()); - } let is_gateway = self.settlement_mode.is_gateway(); - let tx = self.save_eth_tx(storage, &agg_op, is_gateway).await?; + let tx = self + .save_eth_tx( + storage, + &agg_op, + self.timelock_contract_address( + chain_protocol_version_id, + stm_protocol_version_id, + stm_validator_timelock_address, + ), + is_gateway, + ) + .await?; Self::report_eth_tx_saving(storage, &agg_op, &tx).await; self.health_updater.update( @@ -618,6 +742,7 @@ impl EthTxAggregator { &self, storage: &mut Connection<'_, Core>, aggregated_op: &AggregatedOperation, + timelock_contract_address: Address, is_gateway: bool, ) -> Result { let mut transaction = storage.start_transaction().await.unwrap(); @@ -653,7 +778,7 @@ impl EthTxAggregator { nonce, encoded_aggregated_op.calldata, op_type, - self.timelock_contract_address, + timelock_contract_address, eth_tx_predicted_gas, sender_addr, encoded_aggregated_op.sidecar, diff --git a/core/node/eth_sender/src/tester.rs b/core/node/eth_sender/src/tester.rs index e7d9f2ac87e7..943e808cfa6b 100644 --- a/core/node/eth_sender/src/tester.rs +++ b/core/node/eth_sender/src/tester.rs @@ -24,6 +24,7 @@ use crate::{ }; pub(super) const STATE_TRANSITION_CONTRACT_ADDRESS: Address = Address::repeat_byte(0xa0); +pub(super) const STATE_TRANSITION_MANAGER_CONTRACT_ADDRESS: Address = Address::repeat_byte(0xb0); // Alias to conveniently call static methods of `ETHSender`. type MockEthTxManager = EthTxManager; @@ -268,6 +269,7 @@ impl EthSenderTester { gateway.clone(), // ZKsync contract address Address::random(), + STATE_TRANSITION_MANAGER_CONTRACT_ADDRESS, contracts_config.l1_multicall3_addr, STATE_TRANSITION_CONTRACT_ADDRESS, Default::default(), @@ -522,6 +524,7 @@ impl EthSenderTester { .save_eth_tx( &mut self.conn.connection().await.unwrap(), &aggregated_operation, + Address::random(), self.is_l2, ) .await diff --git a/core/node/eth_sender/src/tests.rs b/core/node/eth_sender/src/tests.rs index aab6d2e43d76..f104d222982a 100644 --- a/core/node/eth_sender/src/tests.rs +++ b/core/node/eth_sender/src/tests.rs @@ -11,18 +11,19 @@ use zksync_types::{ commitment::{ L1BatchCommitmentMode, L1BatchMetaParameters, L1BatchMetadata, L1BatchWithMetadata, }, - ethabi, - ethabi::Token, + ethabi::{self, Token}, helpers::unix_timestamp_ms, - web3, - web3::contract::Error, + web3::{self, contract::Error}, Address, ProtocolVersionId, H256, }; use crate::{ abstract_l1_interface::OperatorType, aggregated_operations::AggregatedOperation, - tester::{EthSenderTester, TestL1Batch, STATE_TRANSITION_CONTRACT_ADDRESS}, + tester::{ + EthSenderTester, TestL1Batch, STATE_TRANSITION_CONTRACT_ADDRESS, + STATE_TRANSITION_MANAGER_CONTRACT_ADDRESS, + }, zksync_functions::ZkSyncFunctions, EthSenderError, }; @@ -66,27 +67,53 @@ pub(crate) fn mock_multicall_response(call: &web3::CallRequest) -> Token { panic!("Unexpected input: {tokens:?}"); }; + let validator_timelock_short_selector = functions + .state_transition_manager_contract + .function("validatorTimelock") + .unwrap() + .short_signature(); + let prototol_version_short_selector = functions + .state_transition_manager_contract + .function("protocolVersion") + .unwrap() + .short_signature(); + let calls = tokens.into_iter().map(Multicall3Call::from_token); let response = calls.map(|call| { let call = call.unwrap(); - assert_eq!(call.target, STATE_TRANSITION_CONTRACT_ADDRESS); let output = match &call.calldata[..4] { selector if selector == bootloader_signature => { + assert!(call.target == STATE_TRANSITION_CONTRACT_ADDRESS); vec![1u8; 32] } selector if selector == default_aa_signature => { + assert!(call.target == STATE_TRANSITION_CONTRACT_ADDRESS); vec![2u8; 32] } selector if Some(selector) == evm_emulator_getter_signature => { + assert!(call.target == STATE_TRANSITION_CONTRACT_ADDRESS); vec![3u8; 32] } selector if selector == functions.get_verifier_params.short_signature() => { + assert!(call.target == STATE_TRANSITION_CONTRACT_ADDRESS); vec![4u8; 96] } selector if selector == functions.get_verifier.short_signature() => { + assert!(call.target == STATE_TRANSITION_CONTRACT_ADDRESS); vec![5u8; 32] } selector if selector == functions.get_protocol_version.short_signature() => { + assert!(call.target == STATE_TRANSITION_CONTRACT_ADDRESS); + H256::from_low_u64_be(ProtocolVersionId::default() as u64) + .0 + .to_vec() + } + selector if selector == validator_timelock_short_selector => { + assert!(call.target == STATE_TRANSITION_MANAGER_CONTRACT_ADDRESS); + vec![6u8; 32] + } + selector if selector == prototol_version_short_selector => { + assert!(call.target == STATE_TRANSITION_MANAGER_CONTRACT_ADDRESS); H256::from_low_u64_be(ProtocolVersionId::default() as u64) .0 .to_vec() @@ -208,6 +235,7 @@ async fn resend_each_block(commitment_mode: L1BatchCommitmentMode) -> anyhow::Re .save_eth_tx( &mut tester.conn.connection().await.unwrap(), &get_dummy_operation(0), + Address::random(), false, ) .await?; @@ -729,6 +757,15 @@ async fn parsing_multicall_data(with_evm_emulator: bool) { .to_vec(), ), ]), + Token::Tuple(vec![ + Token::Bool(true), + Token::Bytes( + H256::from_low_u64_be(ProtocolVersionId::latest() as u64) + .0 + .to_vec(), + ), + ]), + Token::Tuple(vec![Token::Bool(true), Token::Bytes(vec![6u8; 32])]), ]; if with_evm_emulator { mock_response.insert( @@ -756,7 +793,15 @@ async fn parsing_multicall_data(with_evm_emulator: bool) { expected_evm_emulator_hash ); assert_eq!(parsed.verifier_address, Address::repeat_byte(5)); - assert_eq!(parsed.protocol_version_id, ProtocolVersionId::latest()); + assert_eq!( + parsed.chain_protocol_version_id, + ProtocolVersionId::latest() + ); + assert_eq!( + parsed.stm_validator_timelock_address, + Address::repeat_byte(6) + ); + assert_eq!(parsed.stm_protocol_version_id, ProtocolVersionId::latest()); } #[test_log::test(tokio::test)] @@ -848,5 +893,5 @@ async fn get_multicall_data(commitment_mode: L1BatchCommitmentMode) { ); assert_eq!(data.base_system_contracts_hashes.evm_emulator, None); assert_eq!(data.verifier_address, Address::repeat_byte(5)); - assert_eq!(data.protocol_version_id, ProtocolVersionId::latest()); + assert_eq!(data.chain_protocol_version_id, ProtocolVersionId::latest()); } diff --git a/core/node/eth_sender/src/zksync_functions.rs b/core/node/eth_sender/src/zksync_functions.rs index f3e4998ef37c..5c2088f7cec7 100644 --- a/core/node/eth_sender/src/zksync_functions.rs +++ b/core/node/eth_sender/src/zksync_functions.rs @@ -1,6 +1,7 @@ use zksync_contracts::{ - hyperchain_contract, multicall_contract, verifier_contract, POST_SHARED_BRIDGE_COMMIT_FUNCTION, - POST_SHARED_BRIDGE_EXECUTE_FUNCTION, POST_SHARED_BRIDGE_PROVE_FUNCTION, + hyperchain_contract, multicall_contract, state_transition_manager_contract, verifier_contract, + POST_SHARED_BRIDGE_COMMIT_FUNCTION, POST_SHARED_BRIDGE_EXECUTE_FUNCTION, + POST_SHARED_BRIDGE_PROVE_FUNCTION, }; use zksync_types::ethabi::{Contract, Function}; @@ -26,6 +27,8 @@ pub(super) struct ZkSyncFunctions { pub(super) multicall_contract: Contract, pub(super) aggregate3: Function, + + pub(super) state_transition_manager_contract: Contract, } fn get_function(contract: &Contract, name: &str) -> Function { @@ -51,6 +54,7 @@ impl Default for ZkSyncFunctions { let zksync_contract = hyperchain_contract(); let verifier_contract = verifier_contract(); let multicall_contract = multicall_contract(); + let state_transition_manager_contract = state_transition_manager_contract(); let post_shared_bridge_commit = POST_SHARED_BRIDGE_COMMIT_FUNCTION.clone(); let post_shared_bridge_prove = POST_SHARED_BRIDGE_PROVE_FUNCTION.clone(); @@ -89,6 +93,7 @@ impl Default for ZkSyncFunctions { verification_key_hash, multicall_contract, aggregate3, + state_transition_manager_contract, } } } diff --git a/core/node/node_framework/src/implementations/layers/eth_sender/aggregator.rs b/core/node/node_framework/src/implementations/layers/eth_sender/aggregator.rs index b412c376f68d..1642db9be367 100644 --- a/core/node/node_framework/src/implementations/layers/eth_sender/aggregator.rs +++ b/core/node/node_framework/src/implementations/layers/eth_sender/aggregator.rs @@ -111,24 +111,33 @@ impl WiringLayer for EthTxAggregatorLayer { tracing::info!("Gateway contracts: {:?}", self.gateway_chain_config); // Get resources. - let (validator_timelock_addr, multicall3_addr, diamond_proxy_addr) = - if self.settlement_mode.is_gateway() { - let gateway_chain_config = self - .gateway_chain_config - .as_ref() - .context("gateway_chain_config")?; - ( - gateway_chain_config.validator_timelock_addr, - gateway_chain_config.multicall3_addr, - gateway_chain_config.diamond_proxy_addr, - ) - } else { - ( - self.contracts_config.validator_timelock_addr, - self.contracts_config.l1_multicall3_addr, - self.contracts_config.diamond_proxy_addr, - ) - }; + let ( + validator_timelock_addr, + multicall3_addr, + diamond_proxy_addr, + state_transition_manager_address, + ) = if self.settlement_mode.is_gateway() { + let gateway_chain_config = self + .gateway_chain_config + .as_ref() + .context("gateway_chain_config")?; + ( + gateway_chain_config.validator_timelock_addr, + gateway_chain_config.multicall3_addr, + gateway_chain_config.diamond_proxy_addr, + gateway_chain_config.state_transition_proxy_addr, + ) + } else { + ( + self.contracts_config.validator_timelock_addr, + self.contracts_config.l1_multicall3_addr, + self.contracts_config.diamond_proxy_addr, + self.contracts_config + .ecosystem_contracts + .context("Missing ecosystem contracts")? + .state_transition_proxy_addr, + ) + }; let eth_client = if self.settlement_mode.is_gateway() { input @@ -167,6 +176,7 @@ impl WiringLayer for EthTxAggregatorLayer { aggregator, eth_client, validator_timelock_addr, + state_transition_manager_address, multicall3_addr, diamond_proxy_addr, self.zksync_network_id, diff --git a/core/node/node_framework/src/implementations/layers/web3_api/server/bridge_addresses.rs b/core/node/node_framework/src/implementations/layers/web3_api/server/bridge_addresses.rs index 4ba8098c8399..785c19846a60 100644 --- a/core/node/node_framework/src/implementations/layers/web3_api/server/bridge_addresses.rs +++ b/core/node/node_framework/src/implementations/layers/web3_api/server/bridge_addresses.rs @@ -1,20 +1,87 @@ use std::time::Duration; +use zksync_eth_client::CallFunctionArgs; use zksync_node_api_server::web3::state::BridgeAddressesHandle; +use zksync_types::{ethabi::Contract, Address}; use zksync_web3_decl::{ - client::{DynClient, L2}, + client::{DynClient, L1, L2}, namespaces::ZksNamespaceClient, }; use crate::{StopReceiver, Task, TaskId}; #[derive(Debug)] -pub struct BridgeAddressesUpdaterTask { +pub struct MainNodeUpdaterInner { pub bridge_address_updater: BridgeAddressesHandle, pub main_node_client: Box>, pub update_interval: Option, } +impl MainNodeUpdaterInner { + async fn loop_iteration(&self) { + match self.main_node_client.get_bridge_contracts().await { + Ok(bridge_addresses) => { + self.bridge_address_updater.update(bridge_addresses).await; + } + Err(err) => { + tracing::error!("Failed to query `get_bridge_contracts`, error: {:?}", err); + } + } + } +} + +#[derive(Debug)] +pub struct L1UpdaterInner { + pub bridge_address_updater: BridgeAddressesHandle, + pub l1_eth_client: Box>, + pub bridgehub_addr: Address, + pub update_interval: Option, + pub bridgehub_abi: Contract, +} + +impl L1UpdaterInner { + async fn loop_iteration(&self) { + let call_result = CallFunctionArgs::new("sharedBridge", ()) + .for_contract(self.bridgehub_addr, &self.bridgehub_abi) + .call(&self.l1_eth_client) + .await; + + match call_result { + Ok(shared_bridge_address) => { + self.bridge_address_updater + .update_l1_shared_bridge(shared_bridge_address) + .await; + } + Err(err) => { + tracing::error!("Failed to query shared bridge address, error: {err:?}"); + } + } + } +} + +// Define the enum to hold either updater +#[derive(Debug)] +pub enum BridgeAddressesUpdaterTask { + L1Updater(L1UpdaterInner), + MainNodeUpdater(MainNodeUpdaterInner), +} + +impl BridgeAddressesUpdaterTask { + async fn loop_iteration(&self) { + match self { + BridgeAddressesUpdaterTask::L1Updater(updater) => updater.loop_iteration().await, + BridgeAddressesUpdaterTask::MainNodeUpdater(updater) => updater.loop_iteration().await, + } + } + + fn update_interval(&self) -> Option { + match self { + BridgeAddressesUpdaterTask::L1Updater(updater) => updater.update_interval, + BridgeAddressesUpdaterTask::MainNodeUpdater(updater) => updater.update_interval, + } + } +} + #[async_trait::async_trait] impl Task for BridgeAddressesUpdaterTask { fn id(&self) -> TaskId { @@ -24,16 +91,9 @@ impl Task for BridgeAddressesUpdaterTask { async fn run(self: Box, mut stop_receiver: StopReceiver) -> anyhow::Result<()> { const DEFAULT_INTERVAL: Duration = Duration::from_secs(30); - let update_interval = self.update_interval.unwrap_or(DEFAULT_INTERVAL); + let update_interval = self.update_interval().unwrap_or(DEFAULT_INTERVAL); while !*stop_receiver.0.borrow_and_update() { - match self.main_node_client.get_bridge_contracts().await { - Ok(bridge_addresses) => { - self.bridge_address_updater.update(bridge_addresses).await; - } - Err(err) => { - tracing::error!("Failed to query `get_bridge_contracts`, error: {err:?}"); - } - } + self.loop_iteration().await; if tokio::time::timeout(update_interval, stop_receiver.0.changed()) .await diff --git a/core/node/node_framework/src/implementations/layers/web3_api/server/mod.rs b/core/node/node_framework/src/implementations/layers/web3_api/server/mod.rs index 390d321647cf..c4c18b6ecb3f 100644 --- a/core/node/node_framework/src/implementations/layers/web3_api/server/mod.rs +++ b/core/node/node_framework/src/implementations/layers/web3_api/server/mod.rs @@ -1,8 +1,11 @@ use std::{num::NonZeroU32, time::Duration}; +use anyhow::Context; +use bridge_addresses::{L1UpdaterInner, MainNodeUpdaterInner}; use tokio::{sync::oneshot, task::JoinHandle}; use zksync_circuit_breaker::replication_lag::ReplicationLagChecker; use zksync_config::configs::api::MaxResponseSize; +use zksync_contracts::bridgehub_contract; use zksync_node_api_server::web3::{ state::{BridgeAddressesHandle, InternalApiConfig, SealedL2BlockNumber}, ApiBuilder, ApiServer, Namespace, @@ -15,6 +18,7 @@ use crate::{ }, resources::{ circuit_breakers::CircuitBreakersResource, + eth_interface::EthInterfaceResource, healthcheck::AppHealthCheckResource, main_node_client::MainNodeClientResource, pools::{PoolResource, ReplicaPool}, @@ -128,6 +132,7 @@ pub struct Input { #[context(default)] pub app_health: AppHealthCheckResource, pub main_node_client: Option, + pub l1_eth_client: EthInterfaceResource, } #[derive(Debug, IntoContext)] @@ -140,7 +145,7 @@ pub struct Output { #[context(task)] pub sealed_l2_block_updater_task: SealedL2BlockUpdaterTask, #[context(task)] - pub bridge_addresses_updater_task: Option, + pub bridge_addresses_updater_task: BridgeAddressesUpdaterTask, } impl Web3ServerLayer { @@ -201,15 +206,28 @@ impl WiringLayer for Web3ServerLayer { number_updater: sealed_l2_block_handle.clone(), pool: updaters_pool, }; - // Bridge addresses updater task must be started for ENs and only for ENs. - let bridge_addresses_updater_task = - input - .main_node_client - .map(|main_node_client| BridgeAddressesUpdaterTask { - bridge_address_updater: bridge_addresses_handle.clone(), - main_node_client: main_node_client.0, - update_interval: self.optional_config.bridge_addresses_refresh_interval, - }); + + // In case it is an EN, the bridge addresses should be updated by fetching values from the main node. + // It is the main node, the bridge addresses need to be updated by querying the L1. + + let bridge_addresses_updater_task = if let Some(main_node_client) = input.main_node_client { + BridgeAddressesUpdaterTask::MainNodeUpdater(MainNodeUpdaterInner { + bridge_address_updater: bridge_addresses_handle.clone(), + main_node_client: main_node_client.0, + update_interval: self.optional_config.bridge_addresses_refresh_interval, + }) + } else { + BridgeAddressesUpdaterTask::L1Updater(L1UpdaterInner { + bridge_address_updater: bridge_addresses_handle.clone(), + l1_eth_client: input.l1_eth_client.0, + bridgehub_addr: self + .internal_api_config + .l1_bridgehub_proxy_addr + .context("Lacking l1 bridgehub proxy address")?, + update_interval: self.optional_config.bridge_addresses_refresh_interval, + bridgehub_abi: bridgehub_contract(), + }) + }; // Build server. let mut api_builder = From f09087bab397778976af42c321cbba93f9706b5a Mon Sep 17 00:00:00 2001 From: perekopskiy <53865202+perekopskiy@users.noreply.github.com> Date: Mon, 6 Jan 2025 18:51:59 +0200 Subject: [PATCH 04/97] feat: pubdata type changes from sync-layer-stable (#3425) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Changes to PubdataType from sync-layer-stable: Validium variant is removed and specific DAs are added. This change is breaking but - for DB only `Rollup` is used for pre-gateway and this variant wasn't changed - for consensus protobuf `pubdata_params` is null for pre-gateway so it should be good. Changes to allow reading system bytecodes from L2 upgrade tx factory deps. Fix `metadata[3]` in eth watcher processor. ## Why ❔ Reduce sync-layer-stable diff ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- core/bin/zksync_server/src/node_builder.rs | 25 +++- core/lib/basic_types/src/commitment.rs | 29 ++++- core/lib/dal/src/consensus/conv.rs | 29 ++--- core/lib/dal/src/consensus/proto/mod.proto | 11 +- core/lib/dal/src/consensus/tests.rs | 10 +- core/lib/dal/src/factory_deps_dal.rs | 40 +++--- core/lib/dal/src/models/storage_block.rs | 4 +- core/lib/dal/src/models/storage_sync.rs | 4 +- core/lib/dal/src/protocol_versions_dal.rs | 51 +------- core/lib/env_config/src/genesis.rs | 1 - .../{rollup.rs => full_builder.rs} | 40 +----- .../{validium.rs => hashed_builder.rs} | 10 +- core/lib/multivm/src/pubdata_builders/mod.rs | 22 ++-- .../lib/multivm/src/pubdata_builders/tests.rs | 22 ++-- .../lib/multivm/src/pubdata_builders/utils.rs | 35 +++++- .../src/versions/testonly/l1_messenger.rs | 6 +- core/lib/multivm/src/versions/testonly/mod.rs | 4 +- core/lib/types/src/commitment/mod.rs | 2 +- core/lib/vm_executor/src/storage.rs | 119 ++++++++++++++++-- core/node/consistency_checker/src/lib.rs | 48 +++---- .../appended_chain_batch_root.rs | 3 + core/node/eth_watch/src/tests/mod.rs | 14 +-- .../src/api_server/tests.rs | 12 +- .../layers/state_keeper/mempool_io.rs | 10 +- core/node/node_sync/src/external_io.rs | 4 +- core/node/state_keeper/src/io/mempool.rs | 29 ++--- 26 files changed, 344 insertions(+), 240 deletions(-) rename core/lib/multivm/src/pubdata_builders/{rollup.rs => full_builder.rs} (65%) rename core/lib/multivm/src/pubdata_builders/{validium.rs => hashed_builder.rs} (90%) diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index f1db73164eb1..96205eb9fbfc 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -72,7 +72,10 @@ use zksync_node_framework::{ service::{ZkStackService, ZkStackServiceBuilder}, }; use zksync_types::{ - pubdata_da::PubdataSendingMode, settlement::SettlementMode, SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, + commitment::{L1BatchCommitmentMode, PubdataType}, + pubdata_da::PubdataSendingMode, + settlement::SettlementMode, + SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, }; use zksync_vlog::prometheus::PrometheusExporterConfig; @@ -118,6 +121,24 @@ impl MainNodeBuilder { self.node.runtime_handle() } + pub fn get_pubdata_type(&self) -> PubdataType { + if self.genesis_config.l1_batch_commit_data_generator_mode == L1BatchCommitmentMode::Rollup + { + return PubdataType::Rollup; + } + + let Some(da_client_config) = self.configs.da_client_config.clone() else { + return PubdataType::NoDA; + }; + + match da_client_config { + DAClientConfig::Avail(_) => PubdataType::Avail, + DAClientConfig::Celestia(_) => PubdataType::Celestia, + DAClientConfig::Eigen(_) => PubdataType::Eigen, + DAClientConfig::ObjectStore(_) => PubdataType::ObjectStore, + } + } + fn add_sigint_handler_layer(mut self) -> anyhow::Result { self.node.add_layer(SigintHandlerLayer); Ok(self) @@ -252,7 +273,7 @@ impl MainNodeBuilder { try_load_config!(self.configs.mempool_config), try_load_config!(wallets.state_keeper), self.contracts_config.l2_da_validator_addr, - self.genesis_config.l1_batch_commit_data_generator_mode, + self.get_pubdata_type(), ); let db_config = try_load_config!(self.configs.db_config); let experimental_vm_config = self diff --git a/core/lib/basic_types/src/commitment.rs b/core/lib/basic_types/src/commitment.rs index 0eed46aad782..c43a55bab4a9 100644 --- a/core/lib/basic_types/src/commitment.rs +++ b/core/lib/basic_types/src/commitment.rs @@ -58,8 +58,35 @@ impl FromStr for L1BatchCommitmentMode { } } +#[derive(Default, Copy, Debug, Clone, PartialEq, Serialize, Deserialize, Display)] +pub enum PubdataType { + #[default] + Rollup, + NoDA, + Avail, + Celestia, + Eigen, + ObjectStore, +} + +impl FromStr for PubdataType { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + match s { + "Rollup" => Ok(Self::Rollup), + "NoDA" => Ok(Self::NoDA), + "Avail" => Ok(Self::Avail), + "Celestia" => Ok(Self::Celestia), + "Eigen" => Ok(Self::Eigen), + "ObjectStore" => Ok(Self::ObjectStore), + _ => Err("Incorrect DA client type; expected one of `Rollup`, `NoDA`, `Avail`, `Celestia`, `Eigen`, `ObjectStore`"), + } + } +} + #[derive(Default, Copy, Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct PubdataParams { pub l2_da_validator_address: Address, - pub pubdata_type: L1BatchCommitmentMode, + pub pubdata_type: PubdataType, } diff --git a/core/lib/dal/src/consensus/conv.rs b/core/lib/dal/src/consensus/conv.rs index 3153343d6014..140c3f6a84c3 100644 --- a/core/lib/dal/src/consensus/conv.rs +++ b/core/lib/dal/src/consensus/conv.rs @@ -5,7 +5,7 @@ use zksync_consensus_roles::{attester, node}; use zksync_protobuf::{read_optional_repr, read_required, required, ProtoFmt, ProtoRepr}; use zksync_types::{ abi, - commitment::{L1BatchCommitmentMode, PubdataParams}, + commitment::{PubdataParams, PubdataType}, ethabi, fee::Fee, h256_to_u256, @@ -112,8 +112,8 @@ impl ProtoRepr for proto::PubdataParams { l2_da_validator_address: required(&self.l2_da_validator_address) .and_then(|a| parse_h160(a)) .context("l2_da_validator_address")?, - pubdata_type: required(&self.pubdata_type) - .and_then(|x| Ok(proto::L1BatchCommitDataGeneratorMode::try_from(*x)?)) + pubdata_type: required(&self.pubdata_info) + .and_then(|x| Ok(proto::PubdataType::try_from(*x)?)) .context("pubdata_type")? .parse(), }) @@ -122,9 +122,7 @@ impl ProtoRepr for proto::PubdataParams { fn build(this: &Self::Type) -> Self { Self { l2_da_validator_address: Some(this.l2_da_validator_address.as_bytes().into()), - pubdata_type: Some( - proto::L1BatchCommitDataGeneratorMode::new(&this.pubdata_type) as i32, - ), + pubdata_info: Some(this.pubdata_type as i32), } } } @@ -572,18 +570,15 @@ impl ProtoRepr for proto::AttesterCommittee { } } -impl proto::L1BatchCommitDataGeneratorMode { - pub(crate) fn new(n: &L1BatchCommitmentMode) -> Self { - match n { - L1BatchCommitmentMode::Rollup => Self::Rollup, - L1BatchCommitmentMode::Validium => Self::Validium, - } - } - - pub(crate) fn parse(&self) -> L1BatchCommitmentMode { +impl proto::PubdataType { + pub(crate) fn parse(&self) -> PubdataType { match self { - Self::Rollup => L1BatchCommitmentMode::Rollup, - Self::Validium => L1BatchCommitmentMode::Validium, + Self::Rollup => PubdataType::Rollup, + Self::NoDa => PubdataType::NoDA, + Self::Avail => PubdataType::Avail, + Self::Celestia => PubdataType::Celestia, + Self::Eigen => PubdataType::Eigen, + Self::ObjectStore => PubdataType::ObjectStore, } } } diff --git a/core/lib/dal/src/consensus/proto/mod.proto b/core/lib/dal/src/consensus/proto/mod.proto index 49a69e8a36ec..6bae78ca5fe9 100644 --- a/core/lib/dal/src/consensus/proto/mod.proto +++ b/core/lib/dal/src/consensus/proto/mod.proto @@ -31,7 +31,8 @@ message Payload { message PubdataParams { optional bytes l2_da_validator_address = 1; // required; H160 - optional L1BatchCommitDataGeneratorMode pubdata_type = 2; // required + optional PubdataType pubdata_info = 3; // required + reserved 2; reserved "pubdata_type"; } message L1Transaction { @@ -149,7 +150,11 @@ message AttestationStatus { optional uint64 next_batch_to_attest = 2; // required } -enum L1BatchCommitDataGeneratorMode { +enum PubdataType { Rollup = 0; - Validium = 1; + NoDA = 1; + Avail = 2; + Celestia = 3; + Eigen = 4; + ObjectStore = 5; } diff --git a/core/lib/dal/src/consensus/tests.rs b/core/lib/dal/src/consensus/tests.rs index 465148dc7b5c..125db0de89f5 100644 --- a/core/lib/dal/src/consensus/tests.rs +++ b/core/lib/dal/src/consensus/tests.rs @@ -9,7 +9,7 @@ use zksync_protobuf::{ }; use zksync_test_contracts::Account; use zksync_types::{ - commitment::{L1BatchCommitmentMode, PubdataParams}, + commitment::{PubdataParams, PubdataType}, web3::Bytes, Execute, ExecuteTransactionCommon, L1BatchNumber, ProtocolVersionId, Transaction, }; @@ -58,8 +58,12 @@ fn payload(rng: &mut impl Rng, protocol_version: ProtocolVersionId) -> Payload { } else { PubdataParams { pubdata_type: match rng.gen_range(0..2) { - 0 => L1BatchCommitmentMode::Rollup, - _ => L1BatchCommitmentMode::Validium, + 0 => PubdataType::Rollup, + 1 => PubdataType::NoDA, + 2 => PubdataType::Avail, + 3 => PubdataType::Celestia, + 4 => PubdataType::Eigen, + _ => PubdataType::ObjectStore, }, l2_da_validator_address: rng.gen(), } diff --git a/core/lib/dal/src/factory_deps_dal.rs b/core/lib/dal/src/factory_deps_dal.rs index 424d708da241..64cbe78fbd1a 100644 --- a/core/lib/dal/src/factory_deps_dal.rs +++ b/core/lib/dal/src/factory_deps_dal.rs @@ -89,39 +89,36 @@ impl FactoryDepsDal<'_, '_> { .map(|row| row.bytecode)) } - pub async fn get_base_system_contracts( + pub async fn get_base_system_contracts_from_factory_deps( &mut self, bootloader_hash: H256, default_aa_hash: H256, evm_emulator_hash: Option, - ) -> anyhow::Result { + ) -> anyhow::Result> { let bootloader_bytecode = self .get_sealed_factory_dep(bootloader_hash) .await - .context("failed loading bootloader code")? - .with_context(|| format!("bootloader code with hash {bootloader_hash:?} should be present in the database"))?; - let bootloader_code = SystemContractCode { - code: bootloader_bytecode, - hash: bootloader_hash, - }; + .context("failed loading bootloader code")?; let default_aa_bytecode = self .get_sealed_factory_dep(default_aa_hash) .await - .context("failed loading default account code")? - .with_context(|| format!("default account code with hash {default_aa_hash:?} should be present in the database"))?; + .context("failed loading default account code")?; - let default_aa_code = SystemContractCode { - code: default_aa_bytecode, - hash: default_aa_hash, + let (Some(bootloader_bytecode), Some(default_aa_bytecode)) = + (bootloader_bytecode, default_aa_bytecode) + else { + return Ok(None); }; let evm_emulator_code = if let Some(evm_emulator_hash) = evm_emulator_hash { let evm_emulator_bytecode = self .get_sealed_factory_dep(evm_emulator_hash) .await - .context("failed loading EVM emulator code")? - .with_context(|| format!("EVM emulator code with hash {evm_emulator_hash:?} should be present in the database"))?; + .context("failed loading EVM emulator code")?; + let Some(evm_emulator_bytecode) = evm_emulator_bytecode else { + return Ok(None); + }; Some(SystemContractCode { code: evm_emulator_bytecode, @@ -131,11 +128,20 @@ impl FactoryDepsDal<'_, '_> { None }; - Ok(BaseSystemContracts { + let bootloader_code = SystemContractCode { + code: bootloader_bytecode, + hash: bootloader_hash, + }; + + let default_aa_code = SystemContractCode { + code: default_aa_bytecode, + hash: default_aa_hash, + }; + Ok(Some(BaseSystemContracts { bootloader: bootloader_code, default_aa: default_aa_code, evm_emulator: evm_emulator_code, - }) + })) } /// Returns bytecodes for factory deps with the specified `hashes`. diff --git a/core/lib/dal/src/models/storage_block.rs b/core/lib/dal/src/models/storage_block.rs index 54635932a1af..1aea27a497f0 100644 --- a/core/lib/dal/src/models/storage_block.rs +++ b/core/lib/dal/src/models/storage_block.rs @@ -7,7 +7,7 @@ use zksync_contracts::BaseSystemContractsHashes; use zksync_types::{ api, block::{L1BatchHeader, L2BlockHeader, UnsealedL1BatchHeader}, - commitment::{L1BatchCommitmentMode, L1BatchMetaParameters, L1BatchMetadata, PubdataParams}, + commitment::{L1BatchMetaParameters, L1BatchMetadata, PubdataParams, PubdataType}, fee_model::{BatchFeeInput, L1PeggedBatchFeeModelInput, PubdataIndependentBatchFeeModelInput}, l2_to_l1_log::{L2ToL1Log, SystemL2ToL1Log, UserL2ToL1Log}, Address, Bloom, L1BatchNumber, L2BlockNumber, ProtocolVersionId, SLChainId, H256, @@ -556,7 +556,7 @@ impl From for L2BlockHeader { .unwrap_or_default(), pubdata_params: PubdataParams { l2_da_validator_address: Address::from_slice(&row.l2_da_validator_address), - pubdata_type: L1BatchCommitmentMode::from_str(&row.pubdata_type).unwrap(), + pubdata_type: PubdataType::from_str(&row.pubdata_type).unwrap(), }, } } diff --git a/core/lib/dal/src/models/storage_sync.rs b/core/lib/dal/src/models/storage_sync.rs index 3f80f52c56eb..bac51c9e14d9 100644 --- a/core/lib/dal/src/models/storage_sync.rs +++ b/core/lib/dal/src/models/storage_sync.rs @@ -4,7 +4,7 @@ use zksync_contracts::BaseSystemContractsHashes; use zksync_db_connection::error::SqlxContext; use zksync_types::{ api::en, - commitment::{L1BatchCommitmentMode, PubdataParams}, + commitment::{PubdataParams, PubdataType}, parse_h160, parse_h256, parse_h256_opt, Address, L1BatchNumber, L2BlockNumber, ProtocolVersionId, Transaction, H256, }; @@ -97,7 +97,7 @@ impl TryFrom for SyncBlock { hash: parse_h256(&block.hash).decode_column("hash")?, protocol_version: parse_protocol_version(block.protocol_version)?, pubdata_params: PubdataParams { - pubdata_type: L1BatchCommitmentMode::from_str(&block.pubdata_type) + pubdata_type: PubdataType::from_str(&block.pubdata_type) .decode_column("Invalid pubdata type")?, l2_da_validator_address: parse_h160(&block.l2_da_validator_address) .decode_column("l2_da_validator_address")?, diff --git a/core/lib/dal/src/protocol_versions_dal.rs b/core/lib/dal/src/protocol_versions_dal.rs index 11f6a93efdc4..18dd7516f4de 100644 --- a/core/lib/dal/src/protocol_versions_dal.rs +++ b/core/lib/dal/src/protocol_versions_dal.rs @@ -1,7 +1,7 @@ use std::convert::TryInto; use anyhow::Context as _; -use zksync_contracts::{BaseSystemContracts, BaseSystemContractsHashes}; +use zksync_contracts::BaseSystemContractsHashes; use zksync_db_connection::{ connection::Connection, error::DalResult, @@ -200,12 +200,10 @@ impl ProtocolVersionsDal<'_, '_> { ProtocolVersionId::try_from(row.id as u16).map_err(|err| sqlx::Error::Decode(err.into())) } - /// Returns base system contracts' hashes. Prefer `load_base_system_contracts_by_version_id` if - /// you also want to load the contracts themselves AND expect the contracts to be in the DB - /// already. + /// Returns base system contracts' hashes. pub async fn get_base_system_contract_hashes_by_version_id( &mut self, - version_id: u16, + version_id: ProtocolVersionId, ) -> anyhow::Result> { let row = sqlx::query!( r#" @@ -218,10 +216,10 @@ impl ProtocolVersionsDal<'_, '_> { WHERE id = $1 "#, - i32::from(version_id) + i32::from(version_id as u16) ) .instrument("get_base_system_contract_hashes_by_version_id") - .with_arg("version_id", &version_id) + .with_arg("version_id", &(version_id as u16)) .fetch_optional(self.storage) .await .context("cannot fetch system contract hashes")?; @@ -237,45 +235,6 @@ impl ProtocolVersionsDal<'_, '_> { }) } - pub async fn load_base_system_contracts_by_version_id( - &mut self, - version_id: u16, - ) -> anyhow::Result> { - let row = sqlx::query!( - r#" - SELECT - bootloader_code_hash, - default_account_code_hash, - evm_emulator_code_hash - FROM - protocol_versions - WHERE - id = $1 - "#, - i32::from(version_id) - ) - .instrument("load_base_system_contracts_by_version_id") - .with_arg("version_id", &version_id) - .fetch_optional(self.storage) - .await - .context("cannot fetch system contract hashes")?; - - Ok(if let Some(row) = row { - let contracts = self - .storage - .factory_deps_dal() - .get_base_system_contracts( - H256::from_slice(&row.bootloader_code_hash), - H256::from_slice(&row.default_account_code_hash), - row.evm_emulator_code_hash.as_deref().map(H256::from_slice), - ) - .await?; - Some(contracts) - } else { - None - }) - } - pub async fn get_protocol_version_with_latest_patch( &mut self, version_id: ProtocolVersionId, diff --git a/core/lib/env_config/src/genesis.rs b/core/lib/env_config/src/genesis.rs index 486d354e6cb5..60c5a48993d7 100644 --- a/core/lib/env_config/src/genesis.rs +++ b/core/lib/env_config/src/genesis.rs @@ -84,7 +84,6 @@ impl FromEnv for GenesisConfig { bootloader_hash: state_keeper.bootloader_hash, default_aa_hash: state_keeper.default_aa_hash, evm_emulator_hash: state_keeper.evm_emulator_hash, - // TODO(EVM-676): for now, the settlement layer is always the same as the L1 network l1_chain_id: L1ChainId(network_config.network.chain_id().0), l2_chain_id: network_config.zksync_network_id, snark_wrapper_vk_hash: contracts_config.snark_wrapper_vk_hash, diff --git a/core/lib/multivm/src/pubdata_builders/rollup.rs b/core/lib/multivm/src/pubdata_builders/full_builder.rs similarity index 65% rename from core/lib/multivm/src/pubdata_builders/rollup.rs rename to core/lib/multivm/src/pubdata_builders/full_builder.rs index 4a818dfe2314..40a260314a0a 100644 --- a/core/lib/multivm/src/pubdata_builders/rollup.rs +++ b/core/lib/multivm/src/pubdata_builders/full_builder.rs @@ -2,28 +2,27 @@ use zksync_types::{ ethabi, ethabi::{ParamType, Token}, l2_to_l1_log::l2_to_l1_logs_tree_size, - writes::compress_state_diffs, Address, ProtocolVersionId, }; use super::utils::{ build_chained_bytecode_hash, build_chained_log_hash, build_chained_message_hash, - build_logs_root, encode_user_logs, + build_logs_root, extend_from_pubdata_input, }; use crate::interface::pubdata::{PubdataBuilder, PubdataInput}; #[derive(Debug, Clone, Copy)] -pub struct RollupPubdataBuilder { +pub struct FullPubdataBuilder { pub l2_da_validator: Address, } -impl RollupPubdataBuilder { +impl FullPubdataBuilder { pub fn new(l2_da_validator: Address) -> Self { Self { l2_da_validator } } } -impl PubdataBuilder for RollupPubdataBuilder { +impl PubdataBuilder for FullPubdataBuilder { fn l2_da_validator(&self) -> Address { self.l2_da_validator } @@ -95,34 +94,3 @@ impl PubdataBuilder for RollupPubdataBuilder { pubdata } } - -fn extend_from_pubdata_input(buffer: &mut Vec, pubdata_input: &PubdataInput) { - let PubdataInput { - user_logs, - l2_to_l1_messages, - published_bytecodes, - state_diffs, - } = pubdata_input; - - // Adding user L2->L1 logs. - buffer.extend(encode_user_logs(user_logs)); - - // Encoding L2->L1 messages - // Format: `[(numberOfMessages as u32) || (messages[1].len() as u32) || messages[1] || ... || (messages[n].len() as u32) || messages[n]]` - buffer.extend((l2_to_l1_messages.len() as u32).to_be_bytes()); - for message in l2_to_l1_messages { - buffer.extend((message.len() as u32).to_be_bytes()); - buffer.extend(message); - } - // Encoding bytecodes - // Format: `[(numberOfBytecodes as u32) || (bytecodes[1].len() as u32) || bytecodes[1] || ... || (bytecodes[n].len() as u32) || bytecodes[n]]` - buffer.extend((published_bytecodes.len() as u32).to_be_bytes()); - for bytecode in published_bytecodes { - buffer.extend((bytecode.len() as u32).to_be_bytes()); - buffer.extend(bytecode); - } - // Encoding state diffs - // Format: `[size of compressed state diffs u32 || compressed state diffs || (# state diffs: intial + repeated) as u32 || sorted state diffs by ]` - let state_diffs_compressed = compress_state_diffs(state_diffs.clone()); - buffer.extend(state_diffs_compressed); -} diff --git a/core/lib/multivm/src/pubdata_builders/validium.rs b/core/lib/multivm/src/pubdata_builders/hashed_builder.rs similarity index 90% rename from core/lib/multivm/src/pubdata_builders/validium.rs rename to core/lib/multivm/src/pubdata_builders/hashed_builder.rs index a9156e970aad..d779e0172d5a 100644 --- a/core/lib/multivm/src/pubdata_builders/validium.rs +++ b/core/lib/multivm/src/pubdata_builders/hashed_builder.rs @@ -13,17 +13,17 @@ use super::utils::{ use crate::interface::pubdata::{PubdataBuilder, PubdataInput}; #[derive(Debug, Clone, Copy)] -pub struct ValidiumPubdataBuilder { +pub struct HashedPubdataBuilder { pub l2_da_validator: Address, } -impl ValidiumPubdataBuilder { +impl HashedPubdataBuilder { pub fn new(l2_da_validator: Address) -> Self { Self { l2_da_validator } } } -impl PubdataBuilder for ValidiumPubdataBuilder { +impl PubdataBuilder for HashedPubdataBuilder { fn l2_da_validator(&self) -> Address { self.l2_da_validator } @@ -35,7 +35,7 @@ impl PubdataBuilder for ValidiumPubdataBuilder { ) -> Vec { assert!( !protocol_version.is_pre_gateway(), - "ValidiumPubdataBuilder must not be called for pre gateway" + "HashedPubdataBuilder must not be called for pre gateway" ); let mut pubdata = vec![]; @@ -79,7 +79,7 @@ impl PubdataBuilder for ValidiumPubdataBuilder { ) -> Vec { assert!( !protocol_version.is_pre_gateway(), - "ValidiumPubdataBuilder must not be called for pre gateway" + "HashedPubdataBuilder must not be called for pre gateway" ); let state_diffs_packed = input diff --git a/core/lib/multivm/src/pubdata_builders/mod.rs b/core/lib/multivm/src/pubdata_builders/mod.rs index c52c4c70c86a..875a093370f4 100644 --- a/core/lib/multivm/src/pubdata_builders/mod.rs +++ b/core/lib/multivm/src/pubdata_builders/mod.rs @@ -1,24 +1,26 @@ use std::rc::Rc; -pub use rollup::RollupPubdataBuilder; -pub use validium::ValidiumPubdataBuilder; -use zksync_types::commitment::{L1BatchCommitmentMode, PubdataParams}; +pub use full_builder::FullPubdataBuilder; +pub use hashed_builder::HashedPubdataBuilder; +use zksync_types::commitment::{PubdataParams, PubdataType}; use crate::interface::pubdata::PubdataBuilder; -mod rollup; +mod full_builder; +mod hashed_builder; #[cfg(test)] mod tests; mod utils; -mod validium; pub fn pubdata_params_to_builder(params: PubdataParams) -> Rc { match params.pubdata_type { - L1BatchCommitmentMode::Rollup => { - Rc::new(RollupPubdataBuilder::new(params.l2_da_validator_address)) - } - L1BatchCommitmentMode::Validium => { - Rc::new(ValidiumPubdataBuilder::new(params.l2_da_validator_address)) + PubdataType::NoDA => Rc::new(HashedPubdataBuilder::new(params.l2_da_validator_address)), + PubdataType::Rollup + | PubdataType::Avail + | PubdataType::Celestia + | PubdataType::Eigen + | PubdataType::ObjectStore => { + Rc::new(FullPubdataBuilder::new(params.l2_da_validator_address)) } } } diff --git a/core/lib/multivm/src/pubdata_builders/tests.rs b/core/lib/multivm/src/pubdata_builders/tests.rs index b06cb9405aa7..a3894110c8a1 100644 --- a/core/lib/multivm/src/pubdata_builders/tests.rs +++ b/core/lib/multivm/src/pubdata_builders/tests.rs @@ -3,7 +3,7 @@ use zksync_types::{ ACCOUNT_CODE_STORAGE_ADDRESS, BOOTLOADER_ADDRESS, }; -use super::{rollup::RollupPubdataBuilder, validium::ValidiumPubdataBuilder}; +use super::{full_builder::FullPubdataBuilder, hashed_builder::HashedPubdataBuilder}; use crate::interface::pubdata::{L1MessengerL2ToL1Log, PubdataBuilder, PubdataInput}; fn mock_input() -> PubdataInput { @@ -54,13 +54,13 @@ fn mock_input() -> PubdataInput { } #[test] -fn test_rollup_pubdata_building() { +fn test_full_pubdata_building() { let input = mock_input(); - let rollup_pubdata_builder = RollupPubdataBuilder::new(Address::zero()); + let full_pubdata_builder = FullPubdataBuilder::new(Address::zero()); let actual = - rollup_pubdata_builder.l1_messenger_operator_input(&input, ProtocolVersionId::Version24); + full_pubdata_builder.l1_messenger_operator_input(&input, ProtocolVersionId::Version24); let expected = "00000001000000000000000000000000000000000000000000008001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000100000004deadbeef0000000100000060bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0100002a040001000000000000000000000000000000000000000000000000000000000000007e090e0000000c0901000000020000000000000000000000000000000000008002000000000000000000000000000000000000000000000000000000000000009b000000000000000000000000000000000000000000000000000000000000007d000000000000000c000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008002000000000000000000000000000000000000000000000000000000000000009c000000000000000000000000000000000000000000000000000000000000007e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; assert_eq!( &hex::encode(actual), @@ -69,7 +69,7 @@ fn test_rollup_pubdata_building() { ); let actual = - rollup_pubdata_builder.settlement_layer_pubdata(&input, ProtocolVersionId::Version24); + full_pubdata_builder.settlement_layer_pubdata(&input, ProtocolVersionId::Version24); let expected = "00000001000000000000000000000000000000000000000000008001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000100000004deadbeef0000000100000060bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0100002a040001000000000000000000000000000000000000000000000000000000000000007e090e0000000c0901"; assert_eq!( &hex::encode(actual), @@ -78,7 +78,7 @@ fn test_rollup_pubdata_building() { ); let actual = - rollup_pubdata_builder.l1_messenger_operator_input(&input, ProtocolVersionId::Version27); + full_pubdata_builder.l1_messenger_operator_input(&input, ProtocolVersionId::Version27); let expected = "89f9a07233e608561d90f7c4e7bcea24d718e425a6bd6c8eefb48a334366143694c75fae278944d856d68e33bbd32937cb3a1ea35cbf7d6eeeb1150f500dd0d64d0efe420d6dafe5897eab2fc27b2e47af303397ed285ace146d836d042717b0a3dc4b28a603a33b28ce1d5c52c593a46a15a99f1afa1c1d92715284288958fd54a93de700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000032300000001000000000000000000000000000000000000000000008001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000100000004deadbeef0000000100000060bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0100002a040001000000000000000000000000000000000000000000000000000000000000007e090e0000000c0901000000020000000000000000000000000000000000008002000000000000000000000000000000000000000000000000000000000000009b000000000000000000000000000000000000000000000000000000000000007d000000000000000c000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008002000000000000000000000000000000000000000000000000000000000000009c000000000000000000000000000000000000000000000000000000000000007e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; assert_eq!( &hex::encode(actual), @@ -87,7 +87,7 @@ fn test_rollup_pubdata_building() { ); let actual = - rollup_pubdata_builder.settlement_layer_pubdata(&input, ProtocolVersionId::Version27); + full_pubdata_builder.settlement_layer_pubdata(&input, ProtocolVersionId::Version27); let expected = "00000001000000000000000000000000000000000000000000008001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000800000000100000004deadbeef0000000100000060bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb0100002a040001000000000000000000000000000000000000000000000000000000000000007e090e0000000c0901"; assert_eq!( &hex::encode(actual), @@ -97,13 +97,13 @@ fn test_rollup_pubdata_building() { } #[test] -fn test_validium_pubdata_building() { +fn test_hashed_pubdata_building() { let input = mock_input(); - let validium_pubdata_builder = ValidiumPubdataBuilder::new(Address::zero()); + let hashed_pubdata_builder = HashedPubdataBuilder::new(Address::zero()); let actual = - validium_pubdata_builder.l1_messenger_operator_input(&input, ProtocolVersionId::Version27); + hashed_pubdata_builder.l1_messenger_operator_input(&input, ProtocolVersionId::Version27); let expected = "89f9a07233e608561d90f7c4e7bcea24d718e425a6bd6c8eefb48a334366143694c75fae278944d856d68e33bbd32937cb3a1ea35cbf7d6eeeb1150f500dd0d64d0efe420d6dafe5897eab2fc27b2e47af303397ed285ace146d836d042717b0a3dc4b28a603a33b28ce1d5c52c593a46a15a99f1afa1c1d92715284288958fd54a93de700000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000005c000000010000000000000000000000000000000000000000000080010000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000008000000000"; assert_eq!( &hex::encode(actual), @@ -112,7 +112,7 @@ fn test_validium_pubdata_building() { ); let actual = - validium_pubdata_builder.settlement_layer_pubdata(&input, ProtocolVersionId::Version27); + hashed_pubdata_builder.settlement_layer_pubdata(&input, ProtocolVersionId::Version27); let expected = "fa96e2436e6fb4d668f5a06681a7c53fcb199b2747ee624ee52a13e85aac5f1e"; assert_eq!( &hex::encode(actual), diff --git a/core/lib/multivm/src/pubdata_builders/utils.rs b/core/lib/multivm/src/pubdata_builders/utils.rs index 83c9b9317640..3b30832b79dd 100644 --- a/core/lib/multivm/src/pubdata_builders/utils.rs +++ b/core/lib/multivm/src/pubdata_builders/utils.rs @@ -1,7 +1,7 @@ use zksync_mini_merkle_tree::MiniMerkleTree; -use zksync_types::{bytecode::BytecodeHash, web3::keccak256}; +use zksync_types::{bytecode::BytecodeHash, web3::keccak256, writes::compress_state_diffs}; -use crate::interface::pubdata::L1MessengerL2ToL1Log; +use crate::interface::pubdata::{L1MessengerL2ToL1Log, PubdataInput}; pub(crate) fn build_chained_log_hash(user_logs: &[L1MessengerL2ToL1Log]) -> Vec { let mut chained_log_hash = vec![0u8; 32]; @@ -68,3 +68,34 @@ pub(crate) fn encode_user_logs(user_logs: &[L1MessengerL2ToL1Log]) -> Vec { } result } + +pub(crate) fn extend_from_pubdata_input(buffer: &mut Vec, pubdata_input: &PubdataInput) { + let PubdataInput { + user_logs, + l2_to_l1_messages, + published_bytecodes, + state_diffs, + } = pubdata_input; + + // Adding user L2->L1 logs. + buffer.extend(encode_user_logs(user_logs)); + + // Encoding L2->L1 messages + // Format: `[(numberOfMessages as u32) || (messages[1].len() as u32) || messages[1] || ... || (messages[n].len() as u32) || messages[n]]` + buffer.extend((l2_to_l1_messages.len() as u32).to_be_bytes()); + for message in l2_to_l1_messages { + buffer.extend((message.len() as u32).to_be_bytes()); + buffer.extend(message); + } + // Encoding bytecodes + // Format: `[(numberOfBytecodes as u32) || (bytecodes[1].len() as u32) || bytecodes[1] || ... || (bytecodes[n].len() as u32) || bytecodes[n]]` + buffer.extend((published_bytecodes.len() as u32).to_be_bytes()); + for bytecode in published_bytecodes { + buffer.extend((bytecode.len() as u32).to_be_bytes()); + buffer.extend(bytecode); + } + // Encoding state diffs + // Format: `[size of compressed state diffs u32 || compressed state diffs || (# state diffs: intial + repeated) as u32 || sorted state diffs by ]` + let state_diffs_compressed = compress_state_diffs(state_diffs.clone()); + buffer.extend(state_diffs_compressed); +} diff --git a/core/lib/multivm/src/versions/testonly/l1_messenger.rs b/core/lib/multivm/src/versions/testonly/l1_messenger.rs index 5d602b1e7d66..dcbc432aafd0 100644 --- a/core/lib/multivm/src/versions/testonly/l1_messenger.rs +++ b/core/lib/multivm/src/versions/testonly/l1_messenger.rs @@ -15,7 +15,7 @@ use crate::{ pubdata::{PubdataBuilder, PubdataInput}, InspectExecutionMode, TxExecutionMode, VmInterfaceExt, }, - pubdata_builders::RollupPubdataBuilder, + pubdata_builders::FullPubdataBuilder, vm_latest::constants::ZK_SYNC_BYTES_PER_BLOB, }; @@ -48,7 +48,7 @@ fn compose_header_for_l1_commit_rollup(input: PubdataInput) -> Vec { let uncompressed_state_diffs_hash = keccak256(&uncompressed_state_diffs); full_header.extend(uncompressed_state_diffs_hash); - let pubdata_builder = RollupPubdataBuilder::new(Address::zero()); + let pubdata_builder = FullPubdataBuilder::new(Address::zero()); let mut full_pubdata = pubdata_builder.settlement_layer_pubdata(&input, ProtocolVersionId::latest()); let full_pubdata_hash = keccak256(&full_pubdata); @@ -127,7 +127,7 @@ pub(crate) fn test_rollup_da_output_hash_match() { let result = vm.vm.execute(InspectExecutionMode::OneTx); assert!(!result.result.is_failed(), "Transaction wasn't successful"); - let pubdata_builder = RollupPubdataBuilder::new(l2_da_validator_address); + let pubdata_builder = FullPubdataBuilder::new(l2_da_validator_address); let batch_result = vm.vm.finish_batch(Rc::new(pubdata_builder)); assert!( !batch_result.block_tip_execution_result.result.is_failed(), diff --git a/core/lib/multivm/src/versions/testonly/mod.rs b/core/lib/multivm/src/versions/testonly/mod.rs index 5ab13df87337..8f24c849272d 100644 --- a/core/lib/multivm/src/versions/testonly/mod.rs +++ b/core/lib/multivm/src/versions/testonly/mod.rs @@ -28,7 +28,7 @@ pub(super) use self::tester::{ validation_params, TestedVm, TestedVmForValidation, VmTester, VmTesterBuilder, }; use crate::{ - interface::storage::InMemoryStorage, pubdata_builders::RollupPubdataBuilder, + interface::storage::InMemoryStorage, pubdata_builders::FullPubdataBuilder, vm_latest::constants::BATCH_COMPUTATIONAL_GAS_LIMIT, }; @@ -123,7 +123,7 @@ pub(super) fn default_l1_batch(number: L1BatchNumber) -> L1BatchEnv { } pub(super) fn default_pubdata_builder() -> Rc { - Rc::new(RollupPubdataBuilder::new(Address::zero())) + Rc::new(FullPubdataBuilder::new(Address::zero())) } pub(super) fn make_address_rich(storage: &mut InMemoryStorage, address: Address) { diff --git a/core/lib/types/src/commitment/mod.rs b/core/lib/types/src/commitment/mod.rs index 786ce03e671d..6d73ad048774 100644 --- a/core/lib/types/src/commitment/mod.rs +++ b/core/lib/types/src/commitment/mod.rs @@ -9,7 +9,7 @@ use std::{collections::HashMap, convert::TryFrom}; use serde::{Deserialize, Serialize}; -pub use zksync_basic_types::commitment::{L1BatchCommitmentMode, PubdataParams}; +pub use zksync_basic_types::commitment::{L1BatchCommitmentMode, PubdataParams, PubdataType}; use zksync_contracts::BaseSystemContractsHashes; use zksync_crypto_primitives::hasher::{keccak::KeccakHasher, Hasher}; use zksync_mini_merkle_tree::MiniMerkleTree; diff --git a/core/lib/vm_executor/src/storage.rs b/core/lib/vm_executor/src/storage.rs index e5a2d404233b..96b0d60ed122 100644 --- a/core/lib/vm_executor/src/storage.rs +++ b/core/lib/vm_executor/src/storage.rs @@ -3,13 +3,13 @@ use std::time::{Duration, Instant}; use anyhow::Context; -use zksync_contracts::BaseSystemContracts; +use zksync_contracts::{BaseSystemContracts, SystemContractCode}; use zksync_dal::{Connection, Core, CoreDal, DalError}; use zksync_multivm::interface::{L1BatchEnv, L2BlockEnv, SystemEnv, TxExecutionMode}; use zksync_types::{ - block::L2BlockHeader, commitment::PubdataParams, fee_model::BatchFeeInput, - snapshots::SnapshotRecoveryStatus, Address, L1BatchNumber, L2BlockNumber, L2ChainId, - ProtocolVersionId, H256, ZKPORTER_IS_AVAILABLE, + block::L2BlockHeader, bytecode::BytecodeHash, commitment::PubdataParams, + fee_model::BatchFeeInput, snapshots::SnapshotRecoveryStatus, Address, L1BatchNumber, + L2BlockNumber, L2ChainId, ProtocolVersionId, H256, ZKPORTER_IS_AVAILABLE, }; const BATCH_COMPUTATIONAL_GAS_LIMIT: u32 = u32::MAX; @@ -308,15 +308,15 @@ impl L1BatchParamsProvider { ); let contract_hashes = first_l2_block_in_batch.header.base_system_contracts_hashes; - let base_system_contracts = storage - .factory_deps_dal() - .get_base_system_contracts( - contract_hashes.bootloader, - contract_hashes.default_aa, - contract_hashes.evm_emulator, - ) - .await - .context("failed getting base system contracts")?; + let base_system_contracts = get_base_system_contracts( + storage, + first_l2_block_in_batch.header.protocol_version, + contract_hashes.bootloader, + contract_hashes.default_aa, + contract_hashes.evm_emulator, + ) + .await + .context("failed getting base system contracts")?; let (system_env, l1_batch_env) = l1_batch_params( first_l2_block_in_batch.l1_batch_number, @@ -373,3 +373,96 @@ impl L1BatchParamsProvider { .map(Some) } } + +async fn get_base_system_contracts( + storage: &mut Connection<'_, Core>, + protocol_version: Option, + bootloader_hash: H256, + default_aa_hash: H256, + evm_simulator_hash: Option, +) -> anyhow::Result { + // There are two potential sources of base contracts bytecode: + // - Factory deps table in case the upgrade transaction has been executed before. + // - Factory deps of the upgrade transaction. + + // Firstly trying from factory deps + if let Some(deps) = storage + .factory_deps_dal() + .get_base_system_contracts_from_factory_deps( + bootloader_hash, + default_aa_hash, + evm_simulator_hash, + ) + .await? + { + return Ok(deps); + } + + let protocol_version = protocol_version.context("Protocol version not provided")?; + + let upgrade_tx = storage + .protocol_versions_dal() + .get_protocol_upgrade_tx(protocol_version) + .await? + .with_context(|| { + format!("Could not find base contracts for version {protocol_version:?}: bootloader {bootloader_hash:?} or {default_aa_hash:?}") + })?; + + anyhow::ensure!( + upgrade_tx.execute.factory_deps.len() >= 2, + "Upgrade transaction does not have enough factory deps" + ); + + let bootloader_preimage = upgrade_tx.execute.factory_deps[0].clone(); + let default_aa_preimage = upgrade_tx.execute.factory_deps[1].clone(); + + anyhow::ensure!( + BytecodeHash::for_bytecode(&bootloader_preimage).value() == bootloader_hash, + "Bootloader hash mismatch" + ); + anyhow::ensure!( + BytecodeHash::for_bytecode(&default_aa_preimage).value() == default_aa_hash, + "Default account hash mismatch" + ); + + if evm_simulator_hash.is_some() { + // TODO(EVM-933): support EVM emulator. + panic!("EVM simulator not supported as part of gateway upgrade"); + } + + Ok(BaseSystemContracts { + bootloader: SystemContractCode { + code: bootloader_preimage, + hash: bootloader_hash, + }, + default_aa: SystemContractCode { + code: default_aa_preimage, + hash: default_aa_hash, + }, + evm_emulator: None, + }) +} + +pub async fn get_base_system_contracts_by_version_id( + storage: &mut Connection<'_, Core>, + version_id: ProtocolVersionId, +) -> anyhow::Result> { + let hashes = storage + .protocol_versions_dal() + .get_base_system_contract_hashes_by_version_id(version_id) + .await?; + let Some(hashes) = hashes else { + return Ok(None); + }; + + Ok(Some( + get_base_system_contracts( + storage, + Some(version_id), + hashes.bootloader, + hashes.default_aa, + hashes.evm_emulator, + ) + .await?, + )) +} diff --git a/core/node/consistency_checker/src/lib.rs b/core/node/consistency_checker/src/lib.rs index a73adc44b83e..f7d904955789 100644 --- a/core/node/consistency_checker/src/lib.rs +++ b/core/node/consistency_checker/src/lib.rs @@ -14,12 +14,9 @@ use zksync_eth_client::{ }; use zksync_health_check::{Health, HealthStatus, HealthUpdater, ReactiveHealthCheck}; use zksync_l1_contract_interface::{ - i_executor::{ - commit::kzg::ZK_SYNC_BYTES_PER_BLOB, - structures::{ - CommitBatchInfo, StoredBatchInfo, PUBDATA_SOURCE_BLOBS, PUBDATA_SOURCE_CALLDATA, - PUBDATA_SOURCE_CUSTOM_PRE_GATEWAY, SUPPORTED_ENCODING_VERSION, - }, + i_executor::structures::{ + CommitBatchInfo, StoredBatchInfo, PUBDATA_SOURCE_BLOBS, PUBDATA_SOURCE_CALLDATA, + PUBDATA_SOURCE_CUSTOM_PRE_GATEWAY, SUPPORTED_ENCODING_VERSION, }, Tokenizable, }; @@ -231,31 +228,19 @@ impl LocalL1BatchCommitData { } /// All returned errors are validation errors. - fn verify_commitment(&self, reference: ðabi::Token) -> anyhow::Result<()> { + fn verify_commitment(&self, reference: ðabi::Token, is_gateway: bool) -> anyhow::Result<()> { let protocol_version = self .l1_batch .header .protocol_version .unwrap_or_else(ProtocolVersionId::last_potentially_undefined); - let da = detect_da(protocol_version, reference, self.commitment_mode) - .context("cannot detect DA source from reference commitment token")?; - - // For rollups with `PubdataSendingMode::Calldata`, it's required that the pubdata fits into a single blob. - if matches!(self.commitment_mode, L1BatchCommitmentMode::Rollup) - && matches!(da, PubdataSendingMode::Calldata) - { - let pubdata_len = self - .l1_batch - .header - .pubdata_input - .as_ref() - .map_or_else(|| self.l1_batch.construct_pubdata().len(), Vec::len); - anyhow::ensure!( - pubdata_len <= ZK_SYNC_BYTES_PER_BLOB, - "pubdata size is too large when using calldata DA source: expected <={ZK_SYNC_BYTES_PER_BLOB} bytes, \ - got {pubdata_len} bytes" - ); - } + let da = detect_da( + protocol_version, + reference, + self.commitment_mode, + is_gateway, + ) + .context("cannot detect DA source from reference commitment token")?; let local_token = CommitBatchInfo::new(self.commitment_mode, &self.l1_batch, da).into_token(); @@ -278,6 +263,7 @@ pub fn detect_da( protocol_version: ProtocolVersionId, reference: &Token, commitment_mode: L1BatchCommitmentMode, + is_gateway: bool, ) -> Result { fn parse_error(message: impl Into>) -> ethabi::Error { ethabi::Error::Other(message.into()) @@ -351,7 +337,11 @@ pub fn detect_da( })? as usize; match last_reference_token.get(65 + 32 * number_of_blobs) { - Some(&byte) if byte == PUBDATA_SOURCE_CALLDATA => Ok(PubdataSendingMode::Calldata), + Some(&byte) if byte == PUBDATA_SOURCE_CALLDATA => if is_gateway { + Ok(PubdataSendingMode::RelayedL2Calldata) + } else { + Ok(PubdataSendingMode::Calldata) + }, Some(&byte) if byte == PUBDATA_SOURCE_BLOBS => Ok(PubdataSendingMode::Blobs), Some(&byte) => Err(parse_error(format!( "unexpected first byte of the last reference token for rollup; expected one of [{PUBDATA_SOURCE_CALLDATA}, {PUBDATA_SOURCE_BLOBS}], \ @@ -565,8 +555,10 @@ impl ConsistencyChecker { format!("failed extracting commit data for transaction {commit_tx_hash:?}") }) .map_err(CheckError::Validation)?; + + let is_gateway = chain_data.chain_id != self.l1_chain_data.chain_id; local - .verify_commitment(&commitment) + .verify_commitment(&commitment, is_gateway) .map_err(CheckError::Validation) } diff --git a/core/node/eth_watch/src/event_processors/appended_chain_batch_root.rs b/core/node/eth_watch/src/event_processors/appended_chain_batch_root.rs index 68f731120c65..158d800d1ec3 100644 --- a/core/node/eth_watch/src/event_processors/appended_chain_batch_root.rs +++ b/core/node/eth_watch/src/event_processors/appended_chain_batch_root.rs @@ -226,6 +226,9 @@ impl BatchRootProcessor { metadata[0] = LOG_PROOF_SUPPORTED_METADATA_VERSION; metadata[1] = chain_agg_proof.chain_id_leaf_proof.len() as u8; + // Chain proofs are always final nodes in the proofs. + metadata[3] = 1; + let mut chain_proof_vector = vec![ u256_to_h256(sl_encoded_data), H256::from_low_u64_be(sl_chain_id.0), diff --git a/core/node/eth_watch/src/tests/mod.rs b/core/node/eth_watch/src/tests/mod.rs index 0b34a34ab63f..4687dac4499b 100644 --- a/core/node/eth_watch/src/tests/mod.rs +++ b/core/node/eth_watch/src/tests/mod.rs @@ -481,7 +481,7 @@ async fn test_batch_root_processor_from_genesis() { .unwrap() .unwrap(); let proof1 = hex::encode(bincode::serialize(&proof1).unwrap()); - assert_eq!(proof1, "000000000600000000000000420000000000000030783030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303042000000000000003078303030303030303030303030303030303030303030303030303030303030303530303030303030303030303030303030303030303030303030303030303030334200000000000000307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030316639420000000000000030783031303230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303042000000000000003078303932343932386331333737613663663234633339633264343666386562396466323365383131623236646333353237653534383339366664346531373362314200000000000000307833373561356266393039636230323134336533363935636136353865303634316537333961613539306630303034646261393335373263343463646239643264"); + assert_eq!(proof1, "000000000600000000000000420000000000000030783030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303042000000000000003078303030303030303030303030303030303030303030303030303030303030303530303030303030303030303030303030303030303030303030303030303030334200000000000000307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030316639420000000000000030783031303230303031303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303042000000000000003078303932343932386331333737613663663234633339633264343666386562396466323365383131623236646333353237653534383339366664346531373362314200000000000000307833373561356266393039636230323134336533363935636136353865303634316537333961613539306630303034646261393335373263343463646239643264"); sl_client.set_last_finalized_block_number(11).await; watcher.loop_iteration(&mut connection).await.unwrap(); @@ -493,7 +493,7 @@ async fn test_batch_root_processor_from_genesis() { .unwrap() .unwrap(); let proof2 = hex::encode(bincode::serialize(&proof2).unwrap()); - assert_eq!(proof2, "0100000007000000000000004200000000000000307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303031420000000000000030783130613265663736653730396433313862343539626534396631653864376630326437313230663262353031626330616664646439333566316138313363363742000000000000003078303030303030303030303030303030303030303030303030303030303030303930303030303030303030303030303030303030303030303030303030303030334200000000000000307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030316639420000000000000030783031303230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303042000000000000003078303932343932386331333737613663663234633339633264343666386562396466323365383131623236646333353237653534383339366664346531373362314200000000000000307861333738613230636132376237616533303731643162643763326164613030343639616263353765343239646436663438613833303932646237303539613138"); + assert_eq!(proof2, "0100000007000000000000004200000000000000307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303031420000000000000030783130613265663736653730396433313862343539626534396631653864376630326437313230663262353031626330616664646439333566316138313363363742000000000000003078303030303030303030303030303030303030303030303030303030303030303930303030303030303030303030303030303030303030303030303030303030334200000000000000307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030316639420000000000000030783031303230303031303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303042000000000000003078303932343932386331333737613663663234633339633264343666386562396466323365383131623236646333353237653534383339366664346531373362314200000000000000307861333738613230636132376237616533303731643162643763326164613030343639616263353765343239646436663438613833303932646237303539613138"); let proof3 = connection .blocks_dal() @@ -502,7 +502,7 @@ async fn test_batch_root_processor_from_genesis() { .unwrap() .unwrap(); let proof3 = hex::encode(bincode::serialize(&proof3).unwrap()); - assert_eq!(proof3, "02000000080000000000000042000000000000003078303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030324200000000000000307834363730306234643430616335633335616632633232646461323738376139316562353637623036633932346138666238616539613035623230633038633231420000000000000030786530633333333066363734623662326435373866393538613164626436366631363464303638623062623561396662303737656361303133393736666461366642000000000000003078303030303030303030303030303030303030303030303030303030303030306230303030303030303030303030303030303030303030303030303030303030334200000000000000307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030316639420000000000000030783031303230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303042000000000000003078303932343932386331333737613663663234633339633264343666386562396466323365383131623236646333353237653534383339366664346531373362314200000000000000307833373561356266393039636230323134336533363935636136353865303634316537333961613539306630303034646261393335373263343463646239643264"); + assert_eq!(proof3, "02000000080000000000000042000000000000003078303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030324200000000000000307834363730306234643430616335633335616632633232646461323738376139316562353637623036633932346138666238616539613035623230633038633231420000000000000030786530633333333066363734623662326435373866393538613164626436366631363464303638623062623561396662303737656361303133393736666461366642000000000000003078303030303030303030303030303030303030303030303030303030303030306230303030303030303030303030303030303030303030303030303030303030334200000000000000307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030316639420000000000000030783031303230303031303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303042000000000000003078303932343932386331333737613663663234633339633264343666386562396466323365383131623236646333353237653534383339366664346531373362314200000000000000307833373561356266393039636230323134336533363935636136353865303634316537333961613539306630303034646261393335373263343463646239643264"); } #[test_log::test(tokio::test)] @@ -568,7 +568,7 @@ async fn test_batch_root_processor_restart() { .unwrap() .unwrap(); let proof = hex::encode(bincode::serialize(&proof).unwrap()); - assert_eq!(proof, "02000000080000000000000042000000000000003078303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030324200000000000000307834363730306234643430616335633335616632633232646461323738376139316562353637623036633932346138666238616539613035623230633038633231420000000000000030786530633333333066363734623662326435373866393538613164626436366631363464303638623062623561396662303737656361303133393736666461366642000000000000003078303030303030303030303030303030303030303030303030303030303030306230303030303030303030303030303030303030303030303030303030303030334200000000000000307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030316639420000000000000030783031303230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303042000000000000003078303932343932386331333737613663663234633339633264343666386562396466323365383131623236646333353237653534383339366664346531373362314200000000000000307833373561356266393039636230323134336533363935636136353865303634316537333961613539306630303034646261393335373263343463646239643264"); + assert_eq!(proof, "02000000080000000000000042000000000000003078303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030324200000000000000307834363730306234643430616335633335616632633232646461323738376139316562353637623036633932346138666238616539613035623230633038633231420000000000000030786530633333333066363734623662326435373866393538613164626436366631363464303638623062623561396662303737656361303133393736666461366642000000000000003078303030303030303030303030303030303030303030303030303030303030306230303030303030303030303030303030303030303030303030303030303030334200000000000000307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030316639420000000000000030783031303230303031303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303042000000000000003078303932343932386331333737613663663234633339633264343666386562396466323365383131623236646333353237653534383339366664346531373362314200000000000000307833373561356266393039636230323134336533363935636136353865303634316537333961613539306630303034646261393335373263343463646239643264"); let proof = connection .blocks_dal() @@ -577,7 +577,7 @@ async fn test_batch_root_processor_restart() { .unwrap() .unwrap(); let proof = hex::encode(bincode::serialize(&proof).unwrap()); - assert_eq!(proof, "02000000080000000000000042000000000000003078303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030334200000000000000307837623765373735373139343639366666393634616233353837393131373362636337663735356132656161393334653935373061636533393139383435313265420000000000000030786530633333333066363734623662326435373866393538613164626436366631363464303638623062623561396662303737656361303133393736666461366642000000000000003078303030303030303030303030303030303030303030303030303030303030306430303030303030303030303030303030303030303030303030303030303030334200000000000000307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030316639420000000000000030783031303230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303042000000000000003078303932343932386331333737613663663234633339633264343666386562396466323365383131623236646333353237653534383339366664346531373362314200000000000000307835353063313735316338653764626166633839303939326634353532333636663064643565623665343362653535353936386264616338633732656466316261"); + assert_eq!(proof, "02000000080000000000000042000000000000003078303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030334200000000000000307837623765373735373139343639366666393634616233353837393131373362636337663735356132656161393334653935373061636533393139383435313265420000000000000030786530633333333066363734623662326435373866393538613164626436366631363464303638623062623561396662303737656361303133393736666461366642000000000000003078303030303030303030303030303030303030303030303030303030303030306430303030303030303030303030303030303030303030303030303030303030334200000000000000307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030316639420000000000000030783031303230303031303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303042000000000000003078303932343932386331333737613663663234633339633264343666386562396466323365383131623236646333353237653534383339366664346531373362314200000000000000307835353063313735316338653764626166633839303939326634353532333636663064643565623665343362653535353936386264616338633732656466316261"); let proof = connection .blocks_dal() @@ -586,7 +586,7 @@ async fn test_batch_root_processor_restart() { .unwrap() .unwrap(); let proof = hex::encode(bincode::serialize(&proof).unwrap()); - assert_eq!(proof, "030000000900000000000000420000000000000030783030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303442000000000000003078303235663065363031353230366661626364326263613930316432633438396536336263356564346231356266356330633963363066396531363735383564614200000000000000307863633463343165646230633230333133343862323932623736386539626163316565386339326330396566386133323737633265636534303963313264383661420000000000000030783533656463316635616437396335393939626435373864666331333566396335316562643766616661343538356236346637316431356232646365316237323842000000000000003078303030303030303030303030303030303030303030303030303030303030306530303030303030303030303030303030303030303030303030303030303030334200000000000000307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030316639420000000000000030783031303230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303042000000000000003078303932343932386331333737613663663234633339633264343666386562396466323365383131623236646333353237653534383339366664346531373362314200000000000000307833373561356266393039636230323134336533363935636136353865303634316537333961613539306630303034646261393335373263343463646239643264"); + assert_eq!(proof, "030000000900000000000000420000000000000030783030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303442000000000000003078303235663065363031353230366661626364326263613930316432633438396536336263356564346231356266356330633963363066396531363735383564614200000000000000307863633463343165646230633230333133343862323932623736386539626163316565386339326330396566386133323737633265636534303963313264383661420000000000000030783533656463316635616437396335393939626435373864666331333566396335316562643766616661343538356236346637316431356232646365316237323842000000000000003078303030303030303030303030303030303030303030303030303030303030306530303030303030303030303030303030303030303030303030303030303030334200000000000000307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030316639420000000000000030783031303230303031303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303042000000000000003078303932343932386331333737613663663234633339633264343666386562396466323365383131623236646333353237653534383339366664346531373362314200000000000000307833373561356266393039636230323134336533363935636136353865303634316537333961613539306630303034646261393335373263343463646239643264"); let proof = connection .blocks_dal() @@ -595,7 +595,7 @@ async fn test_batch_root_processor_restart() { .unwrap() .unwrap(); let proof = hex::encode(bincode::serialize(&proof).unwrap()); - assert_eq!(proof, "030000000900000000000000420000000000000030783030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303542000000000000003078323465653435363834376535373364313635613832333634306632303834383139636331613865333433316562633635633865363064333435343266313637324200000000000000307863633463343165646230633230333133343862323932623736386539626163316565386339326330396566386133323737633265636534303963313264383661420000000000000030783533656463316635616437396335393939626435373864666331333566396335316562643766616661343538356236346637316431356232646365316237323842000000000000003078303030303030303030303030303030303030303030303030303030303030306530303030303030303030303030303030303030303030303030303030303030334200000000000000307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030316639420000000000000030783031303230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303042000000000000003078303932343932386331333737613663663234633339633264343666386562396466323365383131623236646333353237653534383339366664346531373362314200000000000000307833373561356266393039636230323134336533363935636136353865303634316537333961613539306630303034646261393335373263343463646239643264"); + assert_eq!(proof, "030000000900000000000000420000000000000030783030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303542000000000000003078323465653435363834376535373364313635613832333634306632303834383139636331613865333433316562633635633865363064333435343266313637324200000000000000307863633463343165646230633230333133343862323932623736386539626163316565386339326330396566386133323737633265636534303963313264383661420000000000000030783533656463316635616437396335393939626435373864666331333566396335316562643766616661343538356236346637316431356232646365316237323842000000000000003078303030303030303030303030303030303030303030303030303030303030306530303030303030303030303030303030303030303030303030303030303030334200000000000000307830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030316639420000000000000030783031303230303031303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303042000000000000003078303932343932386331333737613663663234633339633264343666386562396466323365383131623236646333353237653534383339366664346531373362314200000000000000307833373561356266393039636230323134336533363935636136353865303634316537333961613539306630303034646261393335373263343463646239643264"); } async fn get_all_db_txs(storage: &mut Connection<'_, Core>) -> Vec { diff --git a/core/node/metadata_calculator/src/api_server/tests.rs b/core/node/metadata_calculator/src/api_server/tests.rs index 9bb994cb4163..483236c3bc9b 100644 --- a/core/node/metadata_calculator/src/api_server/tests.rs +++ b/core/node/metadata_calculator/src/api_server/tests.rs @@ -131,11 +131,13 @@ fn assert_raw_nodes_response(response: &serde_json::Value) { assert_matches!(key, b'0'..=b'9' | b'a'..=b'f'); } - let node = response["0:0"].as_object().expect("not an object"); - assert!( - node.len() == 2 && node.contains_key("internal") && node.contains_key("raw"), - "{node:#?}" - ); + if let Some(value) = response.get("0:0") { + let node = value.as_object().expect("not an object"); + assert!( + node.len() == 2 && node.contains_key("internal") && node.contains_key("raw"), + "{node:#?}" + ); + } } fn assert_raw_stale_keys_response(response: &serde_json::Value) { diff --git a/core/node/node_framework/src/implementations/layers/state_keeper/mempool_io.rs b/core/node/node_framework/src/implementations/layers/state_keeper/mempool_io.rs index 77992f34c7f5..79eb233041a6 100644 --- a/core/node/node_framework/src/implementations/layers/state_keeper/mempool_io.rs +++ b/core/node/node_framework/src/implementations/layers/state_keeper/mempool_io.rs @@ -4,7 +4,7 @@ use zksync_config::configs::{ wallets, }; use zksync_state_keeper::{MempoolFetcher, MempoolGuard, MempoolIO, SequencerSealer}; -use zksync_types::{commitment::L1BatchCommitmentMode, Address, L2ChainId}; +use zksync_types::{commitment::PubdataType, Address, L2ChainId}; use crate::{ implementations::resources::{ @@ -40,7 +40,7 @@ pub struct MempoolIOLayer { mempool_config: MempoolConfig, wallets: wallets::StateKeeper, l2_da_validator_addr: Option
, - l1_batch_commit_data_generator_mode: L1BatchCommitmentMode, + pubdata_type: PubdataType, } #[derive(Debug, FromContext)] @@ -66,7 +66,7 @@ impl MempoolIOLayer { mempool_config: MempoolConfig, wallets: wallets::StateKeeper, l2_da_validator_addr: Option
, - l1_batch_commit_data_generator_mode: L1BatchCommitmentMode, + pubdata_type: PubdataType, ) -> Self { Self { zksync_network_id, @@ -74,7 +74,7 @@ impl MempoolIOLayer { mempool_config, wallets, l2_da_validator_addr, - l1_batch_commit_data_generator_mode, + pubdata_type, } } @@ -136,7 +136,7 @@ impl WiringLayer for MempoolIOLayer { self.mempool_config.delay_interval(), self.zksync_network_id, self.l2_da_validator_addr, - self.l1_batch_commit_data_generator_mode, + self.pubdata_type, )?; // Create sealer. diff --git a/core/node/node_sync/src/external_io.rs b/core/node/node_sync/src/external_io.rs index d3d908cfc169..eb79965fa28b 100644 --- a/core/node/node_sync/src/external_io.rs +++ b/core/node/node_sync/src/external_io.rs @@ -113,7 +113,7 @@ impl ExternalIO { .connection_tagged("sync_layer") .await? .protocol_versions_dal() - .get_base_system_contract_hashes_by_version_id(protocol_version as u16) + .get_base_system_contract_hashes_by_version_id(protocol_version) .await?; if base_system_contract_hashes.is_some() { return Ok(()); @@ -410,7 +410,7 @@ impl StateKeeperIO for ExternalIO { .connection_tagged("sync_layer") .await? .protocol_versions_dal() - .get_base_system_contract_hashes_by_version_id(protocol_version as u16) + .get_base_system_contract_hashes_by_version_id(protocol_version) .await? .with_context(|| { format!("Cannot load base system contracts' hashes for {protocol_version:?}. They should already be present") diff --git a/core/node/state_keeper/src/io/mempool.rs b/core/node/state_keeper/src/io/mempool.rs index cf354891236b..f553fcb57a08 100644 --- a/core/node/state_keeper/src/io/mempool.rs +++ b/core/node/state_keeper/src/io/mempool.rs @@ -15,12 +15,12 @@ use zksync_multivm::{interface::Halt, utils::derive_base_fee_and_gas_per_pubdata use zksync_node_fee_model::BatchFeeModelInputProvider; use zksync_types::{ block::UnsealedL1BatchHeader, - commitment::{L1BatchCommitmentMode, PubdataParams}, + commitment::{PubdataParams, PubdataType}, protocol_upgrade::ProtocolUpgradeTx, utils::display_timestamp, Address, L1BatchNumber, L2BlockNumber, L2ChainId, ProtocolVersionId, Transaction, H256, U256, }; -use zksync_vm_executor::storage::L1BatchParamsProvider; +use zksync_vm_executor::storage::{get_base_system_contracts_by_version_id, L1BatchParamsProvider}; use crate::{ io::{ @@ -58,7 +58,7 @@ pub struct MempoolIO { batch_fee_input_provider: Arc, chain_id: L2ChainId, l2_da_validator_address: Option
, - pubdata_type: L1BatchCommitmentMode, + pubdata_type: PubdataType, } impl IoSealCriteria for MempoolIO { @@ -382,18 +382,15 @@ impl StateKeeperIO for MempoolIO { protocol_version: ProtocolVersionId, _cursor: &IoCursor, ) -> anyhow::Result { - self.pool - .connection_tagged("state_keeper") - .await? - .protocol_versions_dal() - .load_base_system_contracts_by_version_id(protocol_version as u16) - .await - .context("failed loading base system contracts")? - .with_context(|| { - format!( - "no base system contracts persisted for protocol version {protocol_version:?}" - ) - }) + get_base_system_contracts_by_version_id( + &mut self.pool.connection_tagged("state_keeper").await?, + protocol_version, + ) + .await + .context("failed loading base system contracts")? + .with_context(|| { + format!("no base system contracts persisted for protocol version {protocol_version:?}") + }) } async fn load_batch_version_id( @@ -497,7 +494,7 @@ impl MempoolIO { delay_interval: Duration, chain_id: L2ChainId, l2_da_validator_address: Option
, - pubdata_type: L1BatchCommitmentMode, + pubdata_type: PubdataType, ) -> anyhow::Result { Ok(Self { mempool, From ab6231d0e0faa4e7c7d14861f14db6ff55f41834 Mon Sep 17 00:00:00 2001 From: Dima Zhornyk <55756184+dimazhornyk@users.noreply.github.com> Date: Mon, 6 Jan 2025 18:55:56 +0100 Subject: [PATCH 05/97] chore: create base_token_ratios_ratio_timestamp_idx index (#3411) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ This PR adds a migration that creates an index on `ratio_timestamp` field of `base_token_ratios` table. ## Why ❔ Queries to this table are currently much heavier than they should be, that has to be optimized. ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- .../migrations/20241226161705_add_ratio_timestamp_index.down.sql | 1 + .../migrations/20241226161705_add_ratio_timestamp_index.up.sql | 1 + 2 files changed, 2 insertions(+) create mode 100644 core/lib/dal/migrations/20241226161705_add_ratio_timestamp_index.down.sql create mode 100644 core/lib/dal/migrations/20241226161705_add_ratio_timestamp_index.up.sql diff --git a/core/lib/dal/migrations/20241226161705_add_ratio_timestamp_index.down.sql b/core/lib/dal/migrations/20241226161705_add_ratio_timestamp_index.down.sql new file mode 100644 index 000000000000..c7c00d281ac3 --- /dev/null +++ b/core/lib/dal/migrations/20241226161705_add_ratio_timestamp_index.down.sql @@ -0,0 +1 @@ +DROP INDEX IF EXISTS base_token_ratios_ratio_timestamp_idx; diff --git a/core/lib/dal/migrations/20241226161705_add_ratio_timestamp_index.up.sql b/core/lib/dal/migrations/20241226161705_add_ratio_timestamp_index.up.sql new file mode 100644 index 000000000000..7f1074f86f84 --- /dev/null +++ b/core/lib/dal/migrations/20241226161705_add_ratio_timestamp_index.up.sql @@ -0,0 +1 @@ +CREATE INDEX IF NOT EXISTS base_token_ratios_ratio_timestamp_idx ON base_token_ratios (ratio_timestamp); From 0731f607a72d18decd1ff74139f190c253d807ef Mon Sep 17 00:00:00 2001 From: perekopskiy <53865202+perekopskiy@users.noreply.github.com> Date: Tue, 7 Jan 2025 18:36:13 +0200 Subject: [PATCH 06/97] perf(eth-sender): optimize sql query (#3437) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ optimize sql query ## Why ❔ improve performance ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- ...78eeb89dcf134f97acb695e7f232cbd3839e81b.json} | 4 ++-- ...02b8a0949212bf66c602024aaa9843b4c66f965.json} | 4 ++-- core/lib/dal/src/eth_sender_dal.rs | 16 ++++++++++------ 3 files changed, 14 insertions(+), 10 deletions(-) rename core/lib/dal/.sqlx/{query-2234d7728d91cefaee792c900448aafe4b1aa2250fc535bfcdff39172551d42b.json => query-18e351067537a6fc5d837f01778eeb89dcf134f97acb695e7f232cbd3839e81b.json} (75%) rename core/lib/dal/.sqlx/{query-868bfdc5d8ee5eab395fa690891751dfd285628a75a35b152bccb3c73e9cc057.json => query-a8cc4da6200478a72a1b490d302b8a0949212bf66c602024aaa9843b4c66f965.json} (77%) diff --git a/core/lib/dal/.sqlx/query-2234d7728d91cefaee792c900448aafe4b1aa2250fc535bfcdff39172551d42b.json b/core/lib/dal/.sqlx/query-18e351067537a6fc5d837f01778eeb89dcf134f97acb695e7f232cbd3839e81b.json similarity index 75% rename from core/lib/dal/.sqlx/query-2234d7728d91cefaee792c900448aafe4b1aa2250fc535bfcdff39172551d42b.json rename to core/lib/dal/.sqlx/query-18e351067537a6fc5d837f01778eeb89dcf134f97acb695e7f232cbd3839e81b.json index df60f114f5ef..a25294c6bf1f 100644 --- a/core/lib/dal/.sqlx/query-2234d7728d91cefaee792c900448aafe4b1aa2250fc535bfcdff39172551d42b.json +++ b/core/lib/dal/.sqlx/query-18e351067537a6fc5d837f01778eeb89dcf134f97acb695e7f232cbd3839e81b.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n *\n FROM\n eth_txs\n WHERE\n from_addr IS NOT DISTINCT FROM $2 -- can't just use equality as NULL != NULL\n AND is_gateway = $3\n AND id > (\n SELECT\n COALESCE(MAX(eth_tx_id), 0)\n FROM\n eth_txs_history\n JOIN eth_txs ON eth_txs.id = eth_txs_history.eth_tx_id\n WHERE\n eth_txs_history.sent_at_block IS NOT NULL\n AND eth_txs.from_addr IS NOT DISTINCT FROM $2\n AND is_gateway = $3\n )\n ORDER BY\n id\n LIMIT\n $1\n ", + "query": "\n SELECT\n *\n FROM\n eth_txs\n WHERE\n from_addr IS NOT DISTINCT FROM $2 -- can't just use equality as NULL != NULL\n AND is_gateway = $3\n AND id > COALESCE(\n (SELECT\n eth_tx_id\n FROM\n eth_txs_history\n JOIN eth_txs ON eth_txs.id = eth_txs_history.eth_tx_id\n WHERE\n eth_txs_history.sent_at_block IS NOT NULL\n AND eth_txs.from_addr IS NOT DISTINCT FROM $2\n AND is_gateway = $3\n ORDER BY eth_tx_id DESC LIMIT 1),\n 0\n )\n ORDER BY\n id\n LIMIT\n $1\n ", "describe": { "columns": [ { @@ -110,5 +110,5 @@ true ] }, - "hash": "2234d7728d91cefaee792c900448aafe4b1aa2250fc535bfcdff39172551d42b" + "hash": "18e351067537a6fc5d837f01778eeb89dcf134f97acb695e7f232cbd3839e81b" } diff --git a/core/lib/dal/.sqlx/query-868bfdc5d8ee5eab395fa690891751dfd285628a75a35b152bccb3c73e9cc057.json b/core/lib/dal/.sqlx/query-a8cc4da6200478a72a1b490d302b8a0949212bf66c602024aaa9843b4c66f965.json similarity index 77% rename from core/lib/dal/.sqlx/query-868bfdc5d8ee5eab395fa690891751dfd285628a75a35b152bccb3c73e9cc057.json rename to core/lib/dal/.sqlx/query-a8cc4da6200478a72a1b490d302b8a0949212bf66c602024aaa9843b4c66f965.json index e47911f3d776..82b173a6a651 100644 --- a/core/lib/dal/.sqlx/query-868bfdc5d8ee5eab395fa690891751dfd285628a75a35b152bccb3c73e9cc057.json +++ b/core/lib/dal/.sqlx/query-a8cc4da6200478a72a1b490d302b8a0949212bf66c602024aaa9843b4c66f965.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n *\n FROM\n eth_txs\n WHERE\n from_addr IS NOT DISTINCT FROM $1 -- can't just use equality as NULL != NULL\n AND confirmed_eth_tx_history_id IS NULL\n AND is_gateway = $2\n AND id <= (\n SELECT\n COALESCE(MAX(eth_tx_id), 0)\n FROM\n eth_txs_history\n JOIN eth_txs ON eth_txs.id = eth_txs_history.eth_tx_id\n WHERE\n eth_txs_history.sent_at_block IS NOT NULL\n AND eth_txs.from_addr IS NOT DISTINCT FROM $1\n AND is_gateway = $2\n )\n ORDER BY\n id\n ", + "query": "\n SELECT\n *\n FROM\n eth_txs\n WHERE\n from_addr IS NOT DISTINCT FROM $1 -- can't just use equality as NULL != NULL\n AND confirmed_eth_tx_history_id IS NULL\n AND is_gateway = $2\n AND id <= COALESCE(\n (SELECT\n eth_tx_id\n FROM\n eth_txs_history\n JOIN eth_txs ON eth_txs.id = eth_txs_history.eth_tx_id\n WHERE\n eth_txs_history.sent_at_block IS NOT NULL\n AND eth_txs.from_addr IS NOT DISTINCT FROM $1\n AND is_gateway = $2\n ORDER BY eth_tx_id DESC LIMIT 1),\n 0\n )\n ORDER BY\n id\n ", "describe": { "columns": [ { @@ -109,5 +109,5 @@ true ] }, - "hash": "868bfdc5d8ee5eab395fa690891751dfd285628a75a35b152bccb3c73e9cc057" + "hash": "a8cc4da6200478a72a1b490d302b8a0949212bf66c602024aaa9843b4c66f965" } diff --git a/core/lib/dal/src/eth_sender_dal.rs b/core/lib/dal/src/eth_sender_dal.rs index 10f77718ba38..191ea3231d1c 100644 --- a/core/lib/dal/src/eth_sender_dal.rs +++ b/core/lib/dal/src/eth_sender_dal.rs @@ -41,9 +41,9 @@ impl EthSenderDal<'_, '_> { from_addr IS NOT DISTINCT FROM $1 -- can't just use equality as NULL != NULL AND confirmed_eth_tx_history_id IS NULL AND is_gateway = $2 - AND id <= ( - SELECT - COALESCE(MAX(eth_tx_id), 0) + AND id <= COALESCE( + (SELECT + eth_tx_id FROM eth_txs_history JOIN eth_txs ON eth_txs.id = eth_txs_history.eth_tx_id @@ -51,6 +51,8 @@ impl EthSenderDal<'_, '_> { eth_txs_history.sent_at_block IS NOT NULL AND eth_txs.from_addr IS NOT DISTINCT FROM $1 AND is_gateway = $2 + ORDER BY eth_tx_id DESC LIMIT 1), + 0 ) ORDER BY id @@ -172,9 +174,9 @@ impl EthSenderDal<'_, '_> { WHERE from_addr IS NOT DISTINCT FROM $2 -- can't just use equality as NULL != NULL AND is_gateway = $3 - AND id > ( - SELECT - COALESCE(MAX(eth_tx_id), 0) + AND id > COALESCE( + (SELECT + eth_tx_id FROM eth_txs_history JOIN eth_txs ON eth_txs.id = eth_txs_history.eth_tx_id @@ -182,6 +184,8 @@ impl EthSenderDal<'_, '_> { eth_txs_history.sent_at_block IS NOT NULL AND eth_txs.from_addr IS NOT DISTINCT FROM $2 AND is_gateway = $3 + ORDER BY eth_tx_id DESC LIMIT 1), + 0 ) ORDER BY id From 2c778fdd3fcd1e774bcb945f14a640ccf4227a2f Mon Sep 17 00:00:00 2001 From: perekopskiy <53865202+perekopskiy@users.noreply.github.com> Date: Tue, 7 Jan 2025 20:43:43 +0200 Subject: [PATCH 07/97] feat(eth-watch): Change protocol upgrade schema (#3435) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Copies protocol upgrade schema changes from sync-layer-stable in a non-breaking way. ## Why ❔ Support post-gateway upgrade schema. ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- Cargo.lock | 2 + core/lib/types/Cargo.toml | 1 + core/lib/types/src/abi.rs | 276 ++++++++++++++++-- core/lib/types/src/protocol_upgrade.rs | 228 ++++++++++----- core/node/eth_watch/Cargo.toml | 1 + core/node/eth_watch/src/client.rs | 124 +++++++- .../decentralized_upgrades.rs | 39 ++- core/node/eth_watch/src/lib.rs | 25 +- core/node/eth_watch/src/tests/client.rs | 54 +++- core/node/eth_watch/src/tests/mod.rs | 40 ++- .../src/implementations/layers/eth_watch.rs | 7 + prover/Cargo.lock | 1 + zkstack_cli/Cargo.lock | 1 + 13 files changed, 663 insertions(+), 136 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 965bee5a4e7f..abdd3fb6a5cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11981,6 +11981,7 @@ dependencies = [ "tracing", "vise", "zksync_concurrency", + "zksync_config", "zksync_contracts", "zksync_dal", "zksync_eth_client", @@ -13008,6 +13009,7 @@ version = "0.1.0" dependencies = [ "anyhow", "assert_matches", + "async-trait", "bigdecimal", "bincode", "blake2 0.10.6", diff --git a/core/lib/types/Cargo.toml b/core/lib/types/Cargo.toml index 325fe22209a7..6af0e39d14f0 100644 --- a/core/lib/types/Cargo.toml +++ b/core/lib/types/Cargo.toml @@ -19,6 +19,7 @@ zksync_mini_merkle_tree.workspace = true zksync_protobuf.workspace = true zksync_crypto_primitives.workspace = true +async-trait.workspace = true anyhow.workspace = true chrono = { workspace = true, features = ["serde"] } derive_more = { workspace = true, features = ["debug"] } diff --git a/core/lib/types/src/abi.rs b/core/lib/types/src/abi.rs index 92d4cb4c8612..b40aaaf882e2 100644 --- a/core/lib/types/src/abi.rs +++ b/core/lib/types/src/abi.rs @@ -1,4 +1,5 @@ use anyhow::Context as _; +use zksync_basic_types::protocol_version::ProtocolSemanticVersion; use crate::{ bytecode::BytecodeHash, @@ -185,7 +186,7 @@ impl NewPriorityRequest { } /// `VerifierParams` from `l1-contracts/contracts/state-transition/chain-interfaces/IVerifier.sol`. -#[derive(Default, PartialEq)] +#[derive(Debug, Default, PartialEq)] pub struct VerifierParams { pub recursion_node_level_vk_hash: [u8; 32], pub recursion_leaf_level_vk_hash: [u8; 32], @@ -193,9 +194,11 @@ pub struct VerifierParams { } /// `ProposedUpgrade` from, `l1-contracts/contracts/upgrades/BazeZkSyncUpgrade.sol`. +#[derive(Debug)] pub struct ProposedUpgrade { pub l2_protocol_upgrade_tx: Box, - pub factory_deps: Vec>, + // Factory deps are set only pre-gateway upgrades. + pub factory_deps: Option>>, pub bootloader_hash: [u8; 32], pub default_account_hash: [u8; 32], pub verifier: Address, @@ -250,8 +253,8 @@ impl VerifierParams { } impl ProposedUpgrade { - /// RLP schema of the `ProposedUpgrade`. - pub fn schema() -> ParamType { + /// Pre-gateway RLP schema of the `ProposedUpgrade`. + pub fn schema_pre_gateway() -> ParamType { ParamType::Tuple(vec![ L2CanonicalTransaction::schema(), // transaction data ParamType::Array(ParamType::Bytes.into()), // factory deps @@ -266,16 +269,39 @@ impl ProposedUpgrade { ]) } + /// Post-gateway RLP schema of the `ProposedUpgrade`. + pub fn schema_post_gateway() -> ParamType { + ParamType::Tuple(vec![ + L2CanonicalTransaction::schema(), // transaction data + ParamType::FixedBytes(32), // bootloader code hash + ParamType::FixedBytes(32), // default account code hash + ParamType::Address, // verifier address + VerifierParams::schema(), // verifier params + ParamType::Bytes, // l1 custom data + ParamType::Bytes, // l1 post-upgrade custom data + ParamType::Uint(256), // timestamp + ParamType::Uint(256), // version id + ]) + } + /// Encodes `ProposedUpgrade` to a RLP token. pub fn encode(&self) -> Token { - Token::Tuple(vec![ - self.l2_protocol_upgrade_tx.encode(), - Token::Array( + let mut tokens = vec![self.l2_protocol_upgrade_tx.encode()]; + + let protocol_version = ProtocolSemanticVersion::try_from_packed(self.new_protocol_version) + .expect("Version is not supported") + .minor; + if protocol_version.is_pre_gateway() { + tokens.push(Token::Array( self.factory_deps - .iter() - .map(|b| Token::Bytes(b.clone())) + .clone() + .expect("Factory deps should be present in pre-gateway upgrade data") + .into_iter() + .map(Token::Bytes) .collect(), - ), + )); + } + tokens.extend([ Token::FixedBytes(self.bootloader_hash.into()), Token::FixedBytes(self.default_account_hash.into()), Token::Address(self.verifier), @@ -284,32 +310,52 @@ impl ProposedUpgrade { Token::Bytes(self.post_upgrade_calldata.clone()), Token::Uint(self.upgrade_timestamp), Token::Uint(self.new_protocol_version), - ]) + ]); + + Token::Tuple(tokens) } /// Decodes `ProposedUpgrade` from a RLP token. /// Returns an error if token doesn't match the `schema()`. pub fn decode(token: Token) -> anyhow::Result { let tokens = token.into_tuple().context("not a tuple")?; - anyhow::ensure!(tokens.len() == 10); + let tokens_len = tokens.len(); + anyhow::ensure!(tokens_len >= 9); let mut t = tokens.into_iter(); let mut next = || t.next().unwrap(); - Ok(Self { - l2_protocol_upgrade_tx: L2CanonicalTransaction::decode(next()) - .context("l2_protocol_upgrade_tx")? - .into(), - factory_deps: next() - .into_array() - .context("factory_deps")? - .into_iter() - .enumerate() - .map(|(i, b)| b.into_bytes().context(i)) - .collect::>() - .context("factory_deps")?, - bootloader_hash: next() - .into_fixed_bytes() - .and_then(|b| b.try_into().ok()) - .context("bootloader_hash")?, + + let l2_protocol_upgrade_tx = L2CanonicalTransaction::decode(next()) + .context("l2_protocol_upgrade_tx")? + .into(); + let next_token = next(); + let (factory_deps, bootloader_hash) = match next_token { + Token::Array(tokens) => { + anyhow::ensure!(tokens_len == 10); + ( + Some( + tokens + .into_iter() + .enumerate() + .map(|(i, b)| b.into_bytes().context(i)) + .collect::>() + .context("factory_deps")?, + ), + next().into_fixed_bytes(), + ) + } + Token::FixedBytes(bytes) => { + anyhow::ensure!(tokens_len == 9); + (None, Some(bytes)) + } + _ => anyhow::bail!("Unexpected type of the second token"), + }; + let bootloader_hash = bootloader_hash + .and_then(|b| b.try_into().ok()) + .context("bootloader_hash")?; + let upgrade = Self { + l2_protocol_upgrade_tx, + factory_deps, + bootloader_hash, default_account_hash: next() .into_fixed_bytes() .and_then(|b| b.try_into().ok()) @@ -322,7 +368,16 @@ impl ProposedUpgrade { post_upgrade_calldata: next().into_bytes().context("post_upgrade_calldata")?, upgrade_timestamp: next().into_uint().context("upgrade_timestamp")?, new_protocol_version: next().into_uint().context("new_protocol_version")?, - }) + }; + + let protocol_version = + ProtocolSemanticVersion::try_from_packed(upgrade.new_protocol_version) + .map_err(|err| anyhow::anyhow!(err)) + .context("Version is not supported")? + .minor; + anyhow::ensure!(protocol_version.is_pre_gateway() == upgrade.factory_deps.is_some()); + + Ok(upgrade) } } @@ -365,3 +420,166 @@ impl Transaction { }) } } + +pub struct ForceDeployment { + pub bytecode_hash: H256, + pub new_address: Address, + pub call_constructor: bool, + pub value: U256, + pub input: Vec, +} + +impl ForceDeployment { + /// ABI schema of the `ForceDeployment`. + pub fn schema() -> ParamType { + ParamType::Tuple(vec![ + ParamType::FixedBytes(32), + ParamType::Address, + ParamType::Bool, + ParamType::Uint(256), + ParamType::Bytes, + ]) + } + + /// Encodes `ForceDeployment` to a RLP token. + pub fn encode(&self) -> Token { + Token::Tuple(vec![ + Token::FixedBytes(self.bytecode_hash.0.to_vec()), + Token::Address(self.new_address), + Token::Bool(self.call_constructor), + Token::Uint(self.value), + Token::Bytes(self.input.clone()), + ]) + } + + /// Decodes `ForceDeployment` from a RLP token. + /// Returns an error if token doesn't match the `schema()`. + pub fn decode(token: Token) -> anyhow::Result { + let tokens = token.into_tuple().context("not a tuple")?; + anyhow::ensure!(tokens.len() == 5); + let mut t = tokens.into_iter(); + let mut next = || t.next().unwrap(); + Ok(Self { + bytecode_hash: next() + .into_fixed_bytes() + .and_then(|b| Some(H256(b.try_into().ok()?))) + .context("bytecode_hash")?, + new_address: next().into_address().context("new_address")?, + call_constructor: next().into_bool().context("call_constructor")?, + value: next().into_uint().context("value")?, + input: next().into_bytes().context("input")?, + }) + } +} + +pub struct GatewayUpgradeEncodedInput { + pub force_deployments: Vec, + pub l2_gateway_upgrade_position: usize, + pub fixed_force_deployments_data: Vec, + pub ctm_deployer: Address, + pub old_validator_timelock: Address, + pub new_validator_timelock: Address, + pub wrapped_base_token_store: Address, +} + +impl GatewayUpgradeEncodedInput { + /// ABI schema of the `GatewayUpgradeEncodedInput`. + pub fn schema() -> ParamType { + ParamType::Tuple(vec![ + ParamType::Array(Box::new(ForceDeployment::schema())), + ParamType::Uint(256), + ParamType::Bytes, + ParamType::Address, + ParamType::Address, + ParamType::Address, + ParamType::Address, + ]) + } + + /// Decodes `GatewayUpgradeEncodedInput` from a RLP token. + /// Returns an error if token doesn't match the `schema()`. + pub fn decode(token: Token) -> anyhow::Result { + let tokens = token.into_tuple().context("not a tuple")?; + anyhow::ensure!(tokens.len() == 7); + let mut t = tokens.into_iter(); + let mut next = || t.next().unwrap(); + + let force_deployments_array = next().into_array().context("force_deployments_array")?; + let mut force_deployments = vec![]; + for token in force_deployments_array { + force_deployments.push(ForceDeployment::decode(token)?); + } + + Ok(Self { + force_deployments, + l2_gateway_upgrade_position: next() + .into_uint() + .context("l2_gateway_upgrade_position")? + .as_usize(), + fixed_force_deployments_data: next() + .into_bytes() + .context("fixed_force_deployments_data")?, + ctm_deployer: next().into_address().context("ctm_deployer")?, + old_validator_timelock: next().into_address().context("old_validator_timelock")?, + new_validator_timelock: next().into_address().context("new_validator_timelock")?, + wrapped_base_token_store: next().into_address().context("wrapped_base_token_store")?, + }) + } +} + +#[derive(Debug, Clone)] +pub struct ZkChainSpecificUpgradeData { + pub base_token_asset_id: H256, + pub l2_legacy_shared_bridge: Address, + pub predeployed_l2_weth_address: Address, + pub base_token_l1_address: Address, + pub base_token_name: String, + pub base_token_symbol: String, +} + +impl ZkChainSpecificUpgradeData { + pub fn from_partial_components( + base_token_asset_id: Option, + l2_legacy_shared_bridge: Option
, + predeployed_l2_weth_address: Option
, + base_token_l1_address: Option
, + base_token_name: Option, + base_token_symbol: Option, + ) -> Option { + Some(Self { + base_token_asset_id: base_token_asset_id?, + l2_legacy_shared_bridge: l2_legacy_shared_bridge?, + // Note, that some chains may not contain previous deployment of L2 wrapped base + // token. For those, zero address is used. + predeployed_l2_weth_address: predeployed_l2_weth_address.unwrap_or_default(), + base_token_l1_address: base_token_l1_address?, + base_token_name: base_token_name?, + base_token_symbol: base_token_symbol?, + }) + } + + /// ABI schema of the `ZkChainSpecificUpgradeData`. + pub fn schema() -> ParamType { + ParamType::Tuple(vec![ + ParamType::FixedBytes(32), + ParamType::Address, + ParamType::Address, + ]) + } + + /// Encodes `ZkChainSpecificUpgradeData` to a RLP token. + pub fn encode(&self) -> Token { + Token::Tuple(vec![ + Token::FixedBytes(self.base_token_asset_id.0.to_vec()), + Token::Address(self.l2_legacy_shared_bridge), + Token::Address(self.predeployed_l2_weth_address), + Token::Address(self.base_token_l1_address), + Token::String(self.base_token_name.clone()), + Token::String(self.base_token_symbol.clone()), + ]) + } + + pub fn encode_bytes(&self) -> Vec { + ethabi::encode(&[self.encode()]) + } +} diff --git a/core/lib/types/src/protocol_upgrade.rs b/core/lib/types/src/protocol_upgrade.rs index 4ea007239103..3bd9e696ce1f 100644 --- a/core/lib/types/src/protocol_upgrade.rs +++ b/core/lib/types/src/protocol_upgrade.rs @@ -2,20 +2,20 @@ use std::convert::{TryFrom, TryInto}; use anyhow::Context as _; use serde::{Deserialize, Serialize}; -use zksync_basic_types::{ - ethabi, - protocol_version::{ - L1VerifierConfig, ProtocolSemanticVersion, ProtocolVersionId, VerifierParams, - }, -}; -use zksync_contracts::{ - BaseSystemContractsHashes, ADMIN_EXECUTE_UPGRADE_FUNCTION, - ADMIN_UPGRADE_CHAIN_FROM_VERSION_FUNCTION, DIAMOND_CUT, +use zksync_basic_types::protocol_version::{ + L1VerifierConfig, ProtocolSemanticVersion, ProtocolVersionId, VerifierParams, }; +use zksync_contracts::{BaseSystemContractsHashes, DIAMOND_CUT}; use crate::{ - abi, ethabi::ParamType, h256_to_u256, web3::Log, Address, Execute, ExecuteTransactionCommon, - Transaction, TransactionType, H256, U256, + abi::{ + self, ForceDeployment, GatewayUpgradeEncodedInput, ProposedUpgrade, + ZkChainSpecificUpgradeData, + }, + ethabi::{self, decode, encode, ParamType, Token}, + h256_to_u256, u256_to_h256, + web3::Log, + Address, Execute, ExecuteTransactionCommon, Transaction, TransactionType, H256, U256, }; /// Represents a call to be made during governance operation. @@ -93,29 +93,155 @@ impl From for VerifierParams { } } +/// Protocol upgrade transactions do not contain preimages within them. +/// Instead, they are expected to be known and need to be fetched, typically from L1. +#[async_trait::async_trait] +pub trait ProtocolUpgradePreimageOracle: Send + Sync { + async fn get_protocol_upgrade_preimages( + &self, + hashes: Vec, + ) -> anyhow::Result>>; +} + +/// Some upgrades have chain-dependent calldata that has to be prepared properly. +async fn prepare_upgrade_call( + proposed_upgrade: &ProposedUpgrade, + chain_specific: Option, +) -> anyhow::Result> { + // No upgrade + if proposed_upgrade.l2_protocol_upgrade_tx.tx_type == U256::zero() { + return Ok(vec![]); + } + + let minor_version = proposed_upgrade.l2_protocol_upgrade_tx.nonce; + if ProtocolVersionId::try_from(minor_version.as_u32() as u16).unwrap() + != ProtocolVersionId::gateway_upgrade() + { + // We'll just keep it the same for non-Gateway upgrades + return Ok(proposed_upgrade.l2_protocol_upgrade_tx.data.clone()); + } + + // For gateway upgrade, things are bit more complex. + // The source of truth for the code below is the one that is present in + // `GatewayUpgrade.sol`. + let mut encoded_input = GatewayUpgradeEncodedInput::decode( + decode( + &[GatewayUpgradeEncodedInput::schema()], + &proposed_upgrade.post_upgrade_calldata, + )?[0] + .clone(), + )?; + + let gateway_upgrade_calldata = encode(&[ + Token::Address(encoded_input.ctm_deployer), + Token::Bytes(encoded_input.fixed_force_deployments_data), + Token::Bytes(chain_specific.context("chain_specific")?.encode_bytes()), + ]); + + // May not be very idiomatic, but we do it in the same way as it was done in Solidity + // for easier review + encoded_input.force_deployments[encoded_input.l2_gateway_upgrade_position].input = + gateway_upgrade_calldata; + + let force_deployments_as_tokens: Vec<_> = encoded_input + .force_deployments + .iter() + .map(ForceDeployment::encode) + .collect(); + + let full_data = zksync_contracts::deployer_contract() + .function("forceDeployOnAddresses") + .unwrap() + .encode_input(&[Token::Array(force_deployments_as_tokens)]) + .unwrap(); + + Ok(full_data) +} + impl ProtocolUpgrade { - pub fn try_from_diamond_cut(diamond_cut_data: &[u8]) -> anyhow::Result { + pub async fn try_from_diamond_cut( + diamond_cut_data: &[u8], + preimage_oracle: impl ProtocolUpgradePreimageOracle, + chain_specific: Option, + ) -> anyhow::Result { // Unwraps are safe because we have validated the input against the function signature. let diamond_cut_tokens = DIAMOND_CUT.decode_input(diamond_cut_data)?[0] .clone() .into_tuple() .unwrap(); - Self::try_from_init_calldata(&diamond_cut_tokens[2].clone().into_bytes().unwrap()) + Self::try_from_init_calldata( + &diamond_cut_tokens[2].clone().into_bytes().unwrap(), + preimage_oracle, + chain_specific, + ) + .await } /// `l1-contracts/contracts/state-transition/libraries/diamond.sol:DiamondCutData.initCalldata` - fn try_from_init_calldata(init_calldata: &[u8]) -> anyhow::Result { - let upgrade = ethabi::decode( - &[abi::ProposedUpgrade::schema()], + async fn try_from_init_calldata( + init_calldata: &[u8], + preimage_oracle: impl ProtocolUpgradePreimageOracle, + chain_specific: Option, + ) -> anyhow::Result { + let upgrade = if let Ok(upgrade) = ethabi::decode( + &[abi::ProposedUpgrade::schema_pre_gateway()], init_calldata.get(4..).context("need >= 4 bytes")?, - ) - .context("ethabi::decode()")?; - let upgrade = abi::ProposedUpgrade::decode(upgrade.into_iter().next().unwrap()).unwrap(); + ) { + upgrade + } else { + ethabi::decode( + &[abi::ProposedUpgrade::schema_post_gateway()], + init_calldata.get(4..).context("need >= 4 bytes")?, + ) + .context("ethabi::decode()")? + }; + + let mut upgrade = abi::ProposedUpgrade::decode(upgrade.into_iter().next().unwrap()) + .context("ProposedUpgrade::decode()")?; + let bootloader_hash = H256::from_slice(&upgrade.bootloader_hash); let default_account_hash = H256::from_slice(&upgrade.default_account_hash); + + let version = ProtocolSemanticVersion::try_from_packed(upgrade.new_protocol_version) + .map_err(|err| anyhow::format_err!("Version is not supported: {err}"))?; + let tx = if upgrade.l2_protocol_upgrade_tx.tx_type != U256::zero() { + let factory_deps = if version.minor.is_pre_gateway() { + upgrade.factory_deps.clone().unwrap() + } else { + preimage_oracle + .get_protocol_upgrade_preimages( + upgrade + .l2_protocol_upgrade_tx + .factory_deps + .iter() + .map(|&x| u256_to_h256(x)) + .collect(), + ) + .await? + }; + + upgrade.l2_protocol_upgrade_tx.data = + prepare_upgrade_call(&upgrade, chain_specific).await?; + + Some( + Transaction::from_abi( + abi::Transaction::L1 { + tx: upgrade.l2_protocol_upgrade_tx, + factory_deps, + eth_block: 0, + }, + false, + ) + .context("Transaction::try_from()")? + .try_into() + .map_err(|err| anyhow::format_err!("try_into::(): {err}"))?, + ) + } else { + None + }; + Ok(Self { - version: ProtocolSemanticVersion::try_from_packed(upgrade.new_protocol_version) - .map_err(|err| anyhow::format_err!("Version is not supported: {err}"))?, + version, bootloader_code_hash: (bootloader_hash != H256::zero()).then_some(bootloader_hash), default_account_code_hash: (default_account_hash != H256::zero()) .then_some(default_account_hash), @@ -124,21 +250,7 @@ impl ProtocolUpgrade { .then_some(upgrade.verifier_params.into()), verifier_address: (upgrade.verifier != Address::zero()).then_some(upgrade.verifier), timestamp: upgrade.upgrade_timestamp.try_into().unwrap(), - tx: (upgrade.l2_protocol_upgrade_tx.tx_type != U256::zero()) - .then(|| { - Transaction::from_abi( - abi::Transaction::L1 { - tx: upgrade.l2_protocol_upgrade_tx, - factory_deps: upgrade.factory_deps, - eth_block: 0, - }, - true, - ) - .context("Transaction::try_from()")? - .try_into() - .map_err(|err| anyhow::format_err!("try_into::(): {err}")) - }) - .transpose()?, + tx, }) } } @@ -168,50 +280,6 @@ pub fn decode_set_chain_id_event( )) } -impl TryFrom for ProtocolUpgrade { - type Error = anyhow::Error; - - fn try_from(call: Call) -> Result { - anyhow::ensure!(call.data.len() >= 4); - let (signature, data) = call.data.split_at(4); - - let diamond_cut_tokens = - if signature.to_vec() == ADMIN_EXECUTE_UPGRADE_FUNCTION.short_signature().to_vec() { - // Unwraps are safe, because we validate the input against the function signature. - ADMIN_EXECUTE_UPGRADE_FUNCTION - .decode_input(data)? - .pop() - .unwrap() - .into_tuple() - .unwrap() - } else if signature.to_vec() - == ADMIN_UPGRADE_CHAIN_FROM_VERSION_FUNCTION - .short_signature() - .to_vec() - { - let mut data = ADMIN_UPGRADE_CHAIN_FROM_VERSION_FUNCTION.decode_input(data)?; - - assert_eq!( - data.len(), - 2, - "The second method is expected to accept exactly 2 arguments" - ); - - // The second item must be a tuple of diamond cut data - // Unwraps are safe, because we validate the input against the function signature. - data.pop().unwrap().into_tuple().unwrap() - } else { - anyhow::bail!("unknown function"); - }; - - ProtocolUpgrade::try_from_init_calldata( - // Unwrap is safe because we have validated the input against the function signature. - &diamond_cut_tokens[2].clone().into_bytes().unwrap(), - ) - .context("ProtocolUpgrade::try_from_init_calldata()") - } -} - impl TryFrom for GovernanceOperation { type Error = crate::ethabi::Error; diff --git a/core/node/eth_watch/Cargo.toml b/core/node/eth_watch/Cargo.toml index 2a2374cef70e..38e8cf944efc 100644 --- a/core/node/eth_watch/Cargo.toml +++ b/core/node/eth_watch/Cargo.toml @@ -19,6 +19,7 @@ zksync_system_constants.workspace = true zksync_eth_client.workspace = true zksync_shared_metrics.workspace = true zksync_mini_merkle_tree.workspace = true +zksync_config.workspace = true zksync_web3_decl.workspace = true tokio = { workspace = true, features = ["time"] } diff --git a/core/node/eth_watch/src/client.rs b/core/node/eth_watch/src/client.rs index 0197748376ae..a86f760dc398 100644 --- a/core/node/eth_watch/src/client.rs +++ b/core/node/eth_watch/src/client.rs @@ -1,4 +1,4 @@ -use std::{fmt, sync::Arc}; +use std::{collections::HashMap, fmt, sync::Arc}; use anyhow::Context; use zksync_contracts::{ @@ -13,9 +13,11 @@ use zksync_eth_client::{ use zksync_system_constants::L2_MESSAGE_ROOT_ADDRESS; use zksync_types::{ api::{ChainAggProof, Log}, - ethabi::Contract, - web3::{BlockId, BlockNumber, Filter, FilterBuilder}, - Address, L1BatchNumber, L2ChainId, SLChainId, H256, U256, U64, + ethabi::{self, decode, Contract, ParamType}, + tokens::TokenMetadata, + web3::{BlockId, BlockNumber, CallRequest, Filter, FilterBuilder}, + Address, L1BatchNumber, L2ChainId, SLChainId, H256, SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, U256, + U64, }; use zksync_web3_decl::{ client::{Network, L2}, @@ -57,6 +59,13 @@ pub trait EthClient: 'static + fmt::Debug + Send + Sync { packed_version: H256, ) -> EnrichedClientResult>>; + async fn get_published_preimages( + &self, + hashes: Vec, + ) -> EnrichedClientResult>>>; + + async fn get_base_token_metadata(&self) -> Result; + /// Returns ID of the chain. async fn chain_id(&self) -> EnrichedClientResult; @@ -70,6 +79,8 @@ pub trait EthClient: 'static + fmt::Debug + Send + Sync { ) -> Result; } +// This constant is used for reading auxiliary events +const LOOK_BACK_BLOCK_RANGE: u64 = 1_000_000; pub const RETRY_LIMIT: usize = 5; const TOO_MANY_RESULTS_INFURA: &str = "query returned more than"; const TOO_MANY_RESULTS_ALCHEMY: &str = "response size exceeded"; @@ -84,6 +95,8 @@ pub struct EthHttpQueryClient { diamond_proxy_addr: Address, governance_address: Address, new_upgrade_cut_data_signature: H256, + bytecode_published_signature: H256, + bytecode_supplier_addr: Option
, // Only present for post-shared bridge chains. state_transition_manager_address: Option
, chain_admin_address: Option
, @@ -100,6 +113,7 @@ where pub fn new( client: Box>, diamond_proxy_addr: Address, + bytecode_supplier_addr: Option
, state_transition_manager_address: Option
, chain_admin_address: Option
, governance_address: Address, @@ -116,11 +130,16 @@ where state_transition_manager_address, chain_admin_address, governance_address, + bytecode_supplier_addr, new_upgrade_cut_data_signature: state_transition_manager_contract() .event("NewUpgradeCutData") .context("NewUpgradeCutData event is missing in ABI") .unwrap() .signature(), + bytecode_published_signature: ethabi::long_signature( + "BytecodePublished", + &[ParamType::FixedBytes(32), ParamType::Bytes], + ), verifier_contract_abi: verifier_contract(), getters_facet_contract_abi: getters_facet_contract(), message_root_abi: MESSAGE_ROOT_CONTRACT.clone(), @@ -264,6 +283,43 @@ where .await } + async fn get_published_preimages( + &self, + hashes: Vec, + ) -> EnrichedClientResult>>> { + let Some(bytecode_supplier_addr) = self.bytecode_supplier_addr else { + return Ok(vec![None; hashes.len()]); + }; + + let to_block = self.client.block_number().await?; + let from_block = to_block.saturating_sub((LOOK_BACK_BLOCK_RANGE - 1).into()); + + let logs = self + .get_events_inner( + from_block.into(), + to_block.into(), + Some(vec![self.bytecode_published_signature]), + Some(hashes.clone()), + Some(vec![bytecode_supplier_addr]), + RETRY_LIMIT, + ) + .await?; + + let mut preimages = HashMap::new(); + for log in logs { + let hash = log.topics[1]; + let preimage = decode(&[ParamType::Bytes], &log.data.0).expect("Invalid encoding"); + assert_eq!(preimage.len(), 1); + let preimage = preimage[0].clone().into_bytes().unwrap(); + preimages.insert(hash, preimage); + } + + Ok(hashes + .into_iter() + .map(|hash| preimages.get(&hash).cloned()) + .collect()) + } + async fn get_events( &self, from: BlockNumber, @@ -346,8 +402,6 @@ where &self, packed_version: H256, ) -> EnrichedClientResult>> { - const LOOK_BACK_BLOCK_RANGE: u64 = 1_000_000; - let Some(state_transition_manager_address) = self.state_transition_manager_address else { return Ok(None); }; @@ -384,6 +438,53 @@ where .call(&self.client) .await } + + async fn get_base_token_metadata(&self) -> Result { + let base_token_addr: Address = CallFunctionArgs::new("getBaseToken", ()) + .for_contract(self.diamond_proxy_addr, &self.getters_facet_contract_abi) + .call(&self.client) + .await?; + + if base_token_addr == SHARED_BRIDGE_ETHER_TOKEN_ADDRESS { + return Ok(TokenMetadata { + name: String::from("Ether"), + symbol: String::from("ETH"), + decimals: 18, + }); + } + + // TODO(EVM-934): support non-standard tokens. + let selectors: [[u8; 4]; 3] = [ + zksync_types::ethabi::short_signature("name", &[]), + zksync_types::ethabi::short_signature("symbol", &[]), + zksync_types::ethabi::short_signature("decimals", &[]), + ]; + let types: [ParamType; 3] = [ParamType::String, ParamType::String, ParamType::Uint(32)]; + + let mut decoded_result = vec![]; + for (selector, param_type) in selectors.into_iter().zip(types.into_iter()) { + let request = CallRequest { + to: Some(base_token_addr), + data: Some(selector.into()), + ..Default::default() + }; + let result = self.client.call_contract_function(request, None).await?; + // Base tokens are expected to support erc20 metadata + let mut token = zksync_types::ethabi::decode(&[param_type], &result.0) + .expect("base token does not support erc20 metadata"); + decoded_result.push(token.pop().unwrap()); + } + + Ok(TokenMetadata { + name: decoded_result[0].to_string(), + symbol: decoded_result[1].to_string(), + decimals: decoded_result[2] + .clone() + .into_uint() + .expect("decimals not supported") + .as_u32() as u8, + }) + } } /// Encapsulates `eth_getLogs` calls. @@ -530,4 +631,15 @@ impl EthClient for L2EthClientW { ) -> Result { self.0.get_chain_root(block_number, l2_chain_id).await } + + async fn get_base_token_metadata(&self) -> Result { + self.0.get_base_token_metadata().await + } + + async fn get_published_preimages( + &self, + hashes: Vec, + ) -> EnrichedClientResult>>> { + self.0.get_published_preimages(hashes).await + } } diff --git a/core/node/eth_watch/src/event_processors/decentralized_upgrades.rs b/core/node/eth_watch/src/event_processors/decentralized_upgrades.rs index 6e55b9ea0f89..c2f499b8ce64 100644 --- a/core/node/eth_watch/src/event_processors/decentralized_upgrades.rs +++ b/core/node/eth_watch/src/event_processors/decentralized_upgrades.rs @@ -3,8 +3,9 @@ use std::sync::Arc; use anyhow::Context as _; use zksync_dal::{eth_watcher_dal::EventType, Connection, Core, CoreDal, DalError}; use zksync_types::{ - api::Log, ethabi::Contract, protocol_version::ProtocolSemanticVersion, ProtocolUpgrade, H256, - U256, + abi::ZkChainSpecificUpgradeData, api::Log, ethabi::Contract, + protocol_upgrade::ProtocolUpgradePreimageOracle, protocol_version::ProtocolSemanticVersion, + ProtocolUpgrade, H256, U256, }; use crate::{ @@ -19,14 +20,18 @@ pub struct DecentralizedUpgradesEventProcessor { /// Last protocol version seen. Used to skip events for already known upgrade proposals. last_seen_protocol_version: ProtocolSemanticVersion, update_upgrade_timestamp_signature: H256, + chain_specific_data: Option, sl_client: Arc, + l1_client: Arc, } impl DecentralizedUpgradesEventProcessor { pub fn new( last_seen_protocol_version: ProtocolSemanticVersion, chain_admin_contract: &Contract, + chain_specific_data: Option, sl_client: Arc, + l1_client: Arc, ) -> Self { Self { last_seen_protocol_version, @@ -35,11 +40,33 @@ impl DecentralizedUpgradesEventProcessor { .context("UpdateUpgradeTimestamp event is missing in ABI") .unwrap() .signature(), + chain_specific_data, sl_client, + l1_client, } } } +#[async_trait::async_trait] +impl ProtocolUpgradePreimageOracle for &dyn EthClient { + async fn get_protocol_upgrade_preimages( + &self, + hashes: Vec, + ) -> anyhow::Result>> { + let preimages = self.get_published_preimages(hashes.clone()).await?; + + let mut result = vec![]; + for (i, preimage) in preimages.into_iter().enumerate() { + let preimage = preimage.with_context(|| { + format!("Protocol upgrade preimage for {:#?} is missing", hashes[i]) + })?; + result.push(preimage); + } + + Ok(result) + } +} + #[async_trait::async_trait] impl EventProcessor for DecentralizedUpgradesEventProcessor { async fn process_events( @@ -63,8 +90,14 @@ impl EventProcessor for DecentralizedUpgradesEventProcessor { let upgrade = ProtocolUpgrade { timestamp, - ..ProtocolUpgrade::try_from_diamond_cut(&diamond_cut)? + ..ProtocolUpgrade::try_from_diamond_cut( + &diamond_cut, + self.l1_client.as_ref(), + self.chain_specific_data.clone(), + ) + .await? }; + // Scheduler VK is not present in proposal event. It is hard coded in verifier contract. let scheduler_vk_hash = if let Some(address) = upgrade.verifier_address { Some(self.sl_client.scheduler_vk_hash(address).await?) diff --git a/core/node/eth_watch/src/lib.rs b/core/node/eth_watch/src/lib.rs index 908ff4da37f1..59f441457139 100644 --- a/core/node/eth_watch/src/lib.rs +++ b/core/node/eth_watch/src/lib.rs @@ -6,12 +6,14 @@ use std::{sync::Arc, time::Duration}; use anyhow::Context as _; use tokio::sync::watch; +use zksync_config::ContractsConfig; use zksync_dal::{Connection, ConnectionPool, Core, CoreDal, DalError}; use zksync_mini_merkle_tree::MiniMerkleTree; use zksync_system_constants::PRIORITY_EXPIRATION; use zksync_types::{ - ethabi::Contract, protocol_version::ProtocolSemanticVersion, - web3::BlockNumber as Web3BlockNumber, L1BatchNumber, L2ChainId, PriorityOpId, + abi::ZkChainSpecificUpgradeData, ethabi::Contract, protocol_version::ProtocolSemanticVersion, + tokens::TokenMetadata, web3::BlockNumber as Web3BlockNumber, L1BatchNumber, L2ChainId, + PriorityOpId, }; pub use self::client::{EthClient, EthHttpQueryClient, L2EthClient}; @@ -56,6 +58,7 @@ impl EthWatch { sl_l2_client: Option>, pool: ConnectionPool, poll_interval: Duration, + contracts_config: &ContractsConfig, chain_id: L2ChainId, ) -> anyhow::Result { let mut storage = pool.connection_tagged("eth_watch").await?; @@ -76,7 +79,9 @@ impl EthWatch { let decentralized_upgrades_processor = DecentralizedUpgradesEventProcessor::new( state.last_seen_protocol_version, chain_admin_contract, + get_chain_specific_upgrade_params(&l1_client, contracts_config).await?, sl_client.clone(), + l1_client.clone(), ); let mut event_processors: Vec> = vec![ Box::new(priority_ops_processor), @@ -241,3 +246,19 @@ impl EthWatch { Ok(()) } } + +async fn get_chain_specific_upgrade_params( + l1_client: &Arc, + contracts_config: &ContractsConfig, +) -> anyhow::Result> { + let TokenMetadata { name, symbol, .. } = l1_client.get_base_token_metadata().await?; + + Ok(ZkChainSpecificUpgradeData::from_partial_components( + contracts_config.l1_base_token_asset_id, + contracts_config.l2_legacy_shared_bridge_addr, + contracts_config.l2_predeployed_wrapped_base_token_address, + contracts_config.base_token_addr, + Some(name), + Some(symbol), + )) +} diff --git a/core/node/eth_watch/src/tests/client.rs b/core/node/eth_watch/src/tests/client.rs index e94a32096d96..f242488949b1 100644 --- a/core/node/eth_watch/src/tests/client.rs +++ b/core/node/eth_watch/src/tests/client.rs @@ -6,12 +6,13 @@ use zksync_contracts::{ }; use zksync_eth_client::{ContractCallError, EnrichedClientResult}; use zksync_types::{ - abi, - abi::ProposedUpgrade, + abi::{self, ProposedUpgrade}, api::{ChainAggProof, Log}, - ethabi, - ethabi::Token, + bytecode::BytecodeHash, + ethabi::{self, Token}, l1::L1Tx, + protocol_upgrade::ProtocolUpgradeTx, + tokens::TokenMetadata, u256_to_h256, web3::{contract::Tokenizable, BlockNumber}, Address, L1BatchNumber, L2ChainId, ProtocolUpgrade, SLChainId, Transaction, H256, U256, U64, @@ -30,6 +31,7 @@ pub struct FakeEthClientData { chain_log_proofs: HashMap, batch_roots: HashMap>, chain_roots: HashMap, + bytecode_preimages: HashMap>, } impl FakeEthClientData { @@ -44,6 +46,7 @@ impl FakeEthClientData { chain_log_proofs: Default::default(), batch_roots: Default::default(), chain_roots: Default::default(), + bytecode_preimages: Default::default(), } } @@ -68,6 +71,7 @@ impl FakeEthClientData { .entry(*eth_block) .or_default() .push(diamond_upgrade_log(upgrade.clone(), *eth_block)); + self.add_bytecode_preimages(&upgrade.tx); } } @@ -99,6 +103,22 @@ impl FakeEthClientData { self.chain_log_proofs.insert(batch, proof); } } + + fn get_bytecode_preimage(&self, hash: H256) -> Option> { + self.bytecode_preimages.get(&hash).cloned() + } + + fn add_bytecode_preimages(&mut self, upgrade_tx: &Option) { + let Some(tx) = upgrade_tx.as_ref() else { + // Nothing to add + return; + }; + + for dep in tx.execute.factory_deps.iter() { + self.bytecode_preimages + .insert(BytecodeHash::for_bytecode(dep).value(), dep.clone()); + } + } } #[derive(Debug, Clone)] @@ -273,6 +293,27 @@ impl EthClient for MockEthClient { unimplemented!() } + async fn get_published_preimages( + &self, + hashes: Vec, + ) -> EnrichedClientResult>>> { + let mut result = vec![]; + + for hash in hashes { + result.push(self.inner.read().await.get_bytecode_preimage(hash)); + } + + Ok(result) + } + + async fn get_base_token_metadata(&self) -> Result { + Ok(TokenMetadata { + name: "ETH".to_string(), + symbol: "Ether".to_string(), + decimals: 18, + }) + } + async fn fflonk_scheduler_vk_hash( &self, _verifier_address: Address, @@ -435,9 +476,7 @@ fn upgrade_timestamp_log(eth_block: u64) -> Log { } fn upgrade_into_diamond_cut(upgrade: ProtocolUpgrade) -> Token { - let abi::Transaction::L1 { - tx, factory_deps, .. - } = upgrade + let abi::Transaction::L1 { tx, .. } = upgrade .tx .map(|tx| Transaction::from(tx).try_into().unwrap()) .unwrap_or(abi::Transaction::L1 { @@ -448,6 +487,7 @@ fn upgrade_into_diamond_cut(upgrade: ProtocolUpgrade) -> Token { else { unreachable!() }; + let factory_deps = upgrade.version.minor.is_pre_gateway().then(Vec::new); ProposedUpgrade { l2_protocol_upgrade_tx: tx, factory_deps, diff --git a/core/node/eth_watch/src/tests/mod.rs b/core/node/eth_watch/src/tests/mod.rs index 4687dac4499b..e6c7945b7d93 100644 --- a/core/node/eth_watch/src/tests/mod.rs +++ b/core/node/eth_watch/src/tests/mod.rs @@ -1,5 +1,6 @@ use std::convert::TryInto; +use zksync_config::ContractsConfig; use zksync_contracts::chain_admin_contract; use zksync_dal::{Connection, ConnectionPool, Core, CoreDal}; use zksync_types::{ @@ -56,18 +57,22 @@ fn build_l1_tx(serial_id: u64, eth_block: u64) -> L1Tx { tx.try_into().unwrap() } -fn build_upgrade_tx(id: ProtocolVersionId, eth_block: u64) -> ProtocolUpgradeTx { +fn dummy_bytecode() -> Vec { + vec![0u8; 32] +} + +fn build_upgrade_tx(id: ProtocolVersionId) -> ProtocolUpgradeTx { let tx = ProtocolUpgradeTx { execute: Execute { contract_address: Some(Address::repeat_byte(0x11)), calldata: vec![1, 2, 3], - factory_deps: vec![], + factory_deps: vec![dummy_bytecode(), dummy_bytecode()], value: U256::zero(), }, common_data: ProtocolUpgradeTxCommonData { upgrade_id: id, sender: [1u8; 20].into(), - eth_block, + eth_block: 0, gas_limit: Default::default(), max_fee_per_gas: Default::default(), gas_per_pubdata_limit: 1u32.into(), @@ -104,6 +109,7 @@ async fn create_test_watcher( sl_l2_client, connection_pool, std::time::Duration::from_nanos(1), + &ContractsConfig::for_tests(), L2ChainId::default(), ) .await @@ -210,11 +216,14 @@ async fn test_normal_operation_upgrade_timestamp() { None, connection_pool.clone(), std::time::Duration::from_nanos(1), + &ContractsConfig::for_tests(), L2ChainId::default(), ) .await .unwrap(); + let expected_upgrade_tx = build_upgrade_tx(ProtocolVersionId::Version28); + let mut storage = connection_pool.connection().await.unwrap(); client .add_upgrade_timestamp(&[ @@ -228,10 +237,10 @@ async fn test_normal_operation_upgrade_timestamp() { ( ProtocolUpgrade { version: ProtocolSemanticVersion { - minor: ProtocolVersionId::next(), + minor: ProtocolVersionId::Version28, patch: 0.into(), }, - tx: Some(build_upgrade_tx(ProtocolVersionId::next(), 18)), + tx: Some(expected_upgrade_tx.clone()), ..Default::default() }, 18, @@ -239,7 +248,7 @@ async fn test_normal_operation_upgrade_timestamp() { ( ProtocolUpgrade { version: ProtocolSemanticVersion { - minor: ProtocolVersionId::next(), + minor: ProtocolVersionId::Version28, patch: 1.into(), }, tx: None, @@ -263,7 +272,7 @@ async fn test_normal_operation_upgrade_timestamp() { watcher.loop_iteration(&mut storage).await.unwrap(); let db_versions = storage.protocol_versions_dal().all_versions().await; let mut expected_version = ProtocolSemanticVersion { - minor: ProtocolVersionId::next(), + minor: ProtocolVersionId::Version28, patch: 0.into(), }; assert_eq!(db_versions.len(), 4); @@ -274,11 +283,24 @@ async fn test_normal_operation_upgrade_timestamp() { // Check that tx was saved with the second upgrade. let tx = storage .protocol_versions_dal() - .get_protocol_upgrade_tx(ProtocolVersionId::next()) + .get_protocol_upgrade_tx(ProtocolVersionId::Version28) .await .unwrap() .expect("no protocol upgrade transaction"); - assert_eq!(tx.common_data.upgrade_id, ProtocolVersionId::next()); + + let ProtocolUpgradeTx { + execute: expected_execute, + common_data: expected_common_data, + .. + } = expected_upgrade_tx; + + let ProtocolUpgradeTx { + execute, + common_data, + .. + } = tx; + assert_eq!(expected_execute, execute); + assert_eq!(expected_common_data, common_data); } #[test_log::test(tokio::test)] diff --git a/core/node/node_framework/src/implementations/layers/eth_watch.rs b/core/node/node_framework/src/implementations/layers/eth_watch.rs index eeffae4ae6d9..da0e26355cf2 100644 --- a/core/node/node_framework/src/implementations/layers/eth_watch.rs +++ b/core/node/node_framework/src/implementations/layers/eth_watch.rs @@ -94,6 +94,10 @@ impl WiringLayer for EthWatchLayer { let l1_client = EthHttpQueryClient::new( client, self.contracts_config.diamond_proxy_addr, + self.contracts_config + .ecosystem_contracts + .as_ref() + .and_then(|a| a.l1_bytecodes_supplier_addr), self.contracts_config .ecosystem_contracts .as_ref() @@ -109,6 +113,8 @@ impl WiringLayer for EthWatchLayer { Some(Box::new(EthHttpQueryClient::new( gateway_client.0, contracts_config.diamond_proxy_addr, + // Bytecode supplier is only present on L1 + None, Some(contracts_config.state_transition_proxy_addr), contracts_config.chain_admin_addr, contracts_config.governance_addr, @@ -124,6 +130,7 @@ impl WiringLayer for EthWatchLayer { sl_l2_client, main_pool, self.eth_watch_config.poll_interval(), + &self.contracts_config, self.chain_id, ) .await?; diff --git a/prover/Cargo.lock b/prover/Cargo.lock index 6fa199e7d5f4..761c9e398cb4 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -8895,6 +8895,7 @@ name = "zksync_types" version = "0.1.0" dependencies = [ "anyhow", + "async-trait", "bigdecimal", "blake2 0.10.6", "chrono", diff --git a/zkstack_cli/Cargo.lock b/zkstack_cli/Cargo.lock index b31fc9f54421..dd700ea548d0 100644 --- a/zkstack_cli/Cargo.lock +++ b/zkstack_cli/Cargo.lock @@ -7378,6 +7378,7 @@ name = "zksync_types" version = "0.1.0" dependencies = [ "anyhow", + "async-trait", "bigdecimal", "blake2", "chrono", From 1000f6145e74ed63609fa8c3531faaee1e3e47c9 Mon Sep 17 00:00:00 2001 From: D025 Date: Wed, 8 Jan 2025 11:36:56 +0200 Subject: [PATCH 08/97] chore: add fflonk setup key for proof-fri-gpu-compressor (#3426) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Add fflonk key for proof fri GPU compressor ## Why ❔ New version require it ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- .github/workflows/build-docker-from-tag.yml | 10 +++ .../build-proof-fri-gpu-compressor-gar.yml | 71 +++++++++++++++++++ .github/workflows/release-test-stage.yml | 10 +++ .../proof-fri-gpu-compressor-gar/Dockerfile | 21 ++++++ 4 files changed, 112 insertions(+) create mode 100644 .github/workflows/build-proof-fri-gpu-compressor-gar.yml create mode 100644 docker/proof-fri-gpu-compressor-gar/Dockerfile diff --git a/.github/workflows/build-docker-from-tag.yml b/.github/workflows/build-docker-from-tag.yml index 53522dd9b678..34f4b55903d2 100644 --- a/.github/workflows/build-docker-from-tag.yml +++ b/.github/workflows/build-docker-from-tag.yml @@ -119,3 +119,13 @@ jobs: setup_keys_id: ${{ needs.setup.outputs.prover_fri_gpu_key_id }} image_tag_suffix: ${{ needs.setup.outputs.image_tag_suffix }} protocol_version: ${{ needs.build-push-prover-images.outputs.protocol_version }} + + build-gar-proof-fri-gpu-compressor-gar: + name: Build GAR prover FRI GPU + needs: [setup, build-push-prover-images] + uses: ./.github/workflows/build-proof-fri-gpu-compressor-gar.yml + if: contains(github.ref_name, 'prover') + with: + setup_keys_id: ${{ needs.setup.outputs.prover_fri_gpu_key_id }} + image_tag_suffix: ${{ needs.setup.outputs.image_tag_suffix }} + protocol_version: ${{ needs.build-push-prover-images.outputs.protocol_version }} diff --git a/.github/workflows/build-proof-fri-gpu-compressor-gar.yml b/.github/workflows/build-proof-fri-gpu-compressor-gar.yml new file mode 100644 index 000000000000..3ad83706127e --- /dev/null +++ b/.github/workflows/build-proof-fri-gpu-compressor-gar.yml @@ -0,0 +1,71 @@ +name: Build Proof FRI GPU Compressor with builtin setup fflonk key + +on: + workflow_call: + inputs: + image_tag_suffix: + description: "Commit sha or git tag for Docker tag" + required: true + type: string + setup_keys_id: + description: "Commit sha for downloading setup data from bucket dir" + required: true + type: string + protocol_version: + description: "Protocol version to be included in the images tag" + required: true + type: string + +jobs: + build: + name: Build proof FRI GPU Compressor gar + runs-on: [matterlabs-ci-runner-high-performance] + steps: + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4 + with: + submodules: "recursive" + + - name: Download FFLONK key and setup data + run: | + gsutil -m rsync -r gs://matterlabs-setup-keys-us/setup-keys/setup_fflonk_compact.key docker/proof-fri-gpu-compressor-gar + gsutil -m rsync -r gs://matterlabs-setup-data-us/${{ inputs.setup_keys_id }} docker/proof-fri-gpu-compressor-gar + + - name: Login to us-central1 GAR + run: | + gcloud auth print-access-token --lifetime=7200 --impersonate-service-account=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com | docker login -u oauth2accesstoken --password-stdin https://us-docker.pkg.dev + + - name: Set up QEMU + uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 + + - name: Login to Asia GAR + run: | + gcloud auth print-access-token --lifetime=7200 --impersonate-service-account=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com | docker login -u oauth2accesstoken --password-stdin https://asia-docker.pkg.dev + + - name: Login to Europe GAR + run: | + gcloud auth print-access-token --lifetime=7200 --impersonate-service-account=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com | docker login -u oauth2accesstoken --password-stdin https://europe-docker.pkg.dev + + - name: Build and push proof-fri-gpu-compressor-gar + uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 + with: + context: docker/proof-fri-gpu-compressor-gar + build-args: | + PROOF_COMPRESSOR_IMAGE=${{ inputs.protocol_version }}-${{ inputs.image_tag_suffix }} + push: true + tags: | + us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/proof-fri-gpu-compressor-gar:2.0-${{ inputs.protocol_version }}-${{ inputs.image_tag_suffix }} + + - name: Build and push prover-gpu-fri-gar to Asia GAR + run: | + docker buildx imagetools create \ + --tag asia-docker.pkg.dev/matterlabs-infra/matterlabs-docker/proof-fri-gpu-compressor-gar:2.0-${{ inputs.protocol_version }}-${{ inputs.image_tag_suffix }} \ + us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/proof-fri-gpu-compressor-gar:2.0-${{ inputs.protocol_version }}-${{ inputs.image_tag_suffix }} + + - name: Build and push prover-gpu-fri-gar to Europe GAR + run: | + docker buildx imagetools create \ + --tag europe-docker.pkg.dev/matterlabs-infra/matterlabs-docker/proof-fri-gpu-compressor-gar:2.0-${{ inputs.protocol_version }}-${{ inputs.image_tag_suffix }} \ + us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/proof-fri-gpu-compressor-gar:2.0-${{ inputs.protocol_version }}-${{ inputs.image_tag_suffix }} diff --git a/.github/workflows/release-test-stage.yml b/.github/workflows/release-test-stage.yml index d8b39829c1b7..afd33979234d 100644 --- a/.github/workflows/release-test-stage.yml +++ b/.github/workflows/release-test-stage.yml @@ -130,3 +130,13 @@ jobs: setup_keys_id: ${{ needs.setup.outputs.prover_fri_gpu_key_id }} image_tag_suffix: ${{ needs.setup.outputs.image_tag_suffix }} protocol_version: ${{ needs.build-push-prover-images.outputs.protocol_version }} + + build-gar-proof-fri-gpu-compressor-gar: + name: Build GAR proof FRI GPU compressor + needs: [setup, build-push-prover-images] + uses: ./.github/workflows/build-proof-fri-gpu-compressor-gar.yml + if: needs.changed_files.outputs.prover == 'true' || needs.changed_files.outputs.all == 'true' + with: + setup_keys_id: ${{ needs.setup.outputs.prover_fri_gpu_key_id }} + image_tag_suffix: ${{ needs.setup.outputs.image_tag_suffix }} + protocol_version: ${{ needs.build-push-prover-images.outputs.protocol_version }} diff --git a/docker/proof-fri-gpu-compressor-gar/Dockerfile b/docker/proof-fri-gpu-compressor-gar/Dockerfile new file mode 100644 index 000000000000..42127ea6126c --- /dev/null +++ b/docker/proof-fri-gpu-compressor-gar/Dockerfile @@ -0,0 +1,21 @@ +ARG PROOF_COMPRESSOR_IMAGE +FROM us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/proof-fri-gpu-compressor:2.0-$PROOF_COMPRESSOR_IMAGE as proof_fri_gpu +FROM nvidia/cuda:12.4.0-runtime-ubuntu22.04 as app + +# HACK copying to root is the only way to make Docker layer caching work for these files for some reason +COPY *.bin / +COPY ./setup_fflonk_compact.key /setup_fflonk_compact.key + +RUN apt-get update && apt-get install -y curl libpq5 ca-certificates && rm -rf /var/lib/apt/lists/* + + +# copy finalization hints required for assembly generation +COPY --from=proof_fri_gpu /setup_2\^24.key /setup_2\^24.key +COPY --from=proof_fri_gpu /prover/data/keys/ /prover/data/keys/ +COPY --from=proof_fri_gpu /usr/bin/zksync_proof_fri_compressor /usr/bin/ + +ENV CRS_FILE=/setup_2\^24.key +ENV COMPACT_CRS_FILE=/setup_fflonk_compact.key + + +ENTRYPOINT ["zksync_proof_fri_compressor"] From 591cd86a1a1e6e4214d3cec74b4c601356060203 Mon Sep 17 00:00:00 2001 From: Dima Zhornyk <55756184+dimazhornyk@users.noreply.github.com> Date: Wed, 8 Jan 2025 10:25:27 +0000 Subject: [PATCH 09/97] feat: da_dispatcher refactoring (#3409) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ - Don't assume NoDA Validium by default, make the config required even for no DA, that helps avoid misconfiguration issues - Separate threads in da_dispatcer for `dispatch` and `poll_for_inclusion` - Change the default amount of rows that are fetched for dispatching ## Why ❔ To make configuration more resilient and main logic more efficient. ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- core/bin/zksync_server/src/node_builder.rs | 39 +++++++---- .../lib/config/src/configs/da_client/avail.rs | 21 ++++++ core/lib/config/src/configs/da_client/mod.rs | 2 + core/lib/config/src/configs/da_dispatcher.rs | 7 +- core/lib/config/src/testonly.rs | 1 + core/lib/env_config/src/da_client.rs | 1 + core/lib/protobuf_config/src/da_client.rs | 9 ++- .../src/proto/config/da_client.proto | 4 ++ core/node/da_clients/src/avail/client.rs | 8 ++- core/node/da_clients/src/avail/sdk.rs | 15 +++- core/node/da_dispatcher/src/da_dispatcher.rs | 70 ++++++++++++------- etc/env/file_based/overrides/validium.yaml | 2 + 12 files changed, 132 insertions(+), 47 deletions(-) diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index 96205eb9fbfc..9f49f822e8b5 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -121,21 +121,21 @@ impl MainNodeBuilder { self.node.runtime_handle() } - pub fn get_pubdata_type(&self) -> PubdataType { + pub fn get_pubdata_type(&self) -> anyhow::Result { if self.genesis_config.l1_batch_commit_data_generator_mode == L1BatchCommitmentMode::Rollup { - return PubdataType::Rollup; + return Ok(PubdataType::Rollup); } - let Some(da_client_config) = self.configs.da_client_config.clone() else { - return PubdataType::NoDA; - }; - - match da_client_config { - DAClientConfig::Avail(_) => PubdataType::Avail, - DAClientConfig::Celestia(_) => PubdataType::Celestia, - DAClientConfig::Eigen(_) => PubdataType::Eigen, - DAClientConfig::ObjectStore(_) => PubdataType::ObjectStore, + match self.configs.da_client_config.clone() { + None => Err(anyhow::anyhow!("No config for DA client")), + Some(da_client_config) => Ok(match da_client_config { + DAClientConfig::Avail(_) => PubdataType::Avail, + DAClientConfig::Celestia(_) => PubdataType::Celestia, + DAClientConfig::Eigen(_) => PubdataType::Eigen, + DAClientConfig::ObjectStore(_) => PubdataType::ObjectStore, + DAClientConfig::NoDA => PubdataType::NoDA, + }), } } @@ -273,7 +273,7 @@ impl MainNodeBuilder { try_load_config!(self.configs.mempool_config), try_load_config!(wallets.state_keeper), self.contracts_config.l2_da_validator_addr, - self.get_pubdata_type(), + self.get_pubdata_type()?, ); let db_config = try_load_config!(self.configs.db_config); let experimental_vm_config = self @@ -551,11 +551,22 @@ impl MainNodeBuilder { } fn add_da_client_layer(mut self) -> anyhow::Result { + let eth_sender_config = try_load_config!(self.configs.eth); + if let Some(sender_config) = eth_sender_config.sender { + if sender_config.pubdata_sending_mode != PubdataSendingMode::Custom { + tracing::warn!("DA dispatcher is enabled, but the pubdata sending mode is not `Custom`. DA client will not be started."); + return Ok(self); + } + } + let Some(da_client_config) = self.configs.da_client_config.clone() else { - tracing::warn!("No config for DA client, using the NoDA client"); + bail!("No config for DA client"); + }; + + if let DAClientConfig::NoDA = da_client_config { self.node.add_layer(NoDAClientWiringLayer); return Ok(self); - }; + } let secrets = try_load_config!(self.secrets.data_availability); match (da_client_config, secrets) { diff --git a/core/lib/config/src/configs/da_client/avail.rs b/core/lib/config/src/configs/da_client/avail.rs index 48aaf5b0e61e..7b7740999080 100644 --- a/core/lib/config/src/configs/da_client/avail.rs +++ b/core/lib/config/src/configs/da_client/avail.rs @@ -4,6 +4,9 @@ use zksync_basic_types::secrets::{APIKey, SeedPhrase}; pub const AVAIL_GAS_RELAY_CLIENT_NAME: &str = "GasRelay"; pub const AVAIL_FULL_CLIENT_NAME: &str = "FullClient"; +pub const IN_BLOCK_FINALITY_STATE: &str = "inBlock"; +pub const FINALIZED_FINALITY_STATE: &str = "finalized"; + #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(tag = "avail_client")] pub enum AvailClientConfig { @@ -23,6 +26,7 @@ pub struct AvailConfig { pub struct AvailDefaultConfig { pub api_node_url: String, pub app_id: u32, + pub finality_state: Option, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -36,3 +40,20 @@ pub struct AvailSecrets { pub seed_phrase: Option, pub gas_relay_api_key: Option, } + +impl AvailDefaultConfig { + pub fn finality_state(&self) -> anyhow::Result { + match self.finality_state.clone() { + Some(finality_state) => match finality_state.as_str() { + IN_BLOCK_FINALITY_STATE | FINALIZED_FINALITY_STATE => Ok(finality_state), + _ => Err(anyhow::anyhow!( + "Invalid finality state: {}. Supported values are: {}, {}", + finality_state, + IN_BLOCK_FINALITY_STATE, + FINALIZED_FINALITY_STATE + )), + }, + None => Ok(IN_BLOCK_FINALITY_STATE.to_string()), + } + } +} diff --git a/core/lib/config/src/configs/da_client/mod.rs b/core/lib/config/src/configs/da_client/mod.rs index f82fd134edb5..717239814b9b 100644 --- a/core/lib/config/src/configs/da_client/mod.rs +++ b/core/lib/config/src/configs/da_client/mod.rs @@ -8,6 +8,7 @@ pub const AVAIL_CLIENT_CONFIG_NAME: &str = "Avail"; pub const CELESTIA_CLIENT_CONFIG_NAME: &str = "Celestia"; pub const EIGEN_CLIENT_CONFIG_NAME: &str = "Eigen"; pub const OBJECT_STORE_CLIENT_CONFIG_NAME: &str = "ObjectStore"; +pub const NO_DA_CLIENT_CONFIG_NAME: &str = "NoDA"; #[derive(Debug, Clone, PartialEq)] pub enum DAClientConfig { @@ -15,6 +16,7 @@ pub enum DAClientConfig { Celestia(CelestiaConfig), Eigen(EigenConfig), ObjectStore(ObjectStoreConfig), + NoDA, } impl From for DAClientConfig { diff --git a/core/lib/config/src/configs/da_dispatcher.rs b/core/lib/config/src/configs/da_dispatcher.rs index e9ad6bd3c074..dfbd4d517b9d 100644 --- a/core/lib/config/src/configs/da_dispatcher.rs +++ b/core/lib/config/src/configs/da_dispatcher.rs @@ -2,9 +2,14 @@ use std::time::Duration; use serde::Deserialize; +/// The default interval between the `da_dispatcher's` iterations. pub const DEFAULT_POLLING_INTERVAL_MS: u32 = 5000; -pub const DEFAULT_MAX_ROWS_TO_DISPATCH: u32 = 100; +/// The maximum number of rows to fetch from the database in a single query. The value has to be +/// not too high to avoid the dispatcher iteration taking too much time. +pub const DEFAULT_MAX_ROWS_TO_DISPATCH: u32 = 3; +/// The maximum number of retries for the dispatch of a blob. pub const DEFAULT_MAX_RETRIES: u16 = 5; +/// Use dummy value as inclusion proof instead of getting it from the client. pub const DEFAULT_USE_DUMMY_INCLUSION_DATA: bool = false; #[derive(Debug, Clone, PartialEq, Deserialize)] diff --git a/core/lib/config/src/testonly.rs b/core/lib/config/src/testonly.rs index f6ad6bd4dae8..3472cf4e7d0a 100644 --- a/core/lib/config/src/testonly.rs +++ b/core/lib/config/src/testonly.rs @@ -959,6 +959,7 @@ impl Distribution for EncodeDist { config: AvailClientConfig::FullClient(AvailDefaultConfig { api_node_url: self.sample(rng), app_id: self.sample(rng), + finality_state: None, }), }) } diff --git a/core/lib/env_config/src/da_client.rs b/core/lib/env_config/src/da_client.rs index 8ceeb215faf4..4bfce921dddc 100644 --- a/core/lib/env_config/src/da_client.rs +++ b/core/lib/env_config/src/da_client.rs @@ -146,6 +146,7 @@ mod tests { config: AvailClientConfig::FullClient(AvailDefaultConfig { api_node_url: api_node_url.to_string(), app_id, + finality_state: None, }), }) } diff --git a/core/lib/protobuf_config/src/da_client.rs b/core/lib/protobuf_config/src/da_client.rs index 341a6a9e4f43..77d2965149fe 100644 --- a/core/lib/protobuf_config/src/da_client.rs +++ b/core/lib/protobuf_config/src/da_client.rs @@ -5,7 +5,7 @@ use zksync_config::configs::{ avail::{AvailClientConfig, AvailConfig, AvailDefaultConfig, AvailGasRelayConfig}, celestia::CelestiaConfig, eigen::EigenConfig, - DAClientConfig::{Avail, Celestia, Eigen, ObjectStore}, + DAClientConfig::{Avail, Celestia, Eigen, NoDA, ObjectStore}, }, }; use zksync_protobuf::{required, ProtoRepr}; @@ -16,8 +16,7 @@ impl ProtoRepr for proto::DataAvailabilityClient { type Type = configs::DAClientConfig; fn read(&self) -> anyhow::Result { - let config = required(&self.config).context("config")?; - + let config = required(&self.config).context("da_client config")?; let client = match config { proto::data_availability_client::Config::Avail(conf) => Avail(AvailConfig { bridge_api_url: required(&conf.bridge_api_url) @@ -31,6 +30,7 @@ impl ProtoRepr for proto::DataAvailabilityClient { .context("api_node_url")? .clone(), app_id: *required(&full_client_conf.app_id).context("app_id")?, + finality_state: full_client_conf.finality_state.clone(), }) } Some(proto::avail_config::Config::GasRelay(gas_relay_conf)) => { @@ -62,6 +62,7 @@ impl ProtoRepr for proto::DataAvailabilityClient { proto::data_availability_client::Config::ObjectStore(conf) => { ObjectStore(object_store_proto::ObjectStore::read(conf)?) } + proto::data_availability_client::Config::NoDa(_) => NoDA, }; Ok(client) @@ -77,6 +78,7 @@ impl ProtoRepr for proto::DataAvailabilityClient { proto::avail_config::Config::FullClient(proto::AvailClientConfig { api_node_url: Some(conf.api_node_url.clone()), app_id: Some(conf.app_id), + finality_state: conf.finality_state.clone(), }), ), AvailClientConfig::GasRelay(conf) => Some( @@ -102,6 +104,7 @@ impl ProtoRepr for proto::DataAvailabilityClient { ObjectStore(config) => proto::data_availability_client::Config::ObjectStore( object_store_proto::ObjectStore::build(config), ), + NoDA => proto::data_availability_client::Config::NoDa(proto::NoDaConfig {}), }; Self { diff --git a/core/lib/protobuf_config/src/proto/config/da_client.proto b/core/lib/protobuf_config/src/proto/config/da_client.proto index 0a302120d775..9ad7527979e6 100644 --- a/core/lib/protobuf_config/src/proto/config/da_client.proto +++ b/core/lib/protobuf_config/src/proto/config/da_client.proto @@ -22,6 +22,7 @@ message AvailConfig { message AvailClientConfig { optional string api_node_url = 1; optional uint32 app_id = 2; + optional string finality_state = 3; } message AvailGasRelayConfig { @@ -41,6 +42,8 @@ message EigenConfig { optional uint64 inclusion_polling_interval_ms = 2; } +message NoDAConfig {} + message DataAvailabilityClient { // oneof in protobuf allows for None oneof config { @@ -48,5 +51,6 @@ message DataAvailabilityClient { object_store.ObjectStore object_store = 2; CelestiaConfig celestia = 3; EigenConfig eigen = 4; + NoDAConfig no_da = 5; } } diff --git a/core/node/da_clients/src/avail/client.rs b/core/node/da_clients/src/avail/client.rs index c0ead429d91a..115ad77bf44e 100644 --- a/core/node/da_clients/src/avail/client.rs +++ b/core/node/da_clients/src/avail/client.rs @@ -121,8 +121,12 @@ impl AvailClient { .seed_phrase .ok_or_else(|| anyhow::anyhow!("Seed phrase is missing"))?; // these unwraps are safe because we validate in protobuf config - let sdk_client = - RawAvailClient::new(conf.app_id, seed_phrase.0.expose_secret()).await?; + let sdk_client = RawAvailClient::new( + conf.app_id, + seed_phrase.0.expose_secret(), + conf.finality_state()?, + ) + .await?; Ok(Self { config, diff --git a/core/node/da_clients/src/avail/sdk.rs b/core/node/da_clients/src/avail/sdk.rs index 19309dc3cbf3..8f28e797dc9a 100644 --- a/core/node/da_clients/src/avail/sdk.rs +++ b/core/node/da_clients/src/avail/sdk.rs @@ -27,6 +27,7 @@ const PROTOCOL_VERSION: u8 = 4; pub(crate) struct RawAvailClient { app_id: u32, keypair: Keypair, + finality_state: String, } /// Utility type needed for encoding the call data @@ -44,11 +45,19 @@ struct BoundedVec<_0>(pub Vec<_0>); impl RawAvailClient { pub(crate) const MAX_BLOB_SIZE: usize = 512 * 1024; // 512kb - pub(crate) async fn new(app_id: u32, seed: &str) -> anyhow::Result { + pub(crate) async fn new( + app_id: u32, + seed: &str, + finality_state: String, + ) -> anyhow::Result { let mnemonic = Mnemonic::parse(seed)?; let keypair = Keypair::from_phrase(&mnemonic, None)?; - Ok(Self { app_id, keypair }) + Ok(Self { + app_id, + keypair, + finality_state, + }) } /// Returns a hex-encoded extrinsic @@ -291,7 +300,7 @@ impl RawAvailClient { let status = sub.next().await.transpose()?; if status.is_some() && status.as_ref().unwrap().is_object() { - if let Some(block_hash) = status.unwrap().get("finalized") { + if let Some(block_hash) = status.unwrap().get(self.finality_state.as_str()) { break block_hash .as_str() .ok_or_else(|| anyhow::anyhow!("Invalid block hash"))? diff --git a/core/node/da_dispatcher/src/da_dispatcher.rs b/core/node/da_dispatcher/src/da_dispatcher.rs index 2cdde9951be9..f59a30b362ee 100644 --- a/core/node/da_dispatcher/src/da_dispatcher.rs +++ b/core/node/da_dispatcher/src/da_dispatcher.rs @@ -1,4 +1,4 @@ -use std::{future::Future, time::Duration}; +use std::{future::Future, sync::Arc, time::Duration}; use anyhow::Context; use chrono::Utc; @@ -14,7 +14,7 @@ use zksync_types::L1BatchNumber; use crate::metrics::METRICS; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct DataAvailabilityDispatcher { client: Box, pool: ConnectionPool, @@ -35,37 +35,59 @@ impl DataAvailabilityDispatcher { } pub async fn run(self, mut stop_receiver: Receiver) -> anyhow::Result<()> { - loop { - if *stop_receiver.borrow() { - break; - } + let self_arc = Arc::new(self.clone()); - let subtasks = futures::future::join( - async { - if let Err(err) = self.dispatch().await { - tracing::error!("dispatch error {err:?}"); - } - }, - async { - if let Err(err) = self.poll_for_inclusion().await { - tracing::error!("poll_for_inclusion error {err:?}"); - } - }, - ); + let mut stop_receiver_dispatch = stop_receiver.clone(); + let mut stop_receiver_poll_for_inclusion = stop_receiver.clone(); + + let dispatch_task = tokio::spawn(async move { + loop { + if *stop_receiver_dispatch.borrow() { + break; + } - tokio::select! { - _ = subtasks => {}, - _ = stop_receiver.changed() => { + if let Err(err) = self_arc.dispatch().await { + tracing::error!("dispatch error {err:?}"); + } + + if tokio::time::timeout( + self_arc.config.polling_interval(), + stop_receiver_dispatch.changed(), + ) + .await + .is_ok() + { break; } } + }); - if tokio::time::timeout(self.config.polling_interval(), stop_receiver.changed()) + let inclusion_task = tokio::spawn(async move { + loop { + if *stop_receiver_poll_for_inclusion.borrow() { + break; + } + + if let Err(err) = self.poll_for_inclusion().await { + tracing::error!("poll_for_inclusion error {err:?}"); + } + + if tokio::time::timeout( + self.config.polling_interval(), + stop_receiver_poll_for_inclusion.changed(), + ) .await .is_ok() - { - break; + { + break; + } } + }); + + tokio::select! { + _ = dispatch_task => {}, + _ = inclusion_task => {}, + _ = stop_receiver.changed() => {}, } tracing::info!("Stop signal received, da_dispatcher is shutting down"); diff --git a/etc/env/file_based/overrides/validium.yaml b/etc/env/file_based/overrides/validium.yaml index 1af02dd95893..516973f66689 100644 --- a/etc/env/file_based/overrides/validium.yaml +++ b/etc/env/file_based/overrides/validium.yaml @@ -4,3 +4,5 @@ eth: state_keeper: pubdata_overhead_part: 0 compute_overhead_part: 1 +da_client: + no_da: {} From 06ba7023b577d40937b8db4b70c646f3693d95b2 Mon Sep 17 00:00:00 2001 From: D025 Date: Wed, 8 Jan 2025 12:27:12 +0200 Subject: [PATCH 10/97] ci: fix gsutil command for download fflonk key for proof-fri-gpu-compressor (#3441) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Fix gsutil command for download fflonk key ## Why ❔ It's broken ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- .github/workflows/build-proof-fri-gpu-compressor-gar.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-proof-fri-gpu-compressor-gar.yml b/.github/workflows/build-proof-fri-gpu-compressor-gar.yml index 3ad83706127e..e11a49640369 100644 --- a/.github/workflows/build-proof-fri-gpu-compressor-gar.yml +++ b/.github/workflows/build-proof-fri-gpu-compressor-gar.yml @@ -27,8 +27,8 @@ jobs: - name: Download FFLONK key and setup data run: | - gsutil -m rsync -r gs://matterlabs-setup-keys-us/setup-keys/setup_fflonk_compact.key docker/proof-fri-gpu-compressor-gar gsutil -m rsync -r gs://matterlabs-setup-data-us/${{ inputs.setup_keys_id }} docker/proof-fri-gpu-compressor-gar + gsutil -m cp -r gs://matterlabs-setup-keys-us/setup-keys/setup_fflonk_compact.key docker/proof-fri-gpu-compressor-gar - name: Login to us-central1 GAR run: | From e25af907220e3a8d26aa4934fa8cc8680d6cd42f Mon Sep 17 00:00:00 2001 From: EmilLuta Date: Wed, 8 Jan 2025 13:13:41 +0100 Subject: [PATCH 11/97] chore: Quickfix docs for prover keys links (#3442) --- docs/src/guides/advanced/15_prover_keys.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/src/guides/advanced/15_prover_keys.md b/docs/src/guides/advanced/15_prover_keys.md index 5a3a264e8ddd..bf127b7a635f 100644 --- a/docs/src/guides/advanced/15_prover_keys.md +++ b/docs/src/guides/advanced/15_prover_keys.md @@ -19,23 +19,23 @@ We offer 13 distinct types of **'base' circuits**, including Vm, Decommitter, an each corresponding to a basic type, while one is a 'node,' and another is a 'scheduler' that oversees all others. You can find more details in the [full list here][recursive_circuit_list]. -In our new proof system, there's also a final element known as the compressor, or **snark wrapper**, representing an +In our new proof system, there's also a final steps known as the compressor and **snark wrapper**, representing an additional type of circuit. It's essential to note that each circuit type requires its unique set of keys. Also, the base circuits, leaves, node and scheduler are STARK based with FRI commitments, while the snark wrapper is SNARK based with KZG commitment. This results in slightly different contents of the keys, but their role stays the same. +More info about commitment schemes can be found [here](https://en.wikipedia.org/wiki/Commitment_scheme). ## Keys -### Setup key (big, 14GB) +### Setup keys (big, >700MB each) -> In the following [CPU](https://github.com/matter-labs/zksync-era/blob/main/prover/setup-data-cpu-keys.json) and -> [GPU](https://github.com/matter-labs/zksync-era/blob/main/prover/setup-data-gpu-keys.json) links, you'll find GCS -> buckets containing the latest keys. +The following [link](https://github.com/matter-labs/zksync-era/blob/main/prover/setup-data-gpu-keys.json) provides the +GCS buckets containing the latest setup keys. -The primary key for a given circuit is called `setup key`. These keys can be substantial in size - approximately 14GB +The primary key for a given circuit is called `setup key`. These keys can be substantial in size - approximately 700MB for our circuits. Due to their size, we don't store them directly on GitHub; instead, they need to be generated. If you’re wondering what these setup keys contain, think of them as the 'source code of the circuit.' @@ -117,8 +117,7 @@ friendly hash function (currently Poseidon2). https://github.com/matter-labs/era-zkevm_test_harness/blob/3cd647aa57fc2e1180bab53f7a3b61ec47502a46/circuit_definitions/src/circuit_definitions/base_layer/mod.rs#L77 [recursive_circuit_list]: https://github.com/matter-labs/era-zkevm_test_harness/blob/3cd647aa57fc2e1180bab53f7a3b61ec47502a46/circuit_definitions/src/circuit_definitions/recursion_layer/mod.rs#L29 -[verification_key_list]: - https://github.com/matter-labs/zksync-era/tree/6d18061df4a18803d3c6377305ef711ce60317e1/prover/data/keys +[verification_key_list]: https://github.com/matter-labs/zksync-era/tree/main/prover/data/keys [env_variables_for_hash]: https://github.com/matter-labs/zksync-era/blob/6d18061df4a18803d3c6377305ef711ce60317e1/etc/env/base/contracts.toml#L61 [prover_setup_data]: From 7a8cb27b3146148fbeb47a6d11ddfb4d023bcc2b Mon Sep 17 00:00:00 2001 From: D025 Date: Wed, 8 Jan 2025 15:31:31 +0200 Subject: [PATCH 12/97] ci: add ghcr to list of registies (#3440) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Add ghcr.io to list of registries for image storing ## Why ❔ Cost optimization ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- .../build-contract-verifier-template.yml | 14 +++++++++++++- .github/workflows/build-core-template.yml | 13 +++++++++++++ .github/workflows/build-prover-template.yml | 17 +++++++++++++++++ .../build-witness-generator-template.yml | 14 ++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-contract-verifier-template.yml b/.github/workflows/build-contract-verifier-template.yml index 7d75f81fb73c..9c53ca1dc484 100644 --- a/.github/workflows/build-contract-verifier-template.yml +++ b/.github/workflows/build-contract-verifier-template.yml @@ -147,6 +147,9 @@ jobs: build-images: name: Build and Push Docker Images needs: prepare-contracts + permissions: + packages: write + contents: read runs-on: ${{ fromJSON('["matterlabs-ci-runner-high-performance", "matterlabs-ci-runner-arm"]')[contains(matrix.platforms, 'arm')] }} strategy: matrix: @@ -208,6 +211,14 @@ jobs: docker login -u ${{ secrets.DOCKERHUB_USER }} -p ${{ secrets.DOCKERHUB_TOKEN }} gcloud auth configure-docker us-docker.pkg.dev -q + - name: Login to GitHub Container Registry + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + if: ${{ inputs.action == 'push' }} + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 with: @@ -223,13 +234,14 @@ jobs: tags: | us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} matterlabs/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} + ghcr.io/${{ github.repository_owner }}/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} - name: Push docker image if: ${{ inputs.action == 'push' }} run: | docker push us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} docker push matterlabs/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} - + docker push ghcr.io/${{ github.repository_owner }}/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} create_manifest: name: Create release manifest diff --git a/.github/workflows/build-core-template.yml b/.github/workflows/build-core-template.yml index 557d8455a31d..c76a75e9b3ea 100644 --- a/.github/workflows/build-core-template.yml +++ b/.github/workflows/build-core-template.yml @@ -155,6 +155,9 @@ jobs: env: IMAGE_TAG_SUFFIX: ${{ inputs.image_tag_suffix }}${{ (inputs.en_alpha_release && matrix.components == 'external-node') && '-alpha' || '' }} runs-on: ${{ fromJSON('["matterlabs-ci-runner-high-performance", "matterlabs-ci-runner-arm"]')[contains(matrix.platforms, 'arm')] }} + permissions: + packages: write + contents: read strategy: matrix: components: @@ -217,6 +220,14 @@ jobs: docker login -u ${{ secrets.DOCKERHUB_USER }} -p ${{ secrets.DOCKERHUB_TOKEN }} gcloud auth configure-docker us-docker.pkg.dev -q + - name: Login to GitHub Container Registry + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + if: ${{ inputs.action == 'push' }} + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build docker image uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 with: @@ -231,6 +242,7 @@ jobs: RUSTC_WRAPPER=sccache tags: | us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} + ghcr.io/${{ github.repository_owner }}/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} matterlabs/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} - name: Push docker image @@ -238,6 +250,7 @@ jobs: run: | docker push us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} docker push matterlabs/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} + docker push ghcr.io/${{ github.repository_owner }}/${{ matrix.components }}:${{ env.IMAGE_TAG_SHA_TS }}-${{ env.PLATFORM }} create_manifest: name: Create release manifest diff --git a/.github/workflows/build-prover-template.yml b/.github/workflows/build-prover-template.yml index 3a721e4425a8..f04ddf56728d 100644 --- a/.github/workflows/build-prover-template.yml +++ b/.github/workflows/build-prover-template.yml @@ -91,6 +91,9 @@ jobs: env: PROTOCOL_VERSION: ${{ needs.get-protocol-version.outputs.protocol_version }} runs-on: [matterlabs-ci-runner-high-performance] + permissions: + packages: write + contents: read strategy: matrix: components: @@ -153,6 +156,14 @@ jobs: docker login -u ${{ secrets.DOCKERHUB_USER }} -p ${{ secrets.DOCKERHUB_TOKEN }} gcloud auth configure-docker us-docker.pkg.dev -q + - name: Login to GitHub Container Registry + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 with: @@ -170,9 +181,12 @@ jobs: tags: | us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} matterlabs/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} + ghcr.io/${{ github.repository_owner }}/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} matterlabs/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} + ghcr.io/${{ github.repository_owner }}/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:latest + ghcr.io/${{ github.repository_owner }}/${{ matrix.components }}:latest matterlabs/${{ matrix.components }}:latest - name: Push docker image @@ -180,9 +194,12 @@ jobs: run: | docker push us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} docker push matterlabs/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} + docker push ghcr.io/${{ github.repository_owner }}/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} docker push us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} + docker push ghcr.io/${{ github.repository_owner }}/${{ matrix.components }}:${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} docker push matterlabs/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} docker push us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:latest + docker push ghcr.io/${{ github.repository_owner }}/${{ matrix.components }}:latest docker push matterlabs/${{ matrix.components }}:latest copy-images: diff --git a/.github/workflows/build-witness-generator-template.yml b/.github/workflows/build-witness-generator-template.yml index a96d217da832..7b25510ade12 100644 --- a/.github/workflows/build-witness-generator-template.yml +++ b/.github/workflows/build-witness-generator-template.yml @@ -78,6 +78,9 @@ jobs: build-images: name: Build and Push Docker Images needs: get-protocol-version + permissions: + packages: write + contents: read env: PROTOCOL_VERSION: ${{ needs.get-protocol-version.outputs.protocol_version }} runs-on: [matterlabs-ci-runner-c3d] @@ -115,6 +118,14 @@ jobs: docker login -u ${{ secrets.DOCKERHUB_USER }} -p ${{ secrets.DOCKERHUB_TOKEN }} gcloud auth configure-docker us-docker.pkg.dev -q + - name: Login to GitHub Container Registry + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + if: github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')) + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 with: @@ -132,7 +143,10 @@ jobs: tags: | us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} matterlabs/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} + ghcr.io/${{ github.repository_owner }}/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} + ghcr.io/${{ github.repository_owner }}/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} matterlabs/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:latest + ghcr.io/${{ github.repository_owner }}/${{ matrix.components }}:latest matterlabs/${{ matrix.components }}:latest From ecccdda81697004b254369288274919925c4c19c Mon Sep 17 00:00:00 2001 From: D025 Date: Wed, 8 Jan 2025 17:31:46 +0200 Subject: [PATCH 13/97] ci: fix workflow for push prover ghcr images (#3444) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ fix workflow for push prover ghcr images ## Why ❔ Broken ghcr image ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- .github/workflows/build-prover-template.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-prover-template.yml b/.github/workflows/build-prover-template.yml index f04ddf56728d..7d2920a2af4e 100644 --- a/.github/workflows/build-prover-template.yml +++ b/.github/workflows/build-prover-template.yml @@ -196,7 +196,7 @@ jobs: docker push matterlabs/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} docker push ghcr.io/${{ github.repository_owner }}/${{ matrix.components }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ env.IMAGE_TAG_SHA_TS }} docker push us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} - docker push ghcr.io/${{ github.repository_owner }}/${{ matrix.components }}:${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} + docker push ghcr.io/${{ github.repository_owner }}/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} docker push matterlabs/${{ matrix.components }}:2.0-${{ env.IMAGE_TAG_SHA_TS }} docker push us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:latest docker push ghcr.io/${{ github.repository_owner }}/${{ matrix.components }}:latest From 7adb612d1b78098281b7fa657423eade16c8f26e Mon Sep 17 00:00:00 2001 From: Anton Baliasnikov Date: Thu, 9 Jan 2025 08:35:16 +0000 Subject: [PATCH 14/97] refactor: rename zkstack crates with unique names for publishing (#3443) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ * [x] Rename `zkstack_cli` crates with unique names for publishing. * [x] Set individual versions of the `zkstack_cli` workspace crates to the common workspace version. ## Why ❔ To allow publishing to crates.io. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- zkstack_cli/Cargo.lock | 148 +++++++++--------- zkstack_cli/Cargo.toml | 8 +- zkstack_cli/crates/common/Cargo.toml | 8 +- zkstack_cli/crates/common/src/ethereum.rs | 2 +- zkstack_cli/crates/common/src/version.rs | 8 +- zkstack_cli/crates/common/src/wallets.rs | 2 +- zkstack_cli/crates/config/Cargo.toml | 8 +- zkstack_cli/crates/config/src/chain.rs | 2 +- zkstack_cli/crates/config/src/ecosystem.rs | 4 +- .../crates/config/src/explorer_compose.rs | 2 +- .../deploy_gateway_ctm/input.rs | 2 +- .../gateway_chain_upgrade/input.rs | 2 +- .../forge_interface/register_chain/input.rs | 2 +- zkstack_cli/crates/config/src/general.rs | 2 +- zkstack_cli/crates/config/src/portal.rs | 2 +- zkstack_cli/crates/config/src/secrets.rs | 2 +- zkstack_cli/crates/config/src/traits.rs | 6 +- .../crates/config/src/wallet_creation.rs | 4 +- zkstack_cli/crates/config/src/wallets.rs | 2 +- .../crates/git_version_macro/Cargo.toml | 2 +- zkstack_cli/crates/types/Cargo.toml | 4 +- zkstack_cli/crates/zkstack/Cargo.toml | 8 +- .../crates/zkstack/src/accept_ownership.rs | 18 +-- .../zkstack/src/commands/args/containers.rs | 2 +- .../crates/zkstack/src/commands/args/wait.rs | 2 +- .../zkstack/src/commands/autocomplete.rs | 2 +- .../commands/chain/accept_chain_ownership.rs | 4 +- .../commands/chain/args/build_transactions.rs | 2 +- .../zkstack/src/commands/chain/args/create.rs | 6 +- .../src/commands/chain/args/genesis.rs | 4 +- .../src/commands/chain/args/init/configs.rs | 6 +- .../src/commands/chain/args/init/mod.rs | 8 +- .../src/commands/chain/build_transactions.rs | 8 +- .../zkstack/src/commands/chain/common.rs | 10 +- .../src/commands/chain/convert_to_gateway.rs | 10 +- .../zkstack/src/commands/chain/create.rs | 6 +- .../src/commands/chain/deploy_l2_contracts.rs | 6 +- .../src/commands/chain/deploy_paymaster.rs | 6 +- .../src/commands/chain/enable_evm_emulator.rs | 4 +- .../src/commands/chain/gateway_upgrade.rs | 24 +-- .../src/commands/chain/genesis/database.rs | 8 +- .../zkstack/src/commands/chain/genesis/mod.rs | 4 +- .../src/commands/chain/genesis/server.rs | 6 +- .../src/commands/chain/init/configs.rs | 8 +- .../zkstack/src/commands/chain/init/mod.rs | 6 +- .../commands/chain/migrate_from_gateway.rs | 26 +-- .../src/commands/chain/migrate_to_gateway.rs | 26 +-- .../crates/zkstack/src/commands/chain/mod.rs | 2 +- .../src/commands/chain/register_chain.rs | 6 +- .../chain/set_token_multiplier_setter.rs | 12 +- .../src/commands/chain/setup_legacy_bridge.rs | 6 +- .../zkstack/src/commands/consensus/mod.rs | 12 +- .../crates/zkstack/src/commands/containers.rs | 9 +- .../commands/contract_verifier/args/init.rs | 2 +- .../contract_verifier/args/releases.rs | 2 +- .../src/commands/contract_verifier/build.rs | 4 +- .../src/commands/contract_verifier/init.rs | 4 +- .../src/commands/contract_verifier/run.rs | 4 +- .../src/commands/contract_verifier/wait.rs | 4 +- .../src/commands/dev/commands/clean/mod.rs | 4 +- .../commands/dev/commands/config_writer.rs | 4 +- .../src/commands/dev/commands/contracts.rs | 6 +- .../commands/database/args/new_migration.rs | 2 +- .../dev/commands/database/check_sqlx_data.rs | 4 +- .../commands/dev/commands/database/drop.rs | 4 +- .../commands/dev/commands/database/migrate.rs | 4 +- .../dev/commands/database/new_migration.rs | 4 +- .../commands/dev/commands/database/prepare.rs | 4 +- .../commands/dev/commands/database/reset.rs | 4 +- .../commands/dev/commands/database/setup.rs | 4 +- .../zkstack/src/commands/dev/commands/fmt.rs | 4 +- .../src/commands/dev/commands/genesis.rs | 4 +- .../zkstack/src/commands/dev/commands/lint.rs | 4 +- .../dev/commands/prover/args/insert_batch.rs | 4 +- .../commands/prover/args/insert_version.rs | 12 +- .../src/commands/dev/commands/prover/info.rs | 4 +- .../dev/commands/prover/insert_batch.rs | 4 +- .../dev/commands/prover/insert_version.rs | 4 +- .../commands/send_transactions/args/mod.rs | 2 +- .../dev/commands/send_transactions/mod.rs | 4 +- .../src/commands/dev/commands/snapshot.rs | 4 +- .../src/commands/dev/commands/sql_fmt.rs | 2 +- .../src/commands/dev/commands/status/args.rs | 2 +- .../src/commands/dev/commands/status/mod.rs | 2 +- .../src/commands/dev/commands/test/build.rs | 2 +- .../src/commands/dev/commands/test/db.rs | 2 +- .../src/commands/dev/commands/test/fees.rs | 4 +- .../commands/dev/commands/test/integration.rs | 4 +- .../dev/commands/test/l1_contracts.rs | 4 +- .../commands/dev/commands/test/loadtest.rs | 4 +- .../src/commands/dev/commands/test/prover.rs | 4 +- .../commands/dev/commands/test/recovery.rs | 4 +- .../src/commands/dev/commands/test/revert.rs | 4 +- .../src/commands/dev/commands/test/rust.rs | 4 +- .../src/commands/dev/commands/test/upgrade.rs | 4 +- .../src/commands/dev/commands/test/utils.rs | 6 +- .../src/commands/dev/commands/test/wallet.rs | 4 +- .../crates/zkstack/src/commands/dev/dals.rs | 2 +- .../ecosystem/args/build_transactions.rs | 2 +- .../src/commands/ecosystem/args/create.rs | 4 +- .../ecosystem/args/gateway_upgrade.rs | 4 +- .../src/commands/ecosystem/args/init.rs | 4 +- .../commands/ecosystem/build_transactions.rs | 4 +- .../src/commands/ecosystem/change_default.rs | 4 +- .../zkstack/src/commands/ecosystem/common.rs | 8 +- .../zkstack/src/commands/ecosystem/create.rs | 6 +- .../src/commands/ecosystem/create_configs.rs | 4 +- .../zkstack/src/commands/ecosystem/init.rs | 8 +- .../commands/ecosystem/setup_observability.rs | 4 +- .../zkstack/src/commands/ecosystem/utils.rs | 2 +- .../zkstack/src/commands/explorer/backend.rs | 4 +- .../zkstack/src/commands/explorer/init.rs | 10 +- .../zkstack/src/commands/explorer/run.rs | 4 +- .../external_node/args/prepare_configs.rs | 4 +- .../src/commands/external_node/build.rs | 4 +- .../src/commands/external_node/init.rs | 8 +- .../commands/external_node/prepare_configs.rs | 6 +- .../zkstack/src/commands/external_node/run.rs | 4 +- .../src/commands/external_node/wait.rs | 4 +- .../crates/zkstack/src/commands/portal.rs | 10 +- .../commands/prover/args/compressor_keys.rs | 2 +- .../zkstack/src/commands/prover/args/init.rs | 4 +- .../commands/prover/args/init_bellman_cuda.rs | 2 +- .../zkstack/src/commands/prover/args/run.rs | 4 +- .../src/commands/prover/args/setup_keys.rs | 2 +- .../src/commands/prover/compressor_keys.rs | 4 +- .../crates/zkstack/src/commands/prover/gcs.rs | 4 +- .../zkstack/src/commands/prover/init.rs | 6 +- .../src/commands/prover/init_bellman_cuda.rs | 6 +- .../crates/zkstack/src/commands/prover/run.rs | 4 +- .../zkstack/src/commands/prover/setup_keys.rs | 6 +- .../crates/zkstack/src/commands/server.rs | 6 +- .../crates/zkstack/src/commands/update.rs | 6 +- zkstack_cli/crates/zkstack/src/defaults.rs | 2 +- .../crates/zkstack/src/enable_evm_emulator.rs | 10 +- .../crates/zkstack/src/external_node.rs | 6 +- zkstack_cli/crates/zkstack/src/main.rs | 6 +- .../crates/zkstack/src/utils/consensus.rs | 2 +- zkstack_cli/crates/zkstack/src/utils/forge.rs | 4 +- .../crates/zkstack/src/utils/link_to_code.rs | 6 +- zkstack_cli/crates/zkstack/src/utils/ports.rs | 8 +- .../crates/zkstack/src/utils/rocks_db.rs | 2 +- 142 files changed, 446 insertions(+), 429 deletions(-) diff --git a/zkstack_cli/Cargo.lock b/zkstack_cli/Cargo.lock index dd700ea548d0..1ea38d968073 100644 --- a/zkstack_cli/Cargo.lock +++ b/zkstack_cli/Cargo.lock @@ -798,35 +798,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "common" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "clap", - "cliclack", - "console", - "ethers", - "futures", - "git_version_macro", - "once_cell", - "serde", - "serde_json", - "serde_yaml", - "sqlx", - "strum", - "thiserror", - "tokio", - "toml", - "types", - "url", - "xshell", - "zksync_system_constants", - "zksync_types", - "zksync_web3_decl", -] - [[package]] name = "common-path" version = "1.0.0" @@ -848,29 +819,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "config" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap", - "common", - "ethers", - "rand", - "serde", - "serde_json", - "serde_yaml", - "strum", - "thiserror", - "types", - "url", - "xshell", - "zksync_basic_types", - "zksync_config", - "zksync_protobuf", - "zksync_protobuf_config", -] - [[package]] name = "configparser" version = "3.1.0" @@ -2094,13 +2042,6 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" -[[package]] -name = "git_version_macro" -version = "0.1.0" -dependencies = [ - "chrono", -] - [[package]] name = "glob" version = "0.3.1" @@ -6282,18 +6223,6 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" -[[package]] -name = "types" -version = "0.1.0" -dependencies = [ - "clap", - "ethers", - "serde", - "strum", - "thiserror", - "zksync_basic_types", -] - [[package]] name = "uint" version = "0.9.5" @@ -7124,8 +7053,6 @@ dependencies = [ "clap-markdown", "clap_complete", "cliclack", - "common", - "config", "dirs", "ethers", "futures", @@ -7144,9 +7071,11 @@ dependencies = [ "thiserror", "tokio", "toml", - "types", "url", "xshell", + "zkstack_cli_common", + "zkstack_cli_config", + "zkstack_cli_types", "zksync_basic_types", "zksync_config", "zksync_consensus_crypto", @@ -7160,6 +7089,77 @@ dependencies = [ "zksync_web3_decl", ] +[[package]] +name = "zkstack_cli_common" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "clap", + "cliclack", + "console", + "ethers", + "futures", + "once_cell", + "serde", + "serde_json", + "serde_yaml", + "sqlx", + "strum", + "thiserror", + "tokio", + "toml", + "url", + "xshell", + "zkstack_cli_git_version_macro", + "zkstack_cli_types", + "zksync_system_constants", + "zksync_types", + "zksync_web3_decl", +] + +[[package]] +name = "zkstack_cli_config" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "ethers", + "rand", + "serde", + "serde_json", + "serde_yaml", + "strum", + "thiserror", + "url", + "xshell", + "zkstack_cli_common", + "zkstack_cli_types", + "zksync_basic_types", + "zksync_config", + "zksync_protobuf", + "zksync_protobuf_config", +] + +[[package]] +name = "zkstack_cli_git_version_macro" +version = "0.1.0" +dependencies = [ + "chrono", +] + +[[package]] +name = "zkstack_cli_types" +version = "0.1.0" +dependencies = [ + "clap", + "ethers", + "serde", + "strum", + "thiserror", + "zksync_basic_types", +] + [[package]] name = "zksync_basic_types" version = "0.1.0" diff --git a/zkstack_cli/Cargo.toml b/zkstack_cli/Cargo.toml index c382e2059007..6d18d1a531d7 100644 --- a/zkstack_cli/Cargo.toml +++ b/zkstack_cli/Cargo.toml @@ -21,10 +21,10 @@ keywords = ["zk", "cryptography", "blockchain", "ZKStack", "ZKsync"] [workspace.dependencies] # Local dependencies -common = { path = "crates/common" } -config = { path = "crates/config" } -types = { path = "crates/types" } -git_version_macro = { path = "crates/git_version_macro" } +zkstack_cli_common = { path = "crates/common" } +zkstack_cli_config = { path = "crates/config" } +zkstack_cli_types = { path = "crates/types" } +zkstack_cli_git_version_macro = { path = "crates/git_version_macro" } # ZkSync deps zksync_config = { path = "../core/lib/config" } diff --git a/zkstack_cli/crates/common/Cargo.toml b/zkstack_cli/crates/common/Cargo.toml index c906c3d28d04..7af8fab92bb8 100644 --- a/zkstack_cli/crates/common/Cargo.toml +++ b/zkstack_cli/crates/common/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "common" -version = "0.1.0" +name = "zkstack_cli_common" +version.workspace = true edition.workspace = true homepage.workspace = true license.workspace = true @@ -24,12 +24,12 @@ serde_yaml.workspace = true sqlx.workspace = true tokio.workspace = true toml.workspace = true -types.workspace = true +zkstack_cli_types.workspace = true url.workspace = true xshell.workspace = true thiserror.workspace = true strum.workspace = true -git_version_macro.workspace = true +zkstack_cli_git_version_macro.workspace = true async-trait.workspace = true zksync_system_constants.workspace = true zksync_types.workspace = true diff --git a/zkstack_cli/crates/common/src/ethereum.rs b/zkstack_cli/crates/common/src/ethereum.rs index 2100746fecff..96ec8b4f3597 100644 --- a/zkstack_cli/crates/common/src/ethereum.rs +++ b/zkstack_cli/crates/common/src/ethereum.rs @@ -8,7 +8,7 @@ use ethers::{ providers::Middleware, types::{Address, TransactionRequest}, }; -use types::TokenInfo; +use zkstack_cli_types::TokenInfo; use crate::{logger, wallets::Wallet}; diff --git a/zkstack_cli/crates/common/src/version.rs b/zkstack_cli/crates/common/src/version.rs index 43be7a07b7ee..f21018a0d7dd 100644 --- a/zkstack_cli/crates/common/src/version.rs +++ b/zkstack_cli/crates/common/src/version.rs @@ -1,7 +1,7 @@ -const GIT_VERSION: &str = git_version_macro::build_git_revision!(); -const GIT_BRANCH: &str = git_version_macro::build_git_branch!(); -const GIT_SUBMODULES: &[(&str, &str)] = git_version_macro::build_git_submodules!(); -const BUILD_TIMESTAMP: &str = git_version_macro::build_timestamp!(); +const GIT_VERSION: &str = zkstack_cli_git_version_macro::build_git_revision!(); +const GIT_BRANCH: &str = zkstack_cli_git_version_macro::build_git_branch!(); +const GIT_SUBMODULES: &[(&str, &str)] = zkstack_cli_git_version_macro::build_git_submodules!(); +const BUILD_TIMESTAMP: &str = zkstack_cli_git_version_macro::build_timestamp!(); /// Returns a multi-line version message that includes: /// - provided crate version diff --git a/zkstack_cli/crates/common/src/wallets.rs b/zkstack_cli/crates/common/src/wallets.rs index 43a9864474cc..ba1f6d239724 100644 --- a/zkstack_cli/crates/common/src/wallets.rs +++ b/zkstack_cli/crates/common/src/wallets.rs @@ -4,7 +4,7 @@ use ethers::{ types::{Address, H256}, }; use serde::{Deserialize, Serialize}; -use types::parse_h256; +use zkstack_cli_types::parse_h256; #[derive(Serialize, Deserialize)] struct WalletSerde { diff --git a/zkstack_cli/crates/config/Cargo.toml b/zkstack_cli/crates/config/Cargo.toml index 9320beffef22..5ddf36c2d30b 100644 --- a/zkstack_cli/crates/config/Cargo.toml +++ b/zkstack_cli/crates/config/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "config" -version = "0.1.0" +name = "zkstack_cli_config" +version.workspace = true edition.workspace = true homepage.workspace = true license.workspace = true @@ -13,7 +13,7 @@ keywords.workspace = true [dependencies] anyhow.workspace = true clap.workspace = true -common.workspace = true +zkstack_cli_common.workspace = true ethers.workspace = true rand.workspace = true serde.workspace = true @@ -21,7 +21,7 @@ serde_json.workspace = true serde_yaml.workspace = true strum.workspace = true thiserror.workspace = true -types.workspace = true +zkstack_cli_types.workspace = true url.workspace = true xshell.workspace = true diff --git a/zkstack_cli/crates/config/src/chain.rs b/zkstack_cli/crates/config/src/chain.rs index cd93299cfc41..edd6199511d7 100644 --- a/zkstack_cli/crates/config/src/chain.rs +++ b/zkstack_cli/crates/config/src/chain.rs @@ -4,8 +4,8 @@ use std::{ }; use serde::{Deserialize, Serialize, Serializer}; -use types::{BaseToken, L1BatchCommitmentMode, L1Network, ProverMode, WalletCreation}; use xshell::Shell; +use zkstack_cli_types::{BaseToken, L1BatchCommitmentMode, L1Network, ProverMode, WalletCreation}; use zksync_basic_types::L2ChainId; use zksync_config::configs::{GatewayChainConfig, GatewayConfig}; diff --git a/zkstack_cli/crates/config/src/ecosystem.rs b/zkstack_cli/crates/config/src/ecosystem.rs index 5fe85b175de4..906cf8dd012b 100644 --- a/zkstack_cli/crates/config/src/ecosystem.rs +++ b/zkstack_cli/crates/config/src/ecosystem.rs @@ -3,11 +3,11 @@ use std::{ path::{Path, PathBuf}, }; -use common::{config::global_config, logger}; use serde::{Deserialize, Serialize, Serializer}; use thiserror::Error; -use types::{L1Network, ProverMode, WalletCreation}; use xshell::Shell; +use zkstack_cli_common::{config::global_config, logger}; +use zkstack_cli_types::{L1Network, ProverMode, WalletCreation}; use zksync_basic_types::L2ChainId; use crate::{ diff --git a/zkstack_cli/crates/config/src/explorer_compose.rs b/zkstack_cli/crates/config/src/explorer_compose.rs index 13dd665d2e3d..5c6593cd7a46 100644 --- a/zkstack_cli/crates/config/src/explorer_compose.rs +++ b/zkstack_cli/crates/config/src/explorer_compose.rs @@ -4,9 +4,9 @@ use std::{ }; use anyhow::Context; -use common::{db, docker::adjust_localhost_for_docker}; use serde::{Deserialize, Serialize}; use url::Url; +use zkstack_cli_common::{db, docker::adjust_localhost_for_docker}; use crate::{ consts::{ diff --git a/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/input.rs b/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/input.rs index 8f35180077a2..4143a286da62 100644 --- a/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/input.rs +++ b/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/input.rs @@ -1,7 +1,7 @@ /// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. use ethers::abi::Address; use serde::{Deserialize, Serialize}; -use types::ProverMode; +use zkstack_cli_types::ProverMode; use zksync_basic_types::{H256, U256}; use zksync_config::GenesisConfig; diff --git a/zkstack_cli/crates/config/src/forge_interface/gateway_chain_upgrade/input.rs b/zkstack_cli/crates/config/src/forge_interface/gateway_chain_upgrade/input.rs index 5fce7ebe3388..9bedabf59a2d 100644 --- a/zkstack_cli/crates/config/src/forge_interface/gateway_chain_upgrade/input.rs +++ b/zkstack_cli/crates/config/src/forge_interface/gateway_chain_upgrade/input.rs @@ -1,7 +1,7 @@ /// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. use ethers::types::Address; use serde::{Deserialize, Serialize}; -use types::L1BatchCommitmentMode; +use zkstack_cli_types::L1BatchCommitmentMode; use zksync_basic_types::L2ChainId; use crate::{traits::ZkStackConfig, ChainConfig}; diff --git a/zkstack_cli/crates/config/src/forge_interface/register_chain/input.rs b/zkstack_cli/crates/config/src/forge_interface/register_chain/input.rs index 7d30c7f49a70..1e6a31009506 100644 --- a/zkstack_cli/crates/config/src/forge_interface/register_chain/input.rs +++ b/zkstack_cli/crates/config/src/forge_interface/register_chain/input.rs @@ -1,7 +1,7 @@ use ethers::types::Address; use rand::Rng; use serde::{Deserialize, Serialize}; -use types::L1BatchCommitmentMode; +use zkstack_cli_types::L1BatchCommitmentMode; use zksync_basic_types::L2ChainId; use crate::{traits::ZkStackConfig, ChainConfig, ContractsConfig}; diff --git a/zkstack_cli/crates/config/src/general.rs b/zkstack_cli/crates/config/src/general.rs index 0079105b66ca..c1639d6bea15 100644 --- a/zkstack_cli/crates/config/src/general.rs +++ b/zkstack_cli/crates/config/src/general.rs @@ -1,9 +1,9 @@ use std::path::{Path, PathBuf}; use anyhow::Context; -use common::yaml::merge_yaml; use url::Url; use xshell::Shell; +use zkstack_cli_common::yaml::merge_yaml; use zksync_config::configs::object_store::ObjectStoreMode; pub use zksync_config::configs::GeneralConfig; use zksync_protobuf_config::{encode_yaml_repr, read_yaml_repr}; diff --git a/zkstack_cli/crates/config/src/portal.rs b/zkstack_cli/crates/config/src/portal.rs index 2b6f0ffd5156..ab121e8e177d 100644 --- a/zkstack_cli/crates/config/src/portal.rs +++ b/zkstack_cli/crates/config/src/portal.rs @@ -1,8 +1,8 @@ use std::path::{Path, PathBuf}; use serde::{Deserialize, Serialize}; -use types::TokenInfo; use xshell::Shell; +use zkstack_cli_types::TokenInfo; use crate::{ consts::{ diff --git a/zkstack_cli/crates/config/src/secrets.rs b/zkstack_cli/crates/config/src/secrets.rs index cf0a9927c560..91e8964b4651 100644 --- a/zkstack_cli/crates/config/src/secrets.rs +++ b/zkstack_cli/crates/config/src/secrets.rs @@ -1,8 +1,8 @@ use std::{path::Path, str::FromStr}; use anyhow::Context; -use common::db::DatabaseConfig; use xshell::Shell; +use zkstack_cli_common::db::DatabaseConfig; use zksync_basic_types::url::SensitiveUrl; pub use zksync_config::configs::Secrets as SecretsConfig; use zksync_protobuf_config::{encode_yaml_repr, read_yaml_repr}; diff --git a/zkstack_cli/crates/config/src/traits.rs b/zkstack_cli/crates/config/src/traits.rs index a4a4ad22c613..d21641e33ff5 100644 --- a/zkstack_cli/crates/config/src/traits.rs +++ b/zkstack_cli/crates/config/src/traits.rs @@ -1,12 +1,12 @@ use std::path::{Path, PathBuf}; use anyhow::{bail, Context}; -use common::files::{ - read_json_file, read_toml_file, read_yaml_file, save_json_file, save_toml_file, save_yaml_file, -}; use serde::{de::DeserializeOwned, Serialize}; use url::Url; use xshell::Shell; +use zkstack_cli_common::files::{ + read_json_file, read_toml_file, read_yaml_file, save_json_file, save_toml_file, save_yaml_file, +}; // Configs that we use only inside ZK Stack CLI, we don't have protobuf implementation for them. pub trait ZkStackConfig {} diff --git a/zkstack_cli/crates/config/src/wallet_creation.rs b/zkstack_cli/crates/config/src/wallet_creation.rs index 6cfdf08a36d3..547b3a0e1c90 100644 --- a/zkstack_cli/crates/config/src/wallet_creation.rs +++ b/zkstack_cli/crates/config/src/wallet_creation.rs @@ -1,9 +1,9 @@ use std::path::{Path, PathBuf}; -use common::wallets::Wallet; use rand::thread_rng; -use types::WalletCreation; use xshell::Shell; +use zkstack_cli_common::wallets::Wallet; +use zkstack_cli_types::WalletCreation; use crate::{ consts::{BASE_PATH, TEST_CONFIG_PATH}, diff --git a/zkstack_cli/crates/config/src/wallets.rs b/zkstack_cli/crates/config/src/wallets.rs index 735848f6e34d..edd12b16bfe6 100644 --- a/zkstack_cli/crates/config/src/wallets.rs +++ b/zkstack_cli/crates/config/src/wallets.rs @@ -1,6 +1,6 @@ -use common::wallets::Wallet; use rand::{CryptoRng, Rng}; use serde::{Deserialize, Serialize}; +use zkstack_cli_common::wallets::Wallet; use crate::{ consts::WALLETS_FILE, diff --git a/zkstack_cli/crates/git_version_macro/Cargo.toml b/zkstack_cli/crates/git_version_macro/Cargo.toml index eb70b450a4cf..3a8352abafc1 100644 --- a/zkstack_cli/crates/git_version_macro/Cargo.toml +++ b/zkstack_cli/crates/git_version_macro/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "git_version_macro" +name = "zkstack_cli_git_version_macro" edition = "2021" description = "Procedural macro to generate metainformation about build in compile time" version.workspace = true diff --git a/zkstack_cli/crates/types/Cargo.toml b/zkstack_cli/crates/types/Cargo.toml index 97e00e1ba46b..2ce034f1cbd5 100644 --- a/zkstack_cli/crates/types/Cargo.toml +++ b/zkstack_cli/crates/types/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "types" -version = "0.1.0" +name = "zkstack_cli_types" +version.workspace = true edition.workspace = true homepage.workspace = true license.workspace = true diff --git a/zkstack_cli/crates/zkstack/Cargo.toml b/zkstack_cli/crates/zkstack/Cargo.toml index a19ced3f39c4..96d1dbf25be6 100644 --- a/zkstack_cli/crates/zkstack/Cargo.toml +++ b/zkstack_cli/crates/zkstack/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zkstack" -version = "0.1.0" +version.workspace = true edition.workspace = true homepage.workspace = true license.workspace = true @@ -17,8 +17,8 @@ clap.workspace = true clap_complete.workspace = true clap-markdown.workspace = true cliclack.workspace = true -common.workspace = true -config.workspace = true +zkstack_cli_common.workspace = true +zkstack_cli_config.workspace = true dirs.workspace = true ethers.workspace = true futures.workspace = true @@ -34,7 +34,7 @@ sqruff-lib = "0.19.0" thiserror.workspace = true tokio.workspace = true toml.workspace = true -types.workspace = true +zkstack_cli_types.workspace = true url.workspace = true xshell.workspace = true zksync_basic_types.workspace = true diff --git a/zkstack_cli/crates/zkstack/src/accept_ownership.rs b/zkstack_cli/crates/zkstack/src/accept_ownership.rs index ab13661d6adb..cde1a365e8a0 100644 --- a/zkstack_cli/crates/zkstack/src/accept_ownership.rs +++ b/zkstack_cli/crates/zkstack/src/accept_ownership.rs @@ -1,13 +1,4 @@ use anyhow::Context; -use common::{ - forge::{Forge, ForgeScript, ForgeScriptArgs}, - spinner::Spinner, - wallets::Wallet, -}; -use config::{ - forge_interface::script_params::ACCEPT_GOVERNANCE_SCRIPT_PARAMS, ChainConfig, ContractsConfig, - EcosystemConfig, -}; use ethers::{ abi::{parse_abi, Token}, contract::BaseContract, @@ -15,6 +6,15 @@ use ethers::{ }; use lazy_static::lazy_static; use xshell::Shell; +use zkstack_cli_common::{ + forge::{Forge, ForgeScript, ForgeScriptArgs}, + spinner::Spinner, + wallets::Wallet, +}; +use zkstack_cli_config::{ + forge_interface::script_params::ACCEPT_GOVERNANCE_SCRIPT_PARAMS, ChainConfig, ContractsConfig, + EcosystemConfig, +}; use zksync_basic_types::U256; use crate::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/args/containers.rs b/zkstack_cli/crates/zkstack/src/commands/args/containers.rs index c996d65598ff..bff596f93cfe 100644 --- a/zkstack_cli/crates/zkstack/src/commands/args/containers.rs +++ b/zkstack_cli/crates/zkstack/src/commands/args/containers.rs @@ -16,7 +16,7 @@ pub struct ContainersArgsFinal { impl ContainersArgs { pub fn fill_values_with_prompt(self) -> ContainersArgsFinal { let observability = self.observability.unwrap_or_else(|| { - common::PromptConfirm::new(MSG_OBSERVABILITY_RUN_PROMPT) + zkstack_cli_common::PromptConfirm::new(MSG_OBSERVABILITY_RUN_PROMPT) .default(true) .ask() }); diff --git a/zkstack_cli/crates/zkstack/src/commands/args/wait.rs b/zkstack_cli/crates/zkstack/src/commands/args/wait.rs index a3a7e32ae8b4..315552cdb72a 100644 --- a/zkstack_cli/crates/zkstack/src/commands/args/wait.rs +++ b/zkstack_cli/crates/zkstack/src/commands/args/wait.rs @@ -2,10 +2,10 @@ use std::{fmt, future::Future, time::Duration}; use anyhow::Context as _; use clap::Parser; -use common::logger; use reqwest::StatusCode; use serde::{Deserialize, Serialize}; use tokio::time::MissedTickBehavior; +use zkstack_cli_common::logger; use crate::messages::{ msg_wait_connect_err, msg_wait_non_successful_response, msg_wait_not_healthy, diff --git a/zkstack_cli/crates/zkstack/src/commands/autocomplete.rs b/zkstack_cli/crates/zkstack/src/commands/autocomplete.rs index 0f2105cd5efa..ee6526e5aa80 100644 --- a/zkstack_cli/crates/zkstack/src/commands/autocomplete.rs +++ b/zkstack_cli/crates/zkstack/src/commands/autocomplete.rs @@ -6,7 +6,7 @@ use std::{ use anyhow::Context; use clap::CommandFactory; use clap_complete::{generate, Generator}; -use common::logger; +use zkstack_cli_common::logger; use super::args::AutocompleteArgs; use crate::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/accept_chain_ownership.rs b/zkstack_cli/crates/zkstack/src/commands/chain/accept_chain_ownership.rs index cf3e2981b3c7..46b92248ae3f 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/accept_chain_ownership.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/accept_chain_ownership.rs @@ -1,7 +1,7 @@ use anyhow::Context; -use common::{forge::ForgeScriptArgs, logger, spinner::Spinner}; -use config::EcosystemConfig; use xshell::Shell; +use zkstack_cli_common::{forge::ForgeScriptArgs, logger, spinner::Spinner}; +use zkstack_cli_config::EcosystemConfig; use crate::{ accept_ownership::accept_admin, diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/args/build_transactions.rs b/zkstack_cli/crates/zkstack/src/commands/chain/args/build_transactions.rs index 793bea487f7e..ab92c91bbda3 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/args/build_transactions.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/args/build_transactions.rs @@ -1,9 +1,9 @@ use std::path::PathBuf; use clap::Parser; -use common::{config::global_config, forge::ForgeScriptArgs, Prompt}; use serde::{Deserialize, Serialize}; use url::Url; +use zkstack_cli_common::{config::global_config, forge::ForgeScriptArgs, Prompt}; use crate::{ consts::DEFAULT_UNSIGNED_TRANSACTIONS_DIR, diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/args/create.rs b/zkstack_cli/crates/zkstack/src/commands/chain/args/create.rs index 5310a46d23c4..c29696b92dec 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/args/create.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/args/create.rs @@ -2,12 +2,12 @@ use std::{path::PathBuf, str::FromStr}; use anyhow::{bail, Context}; use clap::{Parser, ValueEnum, ValueHint}; -use common::{Prompt, PromptConfirm, PromptSelect}; -use config::forge_interface::deploy_ecosystem::output::Erc20Token; use serde::{Deserialize, Serialize}; use slugify_rs::slugify; use strum::{Display, EnumIter, IntoEnumIterator}; -use types::{BaseToken, L1BatchCommitmentMode, L1Network, ProverMode, WalletCreation}; +use zkstack_cli_common::{Prompt, PromptConfirm, PromptSelect}; +use zkstack_cli_config::forge_interface::deploy_ecosystem::output::Erc20Token; +use zkstack_cli_types::{BaseToken, L1BatchCommitmentMode, L1Network, ProverMode, WalletCreation}; use zksync_basic_types::H160; use crate::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/args/genesis.rs b/zkstack_cli/crates/zkstack/src/commands/chain/args/genesis.rs index f990cbfd77da..ef98a777352e 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/args/genesis.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/args/genesis.rs @@ -1,10 +1,10 @@ use anyhow::Context; use clap::Parser; -use common::{db::DatabaseConfig, Prompt}; -use config::ChainConfig; use serde::{Deserialize, Serialize}; use slugify_rs::slugify; use url::Url; +use zkstack_cli_common::{db::DatabaseConfig, Prompt}; +use zkstack_cli_config::ChainConfig; use crate::{ defaults::{generate_db_names, DBNames, DATABASE_SERVER_URL}, diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/args/init/configs.rs b/zkstack_cli/crates/zkstack/src/commands/chain/args/init/configs.rs index b34809643cf5..adfb5ffaf9b7 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/args/init/configs.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/args/init/configs.rs @@ -1,9 +1,9 @@ use clap::Parser; -use common::Prompt; -use config::ChainConfig; use serde::{Deserialize, Serialize}; -use types::L1Network; use url::Url; +use zkstack_cli_common::Prompt; +use zkstack_cli_config::ChainConfig; +use zkstack_cli_types::L1Network; use crate::{ commands::chain::args::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/args/init/mod.rs b/zkstack_cli/crates/zkstack/src/commands/chain/args/init/mod.rs index 59810964d2c3..23e32306519c 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/args/init/mod.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/args/init/mod.rs @@ -1,9 +1,9 @@ use clap::Parser; -use common::{forge::ForgeScriptArgs, Prompt}; -use config::ChainConfig; use serde::{Deserialize, Serialize}; -use types::L1Network; use url::Url; +use zkstack_cli_common::{forge::ForgeScriptArgs, Prompt}; +use zkstack_cli_config::ChainConfig; +use zkstack_cli_types::L1Network; use crate::{ commands::chain::args::genesis::{GenesisArgs, GenesisArgsFinal}, @@ -58,7 +58,7 @@ impl InitArgs { true } else { self.deploy_paymaster.unwrap_or_else(|| { - common::PromptConfirm::new(MSG_DEPLOY_PAYMASTER_PROMPT) + zkstack_cli_common::PromptConfirm::new(MSG_DEPLOY_PAYMASTER_PROMPT) .default(true) .ask() }) diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/build_transactions.rs b/zkstack_cli/crates/zkstack/src/commands/chain/build_transactions.rs index d3953c656596..2e25d15b0fab 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/build_transactions.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/build_transactions.rs @@ -1,10 +1,10 @@ use anyhow::Context; -use common::{git, logger, spinner::Spinner}; -use config::{ - copy_configs, traits::SaveConfigWithBasePath, update_from_chain_config, EcosystemConfig, -}; use ethers::utils::hex::ToHex; use xshell::Shell; +use zkstack_cli_common::{git, logger, spinner::Spinner}; +use zkstack_cli_config::{ + copy_configs, traits::SaveConfigWithBasePath, update_from_chain_config, EcosystemConfig, +}; use crate::{ commands::chain::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/common.rs b/zkstack_cli/crates/zkstack/src/commands/chain/common.rs index 0c35b3ee4fe0..e14c2460afcd 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/common.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/common.rs @@ -1,6 +1,6 @@ -use common::spinner::Spinner; -use config::{ChainConfig, EcosystemConfig}; -use types::{BaseToken, L1Network, WalletCreation}; +use zkstack_cli_common::spinner::Spinner; +use zkstack_cli_config::{ChainConfig, EcosystemConfig}; +use zkstack_cli_types::{BaseToken, L1Network, WalletCreation}; use crate::{ consts::AMOUNT_FOR_DISTRIBUTION_TO_WALLETS, @@ -30,7 +30,7 @@ pub async fn distribute_eth( if let Some(setter) = chain_wallets.token_multiplier_setter { addresses.push(setter.address) } - common::ethereum::distribute_eth( + zkstack_cli_common::ethereum::distribute_eth( wallets.operator, addresses, l1_rpc_url, @@ -59,7 +59,7 @@ pub async fn mint_base_token( let addresses = vec![wallets.governor.address, chain_wallets.governor.address]; let amount = AMOUNT_FOR_DISTRIBUTION_TO_WALLETS * base_token.nominator as u128 / base_token.denominator as u128; - common::ethereum::mint_token( + zkstack_cli_common::ethereum::mint_token( wallets.governor, base_token.address, addresses, diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/convert_to_gateway.rs b/zkstack_cli/crates/zkstack/src/commands/chain/convert_to_gateway.rs index 3043f3a52837..0ac534382a26 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/convert_to_gateway.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/convert_to_gateway.rs @@ -1,11 +1,14 @@ /// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. use anyhow::Context; -use common::{ +use ethers::{abi::parse_abi, contract::BaseContract, types::Bytes, utils::hex}; +use lazy_static::lazy_static; +use xshell::Shell; +use zkstack_cli_common::{ config::global_config, forge::{Forge, ForgeScriptArgs}, wallets::Wallet, }; -use config::{ +use zkstack_cli_config::{ forge_interface::{ deploy_ecosystem::input::InitialDeploymentConfig, deploy_gateway_ctm::{input::DeployGatewayCTMInput, output::DeployGatewayCTMOutput}, @@ -15,9 +18,6 @@ use config::{ traits::{ReadConfig, SaveConfig, SaveConfigWithBasePath}, ChainConfig, EcosystemConfig, GenesisConfig, }; -use ethers::{abi::parse_abi, contract::BaseContract, types::Bytes, utils::hex}; -use lazy_static::lazy_static; -use xshell::Shell; use zksync_basic_types::H256; use zksync_config::configs::GatewayConfig; diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/create.rs b/zkstack_cli/crates/zkstack/src/commands/chain/create.rs index 367dddd95884..fa5ee1a59df7 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/create.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/create.rs @@ -1,13 +1,13 @@ use std::cell::OnceCell; use anyhow::Context; -use common::{logger, spinner::Spinner}; -use config::{ +use xshell::Shell; +use zkstack_cli_common::{logger, spinner::Spinner}; +use zkstack_cli_config::{ create_local_configs_dir, create_wallets, traits::{ReadConfigWithBasePath, SaveConfigWithBasePath}, ChainConfig, EcosystemConfig, GenesisConfig, }; -use xshell::Shell; use zksync_basic_types::L2ChainId; use crate::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/deploy_l2_contracts.rs b/zkstack_cli/crates/zkstack/src/commands/chain/deploy_l2_contracts.rs index 09732145d1db..d404b29b5a98 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/deploy_l2_contracts.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/deploy_l2_contracts.rs @@ -1,12 +1,13 @@ use std::path::Path; use anyhow::Context; -use common::{ +use xshell::Shell; +use zkstack_cli_common::{ contracts::build_l2_contracts, forge::{Forge, ForgeScriptArgs}, spinner::Spinner, }; -use config::{ +use zkstack_cli_config::{ forge_interface::{ deploy_l2_contracts::{ input::DeployL2ContractsInput, @@ -20,7 +21,6 @@ use config::{ traits::{ReadConfig, SaveConfig, SaveConfigWithBasePath}, ChainConfig, ContractsConfig, EcosystemConfig, }; -use xshell::Shell; use crate::{ messages::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/deploy_paymaster.rs b/zkstack_cli/crates/zkstack/src/commands/chain/deploy_paymaster.rs index c6b48ca87856..1c103ea29910 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/deploy_paymaster.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/deploy_paymaster.rs @@ -1,6 +1,7 @@ use anyhow::Context; -use common::forge::{Forge, ForgeScriptArgs}; -use config::{ +use xshell::Shell; +use zkstack_cli_common::forge::{Forge, ForgeScriptArgs}; +use zkstack_cli_config::{ forge_interface::{ paymaster::{DeployPaymasterInput, DeployPaymasterOutput}, script_params::DEPLOY_PAYMASTER_SCRIPT_PARAMS, @@ -8,7 +9,6 @@ use config::{ traits::{ReadConfig, SaveConfig, SaveConfigWithBasePath}, ChainConfig, ContractsConfig, EcosystemConfig, }; -use xshell::Shell; use crate::{ messages::{MSG_CHAIN_NOT_INITIALIZED, MSG_L1_SECRETS_MUST_BE_PRESENTED}, diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/enable_evm_emulator.rs b/zkstack_cli/crates/zkstack/src/commands/chain/enable_evm_emulator.rs index a6bbd2c9dc70..e15cdbacf103 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/enable_evm_emulator.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/enable_evm_emulator.rs @@ -1,7 +1,7 @@ use anyhow::Context; -use common::{forge::ForgeScriptArgs, logger}; -use config::{traits::ReadConfigWithBasePath, EcosystemConfig, GenesisConfig}; use xshell::Shell; +use zkstack_cli_common::{forge::ForgeScriptArgs, logger}; +use zkstack_cli_config::{traits::ReadConfigWithBasePath, EcosystemConfig, GenesisConfig}; use crate::{ enable_evm_emulator::enable_evm_emulator, diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/gateway_upgrade.rs b/zkstack_cli/crates/zkstack/src/commands/chain/gateway_upgrade.rs index 8153bcfb0b7e..ed84df9d13c4 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/gateway_upgrade.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/gateway_upgrade.rs @@ -1,11 +1,20 @@ /// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. use anyhow::Context; use clap::{Parser, ValueEnum}; -use common::{ +use ethers::{ + abi::{encode, parse_abi}, + contract::BaseContract, + utils::hex, +}; +use lazy_static::lazy_static; +use serde::{Deserialize, Serialize}; +use strum::EnumIter; +use xshell::Shell; +use zkstack_cli_common::{ config::global_config, forge::{Forge, ForgeScriptArgs}, }; -use config::{ +use zkstack_cli_config::{ forge_interface::{ gateway_chain_upgrade::{ input::GatewayChainUpgradeInput, output::GatewayChainUpgradeOutput, @@ -16,16 +25,7 @@ use config::{ traits::{ReadConfig, ReadConfigWithBasePath, SaveConfig, SaveConfigWithBasePath}, ChainConfig, EcosystemConfig, }; -use ethers::{ - abi::{encode, parse_abi}, - contract::BaseContract, - utils::hex, -}; -use lazy_static::lazy_static; -use serde::{Deserialize, Serialize}; -use strum::EnumIter; -use types::L1BatchCommitmentMode; -use xshell::Shell; +use zkstack_cli_types::L1BatchCommitmentMode; use zksync_basic_types::{H256, U256}; use zksync_types::{web3::keccak256, Address, L2_NATIVE_TOKEN_VAULT_ADDRESS}; diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/genesis/database.rs b/zkstack_cli/crates/zkstack/src/commands/chain/genesis/database.rs index edf480946be1..fe12bc017a45 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/genesis/database.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/genesis/database.rs @@ -1,17 +1,17 @@ use std::path::PathBuf; use anyhow::Context; -use common::{ +use xshell::Shell; +use zkstack_cli_common::{ config::global_config, db::{drop_db_if_exists, init_db, migrate_db, DatabaseConfig}, logger, }; -use config::{ +use zkstack_cli_config::{ override_config, set_file_artifacts, set_rocks_db_config, set_server_database, traits::SaveConfigWithBasePath, ChainConfig, EcosystemConfig, FileArtifacts, }; -use types::ProverMode; -use xshell::Shell; +use zkstack_cli_types::ProverMode; use zksync_basic_types::commitment::L1BatchCommitmentMode; use crate::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/genesis/mod.rs b/zkstack_cli/crates/zkstack/src/commands/chain/genesis/mod.rs index c1cc03174aeb..5cb289d32609 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/genesis/mod.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/genesis/mod.rs @@ -1,8 +1,8 @@ use anyhow::Context; use clap::{command, Parser, Subcommand}; -use common::{logger, spinner::Spinner}; -use config::{ChainConfig, EcosystemConfig}; use xshell::Shell; +use zkstack_cli_common::{logger, spinner::Spinner}; +use zkstack_cli_config::{ChainConfig, EcosystemConfig}; use crate::{ commands::chain::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/genesis/server.rs b/zkstack_cli/crates/zkstack/src/commands/chain/genesis/server.rs index 090792e8007a..9a52595c978c 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/genesis/server.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/genesis/server.rs @@ -1,14 +1,14 @@ use anyhow::Context; -use common::{ +use xshell::Shell; +use zkstack_cli_common::{ logger, server::{Server, ServerMode}, spinner::Spinner, }; -use config::{ +use zkstack_cli_config::{ traits::FileConfigWithDefaultName, ChainConfig, ContractsConfig, EcosystemConfig, GeneralConfig, GenesisConfig, SecretsConfig, WalletsConfig, }; -use xshell::Shell; use crate::messages::{ MSG_CHAIN_NOT_INITIALIZED, MSG_FAILED_TO_RUN_SERVER_ERR, MSG_GENESIS_COMPLETED, diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/init/configs.rs b/zkstack_cli/crates/zkstack/src/commands/chain/init/configs.rs index 31c5c681e7d3..5a27f903a72f 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/init/configs.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/init/configs.rs @@ -1,11 +1,11 @@ use anyhow::Context; -use common::logger; -use config::{ +use ethers::types::Address; +use xshell::Shell; +use zkstack_cli_common::logger; +use zkstack_cli_config::{ copy_configs, set_l1_rpc_url, traits::SaveConfigWithBasePath, update_from_chain_config, ChainConfig, ContractsConfig, EcosystemConfig, }; -use ethers::types::Address; -use xshell::Shell; use crate::{ commands::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/init/mod.rs b/zkstack_cli/crates/zkstack/src/commands/chain/init/mod.rs index a4c21b5bc5d1..4100fee22d89 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/init/mod.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/init/mod.rs @@ -1,9 +1,9 @@ use anyhow::Context; use clap::{command, Parser, Subcommand}; -use common::{git, logger, spinner::Spinner}; -use config::{traits::SaveConfigWithBasePath, ChainConfig, EcosystemConfig}; -use types::BaseToken; use xshell::Shell; +use zkstack_cli_common::{git, logger, spinner::Spinner}; +use zkstack_cli_config::{traits::SaveConfigWithBasePath, ChainConfig, EcosystemConfig}; +use zkstack_cli_types::BaseToken; use crate::{ accept_ownership::accept_admin, diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/migrate_from_gateway.rs b/zkstack_cli/crates/zkstack/src/commands/chain/migrate_from_gateway.rs index 71521e62c3e6..573dcf56345f 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/migrate_from_gateway.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/migrate_from_gateway.rs @@ -1,13 +1,23 @@ /// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. use anyhow::Context; use clap::Parser; -use common::{ +use ethers::{ + abi::parse_abi, + contract::BaseContract, + providers::{Http, Middleware, Provider}, + types::Bytes, + utils::hex, +}; +use lazy_static::lazy_static; +use serde::{Deserialize, Serialize}; +use xshell::Shell; +use zkstack_cli_common::{ config::global_config, forge::{Forge, ForgeScriptArgs}, wallets::Wallet, zks_provider::ZKSProvider, }; -use config::{ +use zkstack_cli_config::{ forge_interface::{ gateway_preparation::{input::GatewayPreparationConfig, output::GatewayPreparationOutput}, script_params::GATEWAY_PREPARATION, @@ -15,17 +25,7 @@ use config::{ traits::{ReadConfig, SaveConfig, SaveConfigWithBasePath}, EcosystemConfig, }; -use ethers::{ - abi::parse_abi, - contract::BaseContract, - providers::{Http, Middleware, Provider}, - types::Bytes, - utils::hex, -}; -use lazy_static::lazy_static; -use serde::{Deserialize, Serialize}; -use types::L1BatchCommitmentMode; -use xshell::Shell; +use zkstack_cli_types::L1BatchCommitmentMode; use zksync_basic_types::{ pubdata_da::PubdataSendingMode, settlement::SettlementMode, H256, U256, U64, }; diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/migrate_to_gateway.rs b/zkstack_cli/crates/zkstack/src/commands/chain/migrate_to_gateway.rs index e5202c570183..f6bb25613039 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/migrate_to_gateway.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/migrate_to_gateway.rs @@ -1,11 +1,21 @@ use anyhow::Context; use clap::Parser; -use common::{ +use ethers::{ + abi::parse_abi, + contract::BaseContract, + providers::{Http, Middleware, Provider}, + types::Bytes, + utils::hex, +}; +use lazy_static::lazy_static; +use serde::{Deserialize, Serialize}; +use xshell::Shell; +use zkstack_cli_common::{ config::global_config, forge::{Forge, ForgeScriptArgs}, wallets::Wallet, }; -use config::{ +use zkstack_cli_config::{ forge_interface::{ gateway_preparation::{input::GatewayPreparationConfig, output::GatewayPreparationOutput}, script_params::GATEWAY_PREPARATION, @@ -13,17 +23,7 @@ use config::{ traits::{ReadConfig, SaveConfig, SaveConfigWithBasePath}, EcosystemConfig, }; -use ethers::{ - abi::parse_abi, - contract::BaseContract, - providers::{Http, Middleware, Provider}, - types::Bytes, - utils::hex, -}; -use lazy_static::lazy_static; -use serde::{Deserialize, Serialize}; -use types::L1BatchCommitmentMode; -use xshell::Shell; +use zkstack_cli_types::L1BatchCommitmentMode; use zksync_basic_types::{ pubdata_da::PubdataSendingMode, settlement::SettlementMode, Address, H256, U256, U64, }; diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/mod.rs b/zkstack_cli/crates/zkstack/src/commands/chain/mod.rs index 474f1c779016..7bbe8acf99f3 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/mod.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/mod.rs @@ -1,4 +1,4 @@ -use ::common::forge::ForgeScriptArgs; +use ::zkstack_cli_common::forge::ForgeScriptArgs; use args::build_transactions::BuildTransactionsArgs; pub(crate) use args::create::ChainCreateArgsFinal; use clap::{command, Subcommand}; diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/register_chain.rs b/zkstack_cli/crates/zkstack/src/commands/chain/register_chain.rs index 6269b0af0ea9..626d25438385 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/register_chain.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/register_chain.rs @@ -1,10 +1,11 @@ use anyhow::Context; -use common::{ +use xshell::Shell; +use zkstack_cli_common::{ forge::{Forge, ForgeScriptArgs}, logger, spinner::Spinner, }; -use config::{ +use zkstack_cli_config::{ forge_interface::{ register_chain::{input::RegisterChainL1Config, output::RegisterChainOutput}, script_params::REGISTER_CHAIN_SCRIPT_PARAMS, @@ -12,7 +13,6 @@ use config::{ traits::{ReadConfig, SaveConfig, SaveConfigWithBasePath}, ChainConfig, ContractsConfig, EcosystemConfig, }; -use xshell::Shell; use crate::{ messages::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/set_token_multiplier_setter.rs b/zkstack_cli/crates/zkstack/src/commands/chain/set_token_multiplier_setter.rs index bff3cfe467b5..69a823f8f852 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/set_token_multiplier_setter.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/set_token_multiplier_setter.rs @@ -1,14 +1,16 @@ use anyhow::Context; -use common::{ +use ethers::{abi::parse_abi, contract::BaseContract, utils::hex}; +use lazy_static::lazy_static; +use xshell::Shell; +use zkstack_cli_common::{ forge::{Forge, ForgeScript, ForgeScriptArgs}, logger, spinner::Spinner, wallets::Wallet, }; -use config::{forge_interface::script_params::ACCEPT_GOVERNANCE_SCRIPT_PARAMS, EcosystemConfig}; -use ethers::{abi::parse_abi, contract::BaseContract, utils::hex}; -use lazy_static::lazy_static; -use xshell::Shell; +use zkstack_cli_config::{ + forge_interface::script_params::ACCEPT_GOVERNANCE_SCRIPT_PARAMS, EcosystemConfig, +}; use zksync_basic_types::Address; use crate::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/setup_legacy_bridge.rs b/zkstack_cli/crates/zkstack/src/commands/chain/setup_legacy_bridge.rs index 8973fccced86..631dffffac41 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/setup_legacy_bridge.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/setup_legacy_bridge.rs @@ -1,16 +1,16 @@ use anyhow::Context; -use common::{ +use xshell::Shell; +use zkstack_cli_common::{ forge::{Forge, ForgeScriptArgs}, spinner::Spinner, }; -use config::{ +use zkstack_cli_config::{ forge_interface::{ script_params::SETUP_LEGACY_BRIDGE, setup_legacy_bridge::SetupLegacyBridgeInput, }, traits::SaveConfig, ChainConfig, ContractsConfig, EcosystemConfig, }; -use xshell::Shell; use crate::{ messages::{MSG_DEPLOYING_PAYMASTER, MSG_L1_SECRETS_MUST_BE_PRESENTED}, diff --git a/zkstack_cli/crates/zkstack/src/commands/consensus/mod.rs b/zkstack_cli/crates/zkstack/src/commands/consensus/mod.rs index 7a998efedbf2..ad64207b481c 100644 --- a/zkstack_cli/crates/zkstack/src/commands/consensus/mod.rs +++ b/zkstack_cli/crates/zkstack/src/commands/consensus/mod.rs @@ -3,8 +3,6 @@ use std::{borrow::Borrow, collections::HashMap, path::PathBuf, sync::Arc}; /// Consensus registry contract operations. /// Includes code duplicated from `zksync_node_consensus::registry::abi`. use anyhow::Context as _; -use common::{config::global_config, logger, wallets::Wallet}; -use config::EcosystemConfig; use conv::*; use ethers::{ abi::Detokenize, @@ -16,6 +14,8 @@ use ethers::{ }; use tokio::time::MissedTickBehavior; use xshell::Shell; +use zkstack_cli_common::{config::global_config, logger, wallets::Wallet}; +use zkstack_cli_config::EcosystemConfig; use zksync_consensus_crypto::ByteFmt; use zksync_consensus_roles::{attester, validator}; @@ -144,10 +144,10 @@ fn print_attesters(committee: &attester::Committee) { } struct Setup { - chain: config::ChainConfig, - contracts: config::ContractsConfig, - general: config::GeneralConfig, - genesis: config::GenesisConfig, + chain: zkstack_cli_config::ChainConfig, + contracts: zkstack_cli_config::ContractsConfig, + general: zkstack_cli_config::GeneralConfig, + genesis: zkstack_cli_config::GenesisConfig, } impl Setup { diff --git a/zkstack_cli/crates/zkstack/src/commands/containers.rs b/zkstack_cli/crates/zkstack/src/commands/containers.rs index 8367289bd67f..8469365a295f 100644 --- a/zkstack_cli/crates/zkstack/src/commands/containers.rs +++ b/zkstack_cli/crates/zkstack/src/commands/containers.rs @@ -1,9 +1,9 @@ use std::path::PathBuf; use anyhow::{anyhow, Context}; -use common::{docker, logger, spinner::Spinner}; -use config::{EcosystemConfig, DOCKER_COMPOSE_FILE, ERA_OBSERVABILITY_COMPOSE_FILE}; use xshell::Shell; +use zkstack_cli_common::{docker, logger, spinner::Spinner}; +use zkstack_cli_config::{EcosystemConfig, DOCKER_COMPOSE_FILE, ERA_OBSERVABILITY_COMPOSE_FILE}; use super::args::ContainersArgs; use crate::{ @@ -46,7 +46,10 @@ pub fn initialize_docker(shell: &Shell, ecosystem: &EcosystemConfig) -> anyhow:: fn start_container(shell: &Shell, compose_file: &str, retry_msg: &str) -> anyhow::Result<()> { while let Err(err) = docker::up(shell, compose_file, true) { logger::error(err.to_string()); - if !common::PromptConfirm::new(retry_msg).default(true).ask() { + if !zkstack_cli_common::PromptConfirm::new(retry_msg) + .default(true) + .ask() + { return Err(err); } } diff --git a/zkstack_cli/crates/zkstack/src/commands/contract_verifier/args/init.rs b/zkstack_cli/crates/zkstack/src/commands/contract_verifier/args/init.rs index 7ba7d3cb40cf..a3aadfbce4ae 100644 --- a/zkstack_cli/crates/zkstack/src/commands/contract_verifier/args/init.rs +++ b/zkstack_cli/crates/zkstack/src/commands/contract_verifier/args/init.rs @@ -1,7 +1,7 @@ use anyhow::Context; use clap::Parser; -use common::PromptSelect; use xshell::Shell; +use zkstack_cli_common::PromptSelect; use super::releases::{get_releases_with_arch, Arch, Version}; use crate::messages::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/contract_verifier/args/releases.rs b/zkstack_cli/crates/zkstack/src/commands/contract_verifier/args/releases.rs index ab169220f299..a8199372fc2c 100644 --- a/zkstack_cli/crates/zkstack/src/commands/contract_verifier/args/releases.rs +++ b/zkstack_cli/crates/zkstack/src/commands/contract_verifier/args/releases.rs @@ -1,8 +1,8 @@ use std::str::FromStr; -use common::spinner::Spinner; use serde::Deserialize; use xshell::Shell; +use zkstack_cli_common::spinner::Spinner; use crate::messages::{MSG_INVALID_ARCH_ERR, MSG_NO_RELEASES_FOUND_ERR}; diff --git a/zkstack_cli/crates/zkstack/src/commands/contract_verifier/build.rs b/zkstack_cli/crates/zkstack/src/commands/contract_verifier/build.rs index 0ba72f6b2257..384e9a389265 100644 --- a/zkstack_cli/crates/zkstack/src/commands/contract_verifier/build.rs +++ b/zkstack_cli/crates/zkstack/src/commands/contract_verifier/build.rs @@ -1,7 +1,7 @@ use anyhow::Context; -use common::{cmd::Cmd, logger}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, logger}; +use zkstack_cli_config::EcosystemConfig; use crate::messages::{ MSG_BUILDING_CONTRACT_VERIFIER, MSG_CHAIN_NOT_FOUND_ERR, diff --git a/zkstack_cli/crates/zkstack/src/commands/contract_verifier/init.rs b/zkstack_cli/crates/zkstack/src/commands/contract_verifier/init.rs index b173ad9bbb7f..56f02745050c 100644 --- a/zkstack_cli/crates/zkstack/src/commands/contract_verifier/init.rs +++ b/zkstack_cli/crates/zkstack/src/commands/contract_verifier/init.rs @@ -1,8 +1,8 @@ use std::path::{Path, PathBuf}; -use common::{cmd::Cmd, logger, spinner::Spinner}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, logger, spinner::Spinner}; +use zkstack_cli_config::EcosystemConfig; use super::args::{init::InitContractVerifierArgs, releases::Version}; use crate::messages::{msg_binary_already_exists, msg_downloading_binary_spinner}; diff --git a/zkstack_cli/crates/zkstack/src/commands/contract_verifier/run.rs b/zkstack_cli/crates/zkstack/src/commands/contract_verifier/run.rs index ebc33840bdea..fd872aa67248 100644 --- a/zkstack_cli/crates/zkstack/src/commands/contract_verifier/run.rs +++ b/zkstack_cli/crates/zkstack/src/commands/contract_verifier/run.rs @@ -1,7 +1,7 @@ use anyhow::Context; -use common::{cmd::Cmd, logger}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, logger}; +use zkstack_cli_config::EcosystemConfig; use crate::messages::{ MSG_CHAIN_NOT_FOUND_ERR, MSG_FAILED_TO_RUN_CONTRACT_VERIFIER_ERR, MSG_RUNNING_CONTRACT_VERIFIER, diff --git a/zkstack_cli/crates/zkstack/src/commands/contract_verifier/wait.rs b/zkstack_cli/crates/zkstack/src/commands/contract_verifier/wait.rs index 011c888d3041..0b844df61f4c 100644 --- a/zkstack_cli/crates/zkstack/src/commands/contract_verifier/wait.rs +++ b/zkstack_cli/crates/zkstack/src/commands/contract_verifier/wait.rs @@ -1,7 +1,7 @@ use anyhow::Context as _; -use common::{config::global_config, logger}; -use config::EcosystemConfig; use xshell::Shell; +use zkstack_cli_common::{config::global_config, logger}; +use zkstack_cli_config::EcosystemConfig; use crate::{commands::args::WaitArgs, messages::MSG_CHAIN_NOT_FOUND_ERR}; diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/clean/mod.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/clean/mod.rs index 06dff541f94e..b45272cc97a8 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/clean/mod.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/clean/mod.rs @@ -1,8 +1,8 @@ use anyhow::Context; use clap::Subcommand; -use common::{docker, logger}; -use config::{EcosystemConfig, DOCKER_COMPOSE_FILE}; use xshell::Shell; +use zkstack_cli_common::{docker, logger}; +use zkstack_cli_config::{EcosystemConfig, DOCKER_COMPOSE_FILE}; use crate::commands::dev::messages::{ MSG_CONTRACTS_CLEANING, MSG_CONTRACTS_CLEANING_FINISHED, MSG_DOCKER_COMPOSE_DOWN, diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/config_writer.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/config_writer.rs index 70238ed15f32..01a521334383 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/config_writer.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/config_writer.rs @@ -1,8 +1,8 @@ use anyhow::Context; use clap::Parser; -use common::{logger, Prompt}; -use config::{override_config, EcosystemConfig}; use xshell::Shell; +use zkstack_cli_common::{logger, Prompt}; +use zkstack_cli_config::{override_config, EcosystemConfig}; use crate::commands::dev::messages::{ msg_overriding_config, MSG_CHAIN_NOT_FOUND_ERR, MSG_OVERRIDE_CONFIG_PATH_HELP, diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/contracts.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/contracts.rs index 8e0384cbca99..9a9aeeb30305 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/contracts.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/contracts.rs @@ -1,13 +1,13 @@ use std::path::PathBuf; use clap::Parser; -use common::{ +use xshell::Shell; +use zkstack_cli_common::{ contracts::{build_l1_contracts, build_l2_contracts, build_system_contracts}, logger, spinner::Spinner, }; -use config::EcosystemConfig; -use xshell::Shell; +use zkstack_cli_config::EcosystemConfig; use crate::commands::dev::messages::{ MSG_BUILDING_CONTRACTS, MSG_BUILDING_CONTRACTS_SUCCESS, MSG_BUILDING_L1_CONTRACTS_SPINNER, diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/args/new_migration.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/args/new_migration.rs index b91b048be784..74e40ab53303 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/args/new_migration.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/args/new_migration.rs @@ -1,6 +1,6 @@ use clap::{Parser, ValueEnum}; -use common::{Prompt, PromptSelect}; use strum::{Display, EnumIter, IntoEnumIterator}; +use zkstack_cli_common::{Prompt, PromptSelect}; use crate::commands::dev::messages::{ MSG_DATABASE_NEW_MIGRATION_DATABASE_HELP, MSG_DATABASE_NEW_MIGRATION_DB_PROMPT, diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/check_sqlx_data.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/check_sqlx_data.rs index 990fca78641f..abead3fe00d6 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/check_sqlx_data.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/check_sqlx_data.rs @@ -1,8 +1,8 @@ use std::path::Path; -use common::{cmd::Cmd, logger, spinner::Spinner}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, logger, spinner::Spinner}; +use zkstack_cli_config::EcosystemConfig; use super::args::DatabaseCommonArgs; use crate::commands::dev::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/drop.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/drop.rs index a5578d41f77a..e46a434cec06 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/drop.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/drop.rs @@ -1,9 +1,9 @@ -use common::{ +use xshell::Shell; +use zkstack_cli_common::{ db::{drop_db_if_exists, DatabaseConfig}, logger, spinner::Spinner, }; -use xshell::Shell; use super::args::DatabaseCommonArgs; use crate::commands::dev::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/migrate.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/migrate.rs index fd22f769742e..e67d4a5bf29d 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/migrate.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/migrate.rs @@ -1,8 +1,8 @@ use std::path::Path; -use common::{cmd::Cmd, logger, spinner::Spinner}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, logger, spinner::Spinner}; +use zkstack_cli_config::EcosystemConfig; use super::args::DatabaseCommonArgs; use crate::commands::dev::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/new_migration.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/new_migration.rs index 2d9fa1030538..655a841e060a 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/new_migration.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/new_migration.rs @@ -1,8 +1,8 @@ use std::path::Path; -use common::{cmd::Cmd, logger, spinner::Spinner}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, logger, spinner::Spinner}; +use zkstack_cli_config::EcosystemConfig; use super::args::new_migration::{DatabaseNewMigrationArgs, SelectedDatabase}; use crate::commands::dev::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/prepare.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/prepare.rs index 288a68452fd5..82c9ed2e338b 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/prepare.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/prepare.rs @@ -1,8 +1,8 @@ use std::path::Path; -use common::{cmd::Cmd, logger, spinner::Spinner}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, logger, spinner::Spinner}; +use zkstack_cli_config::EcosystemConfig; use super::args::DatabaseCommonArgs; use crate::commands::dev::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/reset.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/reset.rs index 55d5ab1cbfcb..4a9ec022d723 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/reset.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/reset.rs @@ -1,8 +1,8 @@ use std::path::Path; -use common::logger; -use config::EcosystemConfig; use xshell::Shell; +use zkstack_cli_common::logger; +use zkstack_cli_config::EcosystemConfig; use super::{args::DatabaseCommonArgs, drop::drop_database, setup::setup_database}; use crate::commands::dev::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/setup.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/setup.rs index 74ade66ba481..4eba9b615fc0 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/setup.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/setup.rs @@ -1,8 +1,8 @@ use std::path::Path; -use common::{cmd::Cmd, logger, spinner::Spinner}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, logger, spinner::Spinner}; +use zkstack_cli_config::EcosystemConfig; use super::args::DatabaseCommonArgs; use crate::commands::dev::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/fmt.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/fmt.rs index 569d2a61294e..af6985b006d2 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/fmt.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/fmt.rs @@ -1,9 +1,9 @@ use std::path::PathBuf; use clap::Parser; -use common::{cmd::Cmd, logger, spinner::Spinner}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, logger, spinner::Spinner}; +use zkstack_cli_config::EcosystemConfig; use super::sql_fmt::format_sql; use crate::commands::dev::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/genesis.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/genesis.rs index 683ffe199161..8e7a3973e037 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/genesis.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/genesis.rs @@ -1,7 +1,7 @@ use anyhow::Context; -use common::{cmd::Cmd, spinner::Spinner}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, spinner::Spinner}; +use zkstack_cli_config::EcosystemConfig; use crate::{ commands::dev::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint.rs index fa17ef518901..6b919b8cdb9d 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint.rs @@ -6,9 +6,9 @@ use std::{ use anyhow::{bail, Context}; use clap::Parser; -use common::{cmd::Cmd, logger, spinner::Spinner}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, logger, spinner::Spinner}; +use zkstack_cli_config::EcosystemConfig; use crate::commands::{ autocomplete::{autocomplete_file_name, generate_completions}, diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/args/insert_batch.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/args/insert_batch.rs index e837bbe9eb86..355a750b9e09 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/args/insert_batch.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/args/insert_batch.rs @@ -19,7 +19,7 @@ pub struct InsertBatchArgsFinal { impl InsertBatchArgs { pub(crate) fn fill_values_with_prompts(self, era_version: String) -> InsertBatchArgsFinal { let number = self.number.unwrap_or_else(|| { - common::Prompt::new("Enter the number of the batch to insert").ask() + zkstack_cli_common::Prompt::new("Enter the number of the batch to insert").ask() }); if self.default { @@ -30,7 +30,7 @@ impl InsertBatchArgs { } let version = self.version.unwrap_or_else(|| { - common::Prompt::new("Enter the version of the batch to insert") + zkstack_cli_common::Prompt::new("Enter the version of the batch to insert") .default(&era_version) .ask() }); diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/args/insert_version.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/args/insert_version.rs index 7af98c4a7a43..72a6cd14bcba 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/args/insert_version.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/args/insert_version.rs @@ -35,21 +35,23 @@ impl InsertVersionArgs { } let version = self.version.unwrap_or_else(|| { - common::Prompt::new("Enter the version of the protocol to insert") + zkstack_cli_common::Prompt::new("Enter the version of the protocol to insert") .default(&era_version) .ask() }); let snark_wrapper = self.snark_wrapper.unwrap_or_else(|| { - common::Prompt::new("Enter the snark wrapper of the protocol to insert") + zkstack_cli_common::Prompt::new("Enter the snark wrapper of the protocol to insert") .default(&snark_wrapper) .ask() }); let fflonk_snark_wrapper = self.fflonk_snark_wrapper.unwrap_or_else(|| { - common::Prompt::new("Enter the fflonk snark wrapper of the protocol to insert") - .default(&fflonk_snark_wrapper) - .ask() + zkstack_cli_common::Prompt::new( + "Enter the fflonk snark wrapper of the protocol to insert", + ) + .default(&fflonk_snark_wrapper) + .ask() }); InsertVersionArgsFinal { diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/info.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/info.rs index baf0e6f881cd..44d80d48d0ce 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/info.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/info.rs @@ -4,9 +4,9 @@ use std::{ }; use anyhow::Context as _; -use common::logger; -use config::{ChainConfig, EcosystemConfig}; use xshell::{cmd, Shell}; +use zkstack_cli_common::logger; +use zkstack_cli_config::{ChainConfig, EcosystemConfig}; use crate::commands::dev::messages::MSG_CHAIN_NOT_FOUND_ERR; diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/insert_batch.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/insert_batch.rs index 0e0c0ba33af4..60c4dd3b88c1 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/insert_batch.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/insert_batch.rs @@ -1,6 +1,6 @@ -use common::{check_prerequisites, cmd::Cmd, logger, PROVER_CLI_PREREQUISITE}; -use config::{get_link_to_prover, EcosystemConfig}; use xshell::{cmd, Shell}; +use zkstack_cli_common::{check_prerequisites, cmd::Cmd, logger, PROVER_CLI_PREREQUISITE}; +use zkstack_cli_config::{get_link_to_prover, EcosystemConfig}; use crate::commands::dev::{ commands::prover::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/insert_version.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/insert_version.rs index 86cd73c926b2..18c182ca9057 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/insert_version.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/prover/insert_version.rs @@ -1,6 +1,6 @@ -use common::{check_prerequisites, cmd::Cmd, logger, PROVER_CLI_PREREQUISITE}; -use config::{get_link_to_prover, EcosystemConfig}; use xshell::{cmd, Shell}; +use zkstack_cli_common::{check_prerequisites, cmd::Cmd, logger, PROVER_CLI_PREREQUISITE}; +use zkstack_cli_config::{get_link_to_prover, EcosystemConfig}; use crate::commands::dev::{ commands::prover::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/send_transactions/args/mod.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/send_transactions/args/mod.rs index 03d9ec9b7360..a7bb415fd4da 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/send_transactions/args/mod.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/send_transactions/args/mod.rs @@ -1,8 +1,8 @@ use std::path::PathBuf; use clap::Parser; -use common::Prompt; use url::Url; +use zkstack_cli_common::Prompt; use crate::commands::dev::{ defaults::LOCAL_RPC_URL, diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/send_transactions/mod.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/send_transactions/mod.rs index 2f54579ade9e..0607747c8a1c 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/send_transactions/mod.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/send_transactions/mod.rs @@ -9,12 +9,12 @@ use std::{ use anyhow::Context; use args::SendTransactionsArgs; use chrono::Local; -use common::{ethereum::create_ethers_client, logger}; -use config::EcosystemConfig; use ethers::{abi::Bytes, providers::Middleware, types::TransactionRequest, utils::hex}; use serde::Deserialize; use tokio::time::sleep; use xshell::Shell; +use zkstack_cli_common::{ethereum::create_ethers_client, logger}; +use zkstack_cli_config::EcosystemConfig; use zksync_basic_types::{H160, U256}; use crate::commands::dev::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/snapshot.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/snapshot.rs index 8e4c7183cb55..dcc41af228be 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/snapshot.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/snapshot.rs @@ -1,8 +1,8 @@ use anyhow::Context; use clap::Subcommand; -use common::{cmd::Cmd, logger}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, logger}; +use zkstack_cli_config::EcosystemConfig; use crate::commands::dev::messages::{MSG_CHAIN_NOT_FOUND_ERR, MSG_RUNNING_SNAPSHOT_CREATOR}; diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/sql_fmt.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/sql_fmt.rs index 0f7ce061ce18..388d733a5f3d 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/sql_fmt.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/sql_fmt.rs @@ -1,9 +1,9 @@ use std::mem::take; use anyhow::{bail, Result}; -use common::spinner::Spinner; use sqruff_lib::{api::simple::get_simple_config, core::linter::core::Linter}; use xshell::Shell; +use zkstack_cli_common::spinner::Spinner; use super::lint_utils::{get_unignored_files, IgnoredData, Target}; use crate::commands::dev::messages::{msg_file_is_not_formatted, MSG_RUNNING_SQL_FMT_SPINNER}; diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/status/args.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/status/args.rs index 5ac52bf854a6..1cc65f194cdc 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/status/args.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/status/args.rs @@ -1,7 +1,7 @@ use anyhow::Context; use clap::Parser; -use config::EcosystemConfig; use xshell::Shell; +use zkstack_cli_config::EcosystemConfig; use crate::{ commands::dev::messages::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/status/mod.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/status/mod.rs index 8687fcb04763..7f2db7533655 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/status/mod.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/status/mod.rs @@ -2,12 +2,12 @@ use std::collections::HashMap; use anyhow::Context; use args::{StatusArgs, StatusSubcommands}; -use common::logger; use draw::{bordered_boxes, format_port_info}; use serde::Deserialize; use serde_json::Value; use utils::deslugify; use xshell::Shell; +use zkstack_cli_common::logger; use crate::{ commands::dev::messages::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/build.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/build.rs index dea6a46bbef6..7465d7ca851a 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/build.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/build.rs @@ -1,5 +1,5 @@ -use config::EcosystemConfig; use xshell::Shell; +use zkstack_cli_config::EcosystemConfig; use super::utils::{build_contracts, install_and_build_dependencies}; diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/db.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/db.rs index 19f6307019b8..89adb3febe8a 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/db.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/db.rs @@ -1,7 +1,7 @@ use std::path::Path; -use common::{cmd::Cmd, db::wait_for_db, logger}; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, db::wait_for_db, logger}; use crate::commands::dev::{commands::database, dals::Dal, messages::MSG_RESETTING_TEST_DATABASES}; diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/fees.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/fees.rs index e58a70e6b7cb..c4d08c6ef9d7 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/fees.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/fees.rs @@ -1,9 +1,9 @@ use std::path::PathBuf; use anyhow::Context; -use common::{cmd::Cmd, config::global_config, logger}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, config::global_config, logger}; +use zkstack_cli_config::EcosystemConfig; use super::{ args::fees::FeesArgs, diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/integration.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/integration.rs index 8e9e421c2f4e..7a140644dcec 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/integration.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/integration.rs @@ -1,9 +1,9 @@ use std::path::PathBuf; use anyhow::Context; -use common::{cmd::Cmd, config::global_config, logger}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, config::global_config, logger}; +use zkstack_cli_config::EcosystemConfig; use super::{ args::integration::IntegrationArgs, diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/l1_contracts.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/l1_contracts.rs index 7d163daed671..439926a74b80 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/l1_contracts.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/l1_contracts.rs @@ -1,6 +1,6 @@ -use common::{cmd::Cmd, logger}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, logger}; +use zkstack_cli_config::EcosystemConfig; use crate::commands::dev::messages::MSG_L1_CONTRACTS_TEST_SUCCESS; diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/loadtest.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/loadtest.rs index 72a8f97ff97d..385335890c75 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/loadtest.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/loadtest.rs @@ -1,7 +1,7 @@ use anyhow::Context; -use common::{cmd::Cmd, config::global_config, logger}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, config::global_config, logger}; +use zkstack_cli_config::EcosystemConfig; use crate::commands::dev::messages::MSG_CHAIN_NOT_FOUND_ERR; diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/prover.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/prover.rs index 200baf57215c..b74494c9d915 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/prover.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/prover.rs @@ -1,9 +1,9 @@ use std::str::FromStr; -use common::{cmd::Cmd, logger}; -use config::EcosystemConfig; use url::Url; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, logger}; +use zkstack_cli_config::EcosystemConfig; use crate::commands::dev::{ commands::test::db::reset_test_databases, diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/recovery.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/recovery.rs index ae889969fd2c..e62d6cf9cdc5 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/recovery.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/recovery.rs @@ -1,9 +1,9 @@ use std::path::PathBuf; use anyhow::Context; -use common::{cmd::Cmd, logger, server::Server, spinner::Spinner}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, logger, server::Server, spinner::Spinner}; +use zkstack_cli_config::EcosystemConfig; use super::{ args::recovery::RecoveryArgs, diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/revert.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/revert.rs index dc95c88db205..ae8b5df33222 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/revert.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/revert.rs @@ -1,9 +1,9 @@ use std::path::PathBuf; use anyhow::Context; -use common::{cmd::Cmd, logger, spinner::Spinner}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, logger, spinner::Spinner}; +use zkstack_cli_config::EcosystemConfig; use super::{ args::revert::RevertArgs, diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/rust.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/rust.rs index 8c0c707f6a2e..c415eb7407d2 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/rust.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/rust.rs @@ -1,10 +1,10 @@ use std::str::FromStr; use anyhow::Context; -use common::{cmd::Cmd, logger}; -use config::EcosystemConfig; use url::Url; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, logger}; +use zkstack_cli_config::EcosystemConfig; use super::args::rust::RustArgs; use crate::commands::dev::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/upgrade.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/upgrade.rs index 707e0086ed15..c20bbe163d7f 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/upgrade.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/upgrade.rs @@ -1,6 +1,6 @@ -use common::{cmd::Cmd, logger, spinner::Spinner}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, logger, spinner::Spinner}; +use zkstack_cli_config::EcosystemConfig; use super::{args::upgrade::UpgradeArgs, utils::install_and_build_dependencies}; use crate::commands::dev::messages::{MSG_UPGRADE_TEST_RUN_INFO, MSG_UPGRADE_TEST_RUN_SUCCESS}; diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/utils.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/utils.rs index 8435b437169d..7c042fad1fa9 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/utils.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/utils.rs @@ -1,14 +1,14 @@ use std::collections::HashMap; use anyhow::Context; -use common::{cmd::Cmd, spinner::Spinner, wallets::Wallet}; -use config::{ChainConfig, EcosystemConfig}; use ethers::{ providers::{Http, Middleware, Provider}, utils::hex::ToHex, }; use serde::Deserialize; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, spinner::Spinner, wallets::Wallet}; +use zkstack_cli_config::{ChainConfig, EcosystemConfig}; use crate::commands::dev::messages::{ MSG_INTEGRATION_TESTS_BUILDING_CONTRACTS, MSG_INTEGRATION_TESTS_BUILDING_DEPENDENCIES, @@ -67,7 +67,7 @@ impl TestWallets { let balance = provider.get_balance(wallet.address, None).await?; if balance.is_zero() { - common::ethereum::distribute_eth( + zkstack_cli_common::ethereum::distribute_eth( self.get_main_wallet()?, vec![wallet.address], l1_rpc, diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/wallet.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/wallet.rs index 6953014bf92b..ebacc5ead532 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/wallet.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/wallet.rs @@ -1,9 +1,9 @@ use std::path::PathBuf; use anyhow::Context; -use common::logger; -use config::EcosystemConfig; use xshell::Shell; +use zkstack_cli_common::logger; +use zkstack_cli_config::EcosystemConfig; use super::utils::{TestWallets, TEST_WALLETS_PATH}; use crate::commands::dev::messages::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/dals.rs b/zkstack_cli/crates/zkstack/src/commands/dev/dals.rs index 9626edfed732..199c44bbeb70 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/dals.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/dals.rs @@ -1,7 +1,7 @@ use anyhow::Context as _; -use config::{EcosystemConfig, SecretsConfig}; use url::Url; use xshell::Shell; +use zkstack_cli_config::{EcosystemConfig, SecretsConfig}; use super::{ commands::database::args::DalUrls, diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/build_transactions.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/build_transactions.rs index 697fa518b6e4..3abf0a0e3124 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/build_transactions.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/build_transactions.rs @@ -1,9 +1,9 @@ use std::{path::PathBuf, str::FromStr}; use clap::Parser; -use common::{forge::ForgeScriptArgs, Prompt}; use serde::{Deserialize, Serialize}; use url::Url; +use zkstack_cli_common::{forge::ForgeScriptArgs, Prompt}; use zksync_basic_types::H160; use crate::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/create.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/create.rs index 9665516945e0..c49739d022c9 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/create.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/create.rs @@ -1,12 +1,12 @@ use std::path::PathBuf; use clap::{Parser, ValueHint}; -use common::{Prompt, PromptConfirm, PromptSelect}; use serde::{Deserialize, Serialize}; use slugify_rs::slugify; use strum::IntoEnumIterator; -use types::{L1Network, WalletCreation}; use xshell::Shell; +use zkstack_cli_common::{Prompt, PromptConfirm, PromptSelect}; +use zkstack_cli_types::{L1Network, WalletCreation}; use crate::{ commands::chain::{args::create::ChainCreateArgs, ChainCreateArgsFinal}, diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/gateway_upgrade.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/gateway_upgrade.rs index 21fb714bc491..0301853e1acf 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/gateway_upgrade.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/gateway_upgrade.rs @@ -2,11 +2,11 @@ use std::path::PathBuf; use clap::{Parser, ValueEnum}; -use common::{forge::ForgeScriptArgs, Prompt}; use serde::{Deserialize, Serialize}; use strum::EnumIter; -use types::L1Network; use url::Url; +use zkstack_cli_common::{forge::ForgeScriptArgs, Prompt}; +use zkstack_cli_types::L1Network; use crate::{ defaults::LOCAL_RPC_URL, diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/init.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/init.rs index 9b7ab5abf089..d6c3d675c053 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/init.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/init.rs @@ -1,10 +1,10 @@ use std::path::PathBuf; use clap::Parser; -use common::{forge::ForgeScriptArgs, Prompt, PromptConfirm}; use serde::{Deserialize, Serialize}; -use types::L1Network; use url::Url; +use zkstack_cli_common::{forge::ForgeScriptArgs, Prompt, PromptConfirm}; +use zkstack_cli_types::L1Network; use crate::{ commands::chain::args::genesis::GenesisArgs, diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/build_transactions.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/build_transactions.rs index ff7132360972..3d81e6f3b0b7 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/build_transactions.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/build_transactions.rs @@ -1,7 +1,7 @@ use anyhow::Context; -use common::{git, logger, spinner::Spinner}; -use config::{traits::SaveConfigWithBasePath, EcosystemConfig}; use xshell::Shell; +use zkstack_cli_common::{git, logger, spinner::Spinner}; +use zkstack_cli_config::{traits::SaveConfigWithBasePath, EcosystemConfig}; use super::{ args::build_transactions::BuildTransactionsArgs, diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/change_default.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/change_default.rs index 3bd392c0558d..e5448187afd3 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/change_default.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/change_default.rs @@ -1,6 +1,6 @@ -use common::PromptSelect; -use config::{traits::SaveConfigWithBasePath, EcosystemConfig}; use xshell::Shell; +use zkstack_cli_common::PromptSelect; +use zkstack_cli_config::{traits::SaveConfigWithBasePath, EcosystemConfig}; use crate::{ commands::ecosystem::args::change_default::ChangeDefaultChain, diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/common.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/common.rs index e5ba18fe4c3e..c31aa6252971 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/common.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/common.rs @@ -1,6 +1,7 @@ use anyhow::Context; -use common::forge::{Forge, ForgeScriptArgs}; -use config::{ +use xshell::Shell; +use zkstack_cli_common::forge::{Forge, ForgeScriptArgs}; +use zkstack_cli_config::{ forge_interface::{ deploy_ecosystem::{ input::{DeployL1Config, InitialDeploymentConfig}, @@ -11,8 +12,7 @@ use config::{ traits::{ReadConfig, ReadConfigWithBasePath, SaveConfig}, ContractsConfig, EcosystemConfig, GenesisConfig, }; -use types::{L1Network, ProverMode}; -use xshell::Shell; +use zkstack_cli_types::{L1Network, ProverMode}; use crate::utils::forge::{check_the_balance, fill_forge_private_key, WalletOwner}; diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/create.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/create.rs index d7c6005cd1aa..404589afac2d 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/create.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/create.rs @@ -1,10 +1,10 @@ use anyhow::{bail, Context}; -use common::{logger, spinner::Spinner}; -use config::{ +use xshell::Shell; +use zkstack_cli_common::{logger, spinner::Spinner}; +use zkstack_cli_config::{ create_local_configs_dir, create_wallets, get_default_era_chain_id, traits::SaveConfigWithBasePath, EcosystemConfig, EcosystemConfigFromFileError, }; -use xshell::Shell; use crate::{ commands::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/create_configs.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/create_configs.rs index 38358355ff97..a58e0d38bf11 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/create_configs.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/create_configs.rs @@ -1,11 +1,11 @@ use std::path::Path; -use config::{ +use xshell::Shell; +use zkstack_cli_config::{ forge_interface::deploy_ecosystem::input::{Erc20DeploymentConfig, InitialDeploymentConfig}, traits::{SaveConfigWithBasePath, SaveConfigWithCommentAndBasePath}, AppsEcosystemConfig, }; -use xshell::Shell; use crate::messages::{MSG_SAVE_ERC20_CONFIG_ATTENTION, MSG_SAVE_INITIAL_CONFIG_ATTENTION}; diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/init.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/init.rs index 309dda7abe5b..56c196fe7be9 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/init.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/init.rs @@ -1,7 +1,8 @@ use std::{path::PathBuf, str::FromStr}; use anyhow::Context; -use common::{ +use xshell::Shell; +use zkstack_cli_common::{ config::global_config, contracts::build_system_contracts, forge::{Forge, ForgeScriptArgs}, @@ -9,7 +10,7 @@ use common::{ spinner::Spinner, Prompt, }; -use config::{ +use zkstack_cli_config::{ forge_interface::{ deploy_ecosystem::{ input::{DeployErc20Config, Erc20DeploymentConfig, InitialDeploymentConfig}, @@ -20,8 +21,7 @@ use config::{ traits::{FileConfigWithDefaultName, ReadConfig, SaveConfig, SaveConfigWithBasePath}, ContractsConfig, EcosystemConfig, }; -use types::L1Network; -use xshell::Shell; +use zkstack_cli_types::L1Network; use super::{ args::init::{EcosystemArgsFinal, EcosystemInitArgs, EcosystemInitArgsFinal}, diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/setup_observability.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/setup_observability.rs index f20c3c24157e..23d2b6e2cca4 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/setup_observability.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/setup_observability.rs @@ -1,6 +1,6 @@ -use common::{git, logger, spinner::Spinner}; -use config::{ERA_OBSERBAVILITY_DIR, ERA_OBSERBAVILITY_GIT_REPO}; use xshell::Shell; +use zkstack_cli_common::{git, logger, spinner::Spinner}; +use zkstack_cli_config::{ERA_OBSERBAVILITY_DIR, ERA_OBSERBAVILITY_GIT_REPO}; use crate::messages::{ MSG_DOWNLOADING_ERA_OBSERVABILITY_SPINNER, MSG_ERA_OBSERVABILITY_ALREADY_SETUP, diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/utils.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/utils.rs index a51adc75fb42..5f6994ed38f9 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/utils.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/utils.rs @@ -1,7 +1,7 @@ use std::path::Path; -use common::cmd::Cmd; use xshell::{cmd, Shell}; +use zkstack_cli_common::cmd::Cmd; pub(super) fn install_yarn_dependencies(shell: &Shell, link_to_code: &Path) -> anyhow::Result<()> { let _dir_guard = shell.push_dir(link_to_code); diff --git a/zkstack_cli/crates/zkstack/src/commands/explorer/backend.rs b/zkstack_cli/crates/zkstack/src/commands/explorer/backend.rs index 29cc2ecfbff0..b40205cdbe5e 100644 --- a/zkstack_cli/crates/zkstack/src/commands/explorer/backend.rs +++ b/zkstack_cli/crates/zkstack/src/commands/explorer/backend.rs @@ -1,9 +1,9 @@ use std::path::Path; use anyhow::Context; -use common::docker; -use config::{explorer_compose::ExplorerBackendComposeConfig, EcosystemConfig}; use xshell::Shell; +use zkstack_cli_common::docker; +use zkstack_cli_config::{explorer_compose::ExplorerBackendComposeConfig, EcosystemConfig}; use crate::messages::{ msg_explorer_chain_not_initialized, MSG_CHAIN_NOT_FOUND_ERR, diff --git a/zkstack_cli/crates/zkstack/src/commands/explorer/init.rs b/zkstack_cli/crates/zkstack/src/commands/explorer/init.rs index 096c45da5d8f..8bac0b84d982 100644 --- a/zkstack_cli/crates/zkstack/src/commands/explorer/init.rs +++ b/zkstack_cli/crates/zkstack/src/commands/explorer/init.rs @@ -1,14 +1,14 @@ use anyhow::Context; -use common::{config::global_config, db, logger, Prompt}; -use config::{ +use slugify_rs::slugify; +use url::Url; +use xshell::Shell; +use zkstack_cli_common::{config::global_config, db, logger, Prompt}; +use zkstack_cli_config::{ explorer::{ExplorerChainConfig, ExplorerConfig}, explorer_compose::{ExplorerBackendComposeConfig, ExplorerBackendConfig, ExplorerBackendPorts}, traits::{ConfigWithL2RpcUrl, SaveConfig}, ChainConfig, EcosystemConfig, }; -use slugify_rs::slugify; -use url::Url; -use xshell::Shell; use crate::{ consts::L2_BASE_TOKEN_ADDRESS, diff --git a/zkstack_cli/crates/zkstack/src/commands/explorer/run.rs b/zkstack_cli/crates/zkstack/src/commands/explorer/run.rs index a6519f62edba..4fbc8e1799fb 100644 --- a/zkstack_cli/crates/zkstack/src/commands/explorer/run.rs +++ b/zkstack_cli/crates/zkstack/src/commands/explorer/run.rs @@ -1,9 +1,9 @@ use std::path::Path; use anyhow::Context; -use common::{config::global_config, docker, logger}; -use config::{explorer::*, traits::SaveConfig, AppsEcosystemConfig, EcosystemConfig}; use xshell::Shell; +use zkstack_cli_common::{config::global_config, docker, logger}; +use zkstack_cli_config::{explorer::*, traits::SaveConfig, AppsEcosystemConfig, EcosystemConfig}; use crate::{ consts::{EXPLORER_APP_DOCKER_CONFIG_PATH, EXPLORER_APP_DOCKER_IMAGE}, diff --git a/zkstack_cli/crates/zkstack/src/commands/external_node/args/prepare_configs.rs b/zkstack_cli/crates/zkstack/src/commands/external_node/args/prepare_configs.rs index b1759702c461..2f688ae2eea5 100644 --- a/zkstack_cli/crates/zkstack/src/commands/external_node/args/prepare_configs.rs +++ b/zkstack_cli/crates/zkstack/src/commands/external_node/args/prepare_configs.rs @@ -1,9 +1,9 @@ use clap::Parser; -use common::{db::DatabaseConfig, Prompt}; -use config::ChainConfig; use serde::{Deserialize, Serialize}; use slugify_rs::slugify; use url::Url; +use zkstack_cli_common::{db::DatabaseConfig, Prompt}; +use zkstack_cli_config::ChainConfig; use crate::{ defaults::{generate_external_node_db_name, DATABASE_SERVER_URL, LOCAL_RPC_URL}, diff --git a/zkstack_cli/crates/zkstack/src/commands/external_node/build.rs b/zkstack_cli/crates/zkstack/src/commands/external_node/build.rs index ff15c0c77f30..8a4c4befe8db 100644 --- a/zkstack_cli/crates/zkstack/src/commands/external_node/build.rs +++ b/zkstack_cli/crates/zkstack/src/commands/external_node/build.rs @@ -1,7 +1,7 @@ use anyhow::Context; -use common::{cmd::Cmd, logger}; -use config::EcosystemConfig; use xshell::{cmd, Shell}; +use zkstack_cli_common::{cmd::Cmd, logger}; +use zkstack_cli_config::EcosystemConfig; use crate::messages::{MSG_BUILDING_EN, MSG_CHAIN_NOT_FOUND_ERR, MSG_FAILED_TO_BUILD_EN_ERR}; diff --git a/zkstack_cli/crates/zkstack/src/commands/external_node/init.rs b/zkstack_cli/crates/zkstack/src/commands/external_node/init.rs index 184151764961..526e9fd4bc5f 100644 --- a/zkstack_cli/crates/zkstack/src/commands/external_node/init.rs +++ b/zkstack_cli/crates/zkstack/src/commands/external_node/init.rs @@ -1,10 +1,12 @@ use anyhow::Context; -use common::{ +use xshell::Shell; +use zkstack_cli_common::{ db::{drop_db_if_exists, init_db, migrate_db, DatabaseConfig}, spinner::Spinner, }; -use config::{traits::ReadConfigWithBasePath, ChainConfig, EcosystemConfig, SecretsConfig}; -use xshell::Shell; +use zkstack_cli_config::{ + traits::ReadConfigWithBasePath, ChainConfig, EcosystemConfig, SecretsConfig, +}; use crate::{ consts::SERVER_MIGRATIONS, diff --git a/zkstack_cli/crates/zkstack/src/commands/external_node/prepare_configs.rs b/zkstack_cli/crates/zkstack/src/commands/external_node/prepare_configs.rs index 8e937e3903d4..ed591597584c 100644 --- a/zkstack_cli/crates/zkstack/src/commands/external_node/prepare_configs.rs +++ b/zkstack_cli/crates/zkstack/src/commands/external_node/prepare_configs.rs @@ -1,14 +1,14 @@ use std::{collections::BTreeMap, path::Path, str::FromStr}; use anyhow::Context; -use common::logger; -use config::{ +use xshell::Shell; +use zkstack_cli_common::logger; +use zkstack_cli_config::{ external_node::ENConfig, set_rocks_db_config, traits::{FileConfigWithDefaultName, SaveConfigWithBasePath}, ChainConfig, EcosystemConfig, GeneralConfig, SecretsConfig, }; -use xshell::Shell; use zksync_basic_types::url::SensitiveUrl; use zksync_config::configs::{ consensus::{ConsensusConfig, ConsensusSecrets, NodeSecretKey, Secret}, diff --git a/zkstack_cli/crates/zkstack/src/commands/external_node/run.rs b/zkstack_cli/crates/zkstack/src/commands/external_node/run.rs index 46c98119f893..21714c335fa0 100644 --- a/zkstack_cli/crates/zkstack/src/commands/external_node/run.rs +++ b/zkstack_cli/crates/zkstack/src/commands/external_node/run.rs @@ -1,7 +1,7 @@ use anyhow::Context; -use common::logger; -use config::{ChainConfig, EcosystemConfig}; use xshell::Shell; +use zkstack_cli_common::logger; +use zkstack_cli_config::{ChainConfig, EcosystemConfig}; use crate::{ commands::external_node::{args::run::RunExternalNodeArgs, init}, diff --git a/zkstack_cli/crates/zkstack/src/commands/external_node/wait.rs b/zkstack_cli/crates/zkstack/src/commands/external_node/wait.rs index 72568c36f363..b645314dc9c2 100644 --- a/zkstack_cli/crates/zkstack/src/commands/external_node/wait.rs +++ b/zkstack_cli/crates/zkstack/src/commands/external_node/wait.rs @@ -1,7 +1,7 @@ use anyhow::Context as _; -use common::{config::global_config, logger}; -use config::{traits::ReadConfigWithBasePath, EcosystemConfig}; use xshell::Shell; +use zkstack_cli_common::{config::global_config, logger}; +use zkstack_cli_config::{traits::ReadConfigWithBasePath, EcosystemConfig}; use zksync_config::configs::GeneralConfig; use crate::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/portal.rs b/zkstack_cli/crates/zkstack/src/commands/portal.rs index f9e7fe358609..d534498aaacd 100644 --- a/zkstack_cli/crates/zkstack/src/commands/portal.rs +++ b/zkstack_cli/crates/zkstack/src/commands/portal.rs @@ -1,15 +1,15 @@ use std::path::Path; use anyhow::Context; -use common::{config::global_config, docker, ethereum, logger}; -use config::{ +use ethers::types::Address; +use xshell::Shell; +use zkstack_cli_common::{config::global_config, docker, ethereum, logger}; +use zkstack_cli_config::{ portal::*, traits::{ConfigWithL2RpcUrl, SaveConfig}, AppsEcosystemConfig, ChainConfig, EcosystemConfig, }; -use ethers::types::Address; -use types::{BaseToken, TokenInfo}; -use xshell::Shell; +use zkstack_cli_types::{BaseToken, TokenInfo}; use crate::{ consts::{L2_BASE_TOKEN_ADDRESS, PORTAL_DOCKER_CONFIG_PATH, PORTAL_DOCKER_IMAGE}, diff --git a/zkstack_cli/crates/zkstack/src/commands/prover/args/compressor_keys.rs b/zkstack_cli/crates/zkstack/src/commands/prover/args/compressor_keys.rs index f393a89882af..8b7bfb0fc543 100644 --- a/zkstack_cli/crates/zkstack/src/commands/prover/args/compressor_keys.rs +++ b/zkstack_cli/crates/zkstack/src/commands/prover/args/compressor_keys.rs @@ -1,6 +1,6 @@ use clap::{Parser, ValueEnum}; -use common::Prompt; use strum::EnumIter; +use zkstack_cli_common::Prompt; use crate::messages::MSG_SETUP_COMPRESSOR_KEY_PATH_PROMPT; diff --git a/zkstack_cli/crates/zkstack/src/commands/prover/args/init.rs b/zkstack_cli/crates/zkstack/src/commands/prover/args/init.rs index b40dc180124d..afe57a8f5eb4 100644 --- a/zkstack_cli/crates/zkstack/src/commands/prover/args/init.rs +++ b/zkstack_cli/crates/zkstack/src/commands/prover/args/init.rs @@ -1,11 +1,11 @@ use clap::{Parser, ValueEnum}; -use common::{db::DatabaseConfig, logger, Prompt, PromptConfirm, PromptSelect}; -use config::ChainConfig; use serde::{Deserialize, Serialize}; use slugify_rs::slugify; use strum::{EnumIter, IntoEnumIterator}; use url::Url; use xshell::Shell; +use zkstack_cli_common::{db::DatabaseConfig, logger, Prompt, PromptConfirm, PromptSelect}; +use zkstack_cli_config::ChainConfig; use zksync_config::configs::fri_prover::CloudConnectionMode; use super::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/prover/args/init_bellman_cuda.rs b/zkstack_cli/crates/zkstack/src/commands/prover/args/init_bellman_cuda.rs index 98a5c78be2a6..aa653fc62ade 100644 --- a/zkstack_cli/crates/zkstack/src/commands/prover/args/init_bellman_cuda.rs +++ b/zkstack_cli/crates/zkstack/src/commands/prover/args/init_bellman_cuda.rs @@ -1,7 +1,7 @@ use clap::Parser; -use common::{Prompt, PromptSelect}; use serde::{Deserialize, Serialize}; use strum::{EnumIter, IntoEnumIterator}; +use zkstack_cli_common::{Prompt, PromptSelect}; use crate::messages::{ MSG_BELLMAN_CUDA_DIR_PROMPT, MSG_BELLMAN_CUDA_ORIGIN_SELECT, MSG_BELLMAN_CUDA_SELECTION_CLONE, diff --git a/zkstack_cli/crates/zkstack/src/commands/prover/args/run.rs b/zkstack_cli/crates/zkstack/src/commands/prover/args/run.rs index a356f1874b7a..3aa3d090e4dc 100644 --- a/zkstack_cli/crates/zkstack/src/commands/prover/args/run.rs +++ b/zkstack_cli/crates/zkstack/src/commands/prover/args/run.rs @@ -2,10 +2,10 @@ use std::path::Path; use anyhow::anyhow; use clap::{Parser, ValueEnum}; -use common::{Prompt, PromptSelect}; -use config::ChainConfig; use serde::{Deserialize, Serialize}; use strum::{EnumIter, IntoEnumIterator}; +use zkstack_cli_common::{Prompt, PromptSelect}; +use zkstack_cli_config::ChainConfig; use crate::{ consts::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/prover/args/setup_keys.rs b/zkstack_cli/crates/zkstack/src/commands/prover/args/setup_keys.rs index 155977b8812a..914533e47144 100644 --- a/zkstack_cli/crates/zkstack/src/commands/prover/args/setup_keys.rs +++ b/zkstack_cli/crates/zkstack/src/commands/prover/args/setup_keys.rs @@ -1,6 +1,6 @@ use clap::{Parser, ValueEnum}; -use common::PromptSelect; use strum::{EnumIter, IntoEnumIterator}; +use zkstack_cli_common::PromptSelect; use crate::messages::{MSG_SETUP_KEYS_DOWNLOAD_SELECTION_PROMPT, MSG_SETUP_KEYS_REGION_PROMPT}; diff --git a/zkstack_cli/crates/zkstack/src/commands/prover/compressor_keys.rs b/zkstack_cli/crates/zkstack/src/commands/prover/compressor_keys.rs index 31b970de3a1d..19ba634a4075 100644 --- a/zkstack_cli/crates/zkstack/src/commands/prover/compressor_keys.rs +++ b/zkstack_cli/crates/zkstack/src/commands/prover/compressor_keys.rs @@ -1,7 +1,7 @@ use anyhow::Context; -use common::{logger, spinner::Spinner}; -use config::{get_link_to_prover, EcosystemConfig, GeneralConfig}; use xshell::Shell; +use zkstack_cli_common::{logger, spinner::Spinner}; +use zkstack_cli_config::{get_link_to_prover, EcosystemConfig, GeneralConfig}; use super::args::compressor_keys::{CompressorKeysArgs, CompressorType}; use crate::messages::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/prover/gcs.rs b/zkstack_cli/crates/zkstack/src/commands/prover/gcs.rs index f28c44504b56..5d82647f98d1 100644 --- a/zkstack_cli/crates/zkstack/src/commands/prover/gcs.rs +++ b/zkstack_cli/crates/zkstack/src/commands/prover/gcs.rs @@ -1,5 +1,7 @@ -use common::{check_prerequisites, cmd::Cmd, logger, spinner::Spinner, GCLOUD_PREREQUISITE}; use xshell::{cmd, Shell}; +use zkstack_cli_common::{ + check_prerequisites, cmd::Cmd, logger, spinner::Spinner, GCLOUD_PREREQUISITE, +}; use zksync_config::{configs::object_store::ObjectStoreMode, ObjectStoreConfig}; use super::args::init::ProofStorageGCSCreateBucket; diff --git a/zkstack_cli/crates/zkstack/src/commands/prover/init.rs b/zkstack_cli/crates/zkstack/src/commands/prover/init.rs index d0d9238321a4..b318f4924ec3 100644 --- a/zkstack_cli/crates/zkstack/src/commands/prover/init.rs +++ b/zkstack_cli/crates/zkstack/src/commands/prover/init.rs @@ -1,18 +1,18 @@ use std::path::PathBuf; use anyhow::Context; -use common::{ +use xshell::{cmd, Shell}; +use zkstack_cli_common::{ cmd::Cmd, config::global_config, db::{drop_db_if_exists, init_db, migrate_db, DatabaseConfig}, logger, spinner::Spinner, }; -use config::{ +use zkstack_cli_config::{ copy_configs, get_link_to_prover, set_prover_database, traits::SaveConfigWithBasePath, EcosystemConfig, }; -use xshell::{cmd, Shell}; use zksync_config::{configs::object_store::ObjectStoreMode, ObjectStoreConfig}; use super::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/prover/init_bellman_cuda.rs b/zkstack_cli/crates/zkstack/src/commands/prover/init_bellman_cuda.rs index 615ef841488b..16e33f8d7d3f 100644 --- a/zkstack_cli/crates/zkstack/src/commands/prover/init_bellman_cuda.rs +++ b/zkstack_cli/crates/zkstack/src/commands/prover/init_bellman_cuda.rs @@ -1,7 +1,9 @@ use anyhow::Context; -use common::{check_prerequisites, cmd::Cmd, git, logger, spinner::Spinner, GPU_PREREQUISITES}; -use config::{traits::SaveConfigWithBasePath, EcosystemConfig}; use xshell::{cmd, Shell}; +use zkstack_cli_common::{ + check_prerequisites, cmd::Cmd, git, logger, spinner::Spinner, GPU_PREREQUISITES, +}; +use zkstack_cli_config::{traits::SaveConfigWithBasePath, EcosystemConfig}; use super::args::init_bellman_cuda::InitBellmanCudaArgs; use crate::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/prover/run.rs b/zkstack_cli/crates/zkstack/src/commands/prover/run.rs index c9bf837f787d..495c41ef8255 100644 --- a/zkstack_cli/crates/zkstack/src/commands/prover/run.rs +++ b/zkstack_cli/crates/zkstack/src/commands/prover/run.rs @@ -1,9 +1,9 @@ use std::path::{Path, PathBuf}; use anyhow::{anyhow, Context}; -use common::{check_prerequisites, cmd::Cmd, logger, GPU_PREREQUISITES}; -use config::{get_link_to_prover, ChainConfig, EcosystemConfig}; use xshell::{cmd, Shell}; +use zkstack_cli_common::{check_prerequisites, cmd::Cmd, logger, GPU_PREREQUISITES}; +use zkstack_cli_config::{get_link_to_prover, ChainConfig, EcosystemConfig}; use super::args::run::{ProverComponent, ProverRunArgs}; use crate::messages::{ diff --git a/zkstack_cli/crates/zkstack/src/commands/prover/setup_keys.rs b/zkstack_cli/crates/zkstack/src/commands/prover/setup_keys.rs index ae0480e872dd..463dd788777e 100644 --- a/zkstack_cli/crates/zkstack/src/commands/prover/setup_keys.rs +++ b/zkstack_cli/crates/zkstack/src/commands/prover/setup_keys.rs @@ -1,9 +1,9 @@ use anyhow::Ok; -use common::{ +use xshell::{cmd, Shell}; +use zkstack_cli_common::{ check_prerequisites, cmd::Cmd, logger, spinner::Spinner, GCLOUD_PREREQUISITE, GPU_PREREQUISITES, }; -use config::{get_link_to_prover, EcosystemConfig}; -use xshell::{cmd, Shell}; +use zkstack_cli_config::{get_link_to_prover, EcosystemConfig}; use crate::{ commands::prover::args::setup_keys::{Mode, Region, SetupKeysArgs}, diff --git a/zkstack_cli/crates/zkstack/src/commands/server.rs b/zkstack_cli/crates/zkstack/src/commands/server.rs index 702897edbbc1..cf7abf7dea21 100644 --- a/zkstack_cli/crates/zkstack/src/commands/server.rs +++ b/zkstack_cli/crates/zkstack/src/commands/server.rs @@ -1,15 +1,15 @@ use anyhow::Context; -use common::{ +use xshell::{cmd, Shell}; +use zkstack_cli_common::{ cmd::Cmd, config::global_config, logger, server::{Server, ServerMode}, }; -use config::{ +use zkstack_cli_config::{ traits::FileConfigWithDefaultName, ChainConfig, ContractsConfig, EcosystemConfig, GeneralConfig, GenesisConfig, SecretsConfig, WalletsConfig, }; -use xshell::{cmd, Shell}; use crate::{ commands::args::{RunServerArgs, ServerArgs, ServerCommand, WaitArgs}, diff --git a/zkstack_cli/crates/zkstack/src/commands/update.rs b/zkstack_cli/crates/zkstack/src/commands/update.rs index 534d490e6cae..0e1d385f8fef 100644 --- a/zkstack_cli/crates/zkstack/src/commands/update.rs +++ b/zkstack_cli/crates/zkstack/src/commands/update.rs @@ -1,17 +1,17 @@ use std::path::Path; use anyhow::{Context, Ok}; -use common::{ +use xshell::Shell; +use zkstack_cli_common::{ db::migrate_db, git, logger, spinner::Spinner, yaml::{merge_yaml, ConfigDiff}, }; -use config::{ +use zkstack_cli_config::{ ChainConfig, EcosystemConfig, CONTRACTS_FILE, EN_CONFIG_FILE, ERA_OBSERBAVILITY_DIR, GENERAL_FILE, GENESIS_FILE, SECRETS_FILE, }; -use xshell::Shell; use super::args::UpdateArgs; use crate::{ diff --git a/zkstack_cli/crates/zkstack/src/defaults.rs b/zkstack_cli/crates/zkstack/src/defaults.rs index 2b43009f5594..4fa15be8a118 100644 --- a/zkstack_cli/crates/zkstack/src/defaults.rs +++ b/zkstack_cli/crates/zkstack/src/defaults.rs @@ -1,6 +1,6 @@ -use config::ChainConfig; use lazy_static::lazy_static; use url::Url; +use zkstack_cli_config::ChainConfig; lazy_static! { pub static ref DATABASE_SERVER_URL: Url = diff --git a/zkstack_cli/crates/zkstack/src/enable_evm_emulator.rs b/zkstack_cli/crates/zkstack/src/enable_evm_emulator.rs index bda1bfb3fc81..57e78a30b418 100644 --- a/zkstack_cli/crates/zkstack/src/enable_evm_emulator.rs +++ b/zkstack_cli/crates/zkstack/src/enable_evm_emulator.rs @@ -1,11 +1,13 @@ -use common::{ +use ethers::{abi::parse_abi, contract::BaseContract, types::Address}; +use xshell::Shell; +use zkstack_cli_common::{ forge::{Forge, ForgeScript, ForgeScriptArgs}, spinner::Spinner, wallets::Wallet, }; -use config::{forge_interface::script_params::ENABLE_EVM_EMULATOR_PARAMS, EcosystemConfig}; -use ethers::{abi::parse_abi, contract::BaseContract, types::Address}; -use xshell::Shell; +use zkstack_cli_config::{ + forge_interface::script_params::ENABLE_EVM_EMULATOR_PARAMS, EcosystemConfig, +}; use crate::{ messages::MSG_ENABLING_EVM_EMULATOR, diff --git a/zkstack_cli/crates/zkstack/src/external_node.rs b/zkstack_cli/crates/zkstack/src/external_node.rs index 5ff4ce070250..21d4e0db5592 100644 --- a/zkstack_cli/crates/zkstack/src/external_node.rs +++ b/zkstack_cli/crates/zkstack/src/external_node.rs @@ -1,11 +1,11 @@ use std::path::PathBuf; use anyhow::Context; -use config::{ +use xshell::Shell; +use zkstack_cli_config::{ external_node::ENConfig, traits::FileConfigWithDefaultName, ChainConfig, GeneralConfig, SecretsConfig, }; -use xshell::Shell; use zksync_config::configs::consensus::ConsensusConfig; use crate::messages::MSG_FAILED_TO_RUN_SERVER_ERR; @@ -63,7 +63,7 @@ impl RunExternalNode { consensus_args.push(format!("--consensus-path={}", consensus_config)) } - common::external_node::run( + zkstack_cli_common::external_node::run( shell, code_path, config_general_config, diff --git a/zkstack_cli/crates/zkstack/src/main.rs b/zkstack_cli/crates/zkstack/src/main.rs index ff4589a99cc5..98970e2be682 100644 --- a/zkstack_cli/crates/zkstack/src/main.rs +++ b/zkstack_cli/crates/zkstack/src/main.rs @@ -4,15 +4,15 @@ use commands::{ contract_verifier::ContractVerifierCommands, dev::DevCommands, }; -use common::{ +use xshell::Shell; +use zkstack_cli_common::{ check_general_prerequisites, config::{global_config, init_global_config, GlobalConfig}, error::log_error, init_prompt_theme, logger, version::version_message, }; -use config::EcosystemConfig; -use xshell::Shell; +use zkstack_cli_config::EcosystemConfig; use crate::commands::{ args::ServerArgs, chain::ChainCommands, consensus, ecosystem::EcosystemCommands, diff --git a/zkstack_cli/crates/zkstack/src/utils/consensus.rs b/zkstack_cli/crates/zkstack/src/utils/consensus.rs index 946d28a33fbd..0a1287067434 100644 --- a/zkstack_cli/crates/zkstack/src/utils/consensus.rs +++ b/zkstack_cli/crates/zkstack/src/utils/consensus.rs @@ -1,6 +1,6 @@ use anyhow::Context as _; -use config::ChainConfig; use secrecy::{ExposeSecret, Secret}; +use zkstack_cli_config::ChainConfig; use zksync_config::configs::consensus::{ AttesterPublicKey, AttesterSecretKey, ConsensusSecrets, GenesisSpec, NodePublicKey, NodeSecretKey, ProtocolVersion, ValidatorPublicKey, ValidatorSecretKey, WeightedAttester, diff --git a/zkstack_cli/crates/zkstack/src/utils/forge.rs b/zkstack_cli/crates/zkstack/src/utils/forge.rs index 76f045f82b9e..ccf5c3c84cd8 100644 --- a/zkstack_cli/crates/zkstack/src/utils/forge.rs +++ b/zkstack_cli/crates/zkstack/src/utils/forge.rs @@ -1,6 +1,6 @@ use anyhow::Context as _; -use common::{forge::ForgeScript, wallets::Wallet}; use ethers::types::U256; +use zkstack_cli_common::{forge::ForgeScript, wallets::Wallet}; use crate::{ consts::MINIMUM_BALANCE_FOR_WALLET, @@ -37,7 +37,7 @@ pub async fn check_the_balance(forge: &ForgeScript) -> anyhow::Result<()> { if balance >= expected_balance { return Ok(()); } - if !common::PromptConfirm::new(msg_address_doesnt_have_enough_money_prompt( + if !zkstack_cli_common::PromptConfirm::new(msg_address_doesnt_have_enough_money_prompt( &address, balance, expected_balance, diff --git a/zkstack_cli/crates/zkstack/src/utils/link_to_code.rs b/zkstack_cli/crates/zkstack/src/utils/link_to_code.rs index fcae429966dc..522e0d5e9c84 100644 --- a/zkstack_cli/crates/zkstack/src/utils/link_to_code.rs +++ b/zkstack_cli/crates/zkstack/src/utils/link_to_code.rs @@ -4,10 +4,12 @@ use std::{ }; use anyhow::bail; -use common::{cmd::Cmd, git, logger, spinner::Spinner, Prompt, PromptConfirm, PromptSelect}; -use config::ZKSYNC_ERA_GIT_REPO; use strum::{EnumIter, IntoEnumIterator}; use xshell::{cmd, Shell}; +use zkstack_cli_common::{ + cmd::Cmd, git, logger, spinner::Spinner, Prompt, PromptConfirm, PromptSelect, +}; +use zkstack_cli_config::ZKSYNC_ERA_GIT_REPO; use crate::messages::{ msg_path_to_zksync_does_not_exist_err, MSG_CLONING_ERA_REPO_SPINNER, diff --git a/zkstack_cli/crates/zkstack/src/utils/ports.rs b/zkstack_cli/crates/zkstack/src/utils/ports.rs index 6c299b999136..f46acc9402e3 100644 --- a/zkstack_cli/crates/zkstack/src/utils/ports.rs +++ b/zkstack_cli/crates/zkstack/src/utils/ports.rs @@ -1,13 +1,13 @@ use std::{collections::HashMap, fmt, net::SocketAddr, ops::Range, path::Path}; use anyhow::{bail, Context, Result}; -use config::{ - explorer_compose::ExplorerBackendPorts, EcosystemConfig, DEFAULT_EXPLORER_API_PORT, - DEFAULT_EXPLORER_DATA_FETCHER_PORT, DEFAULT_EXPLORER_WORKER_PORT, -}; use serde_yaml::Value; use url::Url; use xshell::Shell; +use zkstack_cli_config::{ + explorer_compose::ExplorerBackendPorts, EcosystemConfig, DEFAULT_EXPLORER_API_PORT, + DEFAULT_EXPLORER_DATA_FETCHER_PORT, DEFAULT_EXPLORER_WORKER_PORT, +}; use crate::defaults::{DEFAULT_OBSERVABILITY_PORT, PORT_RANGE_END, PORT_RANGE_START}; diff --git a/zkstack_cli/crates/zkstack/src/utils/rocks_db.rs b/zkstack_cli/crates/zkstack/src/utils/rocks_db.rs index 1b7e29dd9722..e365d3650952 100644 --- a/zkstack_cli/crates/zkstack/src/utils/rocks_db.rs +++ b/zkstack_cli/crates/zkstack/src/utils/rocks_db.rs @@ -1,7 +1,7 @@ use std::path::Path; -use config::RocksDbs; use xshell::Shell; +use zkstack_cli_config::RocksDbs; use crate::defaults::{ EN_ROCKS_DB_PREFIX, MAIN_ROCKS_DB_PREFIX, ROCKS_DB_BASIC_WITNESS_INPUT_PRODUCER, From 64d861d1e1d2d46339938ee3174c58cdc3f348c3 Mon Sep 17 00:00:00 2001 From: Artem Fomiuk <88630083+Artemka374@users.noreply.github.com> Date: Thu, 9 Jan 2025 13:01:02 +0200 Subject: [PATCH 15/97] feat: Add logging & metrics for mempool (#3447) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ ## Why ❔ ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- core/lib/dal/src/transactions_dal.rs | 17 +++++++++++++++-- core/lib/mempool/src/mempool_store.rs | 9 +++++++++ core/node/state_keeper/src/mempool_actor.rs | 8 ++++++++ core/node/state_keeper/src/metrics.rs | 4 ++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/core/lib/dal/src/transactions_dal.rs b/core/lib/dal/src/transactions_dal.rs index 20ecf8736d7c..b4e31954be76 100644 --- a/core/lib/dal/src/transactions_dal.rs +++ b/core/lib/dal/src/transactions_dal.rs @@ -1776,7 +1776,7 @@ impl TransactionsDal<'_, '_> { limit: usize, ) -> DalResult> { let stashed_addresses: Vec<_> = stashed_accounts.iter().map(Address::as_bytes).collect(); - sqlx::query!( + let result = sqlx::query!( r#" UPDATE transactions SET @@ -1794,8 +1794,15 @@ impl TransactionsDal<'_, '_> { .execute(self.storage) .await?; + tracing::debug!( + "Updated {} transactions for stashed accounts, stashed accounts amount: {}, stashed_accounts: {:?}", + result.rows_affected(), + stashed_addresses.len(), + stashed_accounts.iter().map(|a|format!("{:x}", a)).collect::>() + ); + let purged_addresses: Vec<_> = purged_accounts.iter().map(Address::as_bytes).collect(); - sqlx::query!( + let result = sqlx::query!( r#" DELETE FROM transactions WHERE @@ -1809,6 +1816,12 @@ impl TransactionsDal<'_, '_> { .execute(self.storage) .await?; + tracing::debug!( + "Updated {} transactions for purged accounts, purged accounts amount: {}", + result.rows_affected(), + purged_addresses.len() + ); + // Note, that transactions are updated in order of their hashes to avoid deadlocks with other UPDATE queries. let transactions = sqlx::query_as!( StorageTransaction, diff --git a/core/lib/mempool/src/mempool_store.rs b/core/lib/mempool/src/mempool_store.rs index 70176b456dd1..c6758574df7a 100644 --- a/core/lib/mempool/src/mempool_store.rs +++ b/core/lib/mempool/src/mempool_store.rs @@ -172,6 +172,8 @@ impl MempoolStore { .rfind(|el| el.matches_filter(filter))? .clone(); + let initial_length = self.stashed_accounts.len(); + // Stash all observed transactions that don't meet criteria for stashed_pointer in self .l2_priority_queue @@ -187,6 +189,13 @@ impl MempoolStore { self.stashed_accounts.push(stashed_pointer.account); } + + tracing::debug!( + "Stashed {} accounts by filter: {:?}", + self.stashed_accounts.len() - initial_length, + filter + ); + // insert pointer to the next transaction if it exists let (transaction, constraint, score) = self .l2_transactions_per_account diff --git a/core/node/state_keeper/src/mempool_actor.rs b/core/node/state_keeper/src/mempool_actor.rs index fea1fcf89291..322f159bf53d 100644 --- a/core/node/state_keeper/src/mempool_actor.rs +++ b/core/node/state_keeper/src/mempool_actor.rs @@ -83,6 +83,14 @@ impl MempoolFetcher { let latency = KEEPER_METRICS.mempool_sync.start(); let mut storage = self.pool.connection_tagged("state_keeper").await?; let mempool_info = self.mempool.get_mempool_info(); + + KEEPER_METRICS + .mempool_stashed_accounts + .set(mempool_info.stashed_accounts.len()); + KEEPER_METRICS + .mempool_purged_accounts + .set(mempool_info.purged_accounts.len()); + let protocol_version = storage .blocks_dal() .pending_protocol_version() diff --git a/core/node/state_keeper/src/metrics.rs b/core/node/state_keeper/src/metrics.rs index 7da5babd2199..14d578f683f4 100644 --- a/core/node/state_keeper/src/metrics.rs +++ b/core/node/state_keeper/src/metrics.rs @@ -64,6 +64,10 @@ pub struct StateKeeperMetrics { /// Latency to synchronize the mempool with Postgres. #[metrics(buckets = Buckets::LATENCIES)] pub mempool_sync: Histogram, + /// Number of stashed accounts in mempool + pub mempool_stashed_accounts: Gauge, + /// Number of purged accounts in mempool + pub mempool_purged_accounts: Gauge, /// Latency of the state keeper waiting for a transaction. #[metrics(buckets = Buckets::LATENCIES)] pub waiting_for_tx: Histogram, From 5a8818d93c66911dbe18ee81e404c28b4d933d1c Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Fri, 10 Jan 2025 23:15:24 +1100 Subject: [PATCH 16/97] chore: re-generate zkstack-cli autocompletion (#3453) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ ## Why ❔ ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- .../crates/zkstack/completion/zkstack.fish | 289 +++++++++++++----- 1 file changed, 212 insertions(+), 77 deletions(-) diff --git a/zkstack_cli/crates/zkstack/completion/zkstack.fish b/zkstack_cli/crates/zkstack/completion/zkstack.fish index 5f5249616c74..b9d4e58f6322 100644 --- a/zkstack_cli/crates/zkstack/completion/zkstack.fish +++ b/zkstack_cli/crates/zkstack/completion/zkstack.fish @@ -44,7 +44,11 @@ complete -c zkstack -n "__fish_zkstack_needs_command" -f -a "consensus" -d 'Cons complete -c zkstack -n "__fish_zkstack_needs_command" -f -a "update" -d 'Update ZKsync' complete -c zkstack -n "__fish_zkstack_needs_command" -f -a "markdown" -d 'Print markdown help' complete -c zkstack -n "__fish_zkstack_needs_command" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' -complete -c zkstack -n "__fish_zkstack_using_subcommand autocomplete" -l generate -d 'The shell to generate the autocomplete script for' -r -f -a "{bash\t'',elvish\t'',fish\t'',powershell\t'',zsh\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand autocomplete" -l generate -d 'The shell to generate the autocomplete script for' -r -f -a "bash\t'' +elvish\t'' +fish\t'' +powershell\t'' +zsh\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand autocomplete" -s o -l out -d 'The out directory to write the autocomplete script to' -r -F complete -c zkstack -n "__fish_zkstack_using_subcommand autocomplete" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand autocomplete" -s v -l verbose -d 'Verbose mode' @@ -61,22 +65,35 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and not __fis complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and not __fish_seen_subcommand_from create build-transactions init change-default-chain setup-observability help" -f -a "setup-observability" -d 'Setup observability for the ecosystem, downloading Grafana dashboards from the era-observability repo' complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and not __fish_seen_subcommand_from create build-transactions init change-default-chain setup-observability help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l ecosystem-name -r -complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l l1-network -d 'L1 Network' -r -f -a "{localhost\t'',sepolia\t'',holesky\t'',mainnet\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l l1-network -d 'L1 Network' -r -f -a "localhost\t'' +sepolia\t'' +holesky\t'' +mainnet\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l link-to-code -d 'Code link' -r -f -a "(__fish_complete_directories)" complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l chain-name -r complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l chain-id -d 'Chain ID' -r -complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l prover-mode -d 'Prover options' -r -f -a "{no-proofs\t'',gpu\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l wallet-creation -d 'Wallet options' -r -f -a "{localhost\t'Load wallets from localhost mnemonic, they are funded for localhost env',random\t'Generate random wallets',empty\t'Generate placeholder wallets',in-file\t'Specify file with wallets'}" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l prover-mode -d 'Prover options' -r -f -a "no-proofs\t'' +gpu\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l wallet-creation -d 'Wallet options' -r -f -a "localhost\t'Load wallets from localhost mnemonic, they are funded for localhost env' +random\t'Generate random wallets' +empty\t'Generate placeholder wallets' +in-file\t'Specify file with wallets'" complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l wallet-path -d 'Wallet path' -r -F -complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l l1-batch-commit-data-generator-mode -d 'Commit data generation mode' -r -f -a "{rollup\t'',validium\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l l1-batch-commit-data-generator-mode -d 'Commit data generation mode' -r -f -a "rollup\t'' +validium\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l base-token-address -d 'Base token address' -r complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l base-token-price-nominator -d 'Base token nominator' -r complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l base-token-price-denominator -d 'Base token denominator' -r -complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l set-as-default -d 'Set as default chain' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l evm-emulator -d 'Enable EVM emulator' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l update-submodules -d 'Whether to update git submodules of repo' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l start-containers -d 'Start reth and postgres containers after creation' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l update-submodules -r -f -a "{true\t'',false\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l set-as-default -d 'Set as default chain' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l evm-emulator -d 'Enable EVM emulator' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l update-submodules -d 'Whether to update git submodules of repo' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l start-containers -d 'Start reth and postgres containers after creation' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l update-submodules -r -f -a "true\t'' +false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -l legacy-bridge complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from create" -s v -l verbose -d 'Verbose mode' @@ -85,8 +102,12 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_se complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -l sender -d 'Address of the transaction sender' -r complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -l l1-rpc-url -d 'L1 RPC URL' -r complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -s o -l out -d 'Output directory for the generated files' -r -F -complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -l verify -d 'Verify deployed contracts' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -l verifier -d 'Verifier to use' -r -f -a "{etherscan\t'',sourcify\t'',blockscout\t'',oklink\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -l verify -d 'Verify deployed contracts' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -l verifier -d 'Verifier to use' -r -f -a "etherscan\t'' +sourcify\t'' +blockscout\t'' +oklink\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -l verifier-url -d 'Verifier URL, if using a custom provider' -r complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -l verifier-api-key -d 'Verifier API key' -r complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r @@ -95,20 +116,29 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_se complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l deploy-erc20 -d 'Deploy ERC20 contracts' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l deploy-ecosystem -d 'Deploy ecosystem contracts' -r -f -a "{true\t'',false\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l deploy-erc20 -d 'Deploy ERC20 contracts' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l deploy-ecosystem -d 'Deploy ecosystem contracts' -r -f -a "true\t'' +false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l ecosystem-contracts-path -d 'Path to ecosystem contracts' -r -F complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l l1-rpc-url -d 'L1 RPC URL' -r -complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l verify -d 'Verify deployed contracts' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l verifier -d 'Verifier to use' -r -f -a "{etherscan\t'',sourcify\t'',blockscout\t'',oklink\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l verify -d 'Verify deployed contracts' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l verifier -d 'Verifier to use' -r -f -a "etherscan\t'' +sourcify\t'' +blockscout\t'' +oklink\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l verifier-url -d 'Verifier URL, if using a custom provider' -r complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l verifier-api-key -d 'Verifier API key' -r complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r -complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l deploy-paymaster -d 'Deploy Paymaster contract' -r -f -a "{true\t'',false\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l deploy-paymaster -d 'Deploy Paymaster contract' -r -f -a "true\t'' +false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l server-db-url -d 'Server database url without database name' -r complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l server-db-name -d 'Server database name' -r -complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -s o -l observability -d 'Enable Grafana' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l update-submodules -r -f -a "{true\t'',false\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -s o -l observability -d 'Enable Grafana' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l update-submodules -r -f -a "true\t'' +false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l resume complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -s d -l dont-drop @@ -154,24 +184,36 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_se complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l chain-name -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l chain-id -d 'Chain ID' -r -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l prover-mode -d 'Prover options' -r -f -a "{no-proofs\t'',gpu\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l wallet-creation -d 'Wallet options' -r -f -a "{localhost\t'Load wallets from localhost mnemonic, they are funded for localhost env',random\t'Generate random wallets',empty\t'Generate placeholder wallets',in-file\t'Specify file with wallets'}" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l prover-mode -d 'Prover options' -r -f -a "no-proofs\t'' +gpu\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l wallet-creation -d 'Wallet options' -r -f -a "localhost\t'Load wallets from localhost mnemonic, they are funded for localhost env' +random\t'Generate random wallets' +empty\t'Generate placeholder wallets' +in-file\t'Specify file with wallets'" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l wallet-path -d 'Wallet path' -r -F -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l l1-batch-commit-data-generator-mode -d 'Commit data generation mode' -r -f -a "{rollup\t'',validium\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l l1-batch-commit-data-generator-mode -d 'Commit data generation mode' -r -f -a "rollup\t'' +validium\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l base-token-address -d 'Base token address' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l base-token-price-nominator -d 'Base token nominator' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l base-token-price-denominator -d 'Base token denominator' -r -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l set-as-default -d 'Set as default chain' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l evm-emulator -d 'Enable EVM emulator' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l update-submodules -d 'Whether to update git submodules of repo' -r -f -a "{true\t'',false\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l set-as-default -d 'Set as default chain' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l evm-emulator -d 'Enable EVM emulator' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l update-submodules -d 'Whether to update git submodules of repo' -r -f -a "true\t'' +false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l legacy-bridge complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -s h -l help -d 'Print help (see more with \'--help\')' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from build-transactions" -s o -l out -d 'Output directory for the generated files' -r -F -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from build-transactions" -l verify -d 'Verify deployed contracts' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from build-transactions" -l verifier -d 'Verifier to use' -r -f -a "{etherscan\t'',sourcify\t'',blockscout\t'',oklink\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from build-transactions" -l verify -d 'Verify deployed contracts' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from build-transactions" -l verifier -d 'Verifier to use' -r -f -a "etherscan\t'' +sourcify\t'' +blockscout\t'' +oklink\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from build-transactions" -l verifier-url -d 'Verifier URL, if using a custom provider' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from build-transactions" -l verifier-api-key -d 'Verifier API key' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from build-transactions" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r @@ -181,16 +223,22 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from build-transactions" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from build-transactions" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from build-transactions" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l verify -d 'Verify deployed contracts' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l verifier -d 'Verifier to use' -r -f -a "{etherscan\t'',sourcify\t'',blockscout\t'',oklink\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l verify -d 'Verify deployed contracts' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l verifier -d 'Verifier to use' -r -f -a "etherscan\t'' +sourcify\t'' +blockscout\t'' +oklink\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l verifier-url -d 'Verifier URL, if using a custom provider' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l verifier-api-key -d 'Verifier API key' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l server-db-url -d 'Server database url without database name' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l server-db-name -d 'Server database name' -r -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l deploy-paymaster -r -f -a "{true\t'',false\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l deploy-paymaster -r -f -a "true\t'' +false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l l1-rpc-url -d 'L1 RPC URL' -r -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l update-submodules -r -f -a "{true\t'',false\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l update-submodules -r -f -a "true\t'' +false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l resume complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -s d -l dont-drop @@ -212,8 +260,12 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from genesis" -f -a "init-database" -d 'Initialize databases' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from genesis" -f -a "server" -d 'Runs server genesis' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from genesis" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from register-chain" -l verify -d 'Verify deployed contracts' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from register-chain" -l verifier -d 'Verifier to use' -r -f -a "{etherscan\t'',sourcify\t'',blockscout\t'',oklink\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from register-chain" -l verify -d 'Verify deployed contracts' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from register-chain" -l verifier -d 'Verifier to use' -r -f -a "etherscan\t'' +sourcify\t'' +blockscout\t'' +oklink\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from register-chain" -l verifier-url -d 'Verifier URL, if using a custom provider' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from register-chain" -l verifier-api-key -d 'Verifier API key' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from register-chain" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r @@ -222,8 +274,12 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from register-chain" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from register-chain" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from register-chain" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-l2-contracts" -l verify -d 'Verify deployed contracts' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-l2-contracts" -l verifier -d 'Verifier to use' -r -f -a "{etherscan\t'',sourcify\t'',blockscout\t'',oklink\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-l2-contracts" -l verify -d 'Verify deployed contracts' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-l2-contracts" -l verifier -d 'Verifier to use' -r -f -a "etherscan\t'' +sourcify\t'' +blockscout\t'' +oklink\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-l2-contracts" -l verifier-url -d 'Verifier URL, if using a custom provider' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-l2-contracts" -l verifier-api-key -d 'Verifier API key' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-l2-contracts" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r @@ -232,8 +288,12 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-l2-contracts" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-l2-contracts" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-l2-contracts" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from accept-chain-ownership" -l verify -d 'Verify deployed contracts' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from accept-chain-ownership" -l verifier -d 'Verifier to use' -r -f -a "{etherscan\t'',sourcify\t'',blockscout\t'',oklink\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from accept-chain-ownership" -l verify -d 'Verify deployed contracts' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from accept-chain-ownership" -l verifier -d 'Verifier to use' -r -f -a "etherscan\t'' +sourcify\t'' +blockscout\t'' +oklink\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from accept-chain-ownership" -l verifier-url -d 'Verifier URL, if using a custom provider' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from accept-chain-ownership" -l verifier-api-key -d 'Verifier API key' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from accept-chain-ownership" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r @@ -242,8 +302,12 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from accept-chain-ownership" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from accept-chain-ownership" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from accept-chain-ownership" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -l verify -d 'Verify deployed contracts' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -l verifier -d 'Verifier to use' -r -f -a "{etherscan\t'',sourcify\t'',blockscout\t'',oklink\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -l verify -d 'Verify deployed contracts' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -l verifier -d 'Verifier to use' -r -f -a "etherscan\t'' +sourcify\t'' +blockscout\t'' +oklink\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -l verifier-url -d 'Verifier URL, if using a custom provider' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -l verifier-api-key -d 'Verifier API key' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r @@ -252,8 +316,12 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-consensus-registry" -l verify -d 'Verify deployed contracts' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-consensus-registry" -l verifier -d 'Verifier to use' -r -f -a "{etherscan\t'',sourcify\t'',blockscout\t'',oklink\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-consensus-registry" -l verify -d 'Verify deployed contracts' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-consensus-registry" -l verifier -d 'Verifier to use' -r -f -a "etherscan\t'' +sourcify\t'' +blockscout\t'' +oklink\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-consensus-registry" -l verifier-url -d 'Verifier URL, if using a custom provider' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-consensus-registry" -l verifier-api-key -d 'Verifier API key' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-consensus-registry" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r @@ -262,8 +330,12 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-consensus-registry" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-consensus-registry" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-consensus-registry" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-multicall3" -l verify -d 'Verify deployed contracts' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-multicall3" -l verifier -d 'Verifier to use' -r -f -a "{etherscan\t'',sourcify\t'',blockscout\t'',oklink\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-multicall3" -l verify -d 'Verify deployed contracts' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-multicall3" -l verifier -d 'Verifier to use' -r -f -a "etherscan\t'' +sourcify\t'' +blockscout\t'' +oklink\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-multicall3" -l verifier-url -d 'Verifier URL, if using a custom provider' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-multicall3" -l verifier-api-key -d 'Verifier API key' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-multicall3" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r @@ -272,8 +344,12 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-multicall3" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-multicall3" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-multicall3" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-timestamp-asserter" -l verify -d 'Verify deployed contracts' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-timestamp-asserter" -l verifier -d 'Verifier to use' -r -f -a "{etherscan\t'',sourcify\t'',blockscout\t'',oklink\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-timestamp-asserter" -l verify -d 'Verify deployed contracts' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-timestamp-asserter" -l verifier -d 'Verifier to use' -r -f -a "etherscan\t'' +sourcify\t'' +blockscout\t'' +oklink\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-timestamp-asserter" -l verifier-url -d 'Verifier URL, if using a custom provider' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-timestamp-asserter" -l verifier-api-key -d 'Verifier API key' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-timestamp-asserter" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r @@ -282,8 +358,12 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-timestamp-asserter" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-timestamp-asserter" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-timestamp-asserter" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-upgrader" -l verify -d 'Verify deployed contracts' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-upgrader" -l verifier -d 'Verifier to use' -r -f -a "{etherscan\t'',sourcify\t'',blockscout\t'',oklink\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-upgrader" -l verify -d 'Verify deployed contracts' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-upgrader" -l verifier -d 'Verifier to use' -r -f -a "etherscan\t'' +sourcify\t'' +blockscout\t'' +oklink\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-upgrader" -l verifier-url -d 'Verifier URL, if using a custom provider' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-upgrader" -l verifier-api-key -d 'Verifier API key' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-upgrader" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r @@ -292,8 +372,12 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-upgrader" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-upgrader" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-upgrader" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-paymaster" -l verify -d 'Verify deployed contracts' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-paymaster" -l verifier -d 'Verifier to use' -r -f -a "{etherscan\t'',sourcify\t'',blockscout\t'',oklink\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-paymaster" -l verify -d 'Verify deployed contracts' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-paymaster" -l verifier -d 'Verifier to use' -r -f -a "etherscan\t'' +sourcify\t'' +blockscout\t'' +oklink\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-paymaster" -l verifier-url -d 'Verifier URL, if using a custom provider' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-paymaster" -l verifier-api-key -d 'Verifier API key' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-paymaster" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r @@ -302,8 +386,12 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-paymaster" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-paymaster" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-paymaster" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from update-token-multiplier-setter" -l verify -d 'Verify deployed contracts' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from update-token-multiplier-setter" -l verifier -d 'Verifier to use' -r -f -a "{etherscan\t'',sourcify\t'',blockscout\t'',oklink\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from update-token-multiplier-setter" -l verify -d 'Verify deployed contracts' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from update-token-multiplier-setter" -l verifier -d 'Verifier to use' -r -f -a "etherscan\t'' +sourcify\t'' +blockscout\t'' +oklink\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from update-token-multiplier-setter" -l verifier-url -d 'Verifier URL, if using a custom provider' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from update-token-multiplier-setter" -l verifier-api-key -d 'Verifier API key' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from update-token-multiplier-setter" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r @@ -312,8 +400,12 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from update-token-multiplier-setter" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from update-token-multiplier-setter" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from update-token-multiplier-setter" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from enable-evm-emulator" -l verify -d 'Verify deployed contracts' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from enable-evm-emulator" -l verifier -d 'Verifier to use' -r -f -a "{etherscan\t'',sourcify\t'',blockscout\t'',oklink\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from enable-evm-emulator" -l verify -d 'Verify deployed contracts' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from enable-evm-emulator" -l verifier -d 'Verifier to use' -r -f -a "etherscan\t'' +sourcify\t'' +blockscout\t'' +oklink\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from enable-evm-emulator" -l verifier-url -d 'Verifier URL, if using a custom provider' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from enable-evm-emulator" -l verifier-api-key -d 'Verifier API key' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from enable-evm-emulator" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r @@ -397,7 +489,14 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_sub complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from snapshot" -s h -l help -d 'Print help' complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from snapshot" -f -a "create" complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from snapshot" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' -complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from lint" -s t -l targets -r -f -a "{md\t'',sol\t'',js\t'',ts\t'',rs\t'',contracts\t'',autocompletion\t'',rust-toolchain\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from lint" -s t -l targets -r -f -a "md\t'' +sol\t'' +js\t'' +ts\t'' +rs\t'' +contracts\t'' +autocompletion\t'' +rust-toolchain\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from lint" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from lint" -s c -l check complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from lint" -s v -l verbose -d 'Verbose mode' @@ -420,9 +519,12 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_sub complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from prover" -f -a "insert-batch" complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from prover" -f -a "insert-version" complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from prover" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' -complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from contracts" -l l1-contracts -d 'Build L1 contracts' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from contracts" -l l2-contracts -d 'Build L2 contracts' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from contracts" -l system-contracts -d 'Build system contracts' -r -f -a "{true\t'',false\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from contracts" -l l1-contracts -d 'Build L1 contracts' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from contracts" -l l2-contracts -d 'Build L2 contracts' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from contracts" -l system-contracts -d 'Build system contracts' -r -f -a "true\t'' +false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from contracts" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from contracts" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from contracts" -l ignore-prerequisites -d 'Ignores prerequisites checks' @@ -480,7 +582,8 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_ complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l bucket-name -r complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l location -r complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l project-id -r -complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l shall-save-to-public-bucket -r -f -a "{true\t'',false\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l shall-save-to-public-bucket -r -f -a "true\t'' +false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l public-store-dir -r complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l public-bucket-base-url -r complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l public-credentials-file -r @@ -488,41 +591,69 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_ complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l public-location -r complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l public-project-id -r complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l bellman-cuda-dir -r -complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l bellman-cuda -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l setup-compressor-key -r -f -a "{true\t'',false\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l bellman-cuda -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l setup-compressor-key -r -f -a "true\t'' +false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l plonk-path -r complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l fflonk-path -r -complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l compressor-type -r -f -a "{fflonk\t'',plonk\t'',all\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l region -r -f -a "{us\t'',europe\t'',asia\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l mode -r -f -a "{download\t'',generate\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l setup-keys -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l setup-database -r -f -a "{true\t'',false\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l compressor-type -r -f -a "fflonk\t'' +plonk\t'' +all\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l region -r -f -a "us\t'' +europe\t'' +asia\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l mode -r -f -a "download\t'' +generate\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l setup-keys -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l setup-database -r -f -a "true\t'' +false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l prover-db-url -d 'Prover database url without database name' -r complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l prover-db-name -d 'Prover database name' -r -complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -s u -l use-default -d 'Use default database urls and names' -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -s d -l dont-drop -r -f -a "{true\t'',false\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l cloud-type -r -f -a "{gcp\t'',local\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -s u -l use-default -d 'Use default database urls and names' -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -s d -l dont-drop -r -f -a "true\t'' +false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l cloud-type -r -f -a "gcp\t'' +local\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l dev complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l clone complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init" -s h -l help -d 'Print help' -complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from setup-keys" -l region -r -f -a "{us\t'',europe\t'',asia\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from setup-keys" -l mode -r -f -a "{download\t'',generate\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from setup-keys" -l region -r -f -a "us\t'' +europe\t'' +asia\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from setup-keys" -l mode -r -f -a "download\t'' +generate\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from setup-keys" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from setup-keys" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from setup-keys" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from setup-keys" -s h -l help -d 'Print help' -complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from run" -l component -r -f -a "{gateway\t'',witness-generator\t'',witness-vector-generator\t'',prover\t'',circuit-prover\t'',compressor\t'',prover-job-monitor\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from run" -l round -r -f -a "{all-rounds\t'',basic-circuits\t'',leaf-aggregation\t'',node-aggregation\t'',recursion-tip\t'',scheduler\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from run" -l component -r -f -a "gateway\t'' +witness-generator\t'' +witness-vector-generator\t'' +prover\t'' +circuit-prover\t'' +compressor\t'' +prover-job-monitor\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from run" -l round -r -f -a "all-rounds\t'' +basic-circuits\t'' +leaf-aggregation\t'' +node-aggregation\t'' +recursion-tip\t'' +scheduler\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from run" -l threads -r complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from run" -l max-allocation -d 'Memory allocation limit in bytes (for prover component)' -r complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from run" -s l -l light-wvg-count -r complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from run" -s h -l heavy-wvg-count -r complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from run" -s m -l max-allocation -r -complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from run" -l mode -r -f -a "{fflonk\t'',plonk\t''}" -complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from run" -l docker -r -f -a "{true\t'',false\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from run" -l mode -r -f -a "fflonk\t'' +plonk\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from run" -l docker -r -f -a "true\t'' +false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from run" -l tag -r complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from run" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from run" -s v -l verbose -d 'Verbose mode' @@ -536,7 +667,9 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_ complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from init-bellman-cuda" -s h -l help -d 'Print help' complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from compressor-keys" -l plonk-path -r complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from compressor-keys" -l fflonk-path -r -complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from compressor-keys" -l compressor-type -r -f -a "{fflonk\t'',plonk\t'',all\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from compressor-keys" -l compressor-type -r -f -a "fflonk\t'' +plonk\t'' +all\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from compressor-keys" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from compressor-keys" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand prover; and __fish_seen_subcommand_from compressor-keys" -l ignore-prerequisites -d 'Ignores prerequisites checks' @@ -609,7 +742,8 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fis complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fish_seen_subcommand_from build" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fish_seen_subcommand_from build" -s h -l help -d 'Print help' complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fish_seen_subcommand_from run" -l components -d 'Components of server to run' -r -complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fish_seen_subcommand_from run" -l enable-consensus -d 'Enable consensus' -r -f -a "{true\t'',false\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fish_seen_subcommand_from run" -l enable-consensus -d 'Enable consensus' -r -f -a "true\t'' +false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fish_seen_subcommand_from run" -s a -l additional-args -d 'Additional arguments that can be passed through the CLI' -r complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fish_seen_subcommand_from run" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fish_seen_subcommand_from run" -l reinit @@ -628,7 +762,8 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fis complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fish_seen_subcommand_from help" -f -a "run" -d 'Run external node' complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fish_seen_subcommand_from help" -f -a "wait" -d 'Wait for external node to start' complete -c zkstack -n "__fish_zkstack_using_subcommand external-node; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' -complete -c zkstack -n "__fish_zkstack_using_subcommand containers" -s o -l observability -d 'Enable Grafana' -r -f -a "{true\t'',false\t''}" +complete -c zkstack -n "__fish_zkstack_using_subcommand containers" -s o -l observability -d 'Enable Grafana' -r -f -a "true\t'' +false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand containers" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand containers" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand containers" -l ignore-prerequisites -d 'Ignores prerequisites checks' From 361243f3f15e01cf1f3e49b73a579cb962cf0124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Grze=C5=9Bkiewicz?= Date: Fri, 10 Jan 2025 19:45:35 +0100 Subject: [PATCH 17/97] feat(en): make documentation more chain agnostic (#3376) --- core/lib/snapshots_applier/README.md | 2 +- core/node/consensus/src/en.rs | 2 +- core/node/db_pruner/README.md | 2 +- core/node/node_storage_init/README.md | 2 +- docs/src/announcements/attester_commitee.md | 6 +- .../guides/external-node/00_quick_start.md | 17 ++- docs/src/guides/external-node/01_intro.md | 119 +++++++++--------- .../guides/external-node/02_configuration.md | 28 ++--- docs/src/guides/external-node/03_running.md | 27 ++-- .../guides/external-node/04_observability.md | 10 +- .../external-node/05_troubleshooting.md | 14 +-- .../src/guides/external-node/06_components.md | 73 ++++++----- .../external-node/07_snapshots_recovery.md | 10 +- docs/src/guides/external-node/08_pruning.md | 7 +- .../guides/external-node/09_treeless_mode.md | 10 +- .../external-node/10_decentralization.md | 10 +- .../11_setup_for_other_chains.md | 50 ++++++++ 17 files changed, 224 insertions(+), 165 deletions(-) create mode 100644 docs/src/guides/external-node/11_setup_for_other_chains.md diff --git a/core/lib/snapshots_applier/README.md b/core/lib/snapshots_applier/README.md index 60f17344f5b1..9ec934cbce35 100644 --- a/core/lib/snapshots_applier/README.md +++ b/core/lib/snapshots_applier/README.md @@ -4,7 +4,7 @@ Library responsible for recovering Postgres from a protocol-level snapshot. ## Recovery workflow -_(See [node docs](../../../docs/guides/external-node/07_snapshots_recovery.md) for a high-level snapshot recovery +_(See [node docs](../../../docs/src/guides/external-node/07_snapshots_recovery.md) for a high-level snapshot recovery overview and [snapshot creator docs](../../bin/snapshots_creator/README.md) for the snapshot format details)_ 1. Recovery is started by querying the main node and determining the snapshot parameters. By default, recovery is diff --git a/core/node/consensus/src/en.rs b/core/node/consensus/src/en.rs index e417b68cf2cb..2bddc3280362 100644 --- a/core/node/consensus/src/en.rs +++ b/core/node/consensus/src/en.rs @@ -178,7 +178,7 @@ impl EN { tracing::warn!("\ WARNING: this node is using ZKsync API synchronization, which will be deprecated soon. \ Please follow this instruction to switch to p2p synchronization: \ - https://github.com/matter-labs/zksync-era/blob/main/docs/guides/external-node/10_decentralization.md"); + https://github.com/matter-labs/zksync-era/blob/main/docs/src/guides/external-node/10_decentralization.md"); let res: ctx::Result<()> = scope::run!(ctx, |ctx, s| async { // Update sync state in the background. s.spawn_bg(self.fetch_state_loop(ctx)); diff --git a/core/node/db_pruner/README.md b/core/node/db_pruner/README.md index ee1317d01e46..b1528bd0ade0 100644 --- a/core/node/db_pruner/README.md +++ b/core/node/db_pruner/README.md @@ -10,7 +10,7 @@ There are two types of objects that are not fully cleaned: ## Pruning workflow -_(See [node docs](../../../docs/guides/external-node/08_pruning.md) for a high-level pruning overview)_ +_(See [node docs](../../../docs/src/guides/external-node/08_pruning.md) for a high-level pruning overview)_ There are two phases of pruning an L1 batch, soft pruning and hard pruning. Every batch that would have its records removed if first _soft-pruned_. Soft-pruned batches cannot safely be used. One minute (this is configurable) after soft diff --git a/core/node/node_storage_init/README.md b/core/node/node_storage_init/README.md index e1b6768878ec..39f7d6fc1205 100644 --- a/core/node/node_storage_init/README.md +++ b/core/node/node_storage_init/README.md @@ -1,5 +1,5 @@ # `zksync_node_storage_init` -A set of actions to ensure that any ZKsync node has initialized storage and can start running. +A set of actions to ensure that any Node has initialized storage and can start running. This includes genesis, but not limited to it, and may involve other steps. diff --git a/docs/src/announcements/attester_commitee.md b/docs/src/announcements/attester_commitee.md index 148e51a4f976..d4205ee52aa9 100644 --- a/docs/src/announcements/attester_commitee.md +++ b/docs/src/announcements/attester_commitee.md @@ -2,8 +2,8 @@ ## Overview -The Attester committee is a subset of ZKSync nodes. After each l1 batch execution, participating nodes sign its -execution result and send back to the network. +The Attester committee is a subset of Nodes. After each l1 batch execution, participating nodes sign its execution +result and send back to the network. The ultimate goal is to make L1 commit operation contingent on such signatures. This will improve the security and finality guarantees: having these signatures on L1 shows that additional actors executed the corresponding blocks - and @@ -36,7 +36,7 @@ Participants can leave the committee at any time. The only action that is required to participate is to share your attester public key with the Main Node operator (by opening an issue in this repo or using any other communication channel). You can find it in the comment in the `consensus_secrets.yaml` file (that was - in most cases - generated by the tool described -[here](https://github.com/matter-labs/zksync-era/blob/main/docs/guides/external-node/10_decentralization.md#generating-secrets)) +[here](https://github.com/matter-labs/zksync-era/blob/main/docs/src/guides/external-node/10_decentralization.md#generating-secrets)) > [!WARNING] > diff --git a/docs/src/guides/external-node/00_quick_start.md b/docs/src/guides/external-node/00_quick_start.md index 07e52085cf4f..547d65fcbc0c 100644 --- a/docs/src/guides/external-node/00_quick_start.md +++ b/docs/src/guides/external-node/00_quick_start.md @@ -6,7 +6,15 @@ Install `docker compose` and `Docker` ## Running ZKsync node locally -These commands start ZKsync node locally inside docker. +These commands start ZKsync Node locally inside docker. + +For adjusting the Dockerfiles to use them with other chains setup using ZK Stack, see +[setup_for_other_chains](11_setup_for_other_chains.md) + +> [!NOTE] +> +> If you want to run Node for a chain different than ZKsync ERA, you can ask the company hosting the chains for the +> ready docker-compose files. To start a mainnet instance, run: @@ -58,11 +66,16 @@ The HTTP JSON-RPC API can be accessed on port `3060` and WebSocket API can be ac > setup). > > For requirements for nodes running from DB dump see the [running](03_running.md) section. DB dumps are a way to start -> ZKsync node with full historical transactions history. +> Node with full historical transactions history. > > For nodes with pruning disabled, expect the storage requirements on mainnet to grow at 1TB per month. If you want to > stop historical DB pruning you can read more about this in the [pruning](08_pruning.md) section. +> [!NOTE] +> +> For chains other than ZKSync Era, the system requirements can be slightly lower (CPU and RAM) or even much lower +> (storage), depending on the chain. + - 32 GB of RAM and a relatively modern CPU - 50 GB of storage for testnet nodes - 500 GB of storage for mainnet nodes diff --git a/docs/src/guides/external-node/01_intro.md b/docs/src/guides/external-node/01_intro.md index 10fc55acac21..b5842e160b6c 100644 --- a/docs/src/guides/external-node/01_intro.md +++ b/docs/src/guides/external-node/01_intro.md @@ -1,39 +1,36 @@ -# ZkSync Node Documentation +# Node Documentation -This documentation explains the basics of the ZKsync Node. +The Node (sometimes referred to as External Node or EN) is a read-only replica of the main node. -## Disclaimers +## What is the Node -- The ZKsync node is in the alpha phase, and should be used with caution. -- The ZKsync node is a read-only replica of the main node. +The Node is a read-replica of the main (centralized) node that can be run by external parties. It functions by receiving +blocks from the given ZK Stack chain and re-applying transactions locally, starting from the genesis block. The Node +shares most of its codebase with the main node. Consequently, when it re-applies transactions, it does so exactly as the +main node did in the past. -## What is the ZKsync node +**It has three modes of initialization:** -The ZKsync node is a read-replica of the main (centralized) node that can be run by external parties. It functions by -receiving blocks from the ZKsync network and re-applying transactions locally, starting from the genesis block. The -ZKsync node shares most of its codebase with the main node. Consequently, when it re-applies transactions, it does so -exactly as the main node did in the past. - -**It has two modes of initialization:** - -- recovery from a DB dump, in Ethereum terms this corresponds to archival node +- recovery from genesis (Not supported on ZKsync Era), in Ethereum terms this corresponds to archival node, this option + is slower than recovery from DB dump, but is the easiest way to spin up new Node. +- recovery from a DB dump, in Ethereum terms this corresponds to archival node. - recovery from a snapshot, in Ethereum terms this corresponds to light node, such nodes will only have access to transactions data from after the node was initialized. The database can be pruned on such nodes. ## High-level overview -At a high level, the ZKsync node can be seen as an application that has the following modules: +At a high level, the Node can be seen as an application that has the following modules: - API server that provides the publicly available Web3 interface. -- Consensus layer that interacts with the peer network and retrieves transactions and blocks to re-execute. +- Consensus layer (ZKsync Era only for now) that interacts with the peer network and retrieves transactions and blocks + to re-execute. - Sequencer component that actually executes and persists transactions received from the synchronization layer. -- Several checker modules that ensure the consistency of the ZKsync node state. +- Several checker modules that ensure the consistency of the Node state. With the EN, you are able to: -- Locally recreate and verify the ZKsync Era mainnet/testnet state. -- Interact with the recreated state in a trustless way (in a sense that the validity is locally verified, and you should - not rely on a third-party API ZKsync Era provides). +- Locally recreate and verify a ZK Stack chain's, for example ZKsync Era's mainnet/testnet state. +- Interact with the recreated state in a trustless way. The validity is locally verified. - Use the Web3 API without having to query the main node. - Send L2 transactions (that will be proxied to the main node). @@ -43,12 +40,12 @@ With the EN, you _can not_: - Generate proofs. - Submit data to L1. -A more detailed overview of the EN's components is provided in the [components](06_components.md) section. +A more detailed overview of the Node's components is provided in the [components](06_components.md) section. ## API overview -API exposed by the ZKsync node strives to be Web3-compliant. If some method is exposed but behaves differently compared -to Ethereum, it should be considered a bug. Please [report][contact_us] such cases. +API exposed by the Node strives to be Web3-compliant. If some method is exposed but behaves differently compared to +Ethereum, it should be considered a bug. Please [report][contact_us] such cases. [contact_us]: https://zksync.io/contact @@ -58,42 +55,42 @@ Data getters in this namespace operate in the L2 space: require/return L2 block Available methods: -| Method | Notes | -| ----------------------------------------- | ---------------------------------------------------------------------------------- | -| `eth_blockNumber` | | -| `eth_chainId` | | -| `eth_call` | | -| `eth_estimateGas` | | -| `eth_gasPrice` | | -| `eth_newFilter` | Maximum amount of installed filters is configurable | -| `eth_newBlockFilter` | Same as above | -| `eth_newPendingTransactionsFilter` | Same as above | -| `eth_uninstallFilter` | | -| `eth_getLogs` | Maximum amount of returned entities can be configured | -| `eth_getFilterLogs` | Same as above | -| `eth_getFilterChanges` | Same as above | -| `eth_getBalance` | | -| `eth_getBlockByNumber` | | -| `eth_getBlockByHash` | | -| `eth_getBlockTransactionCountByNumber` | | -| `eth_getBlockTransactionCountByHash` | | -| `eth_getCode` | | -| `eth_getStorageAt` | | -| `eth_getTransactionCount` | | -| `eth_getTransactionByHash` | | -| `eth_getTransactionByBlockHashAndIndex` | | -| `eth_getTransactionByBlockNumberAndIndex` | | -| `eth_getTransactionReceipt` | | -| `eth_protocolVersion` | | -| `eth_sendRawTransaction` | | -| `eth_syncing` | ZKsync node is considered synced if it's less than 11 blocks behind the main node. | -| `eth_coinbase` | Always returns a zero address | -| `eth_accounts` | Always returns an empty list | -| `eth_getCompilers` | Always returns an empty list | -| `eth_hashrate` | Always returns zero | -| `eth_getUncleCountByBlockHash` | Always returns zero | -| `eth_getUncleCountByBlockNumber` | Always returns zero | -| `eth_mining` | Always returns false | +| Method | Notes | +| ----------------------------------------- | --------------------------------------------------------------------------- | +| `eth_blockNumber` | | +| `eth_chainId` | | +| `eth_call` | | +| `eth_estimateGas` | | +| `eth_gasPrice` | | +| `eth_newFilter` | Maximum amount of installed filters is configurable | +| `eth_newBlockFilter` | Same as above | +| `eth_newPendingTransactionsFilter` | Same as above | +| `eth_uninstallFilter` | | +| `eth_getLogs` | Maximum amount of returned entities can be configured | +| `eth_getFilterLogs` | Same as above | +| `eth_getFilterChanges` | Same as above | +| `eth_getBalance` | | +| `eth_getBlockByNumber` | | +| `eth_getBlockByHash` | | +| `eth_getBlockTransactionCountByNumber` | | +| `eth_getBlockTransactionCountByHash` | | +| `eth_getCode` | | +| `eth_getStorageAt` | | +| `eth_getTransactionCount` | | +| `eth_getTransactionByHash` | | +| `eth_getTransactionByBlockHashAndIndex` | | +| `eth_getTransactionByBlockNumberAndIndex` | | +| `eth_getTransactionReceipt` | | +| `eth_protocolVersion` | | +| `eth_sendRawTransaction` | | +| `eth_syncing` | Node is considered synced if it's less than 11 blocks behind the main node. | +| `eth_coinbase` | Always returns a zero address | +| `eth_accounts` | Always returns an empty list | +| `eth_getCompilers` | Always returns an empty list | +| `eth_hashrate` | Always returns zero | +| `eth_getUncleCountByBlockHash` | Always returns zero | +| `eth_getUncleCountByBlockNumber` | Always returns zero | +| `eth_mining` | Always returns false | ### PubSub @@ -153,5 +150,5 @@ Always refer to the documentation linked above to see the list of stabilized met ### `en` namespace -This namespace contains methods that ZKsync nodes call on the main node while syncing. If this namespace is enabled, -other ENs can sync from this node. +This namespace contains methods that Nodes call on the main node while syncing. If this namespace is enabled, other ENs +can sync from this node. diff --git a/docs/src/guides/external-node/02_configuration.md b/docs/src/guides/external-node/02_configuration.md index 5b8b7512eb3e..90da7c1eea79 100644 --- a/docs/src/guides/external-node/02_configuration.md +++ b/docs/src/guides/external-node/02_configuration.md @@ -1,7 +1,7 @@ -# ZkSync Node Configuration +# Node Configuration -This document outlines various configuration options for the EN. Currently, the ZKsync node requires the definition of -numerous environment variables. To streamline this process, we provide prepared configs for the ZKsync Era - for both +This document outlines various configuration options for the EN. Currently, the Node requires the definition of numerous +environment variables. To streamline this process, we provide prepared configs for the ZKsync Era - for both [mainnet](prepared_configs/mainnet-config.env) and [testnet](prepared_configs/testnet-sepolia-config.env). You can use these files as a starting point and modify only the necessary sections. @@ -10,7 +10,7 @@ default settings.** ## Database -The ZKsync node uses two databases: PostgreSQL and RocksDB. +The Node uses two databases: PostgreSQL and RocksDB. PostgreSQL serves as the main source of truth in the EN, so all the API requests fetch the state from there. The PostgreSQL connection is configured by the `DATABASE_URL`. Additionally, the `DATABASE_POOL_SIZE` variable defines the @@ -22,12 +22,12 @@ recommended to use an NVME SSD for RocksDB. RocksDB requires two variables to be ## L1 Web3 client -ZKsync node requires a connection to an Ethereum node. The corresponding env variable is `EN_ETH_CLIENT_URL`. Make sure -to set the URL corresponding to the correct L1 network (L1 mainnet for L2 mainnet and L1 sepolia for L2 testnet). +Node requires a connection to an Ethereum node. The corresponding env variable is `EN_ETH_CLIENT_URL`. Make sure to set +the URL corresponding to the correct L1 network (L1 mainnet for L2 mainnet and L1 sepolia for L2 testnet). -Note: Currently, the ZKsync node makes 2 requests to the L1 per L1 batch, so the Web3 client usage for a synced node -should not be high. However, during the synchronization phase the new batches would be persisted on the ZKsync node -quickly, so make sure that the L1 client won't exceed any limits (e.g. in case you use Infura). +Note: Currently, the Node makes 2 requests to the L1 per L1 batch, so the Web3 client usage for a synced node should not +be high. However, during the synchronization phase the new batches would be persisted on the Node quickly, so make sure +that the L1 client won't exceed any limits (e.g. in case you use Infura). ## Exposed ports @@ -50,13 +50,13 @@ the metrics, leave this port not configured, and the metrics won't be collected. There are variables that allow you to fine-tune the limits of the RPC servers, such as limits on the number of returned entries or the limit for the accepted transaction size. Provided files contain sane defaults that are recommended for -use, but these can be edited, e.g. to make the ZKsync node more/less restrictive. +use, but these can be edited, e.g. to make the Node more/less restrictive. ## JSON-RPC API namespaces There are 7 total supported API namespaces: `eth`, `net`, `web3`, `debug` - standard ones; `zks` - rollup-specific one; -`pubsub` - a.k.a. `eth_subscribe`; `en` - used by ZKsync nodes while syncing. You can configure what namespaces you want -to enable using `EN_API_NAMESPACES` and specifying namespace names in a comma-separated list. By default, all but the +`pubsub` - a.k.a. `eth_subscribe`; `en` - used by Nodes while syncing. You can configure what namespaces you want to +enable using `EN_API_NAMESPACES` and specifying namespace names in a comma-separated list. By default, all but the `debug` namespace are enabled. ## Logging and observability @@ -64,8 +64,8 @@ to enable using `EN_API_NAMESPACES` and specifying namespace names in a comma-se `MISC_LOG_FORMAT` defines the format in which logs are shown: `plain` corresponds to the human-readable format, while the other option is `json` (recommended for deployments). -`RUST_LOG` variable allows you to set up the logs granularity (e.g. make the ZKsync node emit fewer logs). You can read -about the format [here](https://docs.rs/env_logger/0.10.0/env_logger/#enabling-logging). +`RUST_LOG` variable allows you to set up the logs granularity (e.g. make the Node emit fewer logs). You can read about +the format [here](https://docs.rs/env_logger/0.10.0/env_logger/#enabling-logging). `MISC_SENTRY_URL` and `MISC_OTLP_URL` variables can be configured to set up Sentry and OpenTelemetry exporters. diff --git a/docs/src/guides/external-node/03_running.md b/docs/src/guides/external-node/03_running.md index caa528238aea..c59733825c4d 100644 --- a/docs/src/guides/external-node/03_running.md +++ b/docs/src/guides/external-node/03_running.md @@ -1,8 +1,9 @@ -# Running the ZkSync Node +# Running the Node > [!NOTE] > -> If you want to just run node with recommended default setting, please see the [quick start](00_quick_start.md) page. +> If you want to just run ZKSync node with recommended default setting, please see the [quick start](00_quick_start.md) +> page. This section assumes that you have prepared a configuration file as described on the [previous page](02_configuration.md). @@ -14,12 +15,14 @@ This configuration is approximate and should be considered as **minimal** requir - 32-core CPU - 64GB RAM - SSD storage (NVME recommended): - - Sepolia Testnet - 10GB ZKsync node + 50GB PostgreSQL (at the time of writing, will grow over time, so should be + - ZKsync Sepolia Testnet - 10GB Node + 50GB PostgreSQL (at the time of writing, will grow over time, so should be constantly monitored) - - Mainnet - 3TB ZKsync node + 8TB PostgreSQL (at the time of writing, will grow over time, so should be constantly + - ZKsync Mainnet - 3TB Node + 8TB PostgreSQL (at the time of writing, will grow over time, so should be constantly monitored) - 100 Mbps connection (1 Gbps+ recommended) +For smaller chains, less powerful hardware may be sufficient, especially in terms of disk space. + ## A note about PostgreSQL storage By far, the heaviest table to maintain is the `call_traces` table. This table is only required for the `debug` @@ -36,23 +39,23 @@ it in Docker. There are many of guides on that, [here's one example](https://www.docker.com/blog/how-to-use-the-postgres-docker-official-image/). Note however that if you run PostgresSQL as a stand-alone Docker image (e.g. not in Docker-compose with a network shared -between ZKsync node and Postgres), ZKsync node won't be able to access Postgres via `localhost` or `127.0.0.1` URLs. To -make it work, you'll have to either run it with a `--network host` (on Linux) or use `host.docker.internal` instead of -`localhost` in the ZKsync node configuration ([official docs][host_docker_internal]). +between Node and Postgres), Node won't be able to access Postgres via `localhost` or `127.0.0.1` URLs. To make it work, +you'll have to either run it with a `--network host` (on Linux) or use `host.docker.internal` instead of `localhost` in +the Node configuration ([official docs][host_docker_internal]). Besides running Postgres, you are expected to have a DB dump from a corresponding env. You can restore it using `pg_restore -O -C --dbname=`. You can also refer to -[ZKsync Node configuration management blueprint](https://github.com/matter-labs/zksync-era/blob/main/docs/guides/external-node/00_quick_start.md#advanced-setup) +[Node configuration management blueprint](https://github.com/matter-labs/zksync-era/blob/main/docs/src/guides/external-node/00_quick_start.md#advanced-setup) for advanced DB instance configurations. [host_docker_internal](https://docs.docker.com/desktop/networking/#i-want-to-connect-from-a-container-to-a-service-on-the-host) ## Running -Assuming you have the ZKsync node Docker image, an env file with the prepared configuration, and you have restored your -DB with the pg dump, that is all you need. +Assuming you have the Node Docker image, an env file with the prepared configuration, and you have restored your DB with +the pg dump, that is all you need. Sample running command: @@ -69,9 +72,9 @@ in RocksDB (mainly the Merkle tree) is absent. Before the node can make any prog RocksDB and verify consistency. The exact time required for that depends on the hardware configuration, but it is reasonable to expect the state rebuild on the mainnet to take more than 20 hours. -## Redeploying the ZKsync node with a new PG dump +## Redeploying the Node with a new PG dump -If you've been running the ZKsync node for some time and are going to redeploy it using a new PG dump, you should +If you've been running the Node for some time and are going to redeploy it using a new PG dump, you should - Stop the EN - Remove SK cache (corresponding to `EN_STATE_CACHE_PATH`) diff --git a/docs/src/guides/external-node/04_observability.md b/docs/src/guides/external-node/04_observability.md index 05b39b74c5d2..cdf0f9dd25f8 100644 --- a/docs/src/guides/external-node/04_observability.md +++ b/docs/src/guides/external-node/04_observability.md @@ -1,6 +1,6 @@ -# ZKsync node Observability +# Node Observability -The ZKsync node provides several options for setting up observability. Configuring logs and sentry is described in the +The Node provides several options for setting up observability. Configuring logs and sentry is described in the [configuration](02_configuration.md) section, so this section focuses on the exposed metrics. This section is written with the assumption that you're familiar with @@ -16,8 +16,8 @@ By default, latency histograms are distributed in the following buckets (in seco ## Metrics -ZKsync node exposes a lot of metrics, a significant amount of which aren't interesting outside the development flow. -This section's purpose is to highlight metrics that may be worth observing in the external setup. +Node exposes a lot of metrics, a significant amount of which aren't interesting outside the development flow. This +section's purpose is to highlight metrics that may be worth observing in the external setup. If you are not planning to scrape Prometheus metrics, please unset `EN_PROMETHEUS_PORT` environment variable to prevent memory leaking. @@ -25,7 +25,7 @@ memory leaking. | Metric name | Type | Labels | Description | | ---------------------------------------------- | --------- | ------------------------------------- | ------------------------------------------------------------------ | | `external_node_synced` | Gauge | - | 1 if synced, 0 otherwise. Matches `eth_call` behavior | -| `external_node_sync_lag` | Gauge | - | How many blocks behind the main node the ZKsync node is | +| `external_node_sync_lag` | Gauge | - | How many blocks behind the main node the Node is | | `external_node_fetcher_requests` | Histogram | `stage`, `actor` | Duration of requests performed by the different fetcher components | | `external_node_fetcher_cache_requests` | Histogram | - | Duration of requests performed by the fetcher cache layer | | `external_node_fetcher_miniblock` | Gauge | `status` | The number of the last L2 block update fetched from the main node | diff --git a/docs/src/guides/external-node/05_troubleshooting.md b/docs/src/guides/external-node/05_troubleshooting.md index 43d6ae26b135..49369897143f 100644 --- a/docs/src/guides/external-node/05_troubleshooting.md +++ b/docs/src/guides/external-node/05_troubleshooting.md @@ -1,8 +1,8 @@ -# ZKsync node Troubleshooting +# Node Troubleshooting -The ZKsync node tries to follow the fail-fast principle: if an anomaly is discovered, instead of attempting state -recovery, in most cases it will restart. Most of the time it will manifest as crashes, and if it happens once, it -shouldn't be treated as a problem. +The Node tries to follow the fail-fast principle: if an anomaly is discovered, instead of attempting state recovery, in +most cases it will restart. Most of the time it will manifest as crashes, and if it happens once, it shouldn't be +treated as a problem. However, if the node enters the crash loop or otherwise behaves unexpectedly, it may indicate either a bug in the implementation or a problem with configuration. This section tries to cover common problems. @@ -24,8 +24,8 @@ Other kinds of panic aren't normally expected. While in most cases, the state wi ## Genesis Issues -The ZKsync node is supposed to start with an applied DB dump. If you see any genesis-related errors, it probably means -the ZKsync node was started without an applied dump. +The Node is supposed to start with an applied DB dump. If you see any genesis-related errors, it probably means the Node +was started without an applied dump. [contact_us]: https://zksync.io/contact @@ -43,7 +43,7 @@ you don't consider actionable, you may disable logs for a component by tweaking | WARN | "Following transport error occurred" | There was a problem with fetching data from the main node. | | WARN | "Unable to get the gas price" | There was a problem with fetching data from the main node. | | WARN | "Consistency checker error" | There are problems querying L1, check the Web3 URL you specified in the config. | -| WARN | "Reorg detected" | Reorg was detected on the main node, the ZKsync node will rollback and restart | +| WARN | "Reorg detected" | Reorg was detected on the main node, the Node will rollback and restart | Same as with panics, normally it's only a problem if a WARN+ level log appears many times in a row. diff --git a/docs/src/guides/external-node/06_components.md b/docs/src/guides/external-node/06_components.md index 733400058a82..ec6ff4f9936a 100644 --- a/docs/src/guides/external-node/06_components.md +++ b/docs/src/guides/external-node/06_components.md @@ -1,58 +1,56 @@ -# ZKsync node components +# Node components This section contains an overview of the EN's main components. ## API -The ZKsync node can serve both the HTTP and the WS Web3 API, as well as PubSub. Whenever possible, it provides data -based on the local state, with a few exceptions: +The Node can serve both the HTTP and the WS Web3 API, as well as PubSub. Whenever possible, it provides data based on +the local state, with a few exceptions: - Submitting transactions: Since it is a read replica, submitted transactions are proxied to the main node, and the - response is returned from the main node. -- Querying transactions: The ZKsync node is not aware of the main node's mempool, and it does not sync rejected - transactions. Therefore, if a local lookup for a transaction or its receipt fails, the ZKsync node will attempt the - same query on the main node. + response is returned from the main node. -[06_components.md](06_components.md) Querying transactions: The Node is not + aware of the main node's mempool, and it does not sync rejected transactions. Therefore, if a local lookup for a + transaction or its receipt fails, the Node will attempt the same query on the main node. Apart from these cases, the API does not depend on the main node. Even if the main node is temporarily unavailable, the -ZKsync node can continue to serve the state it has locally. +Node can continue to serve the state it has locally. ## Fetcher -The Fetcher component is responsible for maintaining synchronization between the ZKsync node and the main node. Its -primary task is to fetch new blocks in order to update the local chain state. However, its responsibilities extend -beyond that. For instance, the Fetcher is also responsible for keeping track of L1 batch statuses. This involves -monitoring whether locally applied batches have been committed, proven, or executed on L1. +The Fetcher component is responsible for maintaining synchronization between the Node and the main node. Its primary +task is to fetch new blocks in order to update the local chain state. However, its responsibilities extend beyond that. +For instance, the Fetcher is also responsible for keeping track of L1 batch statuses. This involves monitoring whether +locally applied batches have been committed, proven, or executed on L1. -It is worth noting that in addition to fetching the _state_, the ZKsync node also retrieves the L1 gas price from the -main node for the purpose of estimating fees for L2 transactions (since this also happens based on the local state). -This information is necessary to ensure that gas estimations are performed in the exact same manner as the main node, -thereby reducing the chances of a transaction not being included in a block. +It is worth noting that in addition to fetching the _state_, the Node also retrieves the L1 gas price from the main node +for the purpose of estimating fees for L2 transactions (since this also happens based on the local state). This +information is necessary to ensure that gas estimations are performed in the exact same manner as the main node, thereby +reducing the chances of a transaction not being included in a block. ## State Keeper / VM The State Keeper component serves as the "sequencer" part of the node. It shares most of its functionality with the main node, with one key distinction. The main node retrieves transactions from the mempool and has the authority to decide -when a specific L2 block or L1 batch should be sealed. On the other hand, the ZKsync node retrieves transactions from -the queue populated by the Fetcher and seals the corresponding blocks/batches based on the data obtained from the -Fetcher queue. +when a specific L2 block or L1 batch should be sealed. On the other hand, the Node retrieves transactions from the queue +populated by the Fetcher and seals the corresponding blocks/batches based on the data obtained from the Fetcher queue. -The actual execution of batches takes place within the VM, which is identical in both the Main and ZKsync nodes. +The actual execution of batches takes place within the VM, which is identical in both the Main and Nodes. ## Reorg Detector -In ZKsync Era, it is theoretically possible for L1 batches to be reverted before the corresponding "execute" operation -is applied on L1, that is before the block is [final][finality]. Such situations are highly uncommon and typically occur -due to significant issues: e.g. a bug in the sequencer implementation preventing L1 batch commitment. Prior to batch -finality, the ZKsync operator can perform a rollback, reverting one or more batches and restoring the blockchain state -to a previous point. Finalized batches cannot be reverted at all. +In a ZK Stack chain, it is theoretically possible for L1 batches to be reverted before the corresponding "execute" +operation is applied on L1, that is before the block is [final][finality]. Such situations are highly uncommon and +typically occur due to significant issues: e.g. a bug in the sequencer implementation preventing L1 batch commitment. +Prior to batch finality, the chain operator can perform a rollback, reverting one or more batches and restoring the +blockchain state to a previous point. Finalized batches cannot be reverted at all. -However, even though such situations are rare, the ZKsync node must handle them correctly. +However, even though such situations are rare, the Node must handle them correctly. -To address this, the ZKsync node incorporates a Reorg Detector component. This module keeps track of all L1 batches that -have not yet been finalized. It compares the locally obtained state root hashes with those provided by the main node's -API. If the root hashes for the latest available L1 batch do not match, the Reorg Detector searches for the specific L1 -batch responsible for the divergence. Subsequently, it rolls back the local state and restarts the node. Upon restart, -the EN resumes normal operation. +To address this, the Node incorporates a Reorg Detector component. This module keeps track of all L1 batches that have +not yet been finalized. It compares the locally obtained state root hashes with those provided by the main node's API. +If the root hashes for the latest available L1 batch do not match, the Reorg Detector searches for the specific L1 batch +responsible for the divergence. Subsequently, it rolls back the local state and restarts the node. Upon restart, the EN +resumes normal operation. [finality]: https://docs.zksync.io/zk-stack/concepts/finality @@ -67,13 +65,12 @@ When the Consistency Checker detects that a particular batch has been sent to L1 known as the "block commitment" for the L1 transaction. The block commitment contains crucial data such as the state root and batch number, and is the same commitment that is used for generating a proof for the batch. The Consistency Checker then compares the locally obtained commitment with the actual commitment sent to L1. If the data does not match, -it indicates a potential bug in either the main node or ZKsync node implementation or that the main node API has -provided incorrect data. In either case, the state of the ZKsync node cannot be trusted, and the ZKsync node enters a -crash loop until the issue is resolved. +it indicates a potential bug in either the main node or Node implementation or that the main node API has provided +incorrect data. In either case, the state of the Node cannot be trusted, and the Node enters a crash loop until the +issue is resolved. ## Health check server -The ZKsync node also exposes an additional server that returns HTTP 200 response when the ZKsync node is operating -normally, and HTTP 503 response when some of the health checks don't pass (e.g. when the ZKsync node is not fully -initialized yet). This server can be used, for example, to implement the readiness probe in an orchestration solution -you use. +The Node also exposes an additional server that returns HTTP 200 response when the Node is operating normally, and HTTP +503 response when some of the health checks don't pass (e.g. when the Node is not fully initialized yet). This server +can be used, for example, to implement the readiness probe in an orchestration solution you use. diff --git a/docs/src/guides/external-node/07_snapshots_recovery.md b/docs/src/guides/external-node/07_snapshots_recovery.md index 0053717af063..ecab9c3702df 100644 --- a/docs/src/guides/external-node/07_snapshots_recovery.md +++ b/docs/src/guides/external-node/07_snapshots_recovery.md @@ -39,10 +39,10 @@ error mentioning the first locally retained block or L1 batch if queried this mi used for [pruning](08_pruning.md) because logically, recovering from a snapshot is equivalent to pruning node storage to the snapshot L1 batch. -## Configuration +## Configuration (for ZKsync Era) -To enable snapshot recovery on mainnet, you need to set environment variables for a node before starting it for the -first time: +To enable snapshot recovery on ZKsync mainnet, you need to set environment variables for a node before starting it for +the first time: ```yaml EN_SNAPSHOTS_RECOVERY_ENABLED: 'true' @@ -50,7 +50,7 @@ EN_SNAPSHOTS_OBJECT_STORE_BUCKET_BASE_URL: 'zksync-era-mainnet-external-node-sna EN_SNAPSHOTS_OBJECT_STORE_MODE: 'GCSAnonymousReadOnly' ``` -For the Sepolia testnet, use: +For the ZKsync Sepolia testnet, use: ```yaml EN_SNAPSHOTS_RECOVERY_ENABLED: 'true' @@ -58,7 +58,7 @@ EN_SNAPSHOTS_OBJECT_STORE_BUCKET_BASE_URL: 'zksync-era-boojnet-external-node-sna EN_SNAPSHOTS_OBJECT_STORE_MODE: 'GCSAnonymousReadOnly' ``` -For a working examples of a fully configured Nodes recovering from snapshots, see +For a working examples of a fully configured ZKsync Nodes recovering from snapshots, see [Docker Compose examples](docker-compose-examples) and [_Quick Start_](00_quick_start.md). If a node is already recovered (does not matter whether from a snapshot or from a Postgres dump), setting these env diff --git a/docs/src/guides/external-node/08_pruning.md b/docs/src/guides/external-node/08_pruning.md index 06bd9f8d8a9d..7bb1a64dbed8 100644 --- a/docs/src/guides/external-node/08_pruning.md +++ b/docs/src/guides/external-node/08_pruning.md @@ -1,9 +1,8 @@ # Pruning -It is possible to configure a ZKsync node to periodically prune all data from L1 batches older than a configurable -threshold. Data is pruned both from Postgres and from tree (RocksDB). Pruning happens continuously (i.e., does not -require stopping the node) in the background during normal node operation. It is designed to not significantly impact -node performance. +It is possible to configure a Node to periodically prune all data from L1 batches older than a configurable threshold. +Data is pruned both from Postgres and from tree (RocksDB). Pruning happens continuously (i.e., does not require stopping +the node) in the background during normal node operation. It is designed to not significantly impact node performance. Types of pruned data in Postgres include: diff --git a/docs/src/guides/external-node/09_treeless_mode.md b/docs/src/guides/external-node/09_treeless_mode.md index ceeea6f86c67..05062c30abef 100644 --- a/docs/src/guides/external-node/09_treeless_mode.md +++ b/docs/src/guides/external-node/09_treeless_mode.md @@ -1,10 +1,10 @@ # Treeless Operation Mode -Normally, a ZKsync node needs to run the Merkle tree component (aka _metadata calculator_) in order to compute L1 batch -state root hashes. A state root hash from the previous batch can be accessed by L2 contracts, so processing transactions -in an L1 batch cannot start until the state root hash of the previous L1 batch is computed. Merkle tree requires -non-trivial storage space and RAM (roughly 3 TB and 32 GB respectively for an archival mainnet node as of July 2024). -While storage and RAM requirements can be significantly lowered with [snapshot recovery](07_snapshots_recovery.md) and +Normally, a Node needs to run the Merkle tree component (aka _metadata calculator_) in order to compute L1 batch state +root hashes. A state root hash from the previous batch can be accessed by L2 contracts, so processing transactions in an +L1 batch cannot start until the state root hash of the previous L1 batch is computed. Merkle tree requires non-trivial +storage space and RAM (roughly 3 TB and 32 GB respectively for an archival mainnet node as of July 2024). While storage +and RAM requirements can be significantly lowered with [snapshot recovery](07_snapshots_recovery.md) and [pruning](08_pruning.md), **treeless operation mode** allows to run a node without a local Merkle tree instance at all. ## How it works diff --git a/docs/src/guides/external-node/10_decentralization.md b/docs/src/guides/external-node/10_decentralization.md index 951538e6ab86..7f301cfbf045 100644 --- a/docs/src/guides/external-node/10_decentralization.md +++ b/docs/src/guides/external-node/10_decentralization.md @@ -1,8 +1,8 @@ # Decentralization -In the default setup, the ZKsync node will fetch data from the ZKsync API endpoint maintained by Matter Labs. To reduce -the reliance on this centralized endpoint we have developed a decentralized p2p networking stack (aka gossipnet) which -will eventually be used instead of ZKsync API for synchronizing data. +In the default setup, the Node will fetch data from the ZKsync API endpoint maintained by Matter Labs. To reduce the +reliance on this centralized endpoint we have developed a decentralized p2p networking stack (aka gossipnet) which will +eventually be used instead of ZKsync API for synchronizing data. On the gossipnet, the data integrity will be protected by the BFT (byzantine fault-tolerant) consensus algorithm (currently data is signed just by the main node though). @@ -35,9 +35,9 @@ chmod 600 consensus_secrets.yaml ### Preparing configuration file Copy the template of the consensus configuration file (for -[mainnet](https://github.com/matter-labs/zksync-era/blob/main/docs/guides/external-node/prepared_configs/mainnet_consensus_config.yaml) +[mainnet](https://github.com/matter-labs/zksync-era/blob/main/docs/src/guides/external-node/prepared_configs/mainnet_consensus_config.yaml) or -[testnet](https://github.com/matter-labs/zksync-era/blob/main/docs/guides/external-node/prepared_configs/testnet_consensus_config.yaml) +[testnet](https://github.com/matter-labs/zksync-era/blob/main/docs/src/guides/external-node/prepared_configs/testnet_consensus_config.yaml) ). > [!NOTE] diff --git a/docs/src/guides/external-node/11_setup_for_other_chains.md b/docs/src/guides/external-node/11_setup_for_other_chains.md new file mode 100644 index 000000000000..9d1c88ea91be --- /dev/null +++ b/docs/src/guides/external-node/11_setup_for_other_chains.md @@ -0,0 +1,50 @@ +# Steps to modify the docker-compose files to support Other Chains + +Below are the steps for adjusting ZKsync Era docker-compose files from [here](00_quick_start.md) to support chains other +than ZKsync Era. + +> [!NOTE] +> +> If you want to run Node for a given chain, you can first ask the company hosting the chains for the Dockerfiles. + +## 1. Update `EN_L2_CHAIN_ID` + +The `EN_L2_CHAIN_ID` environment variable specifies the Layer 2 chain ID of the blockchain. + +You can get it using main node rpc call `eth_chainId` or by asking the company hosting the chain. For example: + +``` +curl -X POST https://mainnet.era.zksync.io \ +-H "Content-Type: application/json" \ +-d '{"jsonrpc": "2.0", "method": "eth_chainId", "params": [], "id": 1}' +``` + +returns + +``` +{ "jsonrpc": "2.0", "result": "0x144", "id": 1} +``` + +where `0x144` is the chain ID (324 in decimal) + +## 2. Update `EN_MAIN_NODE_URL` + +The `EN_MAIN_NODE_URL` The EN_MAIN_NODE_URL environment variable should point to the main node URL of the target chain + +## 3. Update snapshots recovery settings + +Snapshots recovery is a feature that allows faster Node startup at the cost of no transaction history. By default the +ZKsync Era docker-compose file has this feature enabled, but it's only recommended to use if the Node first startup time +is too slow. It can be disabled by changing `EN_SNAPSHOTS_RECOVERY_ENABLED` to `false` + +If you want to keep this feature enabled for a Node, ask the company hosting the chain for the bucket name where the +snapshots are stored and update the value of `EN_SNAPSHOTS_OBJECT_STORE_BUCKET_BASE_URL` + +## 4. Disable consensus + +Chains other than ZKsync Era aren't currently running consensus(as of December 2024). You need to disable it by removing +`--enable-consensus` flag from `entrypoint.sh` invocation in docker-compose + +## 5. (Validium chains only) Set `EN_L1_BATCH_COMMIT_DATA_GENERATOR_MODE` + +For validium chains, you need to set `EN_L1_BATCH_COMMIT_DATA_GENERATOR_MODE: "Validium"` From f30aca00962aa34c8a7acd6e4116290a2b214dcb Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Mon, 13 Jan 2025 20:08:10 +1100 Subject: [PATCH 18/97] feat(api_server): report gas price based on open batch (#2868) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ `api_server` should report gas price based on the currently open L1 batch as opposed to the latest sealed L2 block. Note: adding `BatchFeeInput` to L1 batch models did not end up being necessary but I think it still makes sense. ## Why ❔ To avoid gas fluctuation issues on chains with low amount of traffic ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zk fmt` and `zk lint`. --- Cargo.lock | 2 + ...e235deae9a0c54b9713207d53918eb4973600.json | 62 +++++++++++++ ...d30a258bdf36ed98b1422084ef682b29c8cc.json} | 24 ++++- ...33d22c94aa47b7a8863da2afa5ab1a8502bf.json} | 22 ++++- ...eaa79e05b443995c819db0befdcb1449691f.json} | 24 ++++- ...9b01a237f4d0f74c2d387099a3766bf7364b.json} | 24 ++++- ...91f9887cc7dcf456c79bb22edb22cf27682f.json} | 24 ++++- ...635d692a748021ed20b657b6d8f0dfcad5bc.json} | 24 ++++- ...b654bdb1c4139c453889b5fe43902300c361.json} | 24 ++++- ...d94bb65d35ad4b0c819fa7b4afe07d9769e9.json} | 24 ++++- ...95ab975d3e06a6004e01b86b8d4e14788b95.json} | 24 ++++- core/lib/dal/src/blocks_dal.rs | 92 +++++++++++++++---- core/lib/dal/src/models/storage_block.rs | 90 +++++++++++++----- core/lib/types/src/block.rs | 17 +++- core/lib/types/src/fee_model.rs | 24 +++++ core/node/api_server/src/web3/tests/mod.rs | 14 ++- core/node/api_server/src/web3/tests/vm.rs | 88 ++++++++++++++---- core/node/block_reverter/src/tests.rs | 4 +- core/node/fee_model/Cargo.toml | 2 + core/node/fee_model/src/lib.rs | 67 +++++++++++--- core/node/genesis/src/lib.rs | 2 +- .../state_keeper/src/io/seal_logic/mod.rs | 1 + core/node/state_keeper/src/updates/mod.rs | 2 +- core/tests/ts-integration/src/helpers.ts | 47 +++++++++- core/tests/ts-integration/tests/fees.test.ts | 3 + 25 files changed, 627 insertions(+), 104 deletions(-) create mode 100644 core/lib/dal/.sqlx/query-24aca24f8811d87f5ff54757903e235deae9a0c54b9713207d53918eb4973600.json rename core/lib/dal/.sqlx/{query-4bd1a4e612d10f2ca26068c140442f38816f163a3e3fba4fdbb81076b969e970.json => query-442d1b4604c7a4202811d250a531d30a258bdf36ed98b1422084ef682b29c8cc.json} (82%) rename core/lib/dal/.sqlx/{query-4e994d519b9c75e64a74423f8c19fbde6eb6634d7a63005081ffc1eb6c28e9ec.json => query-54f41fccbe8c100015ccf4d87e0e33d22c94aa47b7a8863da2afa5ab1a8502bf.json} (77%) rename core/lib/dal/.sqlx/{query-a42121cd85daeb95ee268ba5cff1806fcc54d73216a7dc54be6ba210ef02d789.json => query-7203b56390ec3768b7f8ed221756eaa79e05b443995c819db0befdcb1449691f.json} (77%) rename core/lib/dal/.sqlx/{query-77864e5eb5eada8edf8f4457aa153369701d7cd5f75ca031bf77ca27d0437cb9.json => query-7cf174da6228113a27dacbcc0eac9b01a237f4d0f74c2d387099a3766bf7364b.json} (84%) rename core/lib/dal/.sqlx/{query-45154c2efc8d07c4f83ae3e229f9892118f5732374e62f35e27800422afb5746.json => query-881ea0a611d35d4a6cc6893067a791f9887cc7dcf456c79bb22edb22cf27682f.json} (75%) rename core/lib/dal/.sqlx/{query-c5aedd2b1871d8f6276a31482caa673e4b5bba059ebe07bbbb64578881db030b.json => query-c70a2e9d09f93b510cb726d6d420635d692a748021ed20b657b6d8f0dfcad5bc.json} (90%) rename core/lib/dal/.sqlx/{query-62e8330881b73917394384adbf73911add046315e5f8877bc57a34e3dadf9e37.json => query-cb1a078ba867415f27a50bdccfbfb654bdb1c4139c453889b5fe43902300c361.json} (82%) rename core/lib/dal/.sqlx/{query-1cb61327bed4d65a3fc81aa2229e01396dacefc0cea8cbcf5807185eb00fc0f7.json => query-d62f8d10c7b469067e20e56d2e58d94bb65d35ad4b0c819fa7b4afe07d9769e9.json} (82%) rename core/lib/dal/.sqlx/{query-b7d448837439a3e3dfe73070d3c20e9c138d0a6d35e9ce7fc396c5e76fbc25dd.json => query-e90a364528fab11f4b0d0e5d069395ab975d3e06a6004e01b86b8d4e14788b95.json} (78%) diff --git a/Cargo.lock b/Cargo.lock index abdd3fb6a5cf..d6e7503bd20f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12439,6 +12439,8 @@ dependencies = [ "zksync_config", "zksync_dal", "zksync_eth_client", + "zksync_node_genesis", + "zksync_node_test_utils", "zksync_types", "zksync_web3_decl", ] diff --git a/core/lib/dal/.sqlx/query-24aca24f8811d87f5ff54757903e235deae9a0c54b9713207d53918eb4973600.json b/core/lib/dal/.sqlx/query-24aca24f8811d87f5ff54757903e235deae9a0c54b9713207d53918eb4973600.json new file mode 100644 index 000000000000..b25eff8b4f55 --- /dev/null +++ b/core/lib/dal/.sqlx/query-24aca24f8811d87f5ff54757903e235deae9a0c54b9713207d53918eb4973600.json @@ -0,0 +1,62 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n number,\n is_sealed,\n timestamp,\n protocol_version,\n fee_address,\n l1_gas_price,\n l2_fair_gas_price,\n fair_pubdata_price\n FROM\n l1_batches\n ORDER BY\n number DESC\n LIMIT\n 1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "number", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "is_sealed", + "type_info": "Bool" + }, + { + "ordinal": 2, + "name": "timestamp", + "type_info": "Int8" + }, + { + "ordinal": 3, + "name": "protocol_version", + "type_info": "Int4" + }, + { + "ordinal": 4, + "name": "fee_address", + "type_info": "Bytea" + }, + { + "ordinal": 5, + "name": "l1_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 6, + "name": "l2_fair_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 7, + "name": "fair_pubdata_price", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false, + true, + false, + false, + false, + false + ] + }, + "hash": "24aca24f8811d87f5ff54757903e235deae9a0c54b9713207d53918eb4973600" +} diff --git a/core/lib/dal/.sqlx/query-4bd1a4e612d10f2ca26068c140442f38816f163a3e3fba4fdbb81076b969e970.json b/core/lib/dal/.sqlx/query-442d1b4604c7a4202811d250a531d30a258bdf36ed98b1422084ef682b29c8cc.json similarity index 82% rename from core/lib/dal/.sqlx/query-4bd1a4e612d10f2ca26068c140442f38816f163a3e3fba4fdbb81076b969e970.json rename to core/lib/dal/.sqlx/query-442d1b4604c7a4202811d250a531d30a258bdf36ed98b1422084ef682b29c8cc.json index 66d3e18075bf..39de8d246376 100644 --- a/core/lib/dal/.sqlx/query-4bd1a4e612d10f2ca26068c140442f38816f163a3e3fba4fdbb81076b969e970.json +++ b/core/lib/dal/.sqlx/query-442d1b4604c7a4202811d250a531d30a258bdf36ed98b1422084ef682b29c8cc.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n number,\n timestamp,\n l1_tx_count,\n l2_tx_count,\n bloom,\n priority_ops_onchain_data,\n hash,\n commitment,\n l2_to_l1_messages,\n used_contract_hashes,\n compressed_initial_writes,\n compressed_repeated_writes,\n l2_l1_merkle_root,\n rollup_last_leaf_index,\n zkporter_is_available,\n bootloader_code_hash,\n default_aa_code_hash,\n evm_emulator_code_hash,\n aux_data_hash,\n pass_through_data_hash,\n meta_parameters_hash,\n protocol_version,\n compressed_state_diffs,\n system_logs,\n events_queue_commitment,\n bootloader_initial_content_commitment,\n pubdata_input,\n fee_address,\n aggregation_root,\n local_root,\n state_diff_hash,\n data_availability.inclusion_data\n FROM\n l1_batches\n LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number\n LEFT JOIN\n data_availability\n ON data_availability.l1_batch_number = l1_batches.number\n WHERE\n number BETWEEN $1 AND $2\n ORDER BY\n number\n LIMIT\n $3\n ", + "query": "\n SELECT\n number,\n timestamp,\n l1_tx_count,\n l2_tx_count,\n bloom,\n priority_ops_onchain_data,\n hash,\n commitment,\n l2_to_l1_messages,\n used_contract_hashes,\n compressed_initial_writes,\n compressed_repeated_writes,\n l2_l1_merkle_root,\n rollup_last_leaf_index,\n zkporter_is_available,\n bootloader_code_hash,\n default_aa_code_hash,\n evm_emulator_code_hash,\n aux_data_hash,\n pass_through_data_hash,\n meta_parameters_hash,\n protocol_version,\n compressed_state_diffs,\n system_logs,\n events_queue_commitment,\n bootloader_initial_content_commitment,\n pubdata_input,\n fee_address,\n aggregation_root,\n local_root,\n state_diff_hash,\n data_availability.inclusion_data,\n l1_gas_price,\n l2_fair_gas_price,\n fair_pubdata_price\n FROM\n l1_batches\n LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number\n LEFT JOIN\n data_availability\n ON data_availability.l1_batch_number = l1_batches.number\n WHERE\n number BETWEEN $1 AND $2\n ORDER BY\n number\n LIMIT\n $3\n ", "describe": { "columns": [ { @@ -162,6 +162,21 @@ "ordinal": 31, "name": "inclusion_data", "type_info": "Bytea" + }, + { + "ordinal": 32, + "name": "l1_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 33, + "name": "l2_fair_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 34, + "name": "fair_pubdata_price", + "type_info": "Int8" } ], "parameters": { @@ -203,8 +218,11 @@ true, true, true, - true + true, + false, + false, + false ] }, - "hash": "4bd1a4e612d10f2ca26068c140442f38816f163a3e3fba4fdbb81076b969e970" + "hash": "442d1b4604c7a4202811d250a531d30a258bdf36ed98b1422084ef682b29c8cc" } diff --git a/core/lib/dal/.sqlx/query-4e994d519b9c75e64a74423f8c19fbde6eb6634d7a63005081ffc1eb6c28e9ec.json b/core/lib/dal/.sqlx/query-54f41fccbe8c100015ccf4d87e0e33d22c94aa47b7a8863da2afa5ab1a8502bf.json similarity index 77% rename from core/lib/dal/.sqlx/query-4e994d519b9c75e64a74423f8c19fbde6eb6634d7a63005081ffc1eb6c28e9ec.json rename to core/lib/dal/.sqlx/query-54f41fccbe8c100015ccf4d87e0e33d22c94aa47b7a8863da2afa5ab1a8502bf.json index 804318120fcc..2cdfad08c830 100644 --- a/core/lib/dal/.sqlx/query-4e994d519b9c75e64a74423f8c19fbde6eb6634d7a63005081ffc1eb6c28e9ec.json +++ b/core/lib/dal/.sqlx/query-54f41fccbe8c100015ccf4d87e0e33d22c94aa47b7a8863da2afa5ab1a8502bf.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n number,\n l1_tx_count,\n l2_tx_count,\n timestamp,\n l2_to_l1_messages,\n bloom,\n priority_ops_onchain_data,\n used_contract_hashes,\n bootloader_code_hash,\n default_aa_code_hash,\n evm_emulator_code_hash,\n protocol_version,\n system_logs,\n pubdata_input,\n fee_address\n FROM\n l1_batches\n WHERE\n is_sealed\n AND number = $1\n ", + "query": "\n SELECT\n number,\n l1_tx_count,\n l2_tx_count,\n timestamp,\n l2_to_l1_messages,\n bloom,\n priority_ops_onchain_data,\n used_contract_hashes,\n bootloader_code_hash,\n default_aa_code_hash,\n evm_emulator_code_hash,\n protocol_version,\n system_logs,\n pubdata_input,\n fee_address,\n l1_gas_price,\n l2_fair_gas_price,\n fair_pubdata_price\n FROM\n l1_batches\n WHERE\n is_sealed\n AND number = $1\n ", "describe": { "columns": [ { @@ -77,6 +77,21 @@ "ordinal": 14, "name": "fee_address", "type_info": "Bytea" + }, + { + "ordinal": 15, + "name": "l1_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 16, + "name": "l2_fair_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 17, + "name": "fair_pubdata_price", + "type_info": "Int8" } ], "parameters": { @@ -99,8 +114,11 @@ true, false, true, + false, + false, + false, false ] }, - "hash": "4e994d519b9c75e64a74423f8c19fbde6eb6634d7a63005081ffc1eb6c28e9ec" + "hash": "54f41fccbe8c100015ccf4d87e0e33d22c94aa47b7a8863da2afa5ab1a8502bf" } diff --git a/core/lib/dal/.sqlx/query-a42121cd85daeb95ee268ba5cff1806fcc54d73216a7dc54be6ba210ef02d789.json b/core/lib/dal/.sqlx/query-7203b56390ec3768b7f8ed221756eaa79e05b443995c819db0befdcb1449691f.json similarity index 77% rename from core/lib/dal/.sqlx/query-a42121cd85daeb95ee268ba5cff1806fcc54d73216a7dc54be6ba210ef02d789.json rename to core/lib/dal/.sqlx/query-7203b56390ec3768b7f8ed221756eaa79e05b443995c819db0befdcb1449691f.json index 9a93ba45978e..8a29dbe2158e 100644 --- a/core/lib/dal/.sqlx/query-a42121cd85daeb95ee268ba5cff1806fcc54d73216a7dc54be6ba210ef02d789.json +++ b/core/lib/dal/.sqlx/query-7203b56390ec3768b7f8ed221756eaa79e05b443995c819db0befdcb1449691f.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n number,\n timestamp,\n l1_tx_count,\n l2_tx_count,\n bloom,\n priority_ops_onchain_data,\n hash,\n commitment,\n l2_to_l1_messages,\n used_contract_hashes,\n compressed_initial_writes,\n compressed_repeated_writes,\n l2_l1_merkle_root,\n rollup_last_leaf_index,\n zkporter_is_available,\n bootloader_code_hash,\n default_aa_code_hash,\n evm_emulator_code_hash,\n aux_data_hash,\n pass_through_data_hash,\n meta_parameters_hash,\n system_logs,\n compressed_state_diffs,\n protocol_version,\n events_queue_commitment,\n bootloader_initial_content_commitment,\n pubdata_input,\n fee_address,\n aggregation_root,\n local_root,\n state_diff_hash,\n data_availability.inclusion_data\n FROM\n (\n SELECT\n l1_batches.*,\n ROW_NUMBER() OVER (\n ORDER BY\n number ASC\n ) AS row_number\n FROM\n l1_batches\n WHERE\n eth_commit_tx_id IS NOT NULL\n AND l1_batches.skip_proof = TRUE\n AND l1_batches.number > $1\n ORDER BY\n number\n LIMIT\n $2\n ) inn\n LEFT JOIN commitments ON commitments.l1_batch_number = inn.number\n LEFT JOIN data_availability ON data_availability.l1_batch_number = inn.number\n WHERE\n number - row_number = $1\n ", + "query": "\n SELECT\n number,\n timestamp,\n l1_tx_count,\n l2_tx_count,\n bloom,\n priority_ops_onchain_data,\n hash,\n commitment,\n l2_to_l1_messages,\n used_contract_hashes,\n compressed_initial_writes,\n compressed_repeated_writes,\n l2_l1_merkle_root,\n rollup_last_leaf_index,\n zkporter_is_available,\n bootloader_code_hash,\n default_aa_code_hash,\n evm_emulator_code_hash,\n aux_data_hash,\n pass_through_data_hash,\n meta_parameters_hash,\n system_logs,\n compressed_state_diffs,\n protocol_version,\n events_queue_commitment,\n bootloader_initial_content_commitment,\n pubdata_input,\n fee_address,\n aggregation_root,\n local_root,\n state_diff_hash,\n data_availability.inclusion_data,\n l1_gas_price,\n l2_fair_gas_price,\n fair_pubdata_price\n FROM\n (\n SELECT\n l1_batches.*,\n ROW_NUMBER() OVER (\n ORDER BY\n number ASC\n ) AS row_number\n FROM\n l1_batches\n WHERE\n eth_commit_tx_id IS NOT NULL\n AND l1_batches.skip_proof = TRUE\n AND l1_batches.number > $1\n ORDER BY\n number\n LIMIT\n $2\n ) inn\n LEFT JOIN commitments ON commitments.l1_batch_number = inn.number\n LEFT JOIN data_availability ON data_availability.l1_batch_number = inn.number\n WHERE\n number - row_number = $1\n ", "describe": { "columns": [ { @@ -162,6 +162,21 @@ "ordinal": 31, "name": "inclusion_data", "type_info": "Bytea" + }, + { + "ordinal": 32, + "name": "l1_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 33, + "name": "l2_fair_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 34, + "name": "fair_pubdata_price", + "type_info": "Int8" } ], "parameters": { @@ -202,8 +217,11 @@ true, true, true, - true + true, + false, + false, + false ] }, - "hash": "a42121cd85daeb95ee268ba5cff1806fcc54d73216a7dc54be6ba210ef02d789" + "hash": "7203b56390ec3768b7f8ed221756eaa79e05b443995c819db0befdcb1449691f" } diff --git a/core/lib/dal/.sqlx/query-77864e5eb5eada8edf8f4457aa153369701d7cd5f75ca031bf77ca27d0437cb9.json b/core/lib/dal/.sqlx/query-7cf174da6228113a27dacbcc0eac9b01a237f4d0f74c2d387099a3766bf7364b.json similarity index 84% rename from core/lib/dal/.sqlx/query-77864e5eb5eada8edf8f4457aa153369701d7cd5f75ca031bf77ca27d0437cb9.json rename to core/lib/dal/.sqlx/query-7cf174da6228113a27dacbcc0eac9b01a237f4d0f74c2d387099a3766bf7364b.json index f4e08abe31c5..c8548e37a5f6 100644 --- a/core/lib/dal/.sqlx/query-77864e5eb5eada8edf8f4457aa153369701d7cd5f75ca031bf77ca27d0437cb9.json +++ b/core/lib/dal/.sqlx/query-7cf174da6228113a27dacbcc0eac9b01a237f4d0f74c2d387099a3766bf7364b.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n number,\n timestamp,\n l1_tx_count,\n l2_tx_count,\n bloom,\n priority_ops_onchain_data,\n hash,\n commitment,\n l2_to_l1_messages,\n used_contract_hashes,\n compressed_initial_writes,\n compressed_repeated_writes,\n l2_l1_merkle_root,\n rollup_last_leaf_index,\n zkporter_is_available,\n bootloader_code_hash,\n default_aa_code_hash,\n evm_emulator_code_hash,\n aux_data_hash,\n pass_through_data_hash,\n meta_parameters_hash,\n protocol_version,\n system_logs,\n compressed_state_diffs,\n events_queue_commitment,\n bootloader_initial_content_commitment,\n pubdata_input,\n fee_address,\n aggregation_root,\n local_root,\n state_diff_hash,\n data_availability.inclusion_data\n FROM\n l1_batches\n LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number\n LEFT JOIN\n data_availability\n ON data_availability.l1_batch_number = l1_batches.number\n WHERE\n is_sealed\n AND number = $1\n ", + "query": "\n SELECT\n number,\n timestamp,\n l1_tx_count,\n l2_tx_count,\n bloom,\n priority_ops_onchain_data,\n hash,\n commitment,\n l2_to_l1_messages,\n used_contract_hashes,\n compressed_initial_writes,\n compressed_repeated_writes,\n l2_l1_merkle_root,\n rollup_last_leaf_index,\n zkporter_is_available,\n bootloader_code_hash,\n default_aa_code_hash,\n evm_emulator_code_hash,\n aux_data_hash,\n pass_through_data_hash,\n meta_parameters_hash,\n protocol_version,\n system_logs,\n compressed_state_diffs,\n events_queue_commitment,\n bootloader_initial_content_commitment,\n pubdata_input,\n fee_address,\n aggregation_root,\n local_root,\n state_diff_hash,\n data_availability.inclusion_data,\n l1_gas_price,\n l2_fair_gas_price,\n fair_pubdata_price\n FROM\n l1_batches\n LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number\n LEFT JOIN\n data_availability\n ON data_availability.l1_batch_number = l1_batches.number\n WHERE\n is_sealed\n AND number = $1\n ", "describe": { "columns": [ { @@ -162,6 +162,21 @@ "ordinal": 31, "name": "inclusion_data", "type_info": "Bytea" + }, + { + "ordinal": 32, + "name": "l1_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 33, + "name": "l2_fair_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 34, + "name": "fair_pubdata_price", + "type_info": "Int8" } ], "parameters": { @@ -201,8 +216,11 @@ true, true, true, - true + true, + false, + false, + false ] }, - "hash": "77864e5eb5eada8edf8f4457aa153369701d7cd5f75ca031bf77ca27d0437cb9" + "hash": "7cf174da6228113a27dacbcc0eac9b01a237f4d0f74c2d387099a3766bf7364b" } diff --git a/core/lib/dal/.sqlx/query-45154c2efc8d07c4f83ae3e229f9892118f5732374e62f35e27800422afb5746.json b/core/lib/dal/.sqlx/query-881ea0a611d35d4a6cc6893067a791f9887cc7dcf456c79bb22edb22cf27682f.json similarity index 75% rename from core/lib/dal/.sqlx/query-45154c2efc8d07c4f83ae3e229f9892118f5732374e62f35e27800422afb5746.json rename to core/lib/dal/.sqlx/query-881ea0a611d35d4a6cc6893067a791f9887cc7dcf456c79bb22edb22cf27682f.json index 11bff1102932..7eb653119357 100644 --- a/core/lib/dal/.sqlx/query-45154c2efc8d07c4f83ae3e229f9892118f5732374e62f35e27800422afb5746.json +++ b/core/lib/dal/.sqlx/query-881ea0a611d35d4a6cc6893067a791f9887cc7dcf456c79bb22edb22cf27682f.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n number,\n l1_batches.timestamp,\n l1_tx_count,\n l2_tx_count,\n bloom,\n priority_ops_onchain_data,\n hash,\n commitment,\n l2_to_l1_messages,\n used_contract_hashes,\n compressed_initial_writes,\n compressed_repeated_writes,\n l2_l1_merkle_root,\n rollup_last_leaf_index,\n zkporter_is_available,\n l1_batches.bootloader_code_hash,\n l1_batches.default_aa_code_hash,\n l1_batches.evm_emulator_code_hash,\n aux_data_hash,\n pass_through_data_hash,\n meta_parameters_hash,\n protocol_version,\n compressed_state_diffs,\n system_logs,\n events_queue_commitment,\n bootloader_initial_content_commitment,\n pubdata_input,\n fee_address,\n aggregation_root,\n local_root,\n state_diff_hash,\n data_availability.inclusion_data\n FROM\n l1_batches\n LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number\n LEFT JOIN\n data_availability\n ON data_availability.l1_batch_number = l1_batches.number\n JOIN protocol_versions ON protocol_versions.id = l1_batches.protocol_version\n WHERE\n eth_commit_tx_id IS NULL\n AND number != 0\n AND protocol_versions.bootloader_code_hash = $1\n AND protocol_versions.default_account_code_hash = $2\n AND commitment IS NOT NULL\n AND (\n protocol_versions.id = $3\n OR protocol_versions.upgrade_tx_hash IS NULL\n )\n AND events_queue_commitment IS NOT NULL\n AND bootloader_initial_content_commitment IS NOT NULL\n AND (\n data_availability.inclusion_data IS NOT NULL\n OR $4 IS FALSE\n )\n ORDER BY\n number\n LIMIT\n $5\n ", + "query": "\n SELECT\n number,\n l1_batches.timestamp,\n l1_tx_count,\n l2_tx_count,\n bloom,\n priority_ops_onchain_data,\n hash,\n commitment,\n l2_to_l1_messages,\n used_contract_hashes,\n compressed_initial_writes,\n compressed_repeated_writes,\n l2_l1_merkle_root,\n rollup_last_leaf_index,\n zkporter_is_available,\n l1_batches.bootloader_code_hash,\n l1_batches.default_aa_code_hash,\n l1_batches.evm_emulator_code_hash,\n aux_data_hash,\n pass_through_data_hash,\n meta_parameters_hash,\n protocol_version,\n compressed_state_diffs,\n system_logs,\n events_queue_commitment,\n bootloader_initial_content_commitment,\n pubdata_input,\n fee_address,\n aggregation_root,\n local_root,\n state_diff_hash,\n data_availability.inclusion_data,\n l1_gas_price,\n l2_fair_gas_price,\n fair_pubdata_price\n FROM\n l1_batches\n LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number\n LEFT JOIN\n data_availability\n ON data_availability.l1_batch_number = l1_batches.number\n JOIN protocol_versions ON protocol_versions.id = l1_batches.protocol_version\n WHERE\n eth_commit_tx_id IS NULL\n AND number != 0\n AND protocol_versions.bootloader_code_hash = $1\n AND protocol_versions.default_account_code_hash = $2\n AND commitment IS NOT NULL\n AND (\n protocol_versions.id = $3\n OR protocol_versions.upgrade_tx_hash IS NULL\n )\n AND events_queue_commitment IS NOT NULL\n AND bootloader_initial_content_commitment IS NOT NULL\n AND (\n data_availability.inclusion_data IS NOT NULL\n OR $4 IS FALSE\n )\n ORDER BY\n number\n LIMIT\n $5\n ", "describe": { "columns": [ { @@ -162,6 +162,21 @@ "ordinal": 31, "name": "inclusion_data", "type_info": "Bytea" + }, + { + "ordinal": 32, + "name": "l1_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 33, + "name": "l2_fair_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 34, + "name": "fair_pubdata_price", + "type_info": "Int8" } ], "parameters": { @@ -205,8 +220,11 @@ true, true, true, - true + true, + false, + false, + false ] }, - "hash": "45154c2efc8d07c4f83ae3e229f9892118f5732374e62f35e27800422afb5746" + "hash": "881ea0a611d35d4a6cc6893067a791f9887cc7dcf456c79bb22edb22cf27682f" } diff --git a/core/lib/dal/.sqlx/query-c5aedd2b1871d8f6276a31482caa673e4b5bba059ebe07bbbb64578881db030b.json b/core/lib/dal/.sqlx/query-c70a2e9d09f93b510cb726d6d420635d692a748021ed20b657b6d8f0dfcad5bc.json similarity index 90% rename from core/lib/dal/.sqlx/query-c5aedd2b1871d8f6276a31482caa673e4b5bba059ebe07bbbb64578881db030b.json rename to core/lib/dal/.sqlx/query-c70a2e9d09f93b510cb726d6d420635d692a748021ed20b657b6d8f0dfcad5bc.json index f97ea8a6ccd5..1f6a3dfec6ea 100644 --- a/core/lib/dal/.sqlx/query-c5aedd2b1871d8f6276a31482caa673e4b5bba059ebe07bbbb64578881db030b.json +++ b/core/lib/dal/.sqlx/query-c70a2e9d09f93b510cb726d6d420635d692a748021ed20b657b6d8f0dfcad5bc.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n number,\n timestamp,\n l1_tx_count,\n l2_tx_count,\n bloom,\n priority_ops_onchain_data,\n hash,\n commitment,\n l2_to_l1_messages,\n used_contract_hashes,\n compressed_initial_writes,\n compressed_repeated_writes,\n l2_l1_merkle_root,\n rollup_last_leaf_index,\n zkporter_is_available,\n bootloader_code_hash,\n default_aa_code_hash,\n evm_emulator_code_hash,\n aux_data_hash,\n pass_through_data_hash,\n meta_parameters_hash,\n protocol_version,\n compressed_state_diffs,\n system_logs,\n events_queue_commitment,\n bootloader_initial_content_commitment,\n pubdata_input,\n fee_address,\n aggregation_root,\n local_root,\n state_diff_hash,\n data_availability.inclusion_data\n FROM\n l1_batches\n LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number\n LEFT JOIN\n data_availability\n ON data_availability.l1_batch_number = l1_batches.number\n WHERE\n eth_prove_tx_id IS NOT NULL\n AND eth_execute_tx_id IS NULL\n ORDER BY\n number\n LIMIT\n $1\n ", + "query": "\n SELECT\n number,\n timestamp,\n l1_tx_count,\n l2_tx_count,\n bloom,\n priority_ops_onchain_data,\n hash,\n commitment,\n l2_to_l1_messages,\n used_contract_hashes,\n compressed_initial_writes,\n compressed_repeated_writes,\n l2_l1_merkle_root,\n rollup_last_leaf_index,\n zkporter_is_available,\n bootloader_code_hash,\n default_aa_code_hash,\n evm_emulator_code_hash,\n aux_data_hash,\n pass_through_data_hash,\n meta_parameters_hash,\n protocol_version,\n compressed_state_diffs,\n system_logs,\n events_queue_commitment,\n bootloader_initial_content_commitment,\n pubdata_input,\n fee_address,\n aggregation_root,\n local_root,\n state_diff_hash,\n data_availability.inclusion_data,\n l1_gas_price,\n l2_fair_gas_price,\n fair_pubdata_price\n FROM\n l1_batches\n LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number\n LEFT JOIN\n data_availability\n ON data_availability.l1_batch_number = l1_batches.number\n WHERE\n eth_prove_tx_id IS NOT NULL\n AND eth_execute_tx_id IS NULL\n ORDER BY\n number\n LIMIT\n $1\n ", "describe": { "columns": [ { @@ -162,6 +162,21 @@ "ordinal": 31, "name": "inclusion_data", "type_info": "Bytea" + }, + { + "ordinal": 32, + "name": "l1_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 33, + "name": "l2_fair_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 34, + "name": "fair_pubdata_price", + "type_info": "Int8" } ], "parameters": { @@ -201,8 +216,11 @@ true, true, true, - true + true, + false, + false, + false ] }, - "hash": "c5aedd2b1871d8f6276a31482caa673e4b5bba059ebe07bbbb64578881db030b" + "hash": "c70a2e9d09f93b510cb726d6d420635d692a748021ed20b657b6d8f0dfcad5bc" } diff --git a/core/lib/dal/.sqlx/query-62e8330881b73917394384adbf73911add046315e5f8877bc57a34e3dadf9e37.json b/core/lib/dal/.sqlx/query-cb1a078ba867415f27a50bdccfbfb654bdb1c4139c453889b5fe43902300c361.json similarity index 82% rename from core/lib/dal/.sqlx/query-62e8330881b73917394384adbf73911add046315e5f8877bc57a34e3dadf9e37.json rename to core/lib/dal/.sqlx/query-cb1a078ba867415f27a50bdccfbfb654bdb1c4139c453889b5fe43902300c361.json index dfdb4b6c82e7..ab9a270abc7b 100644 --- a/core/lib/dal/.sqlx/query-62e8330881b73917394384adbf73911add046315e5f8877bc57a34e3dadf9e37.json +++ b/core/lib/dal/.sqlx/query-cb1a078ba867415f27a50bdccfbfb654bdb1c4139c453889b5fe43902300c361.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n number,\n timestamp,\n l1_tx_count,\n l2_tx_count,\n bloom,\n priority_ops_onchain_data,\n hash,\n commitment,\n l2_to_l1_messages,\n used_contract_hashes,\n compressed_initial_writes,\n compressed_repeated_writes,\n l2_l1_merkle_root,\n rollup_last_leaf_index,\n zkporter_is_available,\n bootloader_code_hash,\n default_aa_code_hash,\n evm_emulator_code_hash,\n aux_data_hash,\n pass_through_data_hash,\n meta_parameters_hash,\n protocol_version,\n compressed_state_diffs,\n system_logs,\n events_queue_commitment,\n bootloader_initial_content_commitment,\n pubdata_input,\n fee_address,\n aggregation_root,\n local_root,\n state_diff_hash,\n data_availability.inclusion_data\n FROM\n l1_batches\n LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number\n LEFT JOIN\n data_availability\n ON data_availability.l1_batch_number = l1_batches.number\n WHERE\n eth_commit_tx_id IS NOT NULL\n AND eth_prove_tx_id IS NULL\n ORDER BY\n number\n LIMIT\n $1\n ", + "query": "\n SELECT\n number,\n timestamp,\n l1_tx_count,\n l2_tx_count,\n bloom,\n priority_ops_onchain_data,\n hash,\n commitment,\n l2_to_l1_messages,\n used_contract_hashes,\n compressed_initial_writes,\n compressed_repeated_writes,\n l2_l1_merkle_root,\n rollup_last_leaf_index,\n zkporter_is_available,\n bootloader_code_hash,\n default_aa_code_hash,\n evm_emulator_code_hash,\n aux_data_hash,\n pass_through_data_hash,\n meta_parameters_hash,\n protocol_version,\n compressed_state_diffs,\n system_logs,\n events_queue_commitment,\n bootloader_initial_content_commitment,\n pubdata_input,\n fee_address,\n aggregation_root,\n local_root,\n state_diff_hash,\n data_availability.inclusion_data,\n l1_gas_price,\n l2_fair_gas_price,\n fair_pubdata_price\n FROM\n l1_batches\n LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number\n LEFT JOIN\n data_availability\n ON data_availability.l1_batch_number = l1_batches.number\n WHERE\n eth_commit_tx_id IS NOT NULL\n AND eth_prove_tx_id IS NULL\n ORDER BY\n number\n LIMIT\n $1\n ", "describe": { "columns": [ { @@ -162,6 +162,21 @@ "ordinal": 31, "name": "inclusion_data", "type_info": "Bytea" + }, + { + "ordinal": 32, + "name": "l1_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 33, + "name": "l2_fair_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 34, + "name": "fair_pubdata_price", + "type_info": "Int8" } ], "parameters": { @@ -201,8 +216,11 @@ true, true, true, - true + true, + false, + false, + false ] }, - "hash": "62e8330881b73917394384adbf73911add046315e5f8877bc57a34e3dadf9e37" + "hash": "cb1a078ba867415f27a50bdccfbfb654bdb1c4139c453889b5fe43902300c361" } diff --git a/core/lib/dal/.sqlx/query-1cb61327bed4d65a3fc81aa2229e01396dacefc0cea8cbcf5807185eb00fc0f7.json b/core/lib/dal/.sqlx/query-d62f8d10c7b469067e20e56d2e58d94bb65d35ad4b0c819fa7b4afe07d9769e9.json similarity index 82% rename from core/lib/dal/.sqlx/query-1cb61327bed4d65a3fc81aa2229e01396dacefc0cea8cbcf5807185eb00fc0f7.json rename to core/lib/dal/.sqlx/query-d62f8d10c7b469067e20e56d2e58d94bb65d35ad4b0c819fa7b4afe07d9769e9.json index 48adcd412676..5d40770fcf1d 100644 --- a/core/lib/dal/.sqlx/query-1cb61327bed4d65a3fc81aa2229e01396dacefc0cea8cbcf5807185eb00fc0f7.json +++ b/core/lib/dal/.sqlx/query-d62f8d10c7b469067e20e56d2e58d94bb65d35ad4b0c819fa7b4afe07d9769e9.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n number,\n timestamp,\n l1_tx_count,\n l2_tx_count,\n bloom,\n priority_ops_onchain_data,\n hash,\n commitment,\n l2_to_l1_messages,\n used_contract_hashes,\n compressed_initial_writes,\n compressed_repeated_writes,\n l2_l1_merkle_root,\n rollup_last_leaf_index,\n zkporter_is_available,\n bootloader_code_hash,\n default_aa_code_hash,\n evm_emulator_code_hash,\n aux_data_hash,\n pass_through_data_hash,\n meta_parameters_hash,\n protocol_version,\n compressed_state_diffs,\n system_logs,\n events_queue_commitment,\n bootloader_initial_content_commitment,\n pubdata_input,\n fee_address,\n aggregation_root,\n local_root,\n state_diff_hash,\n data_availability.inclusion_data\n FROM\n l1_batches\n LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number\n LEFT JOIN\n data_availability\n ON data_availability.l1_batch_number = l1_batches.number\n WHERE\n number = 0\n OR eth_commit_tx_id IS NOT NULL\n AND commitment IS NOT NULL\n ORDER BY\n number DESC\n LIMIT\n 1\n ", + "query": "\n SELECT\n number,\n timestamp,\n l1_tx_count,\n l2_tx_count,\n bloom,\n priority_ops_onchain_data,\n hash,\n commitment,\n l2_to_l1_messages,\n used_contract_hashes,\n compressed_initial_writes,\n compressed_repeated_writes,\n l2_l1_merkle_root,\n rollup_last_leaf_index,\n zkporter_is_available,\n bootloader_code_hash,\n default_aa_code_hash,\n evm_emulator_code_hash,\n aux_data_hash,\n pass_through_data_hash,\n meta_parameters_hash,\n protocol_version,\n compressed_state_diffs,\n system_logs,\n events_queue_commitment,\n bootloader_initial_content_commitment,\n pubdata_input,\n fee_address,\n aggregation_root,\n local_root,\n state_diff_hash,\n data_availability.inclusion_data,\n l1_gas_price,\n l2_fair_gas_price,\n fair_pubdata_price\n FROM\n l1_batches\n LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number\n LEFT JOIN\n data_availability\n ON data_availability.l1_batch_number = l1_batches.number\n WHERE\n number = 0\n OR eth_commit_tx_id IS NOT NULL\n AND commitment IS NOT NULL\n ORDER BY\n number DESC\n LIMIT\n 1\n ", "describe": { "columns": [ { @@ -162,6 +162,21 @@ "ordinal": 31, "name": "inclusion_data", "type_info": "Bytea" + }, + { + "ordinal": 32, + "name": "l1_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 33, + "name": "l2_fair_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 34, + "name": "fair_pubdata_price", + "type_info": "Int8" } ], "parameters": { @@ -199,8 +214,11 @@ true, true, true, - true + true, + false, + false, + false ] }, - "hash": "1cb61327bed4d65a3fc81aa2229e01396dacefc0cea8cbcf5807185eb00fc0f7" + "hash": "d62f8d10c7b469067e20e56d2e58d94bb65d35ad4b0c819fa7b4afe07d9769e9" } diff --git a/core/lib/dal/.sqlx/query-b7d448837439a3e3dfe73070d3c20e9c138d0a6d35e9ce7fc396c5e76fbc25dd.json b/core/lib/dal/.sqlx/query-e90a364528fab11f4b0d0e5d069395ab975d3e06a6004e01b86b8d4e14788b95.json similarity index 78% rename from core/lib/dal/.sqlx/query-b7d448837439a3e3dfe73070d3c20e9c138d0a6d35e9ce7fc396c5e76fbc25dd.json rename to core/lib/dal/.sqlx/query-e90a364528fab11f4b0d0e5d069395ab975d3e06a6004e01b86b8d4e14788b95.json index 8a68b1a9b9bd..69ac542a8a9a 100644 --- a/core/lib/dal/.sqlx/query-b7d448837439a3e3dfe73070d3c20e9c138d0a6d35e9ce7fc396c5e76fbc25dd.json +++ b/core/lib/dal/.sqlx/query-e90a364528fab11f4b0d0e5d069395ab975d3e06a6004e01b86b8d4e14788b95.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n number,\n l1_batches.timestamp,\n l1_tx_count,\n l2_tx_count,\n bloom,\n priority_ops_onchain_data,\n hash,\n commitment,\n l2_to_l1_messages,\n used_contract_hashes,\n compressed_initial_writes,\n compressed_repeated_writes,\n l2_l1_merkle_root,\n rollup_last_leaf_index,\n zkporter_is_available,\n l1_batches.bootloader_code_hash,\n l1_batches.default_aa_code_hash,\n l1_batches.evm_emulator_code_hash,\n aux_data_hash,\n pass_through_data_hash,\n meta_parameters_hash,\n protocol_version,\n compressed_state_diffs,\n system_logs,\n events_queue_commitment,\n bootloader_initial_content_commitment,\n pubdata_input,\n fee_address,\n aggregation_root,\n local_root,\n state_diff_hash,\n data_availability.inclusion_data\n FROM\n l1_batches\n LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number\n JOIN protocol_versions ON protocol_versions.id = l1_batches.protocol_version\n LEFT JOIN\n data_availability\n ON data_availability.l1_batch_number = l1_batches.number\n WHERE\n eth_commit_tx_id IS NULL\n AND number != 0\n AND protocol_versions.bootloader_code_hash = $1\n AND protocol_versions.default_account_code_hash = $2\n AND commitment IS NOT NULL\n AND (\n protocol_versions.id = $3\n OR protocol_versions.upgrade_tx_hash IS NULL\n )\n ORDER BY\n number\n LIMIT\n $4\n ", + "query": "\n SELECT\n number,\n l1_batches.timestamp,\n l1_tx_count,\n l2_tx_count,\n bloom,\n priority_ops_onchain_data,\n hash,\n commitment,\n l2_to_l1_messages,\n used_contract_hashes,\n compressed_initial_writes,\n compressed_repeated_writes,\n l2_l1_merkle_root,\n rollup_last_leaf_index,\n zkporter_is_available,\n l1_batches.bootloader_code_hash,\n l1_batches.default_aa_code_hash,\n l1_batches.evm_emulator_code_hash,\n aux_data_hash,\n pass_through_data_hash,\n meta_parameters_hash,\n protocol_version,\n compressed_state_diffs,\n system_logs,\n events_queue_commitment,\n bootloader_initial_content_commitment,\n pubdata_input,\n fee_address,\n aggregation_root,\n local_root,\n state_diff_hash,\n data_availability.inclusion_data,\n l1_gas_price,\n l2_fair_gas_price,\n fair_pubdata_price\n FROM\n l1_batches\n LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number\n JOIN protocol_versions ON protocol_versions.id = l1_batches.protocol_version\n LEFT JOIN\n data_availability\n ON data_availability.l1_batch_number = l1_batches.number\n WHERE\n eth_commit_tx_id IS NULL\n AND number != 0\n AND protocol_versions.bootloader_code_hash = $1\n AND protocol_versions.default_account_code_hash = $2\n AND commitment IS NOT NULL\n AND (\n protocol_versions.id = $3\n OR protocol_versions.upgrade_tx_hash IS NULL\n )\n ORDER BY\n number\n LIMIT\n $4\n ", "describe": { "columns": [ { @@ -162,6 +162,21 @@ "ordinal": 31, "name": "inclusion_data", "type_info": "Bytea" + }, + { + "ordinal": 32, + "name": "l1_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 33, + "name": "l2_fair_gas_price", + "type_info": "Int8" + }, + { + "ordinal": 34, + "name": "fair_pubdata_price", + "type_info": "Int8" } ], "parameters": { @@ -204,8 +219,11 @@ true, true, true, - true + true, + false, + false, + false ] }, - "hash": "b7d448837439a3e3dfe73070d3c20e9c138d0a6d35e9ce7fc396c5e76fbc25dd" + "hash": "e90a364528fab11f4b0d0e5d069395ab975d3e06a6004e01b86b8d4e14788b95" } diff --git a/core/lib/dal/src/blocks_dal.rs b/core/lib/dal/src/blocks_dal.rs index 9404f4d14332..f39a9c582df9 100644 --- a/core/lib/dal/src/blocks_dal.rs +++ b/core/lib/dal/src/blocks_dal.rs @@ -16,11 +16,10 @@ use zksync_db_connection::{ use zksync_types::{ aggregated_operations::AggregatedActionType, block::{ - L1BatchHeader, L1BatchStatistics, L1BatchTreeData, L2BlockHeader, StorageOracleInfo, - UnsealedL1BatchHeader, + CommonL1BatchHeader, L1BatchHeader, L1BatchStatistics, L1BatchTreeData, L2BlockHeader, + StorageOracleInfo, UnsealedL1BatchHeader, }, commitment::{L1BatchCommitmentArtifacts, L1BatchWithMetadata}, - fee_model::BatchFeeInput, l2_to_l1_log::{BatchAndChainMerklePath, UserL2ToL1Log}, writes::TreeWrite, Address, Bloom, L1BatchNumber, L2BlockNumber, ProtocolVersionId, SLChainId, H256, U256, @@ -32,7 +31,8 @@ use crate::{ models::{ parse_protocol_version, storage_block::{ - StorageL1Batch, StorageL1BatchHeader, StorageL2BlockHeader, UnsealedStorageL1Batch, + CommonStorageL1BatchHeader, StorageL1Batch, StorageL1BatchHeader, StorageL2BlockHeader, + UnsealedStorageL1Batch, }, storage_event::StorageL2ToL1Log, storage_oracle_info::DbStorageOracleInfo, @@ -103,6 +103,7 @@ impl BlocksDal<'_, '_> { Ok(count == 0) } + /// Returns the number of the last sealed L1 batch present in the DB, or `None` if there are no L1 batches. pub async fn get_sealed_l1_batch_number(&mut self) -> DalResult> { let row = sqlx::query!( r#" @@ -122,6 +123,39 @@ impl BlocksDal<'_, '_> { Ok(row.number.map(|num| L1BatchNumber(num as u32))) } + /// Returns latest L1 batch's header (could be unsealed). The header contains fields that are + /// common for both unsealed and sealed batches. Returns `None` if there are no L1 batches. + pub async fn get_latest_l1_batch_header(&mut self) -> DalResult> { + let Some(header) = sqlx::query_as!( + CommonStorageL1BatchHeader, + r#" + SELECT + number, + is_sealed, + timestamp, + protocol_version, + fee_address, + l1_gas_price, + l2_fair_gas_price, + fair_pubdata_price + FROM + l1_batches + ORDER BY + number DESC + LIMIT + 1 + "#, + ) + .instrument("get_latest_l1_batch_header") + .fetch_optional(self.storage) + .await? + else { + return Ok(None); + }; + + Ok(Some(header.into())) + } + pub async fn get_sealed_l2_block_number(&mut self) -> DalResult> { let row = sqlx::query!( r#" @@ -348,7 +382,10 @@ impl BlocksDal<'_, '_> { aggregation_root, local_root, state_diff_hash, - data_availability.inclusion_data + data_availability.inclusion_data, + l1_gas_price, + l2_fair_gas_price, + fair_pubdata_price FROM l1_batches LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number @@ -389,7 +426,10 @@ impl BlocksDal<'_, '_> { protocol_version, system_logs, pubdata_input, - fee_address + fee_address, + l1_gas_price, + l2_fair_gas_price, + fair_pubdata_price FROM l1_batches WHERE @@ -1213,7 +1253,10 @@ impl BlocksDal<'_, '_> { aggregation_root, local_root, state_diff_hash, - data_availability.inclusion_data + data_availability.inclusion_data, + l1_gas_price, + l2_fair_gas_price, + fair_pubdata_price FROM l1_batches LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number @@ -1408,7 +1451,10 @@ impl BlocksDal<'_, '_> { aggregation_root, local_root, state_diff_hash, - data_availability.inclusion_data + data_availability.inclusion_data, + l1_gas_price, + l2_fair_gas_price, + fair_pubdata_price FROM l1_batches LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number @@ -1497,7 +1543,10 @@ impl BlocksDal<'_, '_> { aggregation_root, local_root, state_diff_hash, - data_availability.inclusion_data + data_availability.inclusion_data, + l1_gas_price, + l2_fair_gas_price, + fair_pubdata_price FROM ( SELECT @@ -1577,7 +1626,10 @@ impl BlocksDal<'_, '_> { aggregation_root, local_root, state_diff_hash, - data_availability.inclusion_data + data_availability.inclusion_data, + l1_gas_price, + l2_fair_gas_price, + fair_pubdata_price FROM l1_batches LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number @@ -1737,7 +1789,10 @@ impl BlocksDal<'_, '_> { aggregation_root, local_root, state_diff_hash, - data_availability.inclusion_data + data_availability.inclusion_data, + l1_gas_price, + l2_fair_gas_price, + fair_pubdata_price FROM l1_batches LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number @@ -1810,7 +1865,10 @@ impl BlocksDal<'_, '_> { aggregation_root, local_root, state_diff_hash, - data_availability.inclusion_data + data_availability.inclusion_data, + l1_gas_price, + l2_fair_gas_price, + fair_pubdata_price FROM l1_batches LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number @@ -1897,7 +1955,10 @@ impl BlocksDal<'_, '_> { aggregation_root, local_root, state_diff_hash, - data_availability.inclusion_data + data_availability.inclusion_data, + l1_gas_price, + l2_fair_gas_price, + fair_pubdata_price FROM l1_batches LEFT JOIN commitments ON commitments.l1_batch_number = l1_batches.number @@ -2910,10 +2971,7 @@ impl BlocksDal<'_, '_> { } pub async fn insert_mock_l1_batch(&mut self, header: &L1BatchHeader) -> anyhow::Result<()> { - self.insert_l1_batch( - header.to_unsealed_header(BatchFeeInput::pubdata_independent(100, 100, 100)), - ) - .await?; + self.insert_l1_batch(header.to_unsealed_header()).await?; self.mark_l1_batch_as_sealed(header, &[], &[], &[], Default::default()) .await } diff --git a/core/lib/dal/src/models/storage_block.rs b/core/lib/dal/src/models/storage_block.rs index 1aea27a497f0..cb61c29190eb 100644 --- a/core/lib/dal/src/models/storage_block.rs +++ b/core/lib/dal/src/models/storage_block.rs @@ -6,9 +6,9 @@ use thiserror::Error; use zksync_contracts::BaseSystemContractsHashes; use zksync_types::{ api, - block::{L1BatchHeader, L2BlockHeader, UnsealedL1BatchHeader}, + block::{CommonL1BatchHeader, L1BatchHeader, L2BlockHeader, UnsealedL1BatchHeader}, commitment::{L1BatchMetaParameters, L1BatchMetadata, PubdataParams, PubdataType}, - fee_model::{BatchFeeInput, L1PeggedBatchFeeModelInput, PubdataIndependentBatchFeeModelInput}, + fee_model::BatchFeeInput, l2_to_l1_log::{L2ToL1Log, SystemL2ToL1Log, UserL2ToL1Log}, Address, Bloom, L1BatchNumber, L2BlockNumber, ProtocolVersionId, SLChainId, H256, }; @@ -54,6 +54,10 @@ pub(crate) struct StorageL1BatchHeader { pub system_logs: Vec>, pub pubdata_input: Option>, pub fee_address: Vec, + + pub l1_gas_price: i64, + pub l2_fair_gas_price: i64, + pub fair_pubdata_price: Option, } impl StorageL1BatchHeader { @@ -69,6 +73,14 @@ impl StorageL1BatchHeader { let system_logs = convert_l2_to_l1_logs(self.system_logs); + let batch_fee_input = BatchFeeInput::from_protocol_version( + self.protocol_version + .map(|v| (v as u16).try_into().unwrap()), + self.l1_gas_price as u64, + self.l2_fair_gas_price as u64, + self.fair_pubdata_price.map(|p| p as u64), + ); + L1BatchHeader { number: L1BatchNumber(self.number as u32), timestamp: self.timestamp as u64, @@ -92,6 +104,7 @@ impl StorageL1BatchHeader { .map(|v| (v as u16).try_into().unwrap()), pubdata_input: self.pubdata_input, fee_address: Address::from_slice(&self.fee_address), + batch_fee_input, } } } @@ -159,6 +172,10 @@ pub(crate) struct StorageL1Batch { pub local_root: Option>, pub state_diff_hash: Option>, pub inclusion_data: Option>, + + pub l1_gas_price: i64, + pub l2_fair_gas_price: i64, + pub fair_pubdata_price: Option, } impl StorageL1Batch { @@ -174,6 +191,14 @@ impl StorageL1Batch { let system_logs = convert_l2_to_l1_logs(self.system_logs); + let batch_fee_input = BatchFeeInput::from_protocol_version( + self.protocol_version + .map(|v| (v as u16).try_into().unwrap()), + self.l1_gas_price as u64, + self.l2_fair_gas_price as u64, + self.fair_pubdata_price.map(|p| p as u64), + ); + L1BatchHeader { number: L1BatchNumber(self.number as u32), timestamp: self.timestamp as u64, @@ -197,6 +222,7 @@ impl StorageL1Batch { .map(|v| (v as u16).try_into().unwrap()), pubdata_input: self.pubdata_input, fee_address: Address::from_slice(&self.fee_address), + batch_fee_input, } } } @@ -307,6 +333,39 @@ impl From for UnsealedL1BatchHeader { } } +/// Partial projection of the columns common to both [`L1BatchHeader`] and [`UnsealedL1BatchHeader`]. +pub(crate) struct CommonStorageL1BatchHeader { + pub number: i64, + pub is_sealed: bool, + pub timestamp: i64, + pub protocol_version: Option, + pub fee_address: Vec, + pub l1_gas_price: i64, + pub l2_fair_gas_price: i64, + pub fair_pubdata_price: Option, +} + +impl From for CommonL1BatchHeader { + fn from(batch: CommonStorageL1BatchHeader) -> Self { + let protocol_version: Option = batch + .protocol_version + .map(|v| (v as u16).try_into().unwrap()); + Self { + number: L1BatchNumber(batch.number as u32), + is_sealed: batch.is_sealed, + timestamp: batch.timestamp as u64, + protocol_version, + fee_address: Address::from_slice(&batch.fee_address), + fee_input: BatchFeeInput::for_protocol_version( + protocol_version.unwrap_or_else(ProtocolVersionId::last_potentially_undefined), + batch.l2_fair_gas_price as u64, + batch.fair_pubdata_price.map(|p| p as u64), + batch.l1_gas_price as u64, + ), + } + } +} + #[derive(Debug, Clone, sqlx::FromRow)] pub(crate) struct StorageBlockDetails { pub number: i64, @@ -512,25 +571,12 @@ pub(crate) struct StorageL2BlockHeader { impl From for L2BlockHeader { fn from(row: StorageL2BlockHeader) -> Self { let protocol_version = row.protocol_version.map(|v| (v as u16).try_into().unwrap()); - - let fee_input = protocol_version - .filter(|version: &ProtocolVersionId| version.is_post_1_4_1()) - .map(|_| { - BatchFeeInput::PubdataIndependent(PubdataIndependentBatchFeeModelInput { - fair_pubdata_price: row - .fair_pubdata_price - .expect("No fair pubdata price for 1.4.1 miniblock") - as u64, - fair_l2_gas_price: row.l2_fair_gas_price as u64, - l1_gas_price: row.l1_gas_price as u64, - }) - }) - .unwrap_or_else(|| { - BatchFeeInput::L1Pegged(L1PeggedBatchFeeModelInput { - fair_l2_gas_price: row.l2_fair_gas_price as u64, - l1_gas_price: row.l1_gas_price as u64, - }) - }); + let batch_fee_input = BatchFeeInput::from_protocol_version( + protocol_version, + row.l1_gas_price as u64, + row.l2_fair_gas_price as u64, + row.fair_pubdata_price.map(|p| p as u64), + ); L2BlockHeader { number: L2BlockNumber(row.number as u32), @@ -540,7 +586,7 @@ impl From for L2BlockHeader { l2_tx_count: row.l2_tx_count as u16, fee_account_address: Address::from_slice(&row.fee_account_address), base_fee_per_gas: row.base_fee_per_gas.to_u64().unwrap(), - batch_fee_input: fee_input, + batch_fee_input, base_system_contracts_hashes: convert_base_system_contracts_hashes( row.bootloader_code_hash, row.default_aa_code_hash, diff --git a/core/lib/types/src/block.rs b/core/lib/types/src/block.rs index c4fd3306f2d5..4ae665656ee1 100644 --- a/core/lib/types/src/block.rs +++ b/core/lib/types/src/block.rs @@ -63,20 +63,22 @@ pub struct L1BatchHeader { pub protocol_version: Option, pub pubdata_input: Option>, pub fee_address: Address, + pub batch_fee_input: BatchFeeInput, } impl L1BatchHeader { - pub fn to_unsealed_header(&self, fee_input: BatchFeeInput) -> UnsealedL1BatchHeader { + pub fn to_unsealed_header(&self) -> UnsealedL1BatchHeader { UnsealedL1BatchHeader { number: self.number, timestamp: self.timestamp, protocol_version: self.protocol_version, fee_address: self.fee_address, - fee_input, + fee_input: self.batch_fee_input, } } } +/// Holder for the metadata that is relevant for unsealed batches. #[derive(Debug, Clone, PartialEq)] pub struct UnsealedL1BatchHeader { pub number: L1BatchNumber, @@ -86,6 +88,16 @@ pub struct UnsealedL1BatchHeader { pub fee_input: BatchFeeInput, } +/// Holder for the metadata that is relevant for both sealed and unsealed batches. +pub struct CommonL1BatchHeader { + pub number: L1BatchNumber, + pub is_sealed: bool, + pub timestamp: u64, + pub protocol_version: Option, + pub fee_address: Address, + pub fee_input: BatchFeeInput, +} + /// Holder for the L2 block metadata that is not available from transactions themselves. #[derive(Debug, Clone, PartialEq)] pub struct L2BlockHeader { @@ -153,6 +165,7 @@ impl L1BatchHeader { protocol_version: Some(protocol_version), pubdata_input: Some(vec![]), fee_address: Default::default(), + batch_fee_input: BatchFeeInput::pubdata_independent(0, 0, 0), } } diff --git a/core/lib/types/src/fee_model.rs b/core/lib/types/src/fee_model.rs index 79515e6f63a9..414295071746 100644 --- a/core/lib/types/src/fee_model.rs +++ b/core/lib/types/src/fee_model.rs @@ -45,6 +45,30 @@ impl BatchFeeInput { fair_pubdata_price, }) } + + pub fn from_protocol_version( + protocol_version: Option, + l1_gas_price: u64, + fair_l2_gas_price: u64, + fair_pubdata_price: Option, + ) -> Self { + protocol_version + .filter(|version: &ProtocolVersionId| version.is_post_1_4_1()) + .map(|_| { + Self::PubdataIndependent(PubdataIndependentBatchFeeModelInput { + fair_pubdata_price: fair_pubdata_price + .expect("No fair pubdata price for 1.4.1"), + fair_l2_gas_price, + l1_gas_price, + }) + }) + .unwrap_or_else(|| { + Self::L1Pegged(L1PeggedBatchFeeModelInput { + fair_l2_gas_price, + l1_gas_price, + }) + }) + } } impl Default for BatchFeeInput { diff --git a/core/node/api_server/src/web3/tests/mod.rs b/core/node/api_server/src/web3/tests/mod.rs index 9c5730a23386..25405b50c508 100644 --- a/core/node/api_server/src/web3/tests/mod.rs +++ b/core/node/api_server/src/web3/tests/mod.rs @@ -32,7 +32,7 @@ use zksync_system_constants::{ }; use zksync_types::{ api, - block::{pack_block_info, L2BlockHasher, L2BlockHeader}, + block::{pack_block_info, L2BlockHasher, L2BlockHeader, UnsealedL1BatchHeader}, bytecode::{ testonly::{PADDED_EVM_BYTECODE, PROCESSED_EVM_BYTECODE}, BytecodeHash, @@ -397,6 +397,18 @@ async fn store_custom_l2_block( Ok(()) } +async fn open_l1_batch( + storage: &mut Connection<'_, Core>, + number: L1BatchNumber, + batch_fee_input: BatchFeeInput, +) -> anyhow::Result { + let mut header = create_l1_batch(number.0); + header.batch_fee_input = batch_fee_input; + let header = header.to_unsealed_header(); + storage.blocks_dal().insert_l1_batch(header.clone()).await?; + Ok(header) +} + async fn seal_l1_batch( storage: &mut Connection<'_, Core>, number: L1BatchNumber, diff --git a/core/node/api_server/src/web3/tests/vm.rs b/core/node/api_server/src/web3/tests/vm.rs index a82ca3b9e347..76c49f7ad6db 100644 --- a/core/node/api_server/src/web3/tests/vm.rs +++ b/core/node/api_server/src/web3/tests/vm.rs @@ -212,20 +212,44 @@ impl HttpTest for CallTest { panic!("Unexpected error: {error:?}"); } - // Check that the method handler fetches fee inputs for recent blocks. To do that, we create a new block - // with a large fee input; it should be loaded by `ApiFeeInputProvider` and override the input provided by the wrapped mock provider. - let mut block_header = create_l2_block(2); - block_header.batch_fee_input = scaled_sensible_fee_input(2.5); - store_custom_l2_block(&mut connection, &block_header, &[]).await?; + // Check that the method handler fetches fee input from the open batch. To do that, we open a new batch + // with a large fee input; it should be loaded by `ApiFeeInputProvider` and used instead of the input + // provided by the wrapped mock provider. + let batch_header = open_l1_batch( + &mut connection, + L1BatchNumber(1), + scaled_sensible_fee_input(3.0), + ) + .await?; // Fee input is not scaled further as per `ApiFeeInputProvider` implementation - self.fee_input.expect_custom(block_header.batch_fee_input); - let call_request = Self::call_request(b"block=3"); - let call_result = client.call(call_request, None, None).await?; + self.fee_input.expect_custom(batch_header.fee_input); + let call_request = Self::call_request(b"block=2"); + let call_result = client.call(call_request.clone(), None, None).await?; + assert_eq!(call_result.0, b"output"); + let call_result = client + .call( + call_request, + Some(api::BlockIdVariant::BlockNumber(api::BlockNumber::Pending)), + None, + ) + .await?; + assert_eq!(call_result.0, b"output"); + + // Logic here is arguable, but we consider "latest" requests to be interested in the newly + // open batch's fee input even if the latest block was sealed in the previous batch. + let call_request = Self::call_request(b"block=1"); + let call_result = client + .call( + call_request.clone(), + Some(api::BlockIdVariant::BlockNumber(api::BlockNumber::Latest)), + None, + ) + .await?; assert_eq!(call_result.0, b"output"); let call_request_without_target = CallRequest { to: None, - ..Self::call_request(b"block=3") + ..Self::call_request(b"block=2") }; let err = client .call(call_request_without_target, None, None) @@ -728,8 +752,11 @@ impl HttpTest for TraceCallTest { pool: &ConnectionPool, ) -> anyhow::Result<()> { // Store an additional L2 block because L2 block #0 has some special processing making it work incorrectly. + // First half of the test asserts API server's behavior when there is no open batch. In other words, + // when `ApiFeeInputProvider` is forced to fetch fee params from the main fee provider. let mut connection = pool.connection().await?; store_l2_block(&mut connection, L2BlockNumber(1), &[]).await?; + seal_l1_batch(&mut connection, L1BatchNumber(1)).await?; self.fee_input.expect_default(Self::FEE_SCALE); let call_request = CallTest::call_request(b"pending"); @@ -775,20 +802,44 @@ impl HttpTest for TraceCallTest { panic!("Unexpected error: {error:?}"); } - // Check that the method handler fetches fee inputs for recent blocks. To do that, we create a new block - // with a large fee input; it should be loaded by `ApiFeeInputProvider` and override the input provided by the wrapped mock provider. - let mut block_header = create_l2_block(2); - block_header.batch_fee_input = scaled_sensible_fee_input(3.0); - store_custom_l2_block(&mut connection, &block_header, &[]).await?; + // Check that the method handler fetches fee input from the open batch. To do that, we open a new batch + // with a large fee input; it should be loaded by `ApiFeeInputProvider` and used instead of the input + // provided by the wrapped mock provider. + let batch_header = open_l1_batch( + &mut connection, + L1BatchNumber(2), + scaled_sensible_fee_input(3.0), + ) + .await?; // Fee input is not scaled further as per `ApiFeeInputProvider` implementation - self.fee_input.expect_custom(block_header.batch_fee_input); - let call_request = CallTest::call_request(b"block=3"); + self.fee_input.expect_custom(batch_header.fee_input); + let call_request = CallTest::call_request(b"block=2"); let call_result = client.trace_call(call_request.clone(), None, None).await?; Self::assert_debug_call(&call_request, &call_result.unwrap_default()); + let call_result = client + .trace_call( + call_request.clone(), + Some(api::BlockId::Number(api::BlockNumber::Pending)), + None, + ) + .await?; + Self::assert_debug_call(&call_request, &call_result.unwrap_default()); + + // Logic here is arguable, but we consider "latest" requests to be interested in the newly + // open batch's fee input even if the latest block was sealed in the previous batch. + let call_request = CallTest::call_request(b"block=1"); + let call_result = client + .trace_call( + call_request.clone(), + Some(api::BlockId::Number(api::BlockNumber::Latest)), + None, + ) + .await?; + Self::assert_debug_call(&call_request, &call_result.unwrap_default()); let call_request_without_target = CallRequest { to: None, - ..CallTest::call_request(b"block=3") + ..CallTest::call_request(b"block=2") }; let err = client .call(call_request_without_target, None, None) @@ -897,12 +948,15 @@ impl HttpTest for TraceCallTestWithEvmEmulator { pool: &ConnectionPool, ) -> anyhow::Result<()> { // Store an additional L2 block because L2 block #0 has some special processing making it work incorrectly. + // And make sure there is no open batch so that `ApiFeeInputProvider` is forced to fetch fee params from + // the main fee provider. let mut connection = pool.connection().await?; let block_header = L2BlockHeader { base_system_contracts_hashes: genesis_contract_hashes(&mut connection).await?, ..create_l2_block(1) }; store_custom_l2_block(&mut connection, &block_header, &[]).await?; + seal_l1_batch(&mut connection, L1BatchNumber(1)).await?; client .trace_call(CallTest::call_request(&[]), None, None) diff --git a/core/node/block_reverter/src/tests.rs b/core/node/block_reverter/src/tests.rs index b2c4ee6465f6..21265e7fb0c6 100644 --- a/core/node/block_reverter/src/tests.rs +++ b/core/node/block_reverter/src/tests.rs @@ -12,6 +12,7 @@ use zksync_object_store::{Bucket, MockObjectStore}; use zksync_state::interface::ReadStorage; use zksync_types::{ block::{L1BatchHeader, L2BlockHeader}, + fee_model::BatchFeeInput, snapshots::SnapshotVersion, AccountTreeId, L2BlockNumber, ProtocolVersion, ProtocolVersionId, StorageKey, StorageLog, }; @@ -60,7 +61,7 @@ async fn setup_storage(storage: &mut Connection<'_, Core>, storage_logs: &[Stora l2_tx_count: 0, fee_account_address: Address::default(), base_fee_per_gas: 0, - batch_fee_input: Default::default(), + batch_fee_input: BatchFeeInput::pubdata_independent(0, 0, 0), gas_per_pubdata_limit: 0, base_system_contracts_hashes: Default::default(), protocol_version: Some(ProtocolVersionId::latest()), @@ -89,6 +90,7 @@ async fn setup_storage(storage: &mut Connection<'_, Core>, storage_logs: &[Stora protocol_version: Some(ProtocolVersionId::latest()), pubdata_input: None, fee_address: Default::default(), + batch_fee_input: BatchFeeInput::pubdata_independent(0, 0, 0), }; storage .blocks_dal() diff --git a/core/node/fee_model/Cargo.toml b/core/node/fee_model/Cargo.toml index a84a7c5c2173..5b40164a029f 100644 --- a/core/node/fee_model/Cargo.toml +++ b/core/node/fee_model/Cargo.toml @@ -25,3 +25,5 @@ tracing.workspace = true [dev-dependencies] test-casing.workspace = true +zksync_node_test_utils.workspace = true +zksync_node_genesis.workspace = true diff --git a/core/node/fee_model/src/lib.rs b/core/node/fee_model/src/lib.rs index 380a279cccc1..a66d05f7cb2e 100644 --- a/core/node/fee_model/src/lib.rs +++ b/core/node/fee_model/src/lib.rs @@ -1,6 +1,6 @@ use std::{fmt, fmt::Debug, sync::Arc}; -use anyhow::Context as _; +use anyhow::Context; use async_trait::async_trait; use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_types::fee_model::{ @@ -112,22 +112,34 @@ impl BatchFeeModelInputProvider for ApiFeeInputProvider { l1_gas_price_scale_factor: f64, l1_pubdata_price_scale_factor: f64, ) -> anyhow::Result { + let mut conn = self + .connection_pool + .connection_tagged("api_fee_input_provider") + .await?; + let latest_batch_header = conn + .blocks_dal() + .get_latest_l1_batch_header() + .await? + .context("no batches were found in the DB")?; + + if !latest_batch_header.is_sealed { + tracing::trace!( + latest_batch_number = %latest_batch_header.number, + "Found an open batch; reporting its fee input" + ); + return Ok(latest_batch_header.fee_input); + } + + tracing::trace!( + latest_batch_number = %latest_batch_header.number, + "No open batch found; fetching from base provider" + ); let inner_input = self .inner .get_batch_fee_input_scaled(l1_gas_price_scale_factor, l1_pubdata_price_scale_factor) .await .context("cannot get batch fee input from base provider")?; - let last_l2_block_params = self - .connection_pool - .connection_tagged("api_fee_input_provider") - .await? - .blocks_dal() - .get_last_sealed_l2_block_header() - .await?; - - Ok(last_l2_block_params - .map(|header| inner_input.stricter(header.batch_fee_input)) - .unwrap_or(inner_input)) + Ok(inner_input) } /// Returns the fee model parameters. @@ -161,6 +173,8 @@ mod tests { use l1_gas_price::GasAdjusterClient; use zksync_config::GasAdjusterConfig; use zksync_eth_client::{clients::MockSettlementLayer, BaseFees}; + use zksync_node_genesis::{insert_genesis_batch, GenesisParams}; + use zksync_node_test_utils::create_l1_batch; use zksync_types::{ commitment::L1BatchCommitmentMode, fee_model::{BaseTokenConversionRatio, FeeModelConfigV2}, @@ -378,4 +392,33 @@ mod tests { .await .expect("Failed to create GasAdjuster") } + + #[tokio::test] + async fn test_take_fee_input_from_unsealed_batch() { + let sealed_batch_fee_input = BatchFeeInput::pubdata_independent(1, 2, 3); + let unsealed_batch_fee_input = BatchFeeInput::pubdata_independent(101, 102, 103); + + let pool = ConnectionPool::::test_pool().await; + let mut conn = pool.connection().await.unwrap(); + insert_genesis_batch(&mut conn, &GenesisParams::mock()) + .await + .unwrap(); + + let mut l1_batch_header = create_l1_batch(1); + l1_batch_header.batch_fee_input = sealed_batch_fee_input; + conn.blocks_dal() + .insert_mock_l1_batch(&l1_batch_header) + .await + .unwrap(); + let mut l1_batch_header = create_l1_batch(2); + l1_batch_header.batch_fee_input = unsealed_batch_fee_input; + conn.blocks_dal() + .insert_l1_batch(l1_batch_header.to_unsealed_header()) + .await + .unwrap(); + let provider: &dyn BatchFeeModelInputProvider = + &ApiFeeInputProvider::new(Arc::new(MockBatchFeeParamsProvider::default()), pool); + let fee_input = provider.get_batch_fee_input().await.unwrap(); + assert_eq!(fee_input, unsealed_batch_fee_input); + } } diff --git a/core/node/genesis/src/lib.rs b/core/node/genesis/src/lib.rs index 87457f3b1c01..e68aa59b7696 100644 --- a/core/node/genesis/src/lib.rs +++ b/core/node/genesis/src/lib.rs @@ -517,7 +517,7 @@ pub(crate) async fn create_genesis_l1_batch_from_storage_logs_and_factory_deps( .await?; transaction .blocks_dal() - .insert_l1_batch(genesis_l1_batch_header.to_unsealed_header(batch_fee_input)) + .insert_l1_batch(genesis_l1_batch_header.to_unsealed_header()) .await?; transaction .blocks_dal() diff --git a/core/node/state_keeper/src/io/seal_logic/mod.rs b/core/node/state_keeper/src/io/seal_logic/mod.rs index 655bf182ba8f..475be3319efa 100644 --- a/core/node/state_keeper/src/io/seal_logic/mod.rs +++ b/core/node/state_keeper/src/io/seal_logic/mod.rs @@ -133,6 +133,7 @@ impl UpdatesManager { system_logs: finished_batch.final_execution_state.system_logs.clone(), pubdata_input: finished_batch.pubdata_input.clone(), fee_address: self.fee_account_address, + batch_fee_input: self.batch_fee_input, }; let final_bootloader_memory = finished_batch diff --git a/core/node/state_keeper/src/updates/mod.rs b/core/node/state_keeper/src/updates/mod.rs index 06ac4bcd5de0..b4f548527652 100644 --- a/core/node/state_keeper/src/updates/mod.rs +++ b/core/node/state_keeper/src/updates/mod.rs @@ -30,7 +30,7 @@ pub mod l2_block_updates; pub struct UpdatesManager { batch_timestamp: u64, pub fee_account_address: Address, - batch_fee_input: BatchFeeInput, + pub batch_fee_input: BatchFeeInput, base_fee_per_gas: u64, base_system_contract_hashes: BaseSystemContractsHashes, protocol_version: ProtocolVersionId, diff --git a/core/tests/ts-integration/src/helpers.ts b/core/tests/ts-integration/src/helpers.ts index 40d18f1bad63..d1d84d54a545 100644 --- a/core/tests/ts-integration/src/helpers.ts +++ b/core/tests/ts-integration/src/helpers.ts @@ -3,6 +3,7 @@ import * as zksync from 'zksync-ethers'; import * as ethers from 'ethers'; import * as hre from 'hardhat'; import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-solc/dist/src/types'; +import { TransactionReceipt } from 'ethers'; export const SYSTEM_CONTEXT_ADDRESS = '0x000000000000000000000000000000000000800b'; @@ -71,13 +72,51 @@ export async function anyTransaction(wallet: zksync.Wallet): Promise { - // Send a dummy transaction and wait until the new L1 batch is created. - const oldReceipt = await anyTransaction(wallet); + const MAX_ATTEMPTS = 3; + + let txResponse = null; + let txReceipt: TransactionReceipt | null = null; + let nonce = Number(await wallet.getNonce()); + for (let i = 0; i < MAX_ATTEMPTS; i++) { + // Send a dummy transaction and wait for it to execute. We override `maxFeePerGas` as the default ethers behavior + // is to fetch `maxFeePerGas` from the latest sealed block and double it which is not enough for scenarios with + // extreme gas price fluctuations. + let gasPrice = await wallet.provider.getGasPrice(); + if (!txResponse || !txResponse.maxFeePerGas || txResponse.maxFeePerGas < gasPrice) { + txResponse = await wallet.transfer({ + to: wallet.address, + amount: 0, + overrides: { maxFeePerGas: gasPrice, nonce: nonce, maxPriorityFeePerGas: 0 } + }); + } else { + console.log('Gas price has not gone up, waiting longer'); + } + txReceipt = await wallet.provider.waitForTransaction(txResponse.hash, 1, 3000).catch((e) => { + if (ethers.isError(e, 'TIMEOUT')) { + console.log(`Transaction timed out, potentially gas price went up (attempt ${i + 1}/${MAX_ATTEMPTS})`); + return null; + } else if (ethers.isError(e, 'UNKNOWN_ERROR') && e.message.match(/Not enough gas/)) { + console.log( + `Transaction did not have enough gas, likely gas price went up (attempt ${i + 1}/${MAX_ATTEMPTS})` + ); + return null; + } else { + return Promise.reject(e); + } + }); + if (txReceipt) { + // Transaction got executed, so we can safely assume it will be sealed in the next batch + break; + } + } + if (!txReceipt) { + throw new Error('Failed to force an L1 batch to seal'); + } // Invariant: even with 1 transaction, l1 batch must be eventually sealed, so this loop must exit. - while (!(await wallet.provider.getTransactionReceipt(oldReceipt.hash))?.l1BatchNumber) { + while (!(await wallet.provider.getTransactionReceipt(txReceipt.hash))?.l1BatchNumber) { await zksync.utils.sleep(wallet.provider.pollingInterval); } - return (await wallet.provider.getTransactionReceipt(oldReceipt.hash))!; + return (await wallet.provider.getTransactionReceipt(txReceipt.hash))!; } /** diff --git a/core/tests/ts-integration/tests/fees.test.ts b/core/tests/ts-integration/tests/fees.test.ts index 765dc8b73a81..628a17febd76 100644 --- a/core/tests/ts-integration/tests/fees.test.ts +++ b/core/tests/ts-integration/tests/fees.test.ts @@ -362,6 +362,9 @@ testFees('Test fees', function () { newPubdataPrice: requiredPubdataPrice }); + // Wait for current batch to close so gas price is updated with the new config set above + await waitForNewL1Batch(alice); + const l1Messenger = new ethers.Contract(zksync.utils.L1_MESSENGER_ADDRESS, zksync.utils.L1_MESSENGER, alice); // Firstly, let's test a successful transaction. From 0efb8e6662c1aa9beef45510de7226aacc8845ac Mon Sep 17 00:00:00 2001 From: Igor Borodin Date: Mon, 13 Jan 2025 12:13:09 +0100 Subject: [PATCH 19/97] ci: Update CUDA to 12.2 in zk-environment (#3465) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Updates CUDA to 12.2 in zk-environment CUDA variant ## Why ❔ To be aligned with with CUDA version in github-actions runner image and to support newer Nvidia drivers ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- .github/workflows/zk-environment-publish.yml | 4 +- docker-compose-gpu-runner-cuda-12-0.yml | 4 +- ...kerfile => 22.04_amd64_cuda_12.Dockerfile} | 122 ++++++++---------- 3 files changed, 58 insertions(+), 72 deletions(-) rename docker/zk-environment/{22.04_amd64_cuda_12_0.Dockerfile => 22.04_amd64_cuda_12.Dockerfile} (73%) diff --git a/.github/workflows/zk-environment-publish.yml b/.github/workflows/zk-environment-publish.yml index e9dfac1c6bb2..34eb308e987a 100644 --- a/.github/workflows/zk-environment-publish.yml +++ b/.github/workflows/zk-environment-publish.yml @@ -52,7 +52,7 @@ jobs: - docker/zk-environment/22.04_amd64_cuda_11_8.Dockerfile - .github/workflows/zk-environment-publish.yml zk_env_cuda_12: - - docker/zk-environment/22.04_amd64_cuda_12_0.Dockerfile + - docker/zk-environment/22.04_amd64_cuda_12.Dockerfile - .github/workflows/zk-environment-publish.yml get_short_sha: @@ -197,7 +197,7 @@ jobs: runs-on: [matterlabs-ci-runner-high-performance] strategy: matrix: - cuda_version: ['11_8', '12_0'] + cuda_version: ['11_8', '12'] steps: - name: Evaluate condition id: condition diff --git a/docker-compose-gpu-runner-cuda-12-0.yml b/docker-compose-gpu-runner-cuda-12-0.yml index bd91a5a5b0e4..0bc2b6cedb99 100644 --- a/docker-compose-gpu-runner-cuda-12-0.yml +++ b/docker-compose-gpu-runner-cuda-12-0.yml @@ -16,7 +16,7 @@ services: command: node --dev --datadir /rethdata --http --http.addr 0.0.0.0 --http.port 8545 --http.corsdomain "*" --dev.block-time 300ms --chain /chaindata/reth_config zk: - image: ghcr.io/matter-labs/zk-environment:cuda-12_0-latest + image: ghcr.io/matter-labs/zk-environment:cuda-12-latest depends_on: - reth - postgres @@ -72,4 +72,4 @@ services: volumes: postgres-data: - reth-data: \ No newline at end of file + reth-data: diff --git a/docker/zk-environment/22.04_amd64_cuda_12_0.Dockerfile b/docker/zk-environment/22.04_amd64_cuda_12.Dockerfile similarity index 73% rename from docker/zk-environment/22.04_amd64_cuda_12_0.Dockerfile rename to docker/zk-environment/22.04_amd64_cuda_12.Dockerfile index da041b121816..f37fa430ff57 100644 --- a/docker/zk-environment/22.04_amd64_cuda_12_0.Dockerfile +++ b/docker/zk-environment/22.04_amd64_cuda_12.Dockerfile @@ -115,14 +115,14 @@ ENV ZKSYNC_HOME=/usr/src/zksync ENV PATH="${ZKSYNC_HOME}/bin:${PATH}" ENV CI=1 ENV RUSTC_WRAPPER=/usr/local/cargo/bin/sccache -ENV DEBIAN_FRONTEND noninteractive +ENV DEBIAN_FRONTEND=noninteractive # Setup nvidia-cuda env ENV NVARCH x86_64 -ENV NVIDIA_REQUIRE_CUDA "cuda>=12.0 brand=tesla,driver>=450,driver<451 brand=tesla,driver>=470,driver<471 brand=unknown,driver>=470,driver<471 brand=nvidia,driver>=470,driver<471 brand=nvidiartx,driver>=470,driver<471 brand=geforce,driver>=470,driver<471 brand=geforcertx,driver>=470,driver<471 brand=quadro,driver>=470,driver<471 brand=quadrortx,driver>=470,driver<471 brand=titan,driver>=470,driver<471 brand=titanrtx,driver>=470,driver<471" -ENV NV_CUDA_CUDART_VERSION 12.0.107-1 -ENV NV_CUDA_COMPAT_PACKAGE cuda-compat-12-0 +ENV NVIDIA_REQUIRE_CUDA "cuda>=12.2 brand=tesla,driver>=450,driver<451 brand=tesla,driver>=470,driver<471 brand=unknown,driver>=470,driver<471 brand=nvidia,driver>=470,driver<471 brand=nvidiartx,driver>=470,driver<471 brand=geforce,driver>=470,driver<471 brand=geforcertx,driver>=470,driver<471 brand=quadro,driver>=470,driver<471 brand=quadrortx,driver>=470,driver<471 brand=titan,driver>=470,driver<471 brand=titanrtx,driver>=470,driver<471 brand=tesla,driver>=525,driver<526 brand=unknown,driver>=525,driver<526 brand=nvidia,driver>=525,driver<526 brand=nvidiartx,driver>=525,driver<526 brand=geforce,driver>=525,driver<526 brand=geforcertx,driver>=525,driver<526 brand=quadro,driver>=525,driver<526 brand=quadrortx,driver>=525,driver<526 brand=titan,driver>=525,driver<526 brand=titanrtx,driver>=525,driver<526" +ENV NV_CUDA_CUDART_VERSION 12.2.140-1 +ENV NV_CUDA_COMPAT_PACKAGE cuda-compat-12-2 # curl purging is removed, it's required in next steps RUN apt-get update && apt-get install -y --no-install-recommends \ @@ -131,11 +131,11 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ echo "deb https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/${NVARCH} /" > /etc/apt/sources.list.d/cuda.list && \ rm -rf /var/lib/apt/lists/* -ENV CUDA_VERSION 12.0.0 +ENV CUDA_VERSION 12.2.2 # For libraries in the cuda-compat-* package: https://docs.nvidia.com/cuda/eula/index.html#attachment-a RUN apt-get update && apt-get install -y --no-install-recommends \ - cuda-cudart-12-0=${NV_CUDA_CUDART_VERSION} \ + cuda-cudart-12-2=${NV_CUDA_CUDART_VERSION} \ ${NV_CUDA_COMPAT_PACKAGE} \ && rm -rf /var/lib/apt/lists/* @@ -150,41 +150,27 @@ ENV LD_LIBRARY_PATH /usr/local/nvidia/lib:/usr/local/nvidia/lib64 ENV NVIDIA_VISIBLE_DEVICES all ENV NVIDIA_DRIVER_CAPABILITIES compute,utility -ENV NV_CUDA_LIB_VERSION 12.0.0-1 - -ENV NV_NVTX_VERSION 12.0.76-1 -ENV NV_LIBNPP_VERSION 12.0.0.30-1 -ENV NV_LIBNPP_PACKAGE libnpp-12-0=${NV_LIBNPP_VERSION} -ENV NV_LIBCUSPARSE_VERSION 12.0.0.76-1 - -ENV NV_LIBCUBLAS_PACKAGE_NAME libcublas-12-0 -ENV NV_LIBCUBLAS_VERSION 12.0.1.189-1 -ENV NV_LIBCUBLAS_PACKAGE ${NV_LIBCUBLAS_PACKAGE_NAME}=${NV_LIBCUBLAS_VERSION} - -ENV NV_LIBNCCL_PACKAGE_NAME libnccl2 -ENV NV_LIBNCCL_PACKAGE_VERSION 2.17.1-1 -ENV NCCL_VERSION 2.17.1-1 -ENV NV_LIBNCCL_PACKAGE ${NV_LIBNCCL_PACKAGE_NAME}=${NV_LIBNCCL_PACKAGE_VERSION}+cuda12.0 +ENV NV_CUDA_LIB_VERSION 12.2.2-1 -ENV NV_NVTX_VERSION 12.0.76-1 -ENV NV_LIBNPP_VERSION 12.0.0.30-1 -ENV NV_LIBNPP_PACKAGE libnpp-12-0=${NV_LIBNPP_VERSION} -ENV NV_LIBCUSPARSE_VERSION 12.0.0.76-1 +ENV NV_NVTX_VERSION 12.2.140-1 +ENV NV_LIBNPP_VERSION 12.2.1.4-1 +ENV NV_LIBNPP_PACKAGE libnpp-12-2=${NV_LIBNPP_VERSION} +ENV NV_LIBCUSPARSE_VERSION 12.1.2.141-1 -ENV NV_LIBCUBLAS_PACKAGE_NAME libcublas-12-0 -ENV NV_LIBCUBLAS_VERSION 12.0.1.189-1 +ENV NV_LIBCUBLAS_PACKAGE_NAME libcublas-12-2 +ENV NV_LIBCUBLAS_VERSION 12.2.5.6-1 ENV NV_LIBCUBLAS_PACKAGE ${NV_LIBCUBLAS_PACKAGE_NAME}=${NV_LIBCUBLAS_VERSION} ENV NV_LIBNCCL_PACKAGE_NAME libnccl2 -ENV NV_LIBNCCL_PACKAGE_VERSION 2.17.1-1 -ENV NCCL_VERSION 2.17.1-1 -ENV NV_LIBNCCL_PACKAGE ${NV_LIBNCCL_PACKAGE_NAME}=${NV_LIBNCCL_PACKAGE_VERSION}+cuda12.0 +ENV NV_LIBNCCL_PACKAGE_VERSION 2.19.3-1 +ENV NCCL_VERSION 2.19.3-1 +ENV NV_LIBNCCL_PACKAGE ${NV_LIBNCCL_PACKAGE_NAME}=${NV_LIBNCCL_PACKAGE_VERSION}+cuda12.2 RUN apt-get update && apt-get install -y --no-install-recommends \ - cuda-libraries-12-0=${NV_CUDA_LIB_VERSION} \ + cuda-libraries-12-2=${NV_CUDA_LIB_VERSION} \ ${NV_LIBNPP_PACKAGE} \ - cuda-nvtx-12-0=${NV_NVTX_VERSION} \ - libcusparse-12-0=${NV_LIBCUSPARSE_VERSION} \ + cuda-nvtx-12-2=${NV_NVTX_VERSION} \ + libcusparse-12-2=${NV_LIBCUSPARSE_VERSION} \ ${NV_LIBCUBLAS_PACKAGE} \ ${NV_LIBNCCL_PACKAGE} \ && rm -rf /var/lib/apt/lists/* @@ -194,57 +180,57 @@ RUN apt-mark hold ${NV_LIBCUBLAS_PACKAGE_NAME} ${NV_LIBNCCL_PACKAGE_NAME} #### devel -ENV NV_CUDA_LIB_VERSION "12.0.0-1" +ENV NV_CUDA_LIB_VERSION "12.2.2-1" -ENV NV_CUDA_CUDART_DEV_VERSION 12.0.107-1 -ENV NV_NVML_DEV_VERSION 12.0.76-1 -ENV NV_LIBCUSPARSE_DEV_VERSION 12.0.0.76-1 -ENV NV_LIBNPP_DEV_VERSION 12.0.0.30-1 -ENV NV_LIBNPP_DEV_PACKAGE libnpp-dev-12-0=${NV_LIBNPP_DEV_VERSION} +ENV NV_CUDA_CUDART_DEV_VERSION 12.2.140-1 +ENV NV_NVML_DEV_VERSION 12.2.140-1 +ENV NV_LIBCUSPARSE_DEV_VERSION 12.1.2.141-1 +ENV NV_LIBNPP_DEV_VERSION 12.2.1.4-1 +ENV NV_LIBNPP_DEV_PACKAGE libnpp-dev-12-2=${NV_LIBNPP_DEV_VERSION} -ENV NV_LIBCUBLAS_DEV_VERSION 12.0.1.189-1 -ENV NV_LIBCUBLAS_DEV_PACKAGE_NAME libcublas-dev-12-0 +ENV NV_LIBCUBLAS_DEV_VERSION 12.2.5.6-1 +ENV NV_LIBCUBLAS_DEV_PACKAGE_NAME libcublas-dev-12-2 ENV NV_LIBCUBLAS_DEV_PACKAGE ${NV_LIBCUBLAS_DEV_PACKAGE_NAME}=${NV_LIBCUBLAS_DEV_VERSION} -ENV NV_CUDA_NSIGHT_COMPUTE_VERSION 12.0.0-1 -ENV NV_CUDA_NSIGHT_COMPUTE_DEV_PACKAGE cuda-nsight-compute-12-0=${NV_CUDA_NSIGHT_COMPUTE_VERSION} +ENV NV_CUDA_NSIGHT_COMPUTE_VERSION 12.2.2-1 +ENV NV_CUDA_NSIGHT_COMPUTE_DEV_PACKAGE cuda-nsight-compute-12-2=${NV_CUDA_NSIGHT_COMPUTE_VERSION} -ENV NV_NVPROF_VERSION 12.0.90-1 -ENV NV_NVPROF_DEV_PACKAGE cuda-nvprof-12-0=${NV_NVPROF_VERSION} +ENV NV_NVPROF_VERSION 12.2.142-1 +ENV NV_NVPROF_DEV_PACKAGE cuda-nvprof-12-2=${NV_NVPROF_VERSION} ENV NV_LIBNCCL_DEV_PACKAGE_NAME libnccl-dev -ENV NV_LIBNCCL_DEV_PACKAGE_VERSION 2.17.1-1 -ENV NCCL_VERSION 2.17.1-1 -ENV NV_LIBNCCL_DEV_PACKAGE ${NV_LIBNCCL_DEV_PACKAGE_NAME}=${NV_LIBNCCL_DEV_PACKAGE_VERSION}+cuda12.0 - -ENV NV_CUDA_CUDART_DEV_VERSION 12.0.107-1 -ENV NV_NVML_DEV_VERSION 12.0.76-1 -ENV NV_LIBCUSPARSE_DEV_VERSION 12.0.0.76-1 -ENV NV_LIBNPP_DEV_VERSION 12.0.0.30-1 -ENV NV_LIBNPP_DEV_PACKAGE libnpp-dev-12-0=${NV_LIBNPP_DEV_VERSION} - -ENV NV_LIBCUBLAS_DEV_PACKAGE_NAME libcublas-dev-12-0 -ENV NV_LIBCUBLAS_DEV_VERSION 12.0.1.189-1 +ENV NV_LIBNCCL_DEV_PACKAGE_VERSION 2.19.3-1 +ENV NCCL_VERSION 2.19.3-1 +ENV NV_LIBNCCL_DEV_PACKAGE ${NV_LIBNCCL_DEV_PACKAGE_NAME}=${NV_LIBNCCL_DEV_PACKAGE_VERSION}+cuda12.2 + +ENV NV_CUDA_CUDART_DEV_VERSION 12.2.140-1 +ENV NV_NVML_DEV_VERSION 12.2.140-1 +ENV NV_LIBCUSPARSE_DEV_VERSION 12.1.2.141-1 +ENV NV_LIBNPP_DEV_VERSION 12.2.1.4-1 +ENV NV_LIBNPP_DEV_PACKAGE libnpp-dev-12-2=${NV_LIBNPP_DEV_VERSION} + +ENV NV_LIBCUBLAS_DEV_PACKAGE_NAME libcublas-dev-12-2 +ENV NV_LIBCUBLAS_DEV_VERSION 12.2.5.6-1 ENV NV_LIBCUBLAS_DEV_PACKAGE ${NV_LIBCUBLAS_DEV_PACKAGE_NAME}=${NV_LIBCUBLAS_DEV_VERSION} -ENV NV_CUDA_NSIGHT_COMPUTE_VERSION 12.0.0-1 -ENV NV_CUDA_NSIGHT_COMPUTE_DEV_PACKAGE cuda-nsight-compute-12-0=${NV_CUDA_NSIGHT_COMPUTE_VERSION} +ENV NV_CUDA_NSIGHT_COMPUTE_VERSION 12.2.2-1 +ENV NV_CUDA_NSIGHT_COMPUTE_DEV_PACKAGE cuda-nsight-compute-12-2=${NV_CUDA_NSIGHT_COMPUTE_VERSION} ENV NV_LIBNCCL_DEV_PACKAGE_NAME libnccl-dev -ENV NV_LIBNCCL_DEV_PACKAGE_VERSION 2.17.1-1 -ENV NCCL_VERSION 2.17.1-1 -ENV NV_LIBNCCL_DEV_PACKAGE ${NV_LIBNCCL_DEV_PACKAGE_NAME}=${NV_LIBNCCL_DEV_PACKAGE_VERSION}+cuda12.0 +ENV NV_LIBNCCL_DEV_PACKAGE_VERSION 2.19.3-1 +ENV NCCL_VERSION 2.19.3-1 +ENV NV_LIBNCCL_DEV_PACKAGE ${NV_LIBNCCL_DEV_PACKAGE_NAME}=${NV_LIBNCCL_DEV_PACKAGE_VERSION}+cuda12.2 RUN apt-get update && apt-get install -y --no-install-recommends \ libtinfo5 libncursesw5 \ - cuda-cudart-dev-12-0=${NV_CUDA_CUDART_DEV_VERSION} \ - cuda-command-line-tools-12-0=${NV_CUDA_LIB_VERSION} \ - cuda-minimal-build-12-0=${NV_CUDA_LIB_VERSION} \ - cuda-libraries-dev-12-0=${NV_CUDA_LIB_VERSION} \ - cuda-nvml-dev-12-0=${NV_NVML_DEV_VERSION} \ + cuda-cudart-dev-12-2=${NV_CUDA_CUDART_DEV_VERSION} \ + cuda-command-line-tools-12-2=${NV_CUDA_LIB_VERSION} \ + cuda-minimal-build-12-2=${NV_CUDA_LIB_VERSION} \ + cuda-libraries-dev-12-2=${NV_CUDA_LIB_VERSION} \ + cuda-nvml-dev-12-2=${NV_NVML_DEV_VERSION} \ ${NV_NVPROF_DEV_PACKAGE} \ ${NV_LIBNPP_DEV_PACKAGE} \ - libcusparse-dev-12-0=${NV_LIBCUSPARSE_DEV_VERSION} \ + libcusparse-dev-12-2=${NV_LIBCUSPARSE_DEV_VERSION} \ ${NV_LIBCUBLAS_DEV_PACKAGE} \ ${NV_LIBNCCL_DEV_PACKAGE} \ ${NV_CUDA_NSIGHT_COMPUTE_DEV_PACKAGE} \ From c44f7a7317f170dfd7df97bb5f8709a6d810d9fc Mon Sep 17 00:00:00 2001 From: hatemosphere Date: Mon, 13 Jan 2025 12:18:36 +0100 Subject: [PATCH 20/97] ci: Hotfix condition to build CUDA 12 zk-environment Docker image --- .github/workflows/zk-environment-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/zk-environment-publish.yml b/.github/workflows/zk-environment-publish.yml index 34eb308e987a..569fa66ebeb4 100644 --- a/.github/workflows/zk-environment-publish.yml +++ b/.github/workflows/zk-environment-publish.yml @@ -33,7 +33,7 @@ jobs: outputs: zk_environment: ${{ steps.changed-files-yaml.outputs.zk_env_any_changed }} zk_environment_cuda_11_8: ${{ steps.changed-files-yaml.outputs.zk_env_cuda_11_8_any_changed }} - zk_environment_cuda_12_0: ${{ steps.changed-files-yaml.outputs.zk_env_cuda_12_any_changed }} + zk_environment_cuda_12: ${{ steps.changed-files-yaml.outputs.zk_env_cuda_12_any_changed }} runs-on: ubuntu-latest steps: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 From 84e3e312688e3aaffe81828471d276e24432d496 Mon Sep 17 00:00:00 2001 From: Igor Aleksanov Date: Tue, 14 Jan 2025 13:47:36 +0400 Subject: [PATCH 21/97] fix(api): Propagate fallback errors in traces (#3469) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (hopefully) fixes #3394 See the issue for the context. Looks like some old transactions do not have error stored for out-of-gas errors. However, we know that they're failed, and we know that based on [the error field from the transactions table](https://github.com/matter-labs/zksync-era/blob/main/core/lib/dal/src/models/storage_transaction.rs#L361). This PR adds a "fallback" error -- if no other error is detected in the call trace, we use the error from the sequencer. Granted, I do not have access to the mainnet DB, so not sure what error messages are inside, but if I am to guess, it will be more or less adequate. 😅 ⚠️ To make things spicier, I have no way to test this functionality (per @dutterbutter report in the issue, it does not reproduce with the current protocol version), so it's kind of "hope it helps" fix. Please be careful during the review. --- ...1e30a2a643f7909b7b6803a9708357f8ecbe.json} | 12 +++-- ...f2aed7049c3f44a6d25556967ea867e0caf25.json | 22 --------- ...1d1184afcb08d2bdf310a3fd3fb5b47d4d947.json | 28 +++++++++++ core/lib/dal/src/blocks_web3_dal.rs | 6 ++- .../lib/dal/src/models/storage_transaction.rs | 1 + core/lib/dal/src/transactions_dal.rs | 7 ++- core/lib/types/src/debug_flat_call.rs | 7 +++ .../api_server/src/web3/namespaces/debug.rs | 46 +++++++++++++------ core/node/api_server/src/web3/tests/debug.rs | 4 +- 9 files changed, 88 insertions(+), 45 deletions(-) rename core/lib/dal/.sqlx/{query-7235e50f9ce4b5c4f6f8325117eaccc7108538405743fe1ad71451d0f1842561.json => query-34cb5e326f02cca0dac3483a64d21e30a2a643f7909b7b6803a9708357f8ecbe.json} (52%) delete mode 100644 core/lib/dal/.sqlx/query-87f27295de500591f01ed76731df2aed7049c3f44a6d25556967ea867e0caf25.json create mode 100644 core/lib/dal/.sqlx/query-fd68afd6eb8890f01fe646339951d1184afcb08d2bdf310a3fd3fb5b47d4d947.json diff --git a/core/lib/dal/.sqlx/query-7235e50f9ce4b5c4f6f8325117eaccc7108538405743fe1ad71451d0f1842561.json b/core/lib/dal/.sqlx/query-34cb5e326f02cca0dac3483a64d21e30a2a643f7909b7b6803a9708357f8ecbe.json similarity index 52% rename from core/lib/dal/.sqlx/query-7235e50f9ce4b5c4f6f8325117eaccc7108538405743fe1ad71451d0f1842561.json rename to core/lib/dal/.sqlx/query-34cb5e326f02cca0dac3483a64d21e30a2a643f7909b7b6803a9708357f8ecbe.json index f46674b08bc6..f8e9e650d10a 100644 --- a/core/lib/dal/.sqlx/query-7235e50f9ce4b5c4f6f8325117eaccc7108538405743fe1ad71451d0f1842561.json +++ b/core/lib/dal/.sqlx/query-34cb5e326f02cca0dac3483a64d21e30a2a643f7909b7b6803a9708357f8ecbe.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n transactions.hash AS tx_hash,\n transactions.index_in_block AS tx_index_in_block,\n call_trace\n FROM\n call_traces\n INNER JOIN transactions ON tx_hash = transactions.hash\n WHERE\n transactions.miniblock_number = $1\n ORDER BY\n transactions.index_in_block\n ", + "query": "\n SELECT\n transactions.hash AS tx_hash,\n transactions.index_in_block AS tx_index_in_block,\n call_trace,\n transactions.error AS tx_error\n FROM\n call_traces\n INNER JOIN transactions ON tx_hash = transactions.hash\n WHERE\n transactions.miniblock_number = $1\n ORDER BY\n transactions.index_in_block\n ", "describe": { "columns": [ { @@ -17,6 +17,11 @@ "ordinal": 2, "name": "call_trace", "type_info": "Bytea" + }, + { + "ordinal": 3, + "name": "tx_error", + "type_info": "Varchar" } ], "parameters": { @@ -27,8 +32,9 @@ "nullable": [ false, true, - false + false, + true ] }, - "hash": "7235e50f9ce4b5c4f6f8325117eaccc7108538405743fe1ad71451d0f1842561" + "hash": "34cb5e326f02cca0dac3483a64d21e30a2a643f7909b7b6803a9708357f8ecbe" } diff --git a/core/lib/dal/.sqlx/query-87f27295de500591f01ed76731df2aed7049c3f44a6d25556967ea867e0caf25.json b/core/lib/dal/.sqlx/query-87f27295de500591f01ed76731df2aed7049c3f44a6d25556967ea867e0caf25.json deleted file mode 100644 index dbeaede9ecd2..000000000000 --- a/core/lib/dal/.sqlx/query-87f27295de500591f01ed76731df2aed7049c3f44a6d25556967ea867e0caf25.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n call_trace\n FROM\n call_traces\n WHERE\n tx_hash = $1\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "call_trace", - "type_info": "Bytea" - } - ], - "parameters": { - "Left": [ - "Bytea" - ] - }, - "nullable": [ - false - ] - }, - "hash": "87f27295de500591f01ed76731df2aed7049c3f44a6d25556967ea867e0caf25" -} diff --git a/core/lib/dal/.sqlx/query-fd68afd6eb8890f01fe646339951d1184afcb08d2bdf310a3fd3fb5b47d4d947.json b/core/lib/dal/.sqlx/query-fd68afd6eb8890f01fe646339951d1184afcb08d2bdf310a3fd3fb5b47d4d947.json new file mode 100644 index 000000000000..abdc0a1cfa87 --- /dev/null +++ b/core/lib/dal/.sqlx/query-fd68afd6eb8890f01fe646339951d1184afcb08d2bdf310a3fd3fb5b47d4d947.json @@ -0,0 +1,28 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n call_trace,\n transactions.error AS tx_error\n FROM\n call_traces\n INNER JOIN transactions ON tx_hash = transactions.hash\n WHERE\n tx_hash = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "call_trace", + "type_info": "Bytea" + }, + { + "ordinal": 1, + "name": "tx_error", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Bytea" + ] + }, + "nullable": [ + false, + true + ] + }, + "hash": "fd68afd6eb8890f01fe646339951d1184afcb08d2bdf310a3fd3fb5b47d4d947" +} diff --git a/core/lib/dal/src/blocks_web3_dal.rs b/core/lib/dal/src/blocks_web3_dal.rs index 229f49da6e37..96513da54264 100644 --- a/core/lib/dal/src/blocks_web3_dal.rs +++ b/core/lib/dal/src/blocks_web3_dal.rs @@ -567,7 +567,8 @@ impl BlocksWeb3Dal<'_, '_> { SELECT transactions.hash AS tx_hash, transactions.index_in_block AS tx_index_in_block, - call_trace + call_trace, + transactions.error AS tx_error FROM call_traces INNER JOIN transactions ON tx_hash = transactions.hash @@ -583,7 +584,7 @@ impl BlocksWeb3Dal<'_, '_> { .fetch_all(self.storage) .await? .into_iter() - .map(|call_trace| { + .map(|mut call_trace| { let tx_hash = H256::from_slice(&call_trace.tx_hash); let index = call_trace.tx_index_in_block.unwrap_or_default() as usize; let meta = CallTraceMeta { @@ -591,6 +592,7 @@ impl BlocksWeb3Dal<'_, '_> { tx_hash, block_number: block_number.0, block_hash, + internal_error: call_trace.tx_error.take(), }; (call_trace.into_call(protocol_version), meta) }) diff --git a/core/lib/dal/src/models/storage_transaction.rs b/core/lib/dal/src/models/storage_transaction.rs index cceebc85cf2b..27442e41d7be 100644 --- a/core/lib/dal/src/models/storage_transaction.rs +++ b/core/lib/dal/src/models/storage_transaction.rs @@ -591,6 +591,7 @@ pub(crate) struct CallTrace { pub call_trace: Vec, pub tx_hash: Vec, pub tx_index_in_block: Option, + pub tx_error: Option, } impl CallTrace { diff --git a/core/lib/dal/src/transactions_dal.rs b/core/lib/dal/src/transactions_dal.rs index b4e31954be76..6b35c507b919 100644 --- a/core/lib/dal/src/transactions_dal.rs +++ b/core/lib/dal/src/transactions_dal.rs @@ -2233,9 +2233,11 @@ impl TransactionsDal<'_, '_> { Ok(sqlx::query!( r#" SELECT - call_trace + call_trace, + transactions.error AS tx_error FROM call_traces + INNER JOIN transactions ON tx_hash = transactions.hash WHERE tx_hash = $1 "#, @@ -2245,7 +2247,7 @@ impl TransactionsDal<'_, '_> { .with_arg("tx_hash", &tx_hash) .fetch_optional(self.storage) .await? - .map(|call_trace| { + .map(|mut call_trace| { ( parse_call_trace(&call_trace.call_trace, protocol_version), CallTraceMeta { @@ -2253,6 +2255,7 @@ impl TransactionsDal<'_, '_> { tx_hash, block_number: row.miniblock_number as u32, block_hash: H256::from_slice(&row.miniblocks_hash), + internal_error: call_trace.tx_error.take(), }, ) })) diff --git a/core/lib/types/src/debug_flat_call.rs b/core/lib/types/src/debug_flat_call.rs index 3488b0e5b42c..2f130019446a 100644 --- a/core/lib/types/src/debug_flat_call.rs +++ b/core/lib/types/src/debug_flat_call.rs @@ -49,4 +49,11 @@ pub struct CallTraceMeta { pub tx_hash: H256, pub block_number: u32, pub block_hash: H256, + /// Error message associated with the transaction in the sequencer database. + /// Can be used to identify a failed transaction if error information is not + /// recorded otherwise (e.g. out-of-gas errors in early protocol versions). + /// + /// Should be seen as a fallback value (e.g. if the trace doesn't contain the error + /// or revert reason). + pub internal_error: Option, } diff --git a/core/node/api_server/src/web3/namespaces/debug.rs b/core/node/api_server/src/web3/namespaces/debug.rs index 8e72f5b45991..98af15032008 100644 --- a/core/node/api_server/src/web3/namespaces/debug.rs +++ b/core/node/api_server/src/web3/namespaces/debug.rs @@ -31,13 +31,14 @@ impl DebugNamespace { pub(crate) fn map_call( call: Call, - meta: CallTraceMeta, + mut meta: CallTraceMeta, tracer_option: TracerConfig, ) -> CallTracerResult { match tracer_option.tracer { SupportedTracers::CallTracer => CallTracerResult::CallTrace(Self::map_default_call( call, tracer_option.tracer_config.only_top_call, + meta.internal_error, )), SupportedTracers::FlatCallTracer => { let mut calls = vec![]; @@ -47,19 +48,24 @@ impl DebugNamespace { &mut calls, &mut traces, tracer_option.tracer_config.only_top_call, - &meta, + &mut meta, ); CallTracerResult::FlatCallTrace(calls) } } } - pub(crate) fn map_default_call(call: Call, only_top_call: bool) -> DebugCall { + pub(crate) fn map_default_call( + call: Call, + only_top_call: bool, + internal_error: Option, + ) -> DebugCall { let calls = if only_top_call { vec![] } else { + // We don't need to propagate the internal error to the nested calls. call.calls .into_iter() - .map(|call| Self::map_default_call(call, false)) + .map(|call| Self::map_default_call(call, false, None)) .collect() }; let debug_type = match call.r#type { @@ -76,7 +82,7 @@ impl DebugNamespace { value: call.value, output: web3::Bytes::from(call.output), input: web3::Bytes::from(call.input), - error: call.error, + error: call.error.or(internal_error), revert_reason: call.revert_reason, calls, } @@ -87,7 +93,7 @@ impl DebugNamespace { calls: &mut Vec, trace_address: &mut Vec, only_top_call: bool, - meta: &CallTraceMeta, + meta: &mut CallTraceMeta, ) { let subtraces = call.calls.len(); let debug_type = match call.r#type { @@ -96,16 +102,24 @@ impl DebugNamespace { CallType::NearCall => unreachable!("We have to filter our near calls before"), }; - let (result, error) = match (call.revert_reason, call.error) { - (Some(revert_reason), _) => { + // We only want to set the internal error for topmost call, so we take it. + let internal_error = meta.internal_error.take(); + + let (result, error) = match (call.revert_reason, call.error, internal_error) { + (Some(revert_reason), _, _) => { // If revert_reason exists, it takes priority over VM error (None, Some(revert_reason)) } - (None, Some(vm_error)) => { + (None, Some(vm_error), _) => { // If no revert_reason but VM error exists (None, Some(vm_error)) } - (None, None) => ( + (None, None, Some(internal_error)) => { + // No VM error, but there is an error in the sequencer DB. + // Only to be set as a topmost error. + (None, Some(internal_error)) + } + (None, None, None) => ( Some(CallResult { output: web3::Bytes::from(call.output), gas_used: U256::from(call.gas_used), @@ -175,15 +189,19 @@ impl DebugNamespace { SupportedTracers::CallTracer => CallTracerBlockResult::CallTrace( call_traces .into_iter() - .map(|(call, _)| ResultDebugCall { - result: Self::map_default_call(call, options.tracer_config.only_top_call), + .map(|(call, meta)| ResultDebugCall { + result: Self::map_default_call( + call, + options.tracer_config.only_top_call, + meta.internal_error, + ), }) .collect(), ), SupportedTracers::FlatCallTracer => { let res = call_traces .into_iter() - .map(|(call, meta)| { + .map(|(call, mut meta)| { let mut traces = vec![meta.index_in_block]; let mut flat_calls = vec![]; Self::flatten_call( @@ -191,7 +209,7 @@ impl DebugNamespace { &mut flat_calls, &mut traces, options.tracer_config.only_top_call, - &meta, + &mut meta, ); ResultDebugCallFlat { tx_hash: meta.tx_hash, diff --git a/core/node/api_server/src/web3/tests/debug.rs b/core/node/api_server/src/web3/tests/debug.rs index 28a22511fa98..b2aae53eaa32 100644 --- a/core/node/api_server/src/web3/tests/debug.rs +++ b/core/node/api_server/src/web3/tests/debug.rs @@ -73,7 +73,7 @@ impl HttpTest for TraceBlockTest { let expected_calls: Vec<_> = tx_result .call_traces .iter() - .map(|call| DebugNamespace::map_default_call(call.clone(), false)) + .map(|call| DebugNamespace::map_default_call(call.clone(), false, None)) .collect(); assert_eq!(result.calls, expected_calls); } @@ -216,7 +216,7 @@ impl HttpTest for TraceTransactionTest { let expected_calls: Vec<_> = tx_results[0] .call_traces .iter() - .map(|call| DebugNamespace::map_default_call(call.clone(), false)) + .map(|call| DebugNamespace::map_default_call(call.clone(), false, None)) .collect(); let result = client From 006691fe5e3e4a6ccc65d0430212ad5d50a08011 Mon Sep 17 00:00:00 2001 From: Artem Makhortov <13339874+artmakh@users.noreply.github.com> Date: Tue, 14 Jan 2025 19:46:04 +0700 Subject: [PATCH 22/97] fix: Zk-environment proper Nvidia driver repos (#3471) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Change of deprecated nvidia repos to newer ones, and changing from ubuntu 20 repos to ubuntu 22 ## Why ❔ ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- docker/zk-environment/22.04_amd64_cuda_11_8.Dockerfile | 9 +++++++-- docker/zk-environment/22.04_amd64_cuda_12.Dockerfile | 6 +++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/docker/zk-environment/22.04_amd64_cuda_11_8.Dockerfile b/docker/zk-environment/22.04_amd64_cuda_11_8.Dockerfile index fe44d55acbbc..2715063667b6 100644 --- a/docker/zk-environment/22.04_amd64_cuda_11_8.Dockerfile +++ b/docker/zk-environment/22.04_amd64_cuda_11_8.Dockerfile @@ -123,8 +123,13 @@ ENV NVIDIA_REQUIRE_CUDA "cuda>=11.8 brand=tesla,driver>=450,driver<451 brand=tes ENV NV_CUDA_CUDART_VERSION 11.8.89-1 ENV NV_CUDA_COMPAT_PACKAGE cuda-compat-11-8 -RUN wget -c -O - https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/${NVARCH}/3bf863cc.pub | apt-key add - && \ - echo "deb https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/${NVARCH} /" > /etc/apt/sources.list.d/cuda.list +# curl purging is removed, it's required in next steps +RUN apt-get update && apt-get install -y --no-install-recommends \ + gnupg2 curl ca-certificates && \ + curl -fsSLO https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/${NVARCH}/cuda-keyring_1.0-1_all.deb && \ + dpkg -i cuda-keyring_1.0-1_all.deb && \ + apt-get purge --autoremove -y curl \ + && rm -rf /var/lib/apt/lists/* ENV CUDA_VERSION 11.8.0 diff --git a/docker/zk-environment/22.04_amd64_cuda_12.Dockerfile b/docker/zk-environment/22.04_amd64_cuda_12.Dockerfile index f37fa430ff57..a09dda2ae683 100644 --- a/docker/zk-environment/22.04_amd64_cuda_12.Dockerfile +++ b/docker/zk-environment/22.04_amd64_cuda_12.Dockerfile @@ -127,9 +127,9 @@ ENV NV_CUDA_COMPAT_PACKAGE cuda-compat-12-2 # curl purging is removed, it's required in next steps RUN apt-get update && apt-get install -y --no-install-recommends \ gnupg2 curl ca-certificates && \ - wget -c -O - https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/${NVARCH}/3bf863cc.pub | apt-key add - && \ - echo "deb https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/${NVARCH} /" > /etc/apt/sources.list.d/cuda.list && \ - rm -rf /var/lib/apt/lists/* + curl -fsSLO https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/${NVARCH}/cuda-keyring_1.0-1_all.deb && \ + dpkg -i cuda-keyring_1.0-1_all.deb && \ + && rm -rf /var/lib/apt/lists/* ENV CUDA_VERSION 12.2.2 From e05f844415bb9c486c6c23718b21ab13e8e45c00 Mon Sep 17 00:00:00 2001 From: perekopskiy <53865202+perekopskiy@users.noreply.github.com> Date: Tue, 14 Jan 2025 14:46:26 +0200 Subject: [PATCH 23/97] chore: pin zk foundry in zk env (#3466) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ pin zk foundry in zk env ## Why ❔ ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --------- Co-authored-by: Random Superher <152957193+randomsuperher@users.noreply.github.com> --- docker/zk-environment/22.04_amd64_cuda_11_8.Dockerfile | 2 +- docker/zk-environment/22.04_amd64_cuda_12.Dockerfile | 2 +- docker/zk-environment/Dockerfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/zk-environment/22.04_amd64_cuda_11_8.Dockerfile b/docker/zk-environment/22.04_amd64_cuda_11_8.Dockerfile index 2715063667b6..990f813852d1 100644 --- a/docker/zk-environment/22.04_amd64_cuda_11_8.Dockerfile +++ b/docker/zk-environment/22.04_amd64_cuda_11_8.Dockerfile @@ -84,7 +84,7 @@ RUN cargo install --version=0.8.0 sqlx-cli RUN cargo install cargo-nextest RUN git clone https://github.com/matter-labs/foundry-zksync -RUN cd foundry-zksync && cargo build --release --bins +RUN cd foundry-zksync && git reset --hard 27360d4c8d12beddbb730dae07ad33a206b38f4b && cargo build --release --bins RUN mv ./foundry-zksync/target/release/forge /usr/local/cargo/bin/ RUN mv ./foundry-zksync/target/release/cast /usr/local/cargo/bin/ diff --git a/docker/zk-environment/22.04_amd64_cuda_12.Dockerfile b/docker/zk-environment/22.04_amd64_cuda_12.Dockerfile index a09dda2ae683..7402a8c49101 100644 --- a/docker/zk-environment/22.04_amd64_cuda_12.Dockerfile +++ b/docker/zk-environment/22.04_amd64_cuda_12.Dockerfile @@ -82,7 +82,7 @@ RUN cargo install --version=0.8.0 sqlx-cli RUN cargo install cargo-nextest RUN git clone https://github.com/matter-labs/foundry-zksync -RUN cd foundry-zksync && cargo build --release --bins +RUN cd foundry-zksync && git reset --hard 27360d4c8d12beddbb730dae07ad33a206b38f4b && cargo build --release --bins RUN mv ./foundry-zksync/target/release/forge /usr/local/cargo/bin/ RUN mv ./foundry-zksync/target/release/cast /usr/local/cargo/bin/ diff --git a/docker/zk-environment/Dockerfile b/docker/zk-environment/Dockerfile index c04e5720e4d7..f17347b2ef39 100644 --- a/docker/zk-environment/Dockerfile +++ b/docker/zk-environment/Dockerfile @@ -47,7 +47,7 @@ RUN cargo install cargo-spellcheck RUN cargo install sccache RUN git clone https://github.com/matter-labs/foundry-zksync -RUN cd foundry-zksync && cargo build --release --bins +RUN cd foundry-zksync && git reset --hard 27360d4c8d12beddbb730dae07ad33a206b38f4b && cargo build --release --bins RUN mv ./foundry-zksync/target/release/forge /usr/local/cargo/bin/ RUN mv ./foundry-zksync/target/release/cast /usr/local/cargo/bin/ From 770cad84d7bbcc52da27f0f1c11a71a1f9f1cf22 Mon Sep 17 00:00:00 2001 From: Artem Makhortov <13339874+artmakh@users.noreply.github.com> Date: Tue, 14 Jan 2025 20:14:56 +0700 Subject: [PATCH 24/97] fix: Zk-environment fix Nvidia repo add (#3472) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Proper Nvidia repo add ## Why ❔ ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- docker/zk-environment/22.04_amd64_cuda_11_8.Dockerfile | 3 +-- docker/zk-environment/22.04_amd64_cuda_12.Dockerfile | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docker/zk-environment/22.04_amd64_cuda_11_8.Dockerfile b/docker/zk-environment/22.04_amd64_cuda_11_8.Dockerfile index 990f813852d1..37505a098575 100644 --- a/docker/zk-environment/22.04_amd64_cuda_11_8.Dockerfile +++ b/docker/zk-environment/22.04_amd64_cuda_11_8.Dockerfile @@ -128,8 +128,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ gnupg2 curl ca-certificates && \ curl -fsSLO https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/${NVARCH}/cuda-keyring_1.0-1_all.deb && \ dpkg -i cuda-keyring_1.0-1_all.deb && \ - apt-get purge --autoremove -y curl \ - && rm -rf /var/lib/apt/lists/* + rm -rf /var/lib/apt/lists/* ENV CUDA_VERSION 11.8.0 diff --git a/docker/zk-environment/22.04_amd64_cuda_12.Dockerfile b/docker/zk-environment/22.04_amd64_cuda_12.Dockerfile index 7402a8c49101..b3230edb8c07 100644 --- a/docker/zk-environment/22.04_amd64_cuda_12.Dockerfile +++ b/docker/zk-environment/22.04_amd64_cuda_12.Dockerfile @@ -129,7 +129,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ gnupg2 curl ca-certificates && \ curl -fsSLO https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/${NVARCH}/cuda-keyring_1.0-1_all.deb && \ dpkg -i cuda-keyring_1.0-1_all.deb && \ - && rm -rf /var/lib/apt/lists/* + rm -rf /var/lib/apt/lists/* ENV CUDA_VERSION 12.2.2 From 8518b3e9573577aba64af8ddaffa91634fdfa0a0 Mon Sep 17 00:00:00 2001 From: Artem Makhortov <13339874+artmakh@users.noreply.github.com> Date: Tue, 14 Jan 2025 21:12:26 +0700 Subject: [PATCH 25/97] fix(ci): Proper GPU forwarding in docker compose (#3473) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Fix for GPU forwarding in docker compose ## Why ❔ Docker update broke old way of forwarding GPU ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- docker-compose-gpu-runner-cuda-12-0.yml | 4 +++- docker-compose-gpu-runner.yml | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docker-compose-gpu-runner-cuda-12-0.yml b/docker-compose-gpu-runner-cuda-12-0.yml index 0bc2b6cedb99..dd2311db7bd8 100644 --- a/docker-compose-gpu-runner-cuda-12-0.yml +++ b/docker-compose-gpu-runner-cuda-12-0.yml @@ -59,7 +59,9 @@ services: resources: reservations: devices: - - capabilities: [ gpu ] + - driver: nvidia + count: all + capabilities: [gpu] postgres: image: "postgres:14" diff --git a/docker-compose-gpu-runner.yml b/docker-compose-gpu-runner.yml index 32665eb7010a..807e567de647 100644 --- a/docker-compose-gpu-runner.yml +++ b/docker-compose-gpu-runner.yml @@ -49,7 +49,9 @@ services: resources: reservations: devices: - - capabilities: [ gpu ] + - driver: nvidia + count: all + capabilities: [gpu] postgres: image: "postgres:14" command: postgres -c 'max_connections=200' From c156e791c1d8bbdea32873f43e976d35b228d38e Mon Sep 17 00:00:00 2001 From: Albert Lai Date: Tue, 14 Jan 2025 09:12:16 -0800 Subject: [PATCH 26/97] fix(zkstack): shouldn't attempt to create db on migrate command (#3468) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ `zkstack dev migrate` attempts to create a database first before performing the migration. It should instead only try to perform the migration, and fail if the database does not exist. ## Why ❔ Keeping the create database command may potentially drop and recreate the database - the current behaviour of `cargo sqlx database create` doesn't do this, but it may change in the future. Furthermore, even if the database doesn't exist, it doesn't make sense for a migrate command to perform database creation. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- .../zkstack/src/commands/dev/commands/database/migrate.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/migrate.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/migrate.rs index e67d4a5bf29d..8c21262c0712 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/migrate.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/database/migrate.rs @@ -42,11 +42,6 @@ fn migrate_database(shell: &Shell, link_to_code: impl AsRef, dal: Dal) -> MSG_DATABASE_MIGRATE_GERUND, &dal.path, )); - Cmd::new(cmd!( - shell, - "cargo sqlx database create --database-url {url}" - )) - .run()?; Cmd::new(cmd!(shell, "cargo sqlx migrate run --database-url {url}")).run()?; spinner.finish(); From f06cb79883bf320f50089099e0abeb95eaace470 Mon Sep 17 00:00:00 2001 From: Stanislav Bezkorovainyi Date: Wed, 15 Jan 2025 10:43:58 +0100 Subject: [PATCH 27/97] feat(contracts)!: gateway integration (#1934) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ - v26 contracts - zkstack_cli compatible with v26 contracts & scripts - final server improvements for v26, ProtocolVersionId::latest() is set to v26 - prover version is set to v26 ## Why ❔ ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zk fmt` and `zk lint`. - [ ] Spellcheck has been run via `zk spellcheck`. --------- Co-authored-by: dimazhornyk Co-authored-by: perekopskiy Co-authored-by: perekopskiy <53865202+perekopskiy@users.noreply.github.com> Co-authored-by: Dima Zhornyk <55756184+dimazhornyk@users.noreply.github.com> Co-authored-by: Marcin M <128217157+mm-zk@users.noreply.github.com> Co-authored-by: koloz193 --- .../build-contract-verifier-template.yml | 2 +- .github/workflows/build-core-template.yml | 2 +- .github/workflows/ci-core-reusable.yml | 125 ++- .github/workflows/ci-prover-e2e.yml | 23 +- .github/workflows/ci-prover-reusable.yml | 1 + .github/workflows/ci.yml | 1 + .github/workflows/vm-perf-comparison.yml | 7 +- Cargo.lock | 2 +- contracts | 2 +- core/bin/external_node/src/config/mod.rs | 6 + core/lib/basic_types/src/protocol_version.rs | 10 +- core/lib/basic_types/src/vm.rs | 2 +- core/lib/config/src/configs/contracts.rs | 8 +- core/lib/config/src/testonly.rs | 2 +- core/lib/contracts/src/lib.rs | 77 +- ...9e41923477ed8dc179ca0e1fe64b4700e6572.json | 20 + core/lib/dal/src/eth_sender_dal.rs | 19 + core/lib/env_config/src/contracts.rs | 33 +- core/lib/env_config/src/eth_sender.rs | 1 - core/lib/env_config/src/wallets.rs | 1 + .../src/i_executor/methods/commit_batches.rs | 3 +- .../src/i_executor/methods/prove_batches.rs | 16 +- .../src/i_executor/structures/mod.rs | 4 +- core/lib/multivm/src/versions/shadow/tests.rs | 1 - .../multivm/src/versions/testonly/circuits.rs | 15 +- .../src/versions/testonly/l1_messenger.rs | 15 +- .../src/versions/testonly/l2_blocks.rs | 94 +- .../multivm/src/versions/testonly/refunds.rs | 2 +- .../versions/vm_fast/tests/l1_messenger.rs | 1 - .../versions/vm_latest/tests/l1_messenger.rs | 1 - core/lib/multivm/src/versions/vm_latest/vm.rs | 2 +- core/lib/protobuf_config/src/contracts.rs | 16 +- .../src/proto/config/contracts.proto | 3 +- core/lib/types/src/abi.rs | 6 +- core/lib/types/src/commitment/mod.rs | 1 + core/lib/types/src/protocol_upgrade.rs | 33 +- core/lib/types/src/system_contracts.rs | 50 +- core/lib/vm_executor/src/batch/factory.rs | 1 + core/lib/vm_executor/src/oneshot/contracts.rs | 6 +- core/lib/web3_decl/src/namespaces/unstable.rs | 3 + core/lib/zksync_core_leftovers/src/lib.rs | 1 - .../backend_jsonrpsee/namespaces/unstable.rs | 6 + .../node/api_server/src/web3/namespaces/en.rs | 1 + .../src/web3/namespaces/unstable/mod.rs | 12 + core/node/api_server/src/web3/state.rs | 9 + .../src/base_token_l1_behaviour.rs | 1 + core/node/commitment_generator/src/lib.rs | 3 +- core/node/consistency_checker/src/lib.rs | 10 +- .../node/consistency_checker/src/tests/mod.rs | 7 +- core/node/da_clients/Cargo.toml | 1 + core/node/da_clients/src/avail/client.rs | 32 +- core/node/eth_sender/Cargo.toml | 1 - core/node/eth_watch/src/client.rs | 184 ++-- .../decentralized_upgrades.rs | 15 +- core/node/eth_watch/src/lib.rs | 24 +- core/node/eth_watch/src/tests/client.rs | 28 +- core/node/eth_watch/src/tests/mod.rs | 14 +- core/node/genesis/src/lib.rs | 12 +- .../layers/eth_sender/manager.rs | 27 +- .../src/implementations/layers/eth_watch.rs | 14 +- .../web3_api/server/bridge_addresses.rs | 48 +- .../layers/web3_api/server/mod.rs | 44 +- .../src/main_node/genesis.rs | 17 +- .../src/tree_data_fetcher/provider/mod.rs | 10 +- .../src/tree_data_fetcher/provider/tests.rs | 7 +- .../state_keeper/src/executor/tests/mod.rs | 249 +++++- .../state_keeper/src/executor/tests/tester.rs | 119 ++- core/node/state_keeper/src/keeper.rs | 6 +- core/node/state_keeper/src/testonly/mod.rs | 78 +- core/node/state_keeper/src/tests/mod.rs | 2 +- core/tests/loadnext/src/executor.rs | 16 +- core/tests/loadnext/src/sdk/abi/update-abi.sh | 2 +- core/tests/loadnext/src/sdk/ethereum/mod.rs | 2 +- core/tests/revert-test/tests/utils.ts | 5 +- core/tests/ts-integration/package.json | 8 +- .../tests/ts-integration/src/context-owner.ts | 2 +- core/tests/ts-integration/src/env.ts | 7 +- core/tests/ts-integration/src/helpers.ts | 16 +- .../src/modifiers/balance-checker.ts | 1 - core/tests/ts-integration/src/types.ts | 1 + .../ts-integration/tests/api/web3.test.ts | 5 +- .../ts-integration/tests/base-token.test.ts | 7 +- .../ts-integration/tests/contracts.test.ts | 12 +- core/tests/ts-integration/tests/erc20.test.ts | 22 +- core/tests/ts-integration/tests/ether.test.ts | 15 +- core/tests/ts-integration/tests/fees.test.ts | 23 +- core/tests/ts-integration/tests/l1.test.ts | 10 +- .../ts-integration/tests/l2-erc20.test.ts | 262 ++++++ .../tests/ts-integration/tests/system.test.ts | 8 +- core/tests/upgrade-test/tests/upgrade.test.ts | 115 ++- core/tests/upgrade-test/tests/utils.ts | 14 +- docs/src/specs/l1_smart_contracts.md | 2 +- etc/env/base/chain.toml | 4 +- etc/env/base/contracts.toml | 46 +- etc/env/file_based/general.yaml | 2 +- etc/env/file_based/genesis.yaml | 14 +- etc/multivm_bootloaders/vm_gateway/commit | 2 +- .../fee_estimate.yul/fee_estimate.yul.zbin | Bin 75296 -> 74016 bytes .../vm_gateway/gas_test.yul/gas_test.yul.zbin | Bin 71392 -> 69280 bytes .../playground_batch.yul.zbin | Bin 75424 -> 74208 bytes .../proved_batch.yul/proved_batch.yul.zbin | Bin 71904 -> 69792 bytes .../local-gateway-upgrade-testing/README.md | 118 +++ .../era-cacher/use-new-era.sh | 27 + .../era-cacher/use-old-era.sh | 16 + package.json | 2 + prover/crates/lib/prover_fri_types/src/lib.rs | 2 +- yarn.lock | 158 +++- zkstack_cli/Cargo.lock | 31 + zkstack_cli/Cargo.toml | 1 + zkstack_cli/crates/common/src/contracts.rs | 25 +- zkstack_cli/crates/common/src/forge.rs | 20 + zkstack_cli/crates/config/Cargo.toml | 1 + zkstack_cli/crates/config/src/chain.rs | 32 +- zkstack_cli/crates/config/src/consts.rs | 2 +- zkstack_cli/crates/config/src/contracts.rs | 81 +- .../forge_interface/deploy_ecosystem/input.rs | 8 + .../deploy_ecosystem/output.rs | 57 +- .../deploy_gateway_ctm/input.rs | 2 - .../forge_interface/deploy_gateway_ctm/mod.rs | 1 - .../deploy_gateway_ctm/output.rs | 1 - .../deploy_l2_contracts/input.rs | 15 +- .../deploy_l2_contracts/output.rs | 3 +- .../gateway_chain_upgrade/input.rs | 1 - .../gateway_chain_upgrade/mod.rs | 1 - .../gateway_chain_upgrade/output.rs | 1 - .../gateway_ecosystem_upgrade/input.rs | 4 +- .../gateway_ecosystem_upgrade/mod.rs | 1 - .../gateway_ecosystem_upgrade/output.rs | 11 +- .../gateway_preparation/input.rs | 1 - .../gateway_preparation/mod.rs | 1 - .../gateway_preparation/output.rs | 1 - .../forge_interface/register_chain/input.rs | 58 +- .../forge_interface/register_chain/output.rs | 3 + .../src/forge_interface/script_params.rs | 12 +- .../setup_legacy_bridge/mod.rs | 2 + zkstack_cli/crates/config/src/gateway.rs | 1 - zkstack_cli/crates/types/src/l1_network.rs | 13 + zkstack_cli/crates/zkstack/Cargo.toml | 11 +- .../crates/zkstack/completion/_zkstack.zsh | 62 +- .../crates/zkstack/completion/zkstack.fish | 78 +- .../crates/zkstack/completion/zkstack.sh | 131 +-- .../crates/zkstack/src/accept_ownership.rs | 36 +- .../src/commands/chain/args/init/configs.rs | 7 +- .../commands/chain/args/init/da_configs.rs | 146 ++++ .../src/commands/chain/args/init/mod.rs | 27 +- .../src/commands/chain/build_transactions.rs | 11 +- .../src/commands/chain/convert_to_gateway.rs | 13 +- .../zkstack/src/commands/chain/create.rs | 2 +- .../src/commands/chain/deploy_l2_contracts.rs | 72 +- .../src/commands/chain/gateway_upgrade.rs | 413 ++++----- .../src/commands/chain/init/configs.rs | 38 +- .../zkstack/src/commands/chain/init/mod.rs | 79 +- .../commands/chain/migrate_from_gateway.rs | 5 - .../src/commands/chain/migrate_to_gateway.rs | 5 - .../crates/zkstack/src/commands/chain/mod.rs | 37 +- .../chain/set_token_multiplier_setter.rs | 23 +- .../src/commands/chain/setup_legacy_bridge.rs | 8 + .../zkstack/src/commands/chain/utils.rs | 12 + .../src/commands/dev/commands/contracts.rs | 22 +- .../src/commands/dev/commands/gateway.rs | 815 ++++++++++++++++++ .../zkstack/src/commands/dev/commands/mod.rs | 2 + .../commands/dev/commands/test/integration.rs | 2 +- .../zkstack/src/commands/dev/messages.rs | 5 + .../crates/zkstack/src/commands/dev/mod.rs | 9 +- .../src/commands/ecosystem/args/create.rs | 2 +- .../ecosystem/args/gateway_upgrade.rs | 1 - .../src/commands/ecosystem/args/init.rs | 16 +- .../commands/ecosystem/build_transactions.rs | 1 + .../zkstack/src/commands/ecosystem/common.rs | 5 + .../src/commands/ecosystem/gateway_upgrade.rs | 645 ++++++++++++++ .../zkstack/src/commands/ecosystem/init.rs | 39 +- .../zkstack/src/commands/ecosystem/mod.rs | 7 + .../zkstack/src/commands/ecosystem/utils.rs | 5 + .../commands/external_node/prepare_configs.rs | 3 +- .../crates/zkstack/src/commands/server.rs | 16 +- zkstack_cli/crates/zkstack/src/defaults.rs | 3 + zkstack_cli/crates/zkstack/src/messages.rs | 15 + zkstack_cli/zkstackup/zkstackup | 42 +- 178 files changed, 4585 insertions(+), 1222 deletions(-) create mode 100644 core/lib/dal/.sqlx/query-33d49ec6028974fa8b46d7bf1f79e41923477ed8dc179ca0e1fe64b4700e6572.json create mode 100644 core/tests/ts-integration/tests/l2-erc20.test.ts create mode 100644 infrastructure/local-gateway-upgrade-testing/README.md create mode 100755 infrastructure/local-gateway-upgrade-testing/era-cacher/use-new-era.sh create mode 100755 infrastructure/local-gateway-upgrade-testing/era-cacher/use-old-era.sh create mode 100644 zkstack_cli/crates/zkstack/src/commands/chain/args/init/da_configs.rs create mode 100644 zkstack_cli/crates/zkstack/src/commands/chain/utils.rs create mode 100644 zkstack_cli/crates/zkstack/src/commands/dev/commands/gateway.rs create mode 100644 zkstack_cli/crates/zkstack/src/commands/ecosystem/gateway_upgrade.rs diff --git a/.github/workflows/build-contract-verifier-template.yml b/.github/workflows/build-contract-verifier-template.yml index 9c53ca1dc484..973ed9edcb4d 100644 --- a/.github/workflows/build-contract-verifier-template.yml +++ b/.github/workflows/build-contract-verifier-template.yml @@ -100,7 +100,7 @@ jobs: if: env.BUILD_CONTRACTS == 'true' run: | mkdir ./foundry-zksync - curl -LO https://github.com/matter-labs/foundry-zksync/releases/download/nightly-15bec2f861b3b4c71e58f85e2b2c9dd722585aa8/foundry_nightly_linux_amd64.tar.gz + curl -LO https://github.com/matter-labs/foundry-zksync/releases/download/nightly-27360d4c8d12beddbb730dae07ad33a206b38f4b/foundry_nightly_linux_amd64.tar.gz tar zxf foundry_nightly_linux_amd64.tar.gz -C ./foundry-zksync chmod +x ./foundry-zksync/forge ./foundry-zksync/cast echo "$PWD/foundry-zksync" >> $GITHUB_PATH diff --git a/.github/workflows/build-core-template.yml b/.github/workflows/build-core-template.yml index c76a75e9b3ea..122bbd747147 100644 --- a/.github/workflows/build-core-template.yml +++ b/.github/workflows/build-core-template.yml @@ -105,7 +105,7 @@ jobs: if: env.BUILD_CONTRACTS == 'true' run: | mkdir ./foundry-zksync - curl -LO https://github.com/matter-labs/foundry-zksync/releases/download/nightly-15bec2f861b3b4c71e58f85e2b2c9dd722585aa8/foundry_nightly_linux_amd64.tar.gz + curl -LO https://github.com/matter-labs/foundry-zksync/releases/download/nightly-27360d4c8d12beddbb730dae07ad33a206b38f4b/foundry_nightly_linux_amd64.tar.gz tar zxf foundry_nightly_linux_amd64.tar.gz -C ./foundry-zksync chmod +x ./foundry-zksync/forge ./foundry-zksync/cast echo "$PWD/foundry-zksync" >> $GITHUB_PATH diff --git a/.github/workflows/ci-core-reusable.yml b/.github/workflows/ci-core-reusable.yml index d76bb776968d..25a0da838f42 100644 --- a/.github/workflows/ci-core-reusable.yml +++ b/.github/workflows/ci-core-reusable.yml @@ -65,15 +65,12 @@ jobs: - name: Install zkstack run: | ci_run ./zkstack_cli/zkstackup/install -g --path ./zkstack_cli/zkstackup/zkstackup - ci_run zkstackup -g --local + ci_run zkstackup -g --local --cargo-features gateway - name: Build contracts run: | ci_run zkstack dev contracts - - name: Contracts unit tests - run: ci_run yarn l1-contracts test - - name: Download compilers for contract verifier tests run: ci_run zkstack contract-verifier init --zksolc-version=v1.5.3 --zkvyper-version=v1.5.4 --solc-version=0.8.26 --vyper-version=v0.3.10 --era-vm-solc-version=0.8.26-1.0.1 --only --chain era @@ -131,7 +128,7 @@ jobs: - name: Install zkstack run: | ci_run ./zkstack_cli/zkstackup/install -g --path ./zkstack_cli/zkstackup/zkstackup || true - ci_run zkstackup -g --local + ci_run zkstackup -g --local --cargo-features gateway - name: Create and initialize legacy chain @@ -150,7 +147,7 @@ jobs: --legacy-bridge \ --evm-emulator false - ci_run zkstack ecosystem init --dev --verbose + ci_run zkstack ecosystem init --dev --support-l2-legacy-shared-bridge-test true --verbose # `sleep 60` because we need to wait until server added all the tokens - name: Run server @@ -174,13 +171,22 @@ jobs: integration-tests: runs-on: [ matterlabs-ci-runner-ultra-performance ] + strategy: + # ---------------------------------------------- + # Note, that while the contracts do support gateway chain + # in reality it won't exist for quite some time and so + # we will test both cases here + # ---------------------------------------------- + matrix: + use_gateway_chain: [ "WITH_GATEWAY", "WITHOUT_GATEWAY" ] + # In some cases it's useful to continue one job even if another fails. + fail-fast: false steps: - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4 with: submodules: "recursive" fetch-depth: 0 - - name: Setup environment run: | echo ZKSYNC_HOME=$(pwd) >> $GITHUB_ENV @@ -200,7 +206,7 @@ jobs: - name: Install zkstack run: | ci_run ./zkstack_cli/zkstackup/install -g --path ./zkstack_cli/zkstackup/zkstackup || true - ci_run zkstackup -g --local + ci_run zkstackup -g --local --cargo-features gateway - name: Create log directories run: | @@ -270,7 +276,8 @@ jobs: --l1-rpc-url=http://localhost:8545 \ --server-db-url=postgres://postgres:notsecurepassword@localhost:5432 \ --server-db-name=zksync_server_localhost_validium \ - --chain validium + --chain validium \ + --validium-type no-da - name: Create and initialize chain with Custom Token run: | @@ -292,7 +299,8 @@ jobs: --l1-rpc-url=http://localhost:8545 \ --server-db-url=postgres://postgres:notsecurepassword@localhost:5432 \ --server-db-name=zksync_server_localhost_custom_token \ - --chain custom_token + --chain custom_token \ + --validium-type no-da - name: Create and register chain with transactions signed "offline" run: | @@ -314,7 +322,7 @@ jobs: governor_pk=$(awk '/governor:/ {flag=1} flag && /private_key:/ {print $2; exit}' ./configs/wallets.yaml) ci_run zkstack dev send-transactions \ - --file ./transactions/chain/offline_chain/register-hyperchain-txns.json \ + --file ./transactions/chain/offline_chain/register-zk-chain-txns.json \ --l1-rpc-url http://127.0.0.1:8545 \ --private-key $governor_pk @@ -350,13 +358,67 @@ jobs: --l1-rpc-url=http://localhost:8545 \ --server-db-url=postgres://postgres:notsecurepassword@localhost:5432 \ --server-db-name=zksync_server_localhost_consensus \ - --chain consensus + --chain consensus \ + --validium-type no-da - name: Export chain list to environment variable run: | CHAINS="era,validium,custom_token,consensus" echo "CHAINS=$CHAINS" >> $GITHUB_ENV + # ---------------------------------------------------------------- + # Only create/initialize the gateway chain *if* use_gateway_chain=WITH_GATEWAY + # ---------------------------------------------------------------- + - name: Initialize gateway chain + if: matrix.use_gateway_chain == 'WITH_GATEWAY' + run: | + ci_run zkstack chain create \ + --chain-name gateway \ + --chain-id 505 \ + --prover-mode no-proofs \ + --wallet-creation localhost \ + --l1-batch-commit-data-generator-mode rollup \ + --base-token-address 0x0000000000000000000000000000000000000001 \ + --base-token-price-nominator 1 \ + --base-token-price-denominator 1 \ + --set-as-default false \ + --ignore-prerequisites \ + --evm-emulator false + + ci_run zkstack chain init \ + --deploy-paymaster \ + --l1-rpc-url=http://localhost:8545 \ + --server-db-url=postgres://postgres:notsecurepassword@localhost:5432 \ + --server-db-name=zksync_server_localhost_gateway \ + --chain gateway \ + --validium-type no-da + + ci_run zkstack chain convert-to-gateway --chain gateway --ignore-prerequisites + + - name: Run gateway + if: matrix.use_gateway_chain == 'WITH_GATEWAY' + run: | + ci_run zkstack server --ignore-prerequisites --chain gateway &> ${{ env.SERVER_LOGS_DIR }}/gateway.log & + ci_run zkstack server wait --ignore-prerequisites --verbose --chain gateway + + - name: Migrate chains to gateway + if: matrix.use_gateway_chain == 'WITH_GATEWAY' + run: | + ci_run zkstack chain migrate-to-gateway --chain era --gateway-chain-name gateway + ci_run zkstack chain migrate-to-gateway --chain validium --gateway-chain-name gateway + ci_run zkstack chain migrate-to-gateway --chain custom_token --gateway-chain-name gateway + ci_run zkstack chain migrate-to-gateway --chain consensus --gateway-chain-name gateway + + - name: Migrate back era + if: matrix.use_gateway_chain == 'WITH_GATEWAY' + run: | + ci_run zkstack chain migrate-from-gateway --chain era --gateway-chain-name gateway + + - name: Migrate to gateway again + if: matrix.use_gateway_chain == 'WITH_GATEWAY' + run: | + ci_run zkstack chain migrate-to-gateway --chain era --gateway-chain-name gateway + - name: Build test dependencies run: | ci_run zkstack dev test build @@ -402,20 +464,22 @@ jobs: - name: Init external nodes run: | + GATEWAY_RPC_URL="${{ matrix.use_gateway_chain == 'WITH_GATEWAY' && '--gateway-rpc-url=http://localhost:3550' || '' }}" + ci_run zkstack external-node configs --db-url=postgres://postgres:notsecurepassword@localhost:5432 \ - --db-name=zksync_en_localhost_era_rollup --l1-rpc-url=http://localhost:8545 --chain era + --db-name=zksync_en_localhost_era_rollup --l1-rpc-url=http://localhost:8545 $GATEWAY_RPC_URL --chain era ci_run zkstack external-node init --ignore-prerequisites --chain era ci_run zkstack external-node configs --db-url=postgres://postgres:notsecurepassword@localhost:5432 \ - --db-name=zksync_en_localhost_era_validium1 --l1-rpc-url=http://localhost:8545 --chain validium + --db-name=zksync_en_localhost_era_validium1 --l1-rpc-url=http://localhost:8545 $GATEWAY_RPC_URL --chain validium ci_run zkstack external-node init --ignore-prerequisites --chain validium ci_run zkstack external-node configs --db-url=postgres://postgres:notsecurepassword@localhost:5432 \ - --db-name=zksync_en_localhost_era_custom_token --l1-rpc-url=http://localhost:8545 --chain custom_token + --db-name=zksync_en_localhost_era_custom_token --l1-rpc-url=http://localhost:8545 $GATEWAY_RPC_URL --chain custom_token ci_run zkstack external-node init --ignore-prerequisites --chain custom_token ci_run zkstack external-node configs --db-url=postgres://postgres:notsecurepassword@localhost:5432 \ - --db-name=zksync_en_localhost_era_consensus --l1-rpc-url=http://localhost:8545 --chain consensus + --db-name=zksync_en_localhost_era_consensus --l1-rpc-url=http://localhost:8545 $GATEWAY_RPC_URL --chain consensus ci_run zkstack external-node init --ignore-prerequisites --chain consensus - name: Run recovery tests (from snapshot) @@ -432,7 +496,7 @@ jobs: ci_run zkstack external-node run --ignore-prerequisites --chain validium &> ${{ env.EXTERNAL_NODE_LOGS_DIR }}/validium.log & ci_run zkstack external-node run --ignore-prerequisites --chain custom_token &> ${{ env.EXTERNAL_NODE_LOGS_DIR }}/custom_token.log & ci_run zkstack external-node run --ignore-prerequisites --chain consensus --enable-consensus &> ${{ env.EXTERNAL_NODE_LOGS_DIR }}/consensus.log & - + ci_run zkstack external-node wait --ignore-prerequisites --verbose --chain era ci_run zkstack external-node wait --ignore-prerequisites --verbose --chain validium ci_run zkstack external-node wait --ignore-prerequisites --verbose --chain custom_token @@ -445,6 +509,14 @@ jobs: - name: Fee projection tests run: | ci_run killall -INT zksync_server || true + + # Only start & wait for the gateway server if use_gateway_chain == WITH_GATEWAY + if [ "${{ matrix.use_gateway_chain }}" == "WITH_GATEWAY" ]; then + ci_run zkstack server --ignore-prerequisites --chain gateway &> ${{ env.SERVER_LOGS_DIR }}/gateway.log & + ci_run zkstack server wait --ignore-prerequisites --verbose --chain gateway + fi + + # Always run the chain-specific fee tests ci_run ./bin/run_on_all_chains.sh "zkstack dev test fees --no-deps --no-kill" ${{ env.CHAINS }} ${{ env.FEES_LOGS_DIR }} - name: Run revert tests @@ -452,6 +524,13 @@ jobs: ci_run killall -INT zksync_server || true ci_run killall -INT zksync_external_node || true + # Only start & wait for the gateway server if use_gateway_chain == WITH_GATEWAY + if [ "${{ matrix.use_gateway_chain }}" == "WITH_GATEWAY" ]; then + ci_run zkstack server --ignore-prerequisites --chain gateway &> ${{ env.SERVER_LOGS_DIR }}/gateway.log & + ci_run zkstack server wait --ignore-prerequisites --verbose --chain gateway + fi + + # Always run the chain-specific revert tests ci_run ./bin/run_on_all_chains.sh "zkstack dev test revert --no-deps --external-node --no-kill --ignore-prerequisites" ${{ env.CHAINS }} ${{ env.INTEGRATION_TESTS_LOGS_DIR }} # Upgrade tests should run last, because as soon as they @@ -459,12 +538,20 @@ jobs: # TODO make upgrade tests safe to run multiple times - name: Run upgrade test run: | - ci_run zkstack dev test upgrade --no-deps --chain era + ci_run killall -INT zksync_server || true + # Only start & wait for the gateway server if use_gateway_chain == WITH_GATEWAY + if [ "${{ matrix.use_gateway_chain }}" == "WITH_GATEWAY" ]; then + ci_run zkstack server --ignore-prerequisites --chain gateway &> ${{ env.SERVER_LOGS_DIR }}/gateway.log & + ci_run zkstack server wait --ignore-prerequisites --verbose --chain gateway + fi + + # Always run the upgrade test against era + ci_run zkstack dev test upgrade --no-deps --chain era - name: Upload logs uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 if: always() with: - name: logs + name: logs_${{matrix.use_gateway_chain}} path: logs diff --git a/.github/workflows/ci-prover-e2e.yml b/.github/workflows/ci-prover-e2e.yml index 77d3e2da5758..0a09aee51315 100644 --- a/.github/workflows/ci-prover-e2e.yml +++ b/.github/workflows/ci-prover-e2e.yml @@ -43,10 +43,19 @@ jobs: run: | git fetch # Checkout the commit with the DualVerifier contract to test FFLONK interface - git checkout b4d5b984 + git checkout bcdd1cb05e8f4d9ec2dd41e2cc668cdfe30ee535 git submodule update --init --recursive git rev-parse HEAD + - name: Set new genesis for fflonk + # Note, that while `Verifier` is not explicitly a part of the genensis state, + # it affects it indirectly as it is a part of the repo. + working-directory: ./etc/env/file_based + if: matrix.compressor-mode == 'fflonk' + run: | + sudo sed -i 's/^genesis_root: .*/genesis_root: 0xc3fa60b6769a0c2f222053d7cbd1d6f63be7777e3c8d029cbd61cc075526ab81/' genesis.yaml + sudo sed -i "s/^genesis_batch_commitment: .*/genesis_batch_commitment: 0x17689e705b5749ed0bbd53c845988d17c419697c2cb29eabab8785f1cb775b4a/" genesis.yaml + - name: Init run: | ci_run chmod -R +x ./bin @@ -88,13 +97,13 @@ jobs: - name: Run server run: | ci_run zkstack server --uring --chain=proving_chain --components=api,tree,eth,state_keeper,commitment_generator,proof_data_handler,vm_runner_protective_reads,vm_runner_bwip &>prover_logs_${{matrix.compressor-mode}}/server.log & - - name: Run Gateway + - name: Run prover gateway run: | ci_run zkstack prover run --component=gateway --docker=false &>prover_logs_${{matrix.compressor-mode}}/gateway.log & - name: Run Prover Job Monitor run: | ci_run zkstack prover run --component=prover-job-monitor --docker=false &>prover_logs_${{matrix.compressor-mode}}/prover-job-monitor.log & - - name: Wait for batch to be passed through gateway + - name: Wait for batch to be passed through prover gateway env: DATABASE_URL: postgres://postgres:notsecurepassword@localhost:5432/zksync_prover_localhost_proving_chain BATCH_NUMBER: 1 @@ -126,10 +135,10 @@ jobs: - name: Wait for batch to be executed on L1 env: - DATABASE_URL: postgres://postgres:notsecurepassword@localhost:5432/zksync_prover_localhost_proving_chain - BATCH_NUMBER: 1 - INTERVAL: 30 - TIMEOUT: 1200 + DATABASE_URL: postgres://postgres:notsecurepassword@localhost:5432/zksync_prover_localhost_proving_chain + BATCH_NUMBER: 1 + INTERVAL: 30 + TIMEOUT: 1200 run: | PASSED_ENV_VARS="BATCH_NUMBER,DATABASE_URL,URL,INTERVAL,TIMEOUT" \ ci_run ./bin/prover_checkers/batch_l1_status_checker diff --git a/.github/workflows/ci-prover-reusable.yml b/.github/workflows/ci-prover-reusable.yml index 7f719b2240db..26679cb2232f 100644 --- a/.github/workflows/ci-prover-reusable.yml +++ b/.github/workflows/ci-prover-reusable.yml @@ -1,6 +1,7 @@ name: Workflow template for CI jobs for Prover Components on: workflow_call: + jobs: lint: runs-on: [ matterlabs-ci-runner-highmem-long ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c502a5f0c205..899eaea4b445 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,7 @@ jobs: docs: ${{ steps.changed-files.outputs.docs_any_changed }} all: ${{ steps.changed-files.outputs.all_any_changed }} steps: + - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4 with: fetch-depth: 2 diff --git a/.github/workflows/vm-perf-comparison.yml b/.github/workflows/vm-perf-comparison.yml index 3520419f1337..ac83485a2c12 100644 --- a/.github/workflows/vm-perf-comparison.yml +++ b/.github/workflows/vm-perf-comparison.yml @@ -47,25 +47,26 @@ jobs: run: | run_retried docker compose pull zk docker compose up -d zk - + - name: run benchmarks on base branch shell: bash run: | ci_run zkstackup -g --local - ci_run zkstack dev contracts --system-contracts + ci_run zkstack dev contracts ci_run cargo bench --package vm-benchmark --bench instructions -- --verbose || echo "Instructions benchmark is missing" ci_run cargo run --package vm-benchmark --release --bin instruction_counts | tee base-opcodes - name: checkout PR run: | git checkout --force FETCH_HEAD --recurse-submodules + git submodule update --init --recursive - name: run benchmarks on PR shell: bash id: comparison run: | ci_run zkstackup -g --local - ci_run zkstack dev contracts --system-contracts + ci_run zkstack dev contracts ci_run cargo bench --package vm-benchmark --bench instructions -- --verbose ci_run cargo bench --package vm-benchmark --bench instructions -- --print > instructions.log 2>/dev/null diff --git a/Cargo.lock b/Cargo.lock index d6e7503bd20f..4744b424cea0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11810,6 +11810,7 @@ dependencies = [ "tokio-stream", "tonic 0.11.0", "tracing", + "url", "zksync_basic_types", "zksync_config", "zksync_da_client", @@ -11931,7 +11932,6 @@ dependencies = [ "assert_matches", "async-trait", "chrono", - "once_cell", "serde", "test-casing", "test-log", diff --git a/contracts b/contracts index 46d75088e7dd..16dedf6d7769 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 46d75088e7ddb534101874c3ec15b877da1cb417 +Subproject commit 16dedf6d77695ce00f81fce35a3066381b97fca1 diff --git a/core/bin/external_node/src/config/mod.rs b/core/bin/external_node/src/config/mod.rs index db2c6eac9cf6..235802e1073b 100644 --- a/core/bin/external_node/src/config/mod.rs +++ b/core/bin/external_node/src/config/mod.rs @@ -128,6 +128,7 @@ pub(crate) struct RemoteENConfig { pub l2_weth_bridge_addr: Option
, pub l2_testnet_paymaster_addr: Option
, pub l2_timestamp_asserter_addr: Option
, + pub l1_wrapped_base_token_store: Option
, pub base_token_addr: Address, pub l1_batch_commit_data_generator_mode: L1BatchCommitmentMode, pub dummy_verifier: bool, @@ -195,6 +196,9 @@ impl RemoteENConfig { l1_bytecodes_supplier_addr: ecosystem_contracts .as_ref() .and_then(|a| a.l1_bytecodes_supplier_addr), + l1_wrapped_base_token_store: ecosystem_contracts + .as_ref() + .and_then(|a| a.l1_wrapped_base_token_store), l1_diamond_proxy_addr, l2_testnet_paymaster_addr, l1_erc20_bridge_proxy_addr: bridges.l1_erc20_default_bridge, @@ -235,6 +239,7 @@ impl RemoteENConfig { l2_shared_bridge_addr: Some(Address::repeat_byte(6)), l2_legacy_shared_bridge_addr: Some(Address::repeat_byte(7)), l1_batch_commit_data_generator_mode: L1BatchCommitmentMode::Rollup, + l1_wrapped_base_token_store: None, dummy_verifier: true, l2_timestamp_asserter_addr: None, } @@ -1477,6 +1482,7 @@ impl From<&ExternalNodeConfig> for InternalApiConfig { l2_weth_bridge: config.remote.l2_weth_bridge_addr, }, l1_bytecodes_supplier_addr: config.remote.l1_bytecodes_supplier_addr, + l1_wrapped_base_token_store: config.remote.l1_wrapped_base_token_store, l1_bridgehub_proxy_addr: config.remote.l1_bridgehub_proxy_addr, l1_state_transition_proxy_addr: config.remote.l1_state_transition_proxy_addr, l1_transparent_proxy_admin_addr: config.remote.l1_transparent_proxy_admin_addr, diff --git a/core/lib/basic_types/src/protocol_version.rs b/core/lib/basic_types/src/protocol_version.rs index 89251b3a2a40..5d896040f760 100644 --- a/core/lib/basic_types/src/protocol_version.rs +++ b/core/lib/basic_types/src/protocol_version.rs @@ -75,11 +75,11 @@ pub enum ProtocolVersionId { impl ProtocolVersionId { pub const fn latest() -> Self { - Self::Version25 + Self::Version26 } pub const fn next() -> Self { - Self::Version26 + Self::Version27 } pub fn try_from_packed_semver(packed_semver: U256) -> Result { @@ -123,7 +123,7 @@ impl ProtocolVersionId { ProtocolVersionId::Version23 => VmVersion::Vm1_5_0SmallBootloaderMemory, ProtocolVersionId::Version24 => VmVersion::Vm1_5_0IncreasedBootloaderMemory, ProtocolVersionId::Version25 => VmVersion::Vm1_5_0IncreasedBootloaderMemory, - ProtocolVersionId::Version26 => VmVersion::Vm1_5_0IncreasedBootloaderMemory, + ProtocolVersionId::Version26 => VmVersion::VmGateway, ProtocolVersionId::Version27 => VmVersion::VmGateway, ProtocolVersionId::Version28 => unreachable!("Version 28 is not yet supported"), } @@ -192,7 +192,7 @@ impl ProtocolVersionId { } pub const fn gateway_upgrade() -> Self { - ProtocolVersionId::Version27 + ProtocolVersionId::Version26 } } @@ -298,7 +298,7 @@ impl From for VmVersion { ProtocolVersionId::Version23 => VmVersion::Vm1_5_0SmallBootloaderMemory, ProtocolVersionId::Version24 => VmVersion::Vm1_5_0IncreasedBootloaderMemory, ProtocolVersionId::Version25 => VmVersion::Vm1_5_0IncreasedBootloaderMemory, - ProtocolVersionId::Version26 => VmVersion::Vm1_5_0IncreasedBootloaderMemory, + ProtocolVersionId::Version26 => VmVersion::VmGateway, ProtocolVersionId::Version27 => VmVersion::VmGateway, ProtocolVersionId::Version28 => unreachable!("Version 28 is not yet supported"), } diff --git a/core/lib/basic_types/src/vm.rs b/core/lib/basic_types/src/vm.rs index f11f98596f18..4469785c7411 100644 --- a/core/lib/basic_types/src/vm.rs +++ b/core/lib/basic_types/src/vm.rs @@ -22,7 +22,7 @@ pub enum VmVersion { impl VmVersion { /// Returns the latest supported VM version. pub const fn latest() -> VmVersion { - Self::Vm1_5_0IncreasedBootloaderMemory + Self::VmGateway } } diff --git a/core/lib/config/src/configs/contracts.rs b/core/lib/config/src/configs/contracts.rs index f6bd02f2dfae..561e51fa5dd5 100644 --- a/core/lib/config/src/configs/contracts.rs +++ b/core/lib/config/src/configs/contracts.rs @@ -9,6 +9,10 @@ pub struct EcosystemContracts { pub state_transition_proxy_addr: Address, pub transparent_proxy_admin_addr: Address, pub l1_bytecodes_supplier_addr: Option
, + // Note that on the contract side of things this contract is called `L2WrappedBaseTokenStore`, + // while on the server side for consistency with the conventions, where the prefix denotes + // the location of the contracts we call it `l1_wrapped_base_token_store` + pub l1_wrapped_base_token_store: Option
, } impl EcosystemContracts { @@ -18,6 +22,7 @@ impl EcosystemContracts { state_transition_proxy_addr: Address::repeat_byte(0x15), transparent_proxy_admin_addr: Address::repeat_byte(0x15), l1_bytecodes_supplier_addr: Some(Address::repeat_byte(0x16)), + l1_wrapped_base_token_store: Some(Address::repeat_byte(0x17)), } } } @@ -50,8 +55,6 @@ pub struct ContractsConfig { pub base_token_addr: Option
, pub l1_base_token_asset_id: Option, - pub l2_predeployed_wrapped_base_token_address: Option
, - pub chain_admin_addr: Option
, pub l2_da_validator_addr: Option
, } @@ -76,7 +79,6 @@ impl ContractsConfig { governance_addr: Address::repeat_byte(0x13), base_token_addr: Some(Address::repeat_byte(0x14)), l1_base_token_asset_id: Some(H256::repeat_byte(0x15)), - l2_predeployed_wrapped_base_token_address: Some(Address::repeat_byte(0x1b)), ecosystem_contracts: Some(EcosystemContracts::for_tests()), chain_admin_addr: Some(Address::repeat_byte(0x18)), l2_da_validator_addr: Some(Address::repeat_byte(0x1a)), diff --git a/core/lib/config/src/testonly.rs b/core/lib/config/src/testonly.rs index 3472cf4e7d0a..431fa406d109 100644 --- a/core/lib/config/src/testonly.rs +++ b/core/lib/config/src/testonly.rs @@ -268,7 +268,6 @@ impl Distribution for EncodeDist { ecosystem_contracts: self.sample(rng), base_token_addr: self.sample_opt(|| rng.gen()), l1_base_token_asset_id: self.sample_opt(|| rng.gen()), - l2_predeployed_wrapped_base_token_address: self.sample_opt(|| rng.gen()), chain_admin_addr: self.sample_opt(|| rng.gen()), l2_da_validator_addr: self.sample_opt(|| rng.gen()), } @@ -763,6 +762,7 @@ impl Distribution for EncodeDist { state_transition_proxy_addr: rng.gen(), transparent_proxy_admin_addr: rng.gen(), l1_bytecodes_supplier_addr: rng.gen(), + l1_wrapped_base_token_store: rng.gen(), } } } diff --git a/core/lib/contracts/src/lib.rs b/core/lib/contracts/src/lib.rs index 9ca679fef899..87f102be39d6 100644 --- a/core/lib/contracts/src/lib.rs +++ b/core/lib/contracts/src/lib.rs @@ -37,24 +37,39 @@ const FORGE_PATH_PREFIX: &str = "contracts/l1-contracts/out"; const BRIDGEHUB_CONTRACT_FILE: (&str, &str) = ("bridgehub", "IBridgehub.sol/IBridgehub.json"); const STATE_TRANSITION_CONTRACT_FILE: (&str, &str) = ( "state-transition", - "StateTransitionManager.sol/StateTransitionManager.json", + "ChainTypeManager.sol/ChainTypeManager.json", ); +const BYTECODE_SUPPLIER_CONTRACT_FILE: (&str, &str) = + ("upgrades", "BytecodesSupplier.sol/BytecodesSupplier.json"); const ZKSYNC_HYPERCHAIN_CONTRACT_FILE: (&str, &str) = ( "state-transition/chain-interfaces", - "IZkSyncHyperchain.sol/IZkSyncHyperchain.json", + "IZKChain.sol/IZKChain.json", ); const DIAMOND_INIT_CONTRACT_FILE: (&str, &str) = ( "state-transition", "chain-interfaces/IDiamondInit.sol/IDiamondInit.json", ); const GOVERNANCE_CONTRACT_FILE: (&str, &str) = ("governance", "IGovernance.sol/IGovernance.json"); -const CHAIN_ADMIN_CONTRACT_FILE: (&str, &str) = ("governance", "IChainAdmin.sol/IChainAdmin.json"); +// TODO(EVM-924): We currently only support the "Ownable" chain admin. +const CHAIN_ADMIN_CONTRACT_FILE: (&str, &str) = ( + "governance", + "IChainAdminOwnable.sol/IChainAdminOwnable.json", +); const GETTERS_FACET_CONTRACT_FILE: (&str, &str) = ( "state-transition/chain-interfaces", "IGetters.sol/IGetters.json", ); const MULTICALL3_CONTRACT_FILE: (&str, &str) = ("dev-contracts", "Multicall3.sol/Multicall3.json"); +const L1_ASSET_ROUTER_FILE: (&str, &str) = ( + "bridge/asset-router", + "L1AssetRouter.sol/L1AssetRouter.json", +); +const L2_WRAPPED_BASE_TOKEN_STORE: (&str, &str) = ( + "bridge", + "L2WrappedBaseTokenStore.sol/L2WrappedBaseTokenStore.json", +); + const VERIFIER_CONTRACT_FILE: (&str, &str) = ("state-transition", "Verifier.sol/Verifier.json"); const DUAL_VERIFIER_CONTRACT_FILE: (&str, &str) = ( "state-transition/verifiers", @@ -158,6 +173,10 @@ pub fn state_transition_manager_contract() -> Contract { load_contract_for_both_compilers(STATE_TRANSITION_CONTRACT_FILE) } +pub fn bytecode_supplier_contract() -> Contract { + load_contract_for_both_compilers(BYTECODE_SUPPLIER_CONTRACT_FILE) +} + pub fn hyperchain_contract() -> Contract { load_contract_for_both_compilers(ZKSYNC_HYPERCHAIN_CONTRACT_FILE) } @@ -170,6 +189,14 @@ pub fn multicall_contract() -> Contract { load_contract_for_both_compilers(MULTICALL3_CONTRACT_FILE) } +pub fn l1_asset_router_contract() -> Contract { + load_contract_for_both_compilers(L1_ASSET_ROUTER_FILE) +} + +pub fn wrapped_base_token_store_contract() -> Contract { + load_contract_for_both_compilers(L2_WRAPPED_BASE_TOKEN_STORE) +} + pub fn verifier_contract() -> Contract { let path = format!("{}/{}", FORGE_PATH_PREFIX, DUAL_VERIFIER_CONTRACT_FILE.1); let zksync_home = home_path(); @@ -190,6 +217,14 @@ pub fn l1_messenger_contract() -> Contract { load_sys_contract("L1Messenger") } +pub fn l2_message_root() -> Contract { + load_contract("contracts/l1-contracts/out/MessageRoot.sol/MessageRoot.json") +} + +pub fn l2_rollup_da_validator_bytecode() -> Vec { + read_bytecode("contracts/l2-contracts/zkout/RollupL2DAValidator.sol/RollupL2DAValidator.json") +} + /// Reads bytecode from the path RELATIVE to the Cargo workspace location. pub fn read_bytecode(relative_path: impl AsRef + std::fmt::Debug) -> Vec { read_bytecode_from_path(relative_path).expect("Failed to open file") @@ -719,14 +754,14 @@ pub static PRE_BOOJUM_COMMIT_FUNCTION: Lazy = Lazy::new(|| { serde_json::from_str(abi).unwrap() }); -pub static SET_CHAIN_ID_EVENT: Lazy = Lazy::new(|| { +pub static GENESIS_UPGRADE_EVENT: Lazy = Lazy::new(|| { let abi = r#" { "anonymous": false, "inputs": [ { "indexed": true, - "name": "_stateTransitionChain", + "name": "_hyperchain", "type": "address" }, { @@ -804,9 +839,14 @@ pub static SET_CHAIN_ID_EVENT: Lazy = Lazy::new(|| { "indexed": true, "name": "_protocolVersion", "type": "uint256" + }, + { + "indexed": false, + "name": "_factoryDeps", + "type": "bytes[]" } ], - "name": "SetChainIdUpgrade", + "name": "GenesisUpgrade", "type": "event" }"#; serde_json::from_str(abi).unwrap() @@ -1422,28 +1462,3 @@ pub static POST_SHARED_BRIDGE_EXECUTE_FUNCTION: Lazy = Lazy::new(|| { }"#; serde_json::from_str(abi).unwrap() }); - -// Temporary thing, should be removed when new contracts are merged. -pub static MESSAGE_ROOT_CONTRACT: Lazy = Lazy::new(|| { - let abi = r#" - [{ - "inputs": [ - { - "internalType": "uint256", - "name": "_chainId", - "type": "uint256" - } - ], - "name": "getChainRoot", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }]"#; - serde_json::from_str(abi).unwrap() -}); diff --git a/core/lib/dal/.sqlx/query-33d49ec6028974fa8b46d7bf1f79e41923477ed8dc179ca0e1fe64b4700e6572.json b/core/lib/dal/.sqlx/query-33d49ec6028974fa8b46d7bf1f79e41923477ed8dc179ca0e1fe64b4700e6572.json new file mode 100644 index 000000000000..703a57ae0597 --- /dev/null +++ b/core/lib/dal/.sqlx/query-33d49ec6028974fa8b46d7bf1f79e41923477ed8dc179ca0e1fe64b4700e6572.json @@ -0,0 +1,20 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n COUNT(*)\n FROM\n eth_txs\n WHERE\n confirmed_eth_tx_history_id IS NULL\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "count", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null + ] + }, + "hash": "33d49ec6028974fa8b46d7bf1f79e41923477ed8dc179ca0e1fe64b4700e6572" +} diff --git a/core/lib/dal/src/eth_sender_dal.rs b/core/lib/dal/src/eth_sender_dal.rs index 191ea3231d1c..eecd102f395e 100644 --- a/core/lib/dal/src/eth_sender_dal.rs +++ b/core/lib/dal/src/eth_sender_dal.rs @@ -86,6 +86,25 @@ impl EthSenderDal<'_, '_> { Ok(count.try_into().unwrap()) } + pub async fn get_unconfirmed_txs_count(&mut self) -> DalResult { + let count = sqlx::query!( + r#" + SELECT + COUNT(*) + FROM + eth_txs + WHERE + confirmed_eth_tx_history_id IS NULL + "# + ) + .instrument("get_unconfirmed_txs_count") + .fetch_one(self.storage) + .await? + .count + .unwrap(); + Ok(count.try_into().unwrap()) + } + pub async fn get_eth_l1_batches(&mut self) -> sqlx::Result { struct EthTxRow { number: i64, diff --git a/core/lib/env_config/src/contracts.rs b/core/lib/env_config/src/contracts.rs index 457a946d9831..4cd0b021ff20 100644 --- a/core/lib/env_config/src/contracts.rs +++ b/core/lib/env_config/src/contracts.rs @@ -10,8 +10,14 @@ impl FromEnv for EcosystemContracts { .parse()?, transparent_proxy_admin_addr: std::env::var("CONTRACTS_TRANSPARENT_PROXY_ADMIN_ADDR")? .parse()?, - // Not supported yet - l1_bytecodes_supplier_addr: None, + l1_bytecodes_supplier_addr: std::env::var("CONTRACTS_L1_BYTECODE_SUPPLIER_ADDR")? + .parse() + .ok(), + l1_wrapped_base_token_store: std::env::var( + "CONTRACTS_L1_WRAPPED_BASE_TOKEN_STORE_ADDR", + )? + .parse() + .ok(), }) } } @@ -44,6 +50,9 @@ impl FromEnv for ContractsConfig { #[cfg(test)] mod tests { + use std::str::FromStr; + + use zksync_basic_types::H256; use zksync_config::configs::EcosystemContracts; use zksync_system_constants::SHARED_BRIDGE_ETHER_TOKEN_ADDRESS; @@ -72,11 +81,20 @@ mod tests { bridgehub_proxy_addr: addr("0x35ea7f92f4c5f433efe15284e99c040110cf6297"), state_transition_proxy_addr: addr("0xd90f1c081c6117241624e97cb6147257c3cb2097"), transparent_proxy_admin_addr: addr("0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347e5"), - l1_bytecodes_supplier_addr: None, + l1_bytecodes_supplier_addr: Some(addr( + "0x36ea7f92f4c5f433efe15284e99c040110cf6297", + )), + l1_wrapped_base_token_store: Some(addr( + "0x36ea7f92f4c5f433efe15284e99c040110cf6298", + )), }), base_token_addr: Some(SHARED_BRIDGE_ETHER_TOKEN_ADDRESS), - l1_base_token_asset_id: None, - l2_predeployed_wrapped_base_token_address: None, + l1_base_token_asset_id: Some( + H256::from_str( + "0x0000000000000000000000000000000000000001000000000000000000000000", + ) + .unwrap(), + ), chain_admin_addr: Some(addr("0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347ff")), l2_da_validator_addr: Some(addr("0xed6fa5c14e7550b4caf2aa2818d24c69cbc347ff")), l2_timestamp_asserter_addr: Some(addr("0x0000000000000000000000000000000000000002")), @@ -101,11 +119,16 @@ CONTRACTS_L2_CONSENSUS_REGISTRY_ADDR="D64e136566a9E04eb05B30184fF577F52682D182" CONTRACTS_L1_MULTICALL3_ADDR="0xcA11bde05977b3631167028862bE2a173976CA11" CONTRACTS_L1_SHARED_BRIDGE_PROXY_ADDR="0x8656770FA78c830456B00B4fFCeE6b1De0e1b888" CONTRACTS_L2_SHARED_BRIDGE_ADDR="0x8656770FA78c830456B00B4fFCeE6b1De0e1b888" +CONTRACTS_L1_BYTECODE_SUPPLIER_ADDR="0x36ea7f92f4c5f433efe15284e99c040110cf6297" CONTRACTS_L2_LEGACY_SHARED_BRIDGE_ADDR="0x8656770FA78c830456B00B4fFCeE6b1De0e1b888" CONTRACTS_BRIDGEHUB_PROXY_ADDR="0x35ea7f92f4c5f433efe15284e99c040110cf6297" CONTRACTS_STATE_TRANSITION_PROXY_ADDR="0xd90f1c081c6117241624e97cb6147257c3cb2097" CONTRACTS_TRANSPARENT_PROXY_ADMIN_ADDR="0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347e5" CONTRACTS_BASE_TOKEN_ADDR="0x0000000000000000000000000000000000000001" +CONTRACTS_L1_BASE_TOKEN_ASSET_ID="0x0000000000000000000000000000000000000001000000000000000000000000" +CONTRACTS_L1_WRAPPED_BASE_TOKEN_STORE_ADDR="0x36ea7f92f4c5f433efe15284e99c040110cf6298" +CONTRACTS_L2_NATIVE_TOKEN_VAULT_PROXY_ADDR="0xfc073319977e314f251eae6ae6be76b0b3baeecf" +CONTRACTS_PREDEPLOYED_L2_WRAPPED_BASE_TOKEN_ADDRESS="0x35ea7f92f4c5f433efe15284e99c040110cf6299" CONTRACTS_CHAIN_ADMIN_ADDR="0xdd6fa5c14e7550b4caf2aa2818d24c69cbc347ff" CONTRACTS_L2_DA_VALIDATOR_ADDR="0xed6fa5c14e7550b4caf2aa2818d24c69cbc347ff" CONTRACTS_L2_TIMESTAMP_ASSERTER_ADDR="0x0000000000000000000000000000000000000002" diff --git a/core/lib/env_config/src/eth_sender.rs b/core/lib/env_config/src/eth_sender.rs index b15a153c30c3..3030d4206812 100644 --- a/core/lib/env_config/src/eth_sender.rs +++ b/core/lib/env_config/src/eth_sender.rs @@ -61,7 +61,6 @@ mod tests { aggregated_block_execute_deadline: 4_000, max_aggregated_tx_gas: 4_000_000, max_eth_tx_data_size: 120_000, - timestamp_criteria_max_allowed_lag: 30, max_aggregated_blocks_to_commit: 3, max_aggregated_blocks_to_execute: 4, diff --git a/core/lib/env_config/src/wallets.rs b/core/lib/env_config/src/wallets.rs index 3518d56f7b45..e9574be4456f 100644 --- a/core/lib/env_config/src/wallets.rs +++ b/core/lib/env_config/src/wallets.rs @@ -33,6 +33,7 @@ impl FromEnv for Wallets { } else { None }; + Some(EthSender { operator, blob_operator, diff --git a/core/lib/l1_contract_interface/src/i_executor/methods/commit_batches.rs b/core/lib/l1_contract_interface/src/i_executor/methods/commit_batches.rs index 01e362fb7d65..3487ad81a840 100644 --- a/core/lib/l1_contract_interface/src/i_executor/methods/commit_batches.rs +++ b/core/lib/l1_contract_interface/src/i_executor/methods/commit_batches.rs @@ -18,7 +18,7 @@ pub struct CommitBatches<'a> { pub mode: L1BatchCommitmentMode, } -impl Tokenize for CommitBatches<'_> { +impl Tokenize for &CommitBatches<'_> { fn into_tokens(self) -> Vec { let protocol_version = self.l1_batches[0].header.protocol_version.unwrap(); let stored_batch_info = StoredBatchInfo::from(self.last_committed_l1_batch).into_token(); @@ -27,6 +27,7 @@ impl Tokenize for CommitBatches<'_> { .iter() .map(|batch| CommitBatchInfo::new(self.mode, batch, self.pubdata_da).into_token()) .collect(); + if protocol_version.is_pre_gateway() { vec![stored_batch_info, Token::Array(l1_batches_to_commit)] } else { diff --git a/core/lib/l1_contract_interface/src/i_executor/methods/prove_batches.rs b/core/lib/l1_contract_interface/src/i_executor/methods/prove_batches.rs index 2d02bd5a1764..817448cc1b62 100644 --- a/core/lib/l1_contract_interface/src/i_executor/methods/prove_batches.rs +++ b/core/lib/l1_contract_interface/src/i_executor/methods/prove_batches.rs @@ -58,8 +58,10 @@ impl ProveBatches { } }; + let should_use_fflonk = !is_verifier_pre_fflonk || !protocol_version.is_pre_fflonk(); + if protocol_version.is_pre_gateway() { - let proof_input = if !is_verifier_pre_fflonk || !protocol_version.is_pre_fflonk() { + let proof_input = if should_use_fflonk { Token::Tuple(vec![ Token::Array(vec![verifier_type.into_token()]), Token::Array(proof.into_iter().map(Token::Uint).collect()), @@ -73,7 +75,17 @@ impl ProveBatches { vec![prev_l1_batch_info, batches_arg, proof_input] } else { - let proof_input = Token::Array(proof.into_iter().map(Token::Uint).collect()); + let proof_input = if should_use_fflonk { + Token::Array( + vec![verifier_type] + .into_iter() + .chain(proof) + .map(Token::Uint) + .collect(), + ) + } else { + Token::Array(proof.into_iter().map(Token::Uint).collect()) + }; let encoded_data = encode(&[prev_l1_batch_info, batches_arg, proof_input]); let prove_data = [[SUPPORTED_ENCODING_VERSION].to_vec(), encoded_data] diff --git a/core/lib/l1_contract_interface/src/i_executor/structures/mod.rs b/core/lib/l1_contract_interface/src/i_executor/structures/mod.rs index 9583e0204f75..5035abf6af60 100644 --- a/core/lib/l1_contract_interface/src/i_executor/structures/mod.rs +++ b/core/lib/l1_contract_interface/src/i_executor/structures/mod.rs @@ -3,6 +3,8 @@ mod commit_batch_info; mod stored_batch_info; +pub const SUPPORTED_ENCODING_VERSION: u8 = 0; + #[cfg(test)] mod tests; @@ -13,5 +15,3 @@ pub use self::{ }, stored_batch_info::StoredBatchInfo, }; - -pub const SUPPORTED_ENCODING_VERSION: u8 = 0; diff --git a/core/lib/multivm/src/versions/shadow/tests.rs b/core/lib/multivm/src/versions/shadow/tests.rs index 354459853f11..2d3dd5d3ae30 100644 --- a/core/lib/multivm/src/versions/shadow/tests.rs +++ b/core/lib/multivm/src/versions/shadow/tests.rs @@ -308,7 +308,6 @@ mod l1_messenger { use crate::versions::testonly::l1_messenger::*; #[test] - #[ignore] // Requires post-gateway system contracts fn rollup_da_output_hash_match() { test_rollup_da_output_hash_match::(); } diff --git a/core/lib/multivm/src/versions/testonly/circuits.rs b/core/lib/multivm/src/versions/testonly/circuits.rs index de987a8912db..c379372bc970 100644 --- a/core/lib/multivm/src/versions/testonly/circuits.rs +++ b/core/lib/multivm/src/versions/testonly/circuits.rs @@ -34,8 +34,19 @@ pub(crate) fn test_circuits() { let s = res.statistics.circuit_statistic; // Check `circuit_statistic`. const EXPECTED: [f32; 13] = [ - 1.34935, 0.15026, 1.66666, 0.00315, 1.0594, 0.00058, 0.00348, 0.00076, 0.11945, 0.14285, - 0.0, 0.0, 0.0, + 1.258627, + 0.13982475, + 1.6666666, + 0.003154238, + 0.9247803, + 0.00058723404, + 0.0034893616, + 0.00076709175, + 0.11945392, + 0.14285715, + 0.0, + 0.0, + 0.0, ]; let actual = [ (s.main_vm, "main_vm"), diff --git a/core/lib/multivm/src/versions/testonly/l1_messenger.rs b/core/lib/multivm/src/versions/testonly/l1_messenger.rs index dcbc432aafd0..c8b7b6bd8ed0 100644 --- a/core/lib/multivm/src/versions/testonly/l1_messenger.rs +++ b/core/lib/multivm/src/versions/testonly/l1_messenger.rs @@ -1,15 +1,14 @@ use std::rc::Rc; use ethabi::Token; -use zksync_contracts::l1_messenger_contract; +use zksync_contracts::{l1_messenger_contract, l2_rollup_da_validator_bytecode}; use zksync_test_contracts::{TestContract, TxType}; use zksync_types::{ address_to_h256, u256_to_h256, web3::keccak256, Address, Execute, ProtocolVersionId, L1_MESSENGER_ADDRESS, U256, }; -use zksync_vm_interface::SystemEnv; -use super::{default_system_env, ContractToDeploy, TestedVm, VmTesterBuilder}; +use super::{ContractToDeploy, TestedVm, VmTesterBuilder}; use crate::{ interface::{ pubdata::{PubdataBuilder, PubdataInput}, @@ -22,11 +21,6 @@ use crate::{ const L2_DA_VALIDATOR_OUTPUT_HASH_KEY: usize = 5; const USED_L2_DA_VALIDATOR_ADDRESS_KEY: usize = 6; -// Bytecode is temporary hardcoded, should be removed after contracts are merged. -fn l2_rollup_da_validator_bytecode() -> Vec { - hex::decode("0012000000000002000a000000000002000000000301001900000060043002700000012703400197000100000031035500020000003103550003000000310355000400000031035500050000003103550006000000310355000700000031035500080000003103550009000000310355000a000000310355000b000000310355000c000000310355000d000000310355000e000000310355000f00000031035500100000003103550011000000010355000001270040019d0000008004000039000000400040043f00000001002001900000005d0000c13d000000040030008c000000fe0000413d000000000201043b00000129022001970000012a0020009c000000fe0000c13d000000a40030008c000000fe0000413d0000000002000416000000000002004b000000fe0000c13d0000008402100370000000000202043b000300000002001d0000012b0020009c000000fe0000213d00000003020000290000002302200039000000000032004b000000fe0000813d00000003020000290000000402200039000000000421034f000000000604043b0000012b0060009c000000fe0000213d0000000304000029000700240040003d0000000704600029000000000034004b000000fe0000213d0000004403100370000000000303043b000400000003001d0000006403100370000000000303043b000200000003001d000000040060008c000000fe0000413d0000002002200039000000000221034f000000000202043b000000e00220027000000058022000c90000000804200039000000000064004b000000fe0000213d00000003022000290000002802200039000000000121034f000000000101043b000500e00010027a000600000006001d000000650000c13d00000000090000190000000403000029000000000039004b000000f10000c13d0000014e0040009c000000fb0000a13d0000014001000041000000000010043f0000001101000039000000040010043f00000138010000410000049a000104300000000001000416000000000001004b000000fe0000c13d0000002001000039000001000010044300000120000004430000012801000041000004990001042e000000000800001900000000090000190000014f0040009c000000570000813d0000000403400039000000000063004b000000fe0000213d00000007024000290000001101000367000000000221034f000000000502043b000000e004500270000000000034001a000000570000413d0000000007340019000000000067004b000000fe0000213d00000000020004140000012c0050009c0000007b0000813d0000000003000031000000840000013d000000070330002900000127053001970001000000510355000000000034001a000000570000413d0000000003340019000000000330007b000000570000413d000000000151034f000a00000009001d000800000008001d000900000007001d000001270330019700010000003103e50000012d0020009c000003c20000813d00000000013103df000000c0022002100000012e022001970000012c022001c700010000002103b500000000012103af0000801002000039049804930000040f0000000003010019000000600330027000000127033001970000000100200190000002450000613d0000001f0230003900000130052001970000003f025000390000013104200197000000400200043d0000000004420019000000000024004b000000000600003900000001060040390000012b0040009c0000023f0000213d00000001006001900000023f0000c13d000000400040043f0000000004320436000000000005004b000000b10000613d0000000005540019000000000600003100000011066003670000000007040019000000006806043c0000000007870436000000000057004b000000ad0000c13d0000012f063001980000000005640019000000ba0000613d000000000701034f0000000008040019000000007907043c0000000008980436000000000058004b000000b60000c13d0000001f03300190000000c70000613d000000000161034f0000000303300210000000000605043300000000063601cf000000000636022f000000000101043b0000010003300089000000000131022f00000000013101cf000000000161019f00000000001504350000000001020433000000200010008c0000000a05000029000004210000c13d0000000002040433000000400100043d000000400310003900000000002304350000002002100039000000000052043500000040030000390000000000310435000001320010009c0000023f0000213d0000006003100039000000400030043f000001270020009c000001270200804100000040022002100000000001010433000001270010009c00000127010080410000006001100210000000000121019f0000000002000414000001270020009c0000012702008041000000c002200210000000000112019f00000133011001c700008010020000390498048e0000040f0000000100200190000000060600002900000009040000290000000808000029000000fe0000613d000000000901043b0000000108800039000000050080006c000000670000413d000000520000013d000000400100043d000000440210003900000000009204350000002402100039000000000032043500000134020000410000000000210435000000040210003900000000000204350000042d0000013d0000000403400039000000000063004b000001000000a13d00000000010000190000049a0001043000000007014000290000001101100367000000000101043b000400e00010027a0000025d0000c13d000000000900001900000000050300190000000003090019000000020090006c000002f20000c13d000000060050006c000002fd0000813d00000007015000290000001102000367000000000112034f000000000101043b000000f801100270000000010010008c000003030000c13d00000000060500190000014e0060009c0000000604000029000000570000213d0000000403600039000000000043004b000000fe0000213d00000003016000290000002501100039000000000112034f000000000101043b000000000043004b000002fd0000813d000000e8011002700000000703300029000000000432034f0000000503500039000000000404043b000000000031001a0000000607000029000000570000413d000a00000031001d0000000a0070006b000000fe0000213d000000050600008a0000000a0060006b000000570000213d0000000a050000290000000405500039000000000075004b000000fe0000213d0000000a08000029000300070080002d0000000306200360000000000606043b000400000006001d000000e006600272000500000006001d00090110006000cd0000013f0000613d000000090800002900000005068000fa000001100060008c000000570000c13d000000090050002a000000570000413d000200090050002d000000020070006c000000fe0000413d000000f804400270000000400a00043d0000004406a00039000000800700003900000000007604350000002406a000390000000000460435000001410400004100000000004a043500000007055000290000008404a00039000000090900002900000000009404350000000404a0003900000005060000290000000000640435000000000752034f0000001f0890018f00080000000a001d000000a405a0003900000142099001980000000006950019000001610000613d000000000a07034f000000000b05001900000000ac0a043c000000000bcb043600000000006b004b0000015d0000c13d0000000703300029000000000008004b0000016f0000613d000000000797034f0000000308800210000000000906043300000000098901cf000000000989022f000000000707043b0000010008800089000000000787022f00000000078701cf000000000797019f00000000007604350000000907000029000000000675001900000000000604350000001f06700039000001430660019700000000066500190000000004460049000000080500002900000064055000390000000000450435000000000432034f0000001f0510018f000000000216043600000144061001980000000003620019000001850000613d000000000704034f0000000008020019000000007907043c0000000008980436000000000038004b000001810000c13d000000000005004b000001920000613d000000000464034f0000000305500210000000000603043300000000065601cf000000000656022f000000000404043b0000010005500089000000000454022f00000000045401cf000000000464019f0000000000430435000000000312001900000000000304350000001f011000390000014501100197000000080300002900000000013100490000000001210019000001270010009c00000127010080410000006001100210000001270030009c000001270200004100000000020340190000004002200210000000000121019f0000000002000414000001270020009c0000012702008041000000c002200210000000000112019f0000800e02000039049804890000040f000000000301001900000060033002700000012703300197000000200030008c000000200400003900000000040340190000001f0640018f00000020074001900000000805700029000001b80000613d000000000801034f0000000809000029000000008a08043c0000000009a90436000000000059004b000001b40000c13d000000000006004b000001c50000613d000000000771034f0000000306600210000000000805043300000000086801cf000000000868022f000000000707043b0000010006600089000000000767022f00000000066701cf000000000686019f00000000006504350000000100200190000003480000613d0000001f01400039000000600110018f0000000802100029000000000012004b00000000010000390000000101004039000100000002001d0000012b0020009c0000023f0000213d00000001001001900000023f0000c13d0000000101000029000000400010043f000000200030008c0000000604000029000000fe0000413d00000008010000290000000001010433000800000001001d00000004010000290000012c0010009c000001e10000413d000000090200002900000005012000fa000001100010008c000000570000c13d0000000103000029000000440130003900000024023000390000000403300039000000020440006c000003660000c13d000001460400004100000001050000290000000000450435000000200400003900000000004304350000000a04000029000000000042043500000150034001980000001f0440018f000000000231001900000007050000290000001105500367000001fa0000613d000000000605034f0000000007010019000000006806043c0000000007870436000000000027004b000001f60000c13d000000000004004b000002070000613d000000000335034f0000000304400210000000000502043300000000054501cf000000000545022f000000000303043b0000010004400089000000000343022f00000000034301cf000000000353019f00000000003204350000000a030000290000001f023000390000015002200197000000000131001900000000000104350000004401200039000001270010009c000001270100804100000060011002100000000102000029000001270020009c00000127020080410000004002200210000000000112019f0000000002000414000001270020009c0000012702008041000000c002200210000000000112019f00008011020000390498048e0000040f000000000301001900000060033002700000001f0430018f0000012f0530019700000127033001970000000100200190000003720000613d0000000102500029000000000005004b0000022c0000613d000000000601034f0000000107000029000000006806043c0000000007870436000000000027004b000002280000c13d000000000004004b000002390000613d000000000151034f0000000304400210000000000502043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f00000000001204350000001f0130003900000130011001970000000101100029000900000001001d0000012b0010009c0000038a0000a13d0000014001000041000000000010043f0000004101000039000000040010043f00000138010000410000049a000104300000001f0430018f0000012f023001980000024e0000613d000000000501034f0000000006000019000000005705043c0000000006760436000000000026004b0000024a0000c13d000000000004004b0000025b0000613d000000000121034f0000000304400210000000000502043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f000000000012043500000060013002100000049a00010430000000000800001900000000090000190000014e0030009c000000570000213d0000000402300039000000000062004b000000fe0000213d00000007033000290000001101000367000000000331034f000000000303043b000000e00a30027000000000002a001a000000570000413d00000000072a0019000000000067004b000000fe0000213d0000013600300198000003130000c13d000001390030009c000003190000813d0000013a003001980000031f0000613d000000070420002900000127034001970000000002000414000100000031035500000000004a001a000000570000413d00000000044a0019000000000440007b000000570000413d00090000000a001d000a00000009001d000500000008001d000800000007001d000000000131034f000001270340019700010000003103e5000001270020009c000003c20000213d00000000013103df000000c0022002100000012e022001970000012c022001c700010000002103b500000000012103af0000000202000039049804930000040f00000000030100190000006003300270000001270330019700000001002001900000032a0000613d0000001f0230003900000130052001970000003f025000390000013104200197000000400200043d0000000004420019000000000024004b000000000600003900000001060040390000012b0040009c0000023f0000213d00000001006001900000023f0000c13d000000400040043f0000000004320436000000000005004b000000090a000029000002ad0000613d0000000005540019000000000600003100000011066003670000000007040019000000006806043c0000000007870436000000000057004b000002a90000c13d0000012f063001980000000005640019000002b60000613d000000000701034f0000000008040019000000007907043c0000000008980436000000000058004b000002b20000c13d0000001f03300190000002c30000613d000000000161034f0000000303300210000000000605043300000000063601cf000000000636022f000000000101043b0000010003300089000000000131022f00000000013101cf000000000161019f0000000000150435000000400100043d0000000002020433000000200020008c0000000a05000029000003420000c13d00000000020404330000013d02200197000000db03a002100000013e03300197000000000223019f0000013f022001c7000000400310003900000000002304350000002002100039000000000052043500000040030000390000000000310435000001320010009c0000023f0000213d0000006003100039000000400030043f000001270020009c000001270200804100000040022002100000000001010433000001270010009c00000127010080410000006001100210000000000121019f0000000002000414000001270020009c0000012702008041000000c002200210000000000112019f00000133011001c700008010020000390498048e0000040f0000000100200190000000060600002900000008030000290000000508000029000000fe0000613d000000000901043b0000000108800039000000040080006c0000025f0000413d000001060000013d000000400100043d0000004402100039000000000032043500000024021000390000000203000029000000000032043500000134020000410000000000210435000000040210003900000001030000390000042c0000013d0000014001000041000000000010043f0000003201000039000000040010043f00000138010000410000049a00010430000000400200043d0000004403200039000000000013043500000024012000390000000103000039000000000031043500000134010000410000000000120435000000040120003900000002030000390000000000310435000001270020009c0000012702008041000000400120021000000135011001c70000049a00010430000000400100043d0000013702000041000000000021043500000004021000390000000203000039000003240000013d000000400100043d0000013702000041000000000021043500000004021000390000000103000039000003240000013d000000400100043d00000137020000410000000000210435000000040210003900000003030000390000000000320435000001270010009c0000012701008041000000400110021000000138011001c70000049a000104300000001f0430018f0000012f02300198000003330000613d000000000501034f0000000006000019000000005705043c0000000006760436000000000026004b0000032f0000c13d000000000004004b000003400000613d000000000121034f0000000304400210000000000502043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f000000000012043500000060013002100000049a0001043000000044021000390000013b03000041000000000032043500000024021000390000001903000039000004270000013d0000001f0530018f0000012f06300198000000400200043d0000000004620019000003530000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000048004b0000034f0000c13d000000000005004b000003600000613d000000000161034f0000000305500210000000000604043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f00000000001404350000006001300210000001270020009c00000127020080410000004002200210000000000112019f0000049a000104300000013405000041000000010600002900000000005604350000000305000039000000000053043500000000000204350000000000410435000001270060009c0000012706008041000000400160021000000135011001c70000049a00010430000000400200043d0000000006520019000000000005004b0000037c0000613d000000000701034f0000000008020019000000007907043c0000000008980436000000000068004b000003780000c13d000000000004004b000003600000613d000000000151034f0000000304400210000000000506043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f0000000000160435000003600000013d0000000901000029000000400010043f000000200030008c000000fe0000413d000000010100002900000000010104330000012b0010009c000000fe0000213d000000010230002900000001011000290000001f03100039000000000023004b000000fe0000813d00000000140104340000012b0040009c0000023f0000213d00000005034002100000003f05300039000001470550019700000009055000290000012b0050009c0000023f0000213d000000400050043f000000090500002900000000004504350000000003130019000000000023004b000000fe0000213d000000000004004b000003ae0000613d0000000902000029000000200220003900000000140104340000000000420435000000000031004b000003a90000413d000000000100041400000011020003670000000a0000006b000003b40000c13d0000000003000031000003be0000013d00000007030000290000012704300197000100000042035500000003050000290000000a0050006c000000570000413d0000000305000029000000000350007b000000570000413d000000000242034f000001270330019700010000003203e5000001270010009c000003c90000a13d000000400100043d00000044021000390000014d03000041000000000032043500000024021000390000000803000039000004270000013d00000000023203df000000c0011002100000012e011001970000012c011001c700010000001203b500000000011203af0000801002000039049804930000040f0000000003010019000000600330027000000127033001970000000100200190000004320000613d0000001f0230003900000130052001970000003f025000390000013104200197000000400200043d0000000004420019000000000024004b000000000600003900000001060040390000012b0040009c0000023f0000213d00000001006001900000023f0000c13d000000400040043f0000000004320436000000000005004b000003ef0000613d0000000005540019000000000600003100000011066003670000000007040019000000006806043c0000000007870436000000000057004b000003eb0000c13d0000001f0530018f0000012f063001980000000003640019000003f90000613d000000000701034f0000000008040019000000007907043c0000000008980436000000000038004b000003f50000c13d000000000005004b000004060000613d000000000161034f0000000305500210000000000603043300000000065601cf000000000656022f000000000101043b0000010005500089000000000151022f00000000015101cf000000000161019f00000000001304350000000001020433000000200010008c000004210000c13d000000400100043d00000009020000290000000002020433000001000020008c0000044a0000413d00000064021000390000014a03000041000000000032043500000044021000390000014b0300004100000000003204350000002402100039000000250300003900000000003204350000013c020000410000000000210435000000040210003900000020030000390000000000320435000001270010009c000001270100804100000040011002100000014c011001c70000049a00010430000000400100043d00000044021000390000014803000041000000000032043500000024021000390000001f0300003900000000003204350000013c020000410000000000210435000000040210003900000020030000390000000000320435000001270010009c0000012701008041000000400110021000000135011001c70000049a000104300000001f0430018f0000012f023001980000043b0000613d000000000501034f0000000006000019000000005705043c0000000006760436000000000026004b000004370000c13d000000000004004b000004480000613d000000000121034f0000000304400210000000000502043300000000054501cf000000000545022f000000000101043b0000010004400089000000000141022f00000000014101cf000000000151019f000000000012043500000060013002100000049a000104300000000003040433000000f8022002100000006004100039000000000024043500000040021000390000000000320435000000200210003900000008030000290000000000320435000000610310003900000009040000290000000004040433000000000004004b000004610000613d000000000500001900000009060000290000002006600039000900000006001d000000000606043300000000036304360000000105500039000000000045004b000004590000413d0000000003130049000000200430008a00000000004104350000001f0330003900000150043001970000000003140019000000000043004b000000000400003900000001040040390000012b0030009c0000023f0000213d00000001004001900000023f0000c13d000000400030043f000001270020009c000001270200804100000040022002100000000001010433000001270010009c00000127010080410000006001100210000000000121019f0000000002000414000001270020009c0000012702008041000000c002200210000000000112019f00000133011001c700008010020000390498048e0000040f0000000100200190000000fe0000613d000000000101043b000000400200043d0000000000120435000001270020009c0000012702008041000000400120021000000149011001c7000004990001042e0000048c002104210000000102000039000000000001042d0000000002000019000000000001042d00000491002104230000000102000039000000000001042d0000000002000019000000000001042d00000496002104230000000102000039000000000001042d0000000002000019000000000001042d0000049800000432000004990001042e0000049a00010430000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0000000200000000000000000000000000000040000001000000000000000000ffffffff0000000000000000000000000000000000000000000000000000000089f9a07200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff0000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000ffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffe000000000000000000000000000000000000000000000000000000001ffffffe000000000000000000000000000000000000000000000000000000003ffffffe0000000000000000000000000000000000000000000000000ffffffffffffff9f02000000000000000000000000000000000000000000000000000000000000007f7b0cf70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000001f0000000000000000000000000000000000000000000000000000000043e266b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000007368612072657475726e656420696e76616c696420646174610000000000000008c379a00000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff06ffffff0000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004e487b71000000000000000000000000000000000000000000000000000000006006d8b500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ffffffffe0000000000000000000000000000000000000000000000000000003ffffffffe00000000000000000000000000000000000000000000000000000000000ffffe00000000000000000000000000000000000000000000000000000000001ffffe018876a04000000000000000000000000000000000000000000000000000000007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06b656363616b3235362072657475726e656420696e76616c69642064617461000000000000000000000000000000000000000020000000000000000000000000206269747300000000000000000000000000000000000000000000000000000053616665436173743a2076616c756520646f65736e27742066697420696e203800000000000000000000000000000000000000840000000000000000000000004f766572666c6f77000000000000000000000000000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00000000000000000000000000000000000000000000000000000000000000000e901f5bd8811df26e614332e2110b9bc002e2cbadd82065c67e102f858079d5a").unwrap() -} - fn encoded_uncompressed_state_diffs(input: &PubdataInput) -> Vec { let mut result = vec![]; for state_diff in input.state_diffs.iter() { @@ -78,15 +72,10 @@ pub(crate) fn test_rollup_da_output_hash_match() { // In this test, we check whether the L2 DA output hash is as expected. let l2_da_validator_address = Address::repeat_byte(0x12); - let system_env = SystemEnv { - version: ProtocolVersionId::Version27, - ..default_system_env() - }; let mut vm = VmTesterBuilder::new() .with_empty_in_memory_storage() .with_execution_mode(TxExecutionMode::VerifyExecute) .with_rich_accounts(1) - .with_system_env(system_env) .with_custom_contracts(vec![ContractToDeploy { bytecode: l2_rollup_da_validator_bytecode(), address: l2_da_validator_address, diff --git a/core/lib/multivm/src/versions/testonly/l2_blocks.rs b/core/lib/multivm/src/versions/testonly/l2_blocks.rs index f8813231c9e1..ad14aeb60670 100644 --- a/core/lib/multivm/src/versions/testonly/l2_blocks.rs +++ b/core/lib/multivm/src/versions/testonly/l2_blocks.rs @@ -4,6 +4,7 @@ //! use assert_matches::assert_matches; +use ethabi::{ParamType, Token}; use zksync_system_constants::REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_BYTE; use zksync_types::{ block::{pack_block_info, L2BlockHasher}, @@ -13,6 +14,7 @@ use zksync_types::{ SYSTEM_CONTEXT_CURRENT_L2_BLOCK_INFO_POSITION, SYSTEM_CONTEXT_CURRENT_TX_ROLLING_HASH_POSITION, U256, }; +use zksync_vm_interface::VmRevertReason; use super::{default_l1_batch, get_empty_storage, tester::VmTesterBuilder, TestedVm}; use crate::{ @@ -27,6 +29,29 @@ use crate::{ }, }; +/// Encodes a Solidity function call with parameters into a Vec. +fn encode_function_call( + name: &str, + types: &[ParamType], + params: &[Token], +) -> Result { + let short_sig = ethabi::short_signature(name, types); + + // Check if the provided number of parameters matches the function's expected inputs + if types.len() != params.len() { + return Err(ethabi::Error::InvalidData); + } + + // Encode the function call with the provided parameters + let encoded_data = ethabi::encode(params); + + Ok(VmRevertReason::Unknown { + function_selector: short_sig.to_vec(), + data: [short_sig.to_vec(), encoded_data].concat(), + } + .to_string()) +} + fn get_l1_noop() -> Transaction { Transaction { common_data: ExecuteTransactionCommon::L1(L1TxCommonData { @@ -72,7 +97,7 @@ pub(crate) fn test_l2_block_initialization_timestamp() { assert_matches!( res.result, ExecutionResult::Halt { reason: Halt::FailedToSetL2Block(msg) } - if msg.contains("timestamp") + if msg.contains("0x5e9ad9b0") ); } @@ -107,7 +132,7 @@ pub(crate) fn test_l2_block_initialization_number_non_zero() { res.result, ExecutionResult::Halt { reason: Halt::FailedToSetL2Block( - "L2 block number is never expected to be zero".to_string() + encode_function_call("L2BlockNumberZero", &[], &[]).unwrap() ) } ); @@ -163,7 +188,15 @@ pub(crate) fn test_l2_block_same_l2_block() { // Case 1: Incorrect timestamp test_same_l2_block::( Some(Halt::FailedToSetL2Block( - "The timestamp of the same L2 block must be same".to_string(), + encode_function_call( + "IncorrectSameL2BlockTimestamp", + &[ParamType::Uint(128), ParamType::Uint(128)], + &[ + Token::Uint(U256::zero()), + Token::Uint(U256::from(1_700_000_001)), + ], + ) + .unwrap(), )), Some(0), None, @@ -172,7 +205,20 @@ pub(crate) fn test_l2_block_same_l2_block() { // Case 2: Incorrect previous block hash test_same_l2_block::( Some(Halt::FailedToSetL2Block( - "The previous hash of the same L2 block must be same".to_string(), + encode_function_call( + "IncorrectSameL2BlockPrevBlockHash", + &[ParamType::FixedBytes(32), ParamType::FixedBytes(32)], + &[ + Token::FixedBytes(H256::zero().0.to_vec()), + Token::FixedBytes( + hex::decode( + "e8e77626586f73b955364c7b4bbf0bb7f7685ebd40e852b164633a4acbd3244c", + ) + .unwrap(), + ), + ], + ) + .unwrap(), )), None, Some(H256::zero()), @@ -249,7 +295,12 @@ pub(crate) fn test_l2_block_new_l2_block() { None, None, Some(Halt::FailedToSetL2Block( - "Invalid new L2 block number".to_string(), + encode_function_call( + "InvalidNewL2BlockNumber", + &[ParamType::Uint(256)], + &[Token::Uint(U256::from(3u32))], + ) + .unwrap(), )), ); @@ -259,7 +310,14 @@ pub(crate) fn test_l2_block_new_l2_block() { None, Some(1), None, - Some(Halt::FailedToSetL2Block("The timestamp of the new L2 block must be greater than the timestamp of the previous L2 block".to_string())), + Some(Halt::FailedToSetL2Block( + encode_function_call( + "NonMonotonicL2BlockTimestamp", + &[ParamType::Uint(128), ParamType::Uint(128)], + &[Token::Uint(U256::from(1)), Token::Uint(U256::from(1))], + ) + .unwrap(), + )), ); // Case 3: Incorrect previous block hash @@ -269,7 +327,20 @@ pub(crate) fn test_l2_block_new_l2_block() { None, Some(H256::zero()), Some(Halt::FailedToSetL2Block( - "The current L2 block hash is incorrect".to_string(), + encode_function_call( + "IncorrectL2BlockHash", + &[ParamType::FixedBytes(32), ParamType::FixedBytes(32)], + &[ + Token::FixedBytes(H256::zero().0.to_vec()), + Token::FixedBytes( + hex::decode( + "de4c551714ad02a0a4f51252f966ef90c13376ea4c8a463eedfb242b97551c43", + ) + .unwrap(), + ), + ], + ) + .unwrap(), )), ); @@ -395,7 +466,14 @@ pub(crate) fn test_l2_block_first_in_batch() { prev_block_hash, max_virtual_blocks_to_create: 1, }, - Some(Halt::FailedToSetL2Block("The timestamp of the L2 block must be greater than or equal to the timestamp of the current batch".to_string())), + Some(Halt::FailedToSetL2Block( + encode_function_call( + "L2BlockAndBatchTimestampMismatch", + &[ParamType::Uint(128), ParamType::Uint(128)], + &[Token::Uint(U256::from(9)), Token::Uint(U256::from(12))], + ) + .unwrap(), + )), ); } diff --git a/core/lib/multivm/src/versions/testonly/refunds.rs b/core/lib/multivm/src/versions/testonly/refunds.rs index 384a3edb7dbd..4d549f5a9be0 100644 --- a/core/lib/multivm/src/versions/testonly/refunds.rs +++ b/core/lib/multivm/src/versions/testonly/refunds.rs @@ -140,7 +140,7 @@ pub(crate) fn test_predetermined_refunded_gas() { current_state_without_predefined_refunds.user_l2_to_l1_logs ); - assert_ne!( + assert_eq!( current_state_with_changed_predefined_refunds.system_logs, current_state_without_predefined_refunds.system_logs ); diff --git a/core/lib/multivm/src/versions/vm_fast/tests/l1_messenger.rs b/core/lib/multivm/src/versions/vm_fast/tests/l1_messenger.rs index c7d4594d7692..0bd01c7de134 100644 --- a/core/lib/multivm/src/versions/vm_fast/tests/l1_messenger.rs +++ b/core/lib/multivm/src/versions/vm_fast/tests/l1_messenger.rs @@ -1,7 +1,6 @@ use crate::{versions::testonly::l1_messenger::test_rollup_da_output_hash_match, vm_fast::Vm}; #[test] -#[ignore] // Requires post-gateway system contracts fn rollup_da_output_hash_match() { test_rollup_da_output_hash_match::>(); } diff --git a/core/lib/multivm/src/versions/vm_latest/tests/l1_messenger.rs b/core/lib/multivm/src/versions/vm_latest/tests/l1_messenger.rs index 7d301f33a131..f1dade9dd8e6 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/l1_messenger.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/l1_messenger.rs @@ -4,7 +4,6 @@ use crate::{ }; #[test] -#[ignore] // Requires post-gateway system contracts fn rollup_da_output_hash_match() { test_rollup_da_output_hash_match::>(); } diff --git a/core/lib/multivm/src/versions/vm_latest/vm.rs b/core/lib/multivm/src/versions/vm_latest/vm.rs index 1db369d4ae20..3914bfca17a2 100644 --- a/core/lib/multivm/src/versions/vm_latest/vm.rs +++ b/core/lib/multivm/src/versions/vm_latest/vm.rs @@ -47,7 +47,7 @@ pub(crate) enum MultiVmSubversion { impl MultiVmSubversion { #[cfg(test)] pub(crate) fn latest() -> Self { - Self::IncreasedBootloaderMemory + Self::Gateway } } diff --git a/core/lib/protobuf_config/src/contracts.rs b/core/lib/protobuf_config/src/contracts.rs index 12cbf996697b..1c9711ef62d2 100644 --- a/core/lib/protobuf_config/src/contracts.rs +++ b/core/lib/protobuf_config/src/contracts.rs @@ -34,6 +34,10 @@ impl ProtoRepr for proto::Contracts { .l1_bytecodes_supplier_addr .as_ref() .map(|x| parse_h160(x).expect("Invalid address")), + l1_wrapped_base_token_store: ecosystem_contracts + .l1_wrapped_base_token_store + .as_ref() + .map(|x| parse_h160(x).expect("Invalid address")), }) } else { None @@ -123,12 +127,6 @@ impl ProtoRepr for proto::Contracts { .map(|x| parse_h256(x)) .transpose() .context("base_token_asset_id")?, - l2_predeployed_wrapped_base_token_address: l2 - .predeployed_wrapped_base_token_address - .as_ref() - .map(|x| parse_h160(x)) - .transpose() - .context("l2 predeployed_wrapped_base_token_address")?, chain_admin_addr: l1 .chain_admin_addr .as_ref() @@ -164,6 +162,9 @@ impl ProtoRepr for proto::Contracts { l1_bytecodes_supplier_addr: ecosystem_contracts .l1_bytecodes_supplier_addr .map(|x| format!("{:?}", x)), + l1_wrapped_base_token_store: ecosystem_contracts + .l1_wrapped_base_token_store + .map(|x| format!("{:?}", x)), }); Self { ecosystem_contracts, @@ -184,9 +185,6 @@ impl ProtoRepr for proto::Contracts { legacy_shared_bridge_addr: this .l2_legacy_shared_bridge_addr .map(|a| format!("{:?}", a)), - predeployed_wrapped_base_token_address: this - .l2_predeployed_wrapped_base_token_address - .map(|x| format!("{:?}", x)), timestamp_asserter_addr: this .l2_timestamp_asserter_addr .map(|a| format!("{:?}", a)), diff --git a/core/lib/protobuf_config/src/proto/config/contracts.proto b/core/lib/protobuf_config/src/proto/config/contracts.proto index febbc981478b..538f415ff408 100644 --- a/core/lib/protobuf_config/src/proto/config/contracts.proto +++ b/core/lib/protobuf_config/src/proto/config/contracts.proto @@ -7,6 +7,7 @@ message EcosystemContracts { optional string state_transition_proxy_addr = 2; // optional; h160 optional string transparent_proxy_admin_addr = 3; // optional; h160 optional string l1_bytecodes_supplier_addr = 4; // optional; h160 + optional string l1_wrapped_base_token_store = 5; // optional; h160 } message L1 { @@ -26,7 +27,7 @@ message L2 { optional string da_validator_addr = 2; // optional; H160 optional string legacy_shared_bridge_addr = 3; // optional; H160 optional string timestamp_asserter_addr = 4; // optional; H160 - optional string predeployed_wrapped_base_token_address = 5; // optional; H160 + reserved 5; reserved "predeployed_wrapped_base_token_address"; } message Bridge { diff --git a/core/lib/types/src/abi.rs b/core/lib/types/src/abi.rs index b40aaaf882e2..da51c6297d4d 100644 --- a/core/lib/types/src/abi.rs +++ b/core/lib/types/src/abi.rs @@ -531,7 +531,7 @@ impl GatewayUpgradeEncodedInput { pub struct ZkChainSpecificUpgradeData { pub base_token_asset_id: H256, pub l2_legacy_shared_bridge: Address, - pub predeployed_l2_weth_address: Address, + pub l2_predeployed_wrapped_base_token: Address, pub base_token_l1_address: Address, pub base_token_name: String, pub base_token_symbol: String, @@ -551,7 +551,7 @@ impl ZkChainSpecificUpgradeData { l2_legacy_shared_bridge: l2_legacy_shared_bridge?, // Note, that some chains may not contain previous deployment of L2 wrapped base // token. For those, zero address is used. - predeployed_l2_weth_address: predeployed_l2_weth_address.unwrap_or_default(), + l2_predeployed_wrapped_base_token: predeployed_l2_weth_address.unwrap_or_default(), base_token_l1_address: base_token_l1_address?, base_token_name: base_token_name?, base_token_symbol: base_token_symbol?, @@ -572,7 +572,7 @@ impl ZkChainSpecificUpgradeData { Token::Tuple(vec![ Token::FixedBytes(self.base_token_asset_id.0.to_vec()), Token::Address(self.l2_legacy_shared_bridge), - Token::Address(self.predeployed_l2_weth_address), + Token::Address(self.l2_predeployed_wrapped_base_token), Token::Address(self.base_token_l1_address), Token::String(self.base_token_name.clone()), Token::String(self.base_token_symbol.clone()), diff --git a/core/lib/types/src/commitment/mod.rs b/core/lib/types/src/commitment/mod.rs index 6d73ad048774..9aef6b14a0f2 100644 --- a/core/lib/types/src/commitment/mod.rs +++ b/core/lib/types/src/commitment/mod.rs @@ -112,6 +112,7 @@ pub struct L1BatchMetadata { pub aux_data_hash: H256, pub meta_parameters_hash: H256, pub pass_through_data_hash: H256, + /// The commitment to the final events queue state after the batch is committed. /// Practically, it is a commitment to all events that happened on L2 during the batch execution. pub events_queue_commitment: Option, diff --git a/core/lib/types/src/protocol_upgrade.rs b/core/lib/types/src/protocol_upgrade.rs index 3bd9e696ce1f..324650c97e21 100644 --- a/core/lib/types/src/protocol_upgrade.rs +++ b/core/lib/types/src/protocol_upgrade.rs @@ -255,12 +255,30 @@ impl ProtocolUpgrade { } } -pub fn decode_set_chain_id_event( +pub fn decode_genesis_upgrade_event( event: Log, ) -> Result<(ProtocolVersionId, ProtocolUpgradeTx), ethabi::Error> { - let tx = ethabi::decode(&[abi::L2CanonicalTransaction::schema()], &event.data.0)?; - let tx = abi::L2CanonicalTransaction::decode(tx.into_iter().next().unwrap()).unwrap(); - + let tokens = ethabi::decode( + &[ + abi::L2CanonicalTransaction::schema(), + ParamType::Array(Box::new(ParamType::Bytes)), + ], + &event.data.0, + )?; + let mut t: std::vec::IntoIter = tokens.into_iter(); + let mut next = || t.next().unwrap(); + + let tx = abi::L2CanonicalTransaction::decode(next()).unwrap(); + let factory_deps = next() + .into_array() + .context("factory_deps") + .map_err(|_| ethabi::Error::InvalidData)? + .into_iter() + .enumerate() + .map(|(i, t)| t.into_bytes().context(i)) + .collect::>, _>>() + .context("factory_deps") + .map_err(|_| ethabi::Error::InvalidData)?; let full_version_id = h256_to_u256(event.topics[2]); let protocol_version = ProtocolVersionId::try_from_packed_semver(full_version_id) .unwrap_or_else(|_| panic!("Version is not supported, packed version: {full_version_id}")); @@ -269,8 +287,11 @@ pub fn decode_set_chain_id_event( Transaction::from_abi( abi::Transaction::L1 { tx: tx.into(), - eth_block: 0, - factory_deps: vec![], + eth_block: event + .block_number + .expect("Event block number is missing") + .as_u64(), + factory_deps, }, true, ) diff --git a/core/lib/types/src/system_contracts.rs b/core/lib/types/src/system_contracts.rs index 4d1ff9b554ea..28d8def59277 100644 --- a/core/lib/types/src/system_contracts.rs +++ b/core/lib/types/src/system_contracts.rs @@ -4,8 +4,10 @@ use zksync_basic_types::{AccountTreeId, Address, U256}; use zksync_contracts::{read_sys_contract_bytecode, ContractLanguage, SystemContractsRepo}; use zksync_system_constants::{ BOOTLOADER_UTILITIES_ADDRESS, CODE_ORACLE_ADDRESS, COMPRESSOR_ADDRESS, CREATE2_FACTORY_ADDRESS, - EVENT_WRITER_ADDRESS, EVM_GAS_MANAGER_ADDRESS, P256VERIFY_PRECOMPILE_ADDRESS, - PUBDATA_CHUNK_PUBLISHER_ADDRESS, + EVENT_WRITER_ADDRESS, EVM_GAS_MANAGER_ADDRESS, L2_ASSET_ROUTER_ADDRESS, L2_BRIDGEHUB_ADDRESS, + L2_GENESIS_UPGRADE_ADDRESS, L2_MESSAGE_ROOT_ADDRESS, L2_NATIVE_TOKEN_VAULT_ADDRESS, + L2_WRAPPED_BASE_TOKEN_IMPL, P256VERIFY_PRECOMPILE_ADDRESS, PUBDATA_CHUNK_PUBLISHER_ADDRESS, + SLOAD_CONTRACT_ADDRESS, }; use crate::{ @@ -25,7 +27,7 @@ use crate::{ pub const TX_NONCE_INCREMENT: U256 = U256([1, 0, 0, 0]); // 1 pub const DEPLOYMENT_NONCE_INCREMENT: U256 = U256([0, 0, 1, 0]); // 2^128 -static SYSTEM_CONTRACT_LIST: [(&str, &str, Address, ContractLanguage); 26] = [ +static SYSTEM_CONTRACT_LIST: [(&str, &str, Address, ContractLanguage); 33] = [ ( "", "AccountCodeStorage", @@ -174,6 +176,48 @@ static SYSTEM_CONTRACT_LIST: [(&str, &str, Address, ContractLanguage); 26] = [ CREATE2_FACTORY_ADDRESS, ContractLanguage::Sol, ), + ( + "", + "L2GenesisUpgrade", + L2_GENESIS_UPGRADE_ADDRESS, + ContractLanguage::Sol, + ), + ( + "../../l1-contracts/zkout/", + "Bridgehub", + L2_BRIDGEHUB_ADDRESS, + ContractLanguage::Sol, + ), + ( + "../../l1-contracts/zkout/", + "MessageRoot", + L2_MESSAGE_ROOT_ADDRESS, + ContractLanguage::Sol, + ), + ( + "../../l1-contracts/zkout/", + "L2AssetRouter", + L2_ASSET_ROUTER_ADDRESS, + ContractLanguage::Sol, + ), + ( + "../../l1-contracts/zkout/", + "L2NativeTokenVault", + L2_NATIVE_TOKEN_VAULT_ADDRESS, + ContractLanguage::Sol, + ), + ( + "", + "SloadContract", + SLOAD_CONTRACT_ADDRESS, + ContractLanguage::Sol, + ), + ( + "../../l1-contracts/zkout/", + "L2WrappedBaseToken", + L2_WRAPPED_BASE_TOKEN_IMPL, + ContractLanguage::Sol, + ), ]; /// Gets default set of system contracts, based on Cargo workspace location. diff --git a/core/lib/vm_executor/src/batch/factory.rs b/core/lib/vm_executor/src/batch/factory.rs index 9797e1681032..59b610426669 100644 --- a/core/lib/vm_executor/src/batch/factory.rs +++ b/core/lib/vm_executor/src/batch/factory.rs @@ -238,6 +238,7 @@ impl BatchVm { .expect("failed extracting call traces") .take() .unwrap_or_default(); + BatchTransactionExecutionResult { tx_result: Box::new(tx_result), compressed_bytecodes, diff --git a/core/lib/vm_executor/src/oneshot/contracts.rs b/core/lib/vm_executor/src/oneshot/contracts.rs index 257ede5a7c7c..d67d1dfbc662 100644 --- a/core/lib/vm_executor/src/oneshot/contracts.rs +++ b/core/lib/vm_executor/src/oneshot/contracts.rs @@ -106,10 +106,8 @@ impl MultiVmBaseSystemContracts { ProtocolVersionId::Version21 | ProtocolVersionId::Version22 => &self.post_1_4_2, ProtocolVersionId::Version23 => &self.vm_1_5_0_small_memory, ProtocolVersionId::Version24 => &self.vm_1_5_0_increased_memory, - ProtocolVersionId::Version25 | ProtocolVersionId::Version26 => { - &self.vm_protocol_defense - } - ProtocolVersionId::Version27 => &self.gateway, + ProtocolVersionId::Version25 => &self.vm_protocol_defense, + ProtocolVersionId::Version26 | ProtocolVersionId::Version27 => &self.gateway, ProtocolVersionId::Version28 => unreachable!("Version 28 is not supported yet"), }; let base = base.clone(); diff --git a/core/lib/web3_decl/src/namespaces/unstable.rs b/core/lib/web3_decl/src/namespaces/unstable.rs index f666f02f2811..da18806d126c 100644 --- a/core/lib/web3_decl/src/namespaces/unstable.rs +++ b/core/lib/web3_decl/src/namespaces/unstable.rs @@ -38,4 +38,7 @@ pub trait UnstableNamespace { l1_batch_number: L1BatchNumber, chain_id: L2ChainId, ) -> RpcResult>; + + #[method(name = "unconfirmedTxsCount")] + async fn get_unconfirmed_txs_count(&self) -> RpcResult; } diff --git a/core/lib/zksync_core_leftovers/src/lib.rs b/core/lib/zksync_core_leftovers/src/lib.rs index 87fb7ea28f71..2bdc8094d142 100644 --- a/core/lib/zksync_core_leftovers/src/lib.rs +++ b/core/lib/zksync_core_leftovers/src/lib.rs @@ -3,7 +3,6 @@ use std::str::FromStr; use tokio::sync::oneshot; - pub mod temp_config_store; /// Sets up an interrupt handler and returns a future that resolves once an interrupt signal diff --git a/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/unstable.rs b/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/unstable.rs index cfa8c84b05b0..214e34241cf9 100644 --- a/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/unstable.rs +++ b/core/node/api_server/src/web3/backend_jsonrpsee/namespaces/unstable.rs @@ -40,4 +40,10 @@ impl UnstableNamespaceServer for UnstableNamespace { .await .map_err(|err| self.current_method().map_err(err)) } + + async fn get_unconfirmed_txs_count(&self) -> RpcResult { + self.get_unconfirmed_txs_count_impl() + .await + .map_err(|err| self.current_method().map_err(err)) + } } diff --git a/core/node/api_server/src/web3/namespaces/en.rs b/core/node/api_server/src/web3/namespaces/en.rs index b2baa8497c98..9ccecf9001b5 100644 --- a/core/node/api_server/src/web3/namespaces/en.rs +++ b/core/node/api_server/src/web3/namespaces/en.rs @@ -165,6 +165,7 @@ impl EnNamespace { .l1_transparent_proxy_admin_addr .unwrap(), l1_bytecodes_supplier_addr: self.state.api_config.l1_bytecodes_supplier_addr, + l1_wrapped_base_token_store: self.state.api_config.l1_wrapped_base_token_store, }) .context("Shared bridge doesn't supported")?) } diff --git a/core/node/api_server/src/web3/namespaces/unstable/mod.rs b/core/node/api_server/src/web3/namespaces/unstable/mod.rs index 47e43f10282b..c70cb6f6e0fe 100644 --- a/core/node/api_server/src/web3/namespaces/unstable/mod.rs +++ b/core/node/api_server/src/web3/namespaces/unstable/mod.rs @@ -139,4 +139,16 @@ impl UnstableNamespace { chain_id_leaf_proof_mask: chain_id_leaf_proof_mask as u64, })) } + + pub async fn get_unconfirmed_txs_count_impl(&self) -> Result { + let mut connection = self.state.acquire_connection().await?; + + let result = connection + .eth_sender_dal() + .get_unconfirmed_txs_count() + .await + .map_err(DalError::generalize)?; + + Ok(result) + } } diff --git a/core/node/api_server/src/web3/state.rs b/core/node/api_server/src/web3/state.rs index a50b9d062321..97ffd933c801 100644 --- a/core/node/api_server/src/web3/state.rs +++ b/core/node/api_server/src/web3/state.rs @@ -108,6 +108,7 @@ pub struct InternalApiConfig { pub estimate_gas_optimize_search: bool, pub bridge_addresses: api::BridgeAddresses, pub l1_bytecodes_supplier_addr: Option
, + pub l1_wrapped_base_token_store: Option
, pub l1_bridgehub_proxy_addr: Option
, pub l1_state_transition_proxy_addr: Option
, pub l1_transparent_proxy_admin_addr: Option
, @@ -169,6 +170,10 @@ impl InternalApiConfig { .ecosystem_contracts .as_ref() .and_then(|a| a.l1_bytecodes_supplier_addr), + l1_wrapped_base_token_store: contracts_config + .ecosystem_contracts + .as_ref() + .and_then(|a| a.l1_wrapped_base_token_store), l1_diamond_proxy_addr: contracts_config.diamond_proxy_addr, l2_testnet_paymaster_addr: contracts_config.l2_testnet_paymaster_addr, req_entities_limit: web3_config.req_entities_limit(), @@ -236,6 +241,10 @@ impl BridgeAddressesHandle { self.0.write().await.l1_shared_default_bridge = Some(l1_shared_bridge); } + pub async fn update_l2_shared_bridge(&self, l2_shared_bridge: Address) { + self.0.write().await.l2_shared_default_bridge = Some(l2_shared_bridge); + } + pub async fn read(&self) -> api::BridgeAddresses { self.0.read().await.clone() } diff --git a/core/node/base_token_adjuster/src/base_token_l1_behaviour.rs b/core/node/base_token_adjuster/src/base_token_l1_behaviour.rs index 599aba36f3e9..580083cd1682 100644 --- a/core/node/base_token_adjuster/src/base_token_l1_behaviour.rs +++ b/core/node/base_token_adjuster/src/base_token_l1_behaviour.rs @@ -151,6 +151,7 @@ impl BaseTokenL1Behaviour { }; } + // TODO(EVM-924): this logic supports only `ChainAdminOwnable`. async fn do_update_l1( &self, l1_params: &UpdateOnL1Params, diff --git a/core/node/commitment_generator/src/lib.rs b/core/node/commitment_generator/src/lib.rs index 71b019e230a7..3c7dadb5fd30 100644 --- a/core/node/commitment_generator/src/lib.rs +++ b/core/node/commitment_generator/src/lib.rs @@ -6,6 +6,7 @@ use tokio::{sync::watch, task::JoinHandle}; use zksync_dal::{ConnectionPool, Core, CoreDal}; use zksync_health_check::{Health, HealthStatus, HealthUpdater, ReactiveHealthCheck}; use zksync_l1_contract_interface::i_executor::commit::kzg::pubdata_to_blob_commitments; +use zksync_multivm::zk_evm_latest::ethereum_types::U256; use zksync_types::{ blob::num_blobs_required, commitment::{ @@ -14,7 +15,7 @@ use zksync_types::{ }, h256_to_u256, writes::{InitialStorageWrite, RepeatedStorageWrite, StateDiffRecord}, - L1BatchNumber, ProtocolVersionId, StorageKey, H256, U256, + L1BatchNumber, ProtocolVersionId, StorageKey, H256, }; use crate::{ diff --git a/core/node/consistency_checker/src/lib.rs b/core/node/consistency_checker/src/lib.rs index f7d904955789..c1735a54fd7f 100644 --- a/core/node/consistency_checker/src/lib.rs +++ b/core/node/consistency_checker/src/lib.rs @@ -396,15 +396,9 @@ impl ConsistencyChecker { }; let gateway_chain_data = if let Some(client) = gateway_client { - let contract = bridgehub_contract(); - let function_name = if contract.function("getZKChain").is_ok() { - "getZKChain" - } else { - "getHyperchain" - }; let gateway_diamond_proxy = - CallFunctionArgs::new(function_name, Token::Uint(l2_chain_id.as_u64().into())) - .for_contract(L2_BRIDGEHUB_ADDRESS, &contract) + CallFunctionArgs::new("getZKChain", Token::Uint(l2_chain_id.as_u64().into())) + .for_contract(L2_BRIDGEHUB_ADDRESS, &bridgehub_contract()) .call(&client) .await?; let chain_id = client.fetch_chain_id().await?; diff --git a/core/node/consistency_checker/src/tests/mod.rs b/core/node/consistency_checker/src/tests/mod.rs index 1635bddffb83..57511fbb69c7 100644 --- a/core/node/consistency_checker/src/tests/mod.rs +++ b/core/node/consistency_checker/src/tests/mod.rs @@ -134,13 +134,8 @@ fn create_mock_sl(chain_id: u64, with_get_zk_chain: bool) -> MockSettlementLayer } Some(addr) if with_get_zk_chain && addr == L2_BRIDGEHUB_ADDRESS => { let contract = zksync_contracts::bridgehub_contract(); - let function_name = if contract.function("getZKChain").is_ok() { - "getZKChain" - } else { - "getHyperchain" - }; let expected_input = contract - .function(function_name) + .function("getZKChain") .unwrap() .encode_input(&[Token::Uint(ERA_CHAIN_ID.into())]) .unwrap(); diff --git a/core/node/da_clients/Cargo.toml b/core/node/da_clients/Cargo.toml index e0c85b3030ab..6fcd94225a8b 100644 --- a/core/node/da_clients/Cargo.toml +++ b/core/node/da_clients/Cargo.toml @@ -40,6 +40,7 @@ jsonrpsee = { workspace = true, features = ["ws-client"] } reqwest = { workspace = true } bytes = { workspace = true } backon.workspace = true +url.workspace = true # Celestia dependencies http.workspace = true diff --git a/core/node/da_clients/src/avail/client.rs b/core/node/da_clients/src/avail/client.rs index 115ad77bf44e..411a0354d632 100644 --- a/core/node/da_clients/src/avail/client.rs +++ b/core/node/da_clients/src/avail/client.rs @@ -2,9 +2,11 @@ use std::{fmt::Debug, sync::Arc, time::Duration}; use anyhow::anyhow; use async_trait::async_trait; +use http::StatusCode; use jsonrpsee::ws_client::WsClientBuilder; use serde::{Deserialize, Serialize}; use subxt_signer::ExposeSecret; +use url::Url; use zksync_config::configs::da_client::avail::{AvailClientConfig, AvailConfig, AvailSecrets}; use zksync_da_client::{ types::{DAError, DispatchResponse, InclusionData}, @@ -40,10 +42,10 @@ pub struct AvailClient { pub struct BridgeAPIResponse { blob_root: Option, bridge_root: Option, - data_root_index: Option, + data_root_index: Option, data_root_proof: Option>, leaf: Option, - leaf_index: Option, + leaf_index: Option, leaf_proof: Option>, range_hash: Option, error: Option, @@ -191,19 +193,30 @@ impl DataAvailabilityClient for AvailClient { error: anyhow!("Invalid blob ID format"), is_retriable: false, })?; - let url = format!( - "{}/eth/proof/{}?index={}", - self.config.bridge_api_url, block_hash, tx_idx - ); + let url = Url::parse(&self.config.bridge_api_url) + .map_err(|_| DAError { + error: anyhow!("Invalid URL"), + is_retriable: false, + })? + .join(format!("/eth/proof/{}?index={}", block_hash, tx_idx).as_str()) + .map_err(|_| DAError { + error: anyhow!("Unable to join to URL"), + is_retriable: false, + })?; let response = self .api_client - .get(&url) + .get(url) .timeout(Duration::from_millis(self.config.timeout_ms as u64)) .send() .await .map_err(to_retriable_da_error)?; + // 404 means that the blob is not included in the bridge yet + if response.status() == StatusCode::NOT_FOUND { + return Ok(None); + } + let bridge_api_data = response .json::() .await @@ -213,12 +226,13 @@ impl DataAvailabilityClient for AvailClient { data_root_proof: bridge_api_data.data_root_proof.unwrap(), leaf_proof: bridge_api_data.leaf_proof.unwrap(), range_hash: bridge_api_data.range_hash.unwrap(), - data_root_index: bridge_api_data.data_root_index.unwrap(), + data_root_index: bridge_api_data.data_root_index.unwrap().into(), blob_root: bridge_api_data.blob_root.unwrap(), bridge_root: bridge_api_data.bridge_root.unwrap(), leaf: bridge_api_data.leaf.unwrap(), - leaf_index: bridge_api_data.leaf_index.unwrap(), + leaf_index: bridge_api_data.leaf_index.unwrap().into(), }; + Ok(Some(InclusionData { data: ethabi::encode(&attestation_data.into_tokens()), })) diff --git a/core/node/eth_sender/Cargo.toml b/core/node/eth_sender/Cargo.toml index f578743dcea9..2f95bf54e176 100644 --- a/core/node/eth_sender/Cargo.toml +++ b/core/node/eth_sender/Cargo.toml @@ -25,7 +25,6 @@ zksync_prover_interface.workspace = true zksync_shared_metrics.workspace = true zksync_node_fee_model.workspace = true zksync_mini_merkle_tree.workspace = true -once_cell.workspace = true tokio = { workspace = true, features = ["time"] } anyhow.workspace = true diff --git a/core/node/eth_watch/src/client.rs b/core/node/eth_watch/src/client.rs index a86f760dc398..03b22df55995 100644 --- a/core/node/eth_watch/src/client.rs +++ b/core/node/eth_watch/src/client.rs @@ -2,8 +2,8 @@ use std::{collections::HashMap, fmt, sync::Arc}; use anyhow::Context; use zksync_contracts::{ - getters_facet_contract, state_transition_manager_contract, verifier_contract, - MESSAGE_ROOT_CONTRACT, + bytecode_supplier_contract, getters_facet_contract, l1_asset_router_contract, l2_message_root, + state_transition_manager_contract, verifier_contract, wrapped_base_token_store_contract, }; use zksync_eth_client::{ clients::{DynClient, L1}, @@ -12,12 +12,12 @@ use zksync_eth_client::{ }; use zksync_system_constants::L2_MESSAGE_ROOT_ADDRESS; use zksync_types::{ + abi::ZkChainSpecificUpgradeData, api::{ChainAggProof, Log}, - ethabi::{self, decode, Contract, ParamType}, - tokens::TokenMetadata, - web3::{BlockId, BlockNumber, CallRequest, Filter, FilterBuilder}, - Address, L1BatchNumber, L2ChainId, SLChainId, H256, SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, U256, - U64, + ethabi::{self, decode, encode, Contract, ParamType}, + web3::{keccak256, BlockId, BlockNumber, CallRequest, Filter, FilterBuilder}, + Address, L1BatchNumber, L2ChainId, SLChainId, H256, L2_NATIVE_TOKEN_VAULT_ADDRESS, + SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, U256, U64, }; use zksync_web3_decl::{ client::{Network, L2}, @@ -64,7 +64,9 @@ pub trait EthClient: 'static + fmt::Debug + Send + Sync { hashes: Vec, ) -> EnrichedClientResult>>>; - async fn get_base_token_metadata(&self) -> Result; + async fn get_chain_gateway_upgrade_info( + &self, + ) -> Result, ContractCallError>; /// Returns ID of the chain. async fn chain_id(&self) -> EnrichedClientResult; @@ -97,27 +99,36 @@ pub struct EthHttpQueryClient { new_upgrade_cut_data_signature: H256, bytecode_published_signature: H256, bytecode_supplier_addr: Option
, + wrapped_base_token_store: Option
, + l1_shared_bridge_addr: Option
, // Only present for post-shared bridge chains. state_transition_manager_address: Option
, chain_admin_address: Option
, verifier_contract_abi: Contract, getters_facet_contract_abi: Contract, message_root_abi: Contract, + l1_asset_router_abi: Contract, + wrapped_base_token_store_abi: Contract, confirmations_for_eth_event: Option, + l2_chain_id: L2ChainId, } impl EthHttpQueryClient where Box>: GetLogsClient, { + #[allow(clippy::too_many_arguments)] pub fn new( client: Box>, diamond_proxy_addr: Address, bytecode_supplier_addr: Option
, + wrapped_base_token_store: Option
, + l1_shared_bridge_addr: Option
, state_transition_manager_address: Option
, chain_admin_address: Option
, governance_address: Address, confirmations_for_eth_event: Option, + l2_chain_id: L2ChainId, ) -> Self { tracing::debug!( "New eth client, ZKsync addr: {:x}, governance addr: {:?}", @@ -136,14 +147,20 @@ where .context("NewUpgradeCutData event is missing in ABI") .unwrap() .signature(), - bytecode_published_signature: ethabi::long_signature( - "BytecodePublished", - &[ParamType::FixedBytes(32), ParamType::Bytes], - ), + bytecode_published_signature: bytecode_supplier_contract() + .event("BytecodePublished") + .context("BytecodePublished event is missing in ABI") + .unwrap() + .signature(), verifier_contract_abi: verifier_contract(), getters_facet_contract_abi: getters_facet_contract(), - message_root_abi: MESSAGE_ROOT_CONTRACT.clone(), + message_root_abi: l2_message_root(), + l1_asset_router_abi: l1_asset_router_contract(), + wrapped_base_token_store_abi: wrapped_base_token_store_contract(), confirmations_for_eth_event, + wrapped_base_token_store, + l1_shared_bridge_addr, + l2_chain_id, } } @@ -439,51 +456,100 @@ where .await } - async fn get_base_token_metadata(&self) -> Result { - let base_token_addr: Address = CallFunctionArgs::new("getBaseToken", ()) + async fn get_chain_gateway_upgrade_info( + &self, + ) -> Result, ContractCallError> { + let Some(l1_shared_bridge_addr) = self.l1_shared_bridge_addr else { + tracing::warn!("l1 shared bridge is not provided!"); + return Ok(None); + }; + + let Some(l1_wrapped_base_token_store) = self.wrapped_base_token_store else { + tracing::warn!("l1 wrapped base token store is not provided!"); + return Ok(None); + }; + + let l2_chain_id = U256::from(self.l2_chain_id.as_u64()); + + // It does not matter whether the l1 shared bridge is an L1AssetRouter or L1Nullifier, + // either way it supports the "l2BridgeAddress" method. + let l2_legacy_shared_bridge: Address = + CallFunctionArgs::new("l2BridgeAddress", l2_chain_id) + .for_contract(l1_shared_bridge_addr, &self.l1_asset_router_abi) + .call(&self.client) + .await?; + + if l2_legacy_shared_bridge == Address::zero() { + // This state is not completely impossible, but somewhat undesirable. + // Contracts will still allow the upgrade to go through without + // the shared bridge, so we will allow it here as well. + tracing::error!("L2 shared bridge from L1 is empty"); + } + + let l2_predeployed_wrapped_base_token: Address = + CallFunctionArgs::new("l2WBaseTokenAddress", l2_chain_id) + .for_contract( + l1_wrapped_base_token_store, + &self.wrapped_base_token_store_abi, + ) + .call(&self.client) + .await?; + + if l2_predeployed_wrapped_base_token == Address::zero() { + // This state is not completely impossible, but somewhat undesirable. + // Contracts will still allow the upgrade to go through without + // the l2 predeployed wrapped base token, so we will allow it here as well. + tracing::error!("L2 predeployed wrapped base token is empty"); + } + + let base_token_l1_address: Address = CallFunctionArgs::new("getBaseToken", ()) .for_contract(self.diamond_proxy_addr, &self.getters_facet_contract_abi) .call(&self.client) .await?; - if base_token_addr == SHARED_BRIDGE_ETHER_TOKEN_ADDRESS { - return Ok(TokenMetadata { - name: String::from("Ether"), - symbol: String::from("ETH"), - decimals: 18, - }); - } + let (base_token_name, base_token_symbol) = + if base_token_l1_address == SHARED_BRIDGE_ETHER_TOKEN_ADDRESS { + (String::from("Ether"), String::from("ETH")) + } else { + // TODO(EVM-934): support non-standard tokens. + let selectors: [[u8; 4]; 2] = [ + ethabi::short_signature("name", &[]), + ethabi::short_signature("symbol", &[]), + ]; + let types: [ParamType; 2] = [ParamType::String, ParamType::String]; + + let mut decoded_result = vec![]; + for (selector, param_type) in selectors.into_iter().zip(types.into_iter()) { + let request = CallRequest { + to: Some(base_token_l1_address), + data: Some(selector.into()), + ..Default::default() + }; + let result = self.client.call_contract_function(request, None).await?; + // Base tokens are expected to support erc20 metadata + let mut token = ethabi::decode(&[param_type], &result.0) + .expect("base token does not support erc20 metadata"); + decoded_result.push(token.pop().unwrap()); + } - // TODO(EVM-934): support non-standard tokens. - let selectors: [[u8; 4]; 3] = [ - zksync_types::ethabi::short_signature("name", &[]), - zksync_types::ethabi::short_signature("symbol", &[]), - zksync_types::ethabi::short_signature("decimals", &[]), - ]; - let types: [ParamType; 3] = [ParamType::String, ParamType::String, ParamType::Uint(32)]; - - let mut decoded_result = vec![]; - for (selector, param_type) in selectors.into_iter().zip(types.into_iter()) { - let request = CallRequest { - to: Some(base_token_addr), - data: Some(selector.into()), - ..Default::default() + (decoded_result[0].to_string(), decoded_result[1].to_string()) }; - let result = self.client.call_contract_function(request, None).await?; - // Base tokens are expected to support erc20 metadata - let mut token = zksync_types::ethabi::decode(&[param_type], &result.0) - .expect("base token does not support erc20 metadata"); - decoded_result.push(token.pop().unwrap()); - } - Ok(TokenMetadata { - name: decoded_result[0].to_string(), - symbol: decoded_result[1].to_string(), - decimals: decoded_result[2] - .clone() - .into_uint() - .expect("decimals not supported") - .as_u32() as u8, - }) + let base_token_asset_id = encode_ntv_asset_id( + // Note, that this is correct only for tokens that are being upgraded to the gateway protocol version. + // The chains that were deployed after it may have tokens with non-L1 base tokens. + U256::from(self.chain_id().await?.0), + base_token_l1_address, + ); + + Ok(Some(ZkChainSpecificUpgradeData { + base_token_asset_id, + l2_legacy_shared_bridge, + l2_predeployed_wrapped_base_token, + base_token_l1_address, + base_token_name, + base_token_symbol, + })) } } @@ -632,8 +698,10 @@ impl EthClient for L2EthClientW { self.0.get_chain_root(block_number, l2_chain_id).await } - async fn get_base_token_metadata(&self) -> Result { - self.0.get_base_token_metadata().await + async fn get_chain_gateway_upgrade_info( + &self, + ) -> Result, ContractCallError> { + self.0.get_chain_gateway_upgrade_info().await } async fn get_published_preimages( @@ -643,3 +711,13 @@ impl EthClient for L2EthClientW { self.0.get_published_preimages(hashes).await } } + +pub(crate) fn encode_ntv_asset_id(l1_chain_id: U256, addr: Address) -> H256 { + let encoded_data = encode(&[ + ethabi::Token::Uint(l1_chain_id), + ethabi::Token::Address(L2_NATIVE_TOKEN_VAULT_ADDRESS), + ethabi::Token::Address(addr), + ]); + + H256(keccak256(&encoded_data)) +} diff --git a/core/node/eth_watch/src/event_processors/decentralized_upgrades.rs b/core/node/eth_watch/src/event_processors/decentralized_upgrades.rs index c2f499b8ce64..2892d6ca718f 100644 --- a/core/node/eth_watch/src/event_processors/decentralized_upgrades.rs +++ b/core/node/eth_watch/src/event_processors/decentralized_upgrades.rs @@ -3,9 +3,8 @@ use std::sync::Arc; use anyhow::Context as _; use zksync_dal::{eth_watcher_dal::EventType, Connection, Core, CoreDal, DalError}; use zksync_types::{ - abi::ZkChainSpecificUpgradeData, api::Log, ethabi::Contract, - protocol_upgrade::ProtocolUpgradePreimageOracle, protocol_version::ProtocolSemanticVersion, - ProtocolUpgrade, H256, U256, + api::Log, ethabi::Contract, protocol_upgrade::ProtocolUpgradePreimageOracle, + protocol_version::ProtocolSemanticVersion, ProtocolUpgrade, H256, U256, }; use crate::{ @@ -20,7 +19,6 @@ pub struct DecentralizedUpgradesEventProcessor { /// Last protocol version seen. Used to skip events for already known upgrade proposals. last_seen_protocol_version: ProtocolSemanticVersion, update_upgrade_timestamp_signature: H256, - chain_specific_data: Option, sl_client: Arc, l1_client: Arc, } @@ -29,7 +27,6 @@ impl DecentralizedUpgradesEventProcessor { pub fn new( last_seen_protocol_version: ProtocolSemanticVersion, chain_admin_contract: &Contract, - chain_specific_data: Option, sl_client: Arc, l1_client: Arc, ) -> Self { @@ -40,7 +37,6 @@ impl DecentralizedUpgradesEventProcessor { .context("UpdateUpgradeTimestamp event is missing in ABI") .unwrap() .signature(), - chain_specific_data, sl_client, l1_client, } @@ -58,7 +54,10 @@ impl ProtocolUpgradePreimageOracle for &dyn EthClient { let mut result = vec![]; for (i, preimage) in preimages.into_iter().enumerate() { let preimage = preimage.with_context(|| { - format!("Protocol upgrade preimage for {:#?} is missing", hashes[i]) + format!( + "Protocol upgrade preimage under id {i} for {:#?} is missing", + hashes[i] + ) })?; result.push(preimage); } @@ -93,7 +92,7 @@ impl EventProcessor for DecentralizedUpgradesEventProcessor { ..ProtocolUpgrade::try_from_diamond_cut( &diamond_cut, self.l1_client.as_ref(), - self.chain_specific_data.clone(), + self.l1_client.get_chain_gateway_upgrade_info().await?, ) .await? }; diff --git a/core/node/eth_watch/src/lib.rs b/core/node/eth_watch/src/lib.rs index 59f441457139..f866c8e627c5 100644 --- a/core/node/eth_watch/src/lib.rs +++ b/core/node/eth_watch/src/lib.rs @@ -6,14 +6,12 @@ use std::{sync::Arc, time::Duration}; use anyhow::Context as _; use tokio::sync::watch; -use zksync_config::ContractsConfig; use zksync_dal::{Connection, ConnectionPool, Core, CoreDal, DalError}; use zksync_mini_merkle_tree::MiniMerkleTree; use zksync_system_constants::PRIORITY_EXPIRATION; use zksync_types::{ - abi::ZkChainSpecificUpgradeData, ethabi::Contract, protocol_version::ProtocolSemanticVersion, - tokens::TokenMetadata, web3::BlockNumber as Web3BlockNumber, L1BatchNumber, L2ChainId, - PriorityOpId, + ethabi::Contract, protocol_version::ProtocolSemanticVersion, + web3::BlockNumber as Web3BlockNumber, L1BatchNumber, L2ChainId, PriorityOpId, }; pub use self::client::{EthClient, EthHttpQueryClient, L2EthClient}; @@ -58,7 +56,6 @@ impl EthWatch { sl_l2_client: Option>, pool: ConnectionPool, poll_interval: Duration, - contracts_config: &ContractsConfig, chain_id: L2ChainId, ) -> anyhow::Result { let mut storage = pool.connection_tagged("eth_watch").await?; @@ -79,7 +76,6 @@ impl EthWatch { let decentralized_upgrades_processor = DecentralizedUpgradesEventProcessor::new( state.last_seen_protocol_version, chain_admin_contract, - get_chain_specific_upgrade_params(&l1_client, contracts_config).await?, sl_client.clone(), l1_client.clone(), ); @@ -246,19 +242,3 @@ impl EthWatch { Ok(()) } } - -async fn get_chain_specific_upgrade_params( - l1_client: &Arc, - contracts_config: &ContractsConfig, -) -> anyhow::Result> { - let TokenMetadata { name, symbol, .. } = l1_client.get_base_token_metadata().await?; - - Ok(ZkChainSpecificUpgradeData::from_partial_components( - contracts_config.l1_base_token_asset_id, - contracts_config.l2_legacy_shared_bridge_addr, - contracts_config.l2_predeployed_wrapped_base_token_address, - contracts_config.base_token_addr, - Some(name), - Some(symbol), - )) -} diff --git a/core/node/eth_watch/src/tests/client.rs b/core/node/eth_watch/src/tests/client.rs index f242488949b1..cec297435225 100644 --- a/core/node/eth_watch/src/tests/client.rs +++ b/core/node/eth_watch/src/tests/client.rs @@ -6,19 +6,19 @@ use zksync_contracts::{ }; use zksync_eth_client::{ContractCallError, EnrichedClientResult}; use zksync_types::{ - abi::{self, ProposedUpgrade}, + abi::{self, ProposedUpgrade, ZkChainSpecificUpgradeData}, api::{ChainAggProof, Log}, bytecode::BytecodeHash, ethabi::{self, Token}, l1::L1Tx, protocol_upgrade::ProtocolUpgradeTx, - tokens::TokenMetadata, u256_to_h256, web3::{contract::Tokenizable, BlockNumber}, - Address, L1BatchNumber, L2ChainId, ProtocolUpgrade, SLChainId, Transaction, H256, U256, U64, + Address, L1BatchNumber, L2ChainId, ProtocolUpgrade, SLChainId, Transaction, H256, + SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, U256, U64, }; -use crate::client::{EthClient, L2EthClient, RETRY_LIMIT}; +use crate::client::{encode_ntv_asset_id, EthClient, L2EthClient, RETRY_LIMIT}; #[derive(Debug)] pub struct FakeEthClientData { @@ -306,12 +306,20 @@ impl EthClient for MockEthClient { Ok(result) } - async fn get_base_token_metadata(&self) -> Result { - Ok(TokenMetadata { - name: "ETH".to_string(), - symbol: "Ether".to_string(), - decimals: 18, - }) + async fn get_chain_gateway_upgrade_info( + &self, + ) -> Result, ContractCallError> { + Ok(Some(ZkChainSpecificUpgradeData { + base_token_asset_id: encode_ntv_asset_id( + self.chain_id().await?.0.into(), + SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, + ), + l2_legacy_shared_bridge: Address::repeat_byte(0x01), + l2_predeployed_wrapped_base_token: Address::repeat_byte(0x02), + base_token_l1_address: SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, + base_token_name: String::from("Ether"), + base_token_symbol: String::from("ETH"), + })) } async fn fflonk_scheduler_vk_hash( diff --git a/core/node/eth_watch/src/tests/mod.rs b/core/node/eth_watch/src/tests/mod.rs index e6c7945b7d93..36833eb0f2dc 100644 --- a/core/node/eth_watch/src/tests/mod.rs +++ b/core/node/eth_watch/src/tests/mod.rs @@ -1,6 +1,5 @@ use std::convert::TryInto; -use zksync_config::ContractsConfig; use zksync_contracts::chain_admin_contract; use zksync_dal::{Connection, ConnectionPool, Core, CoreDal}; use zksync_types::{ @@ -72,6 +71,7 @@ fn build_upgrade_tx(id: ProtocolVersionId) -> ProtocolUpgradeTx { common_data: ProtocolUpgradeTxCommonData { upgrade_id: id, sender: [1u8; 20].into(), + // Note, that the field is deprecated eth_block: 0, gas_limit: Default::default(), max_fee_per_gas: Default::default(), @@ -109,7 +109,6 @@ async fn create_test_watcher( sl_l2_client, connection_pool, std::time::Duration::from_nanos(1), - &ContractsConfig::for_tests(), L2ChainId::default(), ) .await @@ -216,13 +215,12 @@ async fn test_normal_operation_upgrade_timestamp() { None, connection_pool.clone(), std::time::Duration::from_nanos(1), - &ContractsConfig::for_tests(), L2ChainId::default(), ) .await .unwrap(); - let expected_upgrade_tx = build_upgrade_tx(ProtocolVersionId::Version28); + let expected_upgrade_tx = build_upgrade_tx(ProtocolVersionId::next()); let mut storage = connection_pool.connection().await.unwrap(); client @@ -237,7 +235,7 @@ async fn test_normal_operation_upgrade_timestamp() { ( ProtocolUpgrade { version: ProtocolSemanticVersion { - minor: ProtocolVersionId::Version28, + minor: ProtocolVersionId::next(), patch: 0.into(), }, tx: Some(expected_upgrade_tx.clone()), @@ -248,7 +246,7 @@ async fn test_normal_operation_upgrade_timestamp() { ( ProtocolUpgrade { version: ProtocolSemanticVersion { - minor: ProtocolVersionId::Version28, + minor: ProtocolVersionId::next(), patch: 1.into(), }, tx: None, @@ -272,7 +270,7 @@ async fn test_normal_operation_upgrade_timestamp() { watcher.loop_iteration(&mut storage).await.unwrap(); let db_versions = storage.protocol_versions_dal().all_versions().await; let mut expected_version = ProtocolSemanticVersion { - minor: ProtocolVersionId::Version28, + minor: ProtocolVersionId::next(), patch: 0.into(), }; assert_eq!(db_versions.len(), 4); @@ -283,7 +281,7 @@ async fn test_normal_operation_upgrade_timestamp() { // Check that tx was saved with the second upgrade. let tx = storage .protocol_versions_dal() - .get_protocol_upgrade_tx(ProtocolVersionId::Version28) + .get_protocol_upgrade_tx(ProtocolVersionId::next()) .await .unwrap() .expect("no protocol upgrade transaction"); diff --git a/core/node/genesis/src/lib.rs b/core/node/genesis/src/lib.rs index e68aa59b7696..0425a475c5bc 100644 --- a/core/node/genesis/src/lib.rs +++ b/core/node/genesis/src/lib.rs @@ -8,7 +8,7 @@ use anyhow::Context as _; use zksync_config::GenesisConfig; use zksync_contracts::{ hyperchain_contract, verifier_contract, BaseSystemContracts, BaseSystemContractsHashes, - SET_CHAIN_ID_EVENT, + GENESIS_UPGRADE_EVENT, }; use zksync_dal::{custom_genesis_export_dal::GenesisState, Connection, Core, CoreDal, DalError}; use zksync_eth_client::{CallFunctionArgs, EthInterface}; @@ -20,7 +20,7 @@ use zksync_types::{ bytecode::BytecodeHash, commitment::{CommitmentInput, L1BatchCommitment}, fee_model::BatchFeeInput, - protocol_upgrade::decode_set_chain_id_event, + protocol_upgrade::decode_genesis_upgrade_event, protocol_version::{L1VerifierConfig, ProtocolSemanticVersion}, system_contracts::get_system_smart_contracts, u256_to_h256, @@ -589,14 +589,14 @@ pub async fn save_set_chain_id_tx( storage: &mut Connection<'_, Core>, query_client: &dyn EthInterface, diamond_proxy_address: Address, - state_transition_manager_address: Address, ) -> anyhow::Result<()> { let to = query_client.block_number().await?.as_u64(); let from = to.saturating_sub(PRIORITY_EXPIRATION); + let filter = FilterBuilder::default() - .address(vec![state_transition_manager_address]) + .address(vec![diamond_proxy_address]) .topics( - Some(vec![SET_CHAIN_ID_EVENT.signature()]), + Some(vec![GENESIS_UPGRADE_EVENT.signature()]), Some(vec![diamond_proxy_address.into()]), None, None, @@ -612,7 +612,7 @@ pub async fn save_set_chain_id_tx( logs ); let (version_id, upgrade_tx) = - decode_set_chain_id_event(logs.remove(0)).context("Chain id event is incorrect")?; + decode_genesis_upgrade_event(logs.remove(0)).context("Chain id event is incorrect")?; tracing::info!("New version id {:?}", version_id); storage diff --git a/core/node/node_framework/src/implementations/layers/eth_sender/manager.rs b/core/node/node_framework/src/implementations/layers/eth_sender/manager.rs index e9ce4cc19e1a..b8951c2a91ca 100644 --- a/core/node/node_framework/src/implementations/layers/eth_sender/manager.rs +++ b/core/node/node_framework/src/implementations/layers/eth_sender/manager.rs @@ -6,7 +6,10 @@ use zksync_eth_sender::EthTxManager; use crate::{ implementations::resources::{ circuit_breakers::CircuitBreakersResource, - eth_interface::{BoundEthInterfaceForBlobsResource, BoundEthInterfaceResource}, + eth_interface::{ + BoundEthInterfaceForBlobsResource, BoundEthInterfaceForL2Resource, + BoundEthInterfaceResource, + }, gas_adjuster::GasAdjusterResource, healthcheck::AppHealthCheckResource, pools::{MasterPool, PoolResource, ReplicaPool}, @@ -46,6 +49,7 @@ pub struct Input { pub replica_pool: PoolResource, pub eth_client: BoundEthInterfaceResource, pub eth_client_blobs: Option, + pub eth_client_gateway: Option, pub gas_adjuster: GasAdjusterResource, #[context(default)] pub circuit_breakers: CircuitBreakersResource, @@ -80,10 +84,9 @@ impl WiringLayer for EthTxManagerLayer { let master_pool = input.master_pool.get().await.unwrap(); let replica_pool = input.replica_pool.get().await.unwrap(); - let settlement_mode = self.eth_sender_config.gas_adjuster.unwrap().settlement_mode; let eth_client = input.eth_client.0.clone(); let eth_client_blobs = input.eth_client_blobs.map(|c| c.0); - let l2_client = input.eth_client.0; + let l2_client = input.eth_client_gateway.map(|c| c.0); let config = self.eth_sender_config.sender.context("sender")?; @@ -93,21 +96,9 @@ impl WiringLayer for EthTxManagerLayer { master_pool, config, gas_adjuster, - if !settlement_mode.is_gateway() { - Some(eth_client) - } else { - None - }, - if !settlement_mode.is_gateway() { - eth_client_blobs - } else { - None - }, - if settlement_mode.is_gateway() { - Some(l2_client) - } else { - None - }, + Some(eth_client), + eth_client_blobs, + l2_client, ); // Insert circuit breaker. diff --git a/core/node/node_framework/src/implementations/layers/eth_watch.rs b/core/node/node_framework/src/implementations/layers/eth_watch.rs index da0e26355cf2..92356e770c40 100644 --- a/core/node/node_framework/src/implementations/layers/eth_watch.rs +++ b/core/node/node_framework/src/implementations/layers/eth_watch.rs @@ -98,6 +98,11 @@ impl WiringLayer for EthWatchLayer { .ecosystem_contracts .as_ref() .and_then(|a| a.l1_bytecodes_supplier_addr), + self.contracts_config + .ecosystem_contracts + .as_ref() + .and_then(|a| a.l1_wrapped_base_token_store), + self.contracts_config.l1_shared_bridge_proxy_addr, self.contracts_config .ecosystem_contracts .as_ref() @@ -105,6 +110,7 @@ impl WiringLayer for EthWatchLayer { self.contracts_config.chain_admin_addr, self.contracts_config.governance_addr, self.eth_watch_config.confirmations_for_eth_event, + self.chain_id, ); let sl_l2_client: Option> = @@ -113,12 +119,17 @@ impl WiringLayer for EthWatchLayer { Some(Box::new(EthHttpQueryClient::new( gateway_client.0, contracts_config.diamond_proxy_addr, - // Bytecode supplier is only present on L1 + // Only present on L1. + None, + // Only present on L1. + None, + // Only present on L1. None, Some(contracts_config.state_transition_proxy_addr), contracts_config.chain_admin_addr, contracts_config.governance_addr, self.eth_watch_config.confirmations_for_eth_event, + self.chain_id, ))) } else { None @@ -130,7 +141,6 @@ impl WiringLayer for EthWatchLayer { sl_l2_client, main_pool, self.eth_watch_config.poll_interval(), - &self.contracts_config, self.chain_id, ) .await?; diff --git a/core/node/node_framework/src/implementations/layers/web3_api/server/bridge_addresses.rs b/core/node/node_framework/src/implementations/layers/web3_api/server/bridge_addresses.rs index 785c19846a60..a515e4cc1db9 100644 --- a/core/node/node_framework/src/implementations/layers/web3_api/server/bridge_addresses.rs +++ b/core/node/node_framework/src/implementations/layers/web3_api/server/bridge_addresses.rs @@ -1,8 +1,8 @@ use std::time::Duration; -use zksync_eth_client::CallFunctionArgs; +use zksync_eth_client::{CallFunctionArgs, ContractCallError}; use zksync_node_api_server::web3::state::BridgeAddressesHandle; -use zksync_types::{ethabi::Contract, Address}; +use zksync_types::{ethabi::Contract, Address, L2_ASSET_ROUTER_ADDRESS}; use zksync_web3_decl::{ client::{DynClient, L1, L2}, namespaces::ZksNamespaceClient, @@ -37,20 +37,52 @@ pub struct L1UpdaterInner { pub bridgehub_addr: Address, pub update_interval: Option, pub bridgehub_abi: Contract, + pub l1_asset_router_abi: Contract, +} + +struct L1SharedBridgeInfo { + l1_shared_bridge_addr: Address, + should_use_l2_asset_router: bool, } impl L1UpdaterInner { - async fn loop_iteration(&self) { - let call_result = CallFunctionArgs::new("sharedBridge", ()) + async fn get_shared_bridge_info(&self) -> Result { + let l1_shared_bridge_addr: Address = CallFunctionArgs::new("sharedBridge", ()) .for_contract(self.bridgehub_addr, &self.bridgehub_abi) .call(&self.l1_eth_client) - .await; + .await?; + + let l1_nullifier_addr: Result = + CallFunctionArgs::new("L1_NULLIFIER", ()) + .for_contract(l1_shared_bridge_addr, &self.l1_asset_router_abi) + .call(&self.l1_eth_client) + .await; - match call_result { - Ok(shared_bridge_address) => { + // In case we can successfully retrieve the l1 nullifier, this is definitely the new l1 asset router. + // The contrary is not necessarily true: the query can fail either due to network issues or + // due to the contract being outdated. To be conservative, we just always treat such cases as `false`. + let should_use_l2_asset_router = l1_nullifier_addr.is_ok(); + + Ok(L1SharedBridgeInfo { + l1_shared_bridge_addr, + should_use_l2_asset_router, + }) + } + + async fn loop_iteration(&self) { + match self.get_shared_bridge_info().await { + Ok(info) => { self.bridge_address_updater - .update_l1_shared_bridge(shared_bridge_address) + .update_l1_shared_bridge(info.l1_shared_bridge_addr) .await; + // We only update one way: + // - Once the L2 asset router should be used, there is never a need to go back + // - To not undo the previous change in case of a network error + if info.should_use_l2_asset_router { + self.bridge_address_updater + .update_l2_shared_bridge(L2_ASSET_ROUTER_ADDRESS) + .await; + } } Err(err) => { tracing::error!("Failed to query shared bridge address, error: {err:?}"); diff --git a/core/node/node_framework/src/implementations/layers/web3_api/server/mod.rs b/core/node/node_framework/src/implementations/layers/web3_api/server/mod.rs index c4c18b6ecb3f..b1d9ca79979e 100644 --- a/core/node/node_framework/src/implementations/layers/web3_api/server/mod.rs +++ b/core/node/node_framework/src/implementations/layers/web3_api/server/mod.rs @@ -5,7 +5,7 @@ use bridge_addresses::{L1UpdaterInner, MainNodeUpdaterInner}; use tokio::{sync::oneshot, task::JoinHandle}; use zksync_circuit_breaker::replication_lag::ReplicationLagChecker; use zksync_config::configs::api::MaxResponseSize; -use zksync_contracts::bridgehub_contract; +use zksync_contracts::{bridgehub_contract, l1_asset_router_contract}; use zksync_node_api_server::web3::{ state::{BridgeAddressesHandle, InternalApiConfig, SealedL2BlockNumber}, ApiBuilder, ApiServer, Namespace, @@ -209,25 +209,26 @@ impl WiringLayer for Web3ServerLayer { // In case it is an EN, the bridge addresses should be updated by fetching values from the main node. // It is the main node, the bridge addresses need to be updated by querying the L1. - - let bridge_addresses_updater_task = if let Some(main_node_client) = input.main_node_client { - BridgeAddressesUpdaterTask::MainNodeUpdater(MainNodeUpdaterInner { - bridge_address_updater: bridge_addresses_handle.clone(), - main_node_client: main_node_client.0, - update_interval: self.optional_config.bridge_addresses_refresh_interval, - }) - } else { - BridgeAddressesUpdaterTask::L1Updater(L1UpdaterInner { - bridge_address_updater: bridge_addresses_handle.clone(), - l1_eth_client: input.l1_eth_client.0, - bridgehub_addr: self - .internal_api_config - .l1_bridgehub_proxy_addr - .context("Lacking l1 bridgehub proxy address")?, - update_interval: self.optional_config.bridge_addresses_refresh_interval, - bridgehub_abi: bridgehub_contract(), - }) - }; + let bridge_addresses_updater_task = + if let Some(main_node_client) = input.main_node_client.clone() { + BridgeAddressesUpdaterTask::MainNodeUpdater(MainNodeUpdaterInner { + bridge_address_updater: bridge_addresses_handle.clone(), + main_node_client: main_node_client.0, + update_interval: self.optional_config.bridge_addresses_refresh_interval, + }) + } else { + BridgeAddressesUpdaterTask::L1Updater(L1UpdaterInner { + bridge_address_updater: bridge_addresses_handle.clone(), + l1_eth_client: input.l1_eth_client.0, + bridgehub_addr: self + .internal_api_config + .l1_bridgehub_proxy_addr + .context("Lacking l1 bridgehub proxy address")?, + update_interval: self.optional_config.bridge_addresses_refresh_interval, + bridgehub_abi: bridgehub_contract(), + l1_asset_router_abi: l1_asset_router_contract(), + }) + }; // Build server. let mut api_builder = @@ -251,6 +252,9 @@ impl WiringLayer for Web3ServerLayer { if let Some(sync_state) = sync_state { api_builder = api_builder.with_sync_state(sync_state); } + if let Some(main_node_client) = input.main_node_client { + api_builder = api_builder.with_l2_l1_log_proof_handler(main_node_client.0) + } let replication_lag_limit = self.optional_config.replication_lag_limit; api_builder = self.optional_config.apply(api_builder); diff --git a/core/node/node_storage_init/src/main_node/genesis.rs b/core/node/node_storage_init/src/main_node/genesis.rs index a5d6c0e628ac..cef25e87ba7c 100644 --- a/core/node/node_storage_init/src/main_node/genesis.rs +++ b/core/node/node_storage_init/src/main_node/genesis.rs @@ -55,16 +55,13 @@ impl InitializeStorage for MainNodeGenesis { ) .await?; - if let Some(ecosystem_contracts) = &self.contracts.ecosystem_contracts { - zksync_node_genesis::save_set_chain_id_tx( - &mut storage, - &self.l1_client, - self.contracts.diamond_proxy_addr, - ecosystem_contracts.state_transition_proxy_addr, - ) - .await - .context("Failed to save SetChainId upgrade transaction")?; - } + zksync_node_genesis::save_set_chain_id_tx( + &mut storage, + &self.l1_client, + self.contracts.diamond_proxy_addr, + ) + .await + .context("Failed to save SetChainId upgrade transaction")?; Ok(()) } diff --git a/core/node/node_sync/src/tree_data_fetcher/provider/mod.rs b/core/node/node_sync/src/tree_data_fetcher/provider/mod.rs index 432808422632..c627006f70e7 100644 --- a/core/node/node_sync/src/tree_data_fetcher/provider/mod.rs +++ b/core/node/node_sync/src/tree_data_fetcher/provider/mod.rs @@ -144,17 +144,11 @@ impl L1DataProvider { diamond_proxy_addr: l1_diamond_proxy_addr, }; let gateway_chain_data = if let Some(client) = gateway_client { - let contract = bridgehub_contract(); - let function_name = if contract.function("getZKChain").is_ok() { - "getZKChain" - } else { - "getHyperchain" - }; let gateway_diamond_proxy = CallFunctionArgs::new( - function_name, + "getZKChain", zksync_types::ethabi::Token::Uint(l2_chain_id.as_u64().into()), ) - .for_contract(L2_BRIDGEHUB_ADDRESS, &contract) + .for_contract(L2_BRIDGEHUB_ADDRESS, &bridgehub_contract()) .call(&client) .await?; let chain_id = client.fetch_chain_id().await?; diff --git a/core/node/node_sync/src/tree_data_fetcher/provider/tests.rs b/core/node/node_sync/src/tree_data_fetcher/provider/tests.rs index e8c855359390..14ab34bab10d 100644 --- a/core/node/node_sync/src/tree_data_fetcher/provider/tests.rs +++ b/core/node/node_sync/src/tree_data_fetcher/provider/tests.rs @@ -256,13 +256,8 @@ fn mock_l1_client(block_number: U64, logs: Vec, chain_id: SLChainId) .method("eth_chainId", move || Ok(U64::from(chain_id.0))) .method("eth_call", move |req: CallRequest, _block_id: BlockId| { let contract = bridgehub_contract(); - let function_name = if contract.function("getZKChain").is_ok() { - "getZKChain" - } else { - "getHyperchain" - }; let expected_input = contract - .function(function_name) + .function("getZKChain") .unwrap() .encode_input(&[ethabi::Token::Uint(ERA_CHAIN_ID.into())]) .unwrap(); diff --git a/core/node/state_keeper/src/executor/tests/mod.rs b/core/node/state_keeper/src/executor/tests/mod.rs index eade0233d0e0..219cacc60c85 100644 --- a/core/node/state_keeper/src/executor/tests/mod.rs +++ b/core/node/state_keeper/src/executor/tests/mod.rs @@ -3,11 +3,13 @@ use assert_matches::assert_matches; use rand::{thread_rng, Rng}; use test_casing::{test_casing, Product}; +use zksync_contracts::l2_message_root; use zksync_dal::{ConnectionPool, Core}; use zksync_multivm::interface::{BatchTransactionExecutionResult, ExecutionResult, Halt}; use zksync_test_contracts::{Account, TestContract}; use zksync_types::{ - get_nonce_key, utils::storage_key_for_eth_balance, vm::FastVmMode, web3, PriorityOpId, H256, + get_nonce_key, utils::storage_key_for_eth_balance, vm::FastVmMode, web3, Execute, PriorityOpId, + H256, L2_MESSAGE_ROOT_ADDRESS, U256, }; use self::tester::{AccountExt, StorageSnapshot, TestConfig, Tester}; @@ -63,7 +65,30 @@ async fn execute_l2_tx(storage_type: StorageType, vm_mode: FastVmMode) { let mut tester = Tester::new(connection_pool, vm_mode); tester.genesis().await; tester.fund(&[alice.address()]).await; - let mut executor = tester.create_batch_executor(storage_type).await; + + let l2_message_root = l2_message_root(); + let encoded_data = l2_message_root + .function("initialize") + .unwrap() + .encode_input(&[]) + .unwrap(); + + let message_root_init_txn = alice.get_l2_tx_for_execute( + Execute { + contract_address: Some(L2_MESSAGE_ROOT_ADDRESS), + calldata: encoded_data, + value: U256::zero(), + factory_deps: vec![], + }, + None, + ); + + let mut executor = tester + .create_batch_executor_with_init_transactions( + storage_type, + &[message_root_init_txn.clone()], + ) + .await; let res = executor.execute_tx(alice.execute()).await.unwrap(); assert_executed(&res); @@ -106,7 +131,25 @@ async fn execute_l2_tx_after_snapshot_recovery( let mut alice = Account::random(); let connection_pool = ConnectionPool::::constrained_test_pool(1).await; - let mut storage_snapshot = StorageSnapshot::new(&connection_pool, &mut alice, 10).await; + let l2_message_root = l2_message_root(); + let encoded_data = l2_message_root + .function("initialize") + .unwrap() + .encode_input(&[]) + .unwrap(); + + let message_root_init_txn = alice.get_l2_tx_for_execute( + Execute { + contract_address: Some(L2_MESSAGE_ROOT_ADDRESS), + calldata: encoded_data, + value: U256::zero(), + factory_deps: vec![], + }, + None, + ); + + let mut storage_snapshot = + StorageSnapshot::new(&connection_pool, &mut alice, 10, &[message_root_init_txn]).await; assert!(storage_snapshot.storage_logs.len() > 10); // sanity check assert!(!storage_snapshot.factory_deps.is_empty()); if let Some(mutation) = mutation { @@ -138,8 +181,29 @@ async fn execute_l1_tx(vm_mode: FastVmMode) { tester.genesis().await; tester.fund(&[alice.address()]).await; + + let l2_message_root = l2_message_root(); + let encoded_data = l2_message_root + .function("initialize") + .unwrap() + .encode_input(&[]) + .unwrap(); + + let message_root_init_txn = alice.get_l2_tx_for_execute( + Execute { + contract_address: Some(L2_MESSAGE_ROOT_ADDRESS), + calldata: encoded_data, + value: U256::zero(), + factory_deps: vec![], + }, + None, + ); + let mut executor = tester - .create_batch_executor(StorageType::AsyncRocksdbCache) + .create_batch_executor_with_init_transactions( + StorageType::AsyncRocksdbCache, + &[message_root_init_txn.clone()], + ) .await; let res = executor @@ -160,8 +224,29 @@ async fn execute_l2_and_l1_txs(vm_mode: FastVmMode) { let mut tester = Tester::new(connection_pool, vm_mode); tester.genesis().await; tester.fund(&[alice.address()]).await; + + let l2_message_root = l2_message_root(); + let encoded_data = l2_message_root + .function("initialize") + .unwrap() + .encode_input(&[]) + .unwrap(); + + let message_root_init_txn = alice.get_l2_tx_for_execute( + Execute { + contract_address: Some(L2_MESSAGE_ROOT_ADDRESS), + calldata: encoded_data, + value: U256::zero(), + factory_deps: vec![], + }, + None, + ); + let mut executor = tester - .create_batch_executor(StorageType::AsyncRocksdbCache) + .create_batch_executor_with_init_transactions( + StorageType::AsyncRocksdbCache, + &[message_root_init_txn.clone()], + ) .await; let res = executor.execute_tx(alice.execute()).await.unwrap(); @@ -243,8 +328,29 @@ async fn rollback(vm_mode: FastVmMode) { tester.genesis().await; tester.fund(&[alice.address()]).await; + + let l2_message_root = l2_message_root(); + let encoded_data = l2_message_root + .function("initialize") + .unwrap() + .encode_input(&[]) + .unwrap(); + + let message_root_init_txn = alice.get_l2_tx_for_execute( + Execute { + contract_address: Some(L2_MESSAGE_ROOT_ADDRESS), + calldata: encoded_data, + value: U256::zero(), + factory_deps: vec![], + }, + None, + ); + let mut executor = tester - .create_batch_executor(StorageType::AsyncRocksdbCache) + .create_batch_executor_with_init_transactions( + StorageType::AsyncRocksdbCache, + &[message_root_init_txn.clone()], + ) .await; let tx = alice.execute(); @@ -297,8 +403,29 @@ async fn too_big_gas_limit(vm_mode: FastVmMode) { let mut tester = Tester::new(connection_pool, vm_mode); tester.genesis().await; tester.fund(&[alice.address()]).await; + + let l2_message_root = l2_message_root(); + let encoded_data = l2_message_root + .function("initialize") + .unwrap() + .encode_input(&[]) + .unwrap(); + + let message_root_init_txn = alice.get_l2_tx_for_execute( + Execute { + contract_address: Some(L2_MESSAGE_ROOT_ADDRESS), + calldata: encoded_data, + value: U256::zero(), + factory_deps: vec![], + }, + None, + ); + let mut executor = tester - .create_batch_executor(StorageType::AsyncRocksdbCache) + .create_batch_executor_with_init_transactions( + StorageType::AsyncRocksdbCache, + &[message_root_init_txn.clone()], + ) .await; let big_gas_limit_tx = alice.execute_with_gas_limit(u32::MAX); @@ -341,8 +468,29 @@ async fn deploy_and_call_loadtest(vm_mode: FastVmMode) { let mut tester = Tester::new(connection_pool, vm_mode); tester.genesis().await; tester.fund(&[alice.address()]).await; + + let l2_message_root = l2_message_root(); + let encoded_data = l2_message_root + .function("initialize") + .unwrap() + .encode_input(&[]) + .unwrap(); + + let message_root_init_txn = alice.get_l2_tx_for_execute( + Execute { + contract_address: Some(L2_MESSAGE_ROOT_ADDRESS), + calldata: encoded_data, + value: U256::zero(), + factory_deps: vec![], + }, + None, + ); + let mut executor = tester - .create_batch_executor(StorageType::AsyncRocksdbCache) + .create_batch_executor_with_init_transactions( + StorageType::AsyncRocksdbCache, + &[message_root_init_txn.clone()], + ) .await; let tx = alice.deploy_loadnext_tx(); @@ -389,13 +537,35 @@ async fn deploy_failedcall(vm_mode: FastVmMode) { async fn execute_reverted_tx(vm_mode: FastVmMode) { let connection_pool = ConnectionPool::::constrained_test_pool(1).await; let mut alice = Account::random(); + let mut bob = Account::random(); let mut tester = Tester::new(connection_pool, vm_mode); tester.genesis().await; - tester.fund(&[alice.address()]).await; + tester.fund(&[alice.address(), bob.address()]).await; + + let l2_message_root = l2_message_root(); + let encoded_data = l2_message_root + .function("initialize") + .unwrap() + .encode_input(&[]) + .unwrap(); + + let message_root_init_txn = bob.get_l2_tx_for_execute( + Execute { + contract_address: Some(L2_MESSAGE_ROOT_ADDRESS), + calldata: encoded_data, + value: U256::zero(), + factory_deps: vec![], + }, + None, + ); + let mut executor = tester - .create_batch_executor(StorageType::AsyncRocksdbCache) + .create_batch_executor_with_init_transactions( + StorageType::AsyncRocksdbCache, + &[message_root_init_txn.clone()], + ) .await; let tx = alice.deploy_loadnext_tx(); @@ -427,8 +597,29 @@ async fn execute_realistic_scenario(vm_mode: FastVmMode) { tester.genesis().await; tester.fund(&[alice.address()]).await; tester.fund(&[bob.address()]).await; + + let l2_message_root = l2_message_root(); + let encoded_data = l2_message_root + .function("initialize") + .unwrap() + .encode_input(&[]) + .unwrap(); + + let message_root_init_txn = alice.get_l2_tx_for_execute( + Execute { + contract_address: Some(L2_MESSAGE_ROOT_ADDRESS), + calldata: encoded_data, + value: U256::zero(), + factory_deps: vec![], + }, + None, + ); + let mut executor = tester - .create_batch_executor(StorageType::AsyncRocksdbCache) + .create_batch_executor_with_init_transactions( + StorageType::AsyncRocksdbCache, + &[message_root_init_txn.clone()], + ) .await; // A good tx should be executed successfully. @@ -567,8 +758,30 @@ async fn catchup_rocksdb_cache() { tester.genesis().await; tester.fund(&[alice.address(), bob.address()]).await; + let l2_message_root = l2_message_root(); + let encoded_data = l2_message_root + .function("initialize") + .unwrap() + .encode_input(&[]) + .unwrap(); + + let message_root_init_txn = alice.get_l2_tx_for_execute( + Execute { + contract_address: Some(L2_MESSAGE_ROOT_ADDRESS), + calldata: encoded_data, + value: U256::zero(), + factory_deps: vec![], + }, + None, + ); + // Execute a bunch of transactions to populate Postgres-based storage (note that RocksDB stays empty) - let mut executor = tester.create_batch_executor(StorageType::Postgres).await; + let mut executor = tester + .create_batch_executor_with_init_transactions( + StorageType::Postgres, + &[message_root_init_txn.clone()], + ) + .await; for _ in 0..10 { let res = executor.execute_tx(alice.execute()).await.unwrap(); assert_executed(&res); @@ -582,7 +795,10 @@ async fn catchup_rocksdb_cache() { // Async RocksDB cache should be aware of the tx and should reject it let mut executor = tester - .create_batch_executor(StorageType::AsyncRocksdbCache) + .create_batch_executor_with_init_transactions( + StorageType::AsyncRocksdbCache, + &[message_root_init_txn.clone()], + ) .await; let res = executor.execute_tx(tx.clone()).await.unwrap(); assert_rejected(&res); @@ -595,7 +811,12 @@ async fn catchup_rocksdb_cache() { tester.wait_for_tasks().await; // Sync RocksDB storage should be aware of the tx and should reject it - let mut executor = tester.create_batch_executor(StorageType::Rocksdb).await; + let mut executor = tester + .create_batch_executor_with_init_transactions( + StorageType::Rocksdb, + &[message_root_init_txn.clone()], + ) + .await; let res = executor.execute_tx(tx).await.unwrap(); assert_rejected(&res); } diff --git a/core/node/state_keeper/src/executor/tests/tester.rs b/core/node/state_keeper/src/executor/tests/tester.rs index 3727d9c16bfb..8b6df7f04840 100644 --- a/core/node/state_keeper/src/executor/tests/tester.rs +++ b/core/node/state_keeper/src/executor/tests/tester.rs @@ -1,16 +1,18 @@ //! Testing harness for the batch executor. //! Contains helper functionality to initialize test context and perform tests without too much boilerplate. -use std::{collections::HashMap, fmt::Debug, sync::Arc}; +use std::{collections::HashMap, fmt::Debug, str::FromStr, sync::Arc}; +use assert_matches::assert_matches; use tempfile::TempDir; use tokio::{sync::watch, task::JoinHandle}; use zksync_config::configs::chain::StateKeeperConfig; -use zksync_dal::{ConnectionPool, Core, CoreDal}; +use zksync_contracts::l2_rollup_da_validator_bytecode; +use zksync_dal::{Connection, ConnectionPool, Core, CoreDal}; use zksync_multivm::{ interface::{ executor::{BatchExecutor, BatchExecutorFactory}, - L1BatchEnv, L2BlockEnv, SystemEnv, + ExecutionResult, L1BatchEnv, L2BlockEnv, SystemEnv, }, utils::StorageWritesDeduplicator, vm_latest::constants::INITIAL_STORAGE_WRITE_PUBDATA_BYTES, @@ -23,8 +25,10 @@ use zksync_test_contracts::{ }; use zksync_types::{ block::L2BlockHasher, + bytecode::BytecodeHash, commitment::PubdataParams, ethabi::Token, + get_code_key, get_known_code_key, protocol_version::ProtocolSemanticVersion, snapshots::{SnapshotRecoveryStatus, SnapshotStorageLog}, system_contracts::get_system_smart_contracts, @@ -38,12 +42,15 @@ use zksync_vm_executor::batch::{MainBatchExecutorFactory, TraceCalls}; use super::{read_storage_factory::RocksdbStorageFactory, StorageType}; use crate::{ - testonly, - testonly::BASE_SYSTEM_CONTRACTS, + testonly::{self, BASE_SYSTEM_CONTRACTS}, tests::{default_l1_batch_env, default_system_env}, AsyncRocksdbCache, }; +fn get_da_contract_address() -> Address { + Address::from_str("7726827caac94a7f9e1b160f7ea819f172f7b6f9").unwrap() +} + /// Representation of configuration parameters used by the state keeper. /// Has sensible defaults for most tests, each of which can be overridden. #[derive(Debug)] @@ -97,6 +104,22 @@ impl Tester { self.config = config; } + /// Extension of `create_batch_executor` that allows us to run some initial transactions to bootstrap the state. + pub(super) async fn create_batch_executor_with_init_transactions( + &mut self, + storage_type: StorageType, + transactions: &[Transaction], + ) -> Box> { + let mut executor = self.create_batch_executor(storage_type).await; + + for txn in transactions { + let res = executor.execute_tx(txn.clone()).await.unwrap(); + assert_matches!(res.tx_result.result, ExecutionResult::Success { .. }); + } + + executor + } + /// Creates a batch executor instance with the specified storage type. /// This function intentionally uses sensible defaults to not introduce boilerplate. pub(super) async fn create_batch_executor( @@ -270,6 +293,9 @@ impl Tester { ) .await .unwrap(); + + // Also setting up the da for tests + Self::setup_da(&mut storage).await; } } @@ -308,6 +334,42 @@ impl Tester { } } + pub async fn setup_contract<'a>( + con: &mut Connection<'a, Core>, + address: Address, + code: Vec, + ) { + let hash: H256 = BytecodeHash::for_bytecode(&code).value(); + let known_code_key = get_known_code_key(&hash); + let code_key = get_code_key(&address); + + let logs = vec![ + StorageLog::new_write_log(known_code_key, H256::from_low_u64_be(1u64)), + StorageLog::new_write_log(code_key, hash), + ]; + + for log in logs { + apply_genesis_log(con, log).await; + } + + let mut factory_deps = HashMap::new(); + factory_deps.insert(hash, code); + + con.factory_deps_dal() + .insert_factory_deps(L2BlockNumber(0), &factory_deps) + .await + .unwrap(); + } + + async fn setup_da<'a>(con: &mut Connection<'a, Core>) { + Self::setup_contract( + con, + get_da_contract_address(), + l2_rollup_da_validator_bytecode(), + ) + .await; + } + pub(super) async fn wait_for_tasks(&mut self) { for task in self.tasks.drain(..) { task.await.expect("Failed to join a task"); @@ -557,6 +619,7 @@ impl StorageSnapshot { connection_pool: &ConnectionPool, alice: &mut Account, transaction_count: u32, + transactions: &[Transaction], ) -> Self { let mut tester = Tester::new(connection_pool.clone(), FastVmMode::Old); tester.genesis().await; @@ -594,6 +657,30 @@ impl StorageSnapshot { }; let mut storage_writes_deduplicator = StorageWritesDeduplicator::new(); + for transaction in transactions { + let tx_hash = transaction.hash(); // probably incorrect + let res = executor.execute_tx(transaction.clone()).await.unwrap(); + if !res.tx_result.result.is_failed() { + let storage_logs = &res.tx_result.logs.storage_logs; + storage_writes_deduplicator + .apply(storage_logs.iter().filter(|log| log.log.is_write())); + } else { + panic!("Unexpected tx execution result: {res:?}"); + }; + + let mut hasher = L2BlockHasher::new( + L2BlockNumber(l2_block_env.number), + l2_block_env.timestamp, + l2_block_env.prev_block_hash, + ); + hasher.push_tx_hash(tx_hash); + + l2_block_env.number += 1; + l2_block_env.timestamp += 1; + l2_block_env.prev_block_hash = hasher.finalize(ProtocolVersionId::latest()); + executor.start_next_l2_block(l2_block_env).await.unwrap(); + } + for _ in 0..transaction_count { let tx = alice.execute(); let tx_hash = tx.hash(); // probably incorrect @@ -679,3 +766,25 @@ impl StorageSnapshot { snapshot } } + +async fn apply_genesis_log<'a>(storage: &mut Connection<'a, Core>, log: StorageLog) { + storage + .storage_logs_dal() + .append_storage_logs(L2BlockNumber(0), &[log]) + .await + .unwrap(); + + if storage + .storage_logs_dedup_dal() + .filter_written_slots(&[log.key.hashed_key()]) + .await + .unwrap() + .is_empty() + { + storage + .storage_logs_dedup_dal() + .insert_initial_writes(L1BatchNumber(0), &[log.key.hashed_key()]) + .await + .unwrap(); + } +} diff --git a/core/node/state_keeper/src/keeper.rs b/core/node/state_keeper/src/keeper.rs index c892fd8534ec..401150a3fccd 100644 --- a/core/node/state_keeper/src/keeper.rs +++ b/core/node/state_keeper/src/keeper.rs @@ -259,7 +259,7 @@ impl ZkSyncStateKeeper { } /// This function is meant to be called only once during the state-keeper initialization. - /// It will check if we should load a protocol upgrade or a `setChainId` transaction, + /// It will check if we should load a protocol upgrade or a `GenesisUpgrade` transaction, /// perform some checks and return it. pub(super) async fn load_protocol_upgrade_tx( &mut self, @@ -268,9 +268,9 @@ impl ZkSyncStateKeeper { l1_batch_number: L1BatchNumber, ) -> Result, Error> { // After the Shared Bridge is integrated, - // there has to be a setChainId upgrade transaction after the chain genesis. + // there has to be a GenesisUpgrade upgrade transaction after the chain genesis. // It has to be the first transaction of the first batch. - // The setChainId upgrade does not bump the protocol version, but attaches an upgrade + // The GenesisUpgrade upgrade does not bump the protocol version, but attaches an upgrade // transaction to the genesis protocol version. let first_batch_in_shared_bridge = l1_batch_number == L1BatchNumber(1) && !protocol_version.is_pre_shared_bridge(); diff --git a/core/node/state_keeper/src/testonly/mod.rs b/core/node/state_keeper/src/testonly/mod.rs index 3da666628b1b..c0f3707f9455 100644 --- a/core/node/state_keeper/src/testonly/mod.rs +++ b/core/node/state_keeper/src/testonly/mod.rs @@ -1,10 +1,12 @@ //! Test utilities that can be used for testing sequencer that may //! be useful outside of this crate. +use std::collections::HashMap; + use async_trait::async_trait; use once_cell::sync::Lazy; use zksync_contracts::BaseSystemContracts; -use zksync_dal::{ConnectionPool, Core, CoreDal as _}; +use zksync_dal::{Connection, ConnectionPool, Core, CoreDal as _}; use zksync_multivm::interface::{ executor::{BatchExecutor, BatchExecutorFactory}, storage::{InMemoryStorage, StorageView}, @@ -13,10 +15,10 @@ use zksync_multivm::interface::{ }; use zksync_state::OwnedStorage; use zksync_types::{ - commitment::PubdataParams, fee::Fee, u256_to_h256, - utils::storage_key_for_standard_token_balance, AccountTreeId, Address, L1BatchNumber, - L2BlockNumber, StorageLog, Transaction, L2_BASE_TOKEN_ADDRESS, SYSTEM_CONTEXT_MINIMAL_BASE_FEE, - U256, + bytecode::BytecodeHash, commitment::PubdataParams, fee::Fee, get_code_key, get_known_code_key, + u256_to_h256, utils::storage_key_for_standard_token_balance, AccountTreeId, Address, + L1BatchNumber, L2BlockNumber, StorageLog, Transaction, H256, L2_BASE_TOKEN_ADDRESS, + SYSTEM_CONTEXT_MINIMAL_BASE_FEE, U256, }; pub mod test_batch_executor; @@ -74,6 +76,27 @@ impl BatchExecutor for MockBatchExecutor { } } +async fn apply_genesis_log<'a>(storage: &mut Connection<'a, Core>, log: StorageLog) { + storage + .storage_logs_dal() + .append_storage_logs(L2BlockNumber(0), &[log]) + .await + .unwrap(); + if storage + .storage_logs_dedup_dal() + .filter_written_slots(&[log.key.hashed_key()]) + .await + .unwrap() + .is_empty() + { + storage + .storage_logs_dedup_dal() + .insert_initial_writes(L1BatchNumber(0), &[log.key.hashed_key()]) + .await + .unwrap(); + } +} + /// Adds funds for specified account list. /// Expects genesis to be performed (i.e. `setup_storage` called beforehand). pub async fn fund(pool: &ConnectionPool, addresses: &[Address]) { @@ -89,27 +112,36 @@ pub async fn fund(pool: &ConnectionPool, addresses: &[Address]) { let value = u256_to_h256(eth_amount); let storage_log = StorageLog::new_write_log(key, value); - storage - .storage_logs_dal() - .append_storage_logs(L2BlockNumber(0), &[storage_log]) - .await - .unwrap(); - if storage - .storage_logs_dedup_dal() - .filter_written_slots(&[storage_log.key.hashed_key()]) - .await - .unwrap() - .is_empty() - { - storage - .storage_logs_dedup_dal() - .insert_initial_writes(L1BatchNumber(0), &[storage_log.key.hashed_key()]) - .await - .unwrap(); - } + apply_genesis_log(&mut storage, storage_log).await; } } +pub async fn setup_contract(pool: &ConnectionPool, address: Address, code: Vec) { + let mut storage = pool.connection().await.unwrap(); + + let hash: H256 = BytecodeHash::for_bytecode(&code).value(); + let known_code_key = get_known_code_key(&hash); + let code_key = get_code_key(&address); + + let logs = vec![ + StorageLog::new_write_log(known_code_key, H256::from_low_u64_be(1u64)), + StorageLog::new_write_log(code_key, hash), + ]; + + for log in logs { + apply_genesis_log(&mut storage, log).await; + } + + let mut factory_deps = HashMap::new(); + factory_deps.insert(hash, code); + + storage + .factory_deps_dal() + .insert_factory_deps(L2BlockNumber(0), &factory_deps) + .await + .unwrap(); +} + pub(crate) const DEFAULT_GAS_PER_PUBDATA: u32 = 10000; pub fn fee(gas_limit: u32) -> Fee { diff --git a/core/node/state_keeper/src/tests/mod.rs b/core/node/state_keeper/src/tests/mod.rs index b73741998a03..e235cddf8423 100644 --- a/core/node/state_keeper/src/tests/mod.rs +++ b/core/node/state_keeper/src/tests/mod.rs @@ -365,7 +365,7 @@ async fn load_upgrade_tx() { // TODO: add one more test case for the shared bridge after it's integrated. // If we are processing the 1st batch while using the shared bridge, - // we should load the upgrade transaction -- that's the `SetChainIdUpgrade`. + // we should load the upgrade transaction -- that's the `GenesisUpgrade`. } /// Unconditionally seal the batch without triggering specific criteria. diff --git a/core/tests/loadnext/src/executor.rs b/core/tests/loadnext/src/executor.rs index 43a1be164b64..d0fbb696b607 100644 --- a/core/tests/loadnext/src/executor.rs +++ b/core/tests/loadnext/src/executor.rs @@ -400,6 +400,7 @@ impl Executor { ) .await .unwrap(); + eth_nonce += U256::one(); eth_txs.push(res); } @@ -428,6 +429,19 @@ impl Executor { } } + let balance = self + .pool + .master_wallet + .get_balance(BlockNumber::Latest, self.l2_main_token) + .await?; + let necessary_balance = + U256::from(self.erc20_transfer_amount() * self.config.accounts_amount as u128); + + tracing::info!( + "Master account token balance on l2: {balance:?}, necessary balance \ + for initial transfers {necessary_balance:?}" + ); + // And then we will prepare an L2 transaction to send ERC20 token (for transfers and fees). let mut builder = master_wallet .start_transfer() @@ -441,10 +455,8 @@ impl Executor { self.l2_main_token, MIN_ALLOWANCE_FOR_PAYMASTER_ESTIMATE.into(), ); - let fee = builder.estimate_fee(Some(paymaster_params)).await?; builder = builder.fee(fee.clone()); - let paymaster_params = get_approval_based_paymaster_input( paymaster_address, self.l2_main_token, diff --git a/core/tests/loadnext/src/sdk/abi/update-abi.sh b/core/tests/loadnext/src/sdk/abi/update-abi.sh index 3fdcd4d58028..34b7e759c6cf 100755 --- a/core/tests/loadnext/src/sdk/abi/update-abi.sh +++ b/core/tests/loadnext/src/sdk/abi/update-abi.sh @@ -7,7 +7,7 @@ cat $ZKSYNC_HOME/contracts/l1-contracts/artifacts/contracts/bridgehub/IBridgehub cat $ZKSYNC_HOME/contracts/l1-contracts/artifacts/contracts/state-transition/IStateTransitionManager.sol/IStateTransitionManager.json | jq '{ abi: .abi}' > IStateTransitionManager.json cat $ZKSYNC_HOME/contracts/l1-contracts/artifacts/contracts/state-transition/chain-interfaces/IZkSyncHyperchain.sol/IZkSyncHyperchain.json | jq '{ abi: .abi}' > IZkSyncHyperchain.json # Default L1 bridge -cat $ZKSYNC_HOME/contracts/l1-contracts/artifacts/contracts/bridge/interfaces/IL1SharedBridge.sol/IL1SharedBridge.json | jq '{ abi: .abi}' > IL1SharedBridge.json +cat $ZKSYNC_HOME/contracts/l1-contracts/artifacts/contracts/bridge/interfaces/IL1AssetRouter.sol/IL1AssetRouter.json | jq '{ abi: .abi}' > IL1AssetRouter.json cat $ZKSYNC_HOME/contracts/l1-contracts/artifacts/contracts/bridge/interfaces/IL1ERC20Bridge.sol/IL1ERC20Bridge.json | jq '{ abi: .abi}' > IL1ERC20Bridge.json # Paymaster interface cat $ZKSYNC_HOME/contracts/l2-contracts/artifacts-zk/contracts/interfaces/IPaymasterFlow.sol/IPaymasterFlow.json | jq '{ abi: .abi}' > IPaymasterFlow.json diff --git a/core/tests/loadnext/src/sdk/ethereum/mod.rs b/core/tests/loadnext/src/sdk/ethereum/mod.rs index 4557c2c43200..bbb3514e2a0d 100644 --- a/core/tests/loadnext/src/sdk/ethereum/mod.rs +++ b/core/tests/loadnext/src/sdk/ethereum/mod.rs @@ -475,7 +475,7 @@ impl EthereumProvider { .as_u64() .ok_or(ClientError::Other)? } else { - 600000u64 + 800000u64 } }; diff --git a/core/tests/revert-test/tests/utils.ts b/core/tests/revert-test/tests/utils.ts index dac19f228ffc..8290598a1feb 100644 --- a/core/tests/revert-test/tests/utils.ts +++ b/core/tests/revert-test/tests/utils.ts @@ -144,6 +144,7 @@ async function runBlockReverter( --secrets-path=${configPaths['secrets.yaml']} --wallets-path=${configPaths['wallets.yaml']} --genesis-path=${configPaths['genesis.yaml']} + --gateway-chain-path=${configPaths['gateway_chain.yaml']} `; } @@ -312,14 +313,14 @@ export class NodeSpawner { public async spawnMainNode(enableExecute: boolean): Promise> { const env = this.env ?? process.env; - env.ETH_SENDER_SENDER_AGGREGATED_BLOCK_EXECUTE_DEADLINE = enableExecute ? '1' : '10000'; + env.ETH_SENDER_SENDER_L1_BATCH_MIN_AGE_BEFORE_EXECUTE_SECONDS = enableExecute ? '0' : '10000'; // Set full mode for the Merkle tree as it is required to get blocks committed. env.DATABASE_MERKLE_TREE_MODE = 'full'; const { fileConfig, pathToHome, options, logs } = this; if (fileConfig.loadFromFile) { - replaceL1BatchMinAgeBeforeExecuteSeconds(pathToHome, fileConfig, enableExecute ? 1 : 10000); + replaceL1BatchMinAgeBeforeExecuteSeconds(pathToHome, fileConfig, enableExecute ? 0 : 10000); } let components = 'api,tree,eth,state_keeper,commitment_generator,da_dispatcher,vm_runner_protective_reads'; diff --git a/core/tests/ts-integration/package.json b/core/tests/ts-integration/package.json index ee0fa9c99848..3362b9d6a89e 100644 --- a/core/tests/ts-integration/package.json +++ b/core/tests/ts-integration/package.json @@ -4,7 +4,7 @@ "license": "MIT", "private": true, "scripts": { - "test": "zk f jest --forceExit --verbose --testTimeout 120000", + "test": "zk f jest --forceExit --verbose --testTimeout 150000", "long-running-test": "zk f jest", "fee-test": "RUN_FEE_TEST=1 zk f jest -- fees.test.ts", "api-test": "zk f jest -- api/web3.test.ts api/debug.test.ts", @@ -22,6 +22,7 @@ "@types/node": "^18.19.15", "@types/node-fetch": "^2.5.7", "chalk": "^4.0.0", + "elliptic": "^6.5.5", "ethereumjs-abi": "^0.6.8", "ethers": "^6.7.1", "hardhat": "=2.22.2", @@ -32,8 +33,7 @@ "ts-jest": "^29.0.1", "ts-node": "^10.1.0", "typescript": "^4.3.5", - "zksync-ethers": "^6.9.0", - "elliptic": "^6.5.5", - "yaml": "^2.4.2" + "yaml": "^2.4.2", + "zksync-ethers": "https://github.com/zksync-sdk/zksync-ethers#sb-use-new-encoding-in-sdk" } } diff --git a/core/tests/ts-integration/src/context-owner.ts b/core/tests/ts-integration/src/context-owner.ts index 6b9c4d0541b2..57ca54da7b27 100644 --- a/core/tests/ts-integration/src/context-owner.ts +++ b/core/tests/ts-integration/src/context-owner.ts @@ -604,7 +604,7 @@ export class TestContextOwner { // Reset the reporter context. this.reporter = new Reporter(); try { - if (this.env.nodeMode == NodeMode.Main && isLocalHost(this.env.network.toLowerCase())) { + if (this.env.nodeMode == NodeMode.Main && isLocalHost(this.env.network)) { // Check that the VM execution hasn't diverged using the VM playground. The component and thus the main node // will crash on divergence, so we just need to make sure that the test doesn't exit before the VM playground // processes all batches on the node. diff --git a/core/tests/ts-integration/src/env.ts b/core/tests/ts-integration/src/env.ts index 58dc5b08a8d9..1c0725acc13a 100644 --- a/core/tests/ts-integration/src/env.ts +++ b/core/tests/ts-integration/src/env.ts @@ -168,6 +168,7 @@ async function loadTestEnvironmentFromFile(fileConfig: FileConfig): Promise { l2Address: baseTokenAddressL2 }, timestampAsserterAddress, - timestampAsserterMinTimeTillEndSec + timestampAsserterMinTimeTillEndSec, + l2WETHAddress: undefined }; } diff --git a/core/tests/ts-integration/src/helpers.ts b/core/tests/ts-integration/src/helpers.ts index d1d84d54a545..88819c669655 100644 --- a/core/tests/ts-integration/src/helpers.ts +++ b/core/tests/ts-integration/src/helpers.ts @@ -30,6 +30,10 @@ export function getContractSource(relativePath: string): string { return source; } +export function readContract(path: string, fileName: string) { + return JSON.parse(fs.readFileSync(`${path}/${fileName}.sol/${fileName}.json`, { encoding: 'utf-8' })); +} + /** * Performs a contract deployment * @@ -86,7 +90,7 @@ export async function waitForNewL1Batch(wallet: zksync.Wallet): Promise { return; } - const EIP1559_TX_TYPE = 2; const amount = 1; const erc20ABI = ['function transfer(address to, uint256 amount)']; const erc20contract = new ethers.Contract(l2Token, erc20ABI, alice); @@ -230,8 +229,9 @@ describe('web3 API compatibility tests', () => { expect(tx1.l1BatchNumber).toEqual(expect.anything()); // Can be anything except `null` or `undefined`. expect(tx1.l1BatchTxIndex).toEqual(expect.anything()); // Can be anything except `null` or `undefined`. expect(tx1.chainId).toEqual(chainId); - expect(tx1.type).toEqual(EIP1559_TX_TYPE); + expect(tx1.type).toEqual(EIP712_TX_TYPE); + const EIP1559_TX_TYPE = 2; expect(receipt!.l1BatchNumber).toEqual(expect.anything()); // Can be anything except `null` or `undefined`. expect(receipt!.l1BatchTxIndex).toEqual(expect.anything()); // Can be anything except `null` or `undefined`. expect(receipt!.logs[0].l1BatchNumber).toEqual(receipt!.l1BatchNumber); @@ -240,6 +240,7 @@ describe('web3 API compatibility tests', () => { expect(block.l1BatchTimestamp).toEqual(expect.anything()); expect(blockWithTransactions.l1BatchNumber).toEqual(receipt!.l1BatchNumber); expect(blockWithTransactions.l1BatchTimestamp).toEqual(expect.anything()); + for (const tx of blockWithTransactions.prefetchedTransactions) { expect(tx.l1BatchNumber).toEqual(expect.anything()); // Can be anything except `null` or `undefined`. expect(tx.l1BatchTxIndex).toEqual(expect.anything()); // Can be anything except `null` or `undefined`. diff --git a/core/tests/ts-integration/tests/base-token.test.ts b/core/tests/ts-integration/tests/base-token.test.ts index 432ce70ae17f..a22014751035 100644 --- a/core/tests/ts-integration/tests/base-token.test.ts +++ b/core/tests/ts-integration/tests/base-token.test.ts @@ -7,7 +7,7 @@ import { Token } from '../src/types'; import * as zksync from 'zksync-ethers'; import * as ethers from 'ethers'; -import { scaledGasPrice } from '../src/helpers'; +import { scaledGasPrice, waitForL2ToL1LogProof } from '../src/helpers'; const SECONDS = 2000; jest.setTimeout(100 * SECONDS); @@ -78,7 +78,7 @@ describe('base ERC20 contract checks', () => { // TODO: should all the following tests use strict equality? const finalEthBalance = await alice.getBalanceL1(); - expect(initialEthBalance).toBeGreaterThan(finalEthBalance + fee); // Fee should be taken from the ETH balance on L1. + expect(initialEthBalance).toBeGreaterThanOrEqual(finalEthBalance + fee); // Fee should be taken from the ETH balance on L1. const finalL1Balance = await alice.getBalanceL1(baseTokenDetails.l1Address); expect(initialL1Balance).toBeGreaterThanOrEqual(finalL1Balance + amount); @@ -167,7 +167,8 @@ describe('base ERC20 contract checks', () => { const withdrawalPromise = alice.withdraw({ token: baseTokenDetails.l2Address, amount }); await expect(withdrawalPromise).toBeAccepted([]); const withdrawalTx = await withdrawalPromise; - await withdrawalTx.waitFinalize(); + const l2Receipt = await withdrawalTx.wait(); + await waitForL2ToL1LogProof(alice, l2Receipt!.blockNumber, withdrawalTx.hash); await expect(alice.finalizeWithdrawal(withdrawalTx.hash)).toBeAccepted([]); const receipt = await alice._providerL2().getTransactionReceipt(withdrawalTx.hash); diff --git a/core/tests/ts-integration/tests/contracts.test.ts b/core/tests/ts-integration/tests/contracts.test.ts index de1c632ab9cc..aa9dbe6e1a89 100644 --- a/core/tests/ts-integration/tests/contracts.test.ts +++ b/core/tests/ts-integration/tests/contracts.test.ts @@ -7,7 +7,7 @@ */ import { TestMaster } from '../src'; -import { deployContract, getTestContract, waitForNewL1Batch } from '../src/helpers'; +import { deployContract, getTestContract, scaledGasPrice, waitForNewL1Batch } from '../src/helpers'; import { shouldOnlyTakeFee } from '../src/modifiers/balance-checker'; import * as ethers from 'ethers'; @@ -99,22 +99,24 @@ describe('Smart contract behavior checks', () => { return; } + const gasPrice = await scaledGasPrice(alice); const infiniteLoop = await deployContract(alice, contracts.infinite, []); // Test eth_call first // TODO: provide a proper error for transactions that consume too much gas. // await expect(infiniteLoop.callStatic.infiniteLoop()).toBeRejected('cannot estimate transaction: out of gas'); // ...and then an actual transaction - await expect(infiniteLoop.infiniteLoop({ gasLimit: 1_000_000 })).toBeReverted([]); + await expect(infiniteLoop.infiniteLoop({ gasLimit: 1_000_000, gasPrice })).toBeReverted([]); }); test('Should test reverting storage logs', async () => { // In this test we check that if transaction reverts, it rolls back the storage slots. const prevValue = await counterContract.get(); + const gasPrice = await scaledGasPrice(alice); - // We manually provide a constant, since otherwise the exception would be thrown - // while estimating gas - await expect(counterContract.incrementWithRevert(5, true, { gasLimit: 5000000 })).toBeReverted([]); + // We manually provide a gas limit and gas price, since otherwise the exception would be thrown + // while querying zks_estimateFee. + await expect(counterContract.incrementWithRevert(5, true, { gasLimit: 5000000, gasPrice })).toBeReverted(); // The tx has been reverted, so the value Should not have been changed: const newValue = await counterContract.get(); diff --git a/core/tests/ts-integration/tests/erc20.test.ts b/core/tests/ts-integration/tests/erc20.test.ts index 9173989ea98b..a0345fb71ab1 100644 --- a/core/tests/ts-integration/tests/erc20.test.ts +++ b/core/tests/ts-integration/tests/erc20.test.ts @@ -8,10 +8,10 @@ import { shouldChangeTokenBalances, shouldOnlyTakeFee } from '../src/modifiers/b import * as zksync from 'zksync-ethers'; import * as ethers from 'ethers'; -import { scaledGasPrice, waitUntilBlockFinalized } from '../src/helpers'; +import { scaledGasPrice, waitForL2ToL1LogProof } from '../src/helpers'; import { L2_DEFAULT_ETH_PER_ACCOUNT } from '../src/context-owner'; -describe('ERC20 contract checks', () => { +describe('L1 ERC20 contract checks', () => { let testMaster: TestMaster; let alice: zksync.Wallet; let bob: zksync.Wallet; @@ -96,6 +96,7 @@ describe('ERC20 contract checks', () => { test('Incorrect transfer should revert', async () => { const value = ethers.parseEther('1000000.0'); + const gasPrice = await scaledGasPrice(alice); // Since gas estimation is expected to fail, we request gas limit for similar non-failing tx. const gasLimit = await aliceErc20.transfer.estimateGas(bob.address, 1); @@ -109,12 +110,16 @@ describe('ERC20 contract checks', () => { const feeTaken = await shouldOnlyTakeFee(alice); // Send transfer, it should revert due to lack of balance. - await expect(aliceErc20.transfer(bob.address, value, { gasLimit })).toBeReverted([noBalanceChange, feeTaken]); + await expect(aliceErc20.transfer(bob.address, value, { gasLimit, gasPrice })).toBeReverted([ + noBalanceChange, + feeTaken + ]); }); test('Transfer to zero address should revert', async () => { const zeroAddress = ethers.ZeroAddress; const value = 200n; + const gasPrice = await scaledGasPrice(alice); // Since gas estimation is expected to fail, we request gas limit for similar non-failing tx. const gasLimit = await aliceErc20.transfer.estimateGas(bob.address, 1); @@ -127,7 +132,10 @@ describe('ERC20 contract checks', () => { const feeTaken = await shouldOnlyTakeFee(alice); // Send transfer, it should revert because transfers to zero address are not allowed. - await expect(aliceErc20.transfer(zeroAddress, value, { gasLimit })).toBeReverted([noBalanceChange, feeTaken]); + await expect(aliceErc20.transfer(zeroAddress, value, { gasLimit, gasPrice })).toBeReverted([ + noBalanceChange, + feeTaken + ]); }); test('Approve and transferFrom should work', async () => { @@ -166,7 +174,8 @@ describe('ERC20 contract checks', () => { }); await expect(withdrawalPromise).toBeAccepted([l2BalanceChange, feeCheck]); const withdrawalTx = await withdrawalPromise; - await withdrawalTx.waitFinalize(); + const l2TxReceipt = await alice.provider.getTransactionReceipt(withdrawalTx.hash); + await waitForL2ToL1LogProof(alice, l2TxReceipt!.blockNumber, withdrawalTx.hash); // Note: For L1 we should use L1 token address. const l1BalanceChange = await shouldChangeTokenBalances( @@ -176,6 +185,7 @@ describe('ERC20 contract checks', () => { l1: true } ); + await expect(alice.finalizeWithdrawal(withdrawalTx.hash)).toBeAccepted([l1BalanceChange]); }); @@ -206,7 +216,7 @@ describe('ERC20 contract checks', () => { // It throws once it gets status == 0 in the receipt and doesn't wait for the finalization. const l2Hash = zksync.utils.getL2HashFromPriorityOp(l1Receipt, await alice.provider.getMainContractAddress()); const l2TxReceipt = await alice.provider.getTransactionReceipt(l2Hash); - await waitUntilBlockFinalized(alice, l2TxReceipt!.blockNumber); + await waitForL2ToL1LogProof(alice, l2TxReceipt!.blockNumber, l2Hash); // Claim failed deposit. await expect(alice.claimFailedDeposit(l2Hash)).toBeAccepted(); await expect(alice.getBalanceL1(tokenDetails.l1Address)).resolves.toEqual(initialBalance); diff --git a/core/tests/ts-integration/tests/ether.test.ts b/core/tests/ts-integration/tests/ether.test.ts index b3f4b6ee14a9..099cf2de8c68 100644 --- a/core/tests/ts-integration/tests/ether.test.ts +++ b/core/tests/ts-integration/tests/ether.test.ts @@ -11,7 +11,7 @@ import { import { checkReceipt } from '../src/modifiers/receipt-check'; import * as zksync from 'zksync-ethers'; -import { scaledGasPrice } from '../src/helpers'; +import { scaledGasPrice, waitForL2ToL1LogProof } from '../src/helpers'; import { ethers } from 'ethers'; describe('ETH token checks', () => { @@ -59,10 +59,9 @@ describe('ETH token checks', () => { const gasPerPubdataByte = zksync.utils.REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT; - const l2GasLimit = await zksync.utils.estimateDefaultBridgeDepositL2Gas( + const l2GasLimit = await alice.provider.estimateDefaultBridgeDepositL2Gas( alice.providerL1!, - alice.provider, - zksync.utils.ETH_ADDRESS, + zksync.utils.ETH_ADDRESS_IN_CONTRACTS, amount, alice.address, alice.address, @@ -203,7 +202,10 @@ describe('ETH token checks', () => { const EIP_1559_TX_TYPE = 0x02; const value = 200n; - await expect(alice.sendTransaction({ type: EIP_2930_TX_TYPE, to: bob.address, value })).toBeRejected( + // SDK sets maxFeePerGas to the type 1 transactions, causing issues on the SDK level + const gasPrice = await scaledGasPrice(alice); + + await expect(alice.sendTransaction({ type: EIP_2930_TX_TYPE, to: bob.address, value, gasPrice })).toBeRejected( 'access lists are not supported' ); @@ -258,7 +260,8 @@ describe('ETH token checks', () => { }); await expect(withdrawalPromise).toBeAccepted([l2ethBalanceChange]); const withdrawalTx = await withdrawalPromise; - await withdrawalTx.waitFinalize(); + const l2TxReceipt = await alice.provider.getTransactionReceipt(withdrawalTx.hash); + await waitForL2ToL1LogProof(alice, l2TxReceipt!.blockNumber, withdrawalTx.hash); // TODO (SMA-1374): Enable L1 ETH checks as soon as they're supported. await expect(alice.finalizeWithdrawal(withdrawalTx.hash)).toBeAccepted(); diff --git a/core/tests/ts-integration/tests/fees.test.ts b/core/tests/ts-integration/tests/fees.test.ts index 628a17febd76..a111f5804852 100644 --- a/core/tests/ts-integration/tests/fees.test.ts +++ b/core/tests/ts-integration/tests/fees.test.ts @@ -190,14 +190,16 @@ testFees('Test fees', function () { await ( await alice.sendTransaction({ to: receiver, - value: BigInt(1) + value: BigInt(1), + type: 2 }) ).wait(); await ( await alice.sendTransaction({ data: aliceErc20.interface.encodeFunctionData('transfer', [receiver, 1n]), - to: tokenDetails.l2Address + to: tokenDetails.l2Address, + type: 2 }) ).wait(); @@ -221,22 +223,26 @@ testFees('Test fees', function () { [ { to: ethers.Wallet.createRandom().address, - value: 1n + value: 1n, + type: 2 }, { to: receiver, - value: 1n + value: 1n, + type: 2 }, { data: aliceErc20.interface.encodeFunctionData('transfer', [ ethers.Wallet.createRandom().address, 1n ]), - to: tokenDetails.l2Address + to: tokenDetails.l2Address, + type: 2 }, { data: aliceErc20.interface.encodeFunctionData('transfer', [receiver, 1n]), - to: tokenDetails.l2Address + to: tokenDetails.l2Address, + type: 2 } ], gasPrice, @@ -444,8 +450,9 @@ async function updateReport( oldReport: string ): Promise { const expectedL1Price = +ethers.formatEther(l1Receipt.gasUsed * newL1GasPrice); - - const estimatedL2GasPrice = await sender.provider.getGasPrice(); + // This is flaky without multiplying by 3. + const estimatedL2GasPrice = ethers.getBigInt(await sender.provider.send('eth_gasPrice', [])) * 3n; + transactionRequest.maxFeePerGas = estimatedL2GasPrice; const estimatedL2GasLimit = await sender.estimateGas(transactionRequest); const estimatedPrice = estimatedL2GasPrice * estimatedL2GasLimit; diff --git a/core/tests/ts-integration/tests/l1.test.ts b/core/tests/ts-integration/tests/l1.test.ts index 2d9b9fd78d69..2e3cddb29f9e 100644 --- a/core/tests/ts-integration/tests/l1.test.ts +++ b/core/tests/ts-integration/tests/l1.test.ts @@ -8,7 +8,14 @@ import { TestMaster } from '../src'; import * as zksync from 'zksync-ethers'; import * as ethers from 'ethers'; -import { bigIntMax, deployContract, getTestContract, scaledGasPrice, waitForNewL1Batch } from '../src/helpers'; +import { + bigIntMax, + deployContract, + getTestContract, + scaledGasPrice, + waitForL2ToL1LogProof, + waitForNewL1Batch +} from '../src/helpers'; import { L1_MESSENGER, L1_MESSENGER_ADDRESS, REQUIRED_L1_TO_L2_GAS_PER_PUBDATA_LIMIT } from 'zksync-ethers/build/utils'; const contracts = { @@ -135,6 +142,7 @@ describe('Tests for L1 behavior', () => { const l2ToL1LogIndex = receipt.l2ToL1Logs.findIndex( (log: zksync.types.L2ToL1Log) => log.sender == L1_MESSENGER_ADDRESS ); + await waitForL2ToL1LogProof(alice, receipt.blockNumber, tx.hash); const msgProof = await alice.provider.getLogProof(tx.hash, l2ToL1LogIndex); expect(msgProof).toBeTruthy(); diff --git a/core/tests/ts-integration/tests/l2-erc20.test.ts b/core/tests/ts-integration/tests/l2-erc20.test.ts new file mode 100644 index 000000000000..16b55b648993 --- /dev/null +++ b/core/tests/ts-integration/tests/l2-erc20.test.ts @@ -0,0 +1,262 @@ +/** + * This suite contains tests checking default ERC-20 contract behavior. + */ + +import { TestMaster } from '../src'; +import { Token } from '../src/types'; +import { shouldChangeTokenBalances, shouldOnlyTakeFee } from '../src/modifiers/balance-checker'; + +import * as zksync from 'zksync-ethers'; +import * as ethers from 'ethers'; +import { Provider, Wallet } from 'ethers'; +import { scaledGasPrice, deployContract, readContract, waitForL2ToL1LogProof } from '../src/helpers'; +import { encodeNTVAssetId } from 'zksync-ethers/build/utils'; + +describe('L2 native ERC20 contract checks', () => { + let testMaster: TestMaster; + let alice: zksync.Wallet; + let isETHBasedChain: boolean; + let baseTokenAddress: string; + let zkTokenAssetId: string; + let tokenDetails: Token; + let aliceErc20: zksync.Contract; + let l1NativeTokenVault: ethers.Contract; + let l1Wallet: Wallet; + let l2Wallet: Wallet; + let l1Provider: Provider; + let l2Provider: Provider; + let l2NativeTokenVault: zksync.Contract; + + beforeAll(async () => { + testMaster = TestMaster.getInstance(__filename); + alice = testMaster.mainAccount(); + const bridgeContracts = await alice.getL1BridgeContracts(); + const assetRouter = bridgeContracts.shared; + l2Provider = alice._providerL2(); + l1Provider = alice._providerL1(); + l2Wallet = new Wallet(alice.privateKey, l2Provider); + l1Wallet = new Wallet(alice.privateKey, l1Provider); + const L2_NATIVE_TOKEN_VAULT_ADDRESS = '0x0000000000000000000000000000000000010004'; + const ARTIFACTS_PATH = '../../../contracts/l1-contracts/out'; + const l2NtvInterface = readContract(`${ARTIFACTS_PATH}`, 'L2NativeTokenVault').abi; + l2NativeTokenVault = new zksync.Contract(L2_NATIVE_TOKEN_VAULT_ADDRESS, l2NtvInterface, l2Wallet); + const l1AssetRouterInterface = readContract(`${ARTIFACTS_PATH}`, 'L1AssetRouter').abi; + const l1NativeTokenVaultInterface = readContract(`${ARTIFACTS_PATH}`, 'L1NativeTokenVault').abi; + const l1AssetRouter = new ethers.Contract(await assetRouter.getAddress(), l1AssetRouterInterface, l1Wallet); + l1NativeTokenVault = new ethers.Contract( + await l1AssetRouter.nativeTokenVault(), + l1NativeTokenVaultInterface, + l1Wallet + ); + + // Get the information about base token address directly from the L2. + baseTokenAddress = await alice._providerL2().getBaseTokenContractAddress(); + isETHBasedChain = baseTokenAddress == zksync.utils.ETH_ADDRESS_IN_CONTRACTS; + + const ZkSyncERC20 = await readContract('../../../contracts/l1-contracts/zkout', 'TestnetERC20Token'); + + aliceErc20 = await deployContract(alice, ZkSyncERC20, ['ZKsync', 'ZK', 18]); + const l2TokenAddress = await aliceErc20.getAddress(); + tokenDetails = { + name: 'ZKsync', + symbol: 'ZK', + decimals: 18n, + l1Address: ethers.ZeroAddress, + l2Address: l2TokenAddress + }; + const mintTx = await aliceErc20.mint(alice.address, 1000n); + await mintTx.wait(); + + // We will test that the token can be withdrawn and work with without explicit registration + const l2ChainId = (await l2Provider.getNetwork()).chainId; + zkTokenAssetId = encodeNTVAssetId(l2ChainId, l2TokenAddress); + + const tokenApprovalTx = await aliceErc20.approve(L2_NATIVE_TOKEN_VAULT_ADDRESS, 100n); + await tokenApprovalTx.wait(); + }); + + test('check weth', async () => { + const weth = testMaster.environment().l2WETHAddress; + if (!weth) { + console.log('skip weth'); + return; + } + const wethabi = await readContract('../../../contracts/l2-contracts/zkout', 'L2WETH').abi; + const wethContract = new zksync.Contract(weth, wethabi, alice); + + const name = await wethContract.name(); + expect(name).toEqual('Wrapped ETH'); + + const addressFromNTV = await l2NativeTokenVault.WETH_TOKEN(); + expect(addressFromNTV.toLowerCase()).toEqual(weth.toLowerCase()); + + const wrapTx = await wethContract.deposit({ value: 1 }); + await expect(wrapTx).toBeAccepted(); + + const balance = await wethContract.balanceOf(alice.address); + expect(balance).toEqual(1n); + + const withdrawTx = alice.withdraw({ + token: weth, + amount: 1 + }); + let thrown = false; + try { + await withdrawTx; + } catch (err: any) { + thrown = true; + // TokenNotSupported(weth) + expect(err.toString()).toContain(ethers.concat(['0x06439c6b', ethers.zeroPadBytes('0x', 12), weth])); + } + expect(thrown).toBeTruthy(); + }); + + test('Token properties are correct', async () => { + await expect(aliceErc20.name()).resolves.toBe(tokenDetails.name); + await expect(aliceErc20.decimals()).resolves.toBe(tokenDetails.decimals); + await expect(aliceErc20.symbol()).resolves.toBe(tokenDetails.symbol); + await expect(aliceErc20.balanceOf(alice.address)).resolves.toBeGreaterThan(0n); // 'Alice should have non-zero balance' + }); + + test('Can perform a withdrawal', async () => { + if (testMaster.isFastMode()) { + return; + } + const amount = 10n; + + const l2BalanceChange = await shouldChangeTokenBalances(tokenDetails.l2Address, [ + { wallet: alice, change: -amount } + ]); + const feeCheck = await shouldOnlyTakeFee(alice); + const withdrawalPromise = alice.withdraw({ + token: tokenDetails.l2Address, + amount + }); + await expect(withdrawalPromise).toBeAccepted([l2BalanceChange, feeCheck]); + const withdrawalTx = await withdrawalPromise; + const l2TxReceipt = await alice.provider.getTransactionReceipt(withdrawalTx.hash); + await withdrawalTx.waitFinalize(); + await waitForL2ToL1LogProof(alice, l2TxReceipt!.blockNumber, withdrawalTx.hash); + + await expect(alice.finalizeWithdrawal(withdrawalTx.hash)).toBeAccepted(); + + tokenDetails.l1Address = await l1NativeTokenVault.tokenAddress(zkTokenAssetId); + const balanceAfterBridging = await alice.getBalanceL1(tokenDetails.l1Address); + expect(balanceAfterBridging).toEqual(10n); + }); + + test('Can perform a deposit', async () => { + const amount = 1n; // 1 wei is enough. + const gasPrice = await scaledGasPrice(alice); + + // Note: for L1 we should use L1 token address. + const l1BalanceChange = await shouldChangeTokenBalances( + tokenDetails.l1Address, + [{ wallet: alice, change: -amount }], + { + l1: true + } + ); + const l2BalanceChange = await shouldChangeTokenBalances(tokenDetails.l2Address, [ + { wallet: alice, change: amount } + ]); + const feeCheck = await shouldOnlyTakeFee(alice, true); + + await expect( + alice.deposit({ + token: tokenDetails.l1Address, + amount, + approveERC20: true, + approveBaseERC20: true, + approveOverrides: { + gasPrice + }, + overrides: { + gasPrice + } + }) + ).toBeAccepted([l1BalanceChange, l2BalanceChange, feeCheck]); + }); + + test('Should claim failed deposit', async () => { + if (testMaster.isFastMode()) { + return; + } + + const amount = 1n; + const initialBalance = await alice.getBalanceL1(tokenDetails.l1Address); + // Deposit to the zero address is forbidden and should fail with the current implementation. + const depositHandle = await alice.deposit({ + token: tokenDetails.l1Address, + to: ethers.ZeroAddress, + amount, + approveERC20: true, + approveBaseERC20: true, + l2GasLimit: 5_000_000 // Setting the limit manually to avoid estimation for L1->L2 transaction + }); + const l1Receipt = await depositHandle.waitL1Commit(); + + // L1 balance should change, but tx should fail in L2. + await expect(alice.getBalanceL1(tokenDetails.l1Address)).resolves.toEqual(initialBalance - amount); + await expect(depositHandle).toBeReverted(); + + // Wait for tx to be finalized. + // `waitFinalize` is not used because it doesn't work as expected for failed transactions. + // It throws once it gets status == 0 in the receipt and doesn't wait for the finalization. + const l2Hash = zksync.utils.getL2HashFromPriorityOp(l1Receipt, await alice.provider.getMainContractAddress()); + const l2TxReceipt = await alice.provider.getTransactionReceipt(l2Hash); + await waitForL2ToL1LogProof(alice, l2TxReceipt!.blockNumber, l2Hash); + + // Claim failed deposit. + await expect(alice.claimFailedDeposit(l2Hash)).toBeAccepted(); + await expect(alice.getBalanceL1(tokenDetails.l1Address)).resolves.toEqual(initialBalance); + }); + + test('Can perform a deposit with precalculated max value', async () => { + if (!isETHBasedChain) { + // approving whole base token balance + const baseTokenDetails = testMaster.environment().baseToken; + const baseTokenMaxAmount = await alice.getBalanceL1(baseTokenDetails.l1Address); + await (await alice.approveERC20(baseTokenDetails.l1Address, baseTokenMaxAmount)).wait(); + } + + // depositing the max amount: the whole balance of the token + const tokenDepositAmount = await alice.getBalanceL1(tokenDetails.l1Address); + + // approving the needed allowance for the deposit + await (await alice.approveERC20(tokenDetails.l1Address, tokenDepositAmount)).wait(); + + // fee of the deposit in ether + const depositFee = await alice.getFullRequiredDepositFee({ + token: tokenDetails.l1Address + }); + + // checking if alice has enough funds to pay the fee + const l1Fee = depositFee.l1GasLimit * (depositFee.maxFeePerGas! || depositFee.gasPrice!); + const l2Fee = depositFee.baseCost; + const aliceBalance = await alice.getBalanceL1(); + if (aliceBalance < l1Fee + l2Fee) { + throw new Error('Not enough balance to pay the fee'); + } + + // deposit handle with the precalculated max amount + const depositHandle = await alice.deposit({ + token: tokenDetails.l1Address, + amount: tokenDepositAmount, + l2GasLimit: depositFee.l2GasLimit, + approveBaseERC20: true, + approveERC20: true, + overrides: depositFee + }); + + // checking the l2 balance change + const l2TokenBalanceChange = await shouldChangeTokenBalances(tokenDetails.l2Address, [ + { wallet: alice, change: tokenDepositAmount } + ]); + await expect(depositHandle).toBeAccepted([l2TokenBalanceChange]); + }); + + afterAll(async () => { + await testMaster.deinitialize(); + }); +}); diff --git a/core/tests/ts-integration/tests/system.test.ts b/core/tests/ts-integration/tests/system.test.ts index 38b21c5839ae..fd833578e86c 100644 --- a/core/tests/ts-integration/tests/system.test.ts +++ b/core/tests/ts-integration/tests/system.test.ts @@ -11,7 +11,7 @@ import { L2_DEFAULT_ETH_PER_ACCOUNT } from '../src/context-owner'; import * as zksync from 'zksync-ethers'; import * as ethers from 'ethers'; -import { SYSTEM_CONTEXT_ADDRESS, getTestContract } from '../src/helpers'; +import { SYSTEM_CONTEXT_ADDRESS, getTestContract, waitForL2ToL1LogProof } from '../src/helpers'; import { DataAvailabityMode } from '../src/types'; import { BigNumberish } from 'ethers'; @@ -251,6 +251,9 @@ describe('System behavior checks', () => { testMaster.reporter.debug( `Obtained withdrawal receipt for Bob: blockNumber=${bobReceipt.blockNumber}, l1BatchNumber=${bobReceipt.l1BatchNumber}, status=${bobReceipt.status}` ); + + await waitForL2ToL1LogProof(alice, aliceReceipt.blockNumber, aliceReceipt.hash); + await waitForL2ToL1LogProof(bob, bobReceipt.blockNumber, bobReceipt.hash); await expect(alice.finalizeWithdrawal(aliceReceipt.hash)).toBeAccepted([aliceChange]); testMaster.reporter.debug('Finalized withdrawal for Alice'); await expect(alice.finalizeWithdrawal(bobReceipt.hash)).toBeAccepted([bobChange]); @@ -295,6 +298,9 @@ describe('System behavior checks', () => { testMaster.reporter.debug( `Obtained withdrawal receipt #2: blockNumber=${receipt2.blockNumber}, l1BatchNumber=${receipt2.l1BatchNumber}, status=${receipt2.status}` ); + + await waitForL2ToL1LogProof(alice, receipt1.blockNumber, receipt1.hash); + await waitForL2ToL1LogProof(alice, receipt2.blockNumber, receipt2.hash); await expect(alice.finalizeWithdrawal(receipt1.hash)).toBeAccepted([change1]); testMaster.reporter.debug('Finalized withdrawal #1'); await expect(alice.finalizeWithdrawal(receipt2.hash)).toBeAccepted([change2]); diff --git a/core/tests/upgrade-test/tests/upgrade.test.ts b/core/tests/upgrade-test/tests/upgrade.test.ts index b4b950028e1e..5a1902ec8671 100644 --- a/core/tests/upgrade-test/tests/upgrade.test.ts +++ b/core/tests/upgrade-test/tests/upgrade.test.ts @@ -24,13 +24,14 @@ async function logsPath(name: string): Promise { return await logsTestPath(fileConfig.chain, 'logs/upgrade/', name); } +const L2_BRIDGEHUB_ADDRESS = '0x0000000000000000000000000000000000010002'; const pathToHome = path.join(__dirname, '../../../..'); const fileConfig = shouldLoadConfigFromFile(); const contracts: Contracts = initContracts(pathToHome, fileConfig.loadFromFile); const ZK_CHAIN_INTERFACE = JSON.parse( - readFileSync(pathToHome + '/contracts/l1-contracts/out/IZkSyncHyperchain.sol/IZkSyncHyperchain.json').toString() + readFileSync(pathToHome + '/contracts/l1-contracts/out/IZKChain.sol/IZKChain.json').toString() ).abi; const depositAmount = ethers.parseEther('0.001'); @@ -70,6 +71,8 @@ describe('Upgrade test', function () { let slMainContract: ethers.Contract; let bootloaderHash: string; + let defaultAccountHash: string; + let bytecodeSupplier: string; let executeOperation: string; let forceDeployAddress: string; let forceDeployBytecode: string; @@ -118,6 +121,7 @@ describe('Upgrade test', function () { ethProviderAddress = secretsConfig.l1.l1_rpc_url; web3JsonRpc = generalConfig.api.web3_json_rpc.http_url; contractsL2DefaultUpgradeAddr = contractsConfig.l2.default_l2_upgrader; + bytecodeSupplier = contractsConfig.ecosystem_contracts.l1_bytecodes_supplier_addr; contractsPriorityTxMaxGasLimit = '72000000'; gatewayInfo = getGatewayInfo(pathToHome, fileConfig.chain); @@ -176,7 +180,7 @@ describe('Upgrade test', function () { const l1CtmContract = new ethers.Contract( contractsConfig.ecosystem_contracts.state_transition_proxy_addr, - contracts.stateTransitionManager, + contracts.chainTypeManager, tester.syncWallet.providerL1 ); ecosystemGovernance = await l1CtmContract.owner(); @@ -262,10 +266,11 @@ describe('Upgrade test', function () { ); bootloaderHash = ethers.hexlify(zksync.utils.hashBytecode(bootloaderCode)); + defaultAccountHash = ethers.hexlify(zksync.utils.hashBytecode(defaultAACode)); - await publishBytecode(tester.syncWallet, bootloaderCode); - await publishBytecode(tester.syncWallet, defaultAACode); - await publishBytecode(tester.syncWallet, forceDeployBytecode); + await publishBytecode(tester.ethWallet, bytecodeSupplier, bootloaderCode); + await publishBytecode(tester.ethWallet, bytecodeSupplier, defaultAACode); + await publishBytecode(tester.ethWallet, bytecodeSupplier, forceDeployBytecode); }); step('Schedule governance call', async () => { @@ -303,11 +308,14 @@ describe('Upgrade test', function () { reserved: [0, 0, 0, 0], data, signature: '0x', - factoryDeps: [ethers.hexlify(zksync.utils.hashBytecode(forceDeployBytecode))], + factoryDeps: [ + bootloaderHash, + defaultAccountHash, + ethers.hexlify(zksync.utils.hashBytecode(forceDeployBytecode)) + ], paymasterInput: '0x', reservedDynamic: '0x' }, - factoryDeps: [forceDeployBytecode], bootloaderHash, upgradeTimestamp: 0 }, @@ -315,6 +323,21 @@ describe('Upgrade test', function () { ); executeOperation = chainUpgradeCalldata; + const pauseMigrationCalldata = await pauseMigrationsCalldata( + alice._providerL1(), + alice._providerL2(), + gatewayInfo + ); + console.log('Scheduling pause migration'); + await sendGovernanceOperation(pauseMigrationCalldata.scheduleTransparentOperation, 0, null); + + console.log('Sending pause migration'); + await sendGovernanceOperation( + pauseMigrationCalldata.executeOperation, + pauseMigrationCalldata.executeOperationValue, + gatewayInfo ? gatewayInfo.gatewayProvider : null + ); + console.log('Sending scheduleTransparentOperation'); await sendGovernanceOperation(stmUpgradeData.scheduleTransparentOperation, 0, null); @@ -326,12 +349,18 @@ describe('Upgrade test', function () { ); console.log('Sending chain admin operation'); - await ( - await slAdminGovWallet.sendTransaction({ - to: await slChainAdminContract.getAddress(), - data: setTimestampCalldata - }) - ).wait(); + // Different chain admin impls are used depending on whether gateway is used. + if (gatewayInfo) { + // ChainAdmin.sol: `setUpgradeTimestamp` has onlySelf so we do multicall. + await sendChainAdminOperation({ + target: await slChainAdminContract.getAddress(), + data: setTimestampCalldata, + value: 0 + }); + } else { + // ChainAdminOwnable.sol: `setUpgradeTimestamp` has onlyOwner so we call it directly. + await chainAdminSetTimestamp(setTimestampCalldata); + } // Wait for server to process L1 event. await utils.sleep(2); @@ -436,6 +465,17 @@ describe('Upgrade test', function () { console.log('Transaction complete!'); } + async function chainAdminSetTimestamp(data: string) { + const transaction = await slAdminGovWallet.sendTransaction({ + to: await slChainAdminContract.getAddress(), + data, + type: 0 + }); + console.log(`Sent chain admin operation, tx_hash=${transaction.hash}, nonce=${transaction.nonce}`); + await transaction.wait(); + console.log(`Chain admin operation succeeded, tx_hash=${transaction.hash}`); + } + async function sendChainAdminOperation(call: Call) { const executeMulticallData = slChainAdminContract.interface.encodeFunctionData('multicall', [[call], true]); @@ -483,18 +523,18 @@ function readCode(newPath: string, legacyPath: string): string { } } -async function publishBytecode(wallet: zksync.Wallet, bytecode: string) { - const txHandle = await wallet.requestExecute({ - contractAddress: ethers.ZeroAddress, - calldata: '0x', - l2GasLimit: 20000000, - factoryDeps: [bytecode], - overrides: { - gasLimit: 3000000 - } - }); - await txHandle.wait(); - await waitForNewL1Batch(wallet); +async function publishBytecode(wallet: ethers.Wallet, bytecodeSupplierAddr: string, bytecode: string) { + const hash = zksync.utils.hashBytecode(bytecode); + const abi = [ + 'function publishBytecode(bytes calldata _bytecode) public', + 'function publishingBlock(bytes32 _hash) public view returns (uint256)' + ]; + + const contract = new ethers.Contract(bytecodeSupplierAddr, abi, wallet); + const block = await contract.publishingBlock(hash); + if (block == BigInt(0)) { + await (await contract.publishBytecode(bytecode)).wait(); + } } async function checkedRandomTransfer(sender: zksync.Wallet, amount: bigint): Promise { @@ -578,7 +618,6 @@ async function prepareUpgradeCalldata( paymasterInput: BytesLike; reservedDynamic: BytesLike; }; - factoryDeps: BytesLike[]; bootloaderHash?: BytesLike; defaultAAHash?: BytesLike; verifier?: string; @@ -604,7 +643,7 @@ async function prepareUpgradeCalldata( const zksyncAddress = await l2Provider.getMainContractAddress(); settlementLayerDiamondProxy = new ethers.Contract(zksyncAddress, ZK_CHAIN_INTERFACE, l1Provider); } - const settlementLayerCTMAddress = await settlementLayerDiamondProxy.getStateTransitionManager(); + const settlementLayerCTMAddress = await settlementLayerDiamondProxy.getChainTypeManager(); const oldProtocolVersion = Number(await settlementLayerDiamondProxy.getProtocolVersion()); const newProtocolVersion = addToProtocolVersion(oldProtocolVersion, 1, 1); @@ -613,7 +652,6 @@ async function prepareUpgradeCalldata( const upgradeInitData = contracts.l1DefaultUpgradeAbi.encodeFunctionData('upgrade', [ [ params.l2ProtocolUpgradeTx, - params.factoryDeps, params.bootloaderHash ?? ethers.ZeroHash, params.defaultAAHash ?? ethers.ZeroHash, params.verifier ?? ethers.ZeroAddress, @@ -633,7 +671,7 @@ async function prepareUpgradeCalldata( }; // Prepare calldata for upgrading STM - const stmUpgradeCalldata = contracts.stateTransitionManager.encodeFunctionData('setNewVersionUpgrade', [ + const stmUpgradeCalldata = contracts.chainTypeManager.encodeFunctionData('setNewVersionUpgrade', [ upgradeParam, oldProtocolVersion, // The protocol version will not have any deadline in this upgrade @@ -670,6 +708,25 @@ async function prepareUpgradeCalldata( }; } +async function pauseMigrationsCalldata( + l1Provider: ethers.Provider, + l2Provider: zksync.Provider, + gatewayInfo: GatewayInfo | null +) { + const l1BridgehubAddr = await l2Provider.getBridgehubContractAddress(); + const to = gatewayInfo ? L2_BRIDGEHUB_ADDRESS : l1BridgehubAddr; + + const iface = new ethers.Interface(['function pauseMigration() external']); + + return prepareGovernanceCalldata( + to, + iface.encodeFunctionData('pauseMigration', []), + l1BridgehubAddr, + l1Provider, + gatewayInfo + ); +} + interface UpgradeCalldata { scheduleTransparentOperation: string; executeOperation: string; diff --git a/core/tests/upgrade-test/tests/utils.ts b/core/tests/upgrade-test/tests/utils.ts index 7ea7efb88cb8..9d29bcda4045 100644 --- a/core/tests/upgrade-test/tests/utils.ts +++ b/core/tests/upgrade-test/tests/utils.ts @@ -40,7 +40,7 @@ export interface Contracts { l2ForceDeployUpgraderAbi: any; complexUpgraderAbi: any; counterBytecode: any; - stateTransitionManager: any; + chainTypeManager: any; } export function initContracts(pathToHome: string, zkStack: boolean): Contracts { @@ -68,10 +68,8 @@ export function initContracts(pathToHome: string, zkStack: boolean): Contracts { counterBytecode: require( `${pathToHome}/core/tests/ts-integration/artifacts-zk/contracts/counter/counter.sol/Counter.json` ).deployedBytecode, - stateTransitionManager: new ethers.Interface( - require( - `${CONTRACTS_FOLDER}/l1-contracts/out/StateTransitionManager.sol/StateTransitionManager.json` - ).abi + chainTypeManager: new ethers.Interface( + require(`${CONTRACTS_FOLDER}/l1-contracts/out/ChainTypeManager.sol/ChainTypeManager.json`).abi ) }; } else { @@ -99,10 +97,8 @@ export function initContracts(pathToHome: string, zkStack: boolean): Contracts { ), counterBytecode: require(`${pathToHome}/core/tests/ts-integration/zkout/counter.sol/Counter.json`) .deployedBytecode, - stateTransitionManager: new ethers.Interface( - require( - `${L1_CONTRACTS_FOLDER}/state-transition/StateTransitionManager.sol/StateTransitionManager.json` - ).abi + chainTypeManager: new ethers.Interface( + require(`${L1_CONTRACTS_FOLDER}/state-transition/ChainTypeManager.sol/ChainTypeManager.json`).abi ) }; } diff --git a/docs/src/specs/l1_smart_contracts.md b/docs/src/specs/l1_smart_contracts.md index 65c408714ba3..23fede090124 100644 --- a/docs/src/specs/l1_smart_contracts.md +++ b/docs/src/specs/l1_smart_contracts.md @@ -184,7 +184,7 @@ fee-on-transfer tokens or other custom logic for handling user balances. The owner of the L1ERC20Bridge is the Governance contract. -### L1SharedBridge +### L1AssetRouter The main bridge implementation handles transfers Ether, ERC20 tokens and of WETH tokens between the two domains. It is designed to streamline and enhance the user experience for bridging WETH tokens by minimizing the number of transactions diff --git a/etc/env/base/chain.toml b/etc/env/base/chain.toml index 6d1fdae53cee..7b632c3ae3a4 100644 --- a/etc/env/base/chain.toml +++ b/etc/env/base/chain.toml @@ -90,8 +90,8 @@ fee_model_version = "V2" validation_computational_gas_limit = 300000 save_call_traces = true -bootloader_hash = "0x010008c3be57ae5800e077b6c2056d9d75ad1a7b4f0ce583407961cc6fe0b678" -default_aa_hash = "0x0100055dba11508480be023137563caec69debc85f826cb3a4b68246a7cabe30" +bootloader_hash = "0x010008c753336bc8d1ddca235602b9f31d346412b2d463cd342899f7bfb73baf" +default_aa_hash = "0x0100055d760f11a3d737e7fd1816e600a4cd874a9f17f7a225d1f1c537c51a1e" protective_reads_persistence_enabled = false diff --git a/etc/env/base/contracts.toml b/etc/env/base/contracts.toml index ef52ed4c711b..1cb22440e33c 100644 --- a/etc/env/base/contracts.toml +++ b/etc/env/base/contracts.toml @@ -15,7 +15,6 @@ DIAMOND_PROXY_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" L1_MULTICALL3_ADDR = "0xcA11bde05977b3631167028862bE2a173976CA11" L1_ERC20_BRIDGE_PROXY_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" L1_ERC20_BRIDGE_IMPL_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" -L2_ERC20_BRIDGE_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" L2_TESTNET_PAYMASTER_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" L1_ALLOW_LIST_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" CREATE2_FACTORY_ADDR = "0xce0042B868300000d44A59004Da54A005ffdcf9f" @@ -26,13 +25,10 @@ RECURSION_NODE_LEVEL_VK_HASH = "0x1186ec268d49f1905f8d9c1e9d39fc33e98c74f91d91a2 RECURSION_LEAF_LEVEL_VK_HASH = "0x101e08b00193e529145ee09823378ef51a3bc8966504064f1f6ba3f1ba863210" RECURSION_CIRCUITS_SET_VKS_HASH = "0x18c1639094f58177409186e8c48d9f577c9410901d2f1d486b3e7d6cf553ae4c" GENESIS_TX_HASH = "0xb99ebfea46cbe05a21cd80fe5597d97b204befc52a16303f579c607dc1ac2e2e" -GENESIS_ROOT = "0x7275936e5a0063b159d5d22734931fea07871e8d57e564d61ef56e4a6ee23e5c" -GENESIS_BATCH_COMMITMENT = "0xf5f9a5abe62e8a6e0cb2d34d27435c3e5a8fbd7e2e54ca1d108fc58cb86c708a" PRIORITY_TX_MAX_GAS_LIMIT = 72000000 DEPLOY_L2_BRIDGE_COUNTERPART_GAS_LIMIT = 10000000 -GENESIS_ROLLUP_LEAF_INDEX = "54" -GENESIS_PROTOCOL_VERSION = "25" -GENESIS_PROTOCOL_SEMANTIC_VERSION = "0.25.0" +GENESIS_PROTOCOL_VERSION = "26" +GENESIS_PROTOCOL_SEMANTIC_VERSION = "0.26.0" L1_WETH_BRIDGE_IMPL_ADDR = "0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9" L1_WETH_BRIDGE_PROXY_ADDR = "0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9" L1_WETH_TOKEN_ADDR = "0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9" @@ -41,6 +37,19 @@ L2_WETH_TOKEN_IMPL_ADDR = "0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9" L2_WETH_TOKEN_PROXY_ADDR = "0x5E6D086F5eC079ADFF4FB3774CDf3e8D6a34F7E9" BLOB_VERSIONED_HASH_RETRIEVER_ADDR = "0x0000000000000000000000000000000000000000" +GENESIS_ROOT = "0x09e68951458b18c24ae5f4100160b53c4888c9b3c3c1859cc674bc02236675ad" +GENESIS_BATCH_COMMITMENT = "0x7238eab6a0e9f5bb84421feae6b6b9ae80816d490c875d29ff3ded375a3e078f" +GENESIS_ROLLUP_LEAF_INDEX = "64" + +# Ecosystem-wide params +L1_ROLLUP_DA_VALIDATOR = "0x0000000000000000000000000000000000000000" +L1_VALIDIUM_DA_VALIDATOR = "0x0000000000000000000000000000000000000000" + +# Chain-specific params +L1_DA_VALIDATOR_ADDR = "0x0000000000000000000000000000000000000000" +L2_DA_VALIDATOR_ADDR = "0x0000000000000000000000000000000000000000" +L1_RELAYED_SL_DA_VALIDATOR = "0x0000000000000000000000000000000000000000" + L1_SHARED_BRIDGE_IMPL_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" # These are currently not used, but will be used once the shared bridge is up BRIDGEHUB_PROXY_ADDR = "0x0000000000000000000000000000000000000000" @@ -48,13 +57,29 @@ BRIDGEHUB_IMPL_ADDR = "0x0000000000000000000000000000000000000000" STATE_TRANSITION_PROXY_ADDR = "0x0000000000000000000000000000000000000000" STATE_TRANSITION_IMPL_ADDR = "0x0000000000000000000000000000000000000000" TRANSPARENT_PROXY_ADMIN_ADDR = "0x0000000000000000000000000000000000000000" +L2_PROXY_ADMIN_ADDR = "0x0000000000000000000000000000000000000000" BASE_TOKEN_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" BASE_TOKEN_BRIDGE_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" GENESIS_UPGRADE_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" -MAX_NUMBER_OF_HYPERCHAINS = 100 +MAX_NUMBER_OF_ZK_CHAINS = 100 L1_SHARED_BRIDGE_PROXY_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" -L2_SHARED_BRIDGE_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" -L2_SHARED_BRIDGE_IMPL_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" +L1_NATIVE_TOKEN_VAULT_IMPL_ADDR ="0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" +L1_NATIVE_TOKEN_VAULT_PROXY_ADDR ="0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" +L2_NATIVE_TOKEN_VAULT_IMPL_ADDR = "0x0000000000000000000000000000000000010004" +L2_NATIVE_TOKEN_VAULT_PROXY_ADDR = "0x0000000000000000000000000000000000010004" +L2_SHARED_BRIDGE_IMPL_ADDR = "0x0000000000000000000000000000000000010003" +L2_SHARED_BRIDGE_ADDR = "0x0000000000000000000000000000000000010003" +L2_ERC20_BRIDGE_ADDR = "0x0000000000000000000000000000000000010003" +CTM_DEPLOYMENT_TRACKER_IMPL_ADDR ="0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" +CTM_DEPLOYMENT_TRACKER_PROXY_ADDR ="0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" +MESSAGE_ROOT_IMPL_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" +MESSAGE_ROOT_PROXY_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" +L1_NULLIFIER_IMPL_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" +L1_NULLIFIER_PROXY_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" +L1_BRIDGED_STANDARD_ERC20_IMPL_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" +L1_BRIDGED_TOKEN_BEACON_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" +L2_LEGACY_SHARED_BRIDGE_IMPL_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" +L2_LEGACY_SHARED_BRIDGE_ADDR = "0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" FRI_RECURSION_LEAF_LEVEL_VK_HASH = "0xf9664f4324c1400fa5c3822d667f30e873f53f1b8033180cd15fe41c1e2355c6" FRI_RECURSION_NODE_LEVEL_VK_HASH = "0xf520cd5b37e74e19fdb369c8d676a04dce8a19457497ac6686d2bb95d94109c8" FRI_RECURSION_SCHEDULER_LEVEL_VK_HASH = "0x14f97b81e54b35fe673d8708cc1a19e1ea5b5e348e12d31e39824ed4f42bbca2" @@ -64,6 +89,9 @@ SHARED_BRIDGE_UPGRADE_STORAGE_SWITCH = 0 ERA_CHAIN_ID = 9 ERA_DIAMOND_PROXY_ADDR = "0x0000000000000000000000000000000000000000" CHAIN_ADMIN_ADDR = "0x0000000000000000000000000000000000000000" +CTM_ASSET_INFO = "0xf9664f4324c1400fa5c3822d667f30e873f53f1b8033180cd15fe41c1e2355c6" + +L1_CHAIN_ID = 9 [contracts.test] dummy_verifier = true easy_priority_mode = false diff --git a/etc/env/file_based/general.yaml b/etc/env/file_based/general.yaml index 31dd1a0ed742..f15b63a757f7 100644 --- a/etc/env/file_based/general.yaml +++ b/etc/env/file_based/general.yaml @@ -41,7 +41,7 @@ api: estimate_gas_scale_factor: 1.3 estimate_gas_acceptable_overestimation: 5000 max_tx_size: 1000000 - api_namespaces: [ en,eth,net,web3,zks,pubsub,debug ] + api_namespaces: [ en,eth,net,web3,zks,pubsub,debug,unstable ] state_keeper: transaction_slots: 8192 max_allowed_l2_tx_gas_limit: 15000000000 diff --git a/etc/env/file_based/genesis.yaml b/etc/env/file_based/genesis.yaml index 9f94dd0c04b6..0cd9959baeaf 100644 --- a/etc/env/file_based/genesis.yaml +++ b/etc/env/file_based/genesis.yaml @@ -1,9 +1,9 @@ -genesis_root: 0x9b30c35100835c0d811c9d385cc9804816dbceb4461b8fe4cbb8d0d5ecdacdec -genesis_rollup_leaf_index: 54 -genesis_batch_commitment: 0x043d432c1b668e54ada198d683516109e45e4f7f81f216ff4c4f469117732e50 -genesis_protocol_version: 25 -default_aa_hash: 0x01000523eadd3061f8e701acda503defb7ac3734ae3371e4daf7494651d8b523 -bootloader_hash: 0x010008e15394cd83a8d463d61e00b4361afbc27c932b07a9d2100861b7d05e78 +genesis_root: 0xd8c9be7efb705e7dcf529c14fce7048ea99dea9eab6a6b4e5f8de1ebf4f2ebf2 +genesis_rollup_leaf_index: 68 +genesis_batch_commitment: 0xf6e873e8894b90f157511a133d941fb6f0892f83147e3d0d2cafa71af8c838e5 +genesis_protocol_version: 26 +default_aa_hash: 0x010004dbf8be36c421254d005352f8245146906919be0099e8a50d0e78df85e0 +bootloader_hash: 0x0100088580465d88420e6369230ee94a32ff356dbcdd407a4be49fc8009b2a81 l1_chain_id: 9 l2_chain_id: 270 fee_account: '0x0000000000000000000000000000000000000001' @@ -11,7 +11,7 @@ prover: fflonk_snark_wrapper_vk_hash: 0x560b19cfd6bcf1049c6409c18d81db288ab7639db080ed3b48df17ddfbcc4666 dummy_verifier: true snark_wrapper_vk_hash: 0x14f97b81e54b35fe673d8708cc1a19e1ea5b5e348e12d31e39824ed4f42bbca2 -genesis_protocol_semantic_version: 0.25.0 +genesis_protocol_semantic_version: 0.26.0 l1_batch_commit_data_generator_mode: Rollup # TODO: uncomment once EVM emulator is present in the `contracts` submodule # evm_emulator_hash: 0x01000e53aa35d9d19fa99341c2e2901cf93b3668f01569dd5c6ca409c7696b91 diff --git a/etc/multivm_bootloaders/vm_gateway/commit b/etc/multivm_bootloaders/vm_gateway/commit index a3547f577034..b6352645c93a 100644 --- a/etc/multivm_bootloaders/vm_gateway/commit +++ b/etc/multivm_bootloaders/vm_gateway/commit @@ -1 +1 @@ -a8bf0ca28d43899882a2e123e2fdf1379f0fd656 +16dedf6d77695ce00f81fce35a3066381b97fca1 diff --git a/etc/multivm_bootloaders/vm_gateway/fee_estimate.yul/fee_estimate.yul.zbin b/etc/multivm_bootloaders/vm_gateway/fee_estimate.yul/fee_estimate.yul.zbin index fb6017f69cf03b963d490070a1d33555531e5d30..9f8ed5b9d67646a747fc7705e4d29690174ac7a5 100644 GIT binary patch literal 74016 zcmeHw3!Gh5b@x8^G56e=Br}=JBQs<&XG|zeEt(*q4jR3eBs@xlU;_9ob4hN(Kr%DT zOhO`3hKSHmY|&bwfOP^bM$ zxtZLk)%pFBbN6fQz1Lpvz0W=>qv$WA`t(z&;cQ!GT&coC*;URh%0d3>>;?GWQJ!iv;~3DAQR=tHO8GDs_iaZPzGuE!CgldX(SB`ETI-9+YI! zj{1>N9L#Zhj6NLC4K^Yu7l<;<19SnsA+<8&}qxGA^*RfHs^j!vjtNgYA^O* zhXJ&zZq#tHoAH0Qqp}wR2&D;c!)V9&RBg^%DOd2Q#u$$@f5uzke`vgmpXmp_08bUZ z3-~hr_S_oO>O}cqsQi0Yc^>6Mq4MuX%WJ~#st>;Ec4?kU^`M_Y!dJ!vy{N+Xvu4~& zc-(|ftV`hsz#EnSQ1FMAv@S~ZXFcankpFPbb8bc2aZ1j|lv;eIUQZ{Fc_Cjlx!LpR zxuYrfLVD(R=T4<3*7h9yf1%JNwR5PyA7MUaE|-3Cyxv3yL~peIvhM5Wxb8nbI_tg| zV`Vz+A$;@rh@O>NDtJ_{(dEPs=cpF0LF09twu3iP${9HLRh=HWigPgfZsRcz{UUJ4k?>V#+d5VQ6 zxNbq@QOg&#JROEqVZO*@bh$@zbpa&NwQJFOn#&j9mzM5Q5As*)9dG1vtA>+pWjLVi@{1TwQElRS;8}v>D2CJI*H_lKU#;sMZX!64 zz^aMxhDQ^@Q$0vxAqPpR+e_31}D;K{wOI85?K zc}+K~)36>peLmZxdLeK2fGi14)}1Cf?zTD>R>e!58K~F7q$g^WRbD{72H~U)VwAPP?A}{c7CI ze}LDiAo%h8b>w*j^9PC0{5{4`7iJJk2gsN8-)H78>+dcUd@uMjjqkiWOzWvUtS8oe z!QKA4@8x;_jmEnw?&WzI{Fiuqm_;J~Z(;oZ$n@_DJ@hwEGyU}!ritzx=qg$V_r4a5 zfA?OC{%(!pzj;RJuX#r34{4^q<{6>CmfHpYEiIb<-W8=k_g2RL=|29aqxg3ZaC^cB z#)rRKX&wRoi-P~*;>{>ujPj!DqiUModyp^mcYx>rcV_<0uJB*WjLg3{!}EWYng4>D zh5nikTJyg#I{%h?W&SPq%KVY$`M2CF^Ive2%zwcGJ^znb^S_bjztx}rOmzOu_sIN# zUzz`nG!Lv$Gv#YM(2QM~ce6|RZU^vn8=jqt@wost%AG{dp1PUk_qR2@^|*{b)D(P- zqy9}8%WkCxs6N3(e-3nch0-uDx%>}wI-}Fml5W%UfHskOcj-2|o`WZ=6{X>BJ z%rl}_9F$YPt3H}Hzb@l&W)jN3tIH22ls}>8L2?B1S_Hd@=jA~z(YgRX zWsGa#Kg;;trGP{0{3+45tq<#UC6SEw&O3GcerZqb2Ihb#!VmT9Oec)<=*$z$t6Ehb zd>u>`PxeQIUwPeN9bny%@2+q#@xTsJ-5H|WX7#x8GJ7HPZ2zMf_2*%ixhtdn#ly$w zGD5dBOr|%-QNu&QI1BaKYwR}xE&;^)Q3nDy1GwNt${;)y#J_WohxEbvBb)4(+wb%Ig@kKicpYH5mTFdBXo~ z!ar8Y_@C@=AeGbpDB}m|l{(GyL3+JTyVO6@+jN@8gY=+Lr`wh+jDEc zZ=vyp`mYnd^8IA3ceGy19MbcDTI5CcY%Q0`!@nFtaLUMe^iO);_}5BFJf(xig?y#q z_CP>Oy3F>0$?pdrqrH1yhT|9RrP67Iua$LlR|Ajb_CPM#`bYfs zpgf+Af^wTqPp$!fo^ny?I6%RU8SJhH#rVY#z>AUb{lz!s<;!3X{KLC*snh})>lKY;_* zB29mN;M1!$9EE@)(NEocB6O}6$b4aTaoj+4>U5sx5$M%ZKzFQf3ld*gBmSbhrl4lZ zuw}^4+f0K)1cb(;+@=zq;6sihxA=T$`qT7WgD$g5_!DxP?m=np4TKH1t=OQhL5_^;ED2ne;yn^hiCOjc*V2hG{)tqfp zpQdMKmD7tS;ho?Ni<~~84e|~4_z7)UH8aHV#4NRQpU-^TlBmCmC?9MO%H!oiP;SeG zp&Ibnew+vV(`vvMdnp)So#XfUYLW8PPT+G?%UNUBfW6u7b+vwJ68WEnA5;PVV?Bk> zUj{m$^--At^;JAx26$MH*5}CjxNA|4IF^xP*~g`yR>U_Pf+PL|9B3Iw>wY~B_8SPS z=;{2uS})I%D{xr`QBOJN3!iJfZ0TRM0Qls3I^JsR`@FW_PXV2=JaJi%x@#2s>+Twt zaopTW2>KawW{G@m6JU&`0Vn`K-&-Je}0b^-5`sgPb@5!LHP z-{x|n*K=D0j-=gns>J<5uXDdDvx3X>*^<`l0`EGFPgCA*jjzYx54%t-C*#M7nK8UT zuj~F6TkzD}-~@VI!|^g+Sf042f^K*{cs;w&Klz_zrS*_p*K&m7y%c9c)Pd!RiF?va zNRE*F@I-z%Mt%&W$PcoA!}6mkQGPHTwg=_${1=qR%a5Skwp(OBBPg#IzQ~WDy$zrD z?T&o>KHqi;-}>^y*o{k&4|_UfhpSQ#Qy9k)XgOi8n03|rluDiY6RbPr2bH6K;E!DB zfRIz{Jef~m$Hn6-D7V++q8jjJJ%aXi!l(URl0PoVOUR3bhm1Wgb{i(ldQJ5RKP~*Q z=36bNPW`FK!L}BiukA)fb{xsI7A-HjVfqX;!G%@6goJqa_d zjn_x;OmQE*E^3{~xeVc->?nzI$a;}J=X&mo30{bPZ2A)Y6wqHhzWj2PlX>9$fZh*A zn#N0Z=NgQ^q{koce-}}Y`PSc`mGLUr&5ZAYwIA=6!~QhvSI9}=3!S9zzc0czONHdT zhpIz(9xeZHmL36o)>m4esRsFn>(r{K9&vt^{liWb?;i$sl1-=L9|q;|bQ+Y~azp&X zpxlN(Pdl)tf2jQ<(0SDF8;<&Y#K!gehmf}iEdLN57}LM_hop~19&7-;rpW`rgU{~? z@z|i;#`75rzhQWG&m{Pmjgc=}?)vdPOfxk;cUBE~qHj^(0MG%atDJ0hTivIfqbIY`-tsBl^kR2z^wBku@ymUW(J8hZm!H3HgwJ z#Tm&jJR|3EP;xTqk3}1`y)J$`va=#rJh6X~v4rcr8{i>NbDx)evEo7KF_Nn|xPY$C^DuANm(!$ohJ5)?b|dLKAer?5*_U)a zE3i|BIlfAECeuCX7g|Sm6PlDm>tf3R6OY7s9GqLCe$YNtzfQTs5MW;|g5J{nNciZ% z?oFGTME)xTPwq>3TmfIjjw__cTaolSFuL}yw!+>~D6ptgjvnuiZLX-4Y$P*sj&xpSl#N$kVMn7aum-;$w?Oz>ne1End^}j2=-z<1c zi0{+B%n6~mToGxC=Y!Z4#o>tWk0hQ?@dvUSe(~{q+2=cAdEhoWo^Rqhi&Qk8pVjp5 zBA@h?@oPlx`2Jg9$1&gfa+$XCLU9OMSJs#AwIpwlB7bQe{@3RjtoR+RpXgUSQtkEq zmj84BmY1}aeM$VTh#e^Vk|Mt;`pffb!(R5lTBQ8`uul%T+xBXSkGrp<@utKN=ev(9 z^YiiMUdR0f_Mfe#@#d%pTL_^Bi@{XMIlLH*qLU?{m`^ zz_<7tGunU9{)x)L&&9fc99a)Q4|ALi@lDykhyTL+G%K-wLlBmBoRa)Z&@|c))U&@o zP5L$PXO;C{mQp2*<0gPDVapZ$LOf?lZ>r(^Fzx>rz1$|sZ~D05w*sFlDtZv%Tz_9d z-lK7E)qeR|#Ai)7*LsNf6X)g$PSfX2`~3NQE-BkIEb}So{j96y>d zc>_l^4b%Buq^ml=D)sWa1rKVAoL7Qg=tp%l3BPC722Y*kgq~;X(=x822l=y>HNVKO zTJfa$+S7JCiQ!?ixjCNqN{=Veiiun$IvE%G3CEKPEi!NSY?4EZf0nq9#p4Q<$J+Iw za?3B#{!gG6Z8_J(ax5sf+hZD}7x34X^B~7cG+v#iI1`@(!_(kR@1ywV>~#BC{Buz6 z>mFg8zwJ~*zrSRD@;vPj#qELT8v9VbpX}>(*wr_298BUMILC1(_}x7~>lN}_?e&-O z26`}-zC!J7`Y`)k#xHM$Au!lnZtrG)+>gt-H`W-(JmWFMZjpG&A4U7q`*t1h|5xG6 z{cQh#p8LiAzFh}Q>OtZUhvTd?F39gd9uP)g=e7Q!p0tJ$s!@Wy`k z;-4nU2j;g>dF=Xy%B}Tt+3ya@RTtZ%UEtSvznj-7s2{&i5GuF&Q!Nai5X64Bd!fdI zIVag2@w*8R1L{5azboUZC#V_pIq>CPB<)%~V3_op!~ui-Q*$n60pN{LIbxd>cl?UB z15AHP&P}xbvCdZxMQQP;&pnWRfd2G4g1E7ueavS$#7TUAepWn5=s762*TwSh-QNdZ zM6Sv?RyvP76oPN#QS<%c!(yLc{u1A9g#|%z3%r;xLv+|7=~l$r2MA9cP6_l!aS4$V zpJ6<9BLdu}K8n0{v3%XVt-BOOU{kx^DiV{ zSRT1I(YQnVZnk_8`4HHv@#`6sTkC1=H9(xjW4`Lrat`{pOni_<|7r5;4LrVpUhMIK zts?Rjuex`iAAIag~Obd z-FgK6z0f=lOauwNzj zny3!MpJiNf|Hebwo>F-0oX3&VesoU8W&9v>KG*o;-THlp7R0|9UMt?`Xnx`H-!*!k z^)UH2ZS2=~AZpF>gXgRD`mX~W7{3q;O>}U-p#x1H(KregDr?R!tO%c9D1OSK1CSN3 zSBHi#b~3GCR@rYoZ@xdo=NsF~FhIy(GWXh@Cw{em&WIT49L5OGzxas8vvOk3 z#T6fw@;1a6!8eQ^YVE$+_(6*O=4l7%Aru=w+Ps&sQp)}J(nY={>N($kWWU-!CkdqJ zeV2s&2lL)X>lbOf4UFg1TK`)6sK)P?xFjJ|_D@M(4K04sr#qW22(^Uw+fff|j6bwX;i9aP7myEeW@O`b~ia(=IZ|Bd`M{Zwih=Py$w z&R00U%YL%^HqbF*ceGbo23)$I%vrnm>E$bg--z&o5wKAXFcK#s6~e z=KLQUxZ;=l{Sf~v)rVcbVX6nbTJ{-*U-{f|E&N99vcfFz7F^$XF9hk5=T;FW3entmERPuO{f2*2;8enaDI^gRDQG|zQ3UErY& z4~m-#yTm^%$05&$<$gPf7sTH?`<6Z;0`KZCG9yi%BH^DB~`w{NPta5F4 zke^KUjel>-LH%CbHpn~7dzQW-`+)S4a`@bpdr<2g-ak2V`dbVif#{#aD&K!i`xLM_ zC`}@spG4L8kwbi#>!H56|JcpQI8eZR@6tX(nX8E$@Q|i;%xk{peFmhD*Yzo0O!YVD zG}$Rg_rC@GtwKJHYd-yvzfJv{{uT@ThV^I9@0tep$NoTQe(M|DpFO{K9^?M*sk6VT zMW5TH{ZXSY8~?AZbA0vk51oGq)APNl{SoJiy+r(64#_v$t`>hakbklEOsKu(Ke)6% zoCcrmfl&K8;m=zRkY5_c8?9Rz*lCp9q4Q_TQB}~gZs1P7R4{i4dg%aFT(hwIA|mLv++s% z*vS5DJxuRy9LxUxs=@t{z6h@$#Y2sXZF{R0e8SIQ1L4biV!`;1_W0=>a2?|}=iBD`-5&R(s9%uu-JV5f zY5&-Kw`XaSd?zU3yFCkEB5`bL=NP|wKK`q17wk)=!};^IP4~as;Qna;C(PG-8{D5g zzbjMw)BC)#56XP~R;s`Fu|&Qm{nHaFkL8P?+?+q2Gu{5h+~{`UJr>>e)S%n93*D0Z zqWJ#8_v`k!hmpUFrE(kOeYvc@pE)n*xK)0Qeh+SqycdUiAo+c{>?(O*E_aILXHSuO z+0FWWxz+T(T;UNVaj-|seK?LsajwFn68BH^(`TZ1aF4cMZ_~*YsdO@bzO(h)vl`r= zZLcmr#{H2$R|eV|<$v4r^V0isPp9(sOd?;?`V5B3WBEE%Zt3fJ+gr9j%HvJMAK4@H z-U{unWwl+x`y%?i%cm0O$#I~?L3!*vL*@27C61JgM~Z_C1$Z*|bCvhTWUtnc9j`G#|+WZxY% z`DVv$$i6Q(P`=snW}w0S*>(k;Yis2E?Ec6fY-E4-KFO;a++POp!}{=r$GE>2HMl=p z-fcd{{aw=F{_HqA(NDR7{BFlXR@K?xol*H(({H@B&ieK4=aN1N)BjkV^QpH#TRx9A zxIdfzFFwZo?QC#=_CDyY2KPt%AYpp>vj+EP^Edgy{*fB`x0 zR4eXuTP^v4bLbo&miq+!@pE%=jr2KsZWTx?bOd^;;CrYx8{jFURTCx;?&6BXPZ4NzRw)`!7{~gO2NMkocXPUxqhY zlDM8z*7;?r=bW$OdVG%u;(Btg*dzL0v0;bfhIU-9X55ALQR|4iELwb&{B-i;!hCj1 zee_ZHxj38d-%@9Pe_o5u<{3wz{iHBH?y7Tq_39~GZ~SSE{xT@XS;iwimuTC)pJ;G@ zF#_j*IvGLg%*aeMO?9!BBaue;O*c{oh*f$$n@ge2%k*#&@*GPx`Em z@vAOZ_CNgZS%{o2d_OCC32$V|y?+*e&2fKm@tH=Bm;k%j7dQi^txA0gEd`SE_;KMrQS55dF-<-RARNb17^?UMj<>SH+ z8-#DXAAD5l*|rlXUY1PPyo~eWAG^P+8{D6LzWfgw+@C$as~X%N*==Dv=r50Pf0GUF z&&KE02KQ(C5#tT+&z|3#YW0WrdY%jUwI3VQxXP*>Z|vc{?5Cn>UFEsH zci#Sdk<;!QssFAQC+)Y%ctYi|`Yu#%`5QD%8bAKx-#h1XjK^TU7wY-GH+{ZxPjP5I z^PTT!xxZ2pehELSrz}h2P(+DE$;t|tOK(fidk*2TXch2Nv~*9D#MeE{;rIb}5*xu?B}-aB>hPP9LtQw=`R zBus}Eow@%^;}7vgtp6D*kEi26&eRK^;^KpIhp)`{roH?slBf1}trUI74R?>|`}r3D zBf=LTIr8_5B}dkMnCKp*O0D2HfXD5ib)xmvu)uRaPk5rQaYf6W!w!uD+6b8M!~b+& z#Pj%>`u%YC(~8eecKl54@5v~JQ=B^-*!i~*k!3tR55AAL3;MTAbgl7}@o=7Vh;Q4a z^JMtr?{Pz`>hI@R-`92*O8mFe)%OZ1NAC5_>+eedGTlqvwLYdse;^ zrSLT&8ux00AEG-zS&v&oKraN2^_ltpG58pA4@3fv2Huv4Kc7W4g?n(+lHWlK!n4Zr zch13oC3!DAiw-D1n=6t0E9rB$+vGbKLA{)Ow`17E?{UudZJ=lSyB!Dp_s3T|Z}a5= zjl*;Plt)9~tDsTI{Rv5Of$jB>KC<`}gv9qTcb=i;2Ay{z{CM(Sye|(g)%rGG?g1UT z-AZle7`tcv-0Yqj@=pc&NkzVQ+BquYcE5oB%b|JN@=V4cz{oWc{4e^Avt3 z{VV)$>>f%(k1Uxmau^Fh^kDS*w9z9>uhxkpzw!Qza zgeUY8A4j6`D587!?{z(BM7mc;>_(K49g8$gGu4ib+_MAvJ%FZUEff9T41O&6^P_uq zNN)-rjlNY8JV{H8i|YYD9iY()^C8BWqUW?e2z@Wk(g!VIb)t9Ro%MlPr(s`D$o<`4 z)c7#pi!$};JxOk_<&Q7#oCLWP{XP=ufdQIF(SoCr+Sic(%^-0e56gQ1&aM;rC}~%J zUykHF_p6+_$$7fBg5e$|xn7v-d=uojravQ}$o?XGb&0;81=Pm!S!DztkgwKvlYIGH zB0V}!`CO{ury02oHHdi+h2&3?Jic4gH_NT$`bUkv{|)0RgIsTl@eS3wST{DY+&iCniqcM-{4#+3GZI&I!lfL_b_tx-tGTBR=AJF>D z!)=I==lDA%k`Fy6`?UD}jI=9}U!EI){Z9RSMEgC6Z%}*pzWgQ{SI$A40e*t}b+w7? z{LVuVlf-YH)WbNA7yYWoai5ICy)V0o&;Ru2Hc`AJui#GtZpS-het9x^c$_9~GHd_% zcp87lord3`;VAuHjUVN#mUu~4p99K?+)_FH-ky1n4|WOW3+(Cl`<&JK9MEcg4ya82 z2mZwVN!L$p9Pz}xQXOCTWud1?JOqPsJm9+>^AUS8eLiGui~sXw!Rs-$d&zI&@7;8Q zMwqUebo#B5?!Z2J1@u<)9!lfCu2d2Stk>`2`!Ib!6G9$-Q@q0C2sad_Sw-g{J3&)2J{?!a zd;0Re@`ZUhKa<<0|lX!J@h2&>eNWIMITK>v?(Heg^kJ-j?rAHhcFOqnWeI9cb zKK>=T9`^d-Jb~z21b$>+UG%SeJ)W|Tt7zR-e~#oR)-UhL`az#z{gea85p>mgwZ;P$ zT=c5ZU+!MLe$VIi+vnqt^;ai!DX;(RbF5jx-w~f<<#Sy}{C@g;&pZCR=9~Dq%51uQ zUiv!CTGxfq`jF0I4}l-Z4{-0x37_@nneUX7DKWo&$Iz=U53;ge@#l^^|5o!G+Qso( zC+tefM?A`7x%B1exMtDY4;+zSUrDd$eg*YHa>Rz?emT7yql(Ifde@WoBf@gI-gEl@ z5uIPd*5_M3Kk@!q=T^f{*>z$M=Jftq(z$RWk8=84cy^tZQ|m+?`S(4XOz|*+FLYp? zd;ikh^pTMlq+f*Z6XnH;EH9q%=g0D*3lUan_lrIEK5zQ6_5QIw7u4u;Pj;L+^&Ar7 zS^Fs-40}_ar(5SMBo3YN_hG=CP*{EV2Zsw!I8R{sKKx&Tz07uwA5Z6e_Zw(Z?*4lE zK7FV>b{`;AZpZ0p{~Ym9cd6q2ysiUcue%RWzZjRcpC~TL`jq5T+Wpw(+y~#Y-2Dwd zUOsI6W$h<>;uiy|w(oLT-fyz@<+~;f-xz)?r0&ai`Ee!S7Jg>eHGcbJvoEjximuli z{ug{P9z8-Q(ro|X2gau~{<}wHoc4Dfrj1`h`xRu*%oYE%A20;`Uq|$bMrfGk2X+0G zj91Rxbbl?0-ef$X@_2d++S~M|$B*&wxf^`uUgGhdvx(`yl;Jo<(G$B4^;mu*ULyG= zD}Pw>-NRrhp;xItO!G%;cqk0$5DFUBX9H};z0!O9|a!7$tm1k<1tf`eOjz~PTTSN`^q}qFXO^I*`5J@7@kju zcSYf5hHZF`hNt(5{C#TJYw+*zw}!{}Ifj#C%42%hd;mE@ajI^_D_9@q^}hOgjEncN z^J{26v-UAcIzDt%_MvC-MH9v?ayTJQK;u1xdB{B;-T#rqABz2+P2CE%31|2=UYqz4B>P6#!HjpxLx-v zjl9u(szg4!|4HKx*$?)3$Q~RFmB)`KuwU%)aC@FT$K7oGSGV2KAibygI>2lEJcDw3 zp2Cl+>o38XpeyFj`Ouebr~aD97w3&nqC?WlCZ73g?bkqG>?eHzIU~=n>*qK0^8tBk zzI`sA&m)fp!UY`L2VxgLhx8BmIjZmkipo@@XFaoWxUr3*E zdt}S`4qYEp3HzunJ3j}!4)xxq?R*uSFSGqDECtcG%8Q@nVLuP&mzFL!`fhIi)l%4=m?>0HXZ!7sseiQ;u|gh4()!VQkbhMW{oiZGP5VQz2i*HWC)fw=c~I!Yeb8SIj=#E( z>hgy*9lg-;7=NXVJoUQvdd(0{o8xM`(N(4u*%+7yG;) z_bU({U!n2n`w>kN-}bV@>NJe=JqFK;{fXXP8h%#JnLA3(k-MKnx*z@F>Fl6!HJw5I z#w&h?mt7@vgmDmlb-UgZ^mwL? ze*;O1@}5r_Jg6<4exvTUS=;@?xsryrlFEA*nEJ+l!3lK2=T#;@vr^+z*3ZW$@J{_1 z_&6}d^QHJd;rI0hzp{SV7w5Py`;}4rp3MFE_?7dmkO~}UbU%qS;}`xD<5&C@@Rj!G z)P^X2Gbht{m;RB)>-|Dkgjb=14ATe3Vd#p_0cd>sauW0too_Rw-sPC@(}I@*>}ol` zYwoj^{T$@W{>wvp|E1)g$8C2&10;5IJSW=zpxF;{xZX6KryA=0b1i2IXejb7F|Ly! zZ{BU_c#iVs9!uVQ&~JZC-E<~tz=iw7aM&<}PLw0_TR(ffq_o#v&QABjCuKE$l z&yJV$AZ}hEJzoUh4C0x0!toNqgTzaUnaKJ6o-rej#cn=E@e&$OC|(kx+ju+$bP$iH zpxjy)WzLH`j>mW@YQNxGV;2dZV>CiXzWslc&Jo%7=6#>|3cbb0kx($tbyyDgB;Sl5 z47;JB{n2|0;r{4eyK)2PXU9#Jr1y8k^labb^Gyk#a=+1K)rCOXJVxw7Yj^Fzm!E*@@he|Eg-zZ=}2jnD6<_jkl}Py8F^=WnI= zbJXeno@3nKI~&{|-MbUU=bi@lM|>LY?^u5)V0VM(XY&E+pYk!(H~({t`};v!f3S1+ z^S+?!g`e$koJZ~-A<3M3F6=5U@BItAUhmyhzjsEL->IK>nR>eYJ4{;O_x_cYzQ?4c zzM?oNc{mufqBtqMF7|hnI*51&(WS(X5nSMWiVutbgSdv2v;RnNIN$vd&41aFM7y23 z>Is#{+J~WX%RY2}TmwG&A%mgz^}>Iw27LBA<^lgFHQ>*8{5KPPtcKS6is2!3MCVzS zbo4;Q%H!83D7V(1$c+4ao}Y*Qep&;c^IfmMv+&9A(0ilt z`D~5P_wxJ#eAxb$%r7XPFFqfyfzNsJuOd4P{w_}C#_d1Ry7E54aht@?bt@1_Wy)5v zPovLo0soR-sqI%y7qjB82POXMmgSu7G{3L1taHwEjd2iN+jM8C-zf|rsh&v+Q=Z||h&-rM#hU4^E#2s*MiqI zG`%TJU#Pc)+PV6@lIbGomg;9QPhKae$R4Dd79tJ(@yBcj0651Lef>%FM0g@PcHYY8 zRb`(N-P69xaqrV|f#ZG47T1uoL3#W-2j#Y$wdJ%ecc^?QL`PQpBH=A$2k3J!O~|Hu zNM^^^q`$y!h#zlIZuO@KgNTP7_sIl)mG#p03-ER|<3He6jyEoAOT;JH>4Ty2`0)kp zt?@~`F=(&xk;gj6#~Vei2k`3b5AShkx`W+z4)B8g@CwPXyr#!YB;H7Pka#2F^WpnM zj{Es9B;H8l3B?;jbY|1xeD$9>p9OtF^s{VHLY$H5lShSfQ=p&yL_ZjpJoP<*u$Lt5 z-vfvibiZH=x6JU9&TG&<0@>RXS0(Dmo+&mV1f(#rO@C$I^SK z+~Thy@n;%*rjt4oG=UrAca#GIioB1uf(uZ?Q_j{LJZN?L|?-`H4Gb0pojPQPl z@NWA#xE`x6ybJvg%$feRUJ?Ihd4CN$4$9-{EGW0=*wD?gC-95rc{}4|n`-E~eb2}(;1T=_do>)l(DnfE%=_&jJlc2$KdYWl zc?~=(xlbvpJhh7Mf3wfQ;r!jIko<%;z_>6zsDsG<`?4oR&KN#8YU@-Zu33}L>%@mW ze?#Y(Fz@3n1Aj5S&-0u?U$>ScHO?9I9Us#3^Pzt`zS-z_D(N}$E6jdjjd*e2v*&h> zqR*TstEIQ-{Gg~Ln`X;C^<(F;xn5MY5v3~a*l%FlHC-@#~OS& ziw+3_?7qGaX*ogns1p2~b}EHq&iWiH z)-fbskuCet+i6@l*8CRuN2}1B;~7}zYs3zob^eC@hrv*J{5XU5_Bf?|Fb^9K z@L;z${Gjz!A8b+5SIj3d^HauOOt{a)+_Td6fL>>u&*ghT)P|hgXVP?`KIhg&{(Ikl zXuU>ql+wOk^GkZ4NWJ$c&;~+Sucg1g1+?q>6lY_*N#8>?3-9ogagWS$gxxB%Ua-pq z&k!xdM`}%0-ydVn74rQt%ZpKbFYoo+w{iOh-QU9c5b??WKLcMp2lT$5_yKe&PftGw z^z&KrqI=ff8Vtz@4I@g$8_>w)6Lp`zEqu!V{|#FoHDgn zck+F#xn{BR54H$hDfJ=FSIzfOeYLVh`vK}*oWEY`7rhqg*VKQz&M*1-zsL2j!#&VM zuXvxA)7v1ACH?o5&U@LLDUAv8eRRuzg5lA8yd3ANPZYWS@?oK?JcWR`eM?d3^4-ny zK1<7IwH!E&_we&t=9%$|8cwk&??Ds~$T%d8_pWcII7)xN(G&W;hzOYg(UF?JlxQ zWM7Nputvme)C@O{P@ShXOZl|My_8D@JWxDc+9W$d(6-m`^A6je=Rs;E#y%&z0XQtWu;$h z(!eNxmy7vp2~w1X4rk*L-}*pO-zQ`8u@2CuRDk_9)qjH;$PG{m`h(Ud)ZVWLc>On_ z-iq4wlxkf_?fTz@`h&IW_rDol8`xipaUzi055Pyx@p)ai z-WipVoz-pL)R;F}o$^MjS9+IJrnX*snisB=@H_jdnNmU#XQO<$M){1QNv8aXT1$hH zTn4a*lS>tK{d8Q{;WmShOuQ$Yp2P2L;W`0+$M#ewCPubZ=Z@g)%AT1pEc_6cJSN-fOxmUmb?ccrWjbCX! zMk~8UwwgXKsciEu**8_4^s0KKrbM3GUX$Mcw%5dAB?==c1^O<>$rqFN)bC#WjnT2u zld8MMr}laA6WBg6whJWXU0$6St?u+D$17W{6-eqG`B-fqZ3#e3ud6(a#CuJ3f=>As5R(3=P+Du$Ot5cV*tL#LWJK@i)b?ozzGY`h* zo6JYfJZyf(J|8(NU41@smbbY&HAUiuH2cPry-RkEZM{6K+?)RK^QR^ZJt_O`;etTF z8T%pHix^_z7SZ3k!L^$v#`ZulZrda@v}s~wYxOi;ZAWEt6KY6_$qa!&+o`dwV>`Xw z<2xoQ(5_Q^v4~UN_K}H6k`iZ?H=lL!d1suldBc`atKjGQ@|N<&8#kPL=9aU9%+N2` zjH!RI>tZs0^4nfo3Sm%e^aF7QUi0$CRaYijS7v8l>Vu;Ip$T_dyWPE@bhJu*?< zb{?#cORJTLZ9?$Iz`PX*Zc~27*u<8J%IGAd`N$aFOiumF-1OKLSEklTpWn|XTee@8 z-X80JdUgBQM0L|dm1K}Ts=JQ=;b+q!%qm?U&H>eG;yQbD`)Ty>mRBO$BCb|)9#DRJ4BCAe9M>?ka2Zl z@DpS`{LzDAKVxKMU+msFb()2w+2v6>xuh~#)eI8Xz0&q~VwGLHcb?|$s7`H|*fF^o zq&@{~C04e2fc-SRYxHs}H=3NNZr?q+?L?p{9*dDw-2bh4nKaZCw2lz^ab!fXTM; zRk}1cjf1aoH_X(381sF)h^DHhM*}+Q3=yi`W zu;^2lRz|}}-kGACsJF>W)5A+s<0D~o%^Kij;EK&Eql_!Aydo7rmC3!a?Mx=gCzdNF z!+MTB`l{33_v9TP+Wkz=72kTal3Orz?~Vt$Px`l|n{IgOg3a%EKj_;g@mv^$p+~Zd z;zojUu}?0n>>Pobox5|U)OR1e`^yn#4|lA8PgeF+x9mL&CPa0TIK^mlxO01`$3W7= z{CfDKALB|GR{8X^W1uTFGRXsvEu&1#kMtv^A3woKlIbMe6NI|%pA^vck%50CrilNybmFqUEaE}(W&ZQ@>Z~6dBlI&l^ru3HCpFulwUA< z`RLe{qvSSD?HixXy&KD$w_JS2OE+w|c=N{dw$uP-6~lh<65^A-jGjynKdH)Os(4hD zboWDz*HB=7{LBx8L?_{w@2c#DV?H)9GPUoFY85^?C>B-2<>7n-#V>~-2^{&;5s?!$ zA1;q5eht7cpRxi|b~y#gOsApe$NC@iLdjV_1Nx-+1qSJjQ~1HJcgOudb^rF0FJHg^ z_AlP>!Ip{5m-n21{dpfdbZ+~_pPu;3P1g*?=#Q0tWBfRE&5s@mr|sVmn>!&DDMT|h z0y}T2vTIx-%3|!j_V$1Ik=-Vo*5e-+Fd-P)R>Xmn)W1;wyzvw6JGCJSum8Z~7pB(P zUz({=eptuNw~xUd4cVqz6#8N?9?br7LVoEZrXW~fgCaJ2CwZQzbeyS}@Pt3Uy)rTp zP=?RB(g5&-;3tz#v_gzl4risu_rf*fe?JhPZE}@ke#Jk3o*tih=BZ~k8~;C0v8WDSEd@UdoG!Uk^ne0Aw53kT{7ZF1^2{8C4(XCnc5vBVt(zJ zO7~Cq>`I@Xp~QseCqneII*vXQ%npB~WcEZ13co~t!0O=eD(p|#RuR{PE12@xmWi|v z{G*Yn$?DEoYn@;=`J;gF+g-O{)vaIs{1bdQaX()8@6S6wF5l#R?}=C5Xnw~%?|8}P zS0=2>VN*Y{J~8}F>l5DZGVRQBYAmf(saKq{`Q_)XyLerB%eu2JK6gX;rTDpVqiKy3 zk5_f4$`3@Brp_Bj_-bmx8(0et9jzjeu--qDaM?&Ye+50o|C+gA`_~@%?)vYHZCrKV dYaV;^iLHy;KD+EWn}(J@e#gJQ{P*t}`2X@(omv0@ literal 75296 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 MqpQDmPU|E84^E!&RR910 diff --git a/etc/multivm_bootloaders/vm_gateway/gas_test.yul/gas_test.yul.zbin b/etc/multivm_bootloaders/vm_gateway/gas_test.yul/gas_test.yul.zbin index c1726d8301ff2ffc1e8dc3dfdf1552926080a3a3..3268a37a313cafd28e0355d7147a6d2edecc1dbb 100644 GIT binary patch literal 69280 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

+$< z!>!Pp1W#qxo47*S$Ma(Cm>2g?@&p)=uUUFIVLr6)i|8Nby=;3`D_?J|cX*VCEm8kE z4@+^#UxkMTwGC%cC~Cy#Zi>UbdkD`g&DKl5{_ zPi#CkX!pI91fRwUf9WixW+=WUd>Q}Atb?qcn?DuAn+rfM$^+W|RF!yh;kg>WWPd=9 zmY3*zjwfk&dOvcH;|t%;=EEY5Tl>3h$2H$B(Q*^ScZjpeFZi6Mx901Xm!au-Xy2pj zccLGnXA9K}@{&D)9(yL?ErqTY|GLR~EaVHDf9s8NedwWAVEjtrTxHfbC7xV}0``yG zpJ#lbcV6~e{~|L)cGUd^;vVcDBnZ*PB0bcJ6mxjeFo;hGtz7IjvK>F6)WCV1 zUp4*l_}V|o&z1N47-yQ^7XAzMax#B}t+ymdbJSKWRKzl7z<{e|Urf08Fv zyv_mr?-e1UCBn~1=1Vib%a@9s)A@*Sed{+< $*1a2fZL;IR`mU3kA$cZ$i=@x& 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 diff --git a/infrastructure/local-gateway-upgrade-testing/README.md b/infrastructure/local-gateway-upgrade-testing/README.md new file mode 100644 index 000000000000..d878055f5ff4 --- /dev/null +++ b/infrastructure/local-gateway-upgrade-testing/README.md @@ -0,0 +1,118 @@ +# Local upgrade testing + +While it is theoretically possible to do it in CI-like style, it generally leads to needless recompilations, esp of rust +programs. + +Here we contain the files/instructions needed to test the gateway upgrade locally. + +## Step 0 + +- pull zksync-era to ~/zksync-era +- pull zksync-era-private to ~/zksync-era-private + +## Step 1: Preparation + +To easiest way to avoid needless is caching. There are two ways to avoid caching: + +- Cache target/etc in a separate directory +- Have two folders of zksync-era and switch between those + +We use the second approach for robustness and simplicity. + +### Enabling `era-cacher` + +Copy `era-cacher` to some other folder (as the zksync-era one will change) and add it to PATH, so it can be invoked. + +You should download a clone of zksync-era, put it into the `zksync-era-old` directory. It should point to the commit of +`main` we will upgrade from. + +## Step 2: spawning old chain + +Run `use-old-era.sh`. The old contents of the zksync-era will be moved to `zksync-era-new` folder (there the gateway +version is stored), while the old one will be present in `zksync-era-new`. + +## Step 3: Move to new chain and upgrade it + +Use upgrade scripts as in the example below. + +## Full flow + +``` +# make sure that there are 2 folders: zksync-era with old era and zksync-era-private with new era +# if test was run previously you probably need to move folder +mv ~/zksync-era-current ~/zksync-era-private + +cd ~ && use-old-era.sh && cd ./zksync-era-current + +zkstackup --local && zkstack dev clean all && zkstack up --observability false + +zkstack ecosystem init --deploy-paymaster --deploy-erc20 \ + --deploy-ecosystem --l1-rpc-url=http://127.0.0.1:8545 \ + --server-db-url=postgres://postgres:notsecurepassword@localhost:5432 \ + --server-db-name=zksync_server_localhost_era \ + --ignore-prerequisites --verbose \ + --observability=false + +cd ~ && use-new-era.sh && cd ./zksync-era-current + +zkstackup --local +zkstack dev contracts +zkstack dev database migrate + +zkstack chain gateway-upgrade -- adapt-config + +# Server should be started in a different window for consistency +zkstack server --ignore-prerequisites --chain era + +zkstack e gateway-upgrade --ecosystem-upgrade-stage no-governance-prepare + +# only if chain has weth deployed before upgrade. +# i.e. you must run it iff `predeployed_l2_wrapped_base_token_address` is set in config. +zkstack chain gateway-upgrade -- set-l2weth-for-chain + +zkstack e gateway-upgrade --ecosystem-upgrade-stage governance-stage1 + +zkstack chain gateway-upgrade -- prepare-stage1 + +# restart the server. wait for all L1 txs to exeucte!!!! + +zkstack chain gateway-upgrade -- schedule-stage1 + +# turn off the server => we need it because we need to somehow update validator timelock +# also getPriorityTreeStartIndex needs to be updated. + +zkstack chain gateway-upgrade -- finalize-stage1 + +# restart the server + +cd ~/zksync-era +zkstack dev test integration --no-deps --ignore-prerequisites --chain era +cd ~/zksync-era-current + +zkstack ecosystem gateway-upgrade --ecosystem-upgrade-stage governance-stage2 +zkstack ecosystem gateway-upgrade --ecosystem-upgrade-stage no-governance-stage2 + +# turn off the server + +zkstack chain gateway-upgrade -- finalize-stage2 + +# turn on the server + +zkstack dev test integration --no-deps --ignore-prerequisites --chain era + + + +zkstack ecosystem gateway-upgrade --ecosystem-upgrade-stage governance-stage3 +zkstack ecosystem gateway-upgrade --ecosystem-upgrade-stage no-governance-stage3 + +# in separate window +zkstack server --ignore-prerequisites --chain gateway + +# wait for era server to finalize all L1 txs +# stop era server! + +zkstack chain migrate-to-gateway --chain era --gateway-chain-name gateway + +# restart era server! +zkstack dev test integration --no-deps --ignore-prerequisites --chain era +``` diff --git a/infrastructure/local-gateway-upgrade-testing/era-cacher/use-new-era.sh b/infrastructure/local-gateway-upgrade-testing/era-cacher/use-new-era.sh new file mode 100755 index 000000000000..7b2bc9ad495a --- /dev/null +++ b/infrastructure/local-gateway-upgrade-testing/era-cacher/use-new-era.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +OLD_REPO=~/zksync-era +NEW_REPO=~/zksync-era-private + +WORKING_DIRECTORY=~/zksync-era-current + +# Check if the folder exists +if [ ! -d "$NEW_REPO" ]; then + echo "Error: The folder '$NEW_REPO' does not exist." + exit 1 +else + echo "Updating to use new era" +fi + +rm -rf $NEW_REPO/chains +mkdir $NEW_REPO/chains +cp -rf $WORKING_DIRECTORY/chains $NEW_REPO + + +rm -rf $NEW_REPO/configs +mkdir $NEW_REPO/configs +cp -rf $WORKING_DIRECTORY/configs $NEW_REPO + + +mv $WORKING_DIRECTORY $OLD_REPO +mv $NEW_REPO $WORKING_DIRECTORY diff --git a/infrastructure/local-gateway-upgrade-testing/era-cacher/use-old-era.sh b/infrastructure/local-gateway-upgrade-testing/era-cacher/use-old-era.sh new file mode 100755 index 000000000000..e52d6ad278b7 --- /dev/null +++ b/infrastructure/local-gateway-upgrade-testing/era-cacher/use-old-era.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +OLD_REPO=~/zksync-era +NEW_REPO=~/zksync-era-private + +WORKING_DIRECTORY=~/zksync-era-current + +# Check if the folder exists +if [ ! -d "$OLD_REPO" ]; then + echo "Error: The folder '$OLD_REPO' does not exist." + exit 1 +else + echo "Updating to use old era." +fi + +mv $OLD_REPO $WORKING_DIRECTORY diff --git a/package.json b/package.json index b293bedd8f69..6c7457ba29c0 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "packages": [ "contracts", "contracts/l1-contracts", + "contracts/da-contracts", "contracts/l2-contracts", "contracts/system-contracts", "etc/ERC20", @@ -28,6 +29,7 @@ "local-prep": "yarn workspace local-setup-preparation", "l1-contracts": "yarn workspace l1-contracts", "l2-contracts": "yarn workspace l2-contracts", + "da-contracts": "yarn workspace da-contracts", "revert-test": "yarn workspace revert-test", "upgrade-test": "yarn workspace upgrade-test", "recovery-test": "yarn workspace recovery-test", diff --git a/prover/crates/lib/prover_fri_types/src/lib.rs b/prover/crates/lib/prover_fri_types/src/lib.rs index 44c54da578ee..9d7c32c21d73 100644 --- a/prover/crates/lib/prover_fri_types/src/lib.rs +++ b/prover/crates/lib/prover_fri_types/src/lib.rs @@ -30,7 +30,7 @@ pub mod queue; pub const MAX_COMPRESSION_CIRCUITS: u8 = 5; // THESE VALUES SHOULD BE UPDATED ON ANY PROTOCOL UPGRADE OF PROVERS -pub const PROVER_PROTOCOL_VERSION: ProtocolVersionId = ProtocolVersionId::Version25; +pub const PROVER_PROTOCOL_VERSION: ProtocolVersionId = ProtocolVersionId::Version26; pub const PROVER_PROTOCOL_PATCH: VersionPatch = VersionPatch(0); pub const PROVER_PROTOCOL_SEMANTIC_VERSION: ProtocolSemanticVersion = ProtocolSemanticVersion { minor: PROVER_PROTOCOL_VERSION, diff --git a/yarn.lock b/yarn.lock index 5df8cb570e0f..732577daeb68 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1737,15 +1737,23 @@ sinon-chai "^3.7.0" ts-morph "^22.0.0" -"@matterlabs/hardhat-zksync-node@^0.0.1-beta.7": - version "0.0.1" - resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-node/-/hardhat-zksync-node-0.0.1.tgz#d44bda3c0069b149e2a67c9697eb81166b169ea6" - integrity sha512-rMabl+I813lzXINqTq5OvujQ30wsfO9mTLMPDXuYzEEhEzvnXlaVxuqynKBXrgXAxjmr+G79rqvcWgeKygtwBA== +"@matterlabs/hardhat-zksync-node@^1.2.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-node/-/hardhat-zksync-node-1.2.1.tgz#786d51b28ad3aa5b8b973831e016151326d844e4" + integrity sha512-BZDJyEB9iu54D6sOKTGeJrN5TRFLrg6k9E1x3lEwpOfewPwg1eTfb9e/LKGSCePbSremZIHzK3eDRr80hVdDjA== dependencies: - "@matterlabs/hardhat-zksync-solc" "^1.0.5" - axios "^1.4.0" - chalk "4.1.2" - fs-extra "^11.1.1" + "@matterlabs/hardhat-zksync-solc" "^1.2.5" + axios "^1.7.2" + chai "^4.3.4" + chalk "^4.1.2" + debug "^4.3.5" + fs-extra "^11.2.0" + proxyquire "^2.1.3" + semver "^7.6.2" + sinon "^18.0.0" + sinon-chai "^3.7.0" + source-map-support "^0.5.21" + undici "^6.18.2" "@matterlabs/hardhat-zksync-solc@0.4.2": version "0.4.2" @@ -1771,16 +1779,7 @@ proper-lockfile "^4.1.2" semver "^7.5.1" -"@matterlabs/hardhat-zksync-solc@^0.3.15": - version "0.3.17" - resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-solc/-/hardhat-zksync-solc-0.3.17.tgz#72f199544dc89b268d7bfc06d022a311042752fd" - integrity sha512-aZgQ0yfXW5xPkfuEH1d44ncWV4T2LzKZd0VVPo4PL5cUrYs2/II1FaEDp5zsf3FxOR1xT3mBsjuSrtJkk4AL8Q== - dependencies: - "@nomiclabs/hardhat-docker" "^2.0.0" - chalk "4.1.2" - dockerode "^3.3.4" - -"@matterlabs/hardhat-zksync-solc@^1.0.5", "@matterlabs/hardhat-zksync-solc@^1.1.4": +"@matterlabs/hardhat-zksync-solc@=1.1.4", "@matterlabs/hardhat-zksync-solc@^1.0.5": version "1.1.4" resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-solc/-/hardhat-zksync-solc-1.1.4.tgz#04a2fad6fb6b6944c64ad969080ee65b9af3f617" integrity sha512-4/usbogh9neewR2/v8Dn2OzqVblZMUuT/iH2MyPZgPRZYQlL4SlZtMvokU9UQjZT6iSoaKCbbdWESHDHSzfUjA== @@ -1797,6 +1796,15 @@ sinon-chai "^3.7.0" undici "^5.14.0" +"@matterlabs/hardhat-zksync-solc@^0.3.15": + version "0.3.17" + resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-solc/-/hardhat-zksync-solc-0.3.17.tgz#72f199544dc89b268d7bfc06d022a311042752fd" + integrity sha512-aZgQ0yfXW5xPkfuEH1d44ncWV4T2LzKZd0VVPo4PL5cUrYs2/II1FaEDp5zsf3FxOR1xT3mBsjuSrtJkk4AL8Q== + dependencies: + "@nomiclabs/hardhat-docker" "^2.0.0" + chalk "4.1.2" + dockerode "^3.3.4" + "@matterlabs/hardhat-zksync-solc@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-solc/-/hardhat-zksync-solc-1.2.0.tgz#c1ccd1eca0381840196f220b339da08320ad9583" @@ -1814,7 +1822,7 @@ sinon-chai "^3.7.0" undici "^6.18.2" -"@matterlabs/hardhat-zksync-solc@^1.2.4": +"@matterlabs/hardhat-zksync-solc@^1.2.4", "@matterlabs/hardhat-zksync-solc@^1.2.5": version "1.2.5" resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-solc/-/hardhat-zksync-solc-1.2.5.tgz#fbeeabc3fea0dd232fa3c8cb31bd93c103eba11a" integrity sha512-iZyznWl1Hoe/Z46hnUe1s2drBZBjJOS/eN+Ql2lIBX9B6NevBl9DYzkKzH5HEIMCLGnX9sWpRAJqUQJWy9UB6w== @@ -1882,6 +1890,11 @@ resolved "https://registry.yarnpkg.com/@matterlabs/prettier-config/-/prettier-config-1.0.3.tgz#3e2eb559c0112bbe9671895f935700dad2a15d38" integrity sha512-JW7nHREPqEtjBWz3EfxLarkmJBD8vi7Kx/1AQ6eBZnz12eHc1VkOyrc6mpR5ogTf0dOUNXFAfZut+cDe2dn4kQ== +"@matterlabs/zksync-contracts@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@matterlabs/zksync-contracts/-/zksync-contracts-0.6.1.tgz#39f061959d5890fd0043a2f1ae710f764b172230" + integrity sha512-+hucLw4DhGmTmQlXOTEtpboYCaOm/X2VJcWmnW4abNcOgQXEHX+mTxQrxEfPjIZT0ZE6z5FTUrOK9+RgUZwBMQ== + "@metamask/eth-sig-util@^4.0.0": version "4.0.1" resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" @@ -2305,11 +2318,21 @@ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.5.tgz#572b5da102fc9be1d73f34968e0ca56765969812" integrity sha512-f7L1//4sLlflAN7fVzJLoRedrf5Na3Oal5PZfIq55NFcVZ90EpV1q5xOvL4lFvg3MNICSDr2hH0JUBxwlxcoPg== +"@openzeppelin/contracts-upgradeable@4.9.5": + version "4.9.5" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.5.tgz#572b5da102fc9be1d73f34968e0ca56765969812" + integrity sha512-f7L1//4sLlflAN7fVzJLoRedrf5Na3Oal5PZfIq55NFcVZ90EpV1q5xOvL4lFvg3MNICSDr2hH0JUBxwlxcoPg== + "@openzeppelin/contracts-v4@npm:@openzeppelin/contracts@4.9.5": version "4.9.5" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.5.tgz#1eed23d4844c861a1835b5d33507c1017fa98de8" integrity sha512-ZK+W5mVhRppff9BE6YdR8CC52C8zAvsVAiWhEtQ5+oNxFE6h1WdeWo+FJSF8KKvtxxVYZ7MTP/5KoVpAU3aSWg== +"@openzeppelin/contracts@4.9.5": + version "4.9.5" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.5.tgz#1eed23d4844c861a1835b5d33507c1017fa98de8" + integrity sha512-ZK+W5mVhRppff9BE6YdR8CC52C8zAvsVAiWhEtQ5+oNxFE6h1WdeWo+FJSF8KKvtxxVYZ7MTP/5KoVpAU3aSWg== + "@openzeppelin/contracts@^4.8.0": version "4.9.6" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.6.tgz#2a880a24eb19b4f8b25adc2a5095f2aa27f39677" @@ -5750,6 +5773,14 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" +fill-keys@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fill-keys/-/fill-keys-1.0.2.tgz#9a8fa36f4e8ad634e3bf6b4f3c8882551452eb20" + integrity sha512-tcgI872xXjwFF4xgQmLxi76GnwJG3g/3isB1l4/G5Z4zrbddGpBjqZCO9oEAcB5wX0Hj/5iQB3toxfO7in1hHA== + dependencies: + is-object "~1.0.1" + merge-descriptors "~1.0.0" + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -6827,6 +6858,13 @@ is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.13.1: dependencies: hasown "^2.0.0" +is-core-module@^2.16.0: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + is-data-view@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" @@ -6895,6 +6933,11 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-object@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" + integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== + is-path-inside@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" @@ -8123,6 +8166,11 @@ memorystream@^0.3.1: resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== +merge-descriptors@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -8380,6 +8428,11 @@ mocha@^9.0.2: yargs-parser "20.2.4" yargs-unparser "2.0.0" +module-not-found-error@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/module-not-found-error/-/module-not-found-error-1.0.1.tgz#cf8b4ff4f29640674d6cdd02b0e3bc523c2bbdc0" + integrity sha512-pEk4ECWQXV6z2zjhRZUongnLJNUeGQJ3w6OQ5ctGwD+i5o93qjRQUk2Rt6VdNeu3sEP0AB4LcfvdebpxBRVr4g== + moo@^0.5.0: version "0.5.2" resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c" @@ -9220,6 +9273,15 @@ proxy-from-env@^1.1.0: resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== +proxyquire@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/proxyquire/-/proxyquire-2.1.3.tgz#2049a7eefa10a9a953346a18e54aab2b4268df39" + integrity sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg== + dependencies: + fill-keys "^1.0.2" + module-not-found-error "^1.0.1" + resolve "^1.11.1" + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -9547,6 +9609,15 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.20.0, resolve@^1.22 path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.11.1: + version "1.22.10" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== + dependencies: + is-core-module "^2.16.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + responselike@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626" @@ -10068,7 +10139,7 @@ source-map-support@0.5.13: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@^0.5.13: +source-map-support@^0.5.13, source-map-support@^0.5.21: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -10196,7 +10267,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -10213,6 +10284,15 @@ string-width@^2.1.0, string-width@^2.1.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -10279,7 +10359,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -10300,6 +10380,13 @@ strip-ansi@^5.1.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -10400,7 +10487,7 @@ synckit@^0.8.6: version "0.1.0" dependencies: "@matterlabs/hardhat-zksync-deploy" "^0.7.0" - "@matterlabs/hardhat-zksync-solc" "^1.1.4" + "@matterlabs/hardhat-zksync-solc" "=1.1.4" "@matterlabs/hardhat-zksync-verify" "^1.4.3" commander "^9.4.1" eslint "^8.51.0" @@ -10582,6 +10669,11 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +toml@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" + integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== + tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -11150,7 +11242,16 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -11231,6 +11332,11 @@ yaml@^2.4.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.2.tgz#7a2b30f2243a5fc299e1f14ca58d475ed4bc5362" integrity sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA== +yaml@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.7.0.tgz#aef9bb617a64c937a9a748803786ad8d3ffe1e98" + integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA== + yargs-parser@20.2.4: version "20.2.4" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" @@ -11315,3 +11421,7 @@ zksync-ethers@^6.9.0: version "6.9.0" resolved "https://registry.yarnpkg.com/zksync-ethers/-/zksync-ethers-6.9.0.tgz#efaff1d59e2cff837eeda84c4ba59fdca4972a91" integrity sha512-2CppwvLHtz689L7E9EhevbFtsqVukKC/lVicwdeUS2yqV46ET4iBR11rYdEfGW2oEo1h6yJuuwIBDFm2SybkIA== + +"zksync-ethers@https://github.com/zksync-sdk/zksync-ethers#sb-use-new-encoding-in-sdk": + version "6.12.1" + resolved "https://github.com/zksync-sdk/zksync-ethers#bc6e3ab201f743fcbb53e0216f3de421bb3a617f" diff --git a/zkstack_cli/Cargo.lock b/zkstack_cli/Cargo.lock index 1ea38d968073..cfc16535db50 100644 --- a/zkstack_cli/Cargo.lock +++ b/zkstack_cli/Cargo.lock @@ -7081,6 +7081,8 @@ dependencies = [ "zksync_consensus_crypto", "zksync_consensus_roles", "zksync_consensus_utils", + "zksync_contracts", + "zksync_eth_client", "zksync_protobuf", "zksync_protobuf_build", "zksync_protobuf_config", @@ -7139,6 +7141,7 @@ dependencies = [ "zksync_config", "zksync_protobuf", "zksync_protobuf_config", + "zksync_system_constants", ] [[package]] @@ -7298,6 +7301,34 @@ dependencies = [ "zksync_basic_types", ] +[[package]] +name = "zksync_eth_client" +version = "0.1.0" +dependencies = [ + "async-trait", + "jsonrpsee", + "rlp", + "thiserror", + "tracing", + "vise", + "zksync_config", + "zksync_contracts", + "zksync_eth_signer", + "zksync_types", + "zksync_web3_decl", +] + +[[package]] +name = "zksync_eth_signer" +version = "0.1.0" +dependencies = [ + "async-trait", + "rlp", + "thiserror", + "zksync_basic_types", + "zksync_crypto_primitives", +] + [[package]] name = "zksync_mini_merkle_tree" version = "0.1.0" diff --git a/zkstack_cli/Cargo.toml b/zkstack_cli/Cargo.toml index 6d18d1a531d7..f3e0d8a9c9d5 100644 --- a/zkstack_cli/Cargo.toml +++ b/zkstack_cli/Cargo.toml @@ -34,6 +34,7 @@ zksync_system_constants = { path = "../core/lib/constants" } zksync_types = { path = "../core/lib/types" } zksync_web3_decl = { path = "../core/lib/web3_decl" } zksync_eth_client = { path = "../core/lib/eth_client" } +zksync_contracts = { path = "../core/lib/contracts" } zksync_consensus_roles = "=0.7.0" zksync_consensus_crypto = "=0.7.0" zksync_consensus_utils = "=0.7.0" diff --git a/zkstack_cli/crates/common/src/contracts.rs b/zkstack_cli/crates/common/src/contracts.rs index 4cef4467f382..268c1a7ae521 100644 --- a/zkstack_cli/crates/common/src/contracts.rs +++ b/zkstack_cli/crates/common/src/contracts.rs @@ -6,32 +6,23 @@ use crate::cmd::Cmd; pub fn build_l1_contracts(shell: Shell, link_to_code: PathBuf) -> anyhow::Result<()> { let _dir_guard = shell.push_dir(link_to_code.join("contracts/l1-contracts")); + Ok(Cmd::new(cmd!(shell, "yarn build:foundry")).run()?) +} + +pub fn build_l1_da_contracts(shell: Shell, link_to_code: PathBuf) -> anyhow::Result<()> { + let _dir_guard = shell.push_dir(link_to_code.join("contracts/da-contracts")); Ok(Cmd::new(cmd!(shell, "forge build")).run()?) } pub fn build_l2_contracts(shell: Shell, link_to_code: PathBuf) -> anyhow::Result<()> { let _dir_guard = shell.push_dir(link_to_code.join("contracts/l2-contracts")); - Ok(Cmd::new(cmd!( - shell, - "forge build --zksync --zk-enable-eravm-extensions" - )) - .run()?) + Cmd::new(cmd!(shell, "yarn build:foundry")).run()?; + Ok(()) } pub fn build_system_contracts(shell: Shell, link_to_code: PathBuf) -> anyhow::Result<()> { let _dir_guard = shell.push_dir(link_to_code.join("contracts/system-contracts")); // Do not update era-contract's lockfile to avoid dirty submodule Cmd::new(cmd!(shell, "yarn install --frozen-lockfile")).run()?; - Cmd::new(cmd!(shell, "yarn preprocess:system-contracts")).run()?; - Cmd::new(cmd!( - shell, - "forge build --zksync --zk-enable-eravm-extensions" - )) - .run()?; - Cmd::new(cmd!(shell, "yarn preprocess:bootloader")).run()?; - Ok(Cmd::new(cmd!( - shell, - "forge build --zksync --zk-enable-eravm-extensions" - )) - .run()?) + Ok(Cmd::new(cmd!(shell, "yarn build:foundry")).run()?) } diff --git a/zkstack_cli/crates/common/src/forge.rs b/zkstack_cli/crates/common/src/forge.rs index a7cf08a50bc0..1bf0570873d4 100644 --- a/zkstack_cli/crates/common/src/forge.rs +++ b/zkstack_cli/crates/common/src/forge.rs @@ -69,6 +69,17 @@ impl ForgeScript { return Ok(res?); } } + + // TODO: This line is very helpful for debugging purposes, + // maybe it makes sense to make it conditionally displayed. + let command = format!( + "forge script {} --legacy {}", + script_path.to_str().unwrap(), + args_no_resume.join(" ") + ); + + println!("Command: {}", command); + let mut cmd = Cmd::new(cmd!( shell, "forge script {script_path} --legacy {args_no_resume...}" @@ -291,6 +302,8 @@ pub struct ForgeScriptArgs { pub verifier_api_key: Option, #[clap(long)] pub resume: bool, + #[clap(long)] + pub zksync: bool, /// List of additional arguments that can be passed through the CLI. /// /// e.g.: `zkstack init -a --private-key=` @@ -304,6 +317,9 @@ impl ForgeScriptArgs { pub fn build(&mut self) -> Vec { self.add_verify_args(); self.cleanup_contract_args(); + if self.zksync { + self.add_arg(ForgeScriptArg::Zksync); + } self.args .iter() .map(|arg| arg.to_string()) @@ -399,6 +415,10 @@ impl ForgeScriptArgs { .iter() .any(|arg| WALLET_ARGS.contains(&arg.as_ref())) } + + pub fn with_zksync(&mut self) { + self.zksync = true; + } } #[derive(Debug, Clone, ValueEnum, Display, Serialize, Deserialize, Default)] diff --git a/zkstack_cli/crates/config/Cargo.toml b/zkstack_cli/crates/config/Cargo.toml index 5ddf36c2d30b..0926f2522cb2 100644 --- a/zkstack_cli/crates/config/Cargo.toml +++ b/zkstack_cli/crates/config/Cargo.toml @@ -29,3 +29,4 @@ zksync_protobuf_config.workspace = true zksync_protobuf.workspace = true zksync_config.workspace = true zksync_basic_types.workspace = true +zksync_system_constants.workspace = true diff --git a/zkstack_cli/crates/config/src/chain.rs b/zkstack_cli/crates/config/src/chain.rs index edd6199511d7..19f275d78f80 100644 --- a/zkstack_cli/crates/config/src/chain.rs +++ b/zkstack_cli/crates/config/src/chain.rs @@ -7,7 +7,10 @@ use serde::{Deserialize, Serialize, Serializer}; use xshell::Shell; use zkstack_cli_types::{BaseToken, L1BatchCommitmentMode, L1Network, ProverMode, WalletCreation}; use zksync_basic_types::L2ChainId; -use zksync_config::configs::{GatewayChainConfig, GatewayConfig}; +use zksync_config::{ + configs::{gateway::GatewayChainConfig, GatewayConfig}, + DAClientConfig::{Avail, NoDA}, +}; use crate::{ consts::{ @@ -19,7 +22,7 @@ use crate::{ FileConfigWithDefaultName, ReadConfig, ReadConfigWithBasePath, SaveConfig, SaveConfigWithBasePath, ZkStackConfig, }, - ContractsConfig, GeneralConfig, GenesisConfig, SecretsConfig, WalletsConfig, + ContractsConfig, GeneralConfig, GenesisConfig, SecretsConfig, WalletsConfig, GATEWAY_FILE, }; /// Chain configuration file. This file is created in the chain @@ -67,6 +70,13 @@ pub struct ChainConfig { pub evm_emulator: bool, } +#[derive(Debug, Clone)] +pub enum DAValidatorType { + Rollup = 0, + NoDA = 1, + Avail = 2, +} + impl Serialize for ChainConfig { fn serialize(&self, serializer: S) -> Result where @@ -101,6 +111,20 @@ impl ChainConfig { } anyhow::bail!("Wallets configs has not been found"); } + + pub fn get_da_validator_type(&self) -> anyhow::Result { + let general = self.get_general_config().expect("General config not found"); + match ( + self.l1_batch_commit_data_generator_mode, + general.da_client_config, + ) { + (L1BatchCommitmentMode::Rollup, _) => Ok(DAValidatorType::Rollup), + (L1BatchCommitmentMode::Validium, None | Some(NoDA)) => Ok(DAValidatorType::NoDA), + (L1BatchCommitmentMode::Validium, Some(Avail(_))) => Ok(DAValidatorType::Avail), + _ => anyhow::bail!("DAValidatorType is not supported"), + } + } + pub fn get_contracts_config(&self) -> anyhow::Result { ContractsConfig::read_with_base_path(self.get_shell(), &self.configs) } @@ -137,6 +161,10 @@ impl ChainConfig { self.configs.join(SECRETS_FILE) } + pub fn path_to_gateway_config(&self) -> PathBuf { + self.configs.join(GATEWAY_FILE) + } + pub fn save_general_config(&self, general_config: &GeneralConfig) -> anyhow::Result<()> { general_config.save_with_base_path(self.get_shell(), &self.configs) } diff --git a/zkstack_cli/crates/config/src/consts.rs b/zkstack_cli/crates/config/src/consts.rs index 6c5dfc8165ce..95c097bf4247 100644 --- a/zkstack_cli/crates/config/src/consts.rs +++ b/zkstack_cli/crates/config/src/consts.rs @@ -29,7 +29,7 @@ pub const ZKSYNC_ERA_GIT_REPO: &str = "https://github.com/matter-labs/zksync-era /// Name of the docker-compose file inside zksync repository pub const DOCKER_COMPOSE_FILE: &str = "docker-compose.yml"; /// Path to the config file with mnemonic for localhost wallets -pub(crate) const CONFIGS_PATH: &str = "etc/env/file_based"; +pub const CONFIGS_PATH: &str = "etc/env/file_based"; /// Path to the docker-compose file for grafana pub const ERA_OBSERVABILITY_COMPOSE_FILE: &str = "era-observability/docker-compose.yml"; /// Path to era observability repository diff --git a/zkstack_cli/crates/config/src/contracts.rs b/zkstack_cli/crates/config/src/contracts.rs index bac17958e3b7..eb88cf3af854 100644 --- a/zkstack_cli/crates/config/src/contracts.rs +++ b/zkstack_cli/crates/config/src/contracts.rs @@ -1,5 +1,6 @@ use ethers::types::{Address, H256}; use serde::{Deserialize, Serialize}; +use zksync_system_constants::{L2_ASSET_ROUTER_ADDRESS, L2_NATIVE_TOKEN_VAULT_ADDRESS}; use crate::{ consts::CONTRACTS_FILE, @@ -38,6 +39,12 @@ impl ContractsConfig { .deployed_addresses .bridges .shared_bridge_proxy_addr; + self.bridges.l1_nullifier_addr = Some( + deploy_l1_output + .deployed_addresses + .bridges + .l1_nullifier_proxy_addr, + ); self.ecosystem_contracts.bridgehub_proxy_addr = deploy_l1_output .deployed_addresses .bridgehub @@ -49,6 +56,26 @@ impl ContractsConfig { self.ecosystem_contracts.transparent_proxy_admin_addr = deploy_l1_output .deployed_addresses .transparent_proxy_admin_addr; + self.ecosystem_contracts.l1_bytecodes_supplier_addr = Some( + deploy_l1_output + .deployed_addresses + .state_transition + .bytecodes_supplier_addr, + ); + self.ecosystem_contracts.stm_deployment_tracker_proxy_addr = Some( + deploy_l1_output + .deployed_addresses + .bridgehub + .ctm_deployment_tracker_proxy_addr, + ); + self.ecosystem_contracts.force_deployments_data = Some( + deploy_l1_output + .contracts_config + .force_deployments_data + .clone(), + ); + self.ecosystem_contracts.expected_rollup_l2_da_validator = + Some(deploy_l1_output.expected_rollup_l2_da_validator_addr); self.l1.default_upgrade_addr = deploy_l1_output .deployed_addresses .state_transition @@ -61,6 +88,8 @@ impl ContractsConfig { self.l1.multicall3_addr = deploy_l1_output.multicall3_addr; self.ecosystem_contracts.validator_timelock_addr = deploy_l1_output.deployed_addresses.validator_timelock_addr; + self.ecosystem_contracts.native_token_vault_addr = + Some(deploy_l1_output.deployed_addresses.native_token_vault_addr); self.l1.verifier_addr = deploy_l1_output .deployed_addresses .state_transition @@ -70,6 +99,21 @@ impl ContractsConfig { self.ecosystem_contracts .diamond_cut_data .clone_from(&deploy_l1_output.contracts_config.diamond_cut_data); + self.l1.rollup_l1_da_validator_addr = Some( + deploy_l1_output + .deployed_addresses + .rollup_l1_da_validator_addr, + ); + self.l1.no_da_validium_l1_validator_addr = Some( + deploy_l1_output + .deployed_addresses + .no_da_validium_l1_validator_addr, + ); + self.l1.avail_l1_da_validator_addr = Some( + deploy_l1_output + .deployed_addresses + .avail_l1_da_validator_addr, + ); self.l1.chain_admin_addr = deploy_l1_output.deployed_addresses.chain_admin; } @@ -77,15 +121,20 @@ impl ContractsConfig { self.l1.diamond_proxy_addr = register_chain_output.diamond_proxy_addr; self.l1.governance_addr = register_chain_output.governance_addr; self.l1.chain_admin_addr = register_chain_output.chain_admin_addr; + self.l1.access_control_restriction_addr = + Some(register_chain_output.access_control_restriction_addr); + self.l1.chain_proxy_admin_addr = Some(register_chain_output.chain_proxy_admin_addr); + self.l2.legacy_shared_bridge_addr = register_chain_output.l2_legacy_shared_bridge_addr; } pub fn set_l2_shared_bridge( &mut self, initialize_bridges_output: &InitializeBridgeOutput, ) -> anyhow::Result<()> { - self.bridges.shared.l2_address = Some(initialize_bridges_output.l2_shared_bridge_proxy); - self.bridges.erc20.l2_address = Some(initialize_bridges_output.l2_shared_bridge_proxy); - self.l2.legacy_shared_bridge_addr = Some(initialize_bridges_output.l2_shared_bridge_proxy); + self.bridges.shared.l2_address = Some(L2_ASSET_ROUTER_ADDRESS); + self.bridges.erc20.l2_address = Some(L2_ASSET_ROUTER_ADDRESS); + self.l2.l2_native_token_vault_proxy_addr = Some(L2_NATIVE_TOKEN_VAULT_ADDRESS); + self.l2.da_validator_addr = Some(initialize_bridges_output.l2_da_validator_address); Ok(()) } @@ -135,27 +184,25 @@ pub struct EcosystemContracts { pub state_transition_proxy_addr: Address, pub transparent_proxy_admin_addr: Address, // `Option` to be able to parse configs from pre-gateway protocol version. - // TODO(EVM-927): not used without gateway version. #[serde(skip_serializing_if = "Option::is_none")] pub stm_deployment_tracker_proxy_addr: Option

, pub validator_timelock_addr: Address, pub diamond_cut_data: String, // `Option` to be able to parse configs from pre-gateway protocol version. - // TODO(EVM-927): not used without gateway version. #[serde(skip_serializing_if = "Option::is_none")] pub force_deployments_data: Option, // `Option` to be able to parse configs from pre-gateway protocol version. - // TODO(EVM-927): not used without gateway version. #[serde(skip_serializing_if = "Option::is_none")] pub native_token_vault_addr: Option
, // `Option` to be able to parse configs from pre-gateway protocol version. - // TODO(EVM-927): not used without gateway version. #[serde(skip_serializing_if = "Option::is_none")] pub l1_bytecodes_supplier_addr: Option
, // `Option` to be able to parse configs from pre-gateway protocol version. - // TODO(EVM-927): not used without gateway version. #[serde(skip_serializing_if = "Option::is_none")] pub expected_rollup_l2_da_validator: Option
, + // `Option` to be able to parse configs from pre-gateway protocol version. + #[serde(skip_serializing_if = "Option::is_none")] + pub l1_wrapped_base_token_store: Option
, } impl ZkStackConfig for EcosystemContracts {} @@ -165,7 +212,6 @@ pub struct BridgesContracts { pub erc20: BridgeContractsDefinition, pub shared: BridgeContractsDefinition, // `Option` to be able to parse configs from pre-gateway protocol version. - // TODO(EVM-927): not used without gateway version. #[serde(skip_serializing_if = "Option::is_none")] pub l1_nullifier_addr: Option
, } @@ -185,11 +231,9 @@ pub struct L1Contracts { #[serde(default)] pub chain_admin_addr: Address, // `Option` to be able to parse configs from pre-gateway protocol version. - // TODO(EVM-927): not used without gateway version. #[serde(skip_serializing_if = "Option::is_none")] pub access_control_restriction_addr: Option
, // `Option` to be able to parse configs from pre-gateway protocol version. - // TODO(EVM-927): not used without gateway version. #[serde(skip_serializing_if = "Option::is_none")] pub chain_proxy_admin_addr: Option
, pub multicall3_addr: Address, @@ -197,23 +241,18 @@ pub struct L1Contracts { pub validator_timelock_addr: Address, pub base_token_addr: Address, // `Option` to be able to parse configs from pre-gateway protocol version. - // TODO(EVM-927): not used without gateway version. #[serde(skip_serializing_if = "Option::is_none")] pub base_token_asset_id: Option, // `Option` to be able to parse configs from pre-gateway protocol version. - // TODO(EVM-927): not used without gateway version. #[serde(skip_serializing_if = "Option::is_none")] pub rollup_l1_da_validator_addr: Option
, // `Option` to be able to parse configs from pre-gateway protocol version. - // TODO(EVM-927): not used without gateway version. #[serde(skip_serializing_if = "Option::is_none")] pub avail_l1_da_validator_addr: Option
, // `Option` to be able to parse configs from pre-gateway protocol version. - // TODO(EVM-927): not used without gateway version. #[serde(skip_serializing_if = "Option::is_none")] pub no_da_validium_l1_validator_addr: Option
, // `Option` to be able to parse configs from pre-gateway protocol version. - // TODO(EVM-927): not used without gateway version. #[serde(skip_serializing_if = "Option::is_none")] pub transaction_filterer_addr: Option
, } @@ -223,19 +262,15 @@ pub struct L2Contracts { pub testnet_paymaster_addr: Address, pub default_l2_upgrader: Address, // `Option` to be able to parse configs from pre-gateway protocol version. - // TODO(EVM-927): not used without gateway version. #[serde(skip_serializing_if = "Option::is_none")] pub da_validator_addr: Option
, // `Option` to be able to parse configs from pre-gateway protocol version. - // TODO(EVM-927): not used without gateway version. #[serde(skip_serializing_if = "Option::is_none")] pub l2_native_token_vault_proxy_addr: Option
, + // `Option` to be able to parse configs from previous protocol version + #[serde(skip_serializing_if = "Option::is_none")] + pub legacy_shared_bridge_addr: Option
, pub consensus_registry: Option
, pub multicall3: Option
, - pub legacy_shared_bridge_addr: Option
, pub timestamp_asserter_addr: Option
, - // `Option` to be able to parse configs from pre-gateway protocol version. - // TODO(EVM-927): not used without gateway version. - #[serde(skip_serializing_if = "Option::is_none")] - pub predeployed_l2_wrapped_base_token_address: Option
, } diff --git a/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/input.rs b/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/input.rs index 17b2bac38a3f..47fe66143250 100644 --- a/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/input.rs +++ b/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/input.rs @@ -6,6 +6,7 @@ use ethers::{ }; use rand::Rng; use serde::{Deserialize, Serialize}; +use zkstack_cli_types::L1Network; use zksync_basic_types::L2ChainId; use crate::{ @@ -111,6 +112,7 @@ pub struct DeployL1Config { pub era_chain_id: L2ChainId, pub owner_address: Address, pub testnet_verifier: bool, + pub support_l2_legacy_shared_bridge_test: bool, pub contracts: ContractsDeployL1Config, pub tokens: TokensDeployL1Config, } @@ -124,11 +126,14 @@ impl DeployL1Config { initial_deployment_config: &InitialDeploymentConfig, era_chain_id: L2ChainId, testnet_verifier: bool, + l1_network: L1Network, + support_l2_legacy_shared_bridge_test: bool, ) -> Self { Self { era_chain_id, testnet_verifier, owner_address: wallets_config.governor.address, + support_l2_legacy_shared_bridge_test, contracts: ContractsDeployL1Config { create2_factory_addr: initial_deployment_config.create2_factory_addr, create2_factory_salt: initial_deployment_config.create2_factory_salt, @@ -162,6 +167,7 @@ impl DeployL1Config { priority_tx_max_gas_limit: initial_deployment_config.priority_tx_max_gas_limit, validator_timelock_execution_delay: initial_deployment_config .validator_timelock_execution_delay, + avail_l1_da_validator_addr: l1_network.avail_l1_da_validator_addr(), }, tokens: TokensDeployL1Config { token_weth_address: initial_deployment_config.token_weth_address, @@ -196,6 +202,8 @@ pub struct ContractsDeployL1Config { pub bootloader_hash: H256, pub default_aa_hash: H256, pub evm_emulator_hash: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub avail_l1_da_validator_addr: Option
, } #[derive(Debug, Deserialize, Serialize, Clone)] diff --git a/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/output.rs b/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/output.rs index 7a922cbdf3c0..a0bca69cafd5 100644 --- a/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/output.rs +++ b/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/output.rs @@ -16,26 +16,10 @@ pub struct DeployL1Output { pub era_chain_id: u32, pub l1_chain_id: u32, pub multicall3_addr: Address, - pub owner_addr: Address, + pub owner_address: Address, pub contracts_config: DeployL1ContractsConfigOutput, pub deployed_addresses: DeployL1DeployedAddressesOutput, -} - -impl ZkStackConfig for DeployL1Output {} - -#[derive(Debug, Deserialize, Serialize, Clone)] -pub struct DeployL1ContractsConfigOutput { - pub diamond_init_max_l2_gas_per_batch: u64, - pub diamond_init_batch_overhead_l1_gas: u64, - pub diamond_init_max_pubdata_per_batch: u64, - pub diamond_init_minimal_l2_gas_price: u64, - pub diamond_init_priority_tx_max_pubdata: u64, - pub diamond_init_pubdata_pricing_mode: u64, - pub priority_tx_max_gas_limit: u64, - pub recursion_circuits_set_vks_hash: H256, - pub recursion_leaf_level_vk_hash: H256, - pub recursion_node_level_vk_hash: H256, - pub diamond_cut_data: String, + pub expected_rollup_l2_da_validator_addr: Address, } #[derive(Debug, Deserialize, Serialize, Clone)] @@ -45,15 +29,33 @@ pub struct DeployL1DeployedAddressesOutput { pub transparent_proxy_admin_addr: Address, pub validator_timelock_addr: Address, pub chain_admin: Address, + pub access_control_restriction_addr: Address, pub bridgehub: L1BridgehubOutput, pub bridges: L1BridgesOutput, pub state_transition: L1StateTransitionOutput, + pub rollup_l1_da_validator_addr: Address, + pub no_da_validium_l1_validator_addr: Address, + pub avail_l1_da_validator_addr: Address, + pub l1_rollup_da_manager: Address, + pub native_token_vault_addr: Address, +} + +impl ZkStackConfig for DeployL1Output {} + +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct DeployL1ContractsConfigOutput { + pub diamond_cut_data: String, + pub force_deployments_data: String, } #[derive(Debug, Deserialize, Serialize, Clone)] pub struct L1BridgehubOutput { pub bridgehub_implementation_addr: Address, pub bridgehub_proxy_addr: Address, + pub ctm_deployment_tracker_proxy_addr: Address, + pub ctm_deployment_tracker_implementation_addr: Address, + pub message_root_proxy_addr: Address, + pub message_root_implementation_addr: Address, } #[derive(Debug, Deserialize, Serialize, Clone)] @@ -62,21 +64,24 @@ pub struct L1BridgesOutput { pub erc20_bridge_proxy_addr: Address, pub shared_bridge_implementation_addr: Address, pub shared_bridge_proxy_addr: Address, + pub l1_nullifier_implementation_addr: Address, + pub l1_nullifier_proxy_addr: Address, } #[derive(Debug, Deserialize, Serialize, Clone)] pub struct L1StateTransitionOutput { + pub state_transition_proxy_addr: Address, + pub state_transition_implementation_addr: Address, + pub verifier_addr: Address, pub admin_facet_addr: Address, - pub default_upgrade_addr: Address, - pub diamond_init_addr: Address, - pub diamond_proxy_addr: Address, + pub mailbox_facet_addr: Address, pub executor_facet_addr: Address, - pub genesis_upgrade_addr: Address, pub getters_facet_addr: Address, - pub mailbox_facet_addr: Address, - pub state_transition_implementation_addr: Address, - pub state_transition_proxy_addr: Address, - pub verifier_addr: Address, + pub diamond_init_addr: Address, + pub genesis_upgrade_addr: Address, + pub default_upgrade_addr: Address, + pub diamond_proxy_addr: Address, + pub bytecodes_supplier_addr: Address, } #[derive(Debug, Deserialize, Serialize, Clone)] diff --git a/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/input.rs b/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/input.rs index 4143a286da62..afd71cd97757 100644 --- a/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/input.rs +++ b/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/input.rs @@ -1,4 +1,3 @@ -/// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. use ethers::abi::Address; use serde::{Deserialize, Serialize}; use zkstack_cli_types::ProverMode; @@ -91,7 +90,6 @@ impl DeployGatewayCTMInput { l1_chain_id: U256::from(ecosystem_config.l1_network.chain_id()), testnet_verifier: ecosystem_config.prover_version == ProverMode::NoProofs, - recursion_node_level_vk_hash: H256::zero(), recursion_leaf_level_vk_hash: H256::zero(), recursion_circuits_set_vks_hash: H256::zero(), diff --git a/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/mod.rs b/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/mod.rs index cb22b3529e85..7d1a54844d0c 100644 --- a/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/mod.rs +++ b/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/mod.rs @@ -1,3 +1,2 @@ -/// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. pub mod input; pub mod output; diff --git a/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/output.rs b/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/output.rs index ee85d11a5eb6..33661fb6ebef 100644 --- a/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/output.rs +++ b/zkstack_cli/crates/config/src/forge_interface/deploy_gateway_ctm/output.rs @@ -1,4 +1,3 @@ -/// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. use ethers::abi::Address; use serde::{Deserialize, Serialize}; diff --git a/zkstack_cli/crates/config/src/forge_interface/deploy_l2_contracts/input.rs b/zkstack_cli/crates/config/src/forge_interface/deploy_l2_contracts/input.rs index 3836dca9d24c..78ffcd16eaa8 100644 --- a/zkstack_cli/crates/config/src/forge_interface/deploy_l2_contracts/input.rs +++ b/zkstack_cli/crates/config/src/forge_interface/deploy_l2_contracts/input.rs @@ -1,8 +1,8 @@ use ethers::types::Address; use serde::{Deserialize, Serialize}; -use zksync_basic_types::L2ChainId; +use zksync_basic_types::{L2ChainId, U256}; -use crate::{traits::ZkStackConfig, ChainConfig}; +use crate::{traits::ZkStackConfig, ChainConfig, ContractsConfig}; impl ZkStackConfig for DeployL2ContractsInput {} @@ -16,20 +16,27 @@ pub struct DeployL2ContractsInput { pub bridgehub: Address, pub governance: Address, pub erc20_bridge: Address, + pub da_validator_type: U256, pub consensus_registry_owner: Address, } impl DeployL2ContractsInput { - pub fn new(chain_config: &ChainConfig, era_chain_id: L2ChainId) -> anyhow::Result { + pub fn new( + chain_config: &ChainConfig, + contracts_config: &ContractsConfig, + era_chain_id: L2ChainId, + ) -> anyhow::Result { let contracts = chain_config.get_contracts_config()?; let wallets = chain_config.get_wallets_config()?; + Ok(Self { era_chain_id, chain_id: chain_config.chain_id, l1_shared_bridge: contracts.bridges.shared.l1_address, bridgehub: contracts.ecosystem_contracts.bridgehub_proxy_addr, - governance: wallets.governor.address, + governance: contracts_config.l1.governance_addr, erc20_bridge: contracts.bridges.erc20.l1_address, + da_validator_type: U256::from(chain_config.get_da_validator_type()? as u8), consensus_registry_owner: wallets.governor.address, }) } diff --git a/zkstack_cli/crates/config/src/forge_interface/deploy_l2_contracts/output.rs b/zkstack_cli/crates/config/src/forge_interface/deploy_l2_contracts/output.rs index 7b2b56c81548..e797686561ae 100644 --- a/zkstack_cli/crates/config/src/forge_interface/deploy_l2_contracts/output.rs +++ b/zkstack_cli/crates/config/src/forge_interface/deploy_l2_contracts/output.rs @@ -12,8 +12,7 @@ impl ZkStackConfig for TimestampAsserterOutput {} #[derive(Debug, Clone, Serialize, Deserialize)] pub struct InitializeBridgeOutput { - pub l2_shared_bridge_implementation: Address, - pub l2_shared_bridge_proxy: Address, + pub l2_da_validator_address: Address, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/zkstack_cli/crates/config/src/forge_interface/gateway_chain_upgrade/input.rs b/zkstack_cli/crates/config/src/forge_interface/gateway_chain_upgrade/input.rs index 9bedabf59a2d..41100c55a2ae 100644 --- a/zkstack_cli/crates/config/src/forge_interface/gateway_chain_upgrade/input.rs +++ b/zkstack_cli/crates/config/src/forge_interface/gateway_chain_upgrade/input.rs @@ -1,4 +1,3 @@ -/// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. use ethers::types::Address; use serde::{Deserialize, Serialize}; use zkstack_cli_types::L1BatchCommitmentMode; diff --git a/zkstack_cli/crates/config/src/forge_interface/gateway_chain_upgrade/mod.rs b/zkstack_cli/crates/config/src/forge_interface/gateway_chain_upgrade/mod.rs index cb22b3529e85..7d1a54844d0c 100644 --- a/zkstack_cli/crates/config/src/forge_interface/gateway_chain_upgrade/mod.rs +++ b/zkstack_cli/crates/config/src/forge_interface/gateway_chain_upgrade/mod.rs @@ -1,3 +1,2 @@ -/// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. pub mod input; pub mod output; diff --git a/zkstack_cli/crates/config/src/forge_interface/gateway_chain_upgrade/output.rs b/zkstack_cli/crates/config/src/forge_interface/gateway_chain_upgrade/output.rs index 57c82effcc47..94b6d25a52bf 100644 --- a/zkstack_cli/crates/config/src/forge_interface/gateway_chain_upgrade/output.rs +++ b/zkstack_cli/crates/config/src/forge_interface/gateway_chain_upgrade/output.rs @@ -1,4 +1,3 @@ -/// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. use ethers::types::Address; use serde::{Deserialize, Serialize}; diff --git a/zkstack_cli/crates/config/src/forge_interface/gateway_ecosystem_upgrade/input.rs b/zkstack_cli/crates/config/src/forge_interface/gateway_ecosystem_upgrade/input.rs index d71f327ede45..8bd300f50581 100644 --- a/zkstack_cli/crates/config/src/forge_interface/gateway_ecosystem_upgrade/input.rs +++ b/zkstack_cli/crates/config/src/forge_interface/gateway_ecosystem_upgrade/input.rs @@ -1,4 +1,3 @@ -/// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. use ethers::types::{Address, H256}; use serde::{Deserialize, Serialize}; use zksync_basic_types::L2ChainId; @@ -15,6 +14,7 @@ pub struct GatewayEcosystemUpgradeInput { pub testnet_verifier: bool, pub contracts: GatewayUpgradeContractsConfig, pub tokens: GatewayUpgradeTokensConfig, + pub governance_upgrade_timer_initial_delay: u64, } impl ZkStackConfig for GatewayEcosystemUpgradeInput {} @@ -33,6 +33,8 @@ impl GatewayEcosystemUpgradeInput { era_chain_id, testnet_verifier, owner_address: current_contracts_config.l1.governance_addr, + // TODO: for local testing, even 0 is fine - but before prod, we should load it from some configuration. + governance_upgrade_timer_initial_delay: 0, contracts: GatewayUpgradeContractsConfig { create2_factory_addr: initial_deployment_config.create2_factory_addr, create2_factory_salt: initial_deployment_config.create2_factory_salt, diff --git a/zkstack_cli/crates/config/src/forge_interface/gateway_ecosystem_upgrade/mod.rs b/zkstack_cli/crates/config/src/forge_interface/gateway_ecosystem_upgrade/mod.rs index cb22b3529e85..7d1a54844d0c 100644 --- a/zkstack_cli/crates/config/src/forge_interface/gateway_ecosystem_upgrade/mod.rs +++ b/zkstack_cli/crates/config/src/forge_interface/gateway_ecosystem_upgrade/mod.rs @@ -1,3 +1,2 @@ -/// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. pub mod input; pub mod output; diff --git a/zkstack_cli/crates/config/src/forge_interface/gateway_ecosystem_upgrade/output.rs b/zkstack_cli/crates/config/src/forge_interface/gateway_ecosystem_upgrade/output.rs index 8f030eb47b73..2aab8a1e5422 100644 --- a/zkstack_cli/crates/config/src/forge_interface/gateway_ecosystem_upgrade/output.rs +++ b/zkstack_cli/crates/config/src/forge_interface/gateway_ecosystem_upgrade/output.rs @@ -1,4 +1,3 @@ -/// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. use ethers::types::{Address, H256}; use serde::{Deserialize, Serialize}; use zksync_basic_types::web3::Bytes; @@ -19,6 +18,10 @@ pub struct GatewayEcosystemUpgradeOutput { pub contracts_config: GatewayEcosystemUpgradeContractsOutput, pub deployed_addresses: GatewayEcosystemUpgradeDeployedAddresses, + /// List of transactions that were executed during the upgrade. + /// This is added later by the zkstack and not present in the toml file that solidity creates. + #[serde(default)] + pub transactions: Vec, } impl FileConfigWithDefaultName for GatewayEcosystemUpgradeOutput { @@ -45,6 +48,12 @@ pub struct GatewayEcosystemUpgradeContractsOutput { pub recursion_circuits_set_vks_hash: H256, pub recursion_leaf_level_vk_hash: H256, pub recursion_node_level_vk_hash: H256, + + pub new_protocol_version: u64, + pub old_protocol_version: u64, + + pub old_validator_timelock: Address, + pub l1_legacy_shared_bridge: Address, } #[derive(Debug, Deserialize, Serialize, Clone)] diff --git a/zkstack_cli/crates/config/src/forge_interface/gateway_preparation/input.rs b/zkstack_cli/crates/config/src/forge_interface/gateway_preparation/input.rs index 263905d9fb35..6c4fc4d764a5 100644 --- a/zkstack_cli/crates/config/src/forge_interface/gateway_preparation/input.rs +++ b/zkstack_cli/crates/config/src/forge_interface/gateway_preparation/input.rs @@ -1,4 +1,3 @@ -/// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. use ethers::utils::hex; use serde::{Deserialize, Serialize}; use zksync_basic_types::{web3::Bytes, Address}; diff --git a/zkstack_cli/crates/config/src/forge_interface/gateway_preparation/mod.rs b/zkstack_cli/crates/config/src/forge_interface/gateway_preparation/mod.rs index cb22b3529e85..7d1a54844d0c 100644 --- a/zkstack_cli/crates/config/src/forge_interface/gateway_preparation/mod.rs +++ b/zkstack_cli/crates/config/src/forge_interface/gateway_preparation/mod.rs @@ -1,3 +1,2 @@ -/// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. pub mod input; pub mod output; diff --git a/zkstack_cli/crates/config/src/forge_interface/gateway_preparation/output.rs b/zkstack_cli/crates/config/src/forge_interface/gateway_preparation/output.rs index 7d27725b2825..c201625be28b 100644 --- a/zkstack_cli/crates/config/src/forge_interface/gateway_preparation/output.rs +++ b/zkstack_cli/crates/config/src/forge_interface/gateway_preparation/output.rs @@ -1,4 +1,3 @@ -/// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. use serde::{Deserialize, Serialize}; use zksync_basic_types::{Address, H256}; diff --git a/zkstack_cli/crates/config/src/forge_interface/register_chain/input.rs b/zkstack_cli/crates/config/src/forge_interface/register_chain/input.rs index 1e6a31009506..f44b8c1f50c3 100644 --- a/zkstack_cli/crates/config/src/forge_interface/register_chain/input.rs +++ b/zkstack_cli/crates/config/src/forge_interface/register_chain/input.rs @@ -2,38 +2,52 @@ use ethers::types::Address; use rand::Rng; use serde::{Deserialize, Serialize}; use zkstack_cli_types::L1BatchCommitmentMode; -use zksync_basic_types::L2ChainId; +use zksync_basic_types::{L2ChainId, H256}; use crate::{traits::ZkStackConfig, ChainConfig, ContractsConfig}; +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct RegisterChainL1Config { + contracts_config: Contracts, + deployed_addresses: DeployedAddresses, + chain: ChainL1Config, + owner_address: Address, + governance: Address, + create2_factory_address: Address, + create2_salt: H256, + initialize_legacy_bridge: bool, +} + #[derive(Debug, Deserialize, Serialize, Clone)] struct Bridgehub { bridgehub_proxy_addr: Address, } +#[derive(Debug, Deserialize, Serialize, Clone)] +struct Bridges { + shared_bridge_proxy_addr: Address, + l1_nullifier_proxy_addr: Address, + erc20_bridge_proxy_addr: Address, +} + #[derive(Debug, Deserialize, Serialize, Clone)] struct StateTransition { - state_transition_proxy_addr: Address, + chain_type_manager_proxy_addr: Address, } #[derive(Debug, Deserialize, Serialize, Clone)] struct DeployedAddresses { state_transition: StateTransition, bridgehub: Bridgehub, + bridges: Bridges, validator_timelock_addr: Address, + native_token_vault_addr: Address, } #[derive(Debug, Deserialize, Serialize, Clone)] struct Contracts { diamond_cut_data: String, -} - -#[derive(Debug, Deserialize, Serialize, Clone)] -pub struct RegisterChainL1Config { - contracts_config: Contracts, - deployed_addresses: DeployedAddresses, - chain: ChainL1Config, - owner_address: Address, + force_deployments_data: String, } #[derive(Debug, Deserialize, Serialize, Clone)] @@ -55,21 +69,39 @@ impl ZkStackConfig for RegisterChainL1Config {} impl RegisterChainL1Config { pub fn new(chain_config: &ChainConfig, contracts: &ContractsConfig) -> anyhow::Result { + let initialize_legacy_bridge = chain_config.legacy_bridge.unwrap_or_default(); let wallets_config = chain_config.get_wallets_config()?; Ok(Self { contracts_config: Contracts { diamond_cut_data: contracts.ecosystem_contracts.diamond_cut_data.clone(), + force_deployments_data: contracts + .ecosystem_contracts + .force_deployments_data + .clone() + .expect("force_deployment_data"), }, deployed_addresses: DeployedAddresses { state_transition: StateTransition { - state_transition_proxy_addr: contracts + chain_type_manager_proxy_addr: contracts .ecosystem_contracts .state_transition_proxy_addr, }, bridgehub: Bridgehub { bridgehub_proxy_addr: contracts.ecosystem_contracts.bridgehub_proxy_addr, }, + bridges: Bridges { + shared_bridge_proxy_addr: contracts.bridges.shared.l1_address, + l1_nullifier_proxy_addr: contracts + .bridges + .l1_nullifier_addr + .expect("l1_nullifier_addr"), + erc20_bridge_proxy_addr: contracts.bridges.erc20.l1_address, + }, validator_timelock_addr: contracts.ecosystem_contracts.validator_timelock_addr, + native_token_vault_addr: contracts + .ecosystem_contracts + .native_token_vault_addr + .expect("native_token_vault_addr"), }, chain: ChainL1Config { chain_chain_id: chain_config.chain_id, @@ -88,6 +120,10 @@ impl RegisterChainL1Config { allow_evm_emulator: chain_config.evm_emulator, }, owner_address: wallets_config.governor.address, + governance: contracts.l1.governance_addr, + create2_factory_address: contracts.create2_factory_addr, + create2_salt: H256::random(), + initialize_legacy_bridge, }) } } diff --git a/zkstack_cli/crates/config/src/forge_interface/register_chain/output.rs b/zkstack_cli/crates/config/src/forge_interface/register_chain/output.rs index a3e23f7bae42..951f36aa9fa8 100644 --- a/zkstack_cli/crates/config/src/forge_interface/register_chain/output.rs +++ b/zkstack_cli/crates/config/src/forge_interface/register_chain/output.rs @@ -8,6 +8,9 @@ pub struct RegisterChainOutput { pub diamond_proxy_addr: Address, pub governance_addr: Address, pub chain_admin_addr: Address, + pub l2_legacy_shared_bridge_addr: Option
, + pub access_control_restriction_addr: Address, + pub chain_proxy_admin_addr: Address, } impl ZkStackConfig for RegisterChainOutput {} diff --git a/zkstack_cli/crates/config/src/forge_interface/script_params.rs b/zkstack_cli/crates/config/src/forge_interface/script_params.rs index eb693c83a54c..b7496540ab18 100644 --- a/zkstack_cli/crates/config/src/forge_interface/script_params.rs +++ b/zkstack_cli/crates/config/src/forge_interface/script_params.rs @@ -39,9 +39,9 @@ pub const DEPLOY_L2_CONTRACTS_SCRIPT_PARAMS: ForgeScriptParams = ForgeScriptPara }; pub const REGISTER_CHAIN_SCRIPT_PARAMS: ForgeScriptParams = ForgeScriptParams { - input: "script-config/register-hyperchain.toml", - output: "script-out/output-register-hyperchain.toml", - script_path: "deploy-scripts/RegisterHyperchain.s.sol", + input: "script-config/register-zk-chain.toml", + output: "script-out/output-register-zk-chain.toml", + script_path: "deploy-scripts/RegisterZKChain.s.sol", }; pub const DEPLOY_ERC20_SCRIPT_PARAMS: ForgeScriptParams = ForgeScriptParams { @@ -74,39 +74,33 @@ pub const ENABLE_EVM_EMULATOR_PARAMS: ForgeScriptParams = ForgeScriptParams { script_path: "deploy-scripts/EnableEvmEmulator.s.sol", }; -// TODO(EVM-927): the following script does not work without gateway contracts. pub const DEPLOY_GATEWAY_CTM: ForgeScriptParams = ForgeScriptParams { input: "script-config/config-deploy-gateway-ctm.toml", output: "script-out/output-deploy-gateway-ctm.toml", script_path: "deploy-scripts/GatewayCTMFromL1.s.sol", }; -// TODO(EVM-927): the following script does not work without gateway contracts. pub const GATEWAY_PREPARATION: ForgeScriptParams = ForgeScriptParams { input: "script-config/gateway-preparation-l1.toml", output: "script-out/output-gateway-preparation-l1.toml", script_path: "deploy-scripts/GatewayPreparation.s.sol", }; -// TODO(EVM-927): the following script does not work without gateway contracts. pub const GATEWAY_GOVERNANCE_TX_PATH1: &str = "contracts/l1-contracts/script-out/gateway-deploy-governance-txs-1.json"; -// TODO(EVM-927): the following script does not work without gateway contracts. pub const GATEWAY_UPGRADE_ECOSYSTEM_PARAMS: ForgeScriptParams = ForgeScriptParams { input: "script-config/gateway-upgrade-ecosystem.toml", output: "script-out/gateway-upgrade-ecosystem.toml", script_path: "deploy-scripts/upgrade/EcosystemUpgrade.s.sol", }; -// TODO(EVM-927): the following script does not work without gateway contracts. pub const GATEWAY_UPGRADE_CHAIN_PARAMS: ForgeScriptParams = ForgeScriptParams { input: "script-config/gateway-upgrade-chain.toml", output: "script-out/gateway-upgrade-chain.toml", script_path: "deploy-scripts/upgrade/ChainUpgrade.s.sol", }; -// TODO(EVM-927): the following script does not work without gateway contracts. pub const FINALIZE_UPGRADE_SCRIPT_PARAMS: ForgeScriptParams = ForgeScriptParams { input: "script-config/gateway-finalize-upgrade.toml", output: "script-out/gateway-finalize-upgrade.toml", diff --git a/zkstack_cli/crates/config/src/forge_interface/setup_legacy_bridge/mod.rs b/zkstack_cli/crates/config/src/forge_interface/setup_legacy_bridge/mod.rs index 201cf86b734b..aa0764864606 100644 --- a/zkstack_cli/crates/config/src/forge_interface/setup_legacy_bridge/mod.rs +++ b/zkstack_cli/crates/config/src/forge_interface/setup_legacy_bridge/mod.rs @@ -8,6 +8,8 @@ pub struct SetupLegacyBridgeInput { pub bridgehub: Address, pub diamond_proxy: Address, pub shared_bridge_proxy: Address, + pub l1_nullifier_proxy: Address, + pub l1_native_token_vault: Address, pub transparent_proxy_admin: Address, pub erc20bridge_proxy: Address, pub token_weth_address: Address, diff --git a/zkstack_cli/crates/config/src/gateway.rs b/zkstack_cli/crates/config/src/gateway.rs index 0bdbcdf25475..67b5ad327cc2 100644 --- a/zkstack_cli/crates/config/src/gateway.rs +++ b/zkstack_cli/crates/config/src/gateway.rs @@ -1,4 +1,3 @@ -/// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. use ethers::utils::hex; use zksync_config::configs::{gateway::GatewayChainConfig, GatewayConfig}; diff --git a/zkstack_cli/crates/types/src/l1_network.rs b/zkstack_cli/crates/types/src/l1_network.rs index cc7b47147548..609af7ef3e7c 100644 --- a/zkstack_cli/crates/types/src/l1_network.rs +++ b/zkstack_cli/crates/types/src/l1_network.rs @@ -1,4 +1,7 @@ +use std::str::FromStr; + use clap::ValueEnum; +use ethers::types::Address; use serde::{Deserialize, Serialize}; use strum::EnumIter; @@ -35,4 +38,14 @@ impl L1Network { L1Network::Mainnet => 1, } } + + pub fn avail_l1_da_validator_addr(&self) -> Option
{ + match self { + L1Network::Localhost => None, + L1Network::Sepolia | L1Network::Holesky => { + Some(Address::from_str("0xd99d6569785547ac72150d0309aeDb30C7871b51").unwrap()) + } + L1Network::Mainnet => None, // TODO: add mainnet address after it is known + } + } } diff --git a/zkstack_cli/crates/zkstack/Cargo.toml b/zkstack_cli/crates/zkstack/Cargo.toml index 96d1dbf25be6..169fe593ba14 100644 --- a/zkstack_cli/crates/zkstack/Cargo.toml +++ b/zkstack_cli/crates/zkstack/Cargo.toml @@ -43,11 +43,13 @@ zksync_consensus_roles.workspace = true zksync_consensus_crypto.workspace = true zksync_protobuf.workspace = true zksync_protobuf_config.workspace = true -prost.workspace = true -reqwest = "0.12.8" zksync_types.workspace = true zksync_web3_decl.workspace = true zksync_system_constants.workspace = true +zksync_eth_client.workspace = true +zksync_contracts.workspace = true +prost.workspace = true +reqwest = "0.12.8" [dev-dependencies] rand.workspace = true @@ -60,3 +62,8 @@ dirs.workspace = true ethers.workspace = true xshell.workspace = true zksync_protobuf_build.workspace = true + +[features] +# Features that allows gateway-chain related actions. +# These should be available for outside users until stabilized. +gateway = [] diff --git a/zkstack_cli/crates/zkstack/completion/_zkstack.zsh b/zkstack_cli/crates/zkstack/completion/_zkstack.zsh index 3b0bed93eb4f..9c42f83f0020 100644 --- a/zkstack_cli/crates/zkstack/completion/_zkstack.zsh +++ b/zkstack_cli/crates/zkstack/completion/_zkstack.zsh @@ -108,6 +108,7 @@ _arguments "${_arguments_options[@]}" : \ '*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ '--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ +'--zksync[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -133,13 +134,17 @@ _arguments "${_arguments_options[@]}" : \ '-o+[Enable Grafana]' \ '--observability=[Enable Grafana]' \ '--update-submodules=[]:UPDATE_SUBMODULES:(true false)' \ +'--validium-type=[Type of the Validium network]:VALIDIUM_TYPE:(no-da avail)' \ +'--support-l2-legacy-shared-bridge-test=[]' \ '--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ +'--zksync[]' \ '-d[]' \ '--dont-drop[]' \ '--ecosystem-only[Initialize ecosystem only and skip chain initialization (chain can be initialized later with \`chain init\` subcommand)]' \ '--dev[Use defaults for all options and flags. Suitable for local development]' \ '--no-port-reallocation[Do not reallocate ports]' \ +'--skip-contract-compilation-override[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -269,6 +274,7 @@ _arguments "${_arguments_options[@]}" : \ '--l1-rpc-url=[L1 RPC URL]:L1_RPC_URL:_default' \ '--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ +'--zksync[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -289,8 +295,10 @@ _arguments "${_arguments_options[@]}" : \ '--deploy-paymaster=[]' \ '--l1-rpc-url=[L1 RPC URL]:L1_RPC_URL:_default' \ '--update-submodules=[]:UPDATE_SUBMODULES:(true false)' \ +'--validium-type=[Type of the Validium network]:VALIDIUM_TYPE:(no-da avail)' \ '--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ +'--zksync[]' \ '-d[]' \ '--dont-drop[]' \ '--no-port-reallocation[Do not reallocate ports]' \ @@ -448,6 +456,7 @@ _arguments "${_arguments_options[@]}" : \ '*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ '--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ +'--zksync[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -465,6 +474,7 @@ _arguments "${_arguments_options[@]}" : \ '*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ '--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ +'--zksync[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -482,23 +492,7 @@ _arguments "${_arguments_options[@]}" : \ '*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ '--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ -'-v[Verbose mode]' \ -'--verbose[Verbose mode]' \ -'--ignore-prerequisites[Ignores prerequisites checks]' \ -'-h[Print help (see more with '\''--help'\'')]' \ -'--help[Print help (see more with '\''--help'\'')]' \ -&& ret=0 -;; -(initialize-bridges) -_arguments "${_arguments_options[@]}" : \ -'--verify=[Verify deployed contracts]' \ -'--verifier=[Verifier to use]:VERIFIER:(etherscan sourcify blockscout oklink)' \ -'--verifier-url=[Verifier URL, if using a custom provider]:VERIFIER_URL:_default' \ -'--verifier-api-key=[Verifier API key]:VERIFIER_API_KEY:_default' \ -'*-a+[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ -'*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ -'--chain=[Chain to use]:CHAIN:_default' \ -'--resume[]' \ +'--zksync[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -516,6 +510,7 @@ _arguments "${_arguments_options[@]}" : \ '*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ '--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ +'--zksync[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -533,6 +528,7 @@ _arguments "${_arguments_options[@]}" : \ '*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ '--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ +'--zksync[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -550,6 +546,7 @@ _arguments "${_arguments_options[@]}" : \ '*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ '--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ +'--zksync[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -567,6 +564,7 @@ _arguments "${_arguments_options[@]}" : \ '*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ '--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ +'--zksync[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -584,6 +582,7 @@ _arguments "${_arguments_options[@]}" : \ '*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ '--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ +'--zksync[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -601,6 +600,7 @@ _arguments "${_arguments_options[@]}" : \ '*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ '--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ +'--zksync[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -618,6 +618,7 @@ _arguments "${_arguments_options[@]}" : \ '*--additional-args=[List of additional arguments that can be passed through the CLI]:ADDITIONAL_ARGS:_default' \ '--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ +'--zksync[]' \ '-v[Verbose mode]' \ '--verbose[Verbose mode]' \ '--ignore-prerequisites[Ignores prerequisites checks]' \ @@ -701,10 +702,6 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ && ret=0 ;; -(initialize-bridges) -_arguments "${_arguments_options[@]}" : \ -&& ret=0 -;; (deploy-consensus-registry) _arguments "${_arguments_options[@]}" : \ && ret=0 @@ -1496,6 +1493,7 @@ esac (contracts) _arguments "${_arguments_options[@]}" : \ '--l1-contracts=[Build L1 contracts]' \ +'--l1-da-contracts=[Build L1 DA contracts]' \ '--l2-contracts=[Build L2 contracts]' \ '--system-contracts=[Build system contracts]' \ '--chain=[Chain to use]:CHAIN:_default' \ @@ -2711,10 +2709,6 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ && ret=0 ;; -(initialize-bridges) -_arguments "${_arguments_options[@]}" : \ -&& ret=0 -;; (deploy-consensus-registry) _arguments "${_arguments_options[@]}" : \ && ret=0 @@ -3264,7 +3258,6 @@ _zkstack__chain_commands() { 'register-chain:Register a new chain on L1 (executed by L1 governor). This command deploys and configures Governance, ChainAdmin, and DiamondProxy contracts, registers chain with BridgeHub and sets pending admin for DiamondProxy. Note\: After completion, L2 governor can accept ownership by running \`accept-chain-ownership\`' \ 'deploy-l2-contracts:Deploy all L2 contracts (executed by L1 governor)' \ 'accept-chain-ownership:Accept ownership of L2 chain (executed by L2 governor). This command should be run after \`register-chain\` to accept ownership of newly created DiamondProxy contract' \ -'initialize-bridges:Initialize bridges on L2' \ 'deploy-consensus-registry:Deploy L2 consensus registry' \ 'deploy-multicall3:Deploy L2 multicall3' \ 'deploy-timestamp-asserter:Deploy L2 TimestampAsserter' \ @@ -3379,7 +3372,6 @@ _zkstack__chain__help_commands() { 'register-chain:Register a new chain on L1 (executed by L1 governor). This command deploys and configures Governance, ChainAdmin, and DiamondProxy contracts, registers chain with BridgeHub and sets pending admin for DiamondProxy. Note\: After completion, L2 governor can accept ownership by running \`accept-chain-ownership\`' \ 'deploy-l2-contracts:Deploy all L2 contracts (executed by L1 governor)' \ 'accept-chain-ownership:Accept ownership of L2 chain (executed by L2 governor). This command should be run after \`register-chain\` to accept ownership of newly created DiamondProxy contract' \ -'initialize-bridges:Initialize bridges on L2' \ 'deploy-consensus-registry:Deploy L2 consensus registry' \ 'deploy-multicall3:Deploy L2 multicall3' \ 'deploy-timestamp-asserter:Deploy L2 TimestampAsserter' \ @@ -3476,11 +3468,6 @@ _zkstack__chain__help__init__configs_commands() { local commands; commands=() _describe -t commands 'zkstack chain help init configs commands' commands "$@" } -(( $+functions[_zkstack__chain__help__initialize-bridges_commands] )) || -_zkstack__chain__help__initialize-bridges_commands() { - local commands; commands=() - _describe -t commands 'zkstack chain help initialize-bridges commands' commands "$@" -} (( $+functions[_zkstack__chain__help__register-chain_commands] )) || _zkstack__chain__help__register-chain_commands() { local commands; commands=() @@ -3522,11 +3509,6 @@ _zkstack__chain__init__help__help_commands() { local commands; commands=() _describe -t commands 'zkstack chain init help help commands' commands "$@" } -(( $+functions[_zkstack__chain__initialize-bridges_commands] )) || -_zkstack__chain__initialize-bridges_commands() { - local commands; commands=() - _describe -t commands 'zkstack chain initialize-bridges commands' commands "$@" -} (( $+functions[_zkstack__chain__register-chain_commands] )) || _zkstack__chain__register-chain_commands() { local commands; commands=() @@ -4703,7 +4685,6 @@ _zkstack__help__chain_commands() { 'register-chain:Register a new chain on L1 (executed by L1 governor). This command deploys and configures Governance, ChainAdmin, and DiamondProxy contracts, registers chain with BridgeHub and sets pending admin for DiamondProxy. Note\: After completion, L2 governor can accept ownership by running \`accept-chain-ownership\`' \ 'deploy-l2-contracts:Deploy all L2 contracts (executed by L1 governor)' \ 'accept-chain-ownership:Accept ownership of L2 chain (executed by L2 governor). This command should be run after \`register-chain\` to accept ownership of newly created DiamondProxy contract' \ -'initialize-bridges:Initialize bridges on L2' \ 'deploy-consensus-registry:Deploy L2 consensus registry' \ 'deploy-multicall3:Deploy L2 multicall3' \ 'deploy-timestamp-asserter:Deploy L2 TimestampAsserter' \ @@ -4794,11 +4775,6 @@ _zkstack__help__chain__init__configs_commands() { local commands; commands=() _describe -t commands 'zkstack help chain init configs commands' commands "$@" } -(( $+functions[_zkstack__help__chain__initialize-bridges_commands] )) || -_zkstack__help__chain__initialize-bridges_commands() { - local commands; commands=() - _describe -t commands 'zkstack help chain initialize-bridges commands' commands "$@" -} (( $+functions[_zkstack__help__chain__register-chain_commands] )) || _zkstack__help__chain__register-chain_commands() { local commands; commands=() diff --git a/zkstack_cli/crates/zkstack/completion/zkstack.fish b/zkstack_cli/crates/zkstack/completion/zkstack.fish index b9d4e58f6322..8ba5afc5a49f 100644 --- a/zkstack_cli/crates/zkstack/completion/zkstack.fish +++ b/zkstack_cli/crates/zkstack/completion/zkstack.fish @@ -113,6 +113,7 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_se complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -l resume +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -l zksync complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from build-transactions" -s h -l help -d 'Print help (see more with \'--help\')' @@ -139,12 +140,18 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_se false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l update-submodules -r -f -a "true\t'' false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l validium-type -d 'Type of the Validium network' -r -f -a "no-da\t'' +avail\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l support-l2-legacy-shared-bridge-test -r -f -a "true\t'' +false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l resume +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l zksync complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -s d -l dont-drop complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l ecosystem-only -d 'Initialize ecosystem only and skip chain initialization (chain can be initialized later with `chain init` subcommand)' complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l dev -d 'Use defaults for all options and flags. Suitable for local development' complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l no-port-reallocation -d 'Do not reallocate ports' +complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l skip-contract-compilation-override complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -s h -l help -d 'Print help (see more with \'--help\')' @@ -162,26 +169,25 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_se complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from help" -f -a "change-default-chain" -d 'Change the default chain' complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from help" -f -a "setup-observability" -d 'Setup observability for the ecosystem, downloading Grafana dashboards from the era-observability repo' complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -l chain -d 'Chain to use' -r -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -s v -l verbose -d 'Verbose mode' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -l ignore-prerequisites -d 'Ignores prerequisites checks' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -s h -l help -d 'Print help' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "create" -d 'Create a new chain, setting the necessary configurations for later initialization' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "build-transactions" -d 'Create unsigned transactions for chain deployment' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "init" -d 'Initialize chain, deploying necessary contracts and performing on-chain operations' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "genesis" -d 'Run server genesis' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "register-chain" -d 'Register a new chain on L1 (executed by L1 governor). This command deploys and configures Governance, ChainAdmin, and DiamondProxy contracts, registers chain with BridgeHub and sets pending admin for DiamondProxy. Note: After completion, L2 governor can accept ownership by running `accept-chain-ownership`' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "deploy-l2-contracts" -d 'Deploy all L2 contracts (executed by L1 governor)' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "accept-chain-ownership" -d 'Accept ownership of L2 chain (executed by L2 governor). This command should be run after `register-chain` to accept ownership of newly created DiamondProxy contract' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "initialize-bridges" -d 'Initialize bridges on L2' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "deploy-consensus-registry" -d 'Deploy L2 consensus registry' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "deploy-multicall3" -d 'Deploy L2 multicall3' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "deploy-timestamp-asserter" -d 'Deploy L2 TimestampAsserter' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "deploy-upgrader" -d 'Deploy Default Upgrader' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "deploy-paymaster" -d 'Deploy paymaster smart contract' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "update-token-multiplier-setter" -d 'Update Token Multiplier Setter address on L1' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "enable-evm-emulator" -d 'Enable EVM emulation on chain (Not supported yet)' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -l chain -d 'Chain to use' -r +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -s v -l verbose -d 'Verbose mode' +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -l ignore-prerequisites -d 'Ignores prerequisites checks' +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -s h -l help -d 'Print help' +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "create" -d 'Create a new chain, setting the necessary configurations for later initialization' +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "build-transactions" -d 'Create unsigned transactions for chain deployment' +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "init" -d 'Initialize chain, deploying necessary contracts and performing on-chain operations' +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "genesis" -d 'Run server genesis' +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "register-chain" -d 'Register a new chain on L1 (executed by L1 governor). This command deploys and configures Governance, ChainAdmin, and DiamondProxy contracts, registers chain with BridgeHub and sets pending admin for DiamondProxy. Note: After completion, L2 governor can accept ownership by running `accept-chain-ownership`' +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "deploy-l2-contracts" -d 'Deploy all L2 contracts (executed by L1 governor)' +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "accept-chain-ownership" -d 'Accept ownership of L2 chain (executed by L2 governor). This command should be run after `register-chain` to accept ownership of newly created DiamondProxy contract' +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "deploy-consensus-registry" -d 'Deploy L2 consensus registry' +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "deploy-multicall3" -d 'Deploy L2 multicall3' +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "deploy-timestamp-asserter" -d 'Deploy L2 TimestampAsserter' +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "deploy-upgrader" -d 'Deploy Default Upgrader' +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "deploy-paymaster" -d 'Deploy paymaster smart contract' +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "update-token-multiplier-setter" -d 'Update Token Multiplier Setter address on L1' +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "enable-evm-emulator" -d 'Enable EVM emulation on chain (Not supported yet)' +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and not __fish_seen_subcommand_from create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l chain-name -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l chain-id -d 'Chain ID' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from create" -l prover-mode -d 'Prover options' -r -f -a "no-proofs\t'' @@ -220,6 +226,7 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from build-transactions" -l l1-rpc-url -d 'L1 RPC URL' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from build-transactions" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from build-transactions" -l resume +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from build-transactions" -l zksync complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from build-transactions" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from build-transactions" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from build-transactions" -s h -l help -d 'Print help (see more with \'--help\')' @@ -239,8 +246,11 @@ false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l l1-rpc-url -d 'L1 RPC URL' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l update-submodules -r -f -a "true\t'' false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l validium-type -d 'Type of the Validium network' -r -f -a "no-da\t'' +avail\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l resume +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l zksync complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -s d -l dont-drop complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l no-port-reallocation -d 'Do not reallocate ports' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l dev -d 'Use defaults for all options and flags. Suitable for local development' @@ -271,6 +281,7 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from register-chain" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from register-chain" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from register-chain" -l resume +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from register-chain" -l zksync complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from register-chain" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from register-chain" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from register-chain" -s h -l help -d 'Print help (see more with \'--help\')' @@ -285,6 +296,7 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-l2-contracts" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-l2-contracts" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-l2-contracts" -l resume +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-l2-contracts" -l zksync complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-l2-contracts" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-l2-contracts" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-l2-contracts" -s h -l help -d 'Print help (see more with \'--help\')' @@ -299,23 +311,10 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from accept-chain-ownership" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from accept-chain-ownership" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from accept-chain-ownership" -l resume +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from accept-chain-ownership" -l zksync complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from accept-chain-ownership" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from accept-chain-ownership" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from accept-chain-ownership" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -l verify -d 'Verify deployed contracts' -r -f -a "true\t'' -false\t''" -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -l verifier -d 'Verifier to use' -r -f -a "etherscan\t'' -sourcify\t'' -blockscout\t'' -oklink\t''" -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -l verifier-url -d 'Verifier URL, if using a custom provider' -r -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -l verifier-api-key -d 'Verifier API key' -r -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -l chain -d 'Chain to use' -r -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -l resume -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -s v -l verbose -d 'Verbose mode' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -l ignore-prerequisites -d 'Ignores prerequisites checks' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from initialize-bridges" -s h -l help -d 'Print help (see more with \'--help\')' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-consensus-registry" -l verify -d 'Verify deployed contracts' -r -f -a "true\t'' false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-consensus-registry" -l verifier -d 'Verifier to use' -r -f -a "etherscan\t'' @@ -327,6 +326,7 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-consensus-registry" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-consensus-registry" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-consensus-registry" -l resume +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-consensus-registry" -l zksync complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-consensus-registry" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-consensus-registry" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-consensus-registry" -s h -l help -d 'Print help (see more with \'--help\')' @@ -341,6 +341,7 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-multicall3" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-multicall3" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-multicall3" -l resume +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-multicall3" -l zksync complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-multicall3" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-multicall3" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-multicall3" -s h -l help -d 'Print help (see more with \'--help\')' @@ -355,6 +356,7 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-timestamp-asserter" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-timestamp-asserter" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-timestamp-asserter" -l resume +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-timestamp-asserter" -l zksync complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-timestamp-asserter" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-timestamp-asserter" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-timestamp-asserter" -s h -l help -d 'Print help (see more with \'--help\')' @@ -369,6 +371,7 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-upgrader" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-upgrader" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-upgrader" -l resume +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-upgrader" -l zksync complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-upgrader" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-upgrader" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-upgrader" -s h -l help -d 'Print help (see more with \'--help\')' @@ -383,6 +386,7 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-paymaster" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-paymaster" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-paymaster" -l resume +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-paymaster" -l zksync complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-paymaster" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-paymaster" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from deploy-paymaster" -s h -l help -d 'Print help (see more with \'--help\')' @@ -397,6 +401,7 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from update-token-multiplier-setter" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from update-token-multiplier-setter" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from update-token-multiplier-setter" -l resume +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from update-token-multiplier-setter" -l zksync complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from update-token-multiplier-setter" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from update-token-multiplier-setter" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from update-token-multiplier-setter" -s h -l help -d 'Print help (see more with \'--help\')' @@ -411,6 +416,7 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from enable-evm-emulator" -s a -l additional-args -d 'List of additional arguments that can be passed through the CLI' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from enable-evm-emulator" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from enable-evm-emulator" -l resume +complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from enable-evm-emulator" -l zksync complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from enable-evm-emulator" -s v -l verbose -d 'Verbose mode' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from enable-evm-emulator" -l ignore-prerequisites -d 'Ignores prerequisites checks' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from enable-evm-emulator" -s h -l help -d 'Print help (see more with \'--help\')' @@ -421,7 +427,6 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from help" -f -a "register-chain" -d 'Register a new chain on L1 (executed by L1 governor). This command deploys and configures Governance, ChainAdmin, and DiamondProxy contracts, registers chain with BridgeHub and sets pending admin for DiamondProxy. Note: After completion, L2 governor can accept ownership by running `accept-chain-ownership`' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from help" -f -a "deploy-l2-contracts" -d 'Deploy all L2 contracts (executed by L1 governor)' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from help" -f -a "accept-chain-ownership" -d 'Accept ownership of L2 chain (executed by L2 governor). This command should be run after `register-chain` to accept ownership of newly created DiamondProxy contract' -complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from help" -f -a "initialize-bridges" -d 'Initialize bridges on L2' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from help" -f -a "deploy-consensus-registry" -d 'Deploy L2 consensus registry' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from help" -f -a "deploy-multicall3" -d 'Deploy L2 multicall3' complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from help" -f -a "deploy-timestamp-asserter" -d 'Deploy L2 TimestampAsserter' @@ -521,6 +526,8 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_sub complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from prover" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from contracts" -l l1-contracts -d 'Build L1 contracts' -r -f -a "true\t'' false\t''" +complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from contracts" -l l1-da-contracts -d 'Build L1 DA contracts' -r -f -a "true\t'' +false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from contracts" -l l2-contracts -d 'Build L2 contracts' -r -f -a "true\t'' false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand dev; and __fish_seen_subcommand_from contracts" -l system-contracts -d 'Build system contracts' -r -f -a "true\t'' @@ -898,7 +905,6 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand help; and __fish_seen_su complete -c zkstack -n "__fish_zkstack_using_subcommand help; and __fish_seen_subcommand_from chain" -f -a "register-chain" -d 'Register a new chain on L1 (executed by L1 governor). This command deploys and configures Governance, ChainAdmin, and DiamondProxy contracts, registers chain with BridgeHub and sets pending admin for DiamondProxy. Note: After completion, L2 governor can accept ownership by running `accept-chain-ownership`' complete -c zkstack -n "__fish_zkstack_using_subcommand help; and __fish_seen_subcommand_from chain" -f -a "deploy-l2-contracts" -d 'Deploy all L2 contracts (executed by L1 governor)' complete -c zkstack -n "__fish_zkstack_using_subcommand help; and __fish_seen_subcommand_from chain" -f -a "accept-chain-ownership" -d 'Accept ownership of L2 chain (executed by L2 governor). This command should be run after `register-chain` to accept ownership of newly created DiamondProxy contract' -complete -c zkstack -n "__fish_zkstack_using_subcommand help; and __fish_seen_subcommand_from chain" -f -a "initialize-bridges" -d 'Initialize bridges on L2' complete -c zkstack -n "__fish_zkstack_using_subcommand help; and __fish_seen_subcommand_from chain" -f -a "deploy-consensus-registry" -d 'Deploy L2 consensus registry' complete -c zkstack -n "__fish_zkstack_using_subcommand help; and __fish_seen_subcommand_from chain" -f -a "deploy-multicall3" -d 'Deploy L2 multicall3' complete -c zkstack -n "__fish_zkstack_using_subcommand help; and __fish_seen_subcommand_from chain" -f -a "deploy-timestamp-asserter" -d 'Deploy L2 TimestampAsserter' diff --git a/zkstack_cli/crates/zkstack/completion/zkstack.sh b/zkstack_cli/crates/zkstack/completion/zkstack.sh index ae934b0e5d3a..280863339c52 100644 --- a/zkstack_cli/crates/zkstack/completion/zkstack.sh +++ b/zkstack_cli/crates/zkstack/completion/zkstack.sh @@ -96,9 +96,6 @@ _zkstack() { zkstack__chain,init) cmd="zkstack__chain__init" ;; - zkstack__chain,initialize-bridges) - cmd="zkstack__chain__initialize__bridges" - ;; zkstack__chain,register-chain) cmd="zkstack__chain__register__chain" ;; @@ -162,9 +159,6 @@ _zkstack() { zkstack__chain__help,init) cmd="zkstack__chain__help__init" ;; - zkstack__chain__help,initialize-bridges) - cmd="zkstack__chain__help__initialize__bridges" - ;; zkstack__chain__help,register-chain) cmd="zkstack__chain__help__register__chain" ;; @@ -804,9 +798,6 @@ _zkstack() { zkstack__help__chain,init) cmd="zkstack__help__chain__init" ;; - zkstack__help__chain,initialize-bridges) - cmd="zkstack__help__chain__initialize__bridges" - ;; zkstack__help__chain,register-chain) cmd="zkstack__help__chain__register__chain" ;; @@ -1144,7 +1135,7 @@ _zkstack() { return 0 ;; zkstack__chain) - opts="-v -h --verbose --chain --ignore-prerequisites --help create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" + opts="-v -h --verbose --chain --ignore-prerequisites --help create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1162,7 +1153,7 @@ _zkstack() { return 0 ;; zkstack__chain__accept__chain__ownership) - opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --additional-args --verbose --chain --ignore-prerequisites --help" + opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --zksync --additional-args --verbose --chain --ignore-prerequisites --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1204,7 +1195,7 @@ _zkstack() { return 0 ;; zkstack__chain__build__transactions) - opts="-o -a -v -h --out --verify --verifier --verifier-url --verifier-api-key --resume --additional-args --l1-rpc-url --verbose --chain --ignore-prerequisites --help" + opts="-o -a -v -h --out --verify --verifier --verifier-url --verifier-api-key --resume --zksync --additional-args --l1-rpc-url --verbose --chain --ignore-prerequisites --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1335,7 +1326,7 @@ _zkstack() { return 0 ;; zkstack__chain__deploy__consensus__registry) - opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --additional-args --verbose --chain --ignore-prerequisites --help" + opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --zksync --additional-args --verbose --chain --ignore-prerequisites --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1377,7 +1368,7 @@ _zkstack() { return 0 ;; zkstack__chain__deploy__l2__contracts) - opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --additional-args --verbose --chain --ignore-prerequisites --help" + opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --zksync --additional-args --verbose --chain --ignore-prerequisites --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1419,7 +1410,7 @@ _zkstack() { return 0 ;; zkstack__chain__deploy__multicall3) - opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --additional-args --verbose --chain --ignore-prerequisites --help" + opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --zksync --additional-args --verbose --chain --ignore-prerequisites --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1461,7 +1452,7 @@ _zkstack() { return 0 ;; zkstack__chain__deploy__paymaster) - opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --additional-args --verbose --chain --ignore-prerequisites --help" + opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --zksync --additional-args --verbose --chain --ignore-prerequisites --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1503,7 +1494,7 @@ _zkstack() { return 0 ;; zkstack__chain__deploy__timestamp__asserter) - opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --additional-args --verbose --chain --ignore-prerequisites --help" + opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --zksync --additional-args --verbose --chain --ignore-prerequisites --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1545,7 +1536,7 @@ _zkstack() { return 0 ;; zkstack__chain__deploy__upgrader) - opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --additional-args --verbose --chain --ignore-prerequisites --help" + opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --zksync --additional-args --verbose --chain --ignore-prerequisites --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1587,7 +1578,7 @@ _zkstack() { return 0 ;; zkstack__chain__enable__evm__emulator) - opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --additional-args --verbose --chain --ignore-prerequisites --help" + opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --zksync --additional-args --verbose --chain --ignore-prerequisites --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1755,7 +1746,7 @@ _zkstack() { return 0 ;; zkstack__chain__help) - opts="create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" + opts="create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1992,20 +1983,6 @@ _zkstack() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; - zkstack__chain__help__initialize__bridges) - opts="" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; zkstack__chain__help__register__chain) opts="" if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then @@ -2035,7 +2012,7 @@ _zkstack() { return 0 ;; zkstack__chain__init) - opts="-a -d -v -h --verify --verifier --verifier-url --verifier-api-key --resume --additional-args --server-db-url --server-db-name --dont-drop --deploy-paymaster --l1-rpc-url --no-port-reallocation --update-submodules --dev --verbose --chain --ignore-prerequisites --help configs help" + opts="-a -d -v -h --verify --verifier --verifier-url --verifier-api-key --resume --zksync --additional-args --server-db-url --server-db-name --dont-drop --deploy-paymaster --l1-rpc-url --no-port-reallocation --update-submodules --dev --validium-type --verbose --chain --ignore-prerequisites --help configs help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -2085,6 +2062,10 @@ _zkstack() { COMPREPLY=($(compgen -W "true false" -- "${cur}")) return 0 ;; + --validium-type) + COMPREPLY=($(compgen -W "no-da avail" -- "${cur}")) + return 0 + ;; --chain) COMPREPLY=($(compgen -f "${cur}")) return 0 @@ -2168,50 +2149,8 @@ _zkstack() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; - zkstack__chain__initialize__bridges) - opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --additional-args --verbose --chain --ignore-prerequisites --help" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - --verify) - COMPREPLY=($(compgen -W "true false" -- "${cur}")) - return 0 - ;; - --verifier) - COMPREPLY=($(compgen -W "etherscan sourcify blockscout oklink" -- "${cur}")) - return 0 - ;; - --verifier-url) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --verifier-api-key) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --additional-args) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - -a) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --chain) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; zkstack__chain__register__chain) - opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --additional-args --verbose --chain --ignore-prerequisites --help" + opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --zksync --additional-args --verbose --chain --ignore-prerequisites --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -2253,7 +2192,7 @@ _zkstack() { return 0 ;; zkstack__chain__update__token__multiplier__setter) - opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --additional-args --verbose --chain --ignore-prerequisites --help" + opts="-a -v -h --verify --verifier --verifier-url --verifier-api-key --resume --zksync --additional-args --verbose --chain --ignore-prerequisites --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -2871,7 +2810,7 @@ _zkstack() { return 0 ;; zkstack__dev__contracts) - opts="-v -h --l1-contracts --l2-contracts --system-contracts --verbose --chain --ignore-prerequisites --help" + opts="-v -h --l1-contracts --l1-da-contracts --l2-contracts --system-contracts --verbose --chain --ignore-prerequisites --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -2881,6 +2820,10 @@ _zkstack() { COMPREPLY=($(compgen -W "true false" -- "${cur}")) return 0 ;; + --l1-da-contracts) + COMPREPLY=($(compgen -W "true false" -- "${cur}")) + return 0 + ;; --l2-contracts) COMPREPLY=($(compgen -W "true false" -- "${cur}")) return 0 @@ -4907,7 +4850,7 @@ _zkstack() { return 0 ;; zkstack__ecosystem__build__transactions) - opts="-o -a -v -h --sender --l1-rpc-url --out --verify --verifier --verifier-url --verifier-api-key --resume --additional-args --verbose --chain --ignore-prerequisites --help" + opts="-o -a -v -h --sender --l1-rpc-url --out --verify --verifier --verifier-url --verifier-api-key --resume --zksync --additional-args --verbose --chain --ignore-prerequisites --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -5181,7 +5124,7 @@ _zkstack() { return 0 ;; zkstack__ecosystem__init) - opts="-a -d -o -v -h --deploy-erc20 --deploy-ecosystem --ecosystem-contracts-path --l1-rpc-url --verify --verifier --verifier-url --verifier-api-key --resume --additional-args --deploy-paymaster --server-db-url --server-db-name --dont-drop --ecosystem-only --dev --observability --no-port-reallocation --update-submodules --verbose --chain --ignore-prerequisites --help" + opts="-a -d -o -v -h --deploy-erc20 --deploy-ecosystem --ecosystem-contracts-path --l1-rpc-url --verify --verifier --verifier-url --verifier-api-key --resume --zksync --additional-args --deploy-paymaster --server-db-url --server-db-name --dont-drop --ecosystem-only --dev --observability --no-port-reallocation --update-submodules --validium-type --support-l2-legacy-shared-bridge-test --skip-contract-compilation-override --verbose --chain --ignore-prerequisites --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -5251,6 +5194,14 @@ _zkstack() { COMPREPLY=($(compgen -W "true false" -- "${cur}")) return 0 ;; + --validium-type) + COMPREPLY=($(compgen -W "no-da avail" -- "${cur}")) + return 0 + ;; + --support-l2-legacy-shared-bridge-test) + COMPREPLY=($(compgen -W "true false" -- "${cur}")) + return 0 + ;; --chain) COMPREPLY=($(compgen -f "${cur}")) return 0 @@ -5701,7 +5652,7 @@ _zkstack() { return 0 ;; zkstack__help__chain) - opts="create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership initialize-bridges deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator" + opts="create build-transactions init genesis register-chain deploy-l2-contracts accept-chain-ownership deploy-consensus-registry deploy-multicall3 deploy-timestamp-asserter deploy-upgrader deploy-paymaster update-token-multiplier-setter enable-evm-emulator" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -5924,20 +5875,6 @@ _zkstack() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; - zkstack__help__chain__initialize__bridges) - opts="" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; zkstack__help__chain__register__chain) opts="" if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then diff --git a/zkstack_cli/crates/zkstack/src/accept_ownership.rs b/zkstack_cli/crates/zkstack/src/accept_ownership.rs index cde1a365e8a0..1123df0f2240 100644 --- a/zkstack_cli/crates/zkstack/src/accept_ownership.rs +++ b/zkstack_cli/crates/zkstack/src/accept_ownership.rs @@ -27,6 +27,12 @@ lazy_static! { parse_abi(&[ "function governanceAcceptOwner(address governor, address target) public", "function chainAdminAcceptAdmin(address admin, address target) public", + "function setDAValidatorPair(address chainAdmin, address target, address l1DaValidator, address l2DaValidator) public", + "function makePermanentRollup(address chainAdmin, address target) public", + "function governanceExecuteCalls(bytes calldata callsToExecute, address target) public", + "function adminExecuteUpgrade(bytes memory diamondCut, address adminAddr, address accessControlRestriction, address chainDiamondProxy)", + "function adminScheduleUpgrade(address adminAddr, address accessControlRestriction, uint256 newProtocolVersion, uint256 timestamp)", + "function updateValidator(address adminAddr,address accessControlRestriction,address validatorTimelock,uint256 chainId,address validatorAddress,bool addValidator) public" ]) .unwrap(), ); @@ -92,8 +98,6 @@ pub async fn accept_owner( accept_ownership(shell, governor, forge).await } -// TODO(EVM-927): this function does not work without the Gateway contracts. -#[allow(unused)] #[allow(clippy::too_many_arguments)] pub async fn set_da_validator_pair( shell: &Shell, @@ -106,9 +110,6 @@ pub async fn set_da_validator_pair( forge_args: &ForgeScriptArgs, l1_rpc_url: String, ) -> anyhow::Result<()> { - // TODO(EVM-927): this function does not work without the Gateway contracts. - anyhow::bail!("Gateway upgrade not supported yet!"); - // resume doesn't properly work here. let mut forge_args = forge_args.clone(); forge_args.resume = false; @@ -137,8 +138,6 @@ pub async fn set_da_validator_pair( accept_ownership(shell, governor, forge).await } -// TODO(EVM-927): this function does not work without the Gateway contracts. -#[allow(unused)] #[allow(clippy::too_many_arguments)] pub async fn make_permanent_rollup( shell: &Shell, @@ -149,9 +148,6 @@ pub async fn make_permanent_rollup( forge_args: &ForgeScriptArgs, l1_rpc_url: String, ) -> anyhow::Result<()> { - // TODO(EVM-927): this function does not work without the Gateway contracts. - anyhow::bail!("Gateway upgrade not supported yet!"); - // resume doesn't properly work here. let mut forge_args = forge_args.clone(); forge_args.resume = false; @@ -175,8 +171,6 @@ pub async fn make_permanent_rollup( accept_ownership(shell, governor, forge).await } -// TODO(EVM-927): this function does not work without the Gateway contracts. -#[allow(unused)] #[allow(clippy::too_many_arguments)] pub async fn governance_execute_calls( shell: &Shell, @@ -186,9 +180,6 @@ pub async fn governance_execute_calls( forge_args: &ForgeScriptArgs, l1_rpc_url: String, ) -> anyhow::Result<()> { - // TODO(EVM-927): this function does not work without the Gateway contracts. - anyhow::bail!("Gateway upgrade not supported yet!"); - // resume doesn't properly work here. let mut forge_args = forge_args.clone(); forge_args.resume = false; @@ -214,8 +205,6 @@ pub async fn governance_execute_calls( accept_ownership(shell, governor, forge).await } -// TODO(EVM-927): this function does not work without the Gateway contracts. -#[allow(unused)] #[allow(clippy::too_many_arguments)] pub async fn admin_execute_upgrade( shell: &Shell, @@ -226,9 +215,6 @@ pub async fn admin_execute_upgrade( forge_args: &ForgeScriptArgs, l1_rpc_url: String, ) -> anyhow::Result<()> { - // TODO(EVM-927): this function does not work without the Gateway contracts. - anyhow::bail!("Gateway upgrade not supported yet!"); - // resume doesn't properly work here. let mut forge_args = forge_args.clone(); forge_args.resume = false; @@ -264,8 +250,6 @@ pub async fn admin_execute_upgrade( accept_ownership(shell, governor, forge).await } -// TODO(EVM-927): this function does not work without the Gateway contracts. -#[allow(unused)] #[allow(clippy::too_many_arguments)] pub async fn admin_schedule_upgrade( shell: &Shell, @@ -277,9 +261,6 @@ pub async fn admin_schedule_upgrade( forge_args: &ForgeScriptArgs, l1_rpc_url: String, ) -> anyhow::Result<()> { - // TODO(EVM-927): this function does not work without the Gateway contracts. - anyhow::bail!("Gateway upgrade not supported yet!"); - // resume doesn't properly work here. let mut forge_args = forge_args.clone(); forge_args.resume = false; @@ -314,8 +295,6 @@ pub async fn admin_schedule_upgrade( accept_ownership(shell, governor, forge).await } -// TODO(EVM-927): this function does not work without the Gateway contracts. -#[allow(unused)] #[allow(clippy::too_many_arguments)] pub async fn admin_update_validator( shell: &Shell, @@ -328,9 +307,6 @@ pub async fn admin_update_validator( forge_args: &ForgeScriptArgs, l1_rpc_url: String, ) -> anyhow::Result<()> { - // TODO(EVM-927): this function does not work without the Gateway contracts. - anyhow::bail!("Gateway upgrade not supported yet!"); - // resume doesn't properly work here. let mut forge_args = forge_args.clone(); forge_args.resume = false; diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/args/init/configs.rs b/zkstack_cli/crates/zkstack/src/commands/chain/args/init/configs.rs index adfb5ffaf9b7..828ad4ebcd43 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/args/init/configs.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/args/init/configs.rs @@ -8,7 +8,7 @@ use zkstack_cli_types::L1Network; use crate::{ commands::chain::args::{ genesis::{GenesisArgs, GenesisArgsFinal}, - init::InitArgsFinal, + init::{da_configs::ValidiumType, InitArgsFinal}, }, defaults::LOCAL_RPC_URL, messages::{ @@ -28,11 +28,12 @@ pub struct InitConfigsArgs { pub no_port_reallocation: bool, } -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Clone)] pub struct InitConfigsArgsFinal { pub genesis_args: GenesisArgsFinal, pub l1_rpc_url: String, pub no_port_reallocation: bool, + pub validium_config: Option, } impl InitConfigsArgs { @@ -55,6 +56,7 @@ impl InitConfigsArgs { genesis_args: self.genesis_args.fill_values_with_prompt(config), l1_rpc_url, no_port_reallocation: self.no_port_reallocation, + validium_config: Some(ValidiumType::read()), } } } @@ -65,6 +67,7 @@ impl InitConfigsArgsFinal { genesis_args: init_args.genesis_args.clone(), l1_rpc_url: init_args.l1_rpc_url.clone(), no_port_reallocation: init_args.no_port_reallocation, + validium_config: init_args.validium_config.clone(), } } } diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/args/init/da_configs.rs b/zkstack_cli/crates/zkstack/src/commands/chain/args/init/da_configs.rs new file mode 100644 index 000000000000..4d0e97e7ef05 --- /dev/null +++ b/zkstack_cli/crates/zkstack/src/commands/chain/args/init/da_configs.rs @@ -0,0 +1,146 @@ +use clap::{Parser, ValueEnum}; +use serde::{Deserialize, Serialize}; +use strum::{Display, EnumIter, IntoEnumIterator}; +use url::Url; +use zkstack_cli_common::{Prompt, PromptSelect}; +use zksync_config::{ + configs::da_client::avail::{ + AvailClientConfig, AvailDefaultConfig, AvailGasRelayConfig, AvailSecrets, + }, + AvailConfig, +}; + +use crate::{ + defaults::{AVAIL_BRIDGE_API_URL, AVAIL_RPC_URL}, + messages::{ + MSG_AVAIL_API_NODE_URL_PROMPT, MSG_AVAIL_API_TIMEOUT_MS, MSG_AVAIL_APP_ID_PROMPT, + MSG_AVAIL_BRIDGE_API_URL_PROMPT, MSG_AVAIL_CLIENT_TYPE_PROMPT, + MSG_AVAIL_FINALITY_STATE_PROMPT, MSG_AVAIL_GAS_RELAY_API_KEY_PROMPT, + MSG_AVAIL_GAS_RELAY_API_URL_PROMPT, MSG_AVAIL_GAS_RELAY_MAX_RETRIES_PROMPT, + MSG_AVAIL_SEED_PHRASE_PROMPT, MSG_INVALID_URL_ERR, MSG_VALIDIUM_TYPE_PROMPT, + }, +}; + +#[derive(Debug, Serialize, Deserialize, Parser, Clone)] +pub struct ValidiumTypeArgs { + #[clap(long, help = "Type of the Validium network")] + pub validium_type: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, EnumIter, Display, ValueEnum)] +pub enum ValidiumTypeInternal { + NoDA, + Avail, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, EnumIter, Display, ValueEnum)] +pub enum AvailClientTypeInternal { + FullClient, + GasRelay, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum ValidiumType { + NoDA, + Avail((AvailConfig, AvailSecrets)), +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, EnumIter, Display, ValueEnum)] +pub enum AvailFinalityState { + InBlock, + Finalized, +} + +impl ValidiumType { + pub fn read() -> Self { + match PromptSelect::new(MSG_VALIDIUM_TYPE_PROMPT, ValidiumTypeInternal::iter()).ask() { + ValidiumTypeInternal::NoDA => ValidiumType::NoDA, + ValidiumTypeInternal::Avail => { + let avail_client_type = PromptSelect::new( + MSG_AVAIL_CLIENT_TYPE_PROMPT, + AvailClientTypeInternal::iter(), + ) + .ask(); + + let client_config = + match avail_client_type { + AvailClientTypeInternal::FullClient => { + AvailClientConfig::FullClient(AvailDefaultConfig { + api_node_url: Prompt::new(MSG_AVAIL_API_NODE_URL_PROMPT) + .default(AVAIL_RPC_URL.as_str()) + .validate_with(url_validator) + .ask(), + app_id: Prompt::new(MSG_AVAIL_APP_ID_PROMPT) + .validate_with(|input: &String| -> Result<(), String> { + input.parse::().map(|_| ()).map_err(|_| { + "Please enter a positive number".to_string() + }) + }) + .ask(), + finality_state: Some( + PromptSelect::new( + MSG_AVAIL_FINALITY_STATE_PROMPT, + AvailFinalityState::iter(), + ) + .ask() + .to_string(), + ), + }) + } + AvailClientTypeInternal::GasRelay => { + AvailClientConfig::GasRelay(AvailGasRelayConfig { + gas_relay_api_url: Prompt::new(MSG_AVAIL_GAS_RELAY_API_URL_PROMPT) + .validate_with(url_validator) + .ask(), + max_retries: Prompt::new(MSG_AVAIL_GAS_RELAY_MAX_RETRIES_PROMPT) + .validate_with(|input: &String| -> Result<(), String> { + input.parse::().map(|_| ()).map_err(|_| { + "Please enter a positive number".to_string() + }) + }) + .ask(), + }) + } + }; + + let avail_config = AvailConfig { + bridge_api_url: Prompt::new(MSG_AVAIL_BRIDGE_API_URL_PROMPT) + .default(AVAIL_BRIDGE_API_URL.as_str()) + .validate_with(url_validator) + .ask(), + timeout_ms: Prompt::new(MSG_AVAIL_API_TIMEOUT_MS) + .validate_with(|input: &String| -> Result<(), String> { + input + .parse::() + .map(|_| ()) + .map_err(|_| "Please enter a positive number".to_string()) + }) + .ask(), + config: client_config, + }; + + let avail_secrets = match avail_client_type { + AvailClientTypeInternal::FullClient => AvailSecrets { + seed_phrase: Some(Prompt::new(MSG_AVAIL_SEED_PHRASE_PROMPT).ask()), + gas_relay_api_key: None, + }, + AvailClientTypeInternal::GasRelay => AvailSecrets { + seed_phrase: None, + gas_relay_api_key: Some( + Prompt::new(MSG_AVAIL_GAS_RELAY_API_KEY_PROMPT).ask(), + ), + }, + }; + + ValidiumType::Avail((avail_config, avail_secrets)) + } + } + } +} + +#[allow(clippy::ptr_arg)] +fn url_validator(val: &String) -> Result<(), String> { + Url::parse(val) + .map(|_| ()) + .map_err(|_| MSG_INVALID_URL_ERR.to_string()) +} diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/args/init/mod.rs b/zkstack_cli/crates/zkstack/src/commands/chain/args/init/mod.rs index 23e32306519c..2d8539620ef0 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/args/init/mod.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/args/init/mod.rs @@ -3,10 +3,13 @@ use serde::{Deserialize, Serialize}; use url::Url; use zkstack_cli_common::{forge::ForgeScriptArgs, Prompt}; use zkstack_cli_config::ChainConfig; -use zkstack_cli_types::L1Network; +use zkstack_cli_types::{L1BatchCommitmentMode, L1Network}; use crate::{ - commands::chain::args::genesis::{GenesisArgs, GenesisArgsFinal}, + commands::chain::args::{ + genesis::{GenesisArgs, GenesisArgsFinal}, + init::da_configs::ValidiumType, + }, defaults::LOCAL_RPC_URL, messages::{ MSG_DEPLOY_PAYMASTER_PROMPT, MSG_DEV_ARG_HELP, MSG_L1_RPC_URL_HELP, @@ -16,6 +19,7 @@ use crate::{ }; pub mod configs; +pub(crate) mod da_configs; #[derive(Debug, Clone, Serialize, Deserialize, Parser)] pub struct InitArgs { @@ -39,6 +43,8 @@ pub struct InitArgs { pub update_submodules: Option, #[clap(long, help = MSG_DEV_ARG_HELP)] pub dev: bool, + #[clap(flatten)] + pub validium_args: da_configs::ValidiumTypeArgs, } impl InitArgs { @@ -82,23 +88,34 @@ impl InitArgs { }) }; + let validium_config = match config.l1_batch_commit_data_generator_mode { + L1BatchCommitmentMode::Validium => match self.validium_args.validium_type { + None => Some(ValidiumType::read()), + Some(da_configs::ValidiumTypeInternal::NoDA) => Some(ValidiumType::NoDA), + Some(da_configs::ValidiumTypeInternal::Avail) => panic!( + "Avail is not supported via CLI args, use interactive mode" // TODO: Add support for configuration via CLI args + ), + }, + _ => None, + }; + InitArgsFinal { forge_args: self.forge_args, genesis_args: genesis.fill_values_with_prompt(config), deploy_paymaster, l1_rpc_url, no_port_reallocation: self.no_port_reallocation, - dev: self.dev, + validium_config, } } } -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Clone)] pub struct InitArgsFinal { pub forge_args: ForgeScriptArgs, pub genesis_args: GenesisArgsFinal, pub deploy_paymaster: bool, pub l1_rpc_url: String, pub no_port_reallocation: bool, - pub dev: bool, + pub validium_config: Option, } diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/build_transactions.rs b/zkstack_cli/crates/zkstack/src/commands/chain/build_transactions.rs index 2e25d15b0fab..3bf4db7188f7 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/build_transactions.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/build_transactions.rs @@ -18,13 +18,12 @@ use crate::{ }, }; -const REGISTER_CHAIN_TXNS_FILE_SRC: &str = - "contracts/l1-contracts/broadcast/RegisterHyperchain.s.sol/9/dry-run/run-latest.json"; -const REGISTER_CHAIN_TXNS_FILE_DST: &str = "register-hyperchain-txns.json"; +pub const REGISTER_CHAIN_TXNS_FILE_SRC: &str = + "contracts/l1-contracts/broadcast/RegisterZKChain.s.sol/9/dry-run/run-latest.json"; +pub const REGISTER_CHAIN_TXNS_FILE_DST: &str = "register-zk-chain-txns.json"; -const SCRIPT_CONFIG_FILE_SRC: &str = - "contracts/l1-contracts/script-config/register-hyperchain.toml"; -const SCRIPT_CONFIG_FILE_DST: &str = "register-hyperchain.toml"; +const SCRIPT_CONFIG_FILE_SRC: &str = "contracts/l1-contracts/script-config/register-zk-chain.toml"; +const SCRIPT_CONFIG_FILE_DST: &str = "register-zk-chain.toml"; pub(crate) async fn run(args: BuildTransactionsArgs, shell: &Shell) -> anyhow::Result<()> { let config = EcosystemConfig::from_file(shell)?; diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/convert_to_gateway.rs b/zkstack_cli/crates/zkstack/src/commands/chain/convert_to_gateway.rs index 0ac534382a26..0b06cd8de3c2 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/convert_to_gateway.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/convert_to_gateway.rs @@ -1,4 +1,3 @@ -/// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. use anyhow::Context; use ethers::{abi::parse_abi, contract::BaseContract, types::Bytes, utils::hex}; use lazy_static::lazy_static; @@ -49,11 +48,7 @@ lazy_static! { ); } -#[allow(unused)] pub async fn run(args: ForgeScriptArgs, shell: &Shell) -> anyhow::Result<()> { - // TODO(EVM-927): this function does not work without the Gateway contracts. - anyhow::bail!("Gateway upgrade not supported yet!"); - let chain_name = global_config().chain_name.clone(); let ecosystem_config = EcosystemConfig::from_file(shell)?; let chain_config = ecosystem_config @@ -163,7 +158,7 @@ pub async fn run(args: ForgeScriptArgs, shell: &Shell) -> anyhow::Result<()> { Ok(()) } -async fn calculate_gateway_ctm( +pub async fn calculate_gateway_ctm( shell: &Shell, forge_args: ForgeScriptArgs, config: &EcosystemConfig, @@ -214,7 +209,7 @@ async fn calculate_gateway_ctm( Ok(gateway_config) } -async fn deploy_gateway_ctm( +pub async fn deploy_gateway_ctm( shell: &Shell, forge_args: ForgeScriptArgs, config: &EcosystemConfig, @@ -259,7 +254,7 @@ async fn deploy_gateway_ctm( Ok(()) } -async fn gateway_governance_whitelisting( +pub async fn gateway_governance_whitelisting( shell: &Shell, forge_args: ForgeScriptArgs, config: &EcosystemConfig, @@ -397,7 +392,7 @@ async fn gateway_governance_whitelisting( } #[allow(clippy::too_many_arguments)] -async fn call_script( +pub async fn call_script( shell: &Shell, forge_args: ForgeScriptArgs, data: &Bytes, diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/create.rs b/zkstack_cli/crates/zkstack/src/commands/chain/create.rs index fa5ee1a59df7..529d861a2559 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/create.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/create.rs @@ -25,7 +25,7 @@ pub fn run(args: ChainCreateArgs, shell: &Shell) -> anyhow::Result<()> { create(args, &mut ecosystem_config, shell) } -fn create( +pub fn create( args: ChainCreateArgs, ecosystem_config: &mut EcosystemConfig, shell: &Shell, diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/deploy_l2_contracts.rs b/zkstack_cli/crates/zkstack/src/commands/chain/deploy_l2_contracts.rs index d404b29b5a98..7cf628b1170b 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/deploy_l2_contracts.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/deploy_l2_contracts.rs @@ -33,7 +33,6 @@ use crate::{ pub enum Deploy2ContractsOption { All, Upgrader, - InitiailizeBridges, ConsensusRegistry, Multicall3, TimestampAsserter, @@ -61,6 +60,7 @@ pub async fn run( &ecosystem_config, &mut contracts, args, + true, ) .await?; } @@ -104,16 +104,6 @@ pub async fn run( ) .await?; } - Deploy2ContractsOption::InitiailizeBridges => { - initialize_bridges( - shell, - &chain_config, - &ecosystem_config, - &mut contracts, - args, - ) - .await? - } } contracts.save_with_base_path(shell, &chain_config.configs)?; @@ -131,39 +121,23 @@ async fn build_and_deploy( forge_args: ForgeScriptArgs, signature: Option<&str>, mut update_config: impl FnMut(&Shell, &Path) -> anyhow::Result<()>, + with_broadcast: bool, ) -> anyhow::Result<()> { build_l2_contracts(shell.clone(), ecosystem_config.link_to_code.clone())?; - call_forge(shell, chain_config, ecosystem_config, forge_args, signature).await?; - update_config( - shell, - &DEPLOY_L2_CONTRACTS_SCRIPT_PARAMS.output(&chain_config.link_to_code), - )?; - Ok(()) -} - -pub async fn initialize_bridges( - shell: &Shell, - chain_config: &ChainConfig, - ecosystem_config: &EcosystemConfig, - contracts_config: &mut ContractsConfig, - forge_args: ForgeScriptArgs, -) -> anyhow::Result<()> { - let signature = if let Some(true) = chain_config.legacy_bridge { - Some("runDeployLegacySharedBridge") - } else { - Some("runDeploySharedBridge") - }; - build_and_deploy( + call_forge( shell, chain_config, ecosystem_config, forge_args, signature, - |shell, out| { - contracts_config.set_l2_shared_bridge(&InitializeBridgeOutput::read(shell, out)?) - }, + with_broadcast, ) - .await + .await?; + update_config( + shell, + &DEPLOY_L2_CONTRACTS_SCRIPT_PARAMS.output(&chain_config.link_to_code), + )?; + Ok(()) } pub async fn deploy_upgrader( @@ -182,6 +156,7 @@ pub async fn deploy_upgrader( |shell, out| { contracts_config.set_default_l2_upgrade(&DefaultL2UpgradeOutput::read(shell, out)?) }, + true, ) .await } @@ -202,6 +177,7 @@ pub async fn deploy_consensus_registry( |shell, out| { contracts_config.set_consensus_registry(&ConsensusRegistryOutput::read(shell, out)?) }, + true, ) .await } @@ -220,6 +196,7 @@ pub async fn deploy_multicall3( forge_args, Some("runDeployMulticall3"), |shell, out| contracts_config.set_multicall3(&Multicall3Output::read(shell, out)?), + true, ) .await } @@ -241,6 +218,7 @@ pub async fn deploy_timestamp_asserter( contracts_config .set_timestamp_asserter_addr(&TimestampAsserterOutput::read(shell, out)?) }, + true, ) .await } @@ -251,18 +229,14 @@ pub async fn deploy_l2_contracts( ecosystem_config: &EcosystemConfig, contracts_config: &mut ContractsConfig, forge_args: ForgeScriptArgs, + with_broadcast: bool, ) -> anyhow::Result<()> { - let signature = if let Some(true) = chain_config.legacy_bridge { - Some("runWithLegacyBridge") - } else { - None - }; build_and_deploy( shell, chain_config, ecosystem_config, forge_args, - signature, + None, |shell, out| { contracts_config.set_l2_shared_bridge(&InitializeBridgeOutput::read(shell, out)?)?; contracts_config.set_default_l2_upgrade(&DefaultL2UpgradeOutput::read(shell, out)?)?; @@ -272,6 +246,7 @@ pub async fn deploy_l2_contracts( .set_timestamp_asserter_addr(&TimestampAsserterOutput::read(shell, out)?)?; Ok(()) }, + with_broadcast, ) .await } @@ -282,8 +257,13 @@ async fn call_forge( ecosystem_config: &EcosystemConfig, forge_args: ForgeScriptArgs, signature: Option<&str>, + with_broadcast: bool, ) -> anyhow::Result<()> { - let input = DeployL2ContractsInput::new(chain_config, ecosystem_config.era_chain_id)?; + let input = DeployL2ContractsInput::new( + chain_config, + &ecosystem_config.get_contracts_config()?, + ecosystem_config.era_chain_id, + )?; let foundry_contracts_path = chain_config.path_to_l1_foundry(); let secrets = chain_config.get_secrets_config()?; input.save( @@ -304,8 +284,10 @@ async fn call_forge( .l1_rpc_url .expose_str() .to_string(), - ) - .with_broadcast(); + ); + if with_broadcast { + forge = forge.with_broadcast(); + } if let Some(signature) = signature { forge = forge.with_signature(signature); diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/gateway_upgrade.rs b/zkstack_cli/crates/zkstack/src/commands/chain/gateway_upgrade.rs index ed84df9d13c4..f096daef032a 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/gateway_upgrade.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/gateway_upgrade.rs @@ -1,9 +1,11 @@ -/// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. use anyhow::Context; use clap::{Parser, ValueEnum}; use ethers::{ - abi::{encode, parse_abi}, + abi::parse_abi, contract::BaseContract, + providers::{Http, Middleware, Provider}, + signers::Signer, + types::{transaction::eip2718::TypedTransaction, Eip1559TransactionRequest}, utils::hex, }; use lazy_static::lazy_static; @@ -16,23 +18,20 @@ use zkstack_cli_common::{ }; use zkstack_cli_config::{ forge_interface::{ - gateway_chain_upgrade::{ - input::GatewayChainUpgradeInput, output::GatewayChainUpgradeOutput, - }, gateway_ecosystem_upgrade::output::GatewayEcosystemUpgradeOutput, - script_params::{GATEWAY_UPGRADE_CHAIN_PARAMS, GATEWAY_UPGRADE_ECOSYSTEM_PARAMS}, + script_params::{ACCEPT_GOVERNANCE_SCRIPT_PARAMS, GATEWAY_UPGRADE_ECOSYSTEM_PARAMS}, }, - traits::{ReadConfig, ReadConfigWithBasePath, SaveConfig, SaveConfigWithBasePath}, + traits::{ReadConfig, ReadConfigWithBasePath, SaveConfigWithBasePath}, ChainConfig, EcosystemConfig, }; use zkstack_cli_types::L1BatchCommitmentMode; -use zksync_basic_types::{H256, U256}; -use zksync_types::{web3::keccak256, Address, L2_NATIVE_TOKEN_VAULT_ADDRESS}; +use zksync_basic_types::U256; +use zksync_types::Address; use crate::{ - accept_ownership::{ - admin_execute_upgrade, admin_schedule_upgrade, admin_update_validator, - set_da_validator_pair, + commands::dev::commands::gateway::{ + check_chain_readiness, fetch_chain_info, get_admin_call_builder, + set_upgrade_timestamp_calldata, DAMode, GatewayUpgradeArgsInner, GatewayUpgradeInfo, }, messages::{MSG_CHAIN_NOT_INITIALIZED, MSG_L1_SECRETS_MUST_BE_PRESENTED}, utils::forge::{fill_forge_private_key, WalletOwner}, @@ -47,9 +46,6 @@ lazy_static! { Debug, Serialize, Deserialize, Clone, Copy, ValueEnum, EnumIter, strum::Display, PartialEq, Eq, )] pub enum GatewayChainUpgradeStage { - // some config paaram - AdaptConfig, - // Does not require admin, still needs to be done to update configs, etc PrepareStage1, @@ -78,6 +74,8 @@ pub struct GatewayUpgradeArgs { pub forge_args: ForgeScriptArgs, chain_upgrade_stage: GatewayChainUpgradeStage, + + l2_wrapped_base_token_addr: Option
, } lazy_static! { @@ -90,11 +88,7 @@ lazy_static! { ); } -#[allow(unused)] pub async fn run(args: GatewayUpgradeArgs, shell: &Shell) -> anyhow::Result<()> { - // TODO(EVM-927): this function does not work without the Gateway contracts. - anyhow::bail!("Gateway upgrade not supported yet!"); - let ecosystem_config = EcosystemConfig::from_file(shell)?; let chain_name = global_config().chain_name.clone(); @@ -111,15 +105,14 @@ pub async fn run(args: GatewayUpgradeArgs, shell: &Shell) -> anyhow::Result<()> .to_string(); match args.chain_upgrade_stage { - GatewayChainUpgradeStage::AdaptConfig => adapt_config(shell, chain_config).await, GatewayChainUpgradeStage::PrepareStage1 => { - prepare_stage1(shell, args, ecosystem_config, chain_config, l1_url).await + prepare_stage1(shell, ecosystem_config, chain_config, l1_url).await } GatewayChainUpgradeStage::ScheduleStage1 => { - schedule_stage1(shell, args, ecosystem_config, chain_config, l1_url).await + schedule_stage1(shell, ecosystem_config, chain_config, l1_url).await } GatewayChainUpgradeStage::FinalizeStage1 => { - finalize_stage1(shell, args, ecosystem_config, chain_config, l1_url).await + finalize_stage1(shell, ecosystem_config, chain_config, l1_url).await } GatewayChainUpgradeStage::FinalizeStage2 => { finalize_stage2(shell, ecosystem_config, chain_config).await @@ -133,173 +126,130 @@ pub async fn run(args: GatewayUpgradeArgs, shell: &Shell) -> anyhow::Result<()> } } -fn encode_ntv_asset_id(l1_chain_id: U256, addr: Address) -> H256 { - let encoded_data = encode(&[ - ethers::abi::Token::Uint(l1_chain_id), - ethers::abi::Token::Address(L2_NATIVE_TOKEN_VAULT_ADDRESS), - ethers::abi::Token::Address(addr), - ]); - - H256(keccak256(&encoded_data)) -} - -async fn adapt_config(shell: &Shell, chain_config: ChainConfig) -> anyhow::Result<()> { - println!("Adapting config"); - let mut contracts_config = chain_config.get_contracts_config()?; - let genesis_config = chain_config.get_genesis_config()?; - - contracts_config.l2.legacy_shared_bridge_addr = contracts_config.bridges.shared.l2_address; - contracts_config.l1.base_token_asset_id = Some(encode_ntv_asset_id( - genesis_config.l1_chain_id.0.into(), - contracts_config.l1.base_token_addr, - )); - - contracts_config.save_with_base_path(shell, &chain_config.configs)?; - println!("Done"); - - Ok(()) -} - async fn prepare_stage1( shell: &Shell, - args: GatewayUpgradeArgs, ecosystem_config: EcosystemConfig, chain_config: ChainConfig, l1_url: String, ) -> anyhow::Result<()> { - let chain_upgrade_config_path = - GATEWAY_UPGRADE_CHAIN_PARAMS.input(&ecosystem_config.link_to_code); - - let gateway_upgrade_input = GatewayChainUpgradeInput::new(&chain_config); - gateway_upgrade_input.save(shell, chain_upgrade_config_path.clone())?; - - let mut forge = Forge::new(&ecosystem_config.path_to_l1_foundry()) - .script( - &GATEWAY_UPGRADE_CHAIN_PARAMS.script(), - args.forge_args.clone(), - ) - .with_ffi() - .with_rpc_url(l1_url) - .with_slow() - .with_broadcast(); - - forge = fill_forge_private_key( - forge, - Some(&chain_config.get_wallets_config()?.governor), - WalletOwner::Governor, - )?; - - println!("Preparing the chain for the upgrade!"); - - forge.run(shell)?; - - println!("done!"); - - let chain_output = GatewayChainUpgradeOutput::read( - shell, - GATEWAY_UPGRADE_CHAIN_PARAMS.output(&ecosystem_config.link_to_code), - )?; - let gateway_ecosystem_preparation_output = GatewayEcosystemUpgradeOutput::read_with_base_path(shell, ecosystem_config.config)?; // No need to save it, we have enough for now let mut contracts_config = chain_config.get_contracts_config()?; + let general_config = chain_config.get_general_config()?; + let genesis_config = chain_config.get_genesis_config()?; - contracts_config - .ecosystem_contracts - .stm_deployment_tracker_proxy_addr = Some( - gateway_ecosystem_preparation_output - .deployed_addresses - .bridgehub - .ctm_deployment_tracker_proxy_addr, - ); - // This is force deployment data for creating new contracts, not really relevant here tbh, - contracts_config.ecosystem_contracts.force_deployments_data = Some(hex::encode( - &gateway_ecosystem_preparation_output - .contracts_config - .force_deployments_data - .0, - )); - contracts_config.ecosystem_contracts.native_token_vault_addr = Some( - gateway_ecosystem_preparation_output - .deployed_addresses - .native_token_vault_addr, + let upgrade_info = GatewayUpgradeInfo::from_gateway_ecosystem_upgrade( + contracts_config.ecosystem_contracts.bridgehub_proxy_addr, + gateway_ecosystem_preparation_output, ); - contracts_config - .ecosystem_contracts - .l1_bytecodes_supplier_addr = Some( - gateway_ecosystem_preparation_output - .deployed_addresses - .l1_bytecodes_supplier_addr, - ); - contracts_config.l1.access_control_restriction_addr = - Some(chain_output.access_control_restriction); - contracts_config.l1.chain_admin_addr = chain_output.chain_admin_addr; - contracts_config.l1.rollup_l1_da_validator_addr = Some( - gateway_ecosystem_preparation_output - .deployed_addresses - .rollup_l1_da_validator_addr, - ); - contracts_config.l1.no_da_validium_l1_validator_addr = Some( - gateway_ecosystem_preparation_output - .deployed_addresses - .validium_l1_da_validator_addr, - ); + let da_mode: DAMode = + if genesis_config.l1_batch_commit_data_generator_mode == L1BatchCommitmentMode::Rollup { + DAMode::PermanentRollup + } else { + DAMode::Validium + }; + + let chain_info = fetch_chain_info( + &upgrade_info, + &GatewayUpgradeArgsInner { + chain_id: chain_config.chain_id.as_u64(), + l1_rpc_url: l1_url, + l2_rpc_url: general_config + .api_config + .context("api config")? + .web3_json_rpc + .http_url, + validator_addr1: chain_config.get_wallets_config()?.operator.address, + validator_addr2: chain_config.get_wallets_config()?.blob_operator.address, + da_mode, + dangerous_no_cross_check: false, + }, + ) + .await?; - let validum = chain_config - .get_genesis_config()? - .l1_batch_commit_data_generator_mode - == L1BatchCommitmentMode::Validium; - - // We do not use chain output because IMHO we should delete it altogether from there - contracts_config.l2.da_validator_addr = if !validum { - Some( - gateway_ecosystem_preparation_output - .contracts_config - .expected_rollup_l2_da_validator, - ) - } else { - Some( - gateway_ecosystem_preparation_output - .contracts_config - .expected_validium_l2_da_validator, - ) - }; - contracts_config.l2.l2_native_token_vault_proxy_addr = Some(L2_NATIVE_TOKEN_VAULT_ADDRESS); - contracts_config.l2.legacy_shared_bridge_addr = contracts_config.bridges.shared.l2_address; + upgrade_info.update_contracts_config(&mut contracts_config, &chain_info, da_mode, true); contracts_config.save_with_base_path(shell, chain_config.configs)?; Ok(()) } -const NEW_PROTOCOL_VERSION: u64 = 0x1b00000000; +async fn call_chain_admin( + l1_url: String, + chain_config: ChainConfig, + data: Vec, +) -> anyhow::Result<()> { + let wallet = chain_config + .get_wallets_config()? + .governor + .private_key + .context("gov pk missing")?; + let contracts_config = chain_config.get_contracts_config()?; + + // Initialize provider + let provider = Provider::::try_from(l1_url)?; + + // Initialize wallet + let chain_id = provider.get_chainid().await?.as_u64(); + let wallet = wallet.with_chain_id(chain_id); + + let tx = TypedTransaction::Eip1559(Eip1559TransactionRequest { + to: Some(contracts_config.l1.chain_admin_addr.into()), + // 10m should be always enough + gas: Some(U256::from(10_000_000)), + data: Some(data.into()), + value: Some(U256::zero()), + nonce: Some( + provider + .get_transaction_count(wallet.address(), None) + .await?, + ), + max_fee_per_gas: Some(provider.get_gas_price().await?), + max_priority_fee_per_gas: Some(U256::zero()), + chain_id: Some(chain_id.into()), + ..Default::default() + }); + + let signed_tx = wallet.sign_transaction(&tx).await.unwrap(); + + let tx = provider + .send_raw_transaction(tx.rlp_signed(&signed_tx)) + .await + .unwrap(); + println!("Sent tx with hash: {}", hex::encode(tx.0)); + + let receipt = tx.await?.context("receipt not present")?; + + if receipt.status.unwrap() != 1.into() { + anyhow::bail!("Transaction failed!"); + } + + Ok(()) +} async fn schedule_stage1( shell: &Shell, - args: GatewayUpgradeArgs, ecosystem_config: EcosystemConfig, chain_config: ChainConfig, l1_url: String, ) -> anyhow::Result<()> { + let gateway_ecosystem_preparation_output = + GatewayEcosystemUpgradeOutput::read_with_base_path(shell, ecosystem_config.config)?; + println!("Schedule stage1 of the upgrade!!"); + let calldata = set_upgrade_timestamp_calldata( + gateway_ecosystem_preparation_output + .contracts_config + .new_protocol_version, + // Immediatelly + 0, + ); - admin_schedule_upgrade( - shell, - &ecosystem_config, - &chain_config.get_contracts_config()?, - // For now it is hardcoded both in scripts and here - U256::from(NEW_PROTOCOL_VERSION), - // We only do instant upgrades for now - U256::zero(), - &chain_config.get_wallets_config()?.governor, - &args.forge_args, - l1_url.clone(), - ) - .await?; + call_chain_admin(l1_url, chain_config, calldata).await?; println!("done!"); @@ -308,109 +258,69 @@ async fn schedule_stage1( async fn finalize_stage1( shell: &Shell, - args: GatewayUpgradeArgs, ecosystem_config: EcosystemConfig, chain_config: ChainConfig, l1_url: String, ) -> anyhow::Result<()> { println!("Finalizing stage1 of chain upgrade!"); - let mut contracts_config = chain_config.get_contracts_config()?; + let contracts_config = chain_config.get_contracts_config()?; + let general_config = chain_config.get_general_config()?; + let genesis_config = chain_config.get_genesis_config()?; + + println!("Checking chain readiness..."); + check_chain_readiness( + l1_url.clone(), + general_config + .api_config + .as_ref() + .context("api")? + .web3_json_rpc + .http_url + .clone(), + chain_config.chain_id.as_u64(), + ) + .await?; + + println!("The chain is ready!"); + let gateway_ecosystem_preparation_output = GatewayEcosystemUpgradeOutput::read_with_base_path(shell, &ecosystem_config.config)?; - let old_validator_timelock = contracts_config.l1.validator_timelock_addr; - let new_validator_timelock = gateway_ecosystem_preparation_output - .deployed_addresses - .validator_timelock_addr; - - let validators = [ - chain_config.get_wallets_config()?.operator.address, - chain_config.get_wallets_config()?.blob_operator.address, - ]; - - println!("Setting new validators!"); - for val in validators { - admin_update_validator( - shell, - &ecosystem_config, - &chain_config, - old_validator_timelock, - val, - false, - &chain_config.get_wallets_config()?.governor, - &args.forge_args, - l1_url.clone(), - ) - .await?; - - admin_update_validator( - shell, - &ecosystem_config, - &chain_config, - new_validator_timelock, - val, - true, - &chain_config.get_wallets_config()?.governor, - &args.forge_args, - l1_url.clone(), - ) - .await?; - } + let da_mode: DAMode = + if genesis_config.l1_batch_commit_data_generator_mode == L1BatchCommitmentMode::Rollup { + DAMode::PermanentRollup + } else { + DAMode::Validium + }; - println!("Setting new validators done!"); + let upgrade_info = GatewayUpgradeInfo::from_gateway_ecosystem_upgrade( + contracts_config.ecosystem_contracts.bridgehub_proxy_addr, + gateway_ecosystem_preparation_output, + ); + let args = GatewayUpgradeArgsInner { + chain_id: chain_config.chain_id.as_u64(), + l1_rpc_url: l1_url.clone(), + l2_rpc_url: general_config + .api_config + .context("api config")? + .web3_json_rpc + .http_url, + validator_addr1: chain_config.get_wallets_config()?.operator.address, + validator_addr2: chain_config.get_wallets_config()?.blob_operator.address, + da_mode, + dangerous_no_cross_check: false, + }; - contracts_config.l1.validator_timelock_addr = gateway_ecosystem_preparation_output - .deployed_addresses - .validator_timelock_addr; + let chain_info = fetch_chain_info(&upgrade_info, &args).await?; - admin_execute_upgrade( - shell, - &ecosystem_config, - &chain_config.get_contracts_config()?, - &chain_config.get_wallets_config()?.governor, - gateway_ecosystem_preparation_output - .chain_upgrade_diamond_cut - .0, - &args.forge_args, - l1_url.clone(), - ) - .await?; + let admin_calls_finalize = get_admin_call_builder(&upgrade_info, &chain_info, args); - let l1_da_validator_contract = if chain_config - .get_genesis_config()? - .l1_batch_commit_data_generator_mode - == L1BatchCommitmentMode::Rollup - { - ecosystem_config - .get_contracts_config()? - .l1 - .rollup_l1_da_validator_addr - } else { - ecosystem_config - .get_contracts_config()? - .l1 - .no_da_validium_l1_validator_addr - } - .context("l1 da validator")?; + admin_calls_finalize.display(); - set_da_validator_pair( - shell, - &ecosystem_config, - contracts_config.l1.chain_admin_addr, - &chain_config.get_wallets_config()?.governor, - contracts_config.l1.diamond_proxy_addr, - l1_da_validator_contract, - contracts_config - .l2 - .da_validator_addr - .context("l2_da_validator_addr")?, - &args.forge_args, - l1_url, - ) - .await?; + let admin_calldata = admin_calls_finalize.compile_full_calldata(); - contracts_config.save_with_base_path(shell, &chain_config.configs)?; + call_chain_admin(l1_url, chain_config, admin_calldata).await?; println!("done!"); @@ -460,7 +370,6 @@ async fn set_weth_for_chain( ]) .unwrap(), ); - let contracts_config = chain_config.get_contracts_config()?; let calldata = contract .encode( "addL2WethToStore", @@ -474,17 +383,15 @@ async fn set_weth_for_chain( .l1 .chain_admin_addr, chain_config.chain_id.as_u64(), - contracts_config - .l2 - .predeployed_l2_wrapped_base_token_address - .expect("No predeployed_l2_wrapped_base_token_address"), + args.l2_wrapped_base_token_addr + .context("l2_wrapped_base_token_addr")?, ), ) .unwrap(); let mut forge = Forge::new(&ecosystem_config.path_to_l1_foundry()) .script( - &GATEWAY_UPGRADE_ECOSYSTEM_PARAMS.script(), + &ACCEPT_GOVERNANCE_SCRIPT_PARAMS.script(), forge_args.clone(), ) .with_ffi() diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/init/configs.rs b/zkstack_cli/crates/zkstack/src/commands/chain/init/configs.rs index 5a27f903a72f..4db4c9927de1 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/init/configs.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/init/configs.rs @@ -6,12 +6,17 @@ use zkstack_cli_config::{ copy_configs, set_l1_rpc_url, traits::SaveConfigWithBasePath, update_from_chain_config, ChainConfig, ContractsConfig, EcosystemConfig, }; +use zksync_config::configs::DataAvailabilitySecrets; use crate::{ commands::{ chain::{ - args::init::configs::{InitConfigsArgs, InitConfigsArgsFinal}, + args::init::{ + configs::{InitConfigsArgs, InitConfigsArgsFinal}, + da_configs::ValidiumType, + }, genesis, + utils::encode_ntv_asset_id, }, portal::update_portal_config, }, @@ -56,6 +61,13 @@ pub async fn init_configs( )?; } + let consensus_keys = generate_consensus_keys(); + + // Initialize secrets config + let mut secrets = chain_config.get_secrets_config()?; + set_l1_rpc_url(&mut secrets, init_args.l1_rpc_url.clone())?; + secrets.consensus = Some(get_consensus_secrets(&consensus_keys)); + let mut general_config = chain_config.get_general_config()?; if general_config.proof_data_handler_config.is_some() && general_config.prover_gateway.is_some() @@ -73,10 +85,22 @@ pub async fn init_configs( .consensus_config .context(MSG_CONSENSUS_CONFIG_MISSING_ERR)?; - let consensus_keys = generate_consensus_keys(); consensus_config.genesis_spec = Some(get_genesis_specs(chain_config, &consensus_keys)); general_config.consensus_config = Some(consensus_config); + if let Some(validium_config) = init_args.validium_config.clone() { + match validium_config { + ValidiumType::NoDA => { + general_config.da_client_config = None; + } + ValidiumType::Avail((avail_config, avail_secrets)) => { + general_config.da_client_config = Some(avail_config.into()); + secrets.data_availability = Some(DataAvailabilitySecrets::Avail(avail_secrets)); + } + } + } + + secrets.save_with_base_path(shell, &chain_config.configs)?; general_config.save_with_base_path(shell, &chain_config.configs)?; // Initialize genesis config @@ -90,14 +114,12 @@ pub async fn init_configs( contracts_config.l1.governance_addr = Address::zero(); contracts_config.l1.chain_admin_addr = Address::zero(); contracts_config.l1.base_token_addr = chain_config.base_token.address; + contracts_config.l1.base_token_asset_id = Some(encode_ntv_asset_id( + genesis_config.l1_chain_id.0.into(), + contracts_config.l1.base_token_addr, + )); contracts_config.save_with_base_path(shell, &chain_config.configs)?; - // Initialize secrets config - let mut secrets = chain_config.get_secrets_config()?; - set_l1_rpc_url(&mut secrets, init_args.l1_rpc_url.clone())?; - secrets.consensus = Some(get_consensus_secrets(&consensus_keys)); - secrets.save_with_base_path(shell, &chain_config.configs)?; - genesis::database::update_configs(init_args.genesis_args.clone(), shell, chain_config)?; update_portal_config(shell, chain_config) diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/init/mod.rs b/zkstack_cli/crates/zkstack/src/commands/chain/init/mod.rs index 4100fee22d89..f115048d1181 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/init/mod.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/init/mod.rs @@ -3,10 +3,12 @@ use clap::{command, Parser, Subcommand}; use xshell::Shell; use zkstack_cli_common::{git, logger, spinner::Spinner}; use zkstack_cli_config::{traits::SaveConfigWithBasePath, ChainConfig, EcosystemConfig}; -use zkstack_cli_types::BaseToken; +use zkstack_cli_types::{BaseToken, L1BatchCommitmentMode}; +use zksync_config::DAClientConfig; +use zksync_types::Address; use crate::{ - accept_ownership::accept_admin, + accept_ownership::{accept_admin, make_permanent_rollup, set_da_validator_pair}, commands::chain::{ args::init::{ configs::{InitConfigsArgs, InitConfigsArgsFinal}, @@ -23,8 +25,8 @@ use crate::{ enable_evm_emulator::enable_evm_emulator, messages::{ msg_initializing_chain, MSG_ACCEPTING_ADMIN_SPINNER, MSG_CHAIN_INITIALIZED, - MSG_CHAIN_NOT_FOUND_ERR, MSG_DEPLOYING_PAYMASTER, MSG_GENESIS_DATABASE_ERR, - MSG_REGISTERING_CHAIN_SPINNER, MSG_SELECTED_CONFIG, + MSG_CHAIN_NOT_FOUND_ERR, MSG_DA_PAIR_REGISTRATION_SPINNER, MSG_DEPLOYING_PAYMASTER, + MSG_GENESIS_DATABASE_ERR, MSG_REGISTERING_CHAIN_SPINNER, MSG_SELECTED_CONFIG, MSG_UPDATING_TOKEN_MULTIPLIER_SETTER_SPINNER, MSG_WALLET_TOKEN_MULTIPLIER_SETTER_NOT_FOUND, }, }; @@ -123,18 +125,24 @@ pub async fn init( // Set token multiplier setter address (run by L2 Governor) if chain_config.base_token != BaseToken::eth() { let spinner = Spinner::new(MSG_UPDATING_TOKEN_MULTIPLIER_SETTER_SPINNER); + let chain_contracts = chain_config.get_contracts_config()?; set_token_multiplier_setter( shell, ecosystem_config, &chain_config.get_wallets_config()?.governor, - contracts_config.l1.chain_admin_addr, + chain_contracts + .l1 + .access_control_restriction_addr + .context("chain_contracts.l1.access_control_restriction_addr")?, + chain_contracts.l1.diamond_proxy_addr, chain_config .get_wallets_config() .unwrap() .token_multiplier_setter .context(MSG_WALLET_TOKEN_MULTIPLIER_SETTER_NOT_FOUND)? .address, - &init_args.forge_args, + chain_contracts.l1.chain_admin_addr, + &init_args.forge_args.clone(), init_args.l1_rpc_url.clone(), ) .await?; @@ -162,10 +170,46 @@ pub async fn init( ecosystem_config, &mut contracts_config, init_args.forge_args.clone(), + true, ) .await?; contracts_config.save_with_base_path(shell, &chain_config.configs)?; + let l1_da_validator_addr = get_l1_da_validator(chain_config); + + let spinner = Spinner::new(MSG_DA_PAIR_REGISTRATION_SPINNER); + set_da_validator_pair( + shell, + ecosystem_config, + contracts_config.l1.chain_admin_addr, + &chain_config.get_wallets_config()?.governor, + contracts_config.l1.diamond_proxy_addr, + l1_da_validator_addr.context("l1_da_validator_addr")?, + contracts_config + .l2 + .da_validator_addr + .context("da_validator_addr")?, + &init_args.forge_args.clone(), + init_args.l1_rpc_url.clone(), + ) + .await?; + spinner.finish(); + + if chain_config.l1_batch_commit_data_generator_mode == L1BatchCommitmentMode::Rollup { + println!("Making permanent rollup!"); + make_permanent_rollup( + shell, + ecosystem_config, + contracts_config.l1.chain_admin_addr, + &chain_config.get_wallets_config()?.governor, + contracts_config.l1.diamond_proxy_addr, + &init_args.forge_args.clone(), + init_args.l1_rpc_url.clone(), + ) + .await?; + println!("Done"); + } + // Setup legacy bridge - shouldn't be used for new chains (run by L1 Governor) if let Some(true) = chain_config.legacy_bridge { setup_legacy_bridge( @@ -200,3 +244,26 @@ pub async fn init( Ok(()) } + +pub(crate) fn get_l1_da_validator(chain_config: &ChainConfig) -> anyhow::Result
{ + let contracts_config = chain_config.get_contracts_config()?; + + let l1_da_validator_contract = match chain_config.l1_batch_commit_data_generator_mode { + L1BatchCommitmentMode::Rollup => contracts_config.l1.rollup_l1_da_validator_addr, + L1BatchCommitmentMode::Validium => { + let general_config = chain_config.get_general_config()?; + if let Some(da_client_config) = general_config.da_client_config { + match da_client_config { + DAClientConfig::Avail(_) => contracts_config.l1.avail_l1_da_validator_addr, + DAClientConfig::NoDA => contracts_config.l1.no_da_validium_l1_validator_addr, + _ => anyhow::bail!("DA client config is not supported"), + } + } else { + contracts_config.l1.no_da_validium_l1_validator_addr + } + } + } + .context("l1 da validator")?; + + Ok(l1_da_validator_contract) +} diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/migrate_from_gateway.rs b/zkstack_cli/crates/zkstack/src/commands/chain/migrate_from_gateway.rs index 573dcf56345f..cf9b9e8e6399 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/migrate_from_gateway.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/migrate_from_gateway.rs @@ -1,4 +1,3 @@ -/// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. use anyhow::Context; use clap::Parser; use ethers::{ @@ -58,11 +57,7 @@ lazy_static! { ); } -#[allow(unused)] pub async fn run(args: MigrateFromGatewayArgs, shell: &Shell) -> anyhow::Result<()> { - // TODO(EVM-927): this function does not work without the Gateway contracts. - anyhow::bail!("Gateway upgrade not supported yet!"); - let ecosystem_config = EcosystemConfig::from_file(shell)?; let chain_name = global_config().chain_name.clone(); diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/migrate_to_gateway.rs b/zkstack_cli/crates/zkstack/src/commands/chain/migrate_to_gateway.rs index f6bb25613039..c51f6414ce97 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/migrate_to_gateway.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/migrate_to_gateway.rs @@ -67,12 +67,7 @@ lazy_static! { ); } -// TODO(EVM-927): merge gateway contracts -#[allow(unused)] pub async fn run(args: MigrateToGatewayArgs, shell: &Shell) -> anyhow::Result<()> { - // TODO(EVM-927): this function does not work without the Gateway contracts. - anyhow::bail!("Gateway upgrade not supported yet!"); - let ecosystem_config = EcosystemConfig::from_file(shell)?; let chain_name = global_config().chain_name.clone(); diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/mod.rs b/zkstack_cli/crates/zkstack/src/commands/chain/mod.rs index 7bbe8acf99f3..d6c1851d0c96 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/mod.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/mod.rs @@ -13,20 +13,25 @@ use crate::commands::chain::{ mod accept_chain_ownership; pub(crate) mod args; mod build_transactions; -mod common; -mod convert_to_gateway; -mod create; +pub(crate) mod common; +#[cfg(feature = "gateway")] +pub(crate) mod convert_to_gateway; +pub(crate) mod create; pub mod deploy_l2_contracts; pub mod deploy_paymaster; mod enable_evm_emulator; +#[cfg(feature = "gateway")] mod gateway_upgrade; pub mod genesis; pub mod init; +#[cfg(feature = "gateway")] mod migrate_from_gateway; +#[cfg(feature = "gateway")] mod migrate_to_gateway; pub mod register_chain; mod set_token_multiplier_setter; mod setup_legacy_bridge; +mod utils; #[derive(Subcommand, Debug)] pub enum ChainCommands { @@ -52,9 +57,6 @@ pub enum ChainCommands { /// DiamondProxy contract. #[command(alias = "accept-ownership")] AcceptChainOwnership(ForgeScriptArgs), - /// Initialize bridges on L2 - #[command(alias = "bridge")] - InitializeBridges(ForgeScriptArgs), /// Deploy L2 consensus registry #[command(alias = "consensus")] DeployConsensusRegistry(ForgeScriptArgs), @@ -72,6 +74,18 @@ pub enum ChainCommands { DeployPaymaster(ForgeScriptArgs), /// Update Token Multiplier Setter address on L1 UpdateTokenMultiplierSetter(ForgeScriptArgs), + /// Prepare chain to be an eligible gateway + #[cfg(feature = "gateway")] + ConvertToGateway(ForgeScriptArgs), + /// Migrate chain to gateway + #[cfg(feature = "gateway")] + MigrateToGateway(migrate_to_gateway::MigrateToGatewayArgs), + /// Migrate chain from gateway + #[cfg(feature = "gateway")] + MigrateFromGateway(migrate_from_gateway::MigrateFromGatewayArgs), + /// Upgrade to the protocol version that supports Gateway + #[cfg(feature = "gateway")] + GatewayUpgrade(gateway_upgrade::GatewayUpgradeArgs), /// Enable EVM emulation on chain (Not supported yet) EnableEvmEmulator(ForgeScriptArgs), } @@ -99,13 +113,18 @@ pub(crate) async fn run(shell: &Shell, args: ChainCommands) -> anyhow::Result<() ChainCommands::DeployUpgrader(args) => { deploy_l2_contracts::run(args, shell, Deploy2ContractsOption::Upgrader).await } - ChainCommands::InitializeBridges(args) => { - deploy_l2_contracts::run(args, shell, Deploy2ContractsOption::InitiailizeBridges).await - } ChainCommands::DeployPaymaster(args) => deploy_paymaster::run(args, shell).await, ChainCommands::UpdateTokenMultiplierSetter(args) => { set_token_multiplier_setter::run(args, shell).await } + #[cfg(feature = "gateway")] + ChainCommands::ConvertToGateway(args) => convert_to_gateway::run(args, shell).await, + #[cfg(feature = "gateway")] + ChainCommands::MigrateToGateway(args) => migrate_to_gateway::run(args, shell).await, + #[cfg(feature = "gateway")] + ChainCommands::MigrateFromGateway(args) => migrate_from_gateway::run(args, shell).await, + #[cfg(feature = "gateway")] + ChainCommands::GatewayUpgrade(args) => gateway_upgrade::run(args, shell).await, ChainCommands::EnableEvmEmulator(args) => enable_evm_emulator::run(args, shell).await, } } diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/set_token_multiplier_setter.rs b/zkstack_cli/crates/zkstack/src/commands/chain/set_token_multiplier_setter.rs index 69a823f8f852..e1a57dcd0f00 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/set_token_multiplier_setter.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/set_token_multiplier_setter.rs @@ -25,7 +25,7 @@ use crate::{ lazy_static! { static ref SET_TOKEN_MULTIPLIER_SETTER: BaseContract = BaseContract::from( parse_abi(&[ - "function chainSetTokenMultiplierSetter(address chainAdmin, address target) public" + "function chainSetTokenMultiplierSetter(address chainAdmin, address accessControlRestriction, address diamondProxyAddress, address setter) public" ]) .unwrap(), ); @@ -56,8 +56,13 @@ pub async fn run(args: ForgeScriptArgs, shell: &Shell) -> anyhow::Result<()> { shell, &ecosystem_config, &chain_config.get_wallets_config()?.governor, - contracts_config.l1.chain_admin_addr, + contracts_config + .l1 + .access_control_restriction_addr + .context("access_control_restriction_addr")?, + contracts_config.l1.diamond_proxy_addr, token_multiplier_setter_address, + contracts_config.l1.chain_admin_addr, &args.clone(), l1_url, ) @@ -72,12 +77,15 @@ pub async fn run(args: ForgeScriptArgs, shell: &Shell) -> anyhow::Result<()> { Ok(()) } +#[allow(clippy::too_many_arguments)] pub async fn set_token_multiplier_setter( shell: &Shell, ecosystem_config: &EcosystemConfig, governor: &Wallet, - chain_admin_address: Address, - target_address: Address, + access_control_restriction_address: Address, + diamond_proxy_address: Address, + new_setter_address: Address, + chain_admin_addr: Address, forge_args: &ForgeScriptArgs, l1_rpc_url: String, ) -> anyhow::Result<()> { @@ -90,7 +98,12 @@ pub async fn set_token_multiplier_setter( let calldata = SET_TOKEN_MULTIPLIER_SETTER .encode( "chainSetTokenMultiplierSetter", - (chain_admin_address, target_address), + ( + chain_admin_addr, + access_control_restriction_address, + diamond_proxy_address, + new_setter_address, + ), ) .unwrap(); let foundry_contracts_path = ecosystem_config.path_to_l1_foundry(); diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/setup_legacy_bridge.rs b/zkstack_cli/crates/zkstack/src/commands/chain/setup_legacy_bridge.rs index 631dffffac41..24ef9d3c16d9 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/setup_legacy_bridge.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/setup_legacy_bridge.rs @@ -31,6 +31,14 @@ pub async fn setup_legacy_bridge( transparent_proxy_admin: contracts_config .ecosystem_contracts .transparent_proxy_admin_addr, + l1_nullifier_proxy: contracts_config + .bridges + .l1_nullifier_addr + .context("`l1_nullifier` missing")?, + l1_native_token_vault: contracts_config + .ecosystem_contracts + .native_token_vault_addr + .context("`native_token_vault` missing")?, erc20bridge_proxy: contracts_config.bridges.erc20.l1_address, token_weth_address: Default::default(), chain_id: chain_config.chain_id, diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/utils.rs b/zkstack_cli/crates/zkstack/src/commands/chain/utils.rs new file mode 100644 index 000000000000..830be06a7bb3 --- /dev/null +++ b/zkstack_cli/crates/zkstack/src/commands/chain/utils.rs @@ -0,0 +1,12 @@ +use ethers::abi::encode; +use zksync_types::{web3::keccak256, Address, H256, L2_NATIVE_TOKEN_VAULT_ADDRESS, U256}; + +pub fn encode_ntv_asset_id(l1_chain_id: U256, addr: Address) -> H256 { + let encoded_data = encode(&[ + ethers::abi::Token::Uint(l1_chain_id), + ethers::abi::Token::Address(L2_NATIVE_TOKEN_VAULT_ADDRESS), + ethers::abi::Token::Address(addr), + ]); + + H256(keccak256(&encoded_data)) +} diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/contracts.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/contracts.rs index 9a9aeeb30305..a3150bc96235 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/contracts.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/contracts.rs @@ -3,7 +3,9 @@ use std::path::PathBuf; use clap::Parser; use xshell::Shell; use zkstack_cli_common::{ - contracts::{build_l1_contracts, build_l2_contracts, build_system_contracts}, + contracts::{ + build_l1_contracts, build_l1_da_contracts, build_l2_contracts, build_system_contracts, + }, logger, spinner::Spinner, }; @@ -11,8 +13,9 @@ use zkstack_cli_config::EcosystemConfig; use crate::commands::dev::messages::{ MSG_BUILDING_CONTRACTS, MSG_BUILDING_CONTRACTS_SUCCESS, MSG_BUILDING_L1_CONTRACTS_SPINNER, - MSG_BUILDING_L2_CONTRACTS_SPINNER, MSG_BUILDING_SYSTEM_CONTRACTS_SPINNER, - MSG_BUILD_L1_CONTRACTS_HELP, MSG_BUILD_L2_CONTRACTS_HELP, MSG_BUILD_SYSTEM_CONTRACTS_HELP, + MSG_BUILDING_L1_DA_CONTRACTS_SPINNER, MSG_BUILDING_L2_CONTRACTS_SPINNER, + MSG_BUILDING_SYSTEM_CONTRACTS_SPINNER, MSG_BUILD_L1_CONTRACTS_HELP, + MSG_BUILD_L1_DA_CONTRACTS_HELP, MSG_BUILD_L2_CONTRACTS_HELP, MSG_BUILD_SYSTEM_CONTRACTS_HELP, MSG_NOTHING_TO_BUILD_MSG, }; @@ -20,6 +23,8 @@ use crate::commands::dev::messages::{ pub struct ContractsArgs { #[clap(long, alias = "l1", help = MSG_BUILD_L1_CONTRACTS_HELP, default_missing_value = "true", num_args = 0..=1)] pub l1_contracts: Option, + #[clap(long, alias = "l1-da", help = MSG_BUILD_L1_DA_CONTRACTS_HELP, default_missing_value = "true", num_args = 0..=1)] + pub l1_da_contracts: Option, #[clap(long, alias = "l2", help = MSG_BUILD_L2_CONTRACTS_HELP, default_missing_value = "true", num_args = 0..=1)] pub l2_contracts: Option, #[clap(long, alias = "sc", help = MSG_BUILD_SYSTEM_CONTRACTS_HELP, default_missing_value = "true", num_args = 0..=1)] @@ -31,9 +36,11 @@ impl ContractsArgs { if self.l1_contracts.is_none() && self.l2_contracts.is_none() && self.system_contracts.is_none() + && self.l1_da_contracts.is_none() { return vec![ ContractType::L1, + ContractType::L1DA, ContractType::L2, ContractType::SystemContracts, ]; @@ -43,6 +50,9 @@ impl ContractsArgs { if self.l1_contracts.unwrap_or(false) { contracts.push(ContractType::L1); } + if self.l1_da_contracts.unwrap_or(false) { + contracts.push(ContractType::L1DA); + } if self.l2_contracts.unwrap_or(false) { contracts.push(ContractType::L2); } @@ -56,6 +66,7 @@ impl ContractsArgs { #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum ContractType { L1, + L1DA, L2, SystemContracts, } @@ -74,6 +85,11 @@ impl ContractBuilder { msg: MSG_BUILDING_L1_CONTRACTS_SPINNER.to_string(), link_to_code: ecosystem.link_to_code.clone(), }, + ContractType::L1DA => Self { + cmd: Box::new(build_l1_da_contracts), + msg: MSG_BUILDING_L1_DA_CONTRACTS_SPINNER.to_string(), + link_to_code: ecosystem.link_to_code.clone(), + }, ContractType::L2 => Self { cmd: Box::new(build_l2_contracts), msg: MSG_BUILDING_L2_CONTRACTS_SPINNER.to_string(), diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/gateway.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/gateway.rs new file mode 100644 index 000000000000..ea8f96de6bcd --- /dev/null +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/gateway.rs @@ -0,0 +1,815 @@ +use std::{num::NonZeroUsize, str::FromStr, sync::Arc}; + +use anyhow::Context; +use clap::{Parser, ValueEnum}; +use ethers::{ + abi::{encode, parse_abi, Token}, + contract::{abigen, BaseContract}, + providers::{Http, Middleware, Provider}, + utils::hex, +}; +use serde::{Deserialize, Serialize}; +use strum::EnumIter; +use xshell::Shell; +use zkstack_cli_config::{ + forge_interface::gateway_ecosystem_upgrade::output::GatewayEcosystemUpgradeOutput, + traits::{ReadConfig, ZkStackConfig}, + ContractsConfig, +}; +use zksync_contracts::{chain_admin_contract, hyperchain_contract, DIAMOND_CUT}; +use zksync_types::{ + ethabi, + url::SensitiveUrl, + web3::{keccak256, Bytes}, + Address, L1BatchNumber, L2BlockNumber, L2ChainId, ProtocolVersionId, H256, + L2_NATIVE_TOKEN_VAULT_ADDRESS, U256, +}; +use zksync_web3_decl::{ + client::{Client, DynClient, L2}, + namespaces::{UnstableNamespaceClient, ZksNamespaceClient}, +}; + +/// To support both functionality of assignment inside local tests +/// and to print out the changes to the user the following function is used. +#[macro_export] +macro_rules! assign_or_print { + ($statement:expr, $value:expr, $should_assign:expr) => { + if $should_assign { + $statement = $value; + } else { + println!("{} = {:#?}", stringify!($statement), $value); + } + }; +} + +#[macro_export] +macro_rules! amend_config_pre_upgrade { + () => { + assign_or_print!() + }; +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub(crate) struct GatewayUpgradeInfo { + // Information about pre-upgrade contracts. + l1_chain_id: u32, + bridgehub_addr: Address, + old_validator_timelock: Address, + l1_legacy_shared_bridge: Address, + + // Information about the post-upgrade contracts. + ctm_deployment_tracker_proxy_addr: Address, + native_token_vault_addr: Address, + l1_bytecodes_supplier_addr: Address, + rollup_l1_da_validator_addr: Address, + no_da_validium_l1_validator_addr: Address, + expected_rollup_l2_da_validator: Address, + expected_validium_l2_da_validator: Address, + new_validator_timelock: Address, + + l1_wrapped_base_token_store: Address, + chain_upgrade_diamond_cut: Bytes, + + new_protocol_version: u64, + old_protocol_version: u64, +} + +#[derive(Debug, Default)] +pub struct FetchedChainInfo { + l2_legacy_shared_bridge_addr: Address, + hyperchain_addr: Address, + base_token_addr: Address, +} + +// Bridgehub ABI +abigen!( + BridgehubAbi, + r"[ + function getHyperchain(uint256)(address) +]" +); + +// L1SharedBridgeLegacyStore ABI +abigen!( + L1SharedBridgeLegacyAbi, + r"[ + function l2BridgeAddress(uint256 _chainId)(address) +]" +); + +// L2WrappedBaseTokenStore ABI +abigen!( + L2WrappedBaseTokenStoreAbi, + r"[ + function l2WBaseTokenAddress(uint256 _chainId)(address) +]" +); + +// ZKChain ABI +abigen!( + ZKChainAbi, + r"[ + function getPubdataPricingMode()(uint256) + function getBaseToken()(address) + function getTotalBatchesCommitted() external view returns (uint256) + function getTotalBatchesVerified() external view returns (uint256) +]" +); + +// ZKChain ABI +abigen!( + ValidatorTimelockAbi, + r"[ + function validators(uint256 _chainId, address _validator)(bool) +]" +); + +async fn verify_next_batch_new_version( + batch_number: u32, + main_node_client: &DynClient, +) -> anyhow::Result<()> { + let (_, right_bound) = main_node_client + .get_l2_block_range(L1BatchNumber(batch_number)) + .await? + .context("Range must be present for a batch")?; + + let next_l2_block = right_bound + 1; + + let block_details = main_node_client + .get_block_details(L2BlockNumber(next_l2_block.as_u32())) + .await? + .with_context(|| format!("No L2 block is present after the batch {}", batch_number))?; + + let protocol_version = block_details.protocol_version.with_context(|| { + format!( + "Protocol version not present for block {}", + next_l2_block.as_u64() + ) + })?; + anyhow::ensure!( + protocol_version >= ProtocolVersionId::gateway_upgrade(), + "THe block does not yet contain the gateway upgrade" + ); + + Ok(()) +} + +pub async fn check_chain_readiness( + l1_rpc_url: String, + l2_rpc_url: String, + l2_chain_id: u64, +) -> anyhow::Result<()> { + let l1_provider = match Provider::::try_from(&l1_rpc_url) { + Ok(provider) => provider, + Err(err) => { + anyhow::bail!("Connection error: {:#?}", err); + } + }; + let l1_client = Arc::new(l1_provider); + + let l2_client = Client::http(SensitiveUrl::from_str(&l2_rpc_url).unwrap()) + .context("failed creating JSON-RPC client for main node")? + .for_network(L2ChainId::new(l2_chain_id).unwrap().into()) + .with_allowed_requests_per_second(NonZeroUsize::new(100_usize).unwrap()) + .build(); + let l2_client = Box::new(l2_client) as Box>; + + let inflight_txs_count: usize = l2_client.get_unconfirmed_txs_count().await?; + let diamond_proxy_addr = l2_client.get_main_contract().await?; + + if inflight_txs_count != 0 { + anyhow::bail!("Chain not ready since there are inflight txs!"); + } + + let zkchain = ZKChainAbi::new(diamond_proxy_addr, l1_client.clone()); + let batches_committed = zkchain.get_total_batches_committed().await?.as_u32(); + let batches_verified = zkchain.get_total_batches_verified().await?.as_u32(); + + verify_next_batch_new_version(batches_committed, l2_client.as_ref()).await?; + verify_next_batch_new_version(batches_verified, l2_client.as_ref()).await?; + + Ok(()) +} + +async fn verify_correct_l2_wrapped_base_token( + l2_rpc_url: String, + addr: Address, +) -> anyhow::Result<()> { + // Connect to the L1 Ethereum network + let l2_provider = match Provider::::try_from(&l2_rpc_url) { + Ok(provider) => provider, + Err(err) => { + anyhow::bail!("Connection error: {:#?}", err); + } + }; + + let code = l2_provider.get_code(addr, None).await?; + + if code.len() == 0 { + anyhow::bail!("L2 wrapped base token code can not be empty"); + } + + // TODO(EVM-939): also verify that the code is correct. + + Ok(()) +} + +pub async fn fetch_chain_info( + upgrade_info: &GatewayUpgradeInfo, + args: &GatewayUpgradeArgsInner, +) -> anyhow::Result { + // Connect to the L1 Ethereum network + let provider = match Provider::::try_from(&args.l1_rpc_url) { + Ok(provider) => provider, + Err(err) => { + anyhow::bail!("Connection error: {:#?}", err); + } + }; + + let client = Arc::new(provider); + let chain_id = U256::from(args.chain_id); + + let bridgehub = BridgehubAbi::new(upgrade_info.bridgehub_addr, client.clone()); + let hyperchain_addr = bridgehub.get_hyperchain(chain_id).await?; + if hyperchain_addr == Address::zero() { + anyhow::bail!("Chain not present in bridgehub"); + } + let l1_legacy_bridge = + L1SharedBridgeLegacyAbi::new(upgrade_info.l1_legacy_shared_bridge, client.clone()); + + let l2_legacy_shared_bridge_addr = l1_legacy_bridge.l_2_bridge_address(chain_id).await?; + // Creation of the shared bridge is one of the steps for chain creation, + // so it is very weird that a chain does not have it, so we fail here. + anyhow::ensure!( + l2_legacy_shared_bridge_addr != Address::zero(), + "Chain not registered inside the L1 shared bridge!" + ); + + let l2_wrapped_base_token_store = + L2WrappedBaseTokenStoreAbi::new(upgrade_info.l1_wrapped_base_token_store, client.clone()); + + let l2_predeployed_wrapped_base_token = l2_wrapped_base_token_store + .l_2w_base_token_address(chain_id) + .await?; + + // Even in case the user does not want the script to fail due to this issue, + // we still display it just in case. + if l2_predeployed_wrapped_base_token == Address::zero() && args.dangerous_no_cross_check { + println!("\n\nWARNING: the chain does not contain wrapped base token. It is dangerous since the security of it depends on the ecosystem admin\n\n"); + } + + let zkchain = ZKChainAbi::new(hyperchain_addr, client.clone()); + + let base_token_addr = zkchain.get_base_token().await?; + + if !args.dangerous_no_cross_check { + // Firstly, check that the validators are present in the current timelock + let old_timelock = + ValidatorTimelockAbi::new(upgrade_info.old_validator_timelock, client.clone()); + + if !old_timelock + .validators(chain_id, args.validator_addr1) + .await? + { + anyhow::bail!( + "{} not validator", + hex_address_display(args.validator_addr1) + ); + } + if !old_timelock + .validators(chain_id, args.validator_addr2) + .await? + { + anyhow::bail!( + "{} not validator", + hex_address_display(args.validator_addr2) + ); + } + + if l2_predeployed_wrapped_base_token == Address::zero() { + anyhow::bail!("the chain does not contain wrapped base token. It is dangerous since the security of it depends on the ecosystem admin"); + } + + verify_correct_l2_wrapped_base_token( + args.l2_rpc_url.clone(), + l2_predeployed_wrapped_base_token, + ) + .await?; + + // Secondly, we check that the DA layer corresponds to the current pubdata pricing mode. + + // On L1 it is an enum with 0 meaaning a rollup and 1 meaning a validium. + // In the old version, it denoted how the pubdata will be checked. We use it to cross-check the + // user's input + let pricing_mode = zkchain.get_pubdata_pricing_mode().await?; + let pricing_mode_rollup = pricing_mode == U256::zero(); + + if args.da_mode.is_rollup() != pricing_mode_rollup { + anyhow::bail!("DA mode in consistent with the current system"); + } + } + + Ok(FetchedChainInfo { + l2_legacy_shared_bridge_addr, + hyperchain_addr, + base_token_addr, + }) +} + +impl ZkStackConfig for GatewayUpgradeInfo {} + +pub fn encode_ntv_asset_id(l1_chain_id: U256, addr: Address) -> H256 { + let encoded_data = encode(&[ + ethers::abi::Token::Uint(l1_chain_id), + ethers::abi::Token::Address(L2_NATIVE_TOKEN_VAULT_ADDRESS), + ethers::abi::Token::Address(addr), + ]); + + H256(keccak256(&encoded_data)) +} + +impl GatewayUpgradeInfo { + pub fn from_gateway_ecosystem_upgrade( + bridgehub_addr: Address, + gateway_ecosystem_upgrade: GatewayEcosystemUpgradeOutput, + ) -> Self { + Self { + l1_chain_id: gateway_ecosystem_upgrade.l1_chain_id, + bridgehub_addr, + old_validator_timelock: gateway_ecosystem_upgrade + .contracts_config + .old_validator_timelock, + l1_legacy_shared_bridge: gateway_ecosystem_upgrade + .contracts_config + .l1_legacy_shared_bridge, + ctm_deployment_tracker_proxy_addr: gateway_ecosystem_upgrade + .deployed_addresses + .bridgehub + .ctm_deployment_tracker_proxy_addr, + native_token_vault_addr: gateway_ecosystem_upgrade + .deployed_addresses + .native_token_vault_addr, + l1_bytecodes_supplier_addr: gateway_ecosystem_upgrade + .deployed_addresses + .l1_bytecodes_supplier_addr, + rollup_l1_da_validator_addr: gateway_ecosystem_upgrade + .deployed_addresses + .rollup_l1_da_validator_addr, + no_da_validium_l1_validator_addr: gateway_ecosystem_upgrade + .deployed_addresses + .validium_l1_da_validator_addr, + expected_rollup_l2_da_validator: gateway_ecosystem_upgrade + .contracts_config + .expected_rollup_l2_da_validator, + expected_validium_l2_da_validator: gateway_ecosystem_upgrade + .contracts_config + .expected_validium_l2_da_validator, + new_validator_timelock: gateway_ecosystem_upgrade + .deployed_addresses + .validator_timelock_addr, + // Note that on the contract side of things this contract is called `L2WrappedBaseTokenStore`, + // while on the server side for consistency with the conventions, where the prefix denotes + // the location of the contracts we call it `l1_wrapped_base_token_store` + l1_wrapped_base_token_store: gateway_ecosystem_upgrade + .deployed_addresses + .l2_wrapped_base_token_store_addr, + chain_upgrade_diamond_cut: gateway_ecosystem_upgrade.chain_upgrade_diamond_cut, + new_protocol_version: gateway_ecosystem_upgrade + .contracts_config + .new_protocol_version, + old_protocol_version: gateway_ecosystem_upgrade + .contracts_config + .old_protocol_version, + } + } + + fn get_l1_da_validator(&self, da_mode: DAMode) -> Address { + if da_mode.is_rollup() { + self.rollup_l1_da_validator_addr + } else { + self.no_da_validium_l1_validator_addr + } + } + + fn get_l2_da_validator(&self, da_mode: DAMode) -> Address { + if da_mode.is_rollup() { + self.expected_rollup_l2_da_validator + } else { + self.expected_validium_l2_da_validator + } + } + + pub fn update_contracts_config( + &self, + contracts_config: &mut ContractsConfig, + chain_info: &FetchedChainInfo, + da_mode: DAMode, + assign: bool, + ) { + assign_or_print!( + contracts_config.l2.legacy_shared_bridge_addr, + Some(chain_info.l2_legacy_shared_bridge_addr), + assign + ); + + let base_token_id = + encode_ntv_asset_id(U256::from(self.l1_chain_id), chain_info.base_token_addr); + assign_or_print!( + contracts_config.l1.base_token_asset_id, + Some(base_token_id), + assign + ); + + assign_or_print!( + contracts_config + .ecosystem_contracts + .l1_wrapped_base_token_store, + Some(self.l1_wrapped_base_token_store), + assign + ); + + assign_or_print!( + contracts_config + .ecosystem_contracts + .stm_deployment_tracker_proxy_addr, + Some(self.ctm_deployment_tracker_proxy_addr), + assign + ); + assign_or_print!( + contracts_config + .ecosystem_contracts + .stm_deployment_tracker_proxy_addr, + Some(self.ctm_deployment_tracker_proxy_addr), + assign + ); + assign_or_print!( + contracts_config.ecosystem_contracts.native_token_vault_addr, + Some(self.native_token_vault_addr), + assign + ); + assign_or_print!( + contracts_config + .ecosystem_contracts + .l1_bytecodes_supplier_addr, + Some(self.l1_bytecodes_supplier_addr), + assign + ); + assign_or_print!( + contracts_config.l1.rollup_l1_da_validator_addr, + Some(self.rollup_l1_da_validator_addr), + assign + ); + assign_or_print!( + contracts_config.l1.no_da_validium_l1_validator_addr, + Some(self.no_da_validium_l1_validator_addr), + assign + ); + + assign_or_print!( + contracts_config.l2.da_validator_addr, + Some(self.get_l2_da_validator(da_mode)), + assign + ); + + assign_or_print!( + contracts_config.l2.l2_native_token_vault_proxy_addr, + Some(L2_NATIVE_TOKEN_VAULT_ADDRESS), + assign + ); + } + + // Updates to the config that should be done somewhere after the upgrade is fully over. + // They do not have to updated for the system to work smoothly during the upgrade, but after + // "stage 2" they are desirable to be updated for consistency + pub fn _post_upgrade_update_contracts_config( + &self, + _config: &mut ContractsConfig, + _assign: bool, + ) { + todo!() + } +} + +#[derive( + Debug, Serialize, Deserialize, Clone, Copy, ValueEnum, EnumIter, strum::Display, PartialEq, Eq, +)] +pub(crate) enum DAMode { + Validium, + TemporaryRollup, + PermanentRollup, +} + +impl DAMode { + fn is_rollup(&self) -> bool { + matches!(self, Self::TemporaryRollup | Self::PermanentRollup) + } +} + +#[derive(Debug, Clone, Serialize)] +struct AdminCall { + description: String, + target: Address, + #[serde(serialize_with = "serialize_hex")] + data: Vec, + value: U256, +} + +impl AdminCall { + fn into_token(self) -> Token { + let Self { + target, + data, + value, + .. + } = self; + Token::Tuple(vec![ + Token::Address(target), + Token::Uint(value), + Token::Bytes(data), + ]) + } +} + +fn hex_address_display(addr: Address) -> String { + format!("0x{}", hex::encode(addr.0)) +} + +fn serialize_hex(bytes: &Vec, serializer: S) -> Result +where + S: serde::Serializer, +{ + let hex_string = format!("0x{}", hex::encode(bytes)); + serializer.serialize_str(&hex_string) +} + +#[derive(Debug, Clone)] +pub struct AdminCallBuilder { + calls: Vec, + validator_timelock_abi: BaseContract, + zkchain_abi: ethabi::Contract, + chain_admin_abi: ethabi::Contract, +} + +impl AdminCallBuilder { + pub fn new() -> Self { + Self { + calls: vec![], + validator_timelock_abi: BaseContract::from( + parse_abi(&[ + "function addValidator(uint256 _chainId, address _newValidator) external", + ]) + .unwrap(), + ), + zkchain_abi: hyperchain_contract(), + chain_admin_abi: chain_admin_contract(), + } + } + + pub fn append_validator( + &mut self, + chain_id: u64, + validator_timelock_addr: Address, + validator_addr: Address, + ) { + let data = self + .validator_timelock_abi + .encode("addValidator", (U256::from(chain_id), validator_addr)) + .unwrap(); + let description = format!( + "Adding validator 0x{}", + hex::encode(validator_timelock_addr.0) + ); + + let call = AdminCall { + description, + data: data.to_vec(), + target: validator_timelock_addr, + value: U256::zero(), + }; + + self.calls.push(call); + } + + pub fn append_execute_upgrade( + &mut self, + hyperchain_addr: Address, + protocol_version: u64, + diamond_cut_data: Bytes, + ) { + let diamond_cut = DIAMOND_CUT.decode_input(&diamond_cut_data.0).unwrap()[0].clone(); + + let data = self + .zkchain_abi + .function("upgradeChainFromVersion") + .unwrap() + .encode_input(&[Token::Uint(protocol_version.into()), diamond_cut]) + .unwrap(); + let description = "Executing upgrade:".to_string(); + + let call = AdminCall { + description, + data: data.to_vec(), + target: hyperchain_addr, + value: U256::zero(), + }; + + self.calls.push(call); + } + + pub fn append_set_da_validator_pair( + &mut self, + hyperchain_addr: Address, + l1_da_validator: Address, + l2_da_validator: Address, + ) { + let data = self + .zkchain_abi + .function("setDAValidatorPair") + .unwrap() + .encode_input(&[ + Token::Address(l1_da_validator), + Token::Address(l2_da_validator), + ]) + .unwrap(); + let description = "Executing upgrade:".to_string(); + + let call = AdminCall { + description, + data: data.to_vec(), + target: hyperchain_addr, + value: U256::zero(), + }; + + self.calls.push(call); + } + + pub fn append_make_permanent_rollup(&mut self, hyperchain_addr: Address) { + let data = self + .zkchain_abi + .function("makePermanentRollup") + .unwrap() + .encode_input(&[]) + .unwrap(); + let description = "Make permanent rollup:".to_string(); + + let call = AdminCall { + description, + data: data.to_vec(), + target: hyperchain_addr, + value: U256::zero(), + }; + + self.calls.push(call); + } + + pub fn display(&self) { + // Serialize with pretty printing + let serialized = serde_json::to_string_pretty(&self.calls).unwrap(); + + // Output the serialized JSON + println!("{}", serialized); + } + + pub fn compile_full_calldata(self) -> Vec { + let tokens: Vec<_> = self.calls.into_iter().map(|x| x.into_token()).collect(); + + let data = self + .chain_admin_abi + .function("multicall") + .unwrap() + .encode_input(&[Token::Array(tokens), Token::Bool(true)]) + .unwrap(); + + data.to_vec() + } +} + +fn chain_admin_abi() -> BaseContract { + BaseContract::from( + parse_abi(&[ + "function setUpgradeTimestamp(uint256 _protocolVersion, uint256 _upgradeTimestamp) external", + ]) + .unwrap(), + ) +} + +pub fn set_upgrade_timestamp_calldata(packed_protocol_version: u64, timestamp: u64) -> Vec { + let chain_admin = chain_admin_abi(); + + chain_admin + .encode("setUpgradeTimestamp", (packed_protocol_version, timestamp)) + .unwrap() + .to_vec() +} + +#[derive(Parser, Debug, Clone)] +pub struct GatewayUpgradeCalldataArgs { + upgrade_description_path: String, + chain_id: u64, + l1_rpc_url: String, + l2_rpc_url: String, + validator_addr1: Address, + validator_addr2: Address, + server_upgrade_timestamp: u64, + da_mode: DAMode, + #[clap(long, default_missing_value = "false")] + dangerous_no_cross_check: Option, +} + +pub struct GatewayUpgradeArgsInner { + pub chain_id: u64, + pub l1_rpc_url: String, + pub l2_rpc_url: String, + pub validator_addr1: Address, + pub validator_addr2: Address, + pub da_mode: DAMode, + pub dangerous_no_cross_check: bool, +} + +impl From for GatewayUpgradeArgsInner { + fn from(value: GatewayUpgradeCalldataArgs) -> Self { + Self { + chain_id: value.chain_id, + l1_rpc_url: value.l1_rpc_url, + l2_rpc_url: value.l2_rpc_url, + validator_addr1: value.validator_addr1, + validator_addr2: value.validator_addr2, + da_mode: value.da_mode, + dangerous_no_cross_check: value.dangerous_no_cross_check.unwrap_or_default(), + } + } +} + +pub fn get_admin_call_builder( + upgrade_info: &GatewayUpgradeInfo, + chain_info: &FetchedChainInfo, + args: GatewayUpgradeArgsInner, +) -> AdminCallBuilder { + let mut admin_calls_finalize = AdminCallBuilder::new(); + + admin_calls_finalize.append_validator( + args.chain_id, + upgrade_info.new_validator_timelock, + args.validator_addr1, + ); + admin_calls_finalize.append_validator( + args.chain_id, + upgrade_info.new_validator_timelock, + args.validator_addr2, + ); + + admin_calls_finalize.append_execute_upgrade( + chain_info.hyperchain_addr, + upgrade_info.old_protocol_version, + upgrade_info.chain_upgrade_diamond_cut.clone(), + ); + + admin_calls_finalize.append_set_da_validator_pair( + chain_info.hyperchain_addr, + upgrade_info.get_l1_da_validator(args.da_mode), + upgrade_info.get_l2_da_validator(args.da_mode), + ); + + if args.da_mode == DAMode::PermanentRollup { + admin_calls_finalize.append_make_permanent_rollup(chain_info.hyperchain_addr); + } + + admin_calls_finalize +} + +pub(crate) async fn run(shell: &Shell, args: GatewayUpgradeCalldataArgs) -> anyhow::Result<()> { + // 0. Read the GatewayUpgradeInfo + + let upgrade_info = GatewayUpgradeInfo::read(shell, &args.upgrade_description_path)?; + + // 1. Update all the configs + + let chain_info = fetch_chain_info(&upgrade_info, &args.clone().into()).await?; + + upgrade_info.update_contracts_config(&mut Default::default(), &chain_info, args.da_mode, false); + + // 2. Generate calldata + + let schedule_calldata = set_upgrade_timestamp_calldata( + args.server_upgrade_timestamp, + upgrade_info.new_protocol_version, + ); + + println!( + "Calldata to schedule upgrade: {}", + hex::encode(&schedule_calldata) + ); + + let admin_calls_finalize = get_admin_call_builder(&upgrade_info, &chain_info, args.into()); + + admin_calls_finalize.display(); + + let chain_admin_calldata = admin_calls_finalize.compile_full_calldata(); + + println!( + "Full calldata to call `ChainAdmin` with : {}", + hex::encode(&chain_admin_calldata) + ); + + Ok(()) +} diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/mod.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/mod.rs index a292168dc6e0..ac41d76b3c4e 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/mod.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/mod.rs @@ -3,6 +3,8 @@ pub mod config_writer; pub mod contracts; pub mod database; pub mod fmt; +#[cfg(feature = "gateway")] +pub mod gateway; pub mod genesis; pub mod lint; pub(crate) mod lint_utils; diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/integration.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/integration.rs index 7a140644dcec..67976c340cb5 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/integration.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/integration.rs @@ -43,7 +43,7 @@ pub async fn run(shell: &Shell, args: IntegrationArgs) -> anyhow::Result<()> { let test_pattern = args.test_pattern; let mut command = cmd!( shell, - "yarn jest --forceExit --testTimeout 120000 -t {test_pattern...}" + "yarn jest --forceExit --testTimeout 350000 -t {test_pattern...}" ) .env("CHAIN_NAME", ecosystem_config.current_chain()) .env("MASTER_WALLET_PK", wallets.get_test_pk(&chain_config)?); diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/messages.rs b/zkstack_cli/crates/zkstack/src/commands/dev/messages.rs index 235aa95ee492..b65750b34341 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/messages.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/messages.rs @@ -13,6 +13,9 @@ pub(super) const MSG_SUBCOMMAND_CLEAN: &str = "Clean artifacts"; pub(super) const MSG_SUBCOMMAND_LINT_ABOUT: &str = "Lint code"; pub(super) const MSG_CONTRACTS_ABOUT: &str = "Build contracts"; pub(super) const MSG_CONFIG_WRITER_ABOUT: &str = "Overwrite general config"; +#[cfg(feature = "gateway")] +pub(super) const MSG_GATEWAY_UPGRADE_CALLDATA: &str = + "Gateway upgrade checker and calldata generator"; pub(super) const MSG_SUBCOMMAND_FMT_ABOUT: &str = "Format code"; @@ -110,9 +113,11 @@ pub(super) const MSG_NOTHING_TO_BUILD_MSG: &str = "Nothing to build!"; pub(super) const MSG_BUILDING_CONTRACTS: &str = "Building contracts"; pub(super) const MSG_BUILDING_L2_CONTRACTS_SPINNER: &str = "Building L2 contracts.."; pub(super) const MSG_BUILDING_L1_CONTRACTS_SPINNER: &str = "Building L1 contracts.."; +pub(super) const MSG_BUILDING_L1_DA_CONTRACTS_SPINNER: &str = "Building L1 DA contracts.."; pub(super) const MSG_BUILDING_SYSTEM_CONTRACTS_SPINNER: &str = "Building system contracts.."; pub(super) const MSG_BUILDING_CONTRACTS_SUCCESS: &str = "Contracts built successfully"; pub(super) const MSG_BUILD_L1_CONTRACTS_HELP: &str = "Build L1 contracts"; +pub(super) const MSG_BUILD_L1_DA_CONTRACTS_HELP: &str = "Build L1 DA contracts"; pub(super) const MSG_BUILD_L2_CONTRACTS_HELP: &str = "Build L2 contracts"; pub(super) const MSG_BUILD_SYSTEM_CONTRACTS_HELP: &str = "Build system contracts"; diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/mod.rs b/zkstack_cli/crates/zkstack/src/commands/dev/mod.rs index 409c3a764eb1..45e429129082 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/mod.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/mod.rs @@ -1,5 +1,7 @@ use clap::Subcommand; use commands::status::args::StatusArgs; +#[cfg(feature = "gateway")] +use messages::MSG_GATEWAY_UPGRADE_CALLDATA; use messages::MSG_STATUS_ABOUT; use xshell::Shell; @@ -15,7 +17,7 @@ use crate::commands::dev::messages::{ MSG_SUBCOMMAND_SNAPSHOTS_CREATOR_ABOUT, MSG_SUBCOMMAND_TESTS_ABOUT, }; -mod commands; +pub(crate) mod commands; mod consts; mod dals; mod defaults; @@ -47,6 +49,9 @@ pub enum DevCommands { Status(StatusArgs), #[command(about = MSG_GENERATE_GENESIS_ABOUT, alias = "genesis")] GenerateGenesis, + #[cfg(feature = "gateway")] + #[command(about = MSG_GATEWAY_UPGRADE_CALLDATA)] + GatewayUpgradeCalldata(commands::gateway::GatewayUpgradeCalldataArgs), } pub async fn run(shell: &Shell, args: DevCommands) -> anyhow::Result<()> { @@ -65,6 +70,8 @@ pub async fn run(shell: &Shell, args: DevCommands) -> anyhow::Result<()> { } DevCommands::Status(args) => commands::status::run(shell, args).await?, DevCommands::GenerateGenesis => commands::genesis::run(shell).await?, + #[cfg(feature = "gateway")] + DevCommands::GatewayUpgradeCalldata(args) => commands::gateway::run(shell, args).await?, } Ok(()) } diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/create.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/create.rs index c49739d022c9..d997230d5611 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/create.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/create.rs @@ -70,7 +70,7 @@ impl EcosystemCreateArgs { link_to_code, wallet_creation: chain.wallet_creation, wallet_path: chain.wallet_path.clone(), - chain_args: chain, + chain_args: chain.clone(), start_containers, update_submodules: self.update_submodules, }) diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/gateway_upgrade.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/gateway_upgrade.rs index 0301853e1acf..50be243ab704 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/gateway_upgrade.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/gateway_upgrade.rs @@ -1,4 +1,3 @@ -/// TODO(EVM-927): Note that the contents of this file are not useable without Gateway contracts. use std::path::PathBuf; use clap::{Parser, ValueEnum}; diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/init.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/init.rs index d6c3d675c053..cb4206c8a995 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/init.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/args/init.rs @@ -7,7 +7,7 @@ use zkstack_cli_common::{forge::ForgeScriptArgs, Prompt, PromptConfirm}; use zkstack_cli_types::L1Network; use crate::{ - commands::chain::args::genesis::GenesisArgs, + commands::chain::args::{genesis::GenesisArgs, init::da_configs::ValidiumTypeArgs}, defaults::LOCAL_RPC_URL, messages::{ MSG_DEPLOY_ECOSYSTEM_PROMPT, MSG_DEPLOY_ERC20_PROMPT, MSG_DEV_ARG_HELP, @@ -105,6 +105,12 @@ pub struct EcosystemInitArgs { pub no_port_reallocation: bool, #[clap(long)] pub update_submodules: Option, + #[clap(flatten)] + pub validium_args: ValidiumTypeArgs, + #[clap(long, default_missing_value = "false", num_args = 0..=1)] + pub support_l2_legacy_shared_bridge_test: Option, + #[clap(long, default_missing_value = "false")] + pub skip_contract_compilation_override: bool, } impl EcosystemInitArgs { @@ -146,6 +152,11 @@ impl EcosystemInitArgs { observability, ecosystem_only: self.ecosystem_only, no_port_reallocation: self.no_port_reallocation, + skip_contract_compilation_override: self.skip_contract_compilation_override, + validium_args: self.validium_args, + support_l2_legacy_shared_bridge_test: self + .support_l2_legacy_shared_bridge_test + .unwrap_or_default(), } } } @@ -159,4 +170,7 @@ pub struct EcosystemInitArgsFinal { pub observability: bool, pub ecosystem_only: bool, pub no_port_reallocation: bool, + pub skip_contract_compilation_override: bool, + pub validium_args: ValidiumTypeArgs, + pub support_l2_legacy_shared_bridge_test: bool, } diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/build_transactions.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/build_transactions.rs index 3d81e6f3b0b7..1cd76debbe15 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/build_transactions.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/build_transactions.rs @@ -49,6 +49,7 @@ pub async fn run(args: BuildTransactionsArgs, shell: &Shell) -> anyhow::Result<( &args.l1_rpc_url, Some(args.sender), false, + false, ) .await?; diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/common.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/common.rs index c31aa6252971..7255ba9e1ca5 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/common.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/common.rs @@ -16,6 +16,7 @@ use zkstack_cli_types::{L1Network, ProverMode}; use crate::utils::forge::{check_the_balance, fill_forge_private_key, WalletOwner}; +#[allow(clippy::too_many_arguments)] pub async fn deploy_l1( shell: &Shell, forge_args: &ForgeScriptArgs, @@ -24,8 +25,10 @@ pub async fn deploy_l1( l1_rpc_url: &str, sender: Option, broadcast: bool, + support_l2_legacy_shared_bridge_test: bool, ) -> anyhow::Result { let deploy_config_path = DEPLOY_ECOSYSTEM_SCRIPT_PARAMS.input(&config.link_to_code); + dbg!(config.get_default_configs_path()); let default_genesis_config = GenesisConfig::read_with_base_path(shell, config.get_default_configs_path()) .context("failed reading genesis config")?; @@ -38,6 +41,8 @@ pub async fn deploy_l1( initial_deployment_config, config.era_chain_id, config.prover_version == ProverMode::NoProofs, + config.l1_network, + support_l2_legacy_shared_bridge_test, ); deploy_config.save(shell, deploy_config_path)?; diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/gateway_upgrade.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/gateway_upgrade.rs new file mode 100644 index 000000000000..01905afb9a5d --- /dev/null +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/gateway_upgrade.rs @@ -0,0 +1,645 @@ +use anyhow::Context; +use ethers::{abi::parse_abi, contract::BaseContract, utils::hex}; +use lazy_static::lazy_static; +use serde::Deserialize; +use xshell::Shell; +use zkstack_cli_common::{db::DatabaseConfig, forge::Forge, git, spinner::Spinner}; +use zkstack_cli_config::{ + forge_interface::{ + gateway_ecosystem_upgrade::{ + input::GatewayEcosystemUpgradeInput, output::GatewayEcosystemUpgradeOutput, + }, + gateway_preparation::input::GatewayPreparationConfig, + script_params::{ + FINALIZE_UPGRADE_SCRIPT_PARAMS, GATEWAY_PREPARATION, GATEWAY_UPGRADE_ECOSYSTEM_PARAMS, + }, + }, + traits::{ReadConfig, ReadConfigWithBasePath, SaveConfig, SaveConfigWithBasePath}, + EcosystemConfig, GenesisConfig, CONFIGS_PATH, +}; +use zkstack_cli_types::ProverMode; +use zksync_basic_types::commitment::L1BatchCommitmentMode; +use zksync_types::{H160, L2_NATIVE_TOKEN_VAULT_ADDRESS, SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, U256}; + +use super::args::gateway_upgrade::{GatewayUpgradeArgs, GatewayUpgradeArgsFinal}; +use crate::{ + accept_ownership::{ + accept_admin, governance_execute_calls, make_permanent_rollup, set_da_validator_pair, + }, + commands::{ + chain, + chain::{ + args::genesis::GenesisArgsFinal, + convert_to_gateway::{ + calculate_gateway_ctm, call_script, GATEWAY_PREPARATION_INTERFACE, + }, + genesis::genesis, + }, + ecosystem::args::gateway_upgrade::GatewayUpgradeStage, + }, + defaults::{generate_db_names, DBNames, DATABASE_SERVER_URL}, + messages::{MSG_CHAIN_NOT_FOUND_ERR, MSG_GENESIS_DATABASE_ERR, MSG_INTALLING_DEPS_SPINNER}, + utils::forge::{fill_forge_private_key, WalletOwner}, +}; + +pub async fn run(args: GatewayUpgradeArgs, shell: &Shell) -> anyhow::Result<()> { + println!("Running ecosystem gateway upgrade args"); + + let mut ecosystem_config = EcosystemConfig::from_file(shell)?; + git::submodule_update(shell, ecosystem_config.link_to_code.clone())?; + + let mut final_ecosystem_args = args.fill_values_with_prompt(ecosystem_config.l1_network, true); + + match final_ecosystem_args.ecosystem_upgrade_stage { + GatewayUpgradeStage::NoGovernancePrepare => { + no_governance_prepare(&mut final_ecosystem_args, shell, &ecosystem_config).await?; + no_governance_prepare_gateway(shell, &mut ecosystem_config).await?; + } + GatewayUpgradeStage::GovernanceStage1 => { + governance_stage_1(&mut final_ecosystem_args, shell, &ecosystem_config).await?; + } + GatewayUpgradeStage::GovernanceStage2 => { + governance_stage_2(&mut final_ecosystem_args, shell, &ecosystem_config).await?; + } + GatewayUpgradeStage::NoGovernanceStage2 => { + no_governance_stage_2(&mut final_ecosystem_args, shell, &ecosystem_config).await?; + } + GatewayUpgradeStage::GovernanceStage3 => { + governance_stage_3(&mut final_ecosystem_args, shell, &ecosystem_config).await?; + } + GatewayUpgradeStage::NoGovernanceStage3 => { + no_governance_stage_3(&mut final_ecosystem_args, shell, &ecosystem_config).await?; + } + } + + Ok(()) +} + +#[derive(Debug, Deserialize)] +struct BroadcastFile { + pub transactions: Vec, +} +#[derive(Debug, Deserialize)] +struct BroadcastFileTransactions { + pub hash: String, +} + +async fn no_governance_prepare( + init_args: &mut GatewayUpgradeArgsFinal, + shell: &Shell, + ecosystem_config: &EcosystemConfig, +) -> anyhow::Result<()> { + let spinner = Spinner::new(MSG_INTALLING_DEPS_SPINNER); + spinner.finish(); + + let forge_args = init_args.forge_args.clone(); + let l1_rpc_url = init_args.l1_rpc_url.clone(); + + let new_genesis_config = GenesisConfig::read_with_base_path(shell, CONFIGS_PATH)?; + let current_contracts_config = ecosystem_config.get_contracts_config()?; + let initial_deployment_config = ecosystem_config.get_initial_deployment_config()?; + + let ecosystem_upgrade_config_path = + GATEWAY_UPGRADE_ECOSYSTEM_PARAMS.input(&ecosystem_config.link_to_code); + + let era_config = ecosystem_config + .load_chain(Some("era".to_string())) + .context("No era")?; + + // FIXME: we will have to force this in production environment + // assert_eq!(era_config.chain_id, ecosystem_config.era_chain_id); + + let gateway_upgrade_input = GatewayEcosystemUpgradeInput::new( + &new_genesis_config, + ¤t_contracts_config, + &initial_deployment_config, + ecosystem_config.era_chain_id, + era_config.get_contracts_config()?.l1.diamond_proxy_addr, + ecosystem_config.prover_version == ProverMode::NoProofs, + ); + gateway_upgrade_input.save(shell, ecosystem_upgrade_config_path.clone())?; + + let mut forge = Forge::new(&ecosystem_config.path_to_l1_foundry()) + .script( + &GATEWAY_UPGRADE_ECOSYSTEM_PARAMS.script(), + forge_args.clone(), + ) + .with_ffi() + .with_rpc_url(l1_rpc_url) + .with_slow() + .with_gas_limit(1_000_000_000_000) + .with_broadcast(); + + forge = fill_forge_private_key( + forge, + ecosystem_config.get_wallets()?.deployer.as_ref(), + WalletOwner::Deployer, + )?; + + println!("Preparing the ecosystem for the upgrade!"); + + forge.run(shell)?; + + println!("done!"); + + let l1_chain_id = era_config.l1_network.chain_id(); + + let broadcast_file: BroadcastFile = { + let file_content = std::fs::read_to_string( + ecosystem_config + .link_to_code + .join("contracts/l1-contracts") + .join(format!( + "broadcast/EcosystemUpgrade.s.sol/{}/run-latest.json", + l1_chain_id + )), + ) + .context("Failed to read broadcast file")?; + serde_json::from_str(&file_content).context("Failed to parse broadcast file")? + }; + + let mut output = GatewayEcosystemUpgradeOutput::read( + shell, + GATEWAY_UPGRADE_ECOSYSTEM_PARAMS.output(&ecosystem_config.link_to_code), + )?; + + // Add all the transaction hashes. + for tx in broadcast_file.transactions { + output.transactions.push(tx.hash); + } + + output.save_with_base_path(shell, &ecosystem_config.config)?; + + Ok(()) +} + +async fn no_governance_prepare_gateway( + shell: &Shell, + ecosystem_config: &mut EcosystemConfig, +) -> anyhow::Result<()> { + let spinner = Spinner::new(MSG_INTALLING_DEPS_SPINNER); + spinner.finish(); + + let mut contracts_config = ecosystem_config.get_contracts_config()?; + + let output = GatewayEcosystemUpgradeOutput::read( + shell, + GATEWAY_UPGRADE_ECOSYSTEM_PARAMS.output(&ecosystem_config.link_to_code), + )?; + + let mut s: String = "0x".to_string(); + s += &hex::encode(output.contracts_config.diamond_cut_data.0); + contracts_config.ecosystem_contracts.diamond_cut_data = s; + + s = "0x".to_string(); + s += &hex::encode(output.contracts_config.force_deployments_data.0); + contracts_config.ecosystem_contracts.force_deployments_data = Some(s); + + contracts_config.l1.rollup_l1_da_validator_addr = + Some(output.deployed_addresses.rollup_l1_da_validator_addr); + contracts_config.l1.no_da_validium_l1_validator_addr = + Some(output.deployed_addresses.validium_l1_da_validator_addr); + + contracts_config + .ecosystem_contracts + .stm_deployment_tracker_proxy_addr = Some( + output + .deployed_addresses + .bridgehub + .ctm_deployment_tracker_proxy_addr, + ); + contracts_config.ecosystem_contracts.native_token_vault_addr = + Some(output.deployed_addresses.native_token_vault_addr); + contracts_config + .ecosystem_contracts + .l1_bytecodes_supplier_addr = Some(output.deployed_addresses.l1_bytecodes_supplier_addr); + contracts_config.bridges.l1_nullifier_addr = Some(contracts_config.bridges.shared.l1_address); + contracts_config.ecosystem_contracts.validator_timelock_addr = + output.deployed_addresses.validator_timelock_addr; + contracts_config.l1.validator_timelock_addr = output.deployed_addresses.validator_timelock_addr; + contracts_config.bridges.shared.l1_address = + output.deployed_addresses.bridges.shared_bridge_proxy_addr; + contracts_config + .ecosystem_contracts + .expected_rollup_l2_da_validator = + Some(output.contracts_config.expected_rollup_l2_da_validator); + + contracts_config.save_with_base_path(shell, &ecosystem_config.config)?; + Ok(()) +} + +// Governance has approved the proposal, now it will insert the new protocol version into our STM (CTM) +async fn governance_stage_1( + init_args: &mut GatewayUpgradeArgsFinal, + shell: &Shell, + ecosystem_config: &EcosystemConfig, +) -> anyhow::Result<()> { + println!("Executing governance stage 1!"); + + let previous_output = GatewayEcosystemUpgradeOutput::read( + shell, + GATEWAY_UPGRADE_ECOSYSTEM_PARAMS.output(&ecosystem_config.link_to_code), + )?; + previous_output.save_with_base_path(shell, &ecosystem_config.config)?; + + // These are ABI-encoded + let stage1_calls = previous_output.governance_stage1_calls; + + governance_execute_calls( + shell, + ecosystem_config, + &ecosystem_config.get_wallets()?.governor, + stage1_calls.0, + &init_args.forge_args.clone(), + init_args.l1_rpc_url.clone(), + ) + .await?; + + let gateway_ecosystem_preparation_output = + GatewayEcosystemUpgradeOutput::read_with_base_path(shell, &ecosystem_config.config)?; + + let mut contracts_config = ecosystem_config.get_contracts_config()?; + + contracts_config + .ecosystem_contracts + .stm_deployment_tracker_proxy_addr = Some( + gateway_ecosystem_preparation_output + .deployed_addresses + .bridgehub + .ctm_deployment_tracker_proxy_addr, + ); + // This is force deployment data for creating new contracts, not really relevant here tbh, + contracts_config.ecosystem_contracts.force_deployments_data = Some(hex::encode( + &gateway_ecosystem_preparation_output + .contracts_config + .force_deployments_data + .0, + )); + contracts_config.ecosystem_contracts.native_token_vault_addr = Some( + gateway_ecosystem_preparation_output + .deployed_addresses + .native_token_vault_addr, + ); + contracts_config + .ecosystem_contracts + .l1_bytecodes_supplier_addr = Some( + gateway_ecosystem_preparation_output + .deployed_addresses + .l1_bytecodes_supplier_addr, + ); + + contracts_config.l1.rollup_l1_da_validator_addr = Some( + gateway_ecosystem_preparation_output + .deployed_addresses + .rollup_l1_da_validator_addr, + ); + + contracts_config.l1.no_da_validium_l1_validator_addr = Some( + gateway_ecosystem_preparation_output + .deployed_addresses + .validium_l1_da_validator_addr, + ); + + // This value is meaningless for the ecosystem, but we'll populate it for consistency + contracts_config.l2.da_validator_addr = Some(H160::zero()); + contracts_config.l2.l2_native_token_vault_proxy_addr = Some(L2_NATIVE_TOKEN_VAULT_ADDRESS); + contracts_config.l2.legacy_shared_bridge_addr = contracts_config.bridges.shared.l2_address; + + contracts_config.save_with_base_path(shell, &ecosystem_config.config)?; + + Ok(()) +} + +// Governance has approved the proposal, now it will insert the new protocol version into our STM (CTM) +async fn governance_stage_2( + init_args: &mut GatewayUpgradeArgsFinal, + shell: &Shell, + ecosystem_config: &EcosystemConfig, +) -> anyhow::Result<()> { + println!("Executing governance stage 2!"); + + let previous_output = + GatewayEcosystemUpgradeOutput::read_with_base_path(shell, &ecosystem_config.config)?; + + // These are ABI-encoded + let stage2_calls = previous_output.governance_stage2_calls; + + governance_execute_calls( + shell, + ecosystem_config, + &ecosystem_config.get_wallets()?.governor, + stage2_calls.0, + &init_args.forge_args.clone(), + init_args.l1_rpc_url.clone(), + ) + .await?; + + let mut contracts_config = ecosystem_config.get_contracts_config()?; + contracts_config.bridges.shared.l1_address = previous_output + .deployed_addresses + .bridges + .shared_bridge_proxy_addr; + + contracts_config.save_with_base_path(shell, &ecosystem_config.config)?; + println!("Stage2 finalized!"); + + Ok(()) +} + +lazy_static! { + static ref FINALIZE_UPGRADE: BaseContract = BaseContract::from( + parse_abi(&[ + "function initChains(address bridgehub, uint256[] chains) public", + "function initTokens(address l1NativeTokenVault, address[] tokens, uint256[] chains) public", + ]) + .unwrap(), + ); +} + +// Governance has approved the proposal, now it will insert the new protocol version into our STM (CTM) +async fn no_governance_stage_2( + init_args: &mut GatewayUpgradeArgsFinal, + shell: &Shell, + ecosystem_config: &EcosystemConfig, +) -> anyhow::Result<()> { + let contracts_config = ecosystem_config.get_contracts_config()?; + let wallets = ecosystem_config.get_wallets()?; + let deployer_private_key = wallets + .deployer + .context("deployer_wallet")? + .private_key_h256() + .context("deployer_priuvate_key")?; + + println!("Finalizing stage2 of the upgrade!"); + + let chains: Vec<_> = ecosystem_config + .list_of_chains() + .into_iter() + .filter_map(|name| { + let chain = ecosystem_config + .load_chain(Some(name)) + .expect("Invalid chain"); + (chain.name != "gateway").then_some(chain) + }) + .collect(); + + let chain_ids: Vec<_> = chains + .into_iter() + .map(|c| ethers::abi::Token::Uint(U256::from(c.chain_id.as_u64()))) + .collect(); + let mut tokens: Vec<_> = ecosystem_config + .get_erc20_tokens() + .into_iter() + .map(|t| ethers::abi::Token::Address(t.address)) + .collect(); + tokens.push(ethers::abi::Token::Address( + SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, + )); + + // Resume for accept admin doesn't work properly. Foundry assumes that if signature of the function is the same, + // than it's the same call, but because we are calling this function multiple times during the init process, + // code assumes that doing only once is enough, but actually we need to accept admin multiple times + let mut forge_args = init_args.forge_args.clone(); + forge_args.resume = false; + + let init_chains_calldata = FINALIZE_UPGRADE + .encode( + "initChains", + ( + ethers::abi::Token::Address( + contracts_config.ecosystem_contracts.bridgehub_proxy_addr, + ), + ethers::abi::Token::Array(chain_ids.clone()), + ), + ) + .unwrap(); + let init_tokens_calldata = FINALIZE_UPGRADE + .encode( + "initTokens", + ( + ethers::abi::Token::Address( + contracts_config + .ecosystem_contracts + .native_token_vault_addr + .context("native_token_vault_addr")?, + ), + ethers::abi::Token::Array(tokens), + ethers::abi::Token::Array(chain_ids), + ), + ) + .unwrap(); + + println!("Initiing chains!"); + let foundry_contracts_path = ecosystem_config.path_to_l1_foundry(); + let forge = Forge::new(&foundry_contracts_path) + .script(&FINALIZE_UPGRADE_SCRIPT_PARAMS.script(), forge_args.clone()) + .with_ffi() + .with_rpc_url(init_args.l1_rpc_url.clone()) + .with_broadcast() + .with_calldata(&init_chains_calldata) + .with_private_key(deployer_private_key); + + forge.run(shell)?; + + println!("Initiing tokens!"); + + let forge = Forge::new(&foundry_contracts_path) + .script(&FINALIZE_UPGRADE_SCRIPT_PARAMS.script(), forge_args.clone()) + .with_ffi() + .with_rpc_url(init_args.l1_rpc_url.clone()) + .with_broadcast() + .with_calldata(&init_tokens_calldata) + .with_private_key(deployer_private_key); + + forge.run(shell)?; + + println!("Done!"); + + Ok(()) +} + +async fn governance_stage_3( + init_args: &mut GatewayUpgradeArgsFinal, + shell: &Shell, + ecosystem_config: &EcosystemConfig, +) -> anyhow::Result<()> { + let chain_config = ecosystem_config + .load_chain(Some("gateway".to_string())) + .context(MSG_CHAIN_NOT_FOUND_ERR)?; + call_script( + shell, + init_args.forge_args.clone(), + &GATEWAY_PREPARATION_INTERFACE + .encode("executeGovernanceTxs", ()) + .unwrap(), + ecosystem_config, + &chain_config, + &ecosystem_config.get_wallets()?.governor, + init_args.l1_rpc_url.clone(), + true, + ) + .await?; + + Ok(()) +} + +async fn no_governance_stage_3( + init_args: &mut GatewayUpgradeArgsFinal, + shell: &Shell, + ecosystem_config: &EcosystemConfig, +) -> anyhow::Result<()> { + let chain_config = ecosystem_config + .load_chain(Some("gateway".to_string())) + .context(MSG_CHAIN_NOT_FOUND_ERR)?; + + let chain_genesis_config = chain_config.get_genesis_config()?; + let mut chain_contracts_config = chain_config.get_contracts_config()?; + + // Fund gateway's governor (chain_config.get_wallets_config()?.governor) + chain::common::distribute_eth( + ecosystem_config, + &chain_config, + init_args.l1_rpc_url.clone(), + ) + .await?; + + // Accept ownership for DiamondProxy (run by L2 Governor) + accept_admin( + shell, + ecosystem_config, + chain_contracts_config.l1.chain_admin_addr, + &chain_config.get_wallets_config()?.governor, + chain_contracts_config.l1.diamond_proxy_addr, + &init_args.forge_args.clone(), + init_args.l1_rpc_url.clone(), + ) + .await?; + + // prepare script input + let gateway_config = calculate_gateway_ctm( + shell, + init_args.forge_args.clone(), + ecosystem_config, + &chain_config, + &chain_genesis_config, + &ecosystem_config.get_initial_deployment_config().unwrap(), + init_args.l1_rpc_url.clone(), + ) + .await?; + + let gateway_preparation_config_path = GATEWAY_PREPARATION.input(&chain_config.link_to_code); + let preparation_config = GatewayPreparationConfig::new( + &chain_config, + &chain_contracts_config, + &ecosystem_config.get_contracts_config()?, + &gateway_config, + )?; + preparation_config.save(shell, gateway_preparation_config_path)?; + + // deploy filterer + let output = call_script( + shell, + init_args.forge_args.clone(), + &GATEWAY_PREPARATION_INTERFACE + .encode("deployAndSetGatewayTransactionFilterer", ()) + .unwrap(), + ecosystem_config, + &chain_config, + &chain_config.get_wallets_config()?.governor, + init_args.l1_rpc_url.clone(), + true, + ) + .await?; + + chain_contracts_config.set_transaction_filterer(output.gateway_transaction_filterer_proxy); + + // whitelist deployer + call_script( + shell, + init_args.forge_args.clone(), + &GATEWAY_PREPARATION_INTERFACE + .encode( + "grantWhitelist", + ( + output.gateway_transaction_filterer_proxy, + vec![ + ecosystem_config.get_contracts_config()?.l1.governance_addr, + ecosystem_config + .get_wallets()? + .deployer + .context("no deployer addr")? + .address, + ], + ), + ) + .unwrap(), + ecosystem_config, + &chain_config, + &chain_config.get_wallets_config()?.governor, + init_args.l1_rpc_url.clone(), + true, + ) + .await?; + + // deploy ctm + chain::convert_to_gateway::deploy_gateway_ctm( + shell, + init_args.forge_args.clone(), + ecosystem_config, + &chain_config, + &chain_genesis_config, + &ecosystem_config.get_initial_deployment_config().unwrap(), + init_args.l1_rpc_url.clone(), + ) + .await?; + + chain_contracts_config.save_with_base_path(shell, &chain_config.configs)?; + + // Set da validators + let validium_mode = + chain_config.l1_batch_commit_data_generator_mode == L1BatchCommitmentMode::Validium; + let l1_da_validator_addr = if validium_mode { + chain_contracts_config.l1.no_da_validium_l1_validator_addr + } else { + chain_contracts_config.l1.rollup_l1_da_validator_addr + }; + set_da_validator_pair( + shell, + ecosystem_config, + chain_contracts_config.l1.chain_admin_addr, + &chain_config.get_wallets_config()?.governor, + chain_contracts_config.l1.diamond_proxy_addr, + l1_da_validator_addr.context("l1_da_validator_addr")?, + chain_contracts_config + .l2 + .da_validator_addr + .context("da_validator_addr")?, + &init_args.forge_args.clone(), + init_args.l1_rpc_url.clone(), + ) + .await?; + if !validium_mode { + make_permanent_rollup( + shell, + ecosystem_config, + chain_contracts_config.l1.chain_admin_addr, + &chain_config.get_wallets_config()?.governor, + chain_contracts_config.l1.diamond_proxy_addr, + &init_args.forge_args.clone(), + init_args.l1_rpc_url.clone(), + ) + .await?; + } + + let DBNames { server_name, .. } = generate_db_names(&chain_config); + let args = GenesisArgsFinal { + server_db: DatabaseConfig::new(DATABASE_SERVER_URL.clone(), server_name), + dont_drop: false, + }; + // Run genesis (create DB and run server with --genesis) + genesis(args, shell, &chain_config) + .await + .context(MSG_GENESIS_DATABASE_ERR)?; + + Ok(()) +} diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/init.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/init.rs index 56c196fe7be9..ac66d47a831c 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/init.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/init.rs @@ -4,7 +4,7 @@ use anyhow::Context; use xshell::Shell; use zkstack_cli_common::{ config::global_config, - contracts::build_system_contracts, + contracts::{build_l1_contracts, build_l2_contracts, build_system_contracts}, forge::{Forge, ForgeScriptArgs}, git, logger, spinner::Spinner, @@ -27,6 +27,7 @@ use super::{ args::init::{EcosystemArgsFinal, EcosystemInitArgs, EcosystemInitArgsFinal}, common::deploy_l1, setup_observability, + utils::{build_da_contracts, install_yarn_dependencies}, }; use crate::{ accept_ownership::{accept_admin, accept_owner}, @@ -110,7 +111,13 @@ async fn init_ecosystem( initial_deployment_config: &InitialDeploymentConfig, ) -> anyhow::Result { let spinner = Spinner::new(MSG_INTALLING_DEPS_SPINNER); - build_system_contracts(shell.clone(), ecosystem_config.link_to_code.clone())?; + install_yarn_dependencies(shell, &ecosystem_config.link_to_code)?; + if !init_args.skip_contract_compilation_override { + build_da_contracts(shell, &ecosystem_config.link_to_code)?; + build_l1_contracts(shell.clone(), ecosystem_config.link_to_code.clone())?; + build_system_contracts(shell.clone(), ecosystem_config.link_to_code.clone())?; + build_l2_contracts(shell.clone(), ecosystem_config.link_to_code.clone())?; + } spinner.finish(); let contracts = deploy_ecosystem( @@ -119,6 +126,7 @@ async fn init_ecosystem( init_args.forge_args.clone(), ecosystem_config, initial_deployment_config, + init_args.support_l2_legacy_shared_bridge_test, ) .await?; contracts.save_with_base_path(shell, &ecosystem_config.config)?; @@ -177,6 +185,7 @@ async fn deploy_ecosystem( forge_args: ForgeScriptArgs, ecosystem_config: &EcosystemConfig, initial_deployment_config: &InitialDeploymentConfig, + support_l2_legacy_shared_bridge_test: bool, ) -> anyhow::Result { if ecosystem.deploy_ecosystem { return deploy_ecosystem_inner( @@ -185,6 +194,7 @@ async fn deploy_ecosystem( ecosystem_config, initial_deployment_config, ecosystem.l1_rpc_url.clone(), + support_l2_legacy_shared_bridge_test, ) .await; } @@ -246,6 +256,7 @@ async fn deploy_ecosystem_inner( config: &EcosystemConfig, initial_deployment_config: &InitialDeploymentConfig, l1_rpc_url: String, + support_l2_legacy_shared_bridge_test: bool, ) -> anyhow::Result { let spinner = Spinner::new(MSG_DEPLOYING_ECOSYSTEM_CONTRACTS_SPINNER); let contracts_config = deploy_l1( @@ -256,6 +267,7 @@ async fn deploy_ecosystem_inner( &l1_rpc_url, None, true, + support_l2_legacy_shared_bridge_test, ) .await?; spinner.finish(); @@ -293,21 +305,26 @@ async fn deploy_ecosystem_inner( ) .await?; - accept_admin( + // Note, that there is no admin in L1 asset router, so we do + // need to accept it + + accept_owner( shell, config, - contracts_config.l1.chain_admin_addr, + contracts_config.l1.governance_addr, &config.get_wallets()?.governor, - contracts_config.bridges.shared.l1_address, + contracts_config + .ecosystem_contracts + .state_transition_proxy_addr, &forge_args, l1_rpc_url.clone(), ) .await?; - accept_owner( + accept_admin( shell, config, - contracts_config.l1.governance_addr, + contracts_config.l1.chain_admin_addr, &config.get_wallets()?.governor, contracts_config .ecosystem_contracts @@ -317,14 +334,15 @@ async fn deploy_ecosystem_inner( ) .await?; - accept_admin( + accept_owner( shell, config, - contracts_config.l1.chain_admin_addr, + contracts_config.l1.governance_addr, &config.get_wallets()?.governor, contracts_config .ecosystem_contracts - .state_transition_proxy_addr, + .stm_deployment_tracker_proxy_addr + .context("stm_deployment_tracker_proxy_addr")?, &forge_args, l1_rpc_url.clone(), ) @@ -373,6 +391,7 @@ async fn init_chains( no_port_reallocation: final_init_args.no_port_reallocation, update_submodules: init_args.update_submodules, dev: final_init_args.dev, + validium_args: final_init_args.validium_args.clone(), }; let final_chain_init_args = chain_init_args.fill_values_with_prompt(&chain_config); diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/mod.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/mod.rs index 3f4aa7565e19..19c2888edd0d 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/mod.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/mod.rs @@ -12,6 +12,8 @@ mod change_default; mod common; mod create; pub mod create_configs; +#[cfg(feature = "gateway")] +mod gateway_upgrade; pub(crate) mod init; pub(crate) mod setup_observability; mod utils; @@ -34,6 +36,9 @@ pub enum EcosystemCommands { /// downloading Grafana dashboards from the era-observability repo #[command(alias = "obs")] SetupObservability, + /// Gateway version upgrade + #[cfg(feature = "gateway")] + GatewayUpgrade(crate::commands::ecosystem::args::gateway_upgrade::GatewayUpgradeArgs), } pub(crate) async fn run(shell: &Shell, args: EcosystemCommands) -> anyhow::Result<()> { @@ -43,5 +48,7 @@ pub(crate) async fn run(shell: &Shell, args: EcosystemCommands) -> anyhow::Resul EcosystemCommands::Init(args) => init::run(args, shell).await, EcosystemCommands::ChangeDefaultChain(args) => change_default::run(args, shell), EcosystemCommands::SetupObservability => setup_observability::run(shell), + #[cfg(feature = "gateway")] + EcosystemCommands::GatewayUpgrade(args) => gateway_upgrade::run(args, shell).await, } } diff --git a/zkstack_cli/crates/zkstack/src/commands/ecosystem/utils.rs b/zkstack_cli/crates/zkstack/src/commands/ecosystem/utils.rs index 5f6994ed38f9..77fc45ff9f83 100644 --- a/zkstack_cli/crates/zkstack/src/commands/ecosystem/utils.rs +++ b/zkstack_cli/crates/zkstack/src/commands/ecosystem/utils.rs @@ -12,3 +12,8 @@ pub(super) fn build_system_contracts(shell: &Shell, link_to_code: &Path) -> anyh let _dir_guard = shell.push_dir(link_to_code.join("contracts")); Ok(Cmd::new(cmd!(shell, "yarn sc build")).run()?) } + +pub(super) fn build_da_contracts(shell: &Shell, link_to_code: &Path) -> anyhow::Result<()> { + let _dir_guard = shell.push_dir(link_to_code.join("contracts")); + Ok(Cmd::new(cmd!(shell, "yarn da build:foundry")).run()?) +} diff --git a/zkstack_cli/crates/zkstack/src/commands/external_node/prepare_configs.rs b/zkstack_cli/crates/zkstack/src/commands/external_node/prepare_configs.rs index ed591597584c..ae36c1c9a1f4 100644 --- a/zkstack_cli/crates/zkstack/src/commands/external_node/prepare_configs.rs +++ b/zkstack_cli/crates/zkstack/src/commands/external_node/prepare_configs.rs @@ -60,6 +60,7 @@ fn prepare_configs( let mut ports = EcosystemPortsScanner::scan(shell)?; let genesis = config.get_genesis_config()?; let general = config.get_general_config()?; + let gateway = config.get_gateway_chain_config().ok(); let en_config = ENConfig { l2_chain_id: genesis.l2_chain_id, l1_chain_id: genesis.l1_chain_id, @@ -74,7 +75,7 @@ fn prepare_configs( )?, main_node_rate_limit_rps: None, bridge_addresses_refresh_interval_sec: None, - gateway_chain_id: None, + gateway_chain_id: gateway.map(|g| g.gateway_chain_id), }; let mut general_en = general.clone(); general_en.consensus_config = None; diff --git a/zkstack_cli/crates/zkstack/src/commands/server.rs b/zkstack_cli/crates/zkstack/src/commands/server.rs index cf7abf7dea21..4088a888d534 100644 --- a/zkstack_cli/crates/zkstack/src/commands/server.rs +++ b/zkstack_cli/crates/zkstack/src/commands/server.rs @@ -10,6 +10,7 @@ use zkstack_cli_config::{ traits::FileConfigWithDefaultName, ChainConfig, ContractsConfig, EcosystemConfig, GeneralConfig, GenesisConfig, SecretsConfig, WalletsConfig, }; +use zksync_config::configs::gateway::GatewayChainConfig; use crate::{ commands::args::{RunServerArgs, ServerArgs, ServerCommand, WaitArgs}, @@ -60,6 +61,19 @@ fn run_server( } else { ServerMode::Normal }; + + let gateway_config = chain_config.get_gateway_chain_config().ok(); + let mut gateway_contracts = None; + if let Some(gateway_config) = gateway_config { + gateway_contracts = if gateway_config.gateway_chain_id.0 != 0_u64 { + Some(GatewayChainConfig::get_path_with_base_path( + &chain_config.configs, + )) + } else { + None + }; + } + server .run( shell, @@ -69,7 +83,7 @@ fn run_server( GeneralConfig::get_path_with_base_path(&chain_config.configs), SecretsConfig::get_path_with_base_path(&chain_config.configs), ContractsConfig::get_path_with_base_path(&chain_config.configs), - None, + gateway_contracts, vec![], ) .context(MSG_FAILED_TO_RUN_SERVER_ERR) diff --git a/zkstack_cli/crates/zkstack/src/defaults.rs b/zkstack_cli/crates/zkstack/src/defaults.rs index 4fa15be8a118..843a15e047e9 100644 --- a/zkstack_cli/crates/zkstack/src/defaults.rs +++ b/zkstack_cli/crates/zkstack/src/defaults.rs @@ -9,6 +9,9 @@ lazy_static! { Url::parse("postgres://postgres:notsecurepassword@localhost:5432").unwrap(); pub static ref DATABASE_EXPLORER_URL: Url = Url::parse("postgres://postgres:notsecurepassword@localhost:5432").unwrap(); + pub static ref AVAIL_RPC_URL: Url = Url::parse("wss://turing-rpc.avail.so/ws").unwrap(); + pub static ref AVAIL_BRIDGE_API_URL: Url = + Url::parse("https://turing-bridge-api.avail.so").unwrap(); } pub const DEFAULT_OBSERVABILITY_PORT: u16 = 3000; diff --git a/zkstack_cli/crates/zkstack/src/messages.rs b/zkstack_cli/crates/zkstack/src/messages.rs index 216c4bd64d3a..179f7100ef9e 100644 --- a/zkstack_cli/crates/zkstack/src/messages.rs +++ b/zkstack_cli/crates/zkstack/src/messages.rs @@ -72,6 +72,7 @@ pub(super) const MSG_DEPLOY_ECOSYSTEM_PROMPT: &str = "Do you want to deploy ecosystem contracts? (Not needed if you already have an existing one)"; pub(super) const MSG_L1_RPC_URL_PROMPT: &str = "What is the RPC URL of the L1 network?"; pub(super) const MSG_DEPLOY_PAYMASTER_PROMPT: &str = "Do you want to deploy Paymaster contract?"; +pub(super) const MSG_VALIDIUM_TYPE_PROMPT: &str = "Select the Validium type"; pub(super) const MSG_DEPLOY_ERC20_PROMPT: &str = "Do you want to deploy some test ERC20s?"; pub(super) const MSG_ECOSYSTEM_CONTRACTS_PATH_PROMPT: &str = "Provide the path to the ecosystem contracts or keep it empty and you will use ZKsync ecosystem config. \ For using this config, you need to have governance wallet"; @@ -97,6 +98,7 @@ pub(super) const MSG_DEPLOYING_ECOSYSTEM_CONTRACTS_SPINNER: &str = "Deploying ecosystem contracts..."; pub(super) const MSG_REGISTERING_CHAIN_SPINNER: &str = "Registering chain..."; pub(super) const MSG_ACCEPTING_ADMIN_SPINNER: &str = "Accepting admin..."; +pub(super) const MSG_DA_PAIR_REGISTRATION_SPINNER: &str = "Registering DA pair..."; pub(super) const MSG_UPDATING_TOKEN_MULTIPLIER_SETTER_SPINNER: &str = "Updating token multiplier setter..."; pub(super) const MSG_TOKEN_MULTIPLIER_SETTER_UPDATED_TO: &str = @@ -592,3 +594,16 @@ pub(super) fn msg_wait_consensus_registry_started_polling(addr: Address, url: &U pub(super) fn msg_consensus_registry_wait_success(addr: Address, code_len: usize) -> String { format!("Consensus registry is deployed at {addr:?}: {code_len} bytes") } + +/// DA clients related messages +pub(super) const MSG_AVAIL_CLIENT_TYPE_PROMPT: &str = "Avail client type"; +pub(super) const MSG_AVAIL_API_TIMEOUT_MS: &str = "Avail API timeout in milliseconds"; +pub(super) const MSG_AVAIL_API_NODE_URL_PROMPT: &str = "Avail API node URL"; +pub(super) const MSG_AVAIL_APP_ID_PROMPT: &str = "Avail app id"; +pub(super) const MSG_AVAIL_FINALITY_STATE_PROMPT: &str = "Avail finality state"; +pub(super) const MSG_AVAIL_GAS_RELAY_API_URL_PROMPT: &str = "Gas relay API URL"; +pub(super) const MSG_AVAIL_GAS_RELAY_MAX_RETRIES_PROMPT: &str = "Gas relay max retries"; +pub(super) const MSG_AVAIL_BRIDGE_API_URL_PROMPT: &str = "Attestation bridge API URL"; +pub(super) const MSG_AVAIL_SEED_PHRASE_PROMPT: &str = "Seed phrase"; +pub(super) const MSG_AVAIL_GAS_RELAY_API_KEY_PROMPT: &str = "Gas relay API key"; +pub(super) const MSG_INVALID_URL_ERR: &str = "Invalid URL format"; diff --git a/zkstack_cli/zkstackup/zkstackup b/zkstack_cli/zkstackup/zkstackup index e91bbc17905c..2c928d8b1194 100755 --- a/zkstack_cli/zkstackup/zkstackup +++ b/zkstack_cli/zkstackup/zkstackup @@ -86,6 +86,10 @@ parse_args() { shift ZKSTACKUP_VERSION=$1 ;; + --cargo-features) + shift + ZKSTACKUP_FEATURES=$1 + ;; -h | --help) usage exit 0 @@ -114,10 +118,12 @@ Options: -b, --branch Git branch to use when installing from a repository. Ignored if --commit or --version is provided. -c, --commit Git commit hash to use when installing from a repository. Ignored if --branch or --version is provided. -v, --version Git tag to use when installing from a repository. Ignored if --branch or --commit is provided. + --cargo-features One or more features passed to cargo install (e.g., "gateway"). -h, --help Show this help message and exit. Examples: $(basename "$0") --repo matter-labs/zksync-era --version 0.1.1 + $(basename "$0") --local --cargo-features "gateway" EOF } @@ -143,7 +149,10 @@ install_local() { for bin in "${BINS[@]}"; do say "Installing $bin" - ensure cargo install --root $LOCAL_DIR --path ./crates/$bin --force + ensure cargo install --root "$LOCAL_DIR" \ + --path "./crates/$bin" \ + --force \ + ${ZKSTACKUP_FEATURES:+--features "$ZKSTACKUP_FEATURES"} chmod +x "$BIN_DIR/$bin" done } @@ -161,19 +170,42 @@ install_from_repo() { if [ -n "$ZKSTACKUP_COMMIT" ] || [ -n "$ZKSTACKUP_BRANCH" ]; then warn "Ignoring --commit and --branch arguments when installing by version" fi - ensure cargo install --root $LOCAL_DIR --git "https://github.com/$ZKSTACKUP_REPO" --tag "zkstack_cli-v$ZKSTACKUP_VERSION" --locked "${BINS[@]}" --force + ensure cargo install --root "$LOCAL_DIR" \ + --git "https://github.com/$ZKSTACKUP_REPO" \ + --tag "zkstack_cli-v$ZKSTACKUP_VERSION" \ + --locked "${BINS[@]}" \ + --force \ + ${ZKSTACKUP_FEATURES:+--features "$ZKSTACKUP_FEATURES"} + elif [ -n "$ZKSTACKUP_COMMIT" ]; then if [ -n "$ZKSTACKUP_BRANCH" ]; then warn "Ignoring --branch argument when installing by commit" fi - ensure cargo install --root $LOCAL_DIR --git "https://github.com/$ZKSTACKUP_REPO" --rev "$ZKSTACKUP_COMMIT" --locked "${BINS[@]}" --force + ensure cargo install --root "$LOCAL_DIR" \ + --git "https://github.com/$ZKSTACKUP_REPO" \ + --rev "$ZKSTACKUP_COMMIT" \ + --locked "${BINS[@]}" \ + --force \ + ${ZKSTACKUP_FEATURES:+--features "$ZKSTACKUP_FEATURES"} + elif [ -n "$ZKSTACKUP_BRANCH" ]; then - ensure cargo install --root $LOCAL_DIR --git "https://github.com/$ZKSTACKUP_REPO" --branch "$ZKSTACKUP_BRANCH" --locked "${BINS[@]}" --force + ensure cargo install --root "$LOCAL_DIR" \ + --git "https://github.com/$ZKSTACKUP_REPO" \ + --branch "$ZKSTACKUP_BRANCH" \ + --locked "${BINS[@]}" \ + --force \ + ${ZKSTACKUP_FEATURES:+--features "$ZKSTACKUP_FEATURES"} + else - ensure cargo install --root $LOCAL_DIR --git "https://github.com/$ZKSTACKUP_REPO" --locked "${BINS[@]}" --force + ensure cargo install --root "$LOCAL_DIR" \ + --git "https://github.com/$ZKSTACKUP_REPO" \ + --locked "${BINS[@]}" \ + --force \ + ${ZKSTACKUP_FEATURES:+--features "$ZKSTACKUP_FEATURES"} fi } + add_bin_folder_to_path() { if [[ ":$PATH:" == *":${BIN_DIR}:"* ]]; then echo "found ${BIN_DIR} in PATH" From 293617ac8b952f2a479a4306e15a8f828757eaad Mon Sep 17 00:00:00 2001 From: Anton Baliasnikov Date: Thu, 16 Jan 2025 11:39:42 +0000 Subject: [PATCH 28/97] refactor: move Cargo.toml from root to core workspace (#3456) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Move `Cargo.toml` from repository root to `core` workspace. ## Why ❔ It is mandatory for proper separation of workspaces and setting up a `release-please` CI that requires all files of a component to be inside the component directory. ## CI failures Currently, the following tests are failing: * `protobuf_compatibility` - the workflow is using base version and will be fixed with the next PRs after merge to `main`. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- .dockerignore | 4 +- .githooks/pre-commit | 8 +- .github/workflows/cargo-license.yaml | 1 + .github/workflows/ci-core-reusable.yml | 2 +- .github/workflows/ci.yml | 4 +- .github/workflows/protobuf.yaml | 20 +- .github/workflows/vm-perf-comparison.yml | 12 +- .gitignore | 6 - Cargo.toml | 326 ------------------ Cargo.lock => core/Cargo.lock | 0 core/Cargo.toml | 326 ++++++++++++++++++ .../system-constants-generator/src/main.rs | 2 +- deny.toml => core/deny.toml | 0 .../lib/contract_verifier/src/resolver/env.rs | 2 +- core/lib/contracts/src/lib.rs | 2 +- core/lib/utils/src/env.rs | 34 +- core/tests/loadnext/src/fs_utils.rs | 2 +- .../tests/revert-and-restart-en.test.ts | 13 +- core/tests/revert-test/tests/utils.ts | 2 +- docker/contract-verifier/Dockerfile | 4 +- docker/external-node/Dockerfile | 6 +- docker/server-v2/Dockerfile | 8 +- docker/snapshots-creator/Dockerfile | 4 +- docker/verified-sources-fetcher/Dockerfile | 4 +- flake.nix | 12 +- infrastructure/zk/src/server.ts | 8 +- .../crates/bin/prover_cli/src/config/mod.rs | 2 +- prover/crates/bin/prover_cli/src/helper.rs | 2 +- .../src/vk_commitment_helper.rs | 2 +- prover/crates/lib/keystore/src/keystore.rs | 2 +- .../crates/common/src/external_node.rs | 2 +- zkstack_cli/crates/common/src/server.rs | 4 +- .../src/commands/contract_verifier/build.rs | 2 +- .../src/commands/contract_verifier/run.rs | 2 +- .../zkstack/src/commands/dev/commands/fmt.rs | 2 +- .../zkstack/src/commands/dev/commands/lint.rs | 4 +- .../src/commands/dev/commands/snapshot.rs | 2 +- .../commands/dev/commands/test/loadtest.rs | 47 +-- .../src/commands/dev/commands/test/rust.rs | 2 +- .../src/commands/external_node/build.rs | 2 +- .../crates/zkstack/src/commands/server.rs | 2 +- 41 files changed, 452 insertions(+), 439 deletions(-) delete mode 100644 Cargo.toml rename Cargo.lock => core/Cargo.lock (100%) create mode 100644 core/Cargo.toml rename deny.toml => core/deny.toml (100%) diff --git a/.dockerignore b/.dockerignore index 39efdabca19a..9be9fd580858 100644 --- a/.dockerignore +++ b/.dockerignore @@ -20,8 +20,8 @@ keys/setup !prover/ !yarn.lock !package.json -!Cargo.lock -!Cargo.toml +!core/Cargo.lock +!core/Cargo.toml !contracts/ !setup_2\^26.key !setup_2\^24.key diff --git a/.githooks/pre-commit b/.githooks/pre-commit index 1f0c6b945b65..df99db16605f 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -15,8 +15,6 @@ check_fmt () { fi } -check_fmt - -cd prover/ - -check_fmt +( cd core/ && check_fmt ) +( cd prover/ && check_fmt ) +( cd zkstack_cli/ && check_fmt ) diff --git a/.github/workflows/cargo-license.yaml b/.github/workflows/cargo-license.yaml index 72eb8d0d865b..8b0c095c628c 100644 --- a/.github/workflows/cargo-license.yaml +++ b/.github/workflows/cargo-license.yaml @@ -7,5 +7,6 @@ jobs: - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - uses: EmbarkStudios/cargo-deny-action@8371184bd11e21dcf8ac82ebf8c9c9f74ebf7268 # v2.0.1 with: + manifest-path: "./core/Cargo.toml" command: check command-arguments: "--hide-inclusion-graph" diff --git a/.github/workflows/ci-core-reusable.yml b/.github/workflows/ci-core-reusable.yml index 25a0da838f42..f44b1f54dc02 100644 --- a/.github/workflows/ci-core-reusable.yml +++ b/.github/workflows/ci-core-reusable.yml @@ -79,7 +79,7 @@ jobs: ci_run zkstack dev test rust # Benchmarks are not tested by `cargo nextest` unless specified explicitly, and even then `criterion` harness is incompatible # with how `cargo nextest` runs tests. Thus, we run criterion-based benchmark tests manually. - ci_run cargo test --release -p vm-benchmark --bench oneshot --bench batch + ci_run cargo test --manifest-path ./core/Cargo.toml --release -p vm-benchmark --bench oneshot --bench batch loadtest: runs-on: [ matterlabs-ci-runner-high-performance ] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 899eaea4b445..a0b7ee1bc40e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,8 +57,8 @@ jobs: - '.github/workflows/build-contract-verifier-template.yml' - '.github/workflows/ci-core-reusable.yml' - '.github/workflows/ci-core-lint-reusable.yml' - - 'Cargo.toml' - - 'Cargo.lock' + - './core/Cargo.toml' + - './core/Cargo.lock' - 'zkstack_cli/**' - '!**/*.md' - '!**/*.MD' diff --git a/.github/workflows/protobuf.yaml b/.github/workflows/protobuf.yaml index f0565919ded1..0d7f43f1c2a2 100644 --- a/.github/workflows/protobuf.yaml +++ b/.github/workflows/protobuf.yaml @@ -47,13 +47,13 @@ jobs: git checkout $(git merge-base $BASE $HEAD) --recurse-submodules working-directory: ./before - name: compile before - run: cargo check --all-targets - working-directory: ./before/ + run: cargo check --manifest-path ./core/Cargo.toml --all-targets + working-directory: ./before - name: build before.binpb run: > perl -ne 'print "$1\n" if /PROTOBUF_DESCRIPTOR="(.*)"/' - `find ./before/target/debug/build/*/output` - | xargs cat > ./before.binpb + `find ./before/core/target/debug/build/*/output` + | xargs cat > ./before/.binpb # after - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4 @@ -62,12 +62,12 @@ jobs: path: after submodules: recursive - name: compile after - run: cargo check --all-targets + run: cargo check --manifest-path ./core/Cargo.toml --all-targets working-directory: ./after - name: build after.binpb run: > perl -ne 'print "$1\n" if /PROTOBUF_DESCRIPTOR="(.*)"/' - `find ./after/target/debug/build/*/output` + `find ./after/core/target/debug/build/*/output` | xargs cat > ./after.binpb # compare @@ -75,6 +75,8 @@ jobs: with: github_token: ${{ github.token }} - name: buf breaking - run: > - buf breaking './after.binpb' --against './before.binpb' --exclude-path 'zksync/config/experimental.proto' - --config '{"version":"v1","breaking":{"use":["WIRE_JSON","WIRE"]}}' --error-format 'github-actions' + run: | + pwd + ls -la + buf breaking './after.binpb' --against './before.binpb' --exclude-path 'zksync/config/experimental.proto' \ + --config '{"version":"v1","breaking":{"use":["WIRE_JSON","WIRE"]}}' --error-format 'github-actions' diff --git a/.github/workflows/vm-perf-comparison.yml b/.github/workflows/vm-perf-comparison.yml index ac83485a2c12..59e44023243c 100644 --- a/.github/workflows/vm-perf-comparison.yml +++ b/.github/workflows/vm-perf-comparison.yml @@ -53,8 +53,10 @@ jobs: run: | ci_run zkstackup -g --local ci_run zkstack dev contracts - ci_run cargo bench --package vm-benchmark --bench instructions -- --verbose || echo "Instructions benchmark is missing" - ci_run cargo run --package vm-benchmark --release --bin instruction_counts | tee base-opcodes + ci_run cargo bench --manifest-path ./core/Cargo.toml \ + --package vm-benchmark --bench instructions -- --verbose || echo "Instructions benchmark is missing" + ci_run cargo run --manifest-path ./core/Cargo.toml \ + --package vm-benchmark --release --bin instruction_counts | tee base-opcodes - name: checkout PR run: | @@ -69,7 +71,8 @@ jobs: ci_run zkstack dev contracts ci_run cargo bench --package vm-benchmark --bench instructions -- --verbose - ci_run cargo bench --package vm-benchmark --bench instructions -- --print > instructions.log 2>/dev/null + ci_run cargo bench --manifest-path ./core/Cargo.toml \ + --package vm-benchmark --bench instructions -- --print > instructions.log 2>/dev/null # Output all lines from the benchmark result starting from the "## ..." comparison header. # Since the output spans multiple lines, we use a heredoc declaration. EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) @@ -77,7 +80,8 @@ jobs: sed -n '/^## /,$p' instructions.log >> $GITHUB_OUTPUT echo "$EOF" >> $GITHUB_OUTPUT - ci_run cargo run --package vm-benchmark --release --bin instruction_counts -- --diff base-opcodes > opcodes.log + ci_run cargo run --manifest-path ./core/Cargo.toml \ + --package vm-benchmark --release --bin instruction_counts -- --diff base-opcodes > opcodes.log echo "opcodes<<$EOF" >> $GITHUB_OUTPUT sed -n '/^## /,$p' opcodes.log >> $GITHUB_OUTPUT echo "$EOF" >> $GITHUB_OUTPUT diff --git a/.gitignore b/.gitignore index 471a601cc34b..d92880f5ae48 100644 --- a/.gitignore +++ b/.gitignore @@ -26,12 +26,6 @@ zksync_pk.key dist todo -Cargo.lock -!/Cargo.lock -!/infrastructure/zksync-crypto/Cargo.lock -!/prover/Cargo.lock -!/zkstack_cli/Cargo.lock - /etc/env/target/* /etc/env/.current /etc/env/configs/* diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index 21a1a342f1ab..000000000000 --- a/Cargo.toml +++ /dev/null @@ -1,326 +0,0 @@ -[workspace] -members = [ - # Binaries - "core/bin/block_reverter", - "core/bin/contract-verifier", - "core/bin/custom_genesis_export", - "core/bin/external_node", - "core/bin/merkle_tree_consistency_checker", - "core/bin/snapshots_creator", - "core/bin/selector_generator", - "core/bin/system-constants-generator", - "core/bin/verified_sources_fetcher", - "core/bin/zksync_server", - "core/bin/genesis_generator", - "core/bin/zksync_tee_prover", - # Node services - "core/node/node_framework", - "core/node/proof_data_handler", - "core/node/block_reverter", - "core/node/commitment_generator", - "core/node/house_keeper", - "core/node/genesis", - "core/node/shared_metrics", - "core/node/db_pruner", - "core/node/fee_model", - "core/node/da_dispatcher", - "core/node/eth_sender", - "core/node/vm_runner", - "core/node/test_utils", - "core/node/state_keeper", - "core/node/reorg_detector", - "core/node/consistency_checker", - "core/node/metadata_calculator", - "core/node/node_sync", - "core/node/node_storage_init", - "core/node/consensus", - "core/node/contract_verification_server", - "core/node/api_server", - "core/node/base_token_adjuster", - "core/node/external_proof_integration_api", - "core/node/logs_bloom_backfill", - "core/node/da_clients", - # Libraries - "core/lib/db_connection", - "core/lib/zksync_core_leftovers", - "core/lib/basic_types", - "core/lib/config", - "core/lib/constants", - "core/lib/contract_verifier", - "core/lib/contracts", - "core/lib/circuit_breaker", - "core/lib/dal", - "core/lib/env_config", - "core/lib/da_client", - "core/lib/eth_client", - "core/lib/eth_signer", - "core/lib/l1_contract_interface", - "core/lib/mempool", - "core/lib/merkle_tree", - "core/lib/mini_merkle_tree", - "core/lib/node_framework_derive", - "core/lib/object_store", - "core/lib/prover_interface", - "core/lib/queued_job_processor", - "core/lib/state", - "core/lib/storage", - "core/lib/tee_verifier", - "core/lib/types", - "core/lib/protobuf_config", - "core/lib/utils", - "core/lib/vlog", - "core/lib/multivm", - "core/lib/vm_interface", - "core/lib/vm_executor", - "core/lib/web3_decl", - "core/lib/snapshots_applier", - "core/lib/crypto_primitives", - "core/lib/external_price_api", - "core/lib/test_contracts", - # Test infrastructure - "core/tests/loadnext", - "core/tests/vm-benchmark", -] -resolver = "2" - -exclude = [] - -# for `perf` profiling -[profile.perf] -inherits = "release" -debug = true - -[workspace.package] -version = "0.1.0" -edition = "2021" -authors = ["The Matter Labs Team "] -homepage = "https://zksync.io/" -repository = "https://github.com/matter-labs/zksync-era" -license = "MIT OR Apache-2.0" -keywords = ["blockchain", "zksync"] -categories = ["cryptography"] - -[workspace.dependencies] -# "External" dependencies -anyhow = "1" -assert_matches = "1.5" -async-trait = "0.1" -async-recursion = "1" -axum = "0.7.5" -backon = "0.4.4" -bigdecimal = "0.4.5" -bincode = "1" -blake2 = "0.10" -bytes = "1" -chrono = "0.4" -clap = "4.2.2" -codegen = "0.2.0" -const-decoder = "0.4.0" -criterion = "0.4.0" -ctrlc = "3.1" -dashmap = "5.5.3" -derive_more = "1.0.0" -envy = "0.4" -ethabi = "18.0.0" -flate2 = "1.0.28" -fraction = "0.15.3" -futures = "0.3" -futures-util = "0.3" -glob = "0.3" -google-cloud-auth = "0.16.0" -google-cloud-storage = "0.20.0" -governor = "0.4.2" -hex = "0.4" -http = "1.1" -http-body-util = "0.1.2" -httpmock = "0.7.0" -hyper = "1.3" -insta = "1.29.0" -itertools = "0.10" -jsonrpsee = { version = "0.23", default-features = false } -leb128 = "0.2.5" -lru = { version = "0.12.1", default-features = false } -mini-moka = "0.10.0" -num = "0.4.0" -num_cpus = "1.13" -num_enum = "0.7.2" -octocrab = "0.41" -once_cell = "1" -opentelemetry = "0.24.0" -opentelemetry_sdk = "0.24.0" -opentelemetry-otlp = "0.17.0" -opentelemetry-semantic-conventions = "0.16.0" -opentelemetry-appender-tracing = "0.5" -pin-project-lite = "0.2.13" -pretty_assertions = "1" -prost = "0.12.6" -rand = "0.8" -rayon = "1.3.1" -regex = "1" -reqwest = "0.12" -rlp = "0.5" -rocksdb = "0.21" -rustc_version = "0.4.0" -rustls = "0.23" -secp256k1 = { version = "0.27.0", features = ["recovery", "global-context"] } -secrecy = "0.8.0" -semver = "1" -sentry = "0.31" -serde = "1" -serde_json = "1" -serde_with = "1" -serde_yaml = "0.9" -sha2 = "0.10.8" -sha3 = "0.10.8" -sqlx = "0.8.1" -static_assertions = "1.1" -structopt = "0.3.20" -strum = "0.26" -tempfile = "3.0.2" -test-casing = "0.1.2" -test-log = "0.2.15" -thiserror = "1" -thread_local = "1.1" -tikv-jemallocator = "0.5" -tiny-keccak = "2" -tokio = "1" -tower = "0.4.13" -tower-http = "0.5.2" -tracing = "0.1" -tracing-subscriber = "0.3" -tracing-opentelemetry = "0.25.0" -time = "0.3.36" # Has to be same as used by `tracing-subscriber` -url = "2" -web3 = "0.19.0" -yab = "0.1.0" - -# Proc-macro -syn = "2.0" -quote = "1.0" -proc-macro2 = "1.0" -trybuild = "1.0" - -# "Internal" dependencies -vise = "0.2.0" -vise-exporter = "0.2.0" -foundry-compilers = { version = "0.11.6", git = "https://github.com/Moonsong-Labs/compilers.git", rev = "7c69695e5c75451f158dd2456bf8c94a7492ea0b" } - -# DA clients' dependencies -# Avail -base58 = "0.2.0" -scale-encode = "0.5.0" -blake2b_simd = "1.0.2" -subxt-metadata = "0.34.0" -parity-scale-codec = { version = "3.6.9", default-features = false } -subxt-signer = { version = "0.34", default-features = false } - -# Celestia -celestia-types = "0.6.1" -bech32 = "0.11.0" -ripemd = "0.1.3" -tonic = { version = "0.11.0", default-features = false } -pbjson-types = "0.6.0" - -# Eigen -tokio-stream = "0.1.16" - -# Here and below: -# We *always* pin the latest version of protocol to disallow accidental changes in the execution logic. -# However, for the historical version of protocol crates, we have lax requirements. Otherwise, -# Bumping a crypto dependency like `boojum` would require us to republish all the historical packages. -circuit_encodings = "=0.150.19" -circuit_sequencer_api = "=0.150.19" -circuit_definitions = "=0.150.19" -crypto_codegen = { package = "zksync_solidity_vk_codegen",version = "=0.30.12" } -kzg = { package = "zksync_kzg", version = "=0.150.19" } -zk_evm = { version = "=0.133.0" } -zk_evm_1_3_1 = { package = "zk_evm", version = "0.131.0-rc.2" } -zk_evm_1_3_3 = { package = "zk_evm", version = "0.133" } -zk_evm_1_4_0 = { package = "zk_evm", version = "0.140" } -zk_evm_1_4_1 = { package = "zk_evm", version = "0.141" } -zk_evm_1_5_0 = { package = "zk_evm", version = "=0.150.19" } -fflonk = "=0.30.12" - -# New VM; pinned to a specific commit because of instability -zksync_vm2 = { git = "https://github.com/matter-labs/vm2.git", rev = "457d8a7eea9093af9440662e33e598c13ba41633" } - -# Consensus dependencies. -zksync_concurrency = "=0.7.0" -zksync_consensus_bft = "=0.7.0" -zksync_consensus_crypto = "=0.7.0" -zksync_consensus_executor = "=0.7.0" -zksync_consensus_network = "=0.7.0" -zksync_consensus_roles = "=0.7.0" -zksync_consensus_storage = "=0.7.0" -zksync_consensus_utils = "=0.7.0" -zksync_protobuf = "=0.7.0" -zksync_protobuf_build = "=0.7.0" - -# "Local" dependencies -zksync_multivm = { version = "0.1.0", path = "core/lib/multivm" } -zksync_vlog = { version = "0.1.0", path = "core/lib/vlog" } -zksync_vm_interface = { version = "0.1.0", path = "core/lib/vm_interface" } -zksync_vm_executor = { version = "0.1.0", path = "core/lib/vm_executor" } -zksync_basic_types = { version = "0.1.0", path = "core/lib/basic_types" } -zksync_circuit_breaker = { version = "0.1.0", path = "core/lib/circuit_breaker" } -zksync_config = { version = "0.1.0", path = "core/lib/config" } -zksync_contract_verifier_lib = { version = "0.1.0", path = "core/lib/contract_verifier" } -zksync_contracts = { version = "0.1.0", path = "core/lib/contracts" } -zksync_core_leftovers = { version = "0.1.0", path = "core/lib/zksync_core_leftovers" } -zksync_dal = { version = "0.1.0", path = "core/lib/dal" } -zksync_db_connection = { version = "0.1.0", path = "core/lib/db_connection" } -zksync_env_config = { version = "0.1.0", path = "core/lib/env_config" } -zksync_eth_client = { version = "0.1.0", path = "core/lib/eth_client" } -zksync_da_client = { version = "0.1.0", path = "core/lib/da_client" } -zksync_eth_signer = { version = "0.1.0", path = "core/lib/eth_signer" } -zksync_health_check = { version = "0.1.0", path = "core/lib/health_check" } -zksync_l1_contract_interface = { version = "0.1.0", path = "core/lib/l1_contract_interface" } -zksync_mempool = { version = "0.1.0", path = "core/lib/mempool" } -zksync_merkle_tree = { version = "0.1.0", path = "core/lib/merkle_tree" } -zksync_bin_metadata = { version = "0.1.0", path = "core/lib/bin_metadata" } -zksync_mini_merkle_tree = { version = "0.1.0", path = "core/lib/mini_merkle_tree" } -zksync_object_store = { version = "0.1.0", path = "core/lib/object_store" } -zksync_protobuf_config = { version = "0.1.0", path = "core/lib/protobuf_config" } -zksync_prover_interface = { version = "0.1.0", path = "core/lib/prover_interface" } -zksync_queued_job_processor = { version = "0.1.0", path = "core/lib/queued_job_processor" } -zksync_snapshots_applier = { version = "0.1.0", path = "core/lib/snapshots_applier" } -zksync_state = { version = "0.1.0", path = "core/lib/state" } -zksync_storage = { version = "0.1.0", path = "core/lib/storage" } -zksync_system_constants = { version = "0.1.0", path = "core/lib/constants" } -zksync_tee_verifier = { version = "0.1.0", path = "core/lib/tee_verifier" } -zksync_test_contracts = { version = "0.1.0", path = "core/lib/test_contracts" } -zksync_types = { version = "0.1.0", path = "core/lib/types" } -zksync_utils = { version = "0.1.0", path = "core/lib/utils" } -zksync_web3_decl = { version = "0.1.0", path = "core/lib/web3_decl" } -zksync_crypto_primitives = { version = "0.1.0", path = "core/lib/crypto_primitives" } -zksync_external_price_api = { version = "0.1.0", path = "core/lib/external_price_api" } - -# Framework and components -zksync_node_framework = { version = "0.1.0", path = "core/node/node_framework" } -zksync_node_framework_derive = { version = "0.1.0", path = "core/lib/node_framework_derive" } -zksync_eth_watch = { version = "0.1.0", path = "core/node/eth_watch" } -zksync_shared_metrics = { version = "0.1.0", path = "core/node/shared_metrics" } -zksync_proof_data_handler = { version = "0.1.0", path = "core/node/proof_data_handler" } -zksync_block_reverter = { version = "0.1.0", path = "core/node/block_reverter" } -zksync_commitment_generator = { version = "0.1.0", path = "core/node/commitment_generator" } -zksync_house_keeper = { version = "0.1.0", path = "core/node/house_keeper" } -zksync_node_genesis = { version = "0.1.0", path = "core/node/genesis" } -zksync_da_dispatcher = { version = "0.1.0", path = "core/node/da_dispatcher" } -zksync_da_clients = { version = "0.1.0", path = "core/node/da_clients" } -zksync_eth_sender = { version = "0.1.0", path = "core/node/eth_sender" } -zksync_node_db_pruner = { version = "0.1.0", path = "core/node/db_pruner" } -zksync_node_fee_model = { version = "0.1.0", path = "core/node/fee_model" } -zksync_vm_runner = { version = "0.1.0", path = "core/node/vm_runner" } -zksync_external_proof_integration_api = { version = "0.1.0", path = "core/node/external_proof_integration_api" } -zksync_node_test_utils = { version = "0.1.0", path = "core/node/test_utils" } -zksync_state_keeper = { version = "0.1.0", path = "core/node/state_keeper" } -zksync_reorg_detector = { version = "0.1.0", path = "core/node/reorg_detector" } -zksync_consistency_checker = { version = "0.1.0", path = "core/node/consistency_checker" } -zksync_metadata_calculator = { version = "0.1.0", path = "core/node/metadata_calculator" } -zksync_node_sync = { version = "0.1.0", path = "core/node/node_sync" } -zksync_node_storage_init = { version = "0.1.0", path = "core/node/node_storage_init" } -zksync_node_consensus = { version = "0.1.0", path = "core/node/consensus" } -zksync_contract_verification_server = { version = "0.1.0", path = "core/node/contract_verification_server" } -zksync_node_api_server = { version = "0.1.0", path = "core/node/api_server" } -zksync_base_token_adjuster = { version = "0.1.0", path = "core/node/base_token_adjuster" } -zksync_logs_bloom_backfill = { version = "0.1.0", path = "core/node/logs_bloom_backfill" } diff --git a/Cargo.lock b/core/Cargo.lock similarity index 100% rename from Cargo.lock rename to core/Cargo.lock diff --git a/core/Cargo.toml b/core/Cargo.toml new file mode 100644 index 000000000000..1c071c2839fa --- /dev/null +++ b/core/Cargo.toml @@ -0,0 +1,326 @@ +[workspace] +members = [ + # Binaries + "bin/block_reverter", + "bin/contract-verifier", + "bin/custom_genesis_export", + "bin/external_node", + "bin/merkle_tree_consistency_checker", + "bin/snapshots_creator", + "bin/selector_generator", + "bin/system-constants-generator", + "bin/verified_sources_fetcher", + "bin/zksync_server", + "bin/genesis_generator", + "bin/zksync_tee_prover", + # Node services + "node/node_framework", + "node/proof_data_handler", + "node/block_reverter", + "node/commitment_generator", + "node/house_keeper", + "node/genesis", + "node/shared_metrics", + "node/db_pruner", + "node/fee_model", + "node/da_dispatcher", + "node/eth_sender", + "node/vm_runner", + "node/test_utils", + "node/state_keeper", + "node/reorg_detector", + "node/consistency_checker", + "node/metadata_calculator", + "node/node_sync", + "node/node_storage_init", + "node/consensus", + "node/contract_verification_server", + "node/api_server", + "node/base_token_adjuster", + "node/external_proof_integration_api", + "node/logs_bloom_backfill", + "node/da_clients", + # Libraries + "lib/db_connection", + "lib/zksync_core_leftovers", + "lib/basic_types", + "lib/config", + "lib/constants", + "lib/contract_verifier", + "lib/contracts", + "lib/circuit_breaker", + "lib/dal", + "lib/env_config", + "lib/da_client", + "lib/eth_client", + "lib/eth_signer", + "lib/l1_contract_interface", + "lib/mempool", + "lib/merkle_tree", + "lib/mini_merkle_tree", + "lib/node_framework_derive", + "lib/object_store", + "lib/prover_interface", + "lib/queued_job_processor", + "lib/state", + "lib/storage", + "lib/tee_verifier", + "lib/types", + "lib/protobuf_config", + "lib/utils", + "lib/vlog", + "lib/multivm", + "lib/vm_interface", + "lib/vm_executor", + "lib/web3_decl", + "lib/snapshots_applier", + "lib/crypto_primitives", + "lib/external_price_api", + "lib/test_contracts", + # Test infrastructure + "tests/loadnext", + "tests/vm-benchmark", +] +resolver = "2" + +exclude = [] + +# for `perf` profiling +[profile.perf] +inherits = "release" +debug = true + +[workspace.package] +version = "0.1.0" +edition = "2021" +authors = ["The Matter Labs Team "] +homepage = "https://zksync.io/" +repository = "https://github.com/matter-labs/zksync-era" +license = "MIT OR Apache-2.0" +keywords = ["blockchain", "zksync"] +categories = ["cryptography"] + +[workspace.dependencies] +# "External" dependencies +anyhow = "1" +assert_matches = "1.5" +async-trait = "0.1" +async-recursion = "1" +axum = "0.7.5" +backon = "0.4.4" +bigdecimal = "0.4.5" +bincode = "1" +blake2 = "0.10" +bytes = "1" +chrono = "0.4" +clap = "4.2.2" +codegen = "0.2.0" +const-decoder = "0.4.0" +criterion = "0.4.0" +ctrlc = "3.1" +dashmap = "5.5.3" +derive_more = "1.0.0" +envy = "0.4" +ethabi = "18.0.0" +flate2 = "1.0.28" +fraction = "0.15.3" +futures = "0.3" +futures-util = "0.3" +glob = "0.3" +google-cloud-auth = "0.16.0" +google-cloud-storage = "0.20.0" +governor = "0.4.2" +hex = "0.4" +http = "1.1" +http-body-util = "0.1.2" +httpmock = "0.7.0" +hyper = "1.3" +insta = "1.29.0" +itertools = "0.10" +jsonrpsee = { version = "0.23", default-features = false } +leb128 = "0.2.5" +lru = { version = "0.12.1", default-features = false } +mini-moka = "0.10.0" +num = "0.4.0" +num_cpus = "1.13" +num_enum = "0.7.2" +octocrab = "0.41" +once_cell = "1" +opentelemetry = "0.24.0" +opentelemetry_sdk = "0.24.0" +opentelemetry-otlp = "0.17.0" +opentelemetry-semantic-conventions = "0.16.0" +opentelemetry-appender-tracing = "0.5" +pin-project-lite = "0.2.13" +pretty_assertions = "1" +prost = "0.12.6" +rand = "0.8" +rayon = "1.3.1" +regex = "1" +reqwest = "0.12" +rlp = "0.5" +rocksdb = "0.21" +rustc_version = "0.4.0" +rustls = "0.23" +secp256k1 = { version = "0.27.0", features = ["recovery", "global-context"] } +secrecy = "0.8.0" +semver = "1" +sentry = "0.31" +serde = "1" +serde_json = "1" +serde_with = "1" +serde_yaml = "0.9" +sha2 = "0.10.8" +sha3 = "0.10.8" +sqlx = "0.8.1" +static_assertions = "1.1" +structopt = "0.3.20" +strum = "0.26" +tempfile = "3.0.2" +test-casing = "0.1.2" +test-log = "0.2.15" +thiserror = "1" +thread_local = "1.1" +tikv-jemallocator = "0.5" +tiny-keccak = "2" +tokio = "1" +tower = "0.4.13" +tower-http = "0.5.2" +tracing = "0.1" +tracing-subscriber = "0.3" +tracing-opentelemetry = "0.25.0" +time = "0.3.36" # Has to be same as used by `tracing-subscriber` +url = "2" +web3 = "0.19.0" +yab = "0.1.0" + +# Proc-macro +syn = "2.0" +quote = "1.0" +proc-macro2 = "1.0" +trybuild = "1.0" + +# "Internal" dependencies +vise = "0.2.0" +vise-exporter = "0.2.0" +foundry-compilers = { version = "0.11.6", git = "https://github.com/Moonsong-Labs/compilers.git", rev = "7c69695e5c75451f158dd2456bf8c94a7492ea0b" } + +# DA clients' dependencies +# Avail +base58 = "0.2.0" +scale-encode = "0.5.0" +blake2b_simd = "1.0.2" +subxt-metadata = "0.34.0" +parity-scale-codec = { version = "3.6.9", default-features = false } +subxt-signer = { version = "0.34", default-features = false } + +# Celestia +celestia-types = "0.6.1" +bech32 = "0.11.0" +ripemd = "0.1.3" +tonic = { version = "0.11.0", default-features = false } +pbjson-types = "0.6.0" + +# Eigen +tokio-stream = "0.1.16" + +# Here and below: +# We *always* pin the latest version of protocol to disallow accidental changes in the execution logic. +# However, for the historical version of protocol crates, we have lax requirements. Otherwise, +# Bumping a crypto dependency like `boojum` would require us to republish all the historical packages. +circuit_encodings = "=0.150.19" +circuit_sequencer_api = "=0.150.19" +circuit_definitions = "=0.150.19" +crypto_codegen = { package = "zksync_solidity_vk_codegen",version = "=0.30.12" } +kzg = { package = "zksync_kzg", version = "=0.150.19" } +zk_evm = { version = "=0.133.0" } +zk_evm_1_3_1 = { package = "zk_evm", version = "0.131.0-rc.2" } +zk_evm_1_3_3 = { package = "zk_evm", version = "0.133" } +zk_evm_1_4_0 = { package = "zk_evm", version = "0.140" } +zk_evm_1_4_1 = { package = "zk_evm", version = "0.141" } +zk_evm_1_5_0 = { package = "zk_evm", version = "=0.150.19" } +fflonk = "=0.30.12" + +# New VM; pinned to a specific commit because of instability +zksync_vm2 = { git = "https://github.com/matter-labs/vm2.git", rev = "457d8a7eea9093af9440662e33e598c13ba41633" } + +# Consensus dependencies. +zksync_concurrency = "=0.7.0" +zksync_consensus_bft = "=0.7.0" +zksync_consensus_crypto = "=0.7.0" +zksync_consensus_executor = "=0.7.0" +zksync_consensus_network = "=0.7.0" +zksync_consensus_roles = "=0.7.0" +zksync_consensus_storage = "=0.7.0" +zksync_consensus_utils = "=0.7.0" +zksync_protobuf = "=0.7.0" +zksync_protobuf_build = "=0.7.0" + +# "Local" dependencies +zksync_multivm = { version = "0.1.0", path = "lib/multivm" } +zksync_vlog = { version = "0.1.0", path = "lib/vlog" } +zksync_vm_interface = { version = "0.1.0", path = "lib/vm_interface" } +zksync_vm_executor = { version = "0.1.0", path = "lib/vm_executor" } +zksync_basic_types = { version = "0.1.0", path = "lib/basic_types" } +zksync_circuit_breaker = { version = "0.1.0", path = "lib/circuit_breaker" } +zksync_config = { version = "0.1.0", path = "lib/config" } +zksync_contract_verifier_lib = { version = "0.1.0", path = "lib/contract_verifier" } +zksync_contracts = { version = "0.1.0", path = "lib/contracts" } +zksync_core_leftovers = { version = "0.1.0", path = "lib/zksync_core_leftovers" } +zksync_dal = { version = "0.1.0", path = "lib/dal" } +zksync_db_connection = { version = "0.1.0", path = "lib/db_connection" } +zksync_env_config = { version = "0.1.0", path = "lib/env_config" } +zksync_eth_client = { version = "0.1.0", path = "lib/eth_client" } +zksync_da_client = { version = "0.1.0", path = "lib/da_client" } +zksync_eth_signer = { version = "0.1.0", path = "lib/eth_signer" } +zksync_health_check = { version = "0.1.0", path = "lib/health_check" } +zksync_l1_contract_interface = { version = "0.1.0", path = "lib/l1_contract_interface" } +zksync_mempool = { version = "0.1.0", path = "lib/mempool" } +zksync_merkle_tree = { version = "0.1.0", path = "lib/merkle_tree" } +zksync_bin_metadata = { version = "0.1.0", path = "lib/bin_metadata" } +zksync_mini_merkle_tree = { version = "0.1.0", path = "lib/mini_merkle_tree" } +zksync_object_store = { version = "0.1.0", path = "lib/object_store" } +zksync_protobuf_config = { version = "0.1.0", path = "lib/protobuf_config" } +zksync_prover_interface = { version = "0.1.0", path = "lib/prover_interface" } +zksync_queued_job_processor = { version = "0.1.0", path = "lib/queued_job_processor" } +zksync_snapshots_applier = { version = "0.1.0", path = "lib/snapshots_applier" } +zksync_state = { version = "0.1.0", path = "lib/state" } +zksync_storage = { version = "0.1.0", path = "lib/storage" } +zksync_system_constants = { version = "0.1.0", path = "lib/constants" } +zksync_tee_verifier = { version = "0.1.0", path = "lib/tee_verifier" } +zksync_test_contracts = { version = "0.1.0", path = "lib/test_contracts" } +zksync_types = { version = "0.1.0", path = "lib/types" } +zksync_utils = { version = "0.1.0", path = "lib/utils" } +zksync_web3_decl = { version = "0.1.0", path = "lib/web3_decl" } +zksync_crypto_primitives = { version = "0.1.0", path = "lib/crypto_primitives" } +zksync_external_price_api = { version = "0.1.0", path = "lib/external_price_api" } + +# Framework and components +zksync_node_framework = { version = "0.1.0", path = "node/node_framework" } +zksync_node_framework_derive = { version = "0.1.0", path = "lib/node_framework_derive" } +zksync_eth_watch = { version = "0.1.0", path = "node/eth_watch" } +zksync_shared_metrics = { version = "0.1.0", path = "node/shared_metrics" } +zksync_proof_data_handler = { version = "0.1.0", path = "node/proof_data_handler" } +zksync_block_reverter = { version = "0.1.0", path = "node/block_reverter" } +zksync_commitment_generator = { version = "0.1.0", path = "node/commitment_generator" } +zksync_house_keeper = { version = "0.1.0", path = "node/house_keeper" } +zksync_node_genesis = { version = "0.1.0", path = "node/genesis" } +zksync_da_dispatcher = { version = "0.1.0", path = "node/da_dispatcher" } +zksync_da_clients = { version = "0.1.0", path = "node/da_clients" } +zksync_eth_sender = { version = "0.1.0", path = "node/eth_sender" } +zksync_node_db_pruner = { version = "0.1.0", path = "node/db_pruner" } +zksync_node_fee_model = { version = "0.1.0", path = "node/fee_model" } +zksync_vm_runner = { version = "0.1.0", path = "node/vm_runner" } +zksync_external_proof_integration_api = { version = "0.1.0", path = "node/external_proof_integration_api" } +zksync_node_test_utils = { version = "0.1.0", path = "node/test_utils" } +zksync_state_keeper = { version = "0.1.0", path = "node/state_keeper" } +zksync_reorg_detector = { version = "0.1.0", path = "node/reorg_detector" } +zksync_consistency_checker = { version = "0.1.0", path = "node/consistency_checker" } +zksync_metadata_calculator = { version = "0.1.0", path = "node/metadata_calculator" } +zksync_node_sync = { version = "0.1.0", path = "node/node_sync" } +zksync_node_storage_init = { version = "0.1.0", path = "node/node_storage_init" } +zksync_node_consensus = { version = "0.1.0", path = "node/consensus" } +zksync_contract_verification_server = { version = "0.1.0", path = "node/contract_verification_server" } +zksync_node_api_server = { version = "0.1.0", path = "node/api_server" } +zksync_base_token_adjuster = { version = "0.1.0", path = "node/base_token_adjuster" } +zksync_logs_bloom_backfill = { version = "0.1.0", path = "node/logs_bloom_backfill" } diff --git a/core/bin/system-constants-generator/src/main.rs b/core/bin/system-constants-generator/src/main.rs index cd795f9f5326..69152545bdfe 100644 --- a/core/bin/system-constants-generator/src/main.rs +++ b/core/bin/system-constants-generator/src/main.rs @@ -212,7 +212,7 @@ fn generate_rust_fee_constants(intrinsic_gas_constants: &IntrinsicSystemGasConst } fn save_file(path_in_repo: &str, content: String) { - let zksync_home = Workspace::locate().core(); + let zksync_home = Workspace::locate().root(); let fee_constants_path = zksync_home.join(path_in_repo); fs::write(fee_constants_path, content) diff --git a/deny.toml b/core/deny.toml similarity index 100% rename from deny.toml rename to core/deny.toml diff --git a/core/lib/contract_verifier/src/resolver/env.rs b/core/lib/contract_verifier/src/resolver/env.rs index 798efde64348..75fdf4e7f472 100644 --- a/core/lib/contract_verifier/src/resolver/env.rs +++ b/core/lib/contract_verifier/src/resolver/env.rs @@ -23,7 +23,7 @@ pub(crate) struct EnvCompilerResolver { impl Default for EnvCompilerResolver { fn default() -> Self { Self { - home_dir: Workspace::locate().core(), + home_dir: Workspace::locate().root(), } } } diff --git a/core/lib/contracts/src/lib.rs b/core/lib/contracts/src/lib.rs index 87f102be39d6..e7cf1508f733 100644 --- a/core/lib/contracts/src/lib.rs +++ b/core/lib/contracts/src/lib.rs @@ -82,7 +82,7 @@ const _FAIL_ON_RECEIVE_CONTRACT_FILE: &str = "contracts/l1-contracts/artifacts/contracts/zksync/dev-contracts/FailOnReceive.sol/FailOnReceive.json"; fn home_path() -> PathBuf { - Workspace::locate().core() + Workspace::locate().root() } fn read_file_to_json_value(path: impl AsRef + std::fmt::Debug) -> Option { diff --git a/core/lib/utils/src/env.rs b/core/lib/utils/src/env.rs index 8f4aa1da9400..0da0ddbc9f70 100644 --- a/core/lib/utils/src/env.rs +++ b/core/lib/utils/src/env.rs @@ -11,11 +11,9 @@ static WORKSPACE: OnceCell> = OnceCell::new(); /// Represents Cargo workspaces available in the repository. #[derive(Debug, Clone, Copy)] pub enum Workspace<'a> { - /// Workspace was not found. - /// Assumes that the code is running in a binary. - /// Will use the current directory as a fallback. - None, /// Root folder. + Root, + /// `core` folder. Core(&'a Path), /// `prover` folder. Prover(&'a Path), @@ -42,21 +40,30 @@ impl Workspace<'static> { result.ok() }) .as_deref(); - path.map_or(Self::None, Self::from) + path.map_or(Self::Root, Self::from) } } impl<'a> Workspace<'a> { + const CORE_DIRECTORY_NAME: &'static str = "core"; const PROVER_DIRECTORY_NAME: &'static str = "prover"; const ZKSTACK_CLI_DIRECTORY_NAME: &'static str = "zkstack_cli"; - /// Returns the path of the core workspace. - /// For `Workspace::None`, considers the current directory to represent core workspace. + /// Returns the path of the repository root. + pub fn root(self) -> PathBuf { + match self { + Self::Root => PathBuf::from("."), + Self::Core(path) | Self::Prover(path) | Self::ZkStackCli(path) => { + path.parent().unwrap().into() + } + } + } + + /// Returns the path of the `core` workspace. pub fn core(self) -> PathBuf { match self { - Self::None => PathBuf::from("."), Self::Core(path) => path.into(), - Self::Prover(path) | Self::ZkStackCli(path) => path.parent().unwrap().into(), + _ => self.root().join(Self::CORE_DIRECTORY_NAME), } } @@ -64,7 +71,7 @@ impl<'a> Workspace<'a> { pub fn prover(self) -> PathBuf { match self { Self::Prover(path) => path.into(), - _ => self.core().join(Self::PROVER_DIRECTORY_NAME), + _ => self.root().join(Self::PROVER_DIRECTORY_NAME), } } @@ -72,7 +79,7 @@ impl<'a> Workspace<'a> { pub fn zkstack_cli(self) -> PathBuf { match self { Self::ZkStackCli(path) => path.into(), - _ => self.core().join(Self::ZKSTACK_CLI_DIRECTORY_NAME), + _ => self.root().join(Self::ZKSTACK_CLI_DIRECTORY_NAME), } } } @@ -83,8 +90,10 @@ impl<'a> From<&'a Path> for Workspace<'a> { Self::Prover(path) } else if path.ends_with(Self::ZKSTACK_CLI_DIRECTORY_NAME) { Self::ZkStackCli(path) - } else { + } else if path.ends_with(Self::CORE_DIRECTORY_NAME) { Self::Core(path) + } else { + Self::Root } } } @@ -150,7 +159,6 @@ mod tests { let _pwd_protector = PwdProtector::new(); // Core. - let workspace = Workspace::locate(); assert_matches!(workspace, Workspace::Core(_)); let core_path = workspace.core(); diff --git a/core/tests/loadnext/src/fs_utils.rs b/core/tests/loadnext/src/fs_utils.rs index 0e5107f40861..9f44b1ff4946 100644 --- a/core/tests/loadnext/src/fs_utils.rs +++ b/core/tests/loadnext/src/fs_utils.rs @@ -17,7 +17,7 @@ pub struct Token { } pub fn read_tokens(network: Network) -> anyhow::Result> { - let home = Workspace::locate().core(); + let home = Workspace::locate().root(); let path = home.join(format!("etc/tokens/{network}.json")); let file = File::open(path)?; let reader = BufReader::new(file); diff --git a/core/tests/revert-test/tests/revert-and-restart-en.test.ts b/core/tests/revert-test/tests/revert-and-restart-en.test.ts index ca7f1735b356..a27e3836461a 100644 --- a/core/tests/revert-test/tests/revert-and-restart-en.test.ts +++ b/core/tests/revert-test/tests/revert-and-restart-en.test.ts @@ -54,7 +54,18 @@ function compileBinaries() { console.log('compiling binaries'); run( 'cargo', - ['build', '--release', '--bin', 'zksync_external_node', '--bin', 'zksync_server', '--bin', 'block_reverter'], + [ + 'build', + '--manifest-path', + './core/Cargo.toml', + '--release', + '--bin', + 'zksync_external_node', + '--bin', + 'zksync_server', + '--bin', + 'block_reverter' + ], { cwd: process.env.ZKSYNC_HOME } ); } diff --git a/core/tests/revert-test/tests/utils.ts b/core/tests/revert-test/tests/utils.ts index 8290598a1feb..d2cf9df837ad 100644 --- a/core/tests/revert-test/tests/utils.ts +++ b/core/tests/revert-test/tests/utils.ts @@ -148,7 +148,7 @@ async function runBlockReverter( `; } - const cmd = `cd ${pathToHome} && RUST_LOG=off cargo run --bin block_reverter --release -- ${args.join( + const cmd = `cd ${pathToHome} && RUST_LOG=off cargo run --manifest-path ./core/Cargo.toml --bin block_reverter --release -- ${args.join( ' ' )} ${fileConfigFlags}`; diff --git a/docker/contract-verifier/Dockerfile b/docker/contract-verifier/Dockerfile index d5f3c53db99f..14cae24c0642 100644 --- a/docker/contract-verifier/Dockerfile +++ b/docker/contract-verifier/Dockerfile @@ -17,7 +17,7 @@ ENV RUSTC_WRAPPER=${RUSTC_WRAPPER} WORKDIR /usr/src/zksync COPY . . -RUN cargo build --release --bin zksync_contract_verifier +RUN cargo build --manifest-path ./core/Cargo.toml --release --bin zksync_contract_verifier FROM ghcr.io/matter-labs/zksync-runtime-base:latest @@ -96,7 +96,7 @@ RUN mkdir -p /etc/vyper-bin/0.4.0 \ && mv vyper0.4.0 /etc/vyper-bin/0.4.0/vyper \ && chmod +x /etc/vyper-bin/0.4.0/vyper -COPY --from=builder /usr/src/zksync/target/release/zksync_contract_verifier /usr/bin/ +COPY --from=builder /usr/src/zksync/core/target/release/zksync_contract_verifier /usr/bin/ COPY contracts/system-contracts/zkout/ /contracts/system-contracts/zkout/ # CMD tail -f /dev/null diff --git a/docker/external-node/Dockerfile b/docker/external-node/Dockerfile index 2effe1051b4a..3c968b896e43 100644 --- a/docker/external-node/Dockerfile +++ b/docker/external-node/Dockerfile @@ -15,12 +15,12 @@ ENV RUSTC_WRAPPER=${RUSTC_WRAPPER} WORKDIR /usr/src/zksync COPY . . -RUN cargo build --release --bin zksync_external_node --bin block_reverter +RUN cargo build --manifest-path ./core/Cargo.toml --release --bin zksync_external_node --bin block_reverter FROM ghcr.io/matter-labs/zksync-runtime-base:latest -COPY --from=builder /usr/src/zksync/target/release/zksync_external_node /usr/bin -COPY --from=builder /usr/src/zksync/target/release/block_reverter /usr/bin +COPY --from=builder /usr/src/zksync/core/target/release/zksync_external_node /usr/bin +COPY --from=builder /usr/src/zksync/core/target/release/block_reverter /usr/bin COPY --from=builder /usr/local/cargo/bin/sqlx /usr/bin COPY --from=builder /usr/src/zksync/docker/external-node/entrypoint.sh /usr/bin COPY contracts/system-contracts/zkout/ /contracts/system-contracts/zkout/ diff --git a/docker/server-v2/Dockerfile b/docker/server-v2/Dockerfile index 9557156fa7c4..5855bab8d7ab 100644 --- a/docker/server-v2/Dockerfile +++ b/docker/server-v2/Dockerfile @@ -17,7 +17,7 @@ WORKDIR /usr/src/zksync COPY . . -RUN cargo build --release --features=rocksdb/io-uring --bin zksync_server --bin block_reverter --bin merkle_tree_consistency_checker +RUN cargo build --manifest-path ./core/Cargo.toml --release --features=rocksdb/io-uring --bin zksync_server --bin block_reverter --bin merkle_tree_consistency_checker FROM ghcr.io/matter-labs/zksync-runtime-base:latest @@ -28,9 +28,9 @@ EXPOSE 3000 EXPOSE 3031 EXPOSE 3030 -COPY --from=builder /usr/src/zksync/target/release/zksync_server /usr/bin -COPY --from=builder /usr/src/zksync/target/release/block_reverter /usr/bin -COPY --from=builder /usr/src/zksync/target/release/merkle_tree_consistency_checker /usr/bin +COPY --from=builder /usr/src/zksync/core/target/release/zksync_server /usr/bin +COPY --from=builder /usr/src/zksync/core/target/release/block_reverter /usr/bin +COPY --from=builder /usr/src/zksync/core/target/release/merkle_tree_consistency_checker /usr/bin COPY contracts/system-contracts/zkout/ /contracts/system-contracts/zkout/ COPY contracts/l1-contracts/out/ /contracts/l1-contracts/out/ COPY contracts/l2-contracts/zkout/ /contracts/l2-contracts/zkout/ diff --git a/docker/snapshots-creator/Dockerfile b/docker/snapshots-creator/Dockerfile index 2d3c83064981..753020094df7 100644 --- a/docker/snapshots-creator/Dockerfile +++ b/docker/snapshots-creator/Dockerfile @@ -15,13 +15,13 @@ ENV RUSTC_WRAPPER=${RUSTC_WRAPPER} WORKDIR /usr/src/zksync COPY . . -RUN cargo build --release --bin snapshots_creator +RUN cargo build --manifest-path ./core/Cargo.toml --release --bin snapshots_creator FROM ghcr.io/matter-labs/zksync-runtime-base:latest RUN apt-get update && apt-get install -y liburing-dev && \ rm -rf /var/lib/apt/lists/* -COPY --from=builder /usr/src/zksync/target/release/snapshots_creator /usr/bin +COPY --from=builder /usr/src/zksync/core/target/release/snapshots_creator /usr/bin ENTRYPOINT ["snapshots_creator"] diff --git a/docker/verified-sources-fetcher/Dockerfile b/docker/verified-sources-fetcher/Dockerfile index 87475f3187f3..ebc6619582fd 100644 --- a/docker/verified-sources-fetcher/Dockerfile +++ b/docker/verified-sources-fetcher/Dockerfile @@ -16,7 +16,7 @@ ENV RUSTC_WRAPPER=${RUSTC_WRAPPER} WORKDIR /usr/src/zksync COPY . . -RUN cargo build --release --bin verified_sources_fetcher +RUN cargo build --manifest-path ./core/Cargo.toml --release --bin verified_sources_fetcher FROM ghcr.io/matter-labs/zksync-runtime-base:latest @@ -26,6 +26,6 @@ RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor - RUN echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list RUN apt-get update && apt-get install -y google-cloud-cli && rm -rf /var/lib/apt/lists/* -COPY --from=builder /usr/src/zksync/target/release/verified_sources_fetcher /usr/bin/ +COPY --from=builder /usr/src/zksync/core/target/release/verified_sources_fetcher /usr/bin/ ENTRYPOINT ["verified_sources_fetcher"] diff --git a/flake.nix b/flake.nix index 8c08e880910d..630d719aa4df 100644 --- a/flake.nix +++ b/flake.nix @@ -84,17 +84,7 @@ snappy.dev ]; - src = with pkgs.lib.fileset; toSource { - root = ./.; - fileset = unions [ - ./Cargo.lock - ./Cargo.toml - ./core - ./prover - ./zkstack_cli - ./.github/release-please/manifest.json - ]; - }; + src = ./core/.; env = { OPENSSL_NO_VENDOR = "1"; diff --git a/infrastructure/zk/src/server.ts b/infrastructure/zk/src/server.ts index 8b10559361ae..14a27f6a48be 100644 --- a/infrastructure/zk/src/server.ts +++ b/infrastructure/zk/src/server.ts @@ -17,7 +17,7 @@ export async function server(rebuildTree: boolean, uring: boolean, components?: if (components) { options += ` --components=${components}`; } - await utils.spawn(`cargo run --bin zksync_server --release ${options}`); + await utils.spawn(`cargo run --manifest-path core/Cargo.toml--bin zksync_server --release ${options}`); } export async function externalNode(reinit: boolean = false, args: string[]) { @@ -37,7 +37,9 @@ export async function externalNode(reinit: boolean = false, args: string[]) { clean(path.dirname(process.env.EN_MERKLE_TREE_PATH!)); } - await utils.spawn(`cargo run --release --bin zksync_external_node -- ${args.join(' ')}`); + await utils.spawn( + `cargo run --manifest-path core/Cargo.toml --release --bin zksync_external_node -- ${args.join(' ')}` + ); } async function create_genesis(cmd: string) { @@ -61,7 +63,7 @@ async function create_genesis(cmd: string) { export async function genesisFromSources() { // Note that that all the chains have the same chainId at genesis. It will be changed // via an upgrade transaction during the registration of the chain. - await create_genesis('cargo run --bin zksync_server --release -- --genesis'); + await create_genesis('cargo run --manifest-path core/Cargo.toml --bin zksync_server --release -- --genesis'); } export async function genesisFromBinary() { diff --git a/prover/crates/bin/prover_cli/src/config/mod.rs b/prover/crates/bin/prover_cli/src/config/mod.rs index b3df2e7d2c56..88cf3a55f908 100644 --- a/prover/crates/bin/prover_cli/src/config/mod.rs +++ b/prover/crates/bin/prover_cli/src/config/mod.rs @@ -6,7 +6,7 @@ pub fn get_envfile() -> anyhow::Result { if let Ok(envfile) = std::env::var("PLI__CONFIG") { return Ok(envfile.into()); } - Ok(Workspace::locate().core().join("etc/pliconfig")) + Ok(Workspace::locate().root().join("etc/pliconfig")) } pub fn load_envfile(path: impl AsRef) -> anyhow::Result<()> { diff --git a/prover/crates/bin/prover_cli/src/helper.rs b/prover/crates/bin/prover_cli/src/helper.rs index 7fe0c990e4e0..b793ce5f2be1 100644 --- a/prover/crates/bin/prover_cli/src/helper.rs +++ b/prover/crates/bin/prover_cli/src/helper.rs @@ -24,7 +24,7 @@ fn read_file_to_json_value(path: &PathBuf) -> serde_json::Value { } fn load_contract_if_present(path: &str) -> Contract { - let path = Workspace::locate().core().join(path); + let path = Workspace::locate().root().join(path); path.exists() .then(|| { serde_json::from_value(read_file_to_json_value(&path)["abi"].take()).unwrap_or_else( diff --git a/prover/crates/bin/vk_setup_data_generator_server_fri/src/vk_commitment_helper.rs b/prover/crates/bin/vk_setup_data_generator_server_fri/src/vk_commitment_helper.rs index 2753799dc722..2f5bbf269267 100644 --- a/prover/crates/bin/vk_setup_data_generator_server_fri/src/vk_commitment_helper.rs +++ b/prover/crates/bin/vk_setup_data_generator_server_fri/src/vk_commitment_helper.rs @@ -24,6 +24,6 @@ pub fn read_contract_toml() -> anyhow::Result { pub fn get_contract_toml_path() -> PathBuf { Workspace::locate() - .core() + .root() .join("etc/env/base/contracts.toml") } diff --git a/prover/crates/lib/keystore/src/keystore.rs b/prover/crates/lib/keystore/src/keystore.rs index fb84436916e7..c4ffb32ce73c 100644 --- a/prover/crates/lib/keystore/src/keystore.rs +++ b/prover/crates/lib/keystore/src/keystore.rs @@ -78,7 +78,7 @@ impl Keystore { // - We're running from the core workspace. // - We're running the binary from the docker. let data_dir_path = match Workspace::locate() { - Workspace::None => { + Workspace::Root => { // We're running a binary, likely in a docker. // Keys can be in one of a few paths. // We want to be very conservative here, and checking diff --git a/zkstack_cli/crates/common/src/external_node.rs b/zkstack_cli/crates/common/src/external_node.rs index 8a5cbc3cd14c..7f9031bae4ff 100644 --- a/zkstack_cli/crates/common/src/external_node.rs +++ b/zkstack_cli/crates/common/src/external_node.rs @@ -17,7 +17,7 @@ pub fn run( let cmd = Cmd::new( cmd!( shell, - "cargo run --release --bin zksync_external_node -- + "cargo run --manifest-path ./core/Cargo.toml --release --bin zksync_external_node -- --config-path {config_path} --secrets-path {secrets_path} --external-node-config-path {en_config_path} diff --git a/zkstack_cli/crates/common/src/server.rs b/zkstack_cli/crates/common/src/server.rs index 7f8c2a90e589..0dd93bcd3324 100644 --- a/zkstack_cli/crates/common/src/server.rs +++ b/zkstack_cli/crates/common/src/server.rs @@ -70,7 +70,7 @@ impl Server { let mut cmd = Cmd::new( cmd!( shell, - "cargo run --release --bin zksync_server {uring...} -- + "cargo run --manifest-path ./core/Cargo.toml --release --bin zksync_server {uring...} -- --genesis-path {genesis_path} --wallets-path {wallets_path} --config-path {general_path} @@ -96,7 +96,7 @@ impl Server { /// Builds the server. pub fn build(&self, shell: &Shell) -> anyhow::Result<()> { - let _dir_guard = shell.push_dir(&self.code_path); + let _dir_guard = shell.push_dir(self.code_path.join("core")); Cmd::new(cmd!(shell, "cargo build --release --bin zksync_server")).run()?; Ok(()) } diff --git a/zkstack_cli/crates/zkstack/src/commands/contract_verifier/build.rs b/zkstack_cli/crates/zkstack/src/commands/contract_verifier/build.rs index 384e9a389265..2e4107383d0a 100644 --- a/zkstack_cli/crates/zkstack/src/commands/contract_verifier/build.rs +++ b/zkstack_cli/crates/zkstack/src/commands/contract_verifier/build.rs @@ -13,7 +13,7 @@ pub(crate) async fn build(shell: &Shell) -> anyhow::Result<()> { let chain = ecosystem .load_current_chain() .context(MSG_CHAIN_NOT_FOUND_ERR)?; - let _dir_guard = shell.push_dir(&chain.link_to_code); + let _dir_guard = shell.push_dir(chain.link_to_code.join("core")); logger::info(MSG_BUILDING_CONTRACT_VERIFIER); diff --git a/zkstack_cli/crates/zkstack/src/commands/contract_verifier/run.rs b/zkstack_cli/crates/zkstack/src/commands/contract_verifier/run.rs index fd872aa67248..fe4221e83a46 100644 --- a/zkstack_cli/crates/zkstack/src/commands/contract_verifier/run.rs +++ b/zkstack_cli/crates/zkstack/src/commands/contract_verifier/run.rs @@ -22,7 +22,7 @@ pub(crate) async fn run(shell: &Shell) -> anyhow::Result<()> { let mut cmd = Cmd::new(cmd!( shell, - "cargo run --release --bin zksync_contract_verifier -- --config-path={config_path} --secrets-path={secrets_path}" + "cargo run --manifest-path ./core/Cargo.toml --release --bin zksync_contract_verifier -- --config-path={config_path} --secrets-path={secrets_path}" )); cmd = cmd.with_force_run(); cmd.run().context(MSG_FAILED_TO_RUN_CONTRACT_VERIFIER_ERR) diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/fmt.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/fmt.rs index af6985b006d2..82594cccf484 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/fmt.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/fmt.rs @@ -42,7 +42,7 @@ async fn prettier_contracts(shell: Shell, check: bool) -> anyhow::Result<()> { } async fn rustfmt(shell: Shell, check: bool, link_to_code: PathBuf) -> anyhow::Result<()> { - for dir in [".", "prover", "zkstack_cli"] { + for dir in ["core", "prover", "zkstack_cli"] { let spinner = Spinner::new(&msg_running_rustfmt_for_dir_spinner(dir)); let _dir = shell.push_dir(link_to_code.join(dir)); let mut cmd = cmd!(shell, "cargo fmt -- --config imports_granularity=Crate --config group_imports=StdExternalCrate"); diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint.rs index 6b919b8cdb9d..7ba6fe27481a 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/lint.rs @@ -70,12 +70,12 @@ pub fn run(shell: &Shell, args: LintArgs) -> anyhow::Result<()> { fn lint_rs(shell: &Shell, ecosystem: &EcosystemConfig, check: bool) -> anyhow::Result<()> { let spinner = Spinner::new(&msg_running_linter_for_extension_spinner(&Target::Rs)); - let link_to_code = &ecosystem.link_to_code; + let link_to_core = &ecosystem.link_to_code.join("core"); let lint_to_prover = &ecosystem.link_to_code.join("prover"); let link_to_zkstack = &ecosystem.link_to_code.join("zkstack_cli"); spinner.freeze(); - for path in [link_to_code, lint_to_prover, link_to_zkstack] { + for path in [link_to_core, lint_to_prover, link_to_zkstack] { let _dir_guard = shell.push_dir(path); let mut cmd = cmd!(shell, "cargo clippy"); let mut common_args = vec!["--locked", "--", "-D", "warnings"]; diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/snapshot.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/snapshot.rs index dcc41af228be..85aaec103c0b 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/snapshot.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/snapshot.rs @@ -32,7 +32,7 @@ async fn create(shell: &Shell) -> anyhow::Result<()> { logger::info(MSG_RUNNING_SNAPSHOT_CREATOR); - let mut cmd = Cmd::new(cmd!(shell, "cargo run --bin snapshots_creator --release -- --config-path={config_path} --secrets-path={secrets_path}")) + let mut cmd = Cmd::new(cmd!(shell, "cargo run --manifest-path ./core/Cargo.toml --bin snapshots_creator --release -- --config-path={config_path} --secrets-path={secrets_path}")) .env("RUST_LOG", "snapshots_creator=debug"); cmd = cmd.with_force_run(); diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/loadtest.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/loadtest.rs index 385335890c75..64ea474fa2fa 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/loadtest.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/loadtest.rs @@ -17,28 +17,31 @@ pub fn run(shell: &Shell) -> anyhow::Result<()> { .api_config .context("API config is not found")?; - let mut command = cmd!(shell, "cargo run --release --bin loadnext") - .env( - "L2_CHAIN_ID", - chain_config - .get_genesis_config()? - .l2_chain_id - .as_u64() - .to_string(), - ) - .env( - "MAIN_TOKEN", - format!( - "{:?}", - ecosystem_config - .get_erc20_tokens() - .first() - .context("NO Erc20 tokens were deployed")? - .address - ), - ) - .env("L2_RPC_ADDRESS", general_api.web3_json_rpc.http_url) - .env("L2_WS_RPC_ADDRESS", general_api.web3_json_rpc.ws_url); + let mut command = cmd!( + shell, + "cargo run --manifest-path ./core/Cargo.toml --release --bin loadnext" + ) + .env( + "L2_CHAIN_ID", + chain_config + .get_genesis_config()? + .l2_chain_id + .as_u64() + .to_string(), + ) + .env( + "MAIN_TOKEN", + format!( + "{:?}", + ecosystem_config + .get_erc20_tokens() + .first() + .context("NO Erc20 tokens were deployed")? + .address + ), + ) + .env("L2_RPC_ADDRESS", general_api.web3_json_rpc.http_url) + .env("L2_WS_RPC_ADDRESS", general_api.web3_json_rpc.ws_url); if global_config().verbose { command = command.env("RUST_LOG", "loadnext=info") diff --git a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/rust.rs b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/rust.rs index c415eb7407d2..dce3cd9022da 100644 --- a/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/rust.rs +++ b/zkstack_cli/crates/zkstack/src/commands/dev/commands/test/rust.rs @@ -59,7 +59,7 @@ pub async fn run(shell: &Shell, args: RustArgs) -> anyhow::Result<()> { reset_test_databases(shell, &link_to_code, dals).await?; - let _dir_guard = shell.push_dir(&link_to_code); + let _dir_guard = shell.push_dir(link_to_code.join("core")); logger::info(MSG_USING_CARGO_NEXTEST); let cmd = cmd!(shell, "cargo nextest run --release"); diff --git a/zkstack_cli/crates/zkstack/src/commands/external_node/build.rs b/zkstack_cli/crates/zkstack/src/commands/external_node/build.rs index 8a4c4befe8db..581f08565162 100644 --- a/zkstack_cli/crates/zkstack/src/commands/external_node/build.rs +++ b/zkstack_cli/crates/zkstack/src/commands/external_node/build.rs @@ -10,7 +10,7 @@ pub(crate) async fn build(shell: &Shell) -> anyhow::Result<()> { let chain = ecosystem .load_current_chain() .context(MSG_CHAIN_NOT_FOUND_ERR)?; - let _dir_guard = shell.push_dir(&chain.link_to_code); + let _dir_guard = shell.push_dir(chain.link_to_code.join("core")); logger::info(MSG_BUILDING_EN); diff --git a/zkstack_cli/crates/zkstack/src/commands/server.rs b/zkstack_cli/crates/zkstack/src/commands/server.rs index 4088a888d534..e1e4ca3ff99d 100644 --- a/zkstack_cli/crates/zkstack/src/commands/server.rs +++ b/zkstack_cli/crates/zkstack/src/commands/server.rs @@ -35,7 +35,7 @@ pub async fn run(shell: &Shell, args: ServerArgs) -> anyhow::Result<()> { } fn build_server(chain_config: &ChainConfig, shell: &Shell) -> anyhow::Result<()> { - let _dir_guard = shell.push_dir(&chain_config.link_to_code); + let _dir_guard = shell.push_dir(chain_config.link_to_code.join("core")); logger::info(MSG_BUILDING_SERVER); From 79f65780993d12b63fa2ed36a129c4f8f66624f9 Mon Sep 17 00:00:00 2001 From: Alex Ostrovski Date: Thu, 16 Jan 2025 15:54:47 +0200 Subject: [PATCH 29/97] refactor(vm): Deduplicate code shared with legacy VM (#3477) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Deduplicates helper code shared between legacy and fast VMs. ## Why ❔ Less code is easier to maintain. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- .../src/tracers/validator/vm_latest/mod.rs | 14 +- .../vm_fast/bootloader_state/l2_block.rs | 78 ---- .../versions/vm_fast/bootloader_state/mod.rs | 8 - .../vm_fast/bootloader_state/state.rs | 323 ----------------- .../versions/vm_fast/bootloader_state/tx.rs | 50 --- .../vm_fast/bootloader_state/utils.rs | 236 ------------ core/lib/multivm/src/versions/vm_fast/hook.rs | 39 -- .../vm_fast/initial_bootloader_memory.rs | 43 --- core/lib/multivm/src/versions/vm_fast/mod.rs | 5 - .../src/versions/vm_fast/transaction_data.rs | 340 ------------------ core/lib/multivm/src/versions/vm_fast/vm.rs | 59 ++- .../src/versions/vm_latest/bootloader/init.rs | 46 +++ .../l2_block.rs | 2 +- .../src/versions/vm_latest/bootloader/mod.rs | 9 + .../bootloader}/snapshot.rs | 0 .../{bootloader_state => bootloader}/state.rs | 4 +- .../{bootloader_state => bootloader}/tx.rs | 2 +- .../{bootloader_state => bootloader}/utils.rs | 2 +- .../vm_latest/bootloader_state/mod.rs | 8 - .../vm_latest/bootloader_state/snapshot.rs | 25 -- .../vm_latest/implementation/snapshots.rs | 2 +- .../versions/vm_latest/implementation/tx.rs | 2 +- .../lib/multivm/src/versions/vm_latest/mod.rs | 11 +- .../src/versions/vm_latest/old_vm/utils.rs | 2 +- .../src/versions/vm_latest/tests/mod.rs | 2 +- .../src/versions/vm_latest/tests/rollbacks.rs | 2 +- .../vm_latest/tracers/circuits_tracer.rs | 4 +- .../vm_latest/tracers/default_tracers.rs | 28 +- .../versions/vm_latest/tracers/dispatcher.rs | 3 +- .../vm_latest/tracers/evm_deploy_tracer.rs | 2 +- .../vm_latest/tracers/pubdata_tracer.rs | 10 +- .../src/versions/vm_latest/tracers/refunds.rs | 83 +---- .../vm_latest/tracers/result_tracer.rs | 9 +- .../src/versions/vm_latest/tracers/traits.rs | 4 +- .../src/versions/vm_latest/tracers/utils.rs | 69 +--- .../src/versions/vm_latest/types/hook.rs | 39 ++ .../versions/vm_latest/types/internals/mod.rs | 7 - .../src/versions/vm_latest/types/l1_batch.rs | 43 --- .../src/versions/vm_latest/types/mod.rs | 11 +- .../types/{internals => }/snapshot.rs | 2 +- .../types/{internals => }/transaction_data.rs | 0 .../types/{internals => }/vm_state.rs | 5 +- .../src/versions/vm_latest/utils/logs.rs | 2 +- .../src/versions/vm_latest/utils/mod.rs | 1 + .../{vm_fast => vm_latest/utils}/refund.rs | 0 .../vm_latest/utils/transaction_encoding.rs | 2 +- core/lib/multivm/src/versions/vm_latest/vm.rs | 4 +- 47 files changed, 222 insertions(+), 1420 deletions(-) delete mode 100644 core/lib/multivm/src/versions/vm_fast/bootloader_state/l2_block.rs delete mode 100644 core/lib/multivm/src/versions/vm_fast/bootloader_state/mod.rs delete mode 100644 core/lib/multivm/src/versions/vm_fast/bootloader_state/state.rs delete mode 100644 core/lib/multivm/src/versions/vm_fast/bootloader_state/tx.rs delete mode 100644 core/lib/multivm/src/versions/vm_fast/bootloader_state/utils.rs delete mode 100644 core/lib/multivm/src/versions/vm_fast/hook.rs delete mode 100644 core/lib/multivm/src/versions/vm_fast/initial_bootloader_memory.rs delete mode 100644 core/lib/multivm/src/versions/vm_fast/transaction_data.rs create mode 100644 core/lib/multivm/src/versions/vm_latest/bootloader/init.rs rename core/lib/multivm/src/versions/vm_latest/{bootloader_state => bootloader}/l2_block.rs (97%) create mode 100644 core/lib/multivm/src/versions/vm_latest/bootloader/mod.rs rename core/lib/multivm/src/versions/{vm_fast/bootloader_state => vm_latest/bootloader}/snapshot.rs (100%) rename core/lib/multivm/src/versions/vm_latest/{bootloader_state => bootloader}/state.rs (99%) rename core/lib/multivm/src/versions/vm_latest/{bootloader_state => bootloader}/tx.rs (98%) rename core/lib/multivm/src/versions/vm_latest/{bootloader_state => bootloader}/utils.rs (99%) delete mode 100644 core/lib/multivm/src/versions/vm_latest/bootloader_state/mod.rs delete mode 100644 core/lib/multivm/src/versions/vm_latest/bootloader_state/snapshot.rs create mode 100644 core/lib/multivm/src/versions/vm_latest/types/hook.rs delete mode 100644 core/lib/multivm/src/versions/vm_latest/types/internals/mod.rs delete mode 100644 core/lib/multivm/src/versions/vm_latest/types/l1_batch.rs rename core/lib/multivm/src/versions/vm_latest/types/{internals => }/snapshot.rs (82%) rename core/lib/multivm/src/versions/vm_latest/types/{internals => }/transaction_data.rs (100%) rename core/lib/multivm/src/versions/vm_latest/types/{internals => }/vm_state.rs (97%) rename core/lib/multivm/src/versions/{vm_fast => vm_latest/utils}/refund.rs (100%) diff --git a/core/lib/multivm/src/tracers/validator/vm_latest/mod.rs b/core/lib/multivm/src/tracers/validator/vm_latest/mod.rs index 5588dd144e95..5ad0ca025937 100644 --- a/core/lib/multivm/src/tracers/validator/vm_latest/mod.rs +++ b/core/lib/multivm/src/tracers/validator/vm_latest/mod.rs @@ -22,8 +22,8 @@ use crate::{ }, }, vm_latest::{ - tracers::utils::{computational_gas_price, get_calldata_page_via_abi, VmHook}, - BootloaderState, SimpleMemory, VmTracer, ZkSyncVmState, + tracers::utils::{computational_gas_price, get_calldata_page_via_abi}, + BootloaderState, SimpleMemory, VmHook, VmTracer, ZkSyncVmState, }, HistoryMode, }; @@ -205,25 +205,25 @@ impl DynTracer> let hook = VmHook::from_opcode_memory(&state, &data, self.vm_version.try_into().unwrap()); let current_mode = self.validation_mode; match (current_mode, hook) { - (ValidationTracerMode::NoValidation, VmHook::AccountValidationEntered) => { + (ValidationTracerMode::NoValidation, Some(VmHook::AccountValidationEntered)) => { // Account validation can be entered when there is no prior validation (i.e. "nested" validations are not allowed) self.validation_mode = ValidationTracerMode::UserTxValidation; } - (ValidationTracerMode::NoValidation, VmHook::PaymasterValidationEntered) => { + (ValidationTracerMode::NoValidation, Some(VmHook::PaymasterValidationEntered)) => { // Paymaster validation can be entered when there is no prior validation (i.e. "nested" validations are not allowed) self.validation_mode = ValidationTracerMode::PaymasterTxValidation; } - (_, VmHook::AccountValidationEntered | VmHook::PaymasterValidationEntered) => { + (_, Some(VmHook::AccountValidationEntered | VmHook::PaymasterValidationEntered)) => { panic!( "Unallowed transition inside the validation tracer. Mode: {:#?}, hook: {:#?}", self.validation_mode, hook ); } - (_, VmHook::NoValidationEntered) => { + (_, Some(VmHook::ValidationExited)) => { // Validation can be always turned off self.validation_mode = ValidationTracerMode::NoValidation; } - (_, VmHook::ValidationStepEndeded) => { + (_, Some(VmHook::ValidationStepEnded)) => { // The validation step has ended. self.should_stop_execution = true; } diff --git a/core/lib/multivm/src/versions/vm_fast/bootloader_state/l2_block.rs b/core/lib/multivm/src/versions/vm_fast/bootloader_state/l2_block.rs deleted file mode 100644 index 4f05ef30a46d..000000000000 --- a/core/lib/multivm/src/versions/vm_fast/bootloader_state/l2_block.rs +++ /dev/null @@ -1,78 +0,0 @@ -use std::cmp::Ordering; - -use zksync_types::{web3::keccak256_concat, L2BlockNumber, H256}; - -use super::{snapshot::L2BlockSnapshot, tx::BootloaderTx}; -use crate::{ - interface::{L2Block, L2BlockEnv}, - vm_latest::utils::l2_blocks::l2_block_hash, -}; - -const EMPTY_TXS_ROLLING_HASH: H256 = H256::zero(); - -#[derive(Debug)] -pub(crate) struct BootloaderL2Block { - pub(crate) number: u32, - pub(crate) timestamp: u64, - pub(crate) txs_rolling_hash: H256, // The rolling hash of all the transactions in the miniblock - pub(crate) prev_block_hash: H256, - // Number of the first L2 block tx in L1 batch - pub(crate) first_tx_index: usize, - pub(crate) max_virtual_blocks_to_create: u32, - pub(crate) txs: Vec, -} - -impl BootloaderL2Block { - pub(crate) fn new(l2_block: L2BlockEnv, first_tx_place: usize) -> Self { - Self { - number: l2_block.number, - timestamp: l2_block.timestamp, - txs_rolling_hash: EMPTY_TXS_ROLLING_HASH, - prev_block_hash: l2_block.prev_block_hash, - first_tx_index: first_tx_place, - max_virtual_blocks_to_create: l2_block.max_virtual_blocks_to_create, - txs: vec![], - } - } - - pub(super) fn push_tx(&mut self, tx: BootloaderTx) { - self.update_rolling_hash(tx.hash); - self.txs.push(tx) - } - - pub(crate) fn get_hash(&self) -> H256 { - l2_block_hash( - L2BlockNumber(self.number), - self.timestamp, - self.prev_block_hash, - self.txs_rolling_hash, - ) - } - - fn update_rolling_hash(&mut self, tx_hash: H256) { - self.txs_rolling_hash = keccak256_concat(self.txs_rolling_hash, tx_hash) - } - - pub(crate) fn make_snapshot(&self) -> L2BlockSnapshot { - L2BlockSnapshot { - txs_rolling_hash: self.txs_rolling_hash, - txs_len: self.txs.len(), - } - } - - pub(crate) fn apply_snapshot(&mut self, snapshot: L2BlockSnapshot) { - self.txs_rolling_hash = snapshot.txs_rolling_hash; - match self.txs.len().cmp(&snapshot.txs_len) { - Ordering::Greater => self.txs.truncate(snapshot.txs_len), - Ordering::Less => panic!("Applying snapshot from future is not supported"), - Ordering::Equal => {} - } - } - pub(crate) fn l2_block(&self) -> L2Block { - L2Block { - number: self.number, - timestamp: self.timestamp, - hash: self.get_hash(), - } - } -} diff --git a/core/lib/multivm/src/versions/vm_fast/bootloader_state/mod.rs b/core/lib/multivm/src/versions/vm_fast/bootloader_state/mod.rs deleted file mode 100644 index 73830de2759b..000000000000 --- a/core/lib/multivm/src/versions/vm_fast/bootloader_state/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod l2_block; -mod snapshot; -mod state; -mod tx; - -pub(crate) mod utils; -pub(crate) use snapshot::BootloaderStateSnapshot; -pub use state::BootloaderState; diff --git a/core/lib/multivm/src/versions/vm_fast/bootloader_state/state.rs b/core/lib/multivm/src/versions/vm_fast/bootloader_state/state.rs deleted file mode 100644 index be4f2881297d..000000000000 --- a/core/lib/multivm/src/versions/vm_fast/bootloader_state/state.rs +++ /dev/null @@ -1,323 +0,0 @@ -use std::cmp::Ordering; - -use once_cell::sync::OnceCell; -use zksync_types::{vm::VmVersion, L2ChainId, ProtocolVersionId, U256}; - -use super::{ - l2_block::BootloaderL2Block, - tx::BootloaderTx, - utils::{apply_l2_block, apply_pubdata_to_memory, apply_tx_to_memory}, - BootloaderStateSnapshot, -}; -use crate::{ - interface::{ - pubdata::{PubdataBuilder, PubdataInput}, - BootloaderMemory, CompressedBytecodeInfo, L2BlockEnv, TxExecutionMode, - }, - versions::vm_fast::transaction_data::TransactionData, - vm_latest::{ - constants::get_tx_description_offset, utils::l2_blocks::assert_next_block, - MultiVmSubversion, - }, -}; - -/// Intermediate bootloader-related VM state. -/// -/// Required to process transactions one by one (since we intercept the VM execution to execute -/// transactions and add new ones to the memory on the fly). -/// Keeps tracking everything related to the bootloader memory and can restore the whole memory. -/// -/// -/// Serves two purposes: -/// - Tracks where next tx should be pushed to in the bootloader memory. -/// - Tracks which transaction should be executed next. -#[derive(Debug)] -pub struct BootloaderState { - /// ID of the next transaction to be executed. - /// See the structure doc-comment for a better explanation of purpose. - tx_to_execute: usize, - /// Stored txs in bootloader memory - l2_blocks: Vec, - /// The number of 32-byte words spent on the already included compressed bytecodes. - compressed_bytecodes_encoding: usize, - /// Initial memory of bootloader - initial_memory: BootloaderMemory, - /// Mode of txs for execution, it can be changed once per vm lunch - execution_mode: TxExecutionMode, - /// Current offset of the free space in the bootloader memory. - free_tx_offset: usize, - /// Information about the pubdata that will be needed to supply to the L1Messenger - pubdata_information: OnceCell, - /// Protocol version. - protocol_version: ProtocolVersionId, - /// Protocol subversion - subversion: MultiVmSubversion, -} - -impl BootloaderState { - pub(crate) fn new( - execution_mode: TxExecutionMode, - initial_memory: BootloaderMemory, - first_l2_block: L2BlockEnv, - protocol_version: ProtocolVersionId, - ) -> Self { - let l2_block = BootloaderL2Block::new(first_l2_block, 0); - Self { - tx_to_execute: 0, - compressed_bytecodes_encoding: 0, - l2_blocks: vec![l2_block], - initial_memory, - execution_mode, - free_tx_offset: 0, - pubdata_information: Default::default(), - protocol_version, - subversion: MultiVmSubversion::try_from(VmVersion::from(protocol_version)).unwrap(), - } - } - - pub(crate) fn set_refund_for_current_tx(&mut self, refund: u64) { - let current_tx = self.current_tx(); - // We can't set the refund for the latest tx or using the latest l2_block for fining tx - // Because we can fill the whole batch first and then execute txs one by one - let tx = self.find_tx_mut(current_tx); - tx.refund = refund; - } - - pub(crate) fn set_pubdata_input(&mut self, info: PubdataInput) { - self.pubdata_information - .set(info) - .expect("Pubdata information is already set"); - } - - pub(crate) fn start_new_l2_block(&mut self, l2_block: L2BlockEnv) { - let last_block = self.last_l2_block(); - assert!( - !last_block.txs.is_empty(), - "Can not create new miniblocks on top of empty ones" - ); - assert_next_block(&last_block.l2_block(), &l2_block); - self.push_l2_block(l2_block); - } - - /// This method bypass sanity checks and should be used carefully. - pub(crate) fn push_l2_block(&mut self, l2_block: L2BlockEnv) { - self.l2_blocks - .push(BootloaderL2Block::new(l2_block, self.free_tx_index())) - } - - pub(crate) fn push_tx( - &mut self, - tx: TransactionData, - predefined_overhead: u32, - predefined_refund: u64, - compressed_bytecodes: Vec, - trusted_ergs_limit: U256, - chain_id: L2ChainId, - ) -> BootloaderMemory { - let tx_offset = self.free_tx_offset(); - let bootloader_tx = BootloaderTx::new( - tx, - predefined_refund, - predefined_overhead, - trusted_ergs_limit, - compressed_bytecodes, - tx_offset, - chain_id, - ); - - let mut memory = vec![]; - let compressed_bytecode_size = apply_tx_to_memory( - &mut memory, - &bootloader_tx, - self.last_l2_block(), - self.free_tx_index(), - self.free_tx_offset(), - self.compressed_bytecodes_encoding, - self.execution_mode, - self.last_l2_block().txs.is_empty(), - self.subversion, - ); - self.compressed_bytecodes_encoding += compressed_bytecode_size; - self.free_tx_offset = tx_offset + bootloader_tx.encoded_len(); - self.last_mut_l2_block().push_tx(bootloader_tx); - memory - } - - pub(crate) fn last_l2_block(&self) -> &BootloaderL2Block { - self.l2_blocks.last().unwrap() - } - - pub(crate) fn get_pubdata_information(&self) -> &PubdataInput { - self.pubdata_information - .get() - .expect("Pubdata information is not set") - } - - pub(crate) fn settlement_layer_pubdata(&self, pubdata_builder: &dyn PubdataBuilder) -> Vec { - let pubdata_information = self - .pubdata_information - .get() - .expect("Pubdata information is not set"); - pubdata_builder.settlement_layer_pubdata(pubdata_information, self.protocol_version) - } - - fn last_mut_l2_block(&mut self) -> &mut BootloaderL2Block { - self.l2_blocks.last_mut().unwrap() - } - - /// Apply all bootloader transaction to the initial memory - pub(crate) fn bootloader_memory( - &self, - pubdata_builder: &dyn PubdataBuilder, - ) -> BootloaderMemory { - let mut initial_memory = self.initial_memory.clone(); - let mut offset = 0; - let mut compressed_bytecodes_offset = 0; - let mut tx_index = 0; - for l2_block in &self.l2_blocks { - for (num, tx) in l2_block.txs.iter().enumerate() { - let compressed_bytecodes_size = apply_tx_to_memory( - &mut initial_memory, - tx, - l2_block, - tx_index, - offset, - compressed_bytecodes_offset, - self.execution_mode, - num == 0, - self.subversion, - ); - offset += tx.encoded_len(); - compressed_bytecodes_offset += compressed_bytecodes_size; - tx_index += 1; - } - if l2_block.txs.is_empty() { - apply_l2_block(&mut initial_memory, l2_block, tx_index, self.subversion) - } - } - - let pubdata_information = self - .pubdata_information - .get() - .expect("Empty pubdata information"); - - apply_pubdata_to_memory( - &mut initial_memory, - pubdata_builder, - pubdata_information, - self.protocol_version, - self.subversion, - ); - initial_memory - } - - fn free_tx_offset(&self) -> usize { - self.free_tx_offset - } - - pub(crate) fn free_tx_index(&self) -> usize { - let l2_block = self.last_l2_block(); - l2_block.first_tx_index + l2_block.txs.len() - } - - pub(crate) fn get_last_tx_compressed_bytecodes(&self) -> &[CompressedBytecodeInfo] { - if let Some(tx) = self.last_l2_block().txs.last() { - &tx.compressed_bytecodes - } else { - &[] - } - } - - /// Returns the id of current tx - pub(crate) fn current_tx(&self) -> usize { - self.tx_to_execute - .checked_sub(1) - .expect("There are no current tx to execute") - } - - /// Returns the ID of the next transaction to be executed and increments the local transaction counter. - pub(crate) fn move_tx_to_execute_pointer(&mut self) -> usize { - assert!( - self.tx_to_execute < self.free_tx_index(), - "Attempt to execute tx that was not pushed to memory. Tx ID: {}, txs in bootloader: {}", - self.tx_to_execute, - self.free_tx_index() - ); - - let old = self.tx_to_execute; - self.tx_to_execute += 1; - old - } - - /// Get offset of tx description - pub(crate) fn get_tx_description_offset(&self, tx_index: usize) -> usize { - get_tx_description_offset(self.subversion) + self.find_tx(tx_index).offset - } - - pub(crate) fn insert_fictive_l2_block(&mut self) -> &BootloaderL2Block { - let block = self.last_l2_block(); - if !block.txs.is_empty() { - self.start_new_l2_block(L2BlockEnv { - timestamp: block.timestamp + 1, - number: block.number + 1, - prev_block_hash: block.get_hash(), - max_virtual_blocks_to_create: 1, - }); - } - self.last_l2_block() - } - - fn find_tx(&self, tx_index: usize) -> &BootloaderTx { - for block in self.l2_blocks.iter().rev() { - if tx_index >= block.first_tx_index { - return &block.txs[tx_index - block.first_tx_index]; - } - } - panic!("The tx with index {} must exist", tx_index) - } - - fn find_tx_mut(&mut self, tx_index: usize) -> &mut BootloaderTx { - for block in self.l2_blocks.iter_mut().rev() { - if tx_index >= block.first_tx_index { - return &mut block.txs[tx_index - block.first_tx_index]; - } - } - panic!("The tx with index {} must exist", tx_index) - } - - pub(crate) fn get_snapshot(&self) -> BootloaderStateSnapshot { - BootloaderStateSnapshot { - tx_to_execute: self.tx_to_execute, - l2_blocks_len: self.l2_blocks.len(), - last_l2_block: self.last_l2_block().make_snapshot(), - compressed_bytecodes_encoding: self.compressed_bytecodes_encoding, - free_tx_offset: self.free_tx_offset, - is_pubdata_information_provided: self.pubdata_information.get().is_some(), - } - } - - pub(crate) fn apply_snapshot(&mut self, snapshot: BootloaderStateSnapshot) { - self.tx_to_execute = snapshot.tx_to_execute; - self.compressed_bytecodes_encoding = snapshot.compressed_bytecodes_encoding; - self.free_tx_offset = snapshot.free_tx_offset; - match self.l2_blocks.len().cmp(&snapshot.l2_blocks_len) { - Ordering::Greater => self.l2_blocks.truncate(snapshot.l2_blocks_len), - Ordering::Less => panic!("Applying snapshot from future is not supported"), - Ordering::Equal => {} - } - self.last_mut_l2_block() - .apply_snapshot(snapshot.last_l2_block); - - if !snapshot.is_pubdata_information_provided { - self.pubdata_information = Default::default(); - } else { - // Under the correct usage of the snapshots of the bootloader state, - // this assertion should never fail, i.e. since the pubdata information - // can be set only once. However, we have this assertion just in case. - assert!( - self.pubdata_information.get().is_some(), - "Snapshot with no pubdata can not rollback to snapshot with one" - ); - } - } -} diff --git a/core/lib/multivm/src/versions/vm_fast/bootloader_state/tx.rs b/core/lib/multivm/src/versions/vm_fast/bootloader_state/tx.rs deleted file mode 100644 index dc0706561d5e..000000000000 --- a/core/lib/multivm/src/versions/vm_fast/bootloader_state/tx.rs +++ /dev/null @@ -1,50 +0,0 @@ -use zksync_types::{L2ChainId, H256, U256}; - -use crate::{ - interface::CompressedBytecodeInfo, versions::vm_fast::transaction_data::TransactionData, -}; - -/// Information about tx necessary for execution in bootloader. -#[derive(Debug, Clone)] -pub(crate) struct BootloaderTx { - pub(crate) hash: H256, - /// Encoded transaction - pub(crate) encoded: Vec, - /// Compressed bytecodes, which has been published during this transaction - pub(crate) compressed_bytecodes: Vec, - /// Refunds for this transaction - pub(crate) refund: u64, - /// Gas overhead - pub(crate) gas_overhead: u32, - /// Gas Limit for this transaction. It can be different from the gas limit inside the transaction - pub(crate) trusted_gas_limit: U256, - /// Offset of the tx in bootloader memory - pub(crate) offset: usize, -} - -impl BootloaderTx { - pub(super) fn new( - tx: TransactionData, - predefined_refund: u64, - predefined_overhead: u32, - trusted_gas_limit: U256, - compressed_bytecodes: Vec, - offset: usize, - chain_id: L2ChainId, - ) -> Self { - let hash = tx.tx_hash(chain_id); - Self { - hash, - encoded: tx.into_tokens(), - compressed_bytecodes, - refund: predefined_refund, - gas_overhead: predefined_overhead, - trusted_gas_limit, - offset, - } - } - - pub(super) fn encoded_len(&self) -> usize { - self.encoded.len() - } -} diff --git a/core/lib/multivm/src/versions/vm_fast/bootloader_state/utils.rs b/core/lib/multivm/src/versions/vm_fast/bootloader_state/utils.rs deleted file mode 100644 index 8883fd33904b..000000000000 --- a/core/lib/multivm/src/versions/vm_fast/bootloader_state/utils.rs +++ /dev/null @@ -1,236 +0,0 @@ -use zksync_types::{ethabi, h256_to_u256, ProtocolVersionId, U256}; - -use super::{l2_block::BootloaderL2Block, tx::BootloaderTx}; -use crate::{ - interface::{ - pubdata::{PubdataBuilder, PubdataInput}, - BootloaderMemory, CompressedBytecodeInfo, TxExecutionMode, - }, - utils::bytecode, - vm_latest::{ - constants::{ - get_bootloader_tx_description_offset, get_compressed_bytecodes_offset, - get_operator_provided_l1_messenger_pubdata_offset, get_operator_refunds_offset, - get_tx_description_offset, get_tx_operator_l2_block_info_offset, - get_tx_overhead_offset, get_tx_trusted_gas_limit_offset, - BOOTLOADER_TX_DESCRIPTION_SIZE, OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS, - TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO, - }, - MultiVmSubversion, - }, -}; - -pub(super) fn get_memory_for_compressed_bytecodes( - compressed_bytecodes: &[CompressedBytecodeInfo], -) -> Vec { - let memory_addition: Vec<_> = compressed_bytecodes - .iter() - .flat_map(bytecode::encode_call) - .collect(); - bytecode::bytes_to_be_words(&memory_addition) -} - -#[allow(clippy::too_many_arguments)] -pub(super) fn apply_tx_to_memory( - memory: &mut BootloaderMemory, - bootloader_tx: &BootloaderTx, - bootloader_l2_block: &BootloaderL2Block, - tx_index: usize, - tx_offset: usize, - compressed_bytecodes_size: usize, - execution_mode: TxExecutionMode, - start_new_l2_block: bool, - subversion: MultiVmSubversion, -) -> usize { - let bootloader_description_offset = get_bootloader_tx_description_offset(subversion) - + BOOTLOADER_TX_DESCRIPTION_SIZE * tx_index; - let tx_description_offset = get_tx_description_offset(subversion) + tx_offset; - - memory.push(( - bootloader_description_offset, - assemble_tx_meta(execution_mode, true), - )); - - memory.push(( - bootloader_description_offset + 1, - U256::from_big_endian(&(32 * tx_description_offset).to_be_bytes()), - )); - - let refund_offset = get_operator_refunds_offset(subversion) + tx_index; - memory.push((refund_offset, bootloader_tx.refund.into())); - - let overhead_offset = get_tx_overhead_offset(subversion) + tx_index; - memory.push((overhead_offset, bootloader_tx.gas_overhead.into())); - - let trusted_gas_limit_offset = get_tx_trusted_gas_limit_offset(subversion) + tx_index; - memory.push((trusted_gas_limit_offset, bootloader_tx.trusted_gas_limit)); - - memory.extend( - (tx_description_offset..tx_description_offset + bootloader_tx.encoded_len()) - .zip(bootloader_tx.encoded.clone()), - ); - apply_l2_block_inner( - memory, - bootloader_l2_block, - tx_index, - start_new_l2_block, - subversion, - ); - - // Note, +1 is moving for pointer - let compressed_bytecodes_offset = - get_compressed_bytecodes_offset(subversion) + 1 + compressed_bytecodes_size; - - let encoded_compressed_bytecodes = - get_memory_for_compressed_bytecodes(&bootloader_tx.compressed_bytecodes); - let compressed_bytecodes_encoding = encoded_compressed_bytecodes.len(); - - memory.extend( - (compressed_bytecodes_offset - ..compressed_bytecodes_offset + encoded_compressed_bytecodes.len()) - .zip(encoded_compressed_bytecodes), - ); - compressed_bytecodes_encoding -} - -pub(crate) fn apply_l2_block( - memory: &mut BootloaderMemory, - bootloader_l2_block: &BootloaderL2Block, - txs_index: usize, - subversion: MultiVmSubversion, -) { - apply_l2_block_inner(memory, bootloader_l2_block, txs_index, true, subversion) -} - -fn apply_l2_block_inner( - memory: &mut BootloaderMemory, - bootloader_l2_block: &BootloaderL2Block, - txs_index: usize, - start_new_l2_block: bool, - subversion: MultiVmSubversion, -) { - // Since L2 block information start from the `TX_OPERATOR_L2_BLOCK_INFO_OFFSET` and each - // L2 block info takes `TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO` slots, the position where the L2 block info - // for this transaction needs to be written is: - - let block_position = get_tx_operator_l2_block_info_offset(subversion) - + txs_index * TX_OPERATOR_SLOTS_PER_L2_BLOCK_INFO; - - memory.extend(vec![ - (block_position, bootloader_l2_block.number.into()), - (block_position + 1, bootloader_l2_block.timestamp.into()), - ( - block_position + 2, - h256_to_u256(bootloader_l2_block.prev_block_hash), - ), - ( - block_position + 3, - if start_new_l2_block { - bootloader_l2_block.max_virtual_blocks_to_create.into() - } else { - U256::zero() - }, - ), - ]) -} - -fn bootloader_memory_input( - pubdata_builder: &dyn PubdataBuilder, - input: &PubdataInput, - protocol_version: ProtocolVersionId, -) -> Vec { - let l2_da_validator_address = pubdata_builder.l2_da_validator(); - let operator_input = pubdata_builder.l1_messenger_operator_input(input, protocol_version); - ethabi::encode(&[ - ethabi::Token::Address(l2_da_validator_address), - ethabi::Token::Bytes(operator_input), - ]) -} - -pub(crate) fn apply_pubdata_to_memory( - memory: &mut BootloaderMemory, - pubdata_builder: &dyn PubdataBuilder, - pubdata_information: &PubdataInput, - protocol_version: ProtocolVersionId, - subversion: MultiVmSubversion, -) { - let (l1_messenger_pubdata_start_slot, pubdata) = match subversion { - MultiVmSubversion::SmallBootloaderMemory | MultiVmSubversion::IncreasedBootloaderMemory => { - // Skipping two slots as they will be filled by the bootloader itself: - // - One slot is for the selector of the call to the L1Messenger. - // - The other slot is for the 0x20 offset for the calldata. - let l1_messenger_pubdata_start_slot = - get_operator_provided_l1_messenger_pubdata_offset(subversion) + 2; - - // Need to skip first word as it represents array offset - // while bootloader expects only [len || data] - let pubdata = ethabi::encode(&[ethabi::Token::Bytes( - pubdata_builder.l1_messenger_operator_input(pubdata_information, protocol_version), - )])[32..] - .to_vec(); - - assert!( - pubdata.len() / 32 <= OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS - 2, - "The encoded pubdata is too big" - ); - - (l1_messenger_pubdata_start_slot, pubdata) - } - MultiVmSubversion::Gateway => { - // Skipping the first slot as it will be filled by the bootloader itself: - // It is for the selector of the call to the L1Messenger. - let l1_messenger_pubdata_start_slot = - get_operator_provided_l1_messenger_pubdata_offset(subversion) + 1; - - let pubdata = - bootloader_memory_input(pubdata_builder, pubdata_information, protocol_version); - - assert!( - // Note that unlike the previous version, the difference is `1`, since now it also includes the offset - pubdata.len() / 32 < OPERATOR_PROVIDED_L1_MESSENGER_PUBDATA_SLOTS, - "The encoded pubdata is too big" - ); - - (l1_messenger_pubdata_start_slot, pubdata) - } - }; - - pubdata - .chunks(32) - .enumerate() - .for_each(|(slot_offset, value)| { - memory.push(( - l1_messenger_pubdata_start_slot + slot_offset, - U256::from(value), - )) - }); -} - -/// Forms a word that contains meta information for the transaction execution. -/// -/// # Current layout -/// -/// - 0 byte (MSB): server-side tx execution mode -/// In the server, we may want to execute different parts of the transaction in the different context -/// For example, when checking validity, we don't want to actually execute transaction and have side effects. -/// -/// Possible values: -/// - 0x00: validate & execute (normal mode) -/// - 0x02: execute but DO NOT validate -/// -/// - 31 byte (LSB): whether to execute transaction or not (at all). -pub(super) fn assemble_tx_meta(execution_mode: TxExecutionMode, execute_tx: bool) -> U256 { - let mut output = [0u8; 32]; - - // Set 0 byte (execution mode) - output[0] = match execution_mode { - TxExecutionMode::VerifyExecute => 0x00, - TxExecutionMode::EstimateFee => 0x00, - TxExecutionMode::EthCall => 0x02, - }; - - // Set 31 byte (marker for tx execution) - output[31] = u8::from(execute_tx); - - U256::from_big_endian(&output) -} diff --git a/core/lib/multivm/src/versions/vm_fast/hook.rs b/core/lib/multivm/src/versions/vm_fast/hook.rs deleted file mode 100644 index b138c6d496d9..000000000000 --- a/core/lib/multivm/src/versions/vm_fast/hook.rs +++ /dev/null @@ -1,39 +0,0 @@ -#[derive(Debug, Copy, Clone)] -pub(crate) enum Hook { - AccountValidationEntered, - PaymasterValidationEntered, - ValidationExited, - ValidationStepEnded, - TxHasEnded, - DebugLog, - DebugReturnData, - NearCallCatch, - AskOperatorForRefund, - NotifyAboutRefund, - PostResult, - FinalBatchInfo, - PubdataRequested, -} - -impl Hook { - /// # Panics - /// Panics if the number does not correspond to any hook. - pub fn from_u32(hook: u32) -> Self { - match hook { - 0 => Hook::AccountValidationEntered, - 1 => Hook::PaymasterValidationEntered, - 2 => Hook::ValidationExited, - 3 => Hook::ValidationStepEnded, - 4 => Hook::TxHasEnded, - 5 => Hook::DebugLog, - 6 => Hook::DebugReturnData, - 7 => Hook::NearCallCatch, - 8 => Hook::AskOperatorForRefund, - 9 => Hook::NotifyAboutRefund, - 10 => Hook::PostResult, - 11 => Hook::FinalBatchInfo, - 12 => Hook::PubdataRequested, - _ => panic!("Unknown hook {}", hook), - } - } -} diff --git a/core/lib/multivm/src/versions/vm_fast/initial_bootloader_memory.rs b/core/lib/multivm/src/versions/vm_fast/initial_bootloader_memory.rs deleted file mode 100644 index 89b22d328ac5..000000000000 --- a/core/lib/multivm/src/versions/vm_fast/initial_bootloader_memory.rs +++ /dev/null @@ -1,43 +0,0 @@ -use zksync_types::{address_to_u256, h256_to_u256, U256}; - -use crate::{interface::L1BatchEnv, vm_latest::utils::fee::get_batch_base_fee}; - -const OPERATOR_ADDRESS_SLOT: usize = 0; -const PREV_BLOCK_HASH_SLOT: usize = 1; -const NEW_BLOCK_TIMESTAMP_SLOT: usize = 2; -const NEW_BLOCK_NUMBER_SLOT: usize = 3; -const FAIR_PUBDATA_PRICE_SLOT: usize = 4; -const FAIR_L2_GAS_PRICE_SLOT: usize = 5; -const EXPECTED_BASE_FEE_SLOT: usize = 6; -const SHOULD_SET_NEW_BLOCK_SLOT: usize = 7; - -/// Returns the initial memory for the bootloader based on the current batch environment. -pub(crate) fn bootloader_initial_memory(l1_batch: &L1BatchEnv) -> Vec<(usize, U256)> { - let (prev_block_hash, should_set_new_block) = l1_batch - .previous_batch_hash - .map(|prev_block_hash| (h256_to_u256(prev_block_hash), U256::one())) - .unwrap_or_default(); - - vec![ - ( - OPERATOR_ADDRESS_SLOT, - address_to_u256(&l1_batch.fee_account), - ), - (PREV_BLOCK_HASH_SLOT, prev_block_hash), - (NEW_BLOCK_TIMESTAMP_SLOT, U256::from(l1_batch.timestamp)), - (NEW_BLOCK_NUMBER_SLOT, U256::from(l1_batch.number.0)), - ( - FAIR_PUBDATA_PRICE_SLOT, - U256::from(l1_batch.fee_input.fair_pubdata_price()), - ), - ( - FAIR_L2_GAS_PRICE_SLOT, - U256::from(l1_batch.fee_input.fair_l2_gas_price()), - ), - ( - EXPECTED_BASE_FEE_SLOT, - U256::from(get_batch_base_fee(l1_batch)), - ), - (SHOULD_SET_NEW_BLOCK_SLOT, should_set_new_block), - ] -} diff --git a/core/lib/multivm/src/versions/vm_fast/mod.rs b/core/lib/multivm/src/versions/vm_fast/mod.rs index dca575138553..58e7ad4b006c 100644 --- a/core/lib/multivm/src/versions/vm_fast/mod.rs +++ b/core/lib/multivm/src/versions/vm_fast/mod.rs @@ -6,17 +6,12 @@ pub use self::{ vm::Vm, }; -mod bootloader_state; mod bytecode; mod events; mod glue; -mod hook; -mod initial_bootloader_memory; -mod refund; #[cfg(test)] mod tests; mod tracers; -mod transaction_data; mod utils; mod version; mod vm; diff --git a/core/lib/multivm/src/versions/vm_fast/transaction_data.rs b/core/lib/multivm/src/versions/vm_fast/transaction_data.rs deleted file mode 100644 index 02697beee341..000000000000 --- a/core/lib/multivm/src/versions/vm_fast/transaction_data.rs +++ /dev/null @@ -1,340 +0,0 @@ -use std::convert::TryInto; - -use zksync_types::{ - address_to_h256, - bytecode::BytecodeHash, - ethabi::{encode, Address, Token}, - fee::{encoding_len, Fee}, - h256_to_u256, - l1::is_l1_tx_type, - l2::{L2Tx, TransactionType}, - transaction_request::{PaymasterParams, TransactionRequest}, - web3::Bytes, - Execute, ExecuteTransactionCommon, L2ChainId, L2TxCommonData, Nonce, Transaction, H256, U256, -}; - -use crate::{ - utils::bytecode::bytes_to_be_words, - vm_latest::{ - constants::{MAX_GAS_PER_PUBDATA_BYTE, TX_MAX_COMPUTE_GAS_LIMIT}, - utils::overhead::derive_overhead, - }, -}; - -/// This structure represents the data that is used by -/// the Bootloader to describe the transaction. -#[derive(Debug, Default, Clone)] -pub(crate) struct TransactionData { - pub(crate) tx_type: u8, - pub(crate) from: Address, - pub(crate) to: Option
, - pub(crate) gas_limit: U256, - pub(crate) pubdata_price_limit: U256, - pub(crate) max_fee_per_gas: U256, - pub(crate) max_priority_fee_per_gas: U256, - pub(crate) paymaster: Address, - pub(crate) nonce: U256, - pub(crate) value: U256, - // The reserved fields that are unique for different types of transactions. - // E.g. nonce is currently used in all transaction, but it should not be mandatory - // in the long run. - pub(crate) reserved: [U256; 4], - pub(crate) data: Vec, - pub(crate) signature: Vec, - // The factory deps provided with the transaction. - // Note that *only hashes* of these bytecodes are signed by the user - // and they are used in the ABI encoding of the struct. - // TODO: include this into the tx signature as part of SMA-1010 - pub(crate) factory_deps: Vec>, - pub(crate) paymaster_input: Vec, - pub(crate) reserved_dynamic: Vec, - pub(crate) raw_bytes: Option>, -} - -impl From for TransactionData { - fn from(execute_tx: Transaction) -> Self { - match execute_tx.common_data { - ExecuteTransactionCommon::L2(common_data) => { - let nonce = U256::from_big_endian(&common_data.nonce.to_be_bytes()); - - let should_check_chain_id = if matches!( - common_data.transaction_type, - TransactionType::LegacyTransaction - ) && common_data.extract_chain_id().is_some() - { - U256([1, 0, 0, 0]) - } else { - U256::zero() - }; - - // Ethereum transactions do not sign gas per pubdata limit, and so for them we need to use - // some default value. We use the maximum possible value that is allowed by the bootloader - // (i.e. we can not use u64::MAX, because the bootloader requires gas per pubdata for such - // transactions to be higher than `MAX_GAS_PER_PUBDATA_BYTE`). - let gas_per_pubdata_limit = if common_data.transaction_type.is_ethereum_type() { - MAX_GAS_PER_PUBDATA_BYTE.into() - } else { - common_data.fee.gas_per_pubdata_limit - }; - - TransactionData { - tx_type: (common_data.transaction_type as u32) as u8, - from: common_data.initiator_address, - to: execute_tx.execute.contract_address, - gas_limit: common_data.fee.gas_limit, - pubdata_price_limit: gas_per_pubdata_limit, - max_fee_per_gas: common_data.fee.max_fee_per_gas, - max_priority_fee_per_gas: common_data.fee.max_priority_fee_per_gas, - paymaster: common_data.paymaster_params.paymaster, - nonce, - value: execute_tx.execute.value, - reserved: [ - should_check_chain_id, - U256::zero(), - U256::zero(), - U256::zero(), - ], - data: execute_tx.execute.calldata, - signature: common_data.signature, - factory_deps: execute_tx.execute.factory_deps, - paymaster_input: common_data.paymaster_params.paymaster_input, - reserved_dynamic: vec![], - raw_bytes: execute_tx.raw_bytes.map(|a| a.0), - } - } - ExecuteTransactionCommon::L1(common_data) => { - let refund_recipient = h256_to_u256(address_to_h256(&common_data.refund_recipient)); - TransactionData { - tx_type: common_data.tx_format() as u8, - from: common_data.sender, - to: execute_tx.execute.contract_address, - gas_limit: common_data.gas_limit, - pubdata_price_limit: common_data.gas_per_pubdata_limit, - // It doesn't matter what we put here, since - // the bootloader does not charge anything - max_fee_per_gas: common_data.max_fee_per_gas, - max_priority_fee_per_gas: U256::zero(), - paymaster: Address::default(), - nonce: U256::from(common_data.serial_id.0), // priority op ID - value: execute_tx.execute.value, - reserved: [ - common_data.to_mint, - refund_recipient, - U256::zero(), - U256::zero(), - ], - data: execute_tx.execute.calldata, - // The signature isn't checked for L1 transactions so we don't care - signature: vec![], - factory_deps: execute_tx.execute.factory_deps, - paymaster_input: vec![], - reserved_dynamic: vec![], - raw_bytes: None, - } - } - ExecuteTransactionCommon::ProtocolUpgrade(common_data) => { - let refund_recipient = h256_to_u256(address_to_h256(&common_data.refund_recipient)); - TransactionData { - tx_type: common_data.tx_format() as u8, - from: common_data.sender, - to: execute_tx.execute.contract_address, - gas_limit: common_data.gas_limit, - pubdata_price_limit: common_data.gas_per_pubdata_limit, - // It doesn't matter what we put here, since - // the bootloader does not charge anything - max_fee_per_gas: common_data.max_fee_per_gas, - max_priority_fee_per_gas: U256::zero(), - paymaster: Address::default(), - nonce: U256::from(common_data.upgrade_id as u16), - value: execute_tx.execute.value, - reserved: [ - common_data.to_mint, - refund_recipient, - U256::zero(), - U256::zero(), - ], - data: execute_tx.execute.calldata, - // The signature isn't checked for L1 transactions so we don't care - signature: vec![], - factory_deps: execute_tx.execute.factory_deps, - paymaster_input: vec![], - reserved_dynamic: vec![], - raw_bytes: None, - } - } - } - } -} - -impl TransactionData { - pub(crate) fn abi_encode_with_custom_factory_deps( - self, - factory_deps_hashes: Vec, - ) -> Vec { - encode(&[Token::Tuple(vec![ - Token::Uint(U256::from_big_endian(&self.tx_type.to_be_bytes())), - Token::Address(self.from), - Token::Address(self.to.unwrap_or_default()), - Token::Uint(self.gas_limit), - Token::Uint(self.pubdata_price_limit), - Token::Uint(self.max_fee_per_gas), - Token::Uint(self.max_priority_fee_per_gas), - Token::Address(self.paymaster), - Token::Uint(self.nonce), - Token::Uint(self.value), - Token::FixedArray(self.reserved.iter().copied().map(Token::Uint).collect()), - Token::Bytes(self.data), - Token::Bytes(self.signature), - Token::Array(factory_deps_hashes.into_iter().map(Token::Uint).collect()), - Token::Bytes(self.paymaster_input), - Token::Bytes(self.reserved_dynamic), - ])]) - } - - pub(crate) fn abi_encode(self) -> Vec { - let factory_deps_hashes = self - .factory_deps - .iter() - .map(|dep| BytecodeHash::for_bytecode(dep).value_u256()) - .collect(); - self.abi_encode_with_custom_factory_deps(factory_deps_hashes) - } - - pub(crate) fn into_tokens(self) -> Vec { - bytes_to_be_words(&self.abi_encode()) - } - - pub(crate) fn overhead_gas(&self) -> u32 { - let encoded_len = encoding_len( - self.data.len() as u64, - self.signature.len() as u64, - self.factory_deps.len() as u64, - self.paymaster_input.len() as u64, - self.reserved_dynamic.len() as u64, - ); - - derive_overhead(encoded_len) - } - - pub(crate) fn trusted_ergs_limit(&self) -> U256 { - // No transaction is allowed to spend more than `TX_MAX_COMPUTE_GAS_LIMIT` gas on compute. - U256::from(TX_MAX_COMPUTE_GAS_LIMIT).min(self.gas_limit) - } - - pub(crate) fn tx_hash(&self, chain_id: L2ChainId) -> H256 { - if is_l1_tx_type(self.tx_type) { - return self.canonical_l1_tx_hash().unwrap(); - } - - let l2_tx: L2Tx = self.clone().try_into().unwrap(); - let mut transaction_request: TransactionRequest = l2_tx.into(); - transaction_request.chain_id = Some(chain_id.as_u64()); - - // It is assumed that the `TransactionData` always has all the necessary components to recover the hash. - transaction_request - .get_tx_hash() - .expect("Could not recover L2 transaction hash") - } - - fn canonical_l1_tx_hash(&self) -> Result { - use zksync_types::web3::keccak256; - - if !is_l1_tx_type(self.tx_type) { - return Err(TxHashCalculationError::CannotCalculateL1HashForL2Tx); - } - - let encoded_bytes = self.clone().abi_encode(); - - Ok(H256(keccak256(&encoded_bytes))) - } -} - -#[derive(Debug, Clone, Copy)] -pub(crate) enum TxHashCalculationError { - CannotCalculateL1HashForL2Tx, - CannotCalculateL2HashForL1Tx, -} - -impl TryInto for TransactionData { - type Error = TxHashCalculationError; - - fn try_into(self) -> Result { - if is_l1_tx_type(self.tx_type) { - return Err(TxHashCalculationError::CannotCalculateL2HashForL1Tx); - } - - let common_data = L2TxCommonData { - transaction_type: (self.tx_type as u32).try_into().unwrap(), - nonce: Nonce(self.nonce.as_u32()), - fee: Fee { - max_fee_per_gas: self.max_fee_per_gas, - max_priority_fee_per_gas: self.max_priority_fee_per_gas, - gas_limit: self.gas_limit, - gas_per_pubdata_limit: self.pubdata_price_limit, - }, - signature: self.signature, - input: None, - initiator_address: self.from, - paymaster_params: PaymasterParams { - paymaster: self.paymaster, - paymaster_input: self.paymaster_input, - }, - }; - let execute = Execute { - contract_address: self.to, - value: self.value, - calldata: self.data, - factory_deps: self.factory_deps, - }; - - Ok(L2Tx { - execute, - common_data, - received_timestamp_ms: 0, - raw_bytes: self.raw_bytes.map(Bytes::from), - }) - } -} - -#[cfg(test)] -mod tests { - use zksync_types::fee::encoding_len; - - use super::*; - - #[test] - fn test_consistency_with_encoding_length() { - let transaction = TransactionData { - tx_type: 113, - from: Address::random(), - to: Some(Address::random()), - gas_limit: U256::from(1u32), - pubdata_price_limit: U256::from(1u32), - max_fee_per_gas: U256::from(1u32), - max_priority_fee_per_gas: U256::from(1u32), - paymaster: Address::random(), - nonce: U256::zero(), - value: U256::zero(), - // The reserved fields that are unique for different types of transactions. - // E.g. nonce is currently used in all transaction, but it should not be mandatory - // in the long run. - reserved: [U256::zero(); 4], - data: vec![0u8; 65], - signature: vec![0u8; 75], - // The factory deps provided with the transaction. - // Note that *only hashes* of these bytecodes are signed by the user - // and they are used in the ABI encoding of the struct. - // TODO: include this into the tx signature as part of SMA-1010 - factory_deps: vec![vec![0u8; 32], vec![1u8; 32]], - paymaster_input: vec![0u8; 85], - reserved_dynamic: vec![0u8; 32], - raw_bytes: None, - }; - - let assumed_encoded_len = encoding_len(65, 75, 2, 85, 32); - - let true_encoding_len = transaction.into_tokens().len(); - - assert_eq!(assumed_encoded_len, true_encoding_len); - } -} diff --git a/core/lib/multivm/src/versions/vm_fast/vm.rs b/core/lib/multivm/src/versions/vm_fast/vm.rs index 6b14409a2e08..5065b8a7c67d 100644 --- a/core/lib/multivm/src/versions/vm_fast/vm.rs +++ b/core/lib/multivm/src/versions/vm_fast/vm.rs @@ -25,12 +25,8 @@ use zksync_vm2::{ }; use super::{ - bootloader_state::{BootloaderState, BootloaderStateSnapshot}, bytecode::compress_bytecodes, - hook::Hook, - initial_bootloader_memory::bootloader_initial_memory, tracers::{DynamicBytecodes, ValidationTracer, WithBuiltinTracers}, - transaction_data::TransactionData, }; use crate::{ glue::GlueInto, @@ -44,16 +40,19 @@ use crate::{ VmInterfaceHistoryEnabled, VmRevertReason, VmTrackingContracts, }, utils::events::extract_l2tol1logs_from_l1_messenger, - vm_fast::{ - bootloader_state::utils::{apply_l2_block, apply_pubdata_to_memory}, - events::merge_events, - refund::compute_refund, - version::FastVmVersion, - }, - vm_latest::constants::{ - get_operator_refunds_offset, get_result_success_first_slot, - get_vm_hook_params_start_position, get_vm_hook_position, TX_GAS_LIMIT_OFFSET, - VM_HOOK_PARAMS_COUNT, + vm_fast::{events::merge_events, version::FastVmVersion}, + vm_latest::{ + bootloader::{ + utils::{apply_l2_block, apply_pubdata_to_memory}, + BootloaderState, BootloaderStateSnapshot, + }, + constants::{ + get_operator_refunds_offset, get_result_success_first_slot, + get_vm_hook_params_start_position, get_vm_hook_position, TX_GAS_LIMIT_OFFSET, + VM_HOOK_PARAMS_COUNT, + }, + utils::refund::compute_refund, + TransactionData, VmHook, }, VmVersion, }; @@ -136,7 +135,7 @@ impl Vm { &system_env.base_system_smart_contracts.bootloader, true, ); - let bootloader_memory = bootloader_initial_memory(&batch_env); + let bootloader_memory = BootloaderState::initial_memory(&batch_env); let mut inner = VirtualMachine::new( BOOTLOADER_ADDRESS, @@ -258,11 +257,11 @@ impl Vm { pub(crate) fn push_transaction_inner( &mut self, - tx: zksync_types::Transaction, + tx: Transaction, refund: u64, with_compression: bool, ) { - let tx: TransactionData = tx.into(); + let tx = TransactionData::new(tx, false); let overhead = tx.overhead_gas(); self.insert_bytecodes(tx.factory_deps.iter().map(|dep| &dep[..])); @@ -432,9 +431,9 @@ where } }; - let hook = Hook::from_u32(hook); + let hook = VmHook::new(hook); match hook { - Hook::AccountValidationEntered => { + VmHook::AccountValidationEntered => { assert!( account_validation_gas_split.is_none(), "Account validation can't be nested" @@ -453,7 +452,7 @@ where self.inner.current_frame().set_gas(gas_given); } - Hook::ValidationExited => { + VmHook::ValidationExited => { tracer.validation.validation_exited(); if let Some(AccountValidationGasSplit { @@ -467,13 +466,13 @@ where } } - Hook::ValidationStepEnded => { + VmHook::ValidationStepEnded => { if Val::STOP_AFTER_VALIDATION { break (ExecutionResult::Success { output: vec![] }, true); } } - Hook::TxHasEnded => { + VmHook::TxHasEnded => { if let VmExecutionMode::OneTx = execution_mode { // The bootloader may invoke `TxHasEnded` hook without posting a tx result previously. One case when this can happen // is estimating gas for L1 transactions, if a transaction runs out of gas during execution. @@ -493,7 +492,7 @@ where break (tx_result, false); } } - Hook::AskOperatorForRefund => { + VmHook::AskOperatorForRefund => { if track_refunds { let [bootloader_refund, gas_spent_on_pubdata, gas_per_pubdata_byte] = self.get_hook_params(); @@ -535,12 +534,12 @@ where .set_refund_for_current_tx(refund_value); } } - Hook::NotifyAboutRefund => { + VmHook::NotifyAboutRefund => { if track_refunds { refunds.gas_refunded = self.get_hook_params()[0].low_u64() } } - Hook::PostResult => { + VmHook::PostResult => { let result = self.get_hook_params()[0]; let value = self.get_hook_params()[1]; let fp = FatPointer::from(value); @@ -556,7 +555,7 @@ where } }); } - Hook::FinalBatchInfo => { + VmHook::FinalBatchInfo => { // set fictive l2 block let txs_index = self.bootloader_state.free_tx_index(); let l2_block = self.bootloader_state.insert_fictive_l2_block(); @@ -564,7 +563,7 @@ where apply_l2_block(&mut memory, l2_block, txs_index, self.vm_version.into()); self.write_to_bootloader_heap(memory); } - Hook::PubdataRequested => { + VmHook::PubdataRequested => { if !matches!(execution_mode, VmExecutionMode::Batch) { unreachable!("We do not provide the pubdata when executing the block tip or a single transaction"); } @@ -614,14 +613,14 @@ where self.write_to_bootloader_heap(memory_to_apply); } - Hook::PaymasterValidationEntered => { /* unused */ } - Hook::DebugLog => { + VmHook::PaymasterValidationEntered => { /* unused */ } + VmHook::DebugLog => { let (log, log_arg) = self.get_debug_log(); let last_tx = self.bootloader_state.last_l2_block().txs.last(); let tx_hash = last_tx.map(|tx| tx.hash); tracing::trace!(tx = ?tx_hash, "{log}: {log_arg}"); } - Hook::DebugReturnData | Hook::NearCallCatch => { + VmHook::DebugReturnData | VmHook::NearCallCatch => { // These hooks are for debug purposes only } } diff --git a/core/lib/multivm/src/versions/vm_latest/bootloader/init.rs b/core/lib/multivm/src/versions/vm_latest/bootloader/init.rs new file mode 100644 index 000000000000..7897ada6ad23 --- /dev/null +++ b/core/lib/multivm/src/versions/vm_latest/bootloader/init.rs @@ -0,0 +1,46 @@ +use zksync_types::{address_to_u256, h256_to_u256, U256}; + +use super::BootloaderState; +use crate::{interface::L1BatchEnv, vm_latest::utils::fee::get_batch_base_fee}; + +const OPERATOR_ADDRESS_SLOT: usize = 0; +const PREV_BLOCK_HASH_SLOT: usize = 1; +const NEW_BLOCK_TIMESTAMP_SLOT: usize = 2; +const NEW_BLOCK_NUMBER_SLOT: usize = 3; +const FAIR_PUBDATA_PRICE_SLOT: usize = 4; +const FAIR_L2_GAS_PRICE_SLOT: usize = 5; +const EXPECTED_BASE_FEE_SLOT: usize = 6; +const SHOULD_SET_NEW_BLOCK_SLOT: usize = 7; + +impl BootloaderState { + /// Returns the initial memory for the bootloader based on the current batch environment. + pub(crate) fn initial_memory(l1_batch: &L1BatchEnv) -> Vec<(usize, U256)> { + let (prev_block_hash, should_set_new_block) = l1_batch + .previous_batch_hash + .map(|prev_block_hash| (h256_to_u256(prev_block_hash), U256::one())) + .unwrap_or_default(); + + vec![ + ( + OPERATOR_ADDRESS_SLOT, + address_to_u256(&l1_batch.fee_account), + ), + (PREV_BLOCK_HASH_SLOT, prev_block_hash), + (NEW_BLOCK_TIMESTAMP_SLOT, U256::from(l1_batch.timestamp)), + (NEW_BLOCK_NUMBER_SLOT, U256::from(l1_batch.number.0)), + ( + FAIR_PUBDATA_PRICE_SLOT, + U256::from(l1_batch.fee_input.fair_pubdata_price()), + ), + ( + FAIR_L2_GAS_PRICE_SLOT, + U256::from(l1_batch.fee_input.fair_l2_gas_price()), + ), + ( + EXPECTED_BASE_FEE_SLOT, + U256::from(get_batch_base_fee(l1_batch)), + ), + (SHOULD_SET_NEW_BLOCK_SLOT, should_set_new_block), + ] + } +} diff --git a/core/lib/multivm/src/versions/vm_latest/bootloader_state/l2_block.rs b/core/lib/multivm/src/versions/vm_latest/bootloader/l2_block.rs similarity index 97% rename from core/lib/multivm/src/versions/vm_latest/bootloader_state/l2_block.rs rename to core/lib/multivm/src/versions/vm_latest/bootloader/l2_block.rs index 95502b8dc60c..922f5384ab3f 100644 --- a/core/lib/multivm/src/versions/vm_latest/bootloader_state/l2_block.rs +++ b/core/lib/multivm/src/versions/vm_latest/bootloader/l2_block.rs @@ -5,7 +5,7 @@ use zksync_types::{web3::keccak256_concat, L2BlockNumber, H256}; use crate::{ interface::{L2Block, L2BlockEnv}, vm_latest::{ - bootloader_state::{snapshot::L2BlockSnapshot, tx::BootloaderTx}, + bootloader::{snapshot::L2BlockSnapshot, tx::BootloaderTx}, utils::l2_blocks::l2_block_hash, }, }; diff --git a/core/lib/multivm/src/versions/vm_latest/bootloader/mod.rs b/core/lib/multivm/src/versions/vm_latest/bootloader/mod.rs new file mode 100644 index 000000000000..02f67f322f0c --- /dev/null +++ b/core/lib/multivm/src/versions/vm_latest/bootloader/mod.rs @@ -0,0 +1,9 @@ +pub(crate) use self::snapshot::BootloaderStateSnapshot; +pub use self::state::BootloaderState; + +mod init; +mod l2_block; +mod snapshot; +mod state; +mod tx; +pub(crate) mod utils; diff --git a/core/lib/multivm/src/versions/vm_fast/bootloader_state/snapshot.rs b/core/lib/multivm/src/versions/vm_latest/bootloader/snapshot.rs similarity index 100% rename from core/lib/multivm/src/versions/vm_fast/bootloader_state/snapshot.rs rename to core/lib/multivm/src/versions/vm_latest/bootloader/snapshot.rs diff --git a/core/lib/multivm/src/versions/vm_latest/bootloader_state/state.rs b/core/lib/multivm/src/versions/vm_latest/bootloader/state.rs similarity index 99% rename from core/lib/multivm/src/versions/vm_latest/bootloader_state/state.rs rename to core/lib/multivm/src/versions/vm_latest/bootloader/state.rs index 8897ed2dc6e0..0b65d961d3ac 100644 --- a/core/lib/multivm/src/versions/vm_latest/bootloader_state/state.rs +++ b/core/lib/multivm/src/versions/vm_latest/bootloader/state.rs @@ -11,13 +11,13 @@ use crate::{ TxExecutionMode, }, vm_latest::{ - bootloader_state::{ + bootloader::{ l2_block::BootloaderL2Block, snapshot::BootloaderStateSnapshot, utils::{apply_l2_block, apply_tx_to_memory}, }, constants::get_tx_description_offset, - types::internals::TransactionData, + types::TransactionData, utils::l2_blocks::assert_next_block, MultiVmSubversion, }, diff --git a/core/lib/multivm/src/versions/vm_latest/bootloader_state/tx.rs b/core/lib/multivm/src/versions/vm_latest/bootloader/tx.rs similarity index 98% rename from core/lib/multivm/src/versions/vm_latest/bootloader_state/tx.rs rename to core/lib/multivm/src/versions/vm_latest/bootloader/tx.rs index 2c63db7e4354..00c7ae43c58f 100644 --- a/core/lib/multivm/src/versions/vm_latest/bootloader_state/tx.rs +++ b/core/lib/multivm/src/versions/vm_latest/bootloader/tx.rs @@ -1,6 +1,6 @@ use zksync_types::{L2ChainId, H256, U256}; -use crate::{interface::CompressedBytecodeInfo, vm_latest::types::internals::TransactionData}; +use crate::{interface::CompressedBytecodeInfo, vm_latest::types::TransactionData}; /// Information about tx necessary for execution in bootloader. #[derive(Debug, Clone)] diff --git a/core/lib/multivm/src/versions/vm_latest/bootloader_state/utils.rs b/core/lib/multivm/src/versions/vm_latest/bootloader/utils.rs similarity index 99% rename from core/lib/multivm/src/versions/vm_latest/bootloader_state/utils.rs rename to core/lib/multivm/src/versions/vm_latest/bootloader/utils.rs index 77a8ef3a1a71..e309c6c45acb 100644 --- a/core/lib/multivm/src/versions/vm_latest/bootloader_state/utils.rs +++ b/core/lib/multivm/src/versions/vm_latest/bootloader/utils.rs @@ -8,7 +8,7 @@ use crate::{ }, utils::bytecode, vm_latest::{ - bootloader_state::l2_block::BootloaderL2Block, + bootloader::l2_block::BootloaderL2Block, constants::{ get_bootloader_tx_description_offset, get_compressed_bytecodes_offset, get_operator_provided_l1_messenger_pubdata_offset, get_operator_refunds_offset, diff --git a/core/lib/multivm/src/versions/vm_latest/bootloader_state/mod.rs b/core/lib/multivm/src/versions/vm_latest/bootloader_state/mod.rs deleted file mode 100644 index 73830de2759b..000000000000 --- a/core/lib/multivm/src/versions/vm_latest/bootloader_state/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -mod l2_block; -mod snapshot; -mod state; -mod tx; - -pub(crate) mod utils; -pub(crate) use snapshot::BootloaderStateSnapshot; -pub use state::BootloaderState; diff --git a/core/lib/multivm/src/versions/vm_latest/bootloader_state/snapshot.rs b/core/lib/multivm/src/versions/vm_latest/bootloader_state/snapshot.rs deleted file mode 100644 index 8f1cec3cb7f1..000000000000 --- a/core/lib/multivm/src/versions/vm_latest/bootloader_state/snapshot.rs +++ /dev/null @@ -1,25 +0,0 @@ -use zksync_types::H256; - -#[derive(Debug, Clone)] -pub(crate) struct BootloaderStateSnapshot { - /// ID of the next transaction to be executed. - pub(crate) tx_to_execute: usize, - /// Stored L2 blocks in bootloader memory - pub(crate) l2_blocks_len: usize, - /// Snapshot of the last L2 block. Only this block could be changed during the rollback - pub(crate) last_l2_block: L2BlockSnapshot, - /// The number of 32-byte words spent on the already included compressed bytecodes. - pub(crate) compressed_bytecodes_encoding: usize, - /// Current offset of the free space in the bootloader memory. - pub(crate) free_tx_offset: usize, - /// Whether the pubdata information has been provided already - pub(crate) is_pubdata_information_provided: bool, -} - -#[derive(Debug, Clone)] -pub(crate) struct L2BlockSnapshot { - /// The rolling hash of all the transactions in the miniblock - pub(crate) txs_rolling_hash: H256, - /// The number of transactions in the last L2 block - pub(crate) txs_len: usize, -} diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/snapshots.rs b/core/lib/multivm/src/versions/vm_latest/implementation/snapshots.rs index 377c4f548b06..82ea5829342b 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/snapshots.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/snapshots.rs @@ -7,7 +7,7 @@ use crate::{ interface::storage::WriteStorage, vm_latest::{ old_vm::{history_recorder::HistoryEnabled, oracles::OracleWithHistory}, - types::internals::VmSnapshot, + types::VmSnapshot, vm::Vm, }, }; diff --git a/core/lib/multivm/src/versions/vm_latest/implementation/tx.rs b/core/lib/multivm/src/versions/vm_latest/implementation/tx.rs index 6dd73866adf2..02adcc3cdad8 100644 --- a/core/lib/multivm/src/versions/vm_latest/implementation/tx.rs +++ b/core/lib/multivm/src/versions/vm_latest/implementation/tx.rs @@ -6,7 +6,7 @@ use crate::{ vm_latest::{ constants::BOOTLOADER_HEAP_PAGE, implementation::bytecode::{bytecode_to_factory_dep, compress_bytecodes}, - types::internals::TransactionData, + types::TransactionData, vm::Vm, }, HistoryMode, diff --git a/core/lib/multivm/src/versions/vm_latest/mod.rs b/core/lib/multivm/src/versions/vm_latest/mod.rs index 46f8db789ddc..4e739efe9516 100644 --- a/core/lib/multivm/src/versions/vm_latest/mod.rs +++ b/core/lib/multivm/src/versions/vm_latest/mod.rs @@ -1,6 +1,5 @@ -pub(crate) use self::vm::MultiVmSubversion; pub use self::{ - bootloader_state::BootloaderState, + bootloader::BootloaderState, old_vm::{ history_recorder::{ AppDataFrameManagerWithHistory, HistoryDisabled, HistoryEnabled, HistoryMode, @@ -12,12 +11,16 @@ pub use self::{ dispatcher::TracerDispatcher, traits::{ToTracerPointer, TracerPointer, VmTracer}, }, - types::internals::ZkSyncVmState, + types::ZkSyncVmState, utils::transaction_encoding::TransactionVmExt, vm::Vm, }; +pub(crate) use self::{ + types::{TransactionData, VmHook}, + vm::MultiVmSubversion, +}; -mod bootloader_state; +pub(crate) mod bootloader; pub mod constants; mod implementation; mod old_vm; diff --git a/core/lib/multivm/src/versions/vm_latest/old_vm/utils.rs b/core/lib/multivm/src/versions/vm_latest/old_vm/utils.rs index c020d1db000a..aa5155fc003b 100644 --- a/core/lib/multivm/src/versions/vm_latest/old_vm/utils.rs +++ b/core/lib/multivm/src/versions/vm_latest/old_vm/utils.rs @@ -10,7 +10,7 @@ use zksync_types::{Address, U256}; use crate::{ interface::storage::WriteStorage, - vm_latest::{old_vm::memory::SimpleMemory, types::internals::ZkSyncVmState, HistoryMode}, + vm_latest::{old_vm::memory::SimpleMemory, types::ZkSyncVmState, HistoryMode}, }; #[derive(Debug, Clone)] diff --git a/core/lib/multivm/src/versions/vm_latest/tests/mod.rs b/core/lib/multivm/src/versions/vm_latest/tests/mod.rs index 0a89ddb0bf50..10750f6ffaaf 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/mod.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/mod.rs @@ -32,7 +32,7 @@ use crate::{ constants::BOOTLOADER_HEAP_PAGE, old_vm::{event_sink::InMemoryEventSink, history_recorder::HistoryRecorder}, tracers::PubdataTracer, - types::internals::TransactionData, + types::TransactionData, utils::logs::StorageLogQuery, AppDataFrameManagerWithHistory, HistoryMode, SimpleMemory, TracerDispatcher, }, diff --git a/core/lib/multivm/src/versions/vm_latest/tests/rollbacks.rs b/core/lib/multivm/src/versions/vm_latest/tests/rollbacks.rs index f126a7f8fbdd..5b99fc4558ed 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/rollbacks.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/rollbacks.rs @@ -18,7 +18,7 @@ use crate::{ VmTesterBuilder, }, vm_latest::{ - types::internals::ZkSyncVmState, BootloaderState, HistoryEnabled, HistoryMode, + bootloader::BootloaderState, types::ZkSyncVmState, HistoryEnabled, HistoryMode, SimpleMemory, ToTracerPointer, Vm, VmTracer, }, }; diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/circuits_tracer.rs b/core/lib/multivm/src/versions/vm_latest/tracers/circuits_tracer.rs index 6a47f3ae2fbe..059c9e67eeb3 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/circuits_tracer.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/circuits_tracer.rs @@ -15,10 +15,10 @@ use crate::{ tracers::dynamic::vm_1_5_0::DynTracer, utils::CircuitCycleStatistic, vm_latest::{ - bootloader_state::BootloaderState, + bootloader::BootloaderState, old_vm::{history_recorder::HistoryMode, memory::SimpleMemory}, tracers::traits::VmTracer, - types::internals::ZkSyncVmState, + types::ZkSyncVmState, }, }; diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs b/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs index 08ff79524b87..05afbbdb398c 100755 --- a/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/default_tracers.rs @@ -23,17 +23,17 @@ use crate::{ }, tracers::dynamic::vm_1_5_0::DynTracer, vm_latest::{ - bootloader_state::{utils::apply_l2_block, BootloaderState}, + bootloader::{utils::apply_l2_block, BootloaderState}, constants::BOOTLOADER_HEAP_PAGE, old_vm::{history_recorder::HistoryMode, memory::SimpleMemory}, tracers::{ dispatcher::TracerDispatcher, - utils::{computational_gas_price, print_debug_if_needed, VmHook}, + utils::{computational_gas_price, print_debug_log, print_debug_returndata}, CircuitsTracer, RefundsTracer, ResultTracer, }, - types::internals::ZkSyncVmState, + types::ZkSyncVmState, vm::MultiVmSubversion, - VmTracer, + VmHook, VmTracer, }, }; @@ -220,22 +220,18 @@ impl Tracer for DefaultExecutionTracer { } let hook = VmHook::from_opcode_memory(&state, &data, self.subversion); - print_debug_if_needed( - &hook, - &state, - memory, - self.result_tracer.get_latest_result_ptr(), - self.subversion, - ); - match hook { - VmHook::TxHasEnded if matches!(self.execution_mode, VmExecutionMode::OneTx) => { + Some(VmHook::TxHasEnded) if matches!(self.execution_mode, VmExecutionMode::OneTx) => { self.result_tracer.tx_finished_in_one_tx_mode = true; self.tx_has_been_processed = true; } - VmHook::NoValidationEntered => self.in_account_validation = false, - VmHook::AccountValidationEntered => self.in_account_validation = true, - VmHook::FinalBatchInfo => self.final_batch_info_requested = true, + Some(VmHook::ValidationExited) => self.in_account_validation = false, + Some(VmHook::AccountValidationEntered) => self.in_account_validation = true, + Some(VmHook::FinalBatchInfo) => self.final_batch_info_requested = true, + Some(VmHook::DebugLog) => print_debug_log(&state, memory, self.subversion), + Some(VmHook::DebugReturnData) => { + print_debug_returndata(memory, self.result_tracer.get_latest_result_ptr()) + } _ => {} } diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/dispatcher.rs b/core/lib/multivm/src/versions/vm_latest/tracers/dispatcher.rs index 3c3ef1173f53..41ff237551f4 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/dispatcher.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/dispatcher.rs @@ -9,7 +9,8 @@ use crate::{ }, tracers::dynamic::vm_1_5_0::DynTracer, vm_latest::{ - BootloaderState, HistoryMode, SimpleMemory, TracerPointer, VmTracer, ZkSyncVmState, + bootloader::BootloaderState, HistoryMode, SimpleMemory, TracerPointer, VmTracer, + ZkSyncVmState, }, }; diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/evm_deploy_tracer.rs b/core/lib/multivm/src/versions/vm_latest/tracers/evm_deploy_tracer.rs index dd03a9427efa..70e055343e61 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/evm_deploy_tracer.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/evm_deploy_tracer.rs @@ -19,7 +19,7 @@ use crate::{ }, tracers::dynamic::vm_1_5_0::DynTracer, utils::bytecode::bytes_to_be_words, - vm_latest::{BootloaderState, HistoryMode, SimpleMemory, ZkSyncVmState}, + vm_latest::{bootloader::BootloaderState, HistoryMode, SimpleMemory, ZkSyncVmState}, }; /// Tracer responsible for collecting information about EVM deploys and providing those diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/pubdata_tracer.rs b/core/lib/multivm/src/versions/vm_latest/tracers/pubdata_tracer.rs index 31309e6ff062..f1f41a2ef3d3 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/pubdata_tracer.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/pubdata_tracer.rs @@ -27,14 +27,14 @@ use crate::{ }, }, vm_latest::{ - bootloader_state::{utils::apply_pubdata_to_memory, BootloaderState}, + bootloader::{utils::apply_pubdata_to_memory, BootloaderState}, constants::BOOTLOADER_HEAP_PAGE, old_vm::{history_recorder::HistoryMode, memory::SimpleMemory}, - tracers::{traits::VmTracer, utils::VmHook}, - types::internals::ZkSyncVmState, + tracers::traits::VmTracer, + types::ZkSyncVmState, utils::logs::collect_events_and_l1_system_logs_after_timestamp, vm::MultiVmSubversion, - StorageOracle, + StorageOracle, VmHook, }, }; @@ -207,7 +207,7 @@ impl DynTracer> for PubdataTracer { _storage: StoragePtr, ) { let hook = VmHook::from_opcode_memory(&state, &data, self.subversion); - if let VmHook::PubdataRequested = hook { + if matches!(hook, Some(VmHook::PubdataRequested)) { self.pubdata_info_requested = true; } } diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/refunds.rs b/core/lib/multivm/src/versions/vm_latest/tracers/refunds.rs index 2bd08e094327..071c0ac37718 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/refunds.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/refunds.rs @@ -5,7 +5,7 @@ use zk_evm_1_5_0::{ aux_structures::Timestamp, tracing::{BeforeExecutionData, VmLocalStateData}, }; -use zksync_types::{ceil_div_u256, H256, U256}; +use zksync_types::H256; use crate::{ interface::{ @@ -15,16 +15,14 @@ use crate::{ }, tracers::dynamic::vm_1_5_0::DynTracer, vm_latest::{ - bootloader_state::BootloaderState, + bootloader::BootloaderState, constants::{get_operator_refunds_offset, BOOTLOADER_HEAP_PAGE, TX_GAS_LIMIT_OFFSET}, old_vm::{history_recorder::HistoryMode, memory::SimpleMemory}, - tracers::{ - traits::VmTracer, - utils::{get_vm_hook_params, VmHook}, - }, - types::internals::ZkSyncVmState, - utils::fee::get_batch_base_fee, + tracers::{traits::VmTracer, utils::get_vm_hook_params}, + types::ZkSyncVmState, + utils::refund::compute_refund, vm::MultiVmSubversion, + VmHook, }, }; @@ -101,58 +99,15 @@ impl RefundsTracer { pubdata_published: u32, tx_hash: H256, ) -> u64 { - let total_gas_spent = tx_gas_limit - bootloader_refund; - - let gas_spent_on_computation = total_gas_spent - .checked_sub(gas_spent_on_pubdata) - .unwrap_or_else(|| { - tracing::error!( - "Gas spent on pubdata is greater than total gas spent. On pubdata: {}, total: {}", - gas_spent_on_pubdata, - total_gas_spent - ); - 0 - }); - - // For now, bootloader charges only for base fee. - let effective_gas_price = get_batch_base_fee(&self.l1_batch); - - let bootloader_eth_price_per_pubdata_byte = - U256::from(effective_gas_price) * U256::from(current_ergs_per_pubdata_byte); - - let fair_eth_price_per_pubdata_byte = - U256::from(self.l1_batch.fee_input.fair_pubdata_price()); - - // For now, L1 originated transactions are allowed to pay less than fair fee per pubdata, - // so we should take it into account. - let eth_price_per_pubdata_byte_for_calculation = std::cmp::min( - bootloader_eth_price_per_pubdata_byte, - fair_eth_price_per_pubdata_byte, - ); - - let fair_fee_eth = U256::from(gas_spent_on_computation) - * U256::from(self.l1_batch.fee_input.fair_l2_gas_price()) - + U256::from(pubdata_published) * eth_price_per_pubdata_byte_for_calculation; - let pre_paid_eth = U256::from(tx_gas_limit) * U256::from(effective_gas_price); - let refund_eth = pre_paid_eth.checked_sub(fair_fee_eth).unwrap_or_else(|| { - tracing::error!( - "Fair fee is greater than pre paid. Fair fee: {} wei, pre paid: {} wei", - fair_fee_eth, - pre_paid_eth - ); - U256::zero() - }); - - tracing::trace!( - "Fee benchmark for transaction with hash {}", - hex::encode(tx_hash.as_bytes()) - ); - tracing::trace!("Gas Limit: {}", tx_gas_limit); - tracing::trace!("Gas spent on computation: {}", gas_spent_on_computation); - tracing::trace!("Gas spent on pubdata: {}", gas_spent_on_pubdata); - tracing::trace!("Pubdata published: {}", pubdata_published); - - ceil_div_u256(refund_eth, effective_gas_price.into()).as_u64() + compute_refund( + &self.l1_batch, + bootloader_refund, + gas_spent_on_pubdata, + tx_gas_limit, + current_ergs_per_pubdata_byte, + pubdata_published, + tx_hash, + ) } pub(crate) fn pubdata_published(&self) -> u32 { @@ -171,16 +126,16 @@ impl DynTracer> for RefundsTracer { self.timestamp_before_cycle = Timestamp(state.vm_local_state.timestamp); let hook = VmHook::from_opcode_memory(&state, &data, self.subversion); match hook { - VmHook::NotifyAboutRefund => { - self.refund_gas = get_vm_hook_params(memory, self.subversion)[0].as_u64() + Some(VmHook::NotifyAboutRefund) => { + self.refund_gas = get_vm_hook_params(memory, self.subversion)[0].as_u64(); } - VmHook::AskOperatorForRefund => { + Some(VmHook::AskOperatorForRefund) => { self.pending_refund_request = Some(RefundRequest { refund: get_vm_hook_params(memory, self.subversion)[0].as_u64(), gas_spent_on_pubdata: get_vm_hook_params(memory, self.subversion)[1].as_u64(), used_gas_per_pubdata_byte: get_vm_hook_params(memory, self.subversion)[2] .as_u32(), - }) + }); } _ => {} } diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/result_tracer.rs b/core/lib/multivm/src/versions/vm_latest/tracers/result_tracer.rs index 80a3147f65d2..a90c7c8a018a 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/result_tracer.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/result_tracer.rs @@ -16,15 +16,16 @@ use crate::{ }, tracers::dynamic::vm_1_5_0::DynTracer, vm_latest::{ + bootloader::BootloaderState, constants::{get_result_success_first_slot, BOOTLOADER_HEAP_PAGE}, old_vm::utils::{vm_may_have_ended_inner, VmExecutionResult}, tracers::{ traits::VmTracer, - utils::{get_vm_hook_params, read_pointer, VmHook}, + utils::{get_vm_hook_params, read_pointer}, }, - types::internals::ZkSyncVmState, + types::ZkSyncVmState, vm::MultiVmSubversion, - BootloaderState, HistoryMode, SimpleMemory, + HistoryMode, SimpleMemory, VmHook, }, }; @@ -155,7 +156,7 @@ impl DynTracer> for ResultTracer { _storage: StoragePtr, ) { let hook = VmHook::from_opcode_memory(&state, &data, self.subversion); - if let VmHook::ExecutionResult = hook { + if matches!(hook, Some(VmHook::PostResult)) { let vm_hook_params = get_vm_hook_params(memory, self.subversion); let success = vm_hook_params[0]; let returndata = self diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/traits.rs b/core/lib/multivm/src/versions/vm_latest/tracers/traits.rs index 76dab3dd70a1..b85f8e3ca4e0 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/traits.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/traits.rs @@ -5,9 +5,9 @@ use crate::{ }, tracers::dynamic::vm_1_5_0::DynTracer, vm_latest::{ - bootloader_state::BootloaderState, + bootloader::BootloaderState, old_vm::{history_recorder::HistoryMode, memory::SimpleMemory}, - types::internals::ZkSyncVmState, + types::ZkSyncVmState, }, }; diff --git a/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs b/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs index 6f81a3ac8de5..ced3193ca490 100644 --- a/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs +++ b/core/lib/multivm/src/versions/vm_latest/tracers/utils.rs @@ -22,33 +22,15 @@ use crate::vm_latest::{ utils::{aux_heap_page_from_base, heap_page_from_base}, }, vm::MultiVmSubversion, + VmHook, }; -#[derive(Clone, Debug, Copy)] -pub(crate) enum VmHook { - AccountValidationEntered, - PaymasterValidationEntered, - NoValidationEntered, - ValidationStepEndeded, - TxHasEnded, - DebugLog, - DebugReturnData, - NoHook, - NearCallCatch, - AskOperatorForRefund, - NotifyAboutRefund, - ExecutionResult, - FinalBatchInfo, - // Hook used to signal that the final pubdata for a batch is requested. - PubdataRequested, -} - impl VmHook { pub(crate) fn from_opcode_memory( state: &VmLocalStateData<'_>, data: &BeforeExecutionData, subversion: MultiVmSubversion, - ) -> Self { + ) -> Option { let opcode_variant = data.opcode.variant; let heap_page = heap_page_from_base(state.vm_local_state.callstack.current.base_memory_page).0; @@ -64,33 +46,18 @@ impl VmHook { || heap_page != BOOTLOADER_HEAP_PAGE || fat_ptr.offset != get_vm_hook_position(subversion) * 32 { - return Self::NoHook; + return None; } - match value.as_u32() { - 0 => Self::AccountValidationEntered, - 1 => Self::PaymasterValidationEntered, - 2 => Self::NoValidationEntered, - 3 => Self::ValidationStepEndeded, - 4 => Self::TxHasEnded, - 5 => Self::DebugLog, - 6 => Self::DebugReturnData, - 7 => Self::NearCallCatch, - 8 => Self::AskOperatorForRefund, - 9 => Self::NotifyAboutRefund, - 10 => Self::ExecutionResult, - 11 => Self::FinalBatchInfo, - 12 => Self::PubdataRequested, - _ => panic!("Unknown hook: {}", value.as_u32()), - } + Some(Self::new(value.as_u32())) } } -pub(crate) fn get_debug_log( +pub(crate) fn print_debug_log( state: &VmLocalStateData<'_>, memory: &SimpleMemory, subversion: MultiVmSubversion, -) -> String { +) { let vm_hook_params: Vec<_> = get_vm_hook_params(memory, subversion) .into_iter() .map(u256_to_h256) @@ -113,7 +80,7 @@ pub(crate) fn get_debug_log( data.to_string() }; let tx_id = state.vm_local_state.tx_number_in_block; - format!("Bootloader transaction {tx_id}: {msg}: {data_str}") + tracing::trace!("Bootloader transaction {tx_id}: {msg}: {data_str}"); } /// Reads the memory slice represented by the fat pointer. @@ -142,33 +109,17 @@ pub(crate) fn read_pointer( /// Outputs the returndata for the latest call. /// This is usually used to output the revert reason. -pub(crate) fn get_debug_returndata( +pub(crate) fn print_debug_returndata( memory: &SimpleMemory, latest_returndata_ptr: Option, -) -> String { +) { let returndata = if let Some(ptr) = latest_returndata_ptr { read_pointer(memory, ptr) } else { vec![] }; - format!("0x{}", hex::encode(returndata)) -} - -/// Accepts a vm hook and, if it requires to output some debug log, outputs it. -pub(crate) fn print_debug_if_needed( - hook: &VmHook, - state: &VmLocalStateData<'_>, - memory: &SimpleMemory, - latest_returndata_ptr: Option, - subversion: MultiVmSubversion, -) { - let log = match hook { - VmHook::DebugLog => get_debug_log(state, memory, subversion), - VmHook::DebugReturnData => get_debug_returndata(memory, latest_returndata_ptr), - _ => return, - }; - tracing::trace!("{log}"); + tracing::trace!("0x{}", hex::encode(returndata)); } pub(crate) fn computational_gas_price( diff --git a/core/lib/multivm/src/versions/vm_latest/types/hook.rs b/core/lib/multivm/src/versions/vm_latest/types/hook.rs new file mode 100644 index 000000000000..1fbd0c826e5e --- /dev/null +++ b/core/lib/multivm/src/versions/vm_latest/types/hook.rs @@ -0,0 +1,39 @@ +#[derive(Debug, Copy, Clone)] +pub(crate) enum VmHook { + AccountValidationEntered, + PaymasterValidationEntered, + ValidationExited, + ValidationStepEnded, + TxHasEnded, + DebugLog, + DebugReturnData, + NearCallCatch, + AskOperatorForRefund, + NotifyAboutRefund, + PostResult, + FinalBatchInfo, + PubdataRequested, +} + +impl VmHook { + /// # Panics + /// Panics if the number does not correspond to any hook. + pub fn new(raw: u32) -> Self { + match raw { + 0 => Self::AccountValidationEntered, + 1 => Self::PaymasterValidationEntered, + 2 => Self::ValidationExited, + 3 => Self::ValidationStepEnded, + 4 => Self::TxHasEnded, + 5 => Self::DebugLog, + 6 => Self::DebugReturnData, + 7 => Self::NearCallCatch, + 8 => Self::AskOperatorForRefund, + 9 => Self::NotifyAboutRefund, + 10 => Self::PostResult, + 11 => Self::FinalBatchInfo, + 12 => Self::PubdataRequested, + _ => panic!("Unknown hook: {raw}"), + } + } +} diff --git a/core/lib/multivm/src/versions/vm_latest/types/internals/mod.rs b/core/lib/multivm/src/versions/vm_latest/types/internals/mod.rs deleted file mode 100644 index 601b7b8bd014..000000000000 --- a/core/lib/multivm/src/versions/vm_latest/types/internals/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub(crate) use snapshot::VmSnapshot; -pub(crate) use transaction_data::TransactionData; -pub(crate) use vm_state::new_vm_state; -pub use vm_state::ZkSyncVmState; -mod snapshot; -mod transaction_data; -mod vm_state; diff --git a/core/lib/multivm/src/versions/vm_latest/types/l1_batch.rs b/core/lib/multivm/src/versions/vm_latest/types/l1_batch.rs deleted file mode 100644 index 89b22d328ac5..000000000000 --- a/core/lib/multivm/src/versions/vm_latest/types/l1_batch.rs +++ /dev/null @@ -1,43 +0,0 @@ -use zksync_types::{address_to_u256, h256_to_u256, U256}; - -use crate::{interface::L1BatchEnv, vm_latest::utils::fee::get_batch_base_fee}; - -const OPERATOR_ADDRESS_SLOT: usize = 0; -const PREV_BLOCK_HASH_SLOT: usize = 1; -const NEW_BLOCK_TIMESTAMP_SLOT: usize = 2; -const NEW_BLOCK_NUMBER_SLOT: usize = 3; -const FAIR_PUBDATA_PRICE_SLOT: usize = 4; -const FAIR_L2_GAS_PRICE_SLOT: usize = 5; -const EXPECTED_BASE_FEE_SLOT: usize = 6; -const SHOULD_SET_NEW_BLOCK_SLOT: usize = 7; - -/// Returns the initial memory for the bootloader based on the current batch environment. -pub(crate) fn bootloader_initial_memory(l1_batch: &L1BatchEnv) -> Vec<(usize, U256)> { - let (prev_block_hash, should_set_new_block) = l1_batch - .previous_batch_hash - .map(|prev_block_hash| (h256_to_u256(prev_block_hash), U256::one())) - .unwrap_or_default(); - - vec![ - ( - OPERATOR_ADDRESS_SLOT, - address_to_u256(&l1_batch.fee_account), - ), - (PREV_BLOCK_HASH_SLOT, prev_block_hash), - (NEW_BLOCK_TIMESTAMP_SLOT, U256::from(l1_batch.timestamp)), - (NEW_BLOCK_NUMBER_SLOT, U256::from(l1_batch.number.0)), - ( - FAIR_PUBDATA_PRICE_SLOT, - U256::from(l1_batch.fee_input.fair_pubdata_price()), - ), - ( - FAIR_L2_GAS_PRICE_SLOT, - U256::from(l1_batch.fee_input.fair_l2_gas_price()), - ), - ( - EXPECTED_BASE_FEE_SLOT, - U256::from(get_batch_base_fee(l1_batch)), - ), - (SHOULD_SET_NEW_BLOCK_SLOT, should_set_new_block), - ] -} diff --git a/core/lib/multivm/src/versions/vm_latest/types/mod.rs b/core/lib/multivm/src/versions/vm_latest/types/mod.rs index a12005734abb..0963b7c458c0 100644 --- a/core/lib/multivm/src/versions/vm_latest/types/mod.rs +++ b/core/lib/multivm/src/versions/vm_latest/types/mod.rs @@ -1,2 +1,9 @@ -pub(crate) mod internals; -mod l1_batch; +mod hook; +mod snapshot; +mod transaction_data; +mod vm_state; + +pub use self::vm_state::ZkSyncVmState; +pub(crate) use self::{ + hook::VmHook, snapshot::VmSnapshot, transaction_data::TransactionData, vm_state::new_vm_state, +}; diff --git a/core/lib/multivm/src/versions/vm_latest/types/internals/snapshot.rs b/core/lib/multivm/src/versions/vm_latest/types/snapshot.rs similarity index 82% rename from core/lib/multivm/src/versions/vm_latest/types/internals/snapshot.rs rename to core/lib/multivm/src/versions/vm_latest/types/snapshot.rs index 76b466fd605b..b43392b5111a 100644 --- a/core/lib/multivm/src/versions/vm_latest/types/internals/snapshot.rs +++ b/core/lib/multivm/src/versions/vm_latest/types/snapshot.rs @@ -1,6 +1,6 @@ use zk_evm_1_5_0::vm_state::VmLocalState; -use crate::vm_latest::bootloader_state::BootloaderStateSnapshot; +use crate::vm_latest::bootloader::BootloaderStateSnapshot; /// A snapshot of the VM that holds enough information to /// rollback the VM to some historical state. diff --git a/core/lib/multivm/src/versions/vm_latest/types/internals/transaction_data.rs b/core/lib/multivm/src/versions/vm_latest/types/transaction_data.rs similarity index 100% rename from core/lib/multivm/src/versions/vm_latest/types/internals/transaction_data.rs rename to core/lib/multivm/src/versions/vm_latest/types/transaction_data.rs diff --git a/core/lib/multivm/src/versions/vm_latest/types/internals/vm_state.rs b/core/lib/multivm/src/versions/vm_latest/types/vm_state.rs similarity index 97% rename from core/lib/multivm/src/versions/vm_latest/types/internals/vm_state.rs rename to core/lib/multivm/src/versions/vm_latest/types/vm_state.rs index 63f06f4fd846..55b504a85f7c 100644 --- a/core/lib/multivm/src/versions/vm_latest/types/internals/vm_state.rs +++ b/core/lib/multivm/src/versions/vm_latest/types/vm_state.rs @@ -20,7 +20,7 @@ use crate::{ }, utils::bytecode::bytes_to_be_words, vm_latest::{ - bootloader_state::BootloaderState, + bootloader::BootloaderState, constants::BOOTLOADER_HEAP_PAGE, old_vm::{ event_sink::InMemoryEventSink, @@ -31,7 +31,6 @@ use crate::{ }, }, oracles::storage::StorageOracle, - types::l1_batch::bootloader_initial_memory, utils::l2_blocks::{assert_next_block, load_last_l2_block}, }, }; @@ -106,7 +105,7 @@ pub(crate) fn new_vm_state( Timestamp(0), ); - let bootloader_initial_memory = bootloader_initial_memory(l1_batch_env); + let bootloader_initial_memory = BootloaderState::initial_memory(l1_batch_env); memory.populate_page( BOOTLOADER_HEAP_PAGE as usize, bootloader_initial_memory.clone(), diff --git a/core/lib/multivm/src/versions/vm_latest/utils/logs.rs b/core/lib/multivm/src/versions/vm_latest/utils/logs.rs index dfa23685dcda..9e4c61ab059c 100644 --- a/core/lib/multivm/src/versions/vm_latest/utils/logs.rs +++ b/core/lib/multivm/src/versions/vm_latest/utils/logs.rs @@ -6,7 +6,7 @@ use crate::{ interface::{storage::WriteStorage, L1BatchEnv, VmEvent}, vm_latest::{ old_vm::{events::merge_events, history_recorder::HistoryMode}, - types::internals::ZkSyncVmState, + types::ZkSyncVmState, }, }; diff --git a/core/lib/multivm/src/versions/vm_latest/utils/mod.rs b/core/lib/multivm/src/versions/vm_latest/utils/mod.rs index 97483633bc54..04821d411f84 100644 --- a/core/lib/multivm/src/versions/vm_latest/utils/mod.rs +++ b/core/lib/multivm/src/versions/vm_latest/utils/mod.rs @@ -6,6 +6,7 @@ pub mod fee; pub mod l2_blocks; pub(crate) mod logs; pub mod overhead; +pub(crate) mod refund; pub mod transaction_encoding; pub const fn heap_page_from_base(base: MemoryPage) -> MemoryPage { diff --git a/core/lib/multivm/src/versions/vm_fast/refund.rs b/core/lib/multivm/src/versions/vm_latest/utils/refund.rs similarity index 100% rename from core/lib/multivm/src/versions/vm_fast/refund.rs rename to core/lib/multivm/src/versions/vm_latest/utils/refund.rs diff --git a/core/lib/multivm/src/versions/vm_latest/utils/transaction_encoding.rs b/core/lib/multivm/src/versions/vm_latest/utils/transaction_encoding.rs index ed532f89dbc6..515f436df89c 100644 --- a/core/lib/multivm/src/versions/vm_latest/utils/transaction_encoding.rs +++ b/core/lib/multivm/src/versions/vm_latest/utils/transaction_encoding.rs @@ -1,6 +1,6 @@ use zksync_types::Transaction; -use crate::vm_latest::types::internals::TransactionData; +use crate::vm_latest::types::TransactionData; /// Extension for transactions, specific for VM. Required for bypassing the orphan rule pub trait TransactionVmExt { diff --git a/core/lib/multivm/src/versions/vm_latest/vm.rs b/core/lib/multivm/src/versions/vm_latest/vm.rs index 3914bfca17a2..9eb342a6c8d6 100644 --- a/core/lib/multivm/src/versions/vm_latest/vm.rs +++ b/core/lib/multivm/src/versions/vm_latest/vm.rs @@ -21,10 +21,10 @@ use crate::{ }, utils::{bytecode::be_words_to_bytes, events::extract_l2tol1logs_from_l1_messenger}, vm_latest::{ - bootloader_state::BootloaderState, + bootloader::BootloaderState, old_vm::{events::merge_events, history_recorder::HistoryEnabled}, tracers::{dispatcher::TracerDispatcher, PubdataTracer}, - types::internals::{new_vm_state, VmSnapshot, ZkSyncVmState}, + types::{new_vm_state, VmSnapshot, ZkSyncVmState}, }, HistoryMode, }; From 6cc9b9e405b03a7e30f3c92735b7452099c165d0 Mon Sep 17 00:00:00 2001 From: perekopskiy <53865202+perekopskiy@users.noreply.github.com> Date: Fri, 17 Jan 2025 10:11:23 +0200 Subject: [PATCH 30/97] fix: eth aggregator restriction (#3490) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ fixes eth aggregator restrictions ## Why ❔ ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- core/node/eth_sender/src/aggregator.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/node/eth_sender/src/aggregator.rs b/core/node/eth_sender/src/aggregator.rs index 33b9500b2d18..69e0e45a9b0d 100644 --- a/core/node/eth_sender/src/aggregator.rs +++ b/core/node/eth_sender/src/aggregator.rs @@ -69,8 +69,7 @@ impl OperationSkippingRestrictions { ) -> bool { if let Some(reason) = reason { tracing::info!( - "Skipping sending commit operation of type {} for batches {}-{} \ - since {}", + "Skipping sending operation of type {} for batches {}-{} since {}", agg_op.get_action_type(), agg_op.l1_batch_range().start(), agg_op.l1_batch_range().end(), @@ -95,13 +94,13 @@ impl OperationSkippingRestrictions { fn filter_prove_op(&self, prove_op: Option) -> Option { let op = AggregatedOperation::PublishProofOnchain(prove_op?); - self.check_for_continuation(&op, self.commit_restriction) + self.check_for_continuation(&op, self.prove_restriction) .then_some(op) } fn filter_execute_op(&self, execute_op: Option) -> Option { let op = AggregatedOperation::Execute(execute_op?); - self.check_for_continuation(&op, self.commit_restriction) + self.check_for_continuation(&op, self.execute_restriction) .then_some(op) } } From cbf2c31e353fd7a5167fcca7e2df87026050c21a Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Fri, 17 Jan 2025 20:41:27 +1100 Subject: [PATCH 31/97] fix(en): make EN use main node's fee input (#3489) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Alternative to https://github.com/matter-labs/zksync-era/pull/3487 Before this PR EN based its gas pricing solely on main node's fee params which are based on immediate market conditions and not representative of the open batch's fee input. This is a problem on environments with long-running batches. This PR makes EN fetch main node's batch fee input along with fee params and report fetched batch fee input from its fee input provider. ## Why ❔ ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- .../src/l1_gas_price/main_node_fetcher.rs | 52 ++++++++++++++----- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs b/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs index 259a5e3e3fed..587142ae499b 100644 --- a/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs +++ b/core/node/fee_model/src/l1_gas_price/main_node_fetcher.rs @@ -3,8 +3,9 @@ use std::{ time::Duration, }; +use async_trait::async_trait; use tokio::sync::watch::Receiver; -use zksync_types::fee_model::FeeParams; +use zksync_types::fee_model::{BatchFeeInput, FeeParams}; use zksync_web3_decl::{ client::{DynClient, L2}, error::ClientRpcContext, @@ -15,8 +16,9 @@ use crate::BatchFeeModelInputProvider; const SLEEP_INTERVAL: Duration = Duration::from_secs(5); -/// This structure maintains the known L1 gas price by periodically querying +/// This structure maintains the known fee params/input by periodically querying /// the main node. +/// /// It is required since the main node doesn't only observe the current L1 gas price, /// but also applies adjustments to it in order to smooth out the spikes. /// The same algorithm cannot be consistently replicated on the external node side, @@ -24,28 +26,40 @@ const SLEEP_INTERVAL: Duration = Duration::from_secs(5); #[derive(Debug)] pub struct MainNodeFeeParamsFetcher { client: Box>, - main_node_fee_params: RwLock, + main_node_fee_state: RwLock<(FeeParams, BatchFeeInput)>, } impl MainNodeFeeParamsFetcher { pub fn new(client: Box>) -> Self { + let fee_params = FeeParams::sensible_v1_default(); + let fee_input = fee_params.scale(1.0, 1.0); Self { client: client.for_component("fee_params_fetcher"), - main_node_fee_params: RwLock::new(FeeParams::sensible_v1_default()), + main_node_fee_state: RwLock::new((fee_params, fee_input)), } } pub async fn run(self: Arc, mut stop_receiver: Receiver) -> anyhow::Result<()> { while !*stop_receiver.borrow_and_update() { - let fetch_result = self - .client - .get_fee_params() - .rpc_context("get_fee_params") - .await; - let main_node_fee_params = match fetch_result { - Ok(price) => price, + // We query fee params and fee input together to minimize the potential for them to be + // out of sync. They can still be fetched out of sync in rare circumstances but nothing + // in the system *directly* relies on `BatchFeeModelInputProvider::get_fee_model_params` + // except for `zks_getFeeParams`. Which is likely fine because EN is essentially + // mimicking how it observed the call to main node. + let (params_result, input_result) = tokio::join!( + self.client.get_fee_params().rpc_context("get_fee_params"), + self.client + .get_batch_fee_input() + .rpc_context("get_batch_fee_input") + ); + let fee_state_result = + params_result.and_then(|params| input_result.map(|input| (params, input))); + let main_node_fee_state = match fee_state_result { + Ok((fee_params, fee_input)) => { + (fee_params, BatchFeeInput::PubdataIndependent(fee_input)) + } Err(err) => { - tracing::warn!("Unable to get the gas price: {}", err); + tracing::warn!("Unable to get main node's fee params/input: {}", err); // A delay to avoid spamming the main node with requests. if tokio::time::timeout(SLEEP_INTERVAL, stop_receiver.changed()) .await @@ -56,7 +70,7 @@ impl MainNodeFeeParamsFetcher { continue; } }; - *self.main_node_fee_params.write().unwrap() = main_node_fee_params; + *self.main_node_fee_state.write().unwrap() = main_node_fee_state; if tokio::time::timeout(SLEEP_INTERVAL, stop_receiver.changed()) .await @@ -71,8 +85,18 @@ impl MainNodeFeeParamsFetcher { } } +#[async_trait] impl BatchFeeModelInputProvider for MainNodeFeeParamsFetcher { + async fn get_batch_fee_input_scaled( + &self, + // EN's scale factors are ignored as we have already fetched scaled fee input from main node + _l1_gas_price_scale_factor: f64, + _l1_pubdata_price_scale_factor: f64, + ) -> anyhow::Result { + Ok(self.main_node_fee_state.read().unwrap().1) + } + fn get_fee_model_params(&self) -> FeeParams { - *self.main_node_fee_params.read().unwrap() + self.main_node_fee_state.read().unwrap().0 } } From b50c973111afa660458abbff53c33d524a7d8a84 Mon Sep 17 00:00:00 2001 From: perekopskiy <53865202+perekopskiy@users.noreply.github.com> Date: Fri, 17 Jan 2025 12:10:33 +0200 Subject: [PATCH 32/97] ci: Update run_loadtest_from_github_actions (#3488) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ make script work after Cargo.toml was moved ## Why ❔ ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- bin/run_loadtest_from_github_actions | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/run_loadtest_from_github_actions b/bin/run_loadtest_from_github_actions index 149988d63d8f..9222673051a4 100755 --- a/bin/run_loadtest_from_github_actions +++ b/bin/run_loadtest_from_github_actions @@ -19,4 +19,4 @@ export CONTRACT_EXECUTION_PARAMS_RECURSIVE_CALLS=${execution_params[5]} export CONTRACT_EXECUTION_PARAMS_DEPLOYS=${execution_params[6]} # Run the test -cargo run --bin loadnext +cd core && cargo run --bin loadnext From cb5c9b73c39ca2ce55984f5689f565823b2e0ca7 Mon Sep 17 00:00:00 2001 From: Anton Baliasnikov Date: Fri, 17 Jan 2025 10:34:51 +0000 Subject: [PATCH 33/97] ci: fix jobs after core workspace refactoring (#3492) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Fix CI jobs after core workspace refactoring: * [x] [Run VM benchmarks](https://github.com/matter-labs/zksync-era/actions/runs/12826132552/job/35765453916?pr=3492) * [x] [Protobuf compatibility](https://github.com/matter-labs/zksync-era/actions/runs/12826132555/job/35765453747?pr=3492) ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- .github/workflows/protobuf.yaml | 6 +++++- .github/workflows/vm-perf-comparison.yml | 2 +- .github/workflows/vm-perf-to-prometheus.yml | 8 ++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/protobuf.yaml b/.github/workflows/protobuf.yaml index 0d7f43f1c2a2..ba667430dd04 100644 --- a/.github/workflows/protobuf.yaml +++ b/.github/workflows/protobuf.yaml @@ -46,14 +46,16 @@ jobs: run: git checkout $(git merge-base $BASE $HEAD) --recurse-submodules working-directory: ./before + - name: compile before run: cargo check --manifest-path ./core/Cargo.toml --all-targets working-directory: ./before + - name: build before.binpb run: > perl -ne 'print "$1\n" if /PROTOBUF_DESCRIPTOR="(.*)"/' `find ./before/core/target/debug/build/*/output` - | xargs cat > ./before/.binpb + | xargs cat > ./before.binpb # after - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4 @@ -61,9 +63,11 @@ jobs: ref: ${{ env.HEAD }} path: after submodules: recursive + - name: compile after run: cargo check --manifest-path ./core/Cargo.toml --all-targets working-directory: ./after + - name: build after.binpb run: > perl -ne 'print "$1\n" if /PROTOBUF_DESCRIPTOR="(.*)"/' diff --git a/.github/workflows/vm-perf-comparison.yml b/.github/workflows/vm-perf-comparison.yml index 59e44023243c..223404fa0b4f 100644 --- a/.github/workflows/vm-perf-comparison.yml +++ b/.github/workflows/vm-perf-comparison.yml @@ -69,7 +69,7 @@ jobs: run: | ci_run zkstackup -g --local ci_run zkstack dev contracts - ci_run cargo bench --package vm-benchmark --bench instructions -- --verbose + ci_run cargo bench --manifest-path ./core/Cargo.toml --package vm-benchmark --bench instructions -- --verbose ci_run cargo bench --manifest-path ./core/Cargo.toml \ --package vm-benchmark --bench instructions -- --print > instructions.log 2>/dev/null diff --git a/.github/workflows/vm-perf-to-prometheus.yml b/.github/workflows/vm-perf-to-prometheus.yml index 93d33116794f..0868e0902342 100644 --- a/.github/workflows/vm-perf-to-prometheus.yml +++ b/.github/workflows/vm-perf-to-prometheus.yml @@ -45,8 +45,8 @@ jobs: - name: run benchmarks run: | - ci_run cargo bench --package vm-benchmark --bench oneshot + ci_run cargo bench --manifest-path ./core/Cargo.toml --package vm-benchmark --bench oneshot # Run only benches with 1,000 transactions per batch to not spend too much time - ci_run cargo bench --package vm-benchmark --bench batch '/1000$' - ci_run cargo bench --package vm-benchmark --bench instructions -- --verbose - ci_run cargo bench --package vm-benchmark --bench instructions -- --print + ci_run cargo bench --manifest-path ./core/Cargo.toml --package vm-benchmark --bench batch '/1000$' + ci_run cargo bench --manifest-path ./core/Cargo.toml --package vm-benchmark --bench instructions -- --verbose + ci_run cargo bench --manifest-path ./core/Cargo.toml --package vm-benchmark --bench instructions -- --print From e30488e3c393a00449634d9ff39a04bfa38504a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Grze=C5=9Bkiewicz?= Date: Fri, 17 Jan 2025 12:32:58 +0100 Subject: [PATCH 34/97] fix(ci): Fixing broken release please script after cargo files changed location (#3494) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Fixing broken release please script after cargo files changed location --- .github/workflows/release-please-cargo-lock.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-please-cargo-lock.yml b/.github/workflows/release-please-cargo-lock.yml index 8c8036dfa47a..3b6e615e2960 100644 --- a/.github/workflows/release-please-cargo-lock.yml +++ b/.github/workflows/release-please-cargo-lock.yml @@ -40,7 +40,7 @@ jobs: - name: Cargo check if: steps.condition.outputs.skip_steps != 'true' - run: ci_run cargo check + run: ci_run cargo check --manifest-path core/Cargo.toml - name: Push changes if: steps.condition.outputs.skip_steps != 'true' @@ -50,6 +50,6 @@ jobs: git config --global user.email "zksync-era-bot@users.noreply.github.com" git config --global user.name "zksync-era-bot" git remote set-url origin 'https://${{ secrets.RELEASE_TOKEN }}@github.com/matter-labs/zksync-era.git' - git add ./Cargo.lock + git add core/Cargo.lock git commit -m "Update Cargo.lock" git push From 7463636197b329769db1e745dd693fe737dbfecc Mon Sep 17 00:00:00 2001 From: zksync-era-bot <147085853+zksync-era-bot@users.noreply.github.com> Date: Fri, 17 Jan 2025 18:19:41 +0400 Subject: [PATCH 35/97] chore(main): release core 26.0.0 (#3407) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit :robot: I have created a release *beep* *boop* --- ## [26.0.0](https://github.com/matter-labs/zksync-era/compare/core-v25.4.0...core-v26.0.0) (2025-01-17) ### ⚠ BREAKING CHANGES * **contracts:** gateway integration ([#1934](https://github.com/matter-labs/zksync-era/issues/1934)) ### Features * Adapt server for new EVM bytecode hash encoding ([#3396](https://github.com/matter-labs/zksync-era/issues/3396)) ([5a1e6d2](https://github.com/matter-labs/zksync-era/commit/5a1e6d2445d4d4310fc1e54ccd44dc4254e5bcbc)) * Add logging & metrics for mempool ([#3447](https://github.com/matter-labs/zksync-era/issues/3447)) ([64d861d](https://github.com/matter-labs/zksync-era/commit/64d861d1e1d2d46339938ee3174c58cdc3f348c3)) * **api_server:** report gas price based on open batch ([#2868](https://github.com/matter-labs/zksync-era/issues/2868)) ([f30aca0](https://github.com/matter-labs/zksync-era/commit/f30aca00962aa34c8a7acd6e4116290a2b214dcb)) * **contracts:** gateway integration ([#1934](https://github.com/matter-labs/zksync-era/issues/1934)) ([f06cb79](https://github.com/matter-labs/zksync-era/commit/f06cb79883bf320f50089099e0abeb95eaace470)) * da_dispatcher refactoring ([#3409](https://github.com/matter-labs/zksync-era/issues/3409)) ([591cd86](https://github.com/matter-labs/zksync-era/commit/591cd86a1a1e6e4214d3cec74b4c601356060203)) * **en:** make documentation more chain agnostic ([#3376](https://github.com/matter-labs/zksync-era/issues/3376)) ([361243f](https://github.com/matter-labs/zksync-era/commit/361243f3f15e01cf1f3e49b73a579cb962cf0124)) * **eth-sender:** make base fee grow at least as fast as priority fee ([#3386](https://github.com/matter-labs/zksync-era/issues/3386)) ([78af2bf](https://github.com/matter-labs/zksync-era/commit/78af2bf786bb4f7a639fef9fd169594101818b79)) * **eth-watch:** Change protocol upgrade schema ([#3435](https://github.com/matter-labs/zksync-era/issues/3435)) ([2c778fd](https://github.com/matter-labs/zksync-era/commit/2c778fdd3fcd1e774bcb945f14a640ccf4227a2f)) * Features for an easier upgrade ([#3422](https://github.com/matter-labs/zksync-era/issues/3422)) ([3037ee6](https://github.com/matter-labs/zksync-era/commit/3037ee6aa976744a09882b5830d6242ad8336717)) * FFLONK support for compressor ([#3359](https://github.com/matter-labs/zksync-era/issues/3359)) ([1a297be](https://github.com/matter-labs/zksync-era/commit/1a297bedd226c56fc2ba02dc54d79129a271a1eb)) * pubdata type changes from sync-layer-stable ([#3425](https://github.com/matter-labs/zksync-era/issues/3425)) ([f09087b](https://github.com/matter-labs/zksync-era/commit/f09087bab397778976af42c321cbba93f9706b5a)) ### Bug Fixes * **api:** Propagate fallback errors in traces ([#3469](https://github.com/matter-labs/zksync-era/issues/3469)) ([84e3e31](https://github.com/matter-labs/zksync-era/commit/84e3e312688e3aaffe81828471d276e24432d496)) * **en:** make EN use main node's fee input ([#3489](https://github.com/matter-labs/zksync-era/issues/3489)) ([cbf2c31](https://github.com/matter-labs/zksync-era/commit/cbf2c31e353fd7a5167fcca7e2df87026050c21a)) * eth aggregator restriction ([#3490](https://github.com/matter-labs/zksync-era/issues/3490)) ([6cc9b9e](https://github.com/matter-labs/zksync-era/commit/6cc9b9e405b03a7e30f3c92735b7452099c165d0)) ### Performance Improvements * **eth-sender:** optimize sql query ([#3437](https://github.com/matter-labs/zksync-era/issues/3437)) ([0731f60](https://github.com/matter-labs/zksync-era/commit/0731f607a72d18decd1ff74139f190c253d807ef)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: Tomasz Grześkiewicz Co-authored-by: zksync-era-bot --- .github/release-please/manifest.json | 2 +- core/CHANGELOG.md | 33 ++++++++++++++++++++++++++++ core/Cargo.lock | 2 +- core/bin/external_node/Cargo.toml | 2 +- 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/.github/release-please/manifest.json b/.github/release-please/manifest.json index c43a992917e1..782c7a30086c 100644 --- a/.github/release-please/manifest.json +++ b/.github/release-please/manifest.json @@ -1,5 +1,5 @@ { - "core": "25.4.0", + "core": "26.0.0", "prover": "17.1.1", "zkstack_cli": "0.1.2" } diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 12d1169f84a3..d0491b179035 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,5 +1,38 @@ # Changelog +## [26.0.0](https://github.com/matter-labs/zksync-era/compare/core-v25.4.0...core-v26.0.0) (2025-01-17) + + +### ⚠ BREAKING CHANGES + +* **contracts:** gateway integration ([#1934](https://github.com/matter-labs/zksync-era/issues/1934)) + +### Features + +* Adapt server for new EVM bytecode hash encoding ([#3396](https://github.com/matter-labs/zksync-era/issues/3396)) ([5a1e6d2](https://github.com/matter-labs/zksync-era/commit/5a1e6d2445d4d4310fc1e54ccd44dc4254e5bcbc)) +* Add logging & metrics for mempool ([#3447](https://github.com/matter-labs/zksync-era/issues/3447)) ([64d861d](https://github.com/matter-labs/zksync-era/commit/64d861d1e1d2d46339938ee3174c58cdc3f348c3)) +* **api_server:** report gas price based on open batch ([#2868](https://github.com/matter-labs/zksync-era/issues/2868)) ([f30aca0](https://github.com/matter-labs/zksync-era/commit/f30aca00962aa34c8a7acd6e4116290a2b214dcb)) +* **contracts:** gateway integration ([#1934](https://github.com/matter-labs/zksync-era/issues/1934)) ([f06cb79](https://github.com/matter-labs/zksync-era/commit/f06cb79883bf320f50089099e0abeb95eaace470)) +* da_dispatcher refactoring ([#3409](https://github.com/matter-labs/zksync-era/issues/3409)) ([591cd86](https://github.com/matter-labs/zksync-era/commit/591cd86a1a1e6e4214d3cec74b4c601356060203)) +* **en:** make documentation more chain agnostic ([#3376](https://github.com/matter-labs/zksync-era/issues/3376)) ([361243f](https://github.com/matter-labs/zksync-era/commit/361243f3f15e01cf1f3e49b73a579cb962cf0124)) +* **eth-sender:** make base fee grow at least as fast as priority fee ([#3386](https://github.com/matter-labs/zksync-era/issues/3386)) ([78af2bf](https://github.com/matter-labs/zksync-era/commit/78af2bf786bb4f7a639fef9fd169594101818b79)) +* **eth-watch:** Change protocol upgrade schema ([#3435](https://github.com/matter-labs/zksync-era/issues/3435)) ([2c778fd](https://github.com/matter-labs/zksync-era/commit/2c778fdd3fcd1e774bcb945f14a640ccf4227a2f)) +* Features for an easier upgrade ([#3422](https://github.com/matter-labs/zksync-era/issues/3422)) ([3037ee6](https://github.com/matter-labs/zksync-era/commit/3037ee6aa976744a09882b5830d6242ad8336717)) +* FFLONK support for compressor ([#3359](https://github.com/matter-labs/zksync-era/issues/3359)) ([1a297be](https://github.com/matter-labs/zksync-era/commit/1a297bedd226c56fc2ba02dc54d79129a271a1eb)) +* pubdata type changes from sync-layer-stable ([#3425](https://github.com/matter-labs/zksync-era/issues/3425)) ([f09087b](https://github.com/matter-labs/zksync-era/commit/f09087bab397778976af42c321cbba93f9706b5a)) + + +### Bug Fixes + +* **api:** Propagate fallback errors in traces ([#3469](https://github.com/matter-labs/zksync-era/issues/3469)) ([84e3e31](https://github.com/matter-labs/zksync-era/commit/84e3e312688e3aaffe81828471d276e24432d496)) +* **en:** make EN use main node's fee input ([#3489](https://github.com/matter-labs/zksync-era/issues/3489)) ([cbf2c31](https://github.com/matter-labs/zksync-era/commit/cbf2c31e353fd7a5167fcca7e2df87026050c21a)) +* eth aggregator restriction ([#3490](https://github.com/matter-labs/zksync-era/issues/3490)) ([6cc9b9e](https://github.com/matter-labs/zksync-era/commit/6cc9b9e405b03a7e30f3c92735b7452099c165d0)) + + +### Performance Improvements + +* **eth-sender:** optimize sql query ([#3437](https://github.com/matter-labs/zksync-era/issues/3437)) ([0731f60](https://github.com/matter-labs/zksync-era/commit/0731f607a72d18decd1ff74139f190c253d807ef)) + ## [25.4.0](https://github.com/matter-labs/zksync-era/compare/core-v25.3.0...core-v25.4.0) (2024-12-19) diff --git a/core/Cargo.lock b/core/Cargo.lock index 4744b424cea0..0c7b38d2d163 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -11994,7 +11994,7 @@ dependencies = [ [[package]] name = "zksync_external_node" -version = "25.4.0" +version = "26.0.0" dependencies = [ "anyhow", "assert_matches", diff --git a/core/bin/external_node/Cargo.toml b/core/bin/external_node/Cargo.toml index 799108f30723..c8b63a12f70f 100644 --- a/core/bin/external_node/Cargo.toml +++ b/core/bin/external_node/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "zksync_external_node" description = "Non-validator ZKsync node" -version = "25.4.0" # x-release-please-version +version = "26.0.0" # x-release-please-version edition.workspace = true authors.workspace = true homepage.workspace = true From 9b121c96bbb2e53be74aa81e0ca250ce9251f8db Mon Sep 17 00:00:00 2001 From: perekopskiy <53865202+perekopskiy@users.noreply.github.com> Date: Fri, 17 Jan 2025 17:01:10 +0200 Subject: [PATCH 36/97] perf: optimize get_unsealed_l1_batch_inner (#3491) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ optimize get_unsealed_l1_batch_inner ## Why ❔ ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- ...69128b9f77d7ce3b898e44c497dc56a40149.json} | 4 ++-- core/lib/dal/src/blocks_dal.rs | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) rename core/lib/dal/.sqlx/{query-8435ed4ee2a9b962116ecfa522f4ba52c9a0e64d1badc39cc2fef29b1468621a.json => query-970c457cc4513615d9bb6ecd6f1a69128b9f77d7ce3b898e44c497dc56a40149.json} (67%) diff --git a/core/lib/dal/.sqlx/query-8435ed4ee2a9b962116ecfa522f4ba52c9a0e64d1badc39cc2fef29b1468621a.json b/core/lib/dal/.sqlx/query-970c457cc4513615d9bb6ecd6f1a69128b9f77d7ce3b898e44c497dc56a40149.json similarity index 67% rename from core/lib/dal/.sqlx/query-8435ed4ee2a9b962116ecfa522f4ba52c9a0e64d1badc39cc2fef29b1468621a.json rename to core/lib/dal/.sqlx/query-970c457cc4513615d9bb6ecd6f1a69128b9f77d7ce3b898e44c497dc56a40149.json index df856b977026..d0b576bfa688 100644 --- a/core/lib/dal/.sqlx/query-8435ed4ee2a9b962116ecfa522f4ba52c9a0e64d1badc39cc2fef29b1468621a.json +++ b/core/lib/dal/.sqlx/query-970c457cc4513615d9bb6ecd6f1a69128b9f77d7ce3b898e44c497dc56a40149.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT\n number,\n timestamp,\n protocol_version,\n fee_address,\n l1_gas_price,\n l2_fair_gas_price,\n fair_pubdata_price\n FROM\n l1_batches\n WHERE\n NOT is_sealed\n ", + "query": "\n SELECT\n number,\n timestamp,\n protocol_version,\n fee_address,\n l1_gas_price,\n l2_fair_gas_price,\n fair_pubdata_price\n FROM (\n SELECT\n number,\n timestamp,\n protocol_version,\n fee_address,\n l1_gas_price,\n l2_fair_gas_price,\n fair_pubdata_price,\n is_sealed\n FROM l1_batches\n ORDER BY number DESC\n LIMIT 1\n ) AS u\n WHERE NOT is_sealed\n ", "describe": { "columns": [ { @@ -52,5 +52,5 @@ false ] }, - "hash": "8435ed4ee2a9b962116ecfa522f4ba52c9a0e64d1badc39cc2fef29b1468621a" + "hash": "970c457cc4513615d9bb6ecd6f1a69128b9f77d7ce3b898e44c497dc56a40149" } diff --git a/core/lib/dal/src/blocks_dal.rs b/core/lib/dal/src/blocks_dal.rs index f39a9c582df9..7b2f2e33fe2a 100644 --- a/core/lib/dal/src/blocks_dal.rs +++ b/core/lib/dal/src/blocks_dal.rs @@ -835,10 +835,21 @@ impl BlocksDal<'_, '_> { l1_gas_price, l2_fair_gas_price, fair_pubdata_price - FROM - l1_batches - WHERE - NOT is_sealed + FROM ( + SELECT + number, + timestamp, + protocol_version, + fee_address, + l1_gas_price, + l2_fair_gas_price, + fair_pubdata_price, + is_sealed + FROM l1_batches + ORDER BY number DESC + LIMIT 1 + ) AS u + WHERE NOT is_sealed "#, ) .instrument("get_unsealed_l1_batch") From c007aa889ba8ed745b8e0018f63b014b34df0609 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 12:27:05 -0300 Subject: [PATCH 37/97] use `H160` for `DEFAULT_EIGENDA_SVC_MANAGER_ADDRESS` --- core/lib/config/src/configs/da_client/eigen.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/lib/config/src/configs/da_client/eigen.rs b/core/lib/config/src/configs/da_client/eigen.rs index 886b67768f4a..cf92e7e969bf 100644 --- a/core/lib/config/src/configs/da_client/eigen.rs +++ b/core/lib/config/src/configs/da_client/eigen.rs @@ -1,10 +1,11 @@ -use std::str::FromStr; - use serde::Deserialize; use zksync_basic_types::{secrets::PrivateKey, Address, H160}; /// Default address of the EigenDA service manager contract deployed on Holesky. -const DEFAULT_EIGENDA_SVC_MANAGER_ADDRESS: &str = "0xD4A7E1Bd8015057293f0D0A557088c286942e84b"; +const DEFAULT_EIGENDA_SVC_MANAGER_ADDRESS: H160 = H160([ + 0xd4, 0xa7, 0xe1, 0xbd, 0x80, 0x15, 0x05, 0x72, 0x93, 0xf0, 0xd0, 0xa5, 0x57, 0x08, 0x8c, 0x28, + 0x69, 0x42, 0xe8, 0x4b, +]); /// Configuration for the EigenDA remote disperser client. #[derive(Clone, Debug, PartialEq, Deserialize)] @@ -36,7 +37,7 @@ impl Default for EigenConfig { disperser_rpc: "https://disperser-holesky.eigenda.xyz:443".to_string(), settlement_layer_confirmation_depth: 0, eigenda_eth_rpc: Some("https://ethereum-holesky-rpc.publicnode.com".to_string()), - eigenda_svc_manager_address: H160::from_str(DEFAULT_EIGENDA_SVC_MANAGER_ADDRESS).unwrap_or_default(), + eigenda_svc_manager_address: DEFAULT_EIGENDA_SVC_MANAGER_ADDRESS, wait_for_finalization: false, authenticated: false, g1_url: "https://github.com/Layr-Labs/eigenda-proxy/raw/2fd70b99ef5bf137d7bbca3461cf9e1f2c899451/resources/g1.point".to_string(), From 2e7d6cca298ce7f18c7526e06a2080918a80725f Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 12:29:47 -0300 Subject: [PATCH 38/97] `parse` instead of `from_str` --- core/lib/env_config/src/da_client.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/core/lib/env_config/src/da_client.rs b/core/lib/env_config/src/da_client.rs index 2c40a810b05c..51fdd06877e9 100644 --- a/core/lib/env_config/src/da_client.rs +++ b/core/lib/env_config/src/da_client.rs @@ -90,9 +90,6 @@ impl FromEnv for DataAvailabilitySecrets { #[cfg(test)] mod tests { - use std::str::FromStr; - - use zksync_basic_types::H160; use zksync_config::{ configs::{ da_client::{ @@ -270,10 +267,9 @@ mod tests { disperser_rpc: "http://localhost:8080".to_string(), settlement_layer_confirmation_depth: 0, eigenda_eth_rpc: Some("http://localhost:8545".to_string()), - eigenda_svc_manager_address: H160::from_str( - "0x0000000000000000000000000000000000000123" - ) - .unwrap(), + eigenda_svc_manager_address: "0x0000000000000000000000000000000000000123" + .parse() + .unwrap(), wait_for_finalization: true, authenticated: false, g1_url: "resources1".to_string(), From 405b4130570a23caec3841ca72718085dac61b17 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 12:32:52 -0300 Subject: [PATCH 39/97] replace val with shif operation --- core/node/da_clients/src/eigen/verifier.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/node/da_clients/src/eigen/verifier.rs b/core/node/da_clients/src/eigen/verifier.rs index 9f60c0ad3912..be9ab2fb4782 100644 --- a/core/node/da_clients/src/eigen/verifier.rs +++ b/core/node/da_clients/src/eigen/verifier.rs @@ -117,7 +117,7 @@ pub struct Verifier { impl Verifier { pub const DEFAULT_PRIORITY_FEE_PER_GAS: u64 = 100; - pub const SRSORDER: u32 = 268435456; // 2 ^ 28 + pub const SRSORDER: u32 = 1 << 28; // 2 ^ 28 pub const G1POINT: &'static str = "g1.point"; pub const G2POINT: &'static str = "g2.point.powerOf2"; pub const POINT_SIZE: u32 = 32; From 9b6fe48ebe2079427760f840c0c8f5468ad030a5 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 12:36:54 -0300 Subject: [PATCH 40/97] replace `.map_err` with `.context` --- core/node/da_clients/src/eigen/sdk.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/node/da_clients/src/eigen/sdk.rs b/core/node/da_clients/src/eigen/sdk.rs index a3cad7052c13..0289b80a09d3 100644 --- a/core/node/da_clients/src/eigen/sdk.rs +++ b/core/node/da_clients/src/eigen/sdk.rs @@ -193,7 +193,7 @@ impl RawEigenClient { } self.verifier .verify_commitment(blob_info.blob_header.commitment.clone(), &data) - .map_err(|_| anyhow::anyhow!("Failed to verify commitment"))?; + .context("Failed to verify commitment")?; let result = self .verifier From 5a144ad152c9f67a7e005515fb13ad0846e557da Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 12:43:39 -0300 Subject: [PATCH 41/97] remove needless parentheses --- core/node/da_clients/src/eigen/sdk.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/node/da_clients/src/eigen/sdk.rs b/core/node/da_clients/src/eigen/sdk.rs index 0289b80a09d3..7f387dda7f97 100644 --- a/core/node/da_clients/src/eigen/sdk.rs +++ b/core/node/da_clients/src/eigen/sdk.rs @@ -385,7 +385,7 @@ fn get_account_id(secret_key: &SecretKey) -> String { fn convert_by_padding_empty_byte(data: &[u8]) -> Vec { let parse_size = DATA_CHUNK_SIZE - 1; - let chunk_count = (data.len()).div_ceil(parse_size); + let chunk_count = data.len().div_ceil(parse_size); let mut valid_data = Vec::with_capacity(data.len() + chunk_count); for chunk in data.chunks(parse_size) { From 2431a0cd4dbae804cdaf8e14acc2913698558c7f Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 12:48:29 -0300 Subject: [PATCH 42/97] use `div_ceil` --- core/node/da_clients/src/eigen/sdk.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/node/da_clients/src/eigen/sdk.rs b/core/node/da_clients/src/eigen/sdk.rs index 7f387dda7f97..275cb01c7d22 100644 --- a/core/node/da_clients/src/eigen/sdk.rs +++ b/core/node/da_clients/src/eigen/sdk.rs @@ -399,8 +399,8 @@ fn convert_by_padding_empty_byte(data: &[u8]) -> Vec { fn remove_empty_byte_from_padded_bytes(data: &[u8]) -> Vec { let parse_size = DATA_CHUNK_SIZE; - let data_len = (data.len() + parse_size - 1) / parse_size; - let mut valid_data = Vec::with_capacity(data.len() - data_len); + let chunk_count = data.len().div_ceil(parse_size); + let mut valid_data = Vec::with_capacity(data.len() - chunk_count); for chunk in data.chunks(parse_size) { valid_data.extend_from_slice(&chunk[1..]); From a8803751cfa6d9e6befbeaf1269ff9ff7115c82c Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 12:51:55 -0300 Subject: [PATCH 43/97] remove needles `Clone` derives --- core/node/da_clients/src/eigen/sdk.rs | 2 +- core/node/da_clients/src/eigen/verifier.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/node/da_clients/src/eigen/sdk.rs b/core/node/da_clients/src/eigen/sdk.rs index 275cb01c7d22..de69e481d4a9 100644 --- a/core/node/da_clients/src/eigen/sdk.rs +++ b/core/node/da_clients/src/eigen/sdk.rs @@ -32,7 +32,7 @@ use crate::eigen::{ verifier::VerificationError, }; -#[derive(Debug, Clone)] +#[derive(Debug)] pub(crate) struct RawEigenClient { client: DisperserClient, private_key: SecretKey, diff --git a/core/node/da_clients/src/eigen/verifier.rs b/core/node/da_clients/src/eigen/verifier.rs index be9ab2fb4782..6b542785323d 100644 --- a/core/node/da_clients/src/eigen/verifier.rs +++ b/core/node/da_clients/src/eigen/verifier.rs @@ -108,7 +108,7 @@ pub struct VerifierConfig { /// Verifier used to verify the integrity of the blob info /// Kzg is used for commitment verification /// EigenDA service manager is used to connect to the service manager contract -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Verifier { kzg: Kzg, cfg: VerifierConfig, From 801aca63d3c5a1b1841a9462a69315757e99fff0 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 12:56:38 -0300 Subject: [PATCH 44/97] remove redundant `to_vec()` --- core/node/da_clients/src/eigen/verifier.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/node/da_clients/src/eigen/verifier.rs b/core/node/da_clients/src/eigen/verifier.rs index 6b542785323d..2a2f88db9c45 100644 --- a/core/node/da_clients/src/eigen/verifier.rs +++ b/core/node/da_clients/src/eigen/verifier.rs @@ -439,7 +439,7 @@ impl Verifier { .await .map_err(ServiceManagerError::EnrichedClient)?; - let percentages = self.decode_bytes(res.0.to_vec())?; + let percentages = self.decode_bytes(res.0)?; if percentages.len() > quorum_number as usize { return Ok(percentages[quorum_number as usize]); From cd4c631c3f0fb00b68d46b899cd20e52bfee4210 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 13:04:03 -0300 Subject: [PATCH 45/97] use `anyhow::bail!` --- core/node/da_clients/src/eigen/sdk.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/node/da_clients/src/eigen/sdk.rs b/core/node/da_clients/src/eigen/sdk.rs index de69e481d4a9..6e36736c5988 100644 --- a/core/node/da_clients/src/eigen/sdk.rs +++ b/core/node/da_clients/src/eigen/sdk.rs @@ -316,9 +316,9 @@ impl RawEigenClient { match disperser::BlobStatus::try_from(resp.status)? { disperser::BlobStatus::Processing | disperser::BlobStatus::Dispersing => Ok(None), - disperser::BlobStatus::Failed => Err(anyhow::anyhow!("Blob dispatch failed")), + disperser::BlobStatus::Failed => anyhow::bail!("Blob dispatch failed"), disperser::BlobStatus::InsufficientSignatures => { - Err(anyhow::anyhow!("Insufficient signatures")) + anyhow::bail!("Insufficient signatures") } disperser::BlobStatus::Confirmed => { if !self.config.wait_for_finalization { @@ -331,8 +331,7 @@ impl RawEigenClient { let blob_info = resp.info.context("No blob header in response")?; Ok(Some(blob_info)) } - - _ => Err(anyhow::anyhow!("Received unknown blob status")), + _ => anyhow::bail!("Received unknown blob status"), } } From 68825c4952ff28785e5074e68689137a52bea0f5 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 13:10:53 -0300 Subject: [PATCH 46/97] remove needless `Option` and obscuring `anyhow::Result` --- core/node/da_clients/src/eigen/client.rs | 15 ++++++--------- core/node/da_clients/src/eigen/sdk.rs | 11 +++-------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/core/node/da_clients/src/eigen/client.rs b/core/node/da_clients/src/eigen/client.rs index 823dcc746504..d90f13c8d843 100644 --- a/core/node/da_clients/src/eigen/client.rs +++ b/core/node/da_clients/src/eigen/client.rs @@ -99,10 +99,7 @@ mod tests { use crate::eigen::{blob_info::BlobInfo, EigenClient, GetBlobData}; impl EigenClient { - async fn get_blob_data( - &self, - blob_id: BlobInfo, - ) -> anyhow::Result>, DAError> { + async fn get_blob_data(&self, blob_id: BlobInfo) -> anyhow::Result, DAError> { self.client.get_blob_data(blob_id).await } @@ -177,7 +174,7 @@ mod tests { .data; assert_eq!(expected_inclusion_data, actual_inclusion_data); let retrieved_data = client.get_blob_data(blob_info).await.unwrap(); - assert_eq!(retrieved_data.unwrap(), data); + assert_eq!(retrieved_data, data); } #[ignore = "depends on external RPC"] @@ -205,7 +202,7 @@ mod tests { .data; assert_eq!(expected_inclusion_data, actual_inclusion_data); let retrieved_data = client.get_blob_data(blob_info).await.unwrap(); - assert_eq!(retrieved_data.unwrap(), data); + assert_eq!(retrieved_data, data); } #[ignore = "depends on external RPC"] @@ -235,7 +232,7 @@ mod tests { .data; assert_eq!(expected_inclusion_data, actual_inclusion_data); let retrieved_data = client.get_blob_data(blob_info).await.unwrap(); - assert_eq!(retrieved_data.unwrap(), data); + assert_eq!(retrieved_data, data); } #[ignore = "depends on external RPC"] @@ -263,7 +260,7 @@ mod tests { .data; assert_eq!(expected_inclusion_data, actual_inclusion_data); let retrieved_data = client.get_blob_data(blob_info).await.unwrap(); - assert_eq!(retrieved_data.unwrap(), data); + assert_eq!(retrieved_data, data); } #[ignore = "depends on external RPC"] @@ -292,6 +289,6 @@ mod tests { .data; assert_eq!(expected_inclusion_data, actual_inclusion_data); let retrieved_data = client.get_blob_data(blob_info).await.unwrap(); - assert_eq!(retrieved_data.unwrap(), data); + assert_eq!(retrieved_data, data); } } diff --git a/core/node/da_clients/src/eigen/sdk.rs b/core/node/da_clients/src/eigen/sdk.rs index 6e36736c5988..157607a27ea4 100644 --- a/core/node/da_clients/src/eigen/sdk.rs +++ b/core/node/da_clients/src/eigen/sdk.rs @@ -180,9 +180,7 @@ impl RawEigenClient { let blob_info = blob_info::BlobInfo::try_from(blob_info) .map_err(|e| anyhow::anyhow!("Failed to convert blob info: {}", e))?; - let Some(data) = self.get_blob_data(blob_info.clone()).await? else { - return Err(anyhow::anyhow!("Failed to get blob data")); - }; + let data = self.get_blob_data(blob_info.clone()).await?; let data_db = self.get_blob_data.get_blob_data(request_id).await?; if let Some(data_db) = data_db { if data_db != data { @@ -335,10 +333,7 @@ impl RawEigenClient { } } - pub async fn get_blob_data( - &self, - blob_info: BlobInfo, - ) -> anyhow::Result>, DAError> { + pub async fn get_blob_data(&self, blob_info: BlobInfo) -> Result, DAError> { use anyhow::anyhow; use zksync_da_client::types::DAError; @@ -369,7 +364,7 @@ impl RawEigenClient { } let data = remove_empty_byte_from_padded_bytes(&get_response.data); - Ok(Some(data)) + Ok(data) } } From e3b6f17d699469c6b6e58fa51bee8e8c6508d7ae Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 13:40:32 -0300 Subject: [PATCH 47/97] move verifier test to separate file --- .../node/da_clients/src/eigen/verifier/mod.rs | 536 +++++++++++++++++ .../eigen/{verifier.rs => verifier/tests.rs} | 545 +----------------- 2 files changed, 541 insertions(+), 540 deletions(-) create mode 100644 core/node/da_clients/src/eigen/verifier/mod.rs rename core/node/da_clients/src/eigen/{verifier.rs => verifier/tests.rs} (67%) diff --git a/core/node/da_clients/src/eigen/verifier/mod.rs b/core/node/da_clients/src/eigen/verifier/mod.rs new file mode 100644 index 000000000000..fdd42da38f64 --- /dev/null +++ b/core/node/da_clients/src/eigen/verifier/mod.rs @@ -0,0 +1,536 @@ +use std::{collections::HashMap, path::Path, sync::Arc}; + +use ark_bn254::{Fq, G1Affine}; +use ethabi::{encode, ParamType, Token}; +use rust_kzg_bn254::{blob::Blob, kzg::Kzg, polynomial::PolynomialFormat}; +use tokio::{fs::File, io::AsyncWriteExt}; +use url::Url; +use zksync_basic_types::web3::CallRequest; +use zksync_eth_client::{clients::PKSigningClient, EnrichedClientError, EnrichedClientResult}; +use zksync_types::{ + web3::{self, BlockId, BlockNumber}, + Address, U256, U64, +}; + +use super::blob_info::{BatchHeader, BlobHeader, BlobInfo, BlobQuorumParam, G1Commitment}; + +mod tests; + +#[async_trait::async_trait] +pub trait VerifierClient: Sync + Send + std::fmt::Debug { + /// Returns the current block number. + async fn block_number(&self) -> EnrichedClientResult; + + /// Invokes a function on a contract specified by `contract_address` / `contract_abi` using `eth_call`. + async fn call_contract_function( + &self, + request: web3::CallRequest, + block: Option, + ) -> EnrichedClientResult; +} + +#[async_trait::async_trait] +impl VerifierClient for PKSigningClient { + async fn block_number(&self) -> EnrichedClientResult { + self.as_ref().block_number().await + } + + async fn call_contract_function( + &self, + request: web3::CallRequest, + block: Option, + ) -> EnrichedClientResult { + self.as_ref().call_contract_function(request, block).await + } +} + +#[derive(Debug, thiserror::Error)] +pub enum KzgError { + #[error("Kzg setup error: {0}")] + Setup(String), + #[error(transparent)] + Internal(#[from] rust_kzg_bn254::errors::KzgError), +} + +#[derive(Debug, thiserror::Error)] +pub enum ServiceManagerError { + #[error(transparent)] + EnrichedClient(#[from] EnrichedClientError), + #[error("Decoding error: {0}")] + Decoding(String), + #[cfg(test)] + #[error("Parsing error: {0}")] + Parsing(String), +} + +#[derive(Debug, thiserror::Error)] +pub enum VerificationError { + #[error(transparent)] + ServiceManager(#[from] ServiceManagerError), + #[error(transparent)] + Kzg(#[from] KzgError), + #[error("Wrong proof")] + WrongProof, + #[error("Different commitments: expected {expected:?}, got {actual:?}")] + DifferentCommitments { + expected: G1Affine, + actual: G1Affine, + }, + #[error("Different roots: expected {expected:?}, got {actual:?}")] + DifferentRoots { expected: String, actual: String }, + #[error("Empty hash")] + EmptyHash, + #[error("Different hashes: expected {expected:?}, got {actual:?}")] + DifferentHashes { expected: String, actual: String }, + #[error("Wrong quorum params: {blob_quorum_params:?}")] + WrongQuorumParams { blob_quorum_params: BlobQuorumParam }, + #[error("Quorum not confirmed")] + QuorumNotConfirmed, + #[error("Commitment not on curve: {0}")] + CommitmentNotOnCurve(G1Affine), + #[error("Commitment not on correct subgroup: {0}")] + CommitmentNotOnCorrectSubgroup(G1Affine), + #[error("Link Error: {0}")] + LinkError(String), +} + +/// Configuration for the verifier used for authenticated dispersals +#[derive(Debug, Clone)] +pub struct VerifierConfig { + pub rpc_url: String, + pub svc_manager_addr: Address, + pub max_blob_size: u32, + pub g1_url: Url, + pub g2_url: Url, + pub settlement_layer_confirmation_depth: u32, + pub private_key: String, + pub chain_id: u64, +} + +/// Verifier used to verify the integrity of the blob info +/// Kzg is used for commitment verification +/// EigenDA service manager is used to connect to the service manager contract +#[derive(Debug)] +pub struct Verifier { + kzg: Kzg, + cfg: VerifierConfig, + signing_client: Arc, +} + +impl Verifier { + pub const DEFAULT_PRIORITY_FEE_PER_GAS: u64 = 100; + pub const SRSORDER: u32 = 1 << 28; // 2 ^ 28 + pub const G1POINT: &'static str = "g1.point"; + pub const G2POINT: &'static str = "g2.point.powerOf2"; + pub const POINT_SIZE: u32 = 32; + + async fn save_point(url: Url, point: String) -> Result<(), VerificationError> { + let response = reqwest::get(url) + .await + .map_err(|e| VerificationError::LinkError(e.to_string()))?; + if !response.status().is_success() { + return Err(VerificationError::LinkError( + "Failed to get point".to_string(), + )); + } + let path = format!("./{}", point); + let path = Path::new(&path); + let mut file = File::create(path) + .await + .map_err(|e| VerificationError::LinkError(e.to_string()))?; + let content = response + .bytes() + .await + .map_err(|e| VerificationError::LinkError(e.to_string()))?; + file.write_all(&content) + .await + .map_err(|e| VerificationError::LinkError(e.to_string()))?; + Ok(()) + } + + async fn save_points(url_g1: Url, url_g2: Url) -> Result { + Self::save_point(url_g1, Self::G1POINT.to_string()).await?; + Self::save_point(url_g2, Self::G2POINT.to_string()).await?; + + Ok(".".to_string()) + } + + pub(crate) async fn new( + cfg: VerifierConfig, + signing_client: Arc, + ) -> Result { + let srs_points_to_load = cfg.max_blob_size / Self::POINT_SIZE; + let path = Self::save_points(cfg.clone().g1_url, cfg.clone().g2_url).await?; + let kzg_handle = tokio::task::spawn_blocking(move || { + Kzg::setup( + &format!("{}/{}", path, Self::G1POINT), + "", + &format!("{}/{}", path, Self::G2POINT), + Self::SRSORDER, + srs_points_to_load, + "".to_string(), + ) + }); + + let kzg = kzg_handle + .await + .map_err(|e| VerificationError::Kzg(KzgError::Setup(e.to_string())))? + .map_err(KzgError::Internal)?; + + Ok(Self { + kzg, + cfg, + signing_client, + }) + } + + /// Return the commitment from a blob + fn commit(&self, blob: &[u8]) -> Result { + let blob = Blob::from_bytes_and_pad(blob); + self.kzg + .blob_to_kzg_commitment(&blob, PolynomialFormat::InEvaluationForm) + .map_err(|e| VerificationError::Kzg(KzgError::Internal(e))) + } + + /// Compare the given commitment with the commitment generated with the blob + pub fn verify_commitment( + &self, + expected_commitment: G1Commitment, + blob: &[u8], + ) -> Result<(), VerificationError> { + let actual_commitment = self.commit(blob)?; + let expected_commitment = G1Affine::new_unchecked( + Fq::from(num_bigint::BigUint::from_bytes_be(&expected_commitment.x)), + Fq::from(num_bigint::BigUint::from_bytes_be(&expected_commitment.y)), + ); + if !expected_commitment.is_on_curve() { + return Err(VerificationError::CommitmentNotOnCurve(expected_commitment)); + } + if !expected_commitment.is_in_correct_subgroup_assuming_on_curve() { + return Err(VerificationError::CommitmentNotOnCorrectSubgroup( + expected_commitment, + )); + } + if actual_commitment != expected_commitment { + return Err(VerificationError::DifferentCommitments { + expected: expected_commitment, + actual: actual_commitment, + }); + } + Ok(()) + } + + pub(crate) fn hash_encode_blob_header(&self, blob_header: &BlobHeader) -> Vec { + let mut blob_quorums = vec![]; + for quorum in &blob_header.blob_quorum_params { + let quorum = Token::Tuple(vec![ + Token::Uint(ethabi::Uint::from(quorum.quorum_number)), + Token::Uint(ethabi::Uint::from(quorum.adversary_threshold_percentage)), + Token::Uint(ethabi::Uint::from(quorum.confirmation_threshold_percentage)), + Token::Uint(ethabi::Uint::from(quorum.chunk_length)), + ]); + blob_quorums.push(quorum); + } + let blob_header = Token::Tuple(vec![ + Token::Tuple(vec![ + Token::Uint(ethabi::Uint::from_big_endian(&blob_header.commitment.x)), + Token::Uint(ethabi::Uint::from_big_endian(&blob_header.commitment.y)), + ]), + Token::Uint(ethabi::Uint::from(blob_header.data_length)), + Token::Array(blob_quorums), + ]); + + let encoded = encode(&[blob_header]); + web3::keccak256(&encoded).to_vec() + } + + pub(crate) fn process_inclusion_proof( + &self, + proof: &[u8], + leaf: [u8; 32], + index: u32, + ) -> Result, VerificationError> { + let mut index = index; + if proof.is_empty() || proof.len() % 32 != 0 { + return Err(VerificationError::WrongProof); + } + let mut computed_hash = leaf.to_vec(); + for chunk in proof.chunks(32) { + let mut buffer = [0u8; 64]; + if index % 2 == 0 { + buffer[..32].copy_from_slice(&computed_hash); + buffer[32..].copy_from_slice(chunk); + } else { + buffer[..32].copy_from_slice(chunk); + buffer[32..].copy_from_slice(&computed_hash); + } + computed_hash = web3::keccak256(&buffer).to_vec(); + index /= 2; + } + + Ok(computed_hash) + } + + /// Verifies the certificate's batch root + pub(crate) fn verify_merkle_proof(&self, cert: &BlobInfo) -> Result<(), VerificationError> { + let inclusion_proof = &cert.blob_verification_proof.inclusion_proof; + let root = &cert + .blob_verification_proof + .batch_medatada + .batch_header + .batch_root; + let blob_index = cert.blob_verification_proof.blob_index; + let blob_header = &cert.blob_header; + + let blob_header_hash = self.hash_encode_blob_header(blob_header); + let leaf_hash = web3::keccak256(&blob_header_hash); + + let generated_root = + self.process_inclusion_proof(inclusion_proof, leaf_hash, blob_index)?; + + if generated_root != *root { + return Err(VerificationError::DifferentRoots { + expected: hex::encode(root), + actual: hex::encode(&generated_root), + }); + } + Ok(()) + } + + fn hash_batch_metadata( + &self, + batch_header: &BatchHeader, + signatory_record_hash: &[u8], + confirmation_block_number: u32, + ) -> Vec { + let batch_header_token = Token::Tuple(vec![ + Token::FixedBytes(batch_header.batch_root.clone()), // Clone only where necessary + Token::Bytes(batch_header.quorum_numbers.clone()), + Token::Bytes(batch_header.quorum_signed_percentages.clone()), + Token::Uint(ethabi::Uint::from(batch_header.reference_block_number)), + ]); + + let encoded = encode(&[batch_header_token]); + let header_hash = web3::keccak256(&encoded).to_vec(); + + let hash_token = Token::Tuple(vec![ + Token::FixedBytes(header_hash.to_vec()), + Token::FixedBytes(signatory_record_hash.to_owned()), // Clone only if required + ]); + + let mut hash_encoded = encode(&[hash_token]); + + hash_encoded.append(&mut confirmation_block_number.to_be_bytes().to_vec()); + web3::keccak256(&hash_encoded).to_vec() + } + + /// Retrieves the block to make the request to the service manager + async fn get_context_block(&self) -> Result { + let latest = self + .signing_client + .as_ref() + .block_number() + .await + .map_err(ServiceManagerError::EnrichedClient)? + .as_u64(); + + let depth = self + .cfg + .settlement_layer_confirmation_depth + .saturating_sub(1); + let block_to_return = latest.saturating_sub(depth as u64); + Ok(block_to_return) + } + + async fn call_batch_id_to_metadata_hash( + &self, + blob_info: &BlobInfo, + ) -> Result, VerificationError> { + let context_block = self.get_context_block().await?; + + let func_selector = + ethabi::short_signature("batchIdToBatchMetadataHash", &[ParamType::Uint(32)]); + let mut data = func_selector.to_vec(); + let mut batch_id_vec = [0u8; 32]; + U256::from(blob_info.blob_verification_proof.batch_id).to_big_endian(&mut batch_id_vec); + data.append(batch_id_vec.to_vec().as_mut()); + + let call_request = CallRequest { + to: Some(self.cfg.svc_manager_addr), + data: Some(zksync_basic_types::web3::Bytes(data)), + ..Default::default() + }; + + let res = self + .signing_client + .as_ref() + .call_contract_function( + call_request, + Some(BlockId::Number(BlockNumber::Number(context_block.into()))), + ) + .await + .map_err(ServiceManagerError::EnrichedClient)?; + + Ok(res.0.to_vec()) + } + + /// Verifies the certificate batch hash + pub(crate) async fn verify_batch(&self, blob_info: &BlobInfo) -> Result<(), VerificationError> { + let expected_hash = self.call_batch_id_to_metadata_hash(blob_info).await?; + + if expected_hash == vec![0u8; 32] { + return Err(VerificationError::EmptyHash); + } + + let actual_hash = self.hash_batch_metadata( + &blob_info + .blob_verification_proof + .batch_medatada + .batch_header, + &blob_info + .blob_verification_proof + .batch_medatada + .signatory_record_hash, + blob_info + .blob_verification_proof + .batch_medatada + .confirmation_block_number, + ); + + if expected_hash != actual_hash { + return Err(VerificationError::DifferentHashes { + expected: hex::encode(&expected_hash), + actual: hex::encode(&actual_hash), + }); + } + Ok(()) + } + + fn decode_bytes(&self, encoded: Vec) -> Result, VerificationError> { + let output_type = [ParamType::Bytes]; + let tokens: Vec = ethabi::decode(&output_type, &encoded) + .map_err(|e| ServiceManagerError::Decoding(e.to_string()))?; + let token = tokens + .first() + .ok_or(ServiceManagerError::Decoding("No tokens found".to_string()))?; + match token { + Token::Bytes(data) => Ok(data.to_vec()), + _ => Err(VerificationError::from(ServiceManagerError::Decoding( + "Token type mismatch".to_string(), + ))), + } + } + + async fn get_quorum_adversary_threshold( + &self, + quorum_number: u32, + ) -> Result { + let func_selector = ethabi::short_signature("quorumAdversaryThresholdPercentages", &[]); + let data = func_selector.to_vec(); + + let call_request = CallRequest { + to: Some(self.cfg.svc_manager_addr), + data: Some(zksync_basic_types::web3::Bytes(data)), + ..Default::default() + }; + + let res = self + .signing_client + .as_ref() + .call_contract_function(call_request, None) + .await + .map_err(ServiceManagerError::EnrichedClient)?; + + let percentages = self.decode_bytes(res.0)?; + + if percentages.len() > quorum_number as usize { + return Ok(percentages[quorum_number as usize]); + } + Ok(0) + } + + async fn call_quorum_numbers_required(&self) -> Result, VerificationError> { + let func_selector = ethabi::short_signature("quorumNumbersRequired", &[]); + let data = func_selector.to_vec(); + let call_request = CallRequest { + to: Some(self.cfg.svc_manager_addr), + data: Some(zksync_basic_types::web3::Bytes(data)), + ..Default::default() + }; + + let res = self + .signing_client + .as_ref() + .call_contract_function(call_request, None) + .await + .map_err(ServiceManagerError::EnrichedClient)?; + + self.decode_bytes(res.0.to_vec()) + } + + /// Verifies that the certificate's blob quorum params are correct + pub async fn verify_security_params(&self, cert: &BlobInfo) -> Result<(), VerificationError> { + let blob_header = &cert.blob_header; + let batch_header = &cert.blob_verification_proof.batch_medatada.batch_header; + + let mut confirmed_quorums: HashMap = HashMap::new(); + for i in 0..blob_header.blob_quorum_params.len() { + if batch_header.quorum_numbers[i] as u32 + != blob_header.blob_quorum_params[i].quorum_number + { + return Err(VerificationError::WrongQuorumParams { + blob_quorum_params: blob_header.blob_quorum_params[i].clone(), + }); + } + if blob_header.blob_quorum_params[i].adversary_threshold_percentage + > blob_header.blob_quorum_params[i].confirmation_threshold_percentage + { + return Err(VerificationError::WrongQuorumParams { + blob_quorum_params: blob_header.blob_quorum_params[i].clone(), + }); + } + let quorum_adversary_threshold = self + .get_quorum_adversary_threshold(blob_header.blob_quorum_params[i].quorum_number) + .await?; + + if quorum_adversary_threshold > 0 + && blob_header.blob_quorum_params[i].adversary_threshold_percentage + < quorum_adversary_threshold as u32 + { + return Err(VerificationError::WrongQuorumParams { + blob_quorum_params: blob_header.blob_quorum_params[i].clone(), + }); + } + + if (batch_header.quorum_signed_percentages[i] as u32) + < blob_header.blob_quorum_params[i].confirmation_threshold_percentage + { + return Err(VerificationError::WrongQuorumParams { + blob_quorum_params: blob_header.blob_quorum_params[i].clone(), + }); + } + + confirmed_quorums.insert(blob_header.blob_quorum_params[i].quorum_number, true); + } + + let required_quorums = self.call_quorum_numbers_required().await?; + + for quorum in required_quorums { + if !confirmed_quorums.contains_key(&(quorum as u32)) { + return Err(VerificationError::QuorumNotConfirmed); + } + } + Ok(()) + } + + /// Verifies that the certificate is valid + pub async fn verify_inclusion_data_against_settlement_layer( + &self, + cert: &BlobInfo, + ) -> Result<(), VerificationError> { + self.verify_batch(cert).await?; + self.verify_merkle_proof(cert)?; + self.verify_security_params(cert).await?; + Ok(()) + } +} diff --git a/core/node/da_clients/src/eigen/verifier.rs b/core/node/da_clients/src/eigen/verifier/tests.rs similarity index 67% rename from core/node/da_clients/src/eigen/verifier.rs rename to core/node/da_clients/src/eigen/verifier/tests.rs index 2a2f88db9c45..262ec3195d01 100644 --- a/core/node/da_clients/src/eigen/verifier.rs +++ b/core/node/da_clients/src/eigen/verifier/tests.rs @@ -1,540 +1,5 @@ -use std::{collections::HashMap, path::Path, sync::Arc}; - -use ark_bn254::{Fq, G1Affine}; -use ethabi::{encode, ParamType, Token}; -use rust_kzg_bn254::{blob::Blob, kzg::Kzg, polynomial::PolynomialFormat}; -use tokio::{fs::File, io::AsyncWriteExt}; -use url::Url; -use zksync_basic_types::web3::CallRequest; -use zksync_eth_client::{clients::PKSigningClient, EnrichedClientError, EnrichedClientResult}; -use zksync_types::{ - web3::{self, BlockId, BlockNumber}, - Address, U256, U64, -}; - -use super::blob_info::{BatchHeader, BlobHeader, BlobInfo, BlobQuorumParam, G1Commitment}; - -#[async_trait::async_trait] -pub trait VerifierClient: Sync + Send + std::fmt::Debug { - /// Returns the current block number. - async fn block_number(&self) -> EnrichedClientResult; - - /// Invokes a function on a contract specified by `contract_address` / `contract_abi` using `eth_call`. - async fn call_contract_function( - &self, - request: web3::CallRequest, - block: Option, - ) -> EnrichedClientResult; -} - -#[async_trait::async_trait] -impl VerifierClient for PKSigningClient { - async fn block_number(&self) -> EnrichedClientResult { - self.as_ref().block_number().await - } - - async fn call_contract_function( - &self, - request: web3::CallRequest, - block: Option, - ) -> EnrichedClientResult { - self.as_ref().call_contract_function(request, block).await - } -} - -#[derive(Debug, thiserror::Error)] -pub enum KzgError { - #[error("Kzg setup error: {0}")] - Setup(String), - #[error(transparent)] - Internal(#[from] rust_kzg_bn254::errors::KzgError), -} - -#[derive(Debug, thiserror::Error)] -pub enum ServiceManagerError { - #[error(transparent)] - EnrichedClient(#[from] EnrichedClientError), - #[error("Decoding error: {0}")] - Decoding(String), - #[cfg(test)] - #[error("Parsing error: {0}")] - Parsing(String), -} - -#[derive(Debug, thiserror::Error)] -pub enum VerificationError { - #[error(transparent)] - ServiceManager(#[from] ServiceManagerError), - #[error(transparent)] - Kzg(#[from] KzgError), - #[error("Wrong proof")] - WrongProof, - #[error("Different commitments: expected {expected:?}, got {actual:?}")] - DifferentCommitments { - expected: G1Affine, - actual: G1Affine, - }, - #[error("Different roots: expected {expected:?}, got {actual:?}")] - DifferentRoots { expected: String, actual: String }, - #[error("Empty hash")] - EmptyHash, - #[error("Different hashes: expected {expected:?}, got {actual:?}")] - DifferentHashes { expected: String, actual: String }, - #[error("Wrong quorum params: {blob_quorum_params:?}")] - WrongQuorumParams { blob_quorum_params: BlobQuorumParam }, - #[error("Quorum not confirmed")] - QuorumNotConfirmed, - #[error("Commitment not on curve: {0}")] - CommitmentNotOnCurve(G1Affine), - #[error("Commitment not on correct subgroup: {0}")] - CommitmentNotOnCorrectSubgroup(G1Affine), - #[error("Link Error: {0}")] - LinkError(String), -} - -/// Configuration for the verifier used for authenticated dispersals -#[derive(Debug, Clone)] -pub struct VerifierConfig { - pub rpc_url: String, - pub svc_manager_addr: Address, - pub max_blob_size: u32, - pub g1_url: Url, - pub g2_url: Url, - pub settlement_layer_confirmation_depth: u32, - pub private_key: String, - pub chain_id: u64, -} - -/// Verifier used to verify the integrity of the blob info -/// Kzg is used for commitment verification -/// EigenDA service manager is used to connect to the service manager contract -#[derive(Debug)] -pub struct Verifier { - kzg: Kzg, - cfg: VerifierConfig, - signing_client: Arc, -} - -impl Verifier { - pub const DEFAULT_PRIORITY_FEE_PER_GAS: u64 = 100; - pub const SRSORDER: u32 = 1 << 28; // 2 ^ 28 - pub const G1POINT: &'static str = "g1.point"; - pub const G2POINT: &'static str = "g2.point.powerOf2"; - pub const POINT_SIZE: u32 = 32; - - async fn save_point(url: Url, point: String) -> Result<(), VerificationError> { - let response = reqwest::get(url) - .await - .map_err(|e| VerificationError::LinkError(e.to_string()))?; - if !response.status().is_success() { - return Err(VerificationError::LinkError( - "Failed to get point".to_string(), - )); - } - let path = format!("./{}", point); - let path = Path::new(&path); - let mut file = File::create(path) - .await - .map_err(|e| VerificationError::LinkError(e.to_string()))?; - let content = response - .bytes() - .await - .map_err(|e| VerificationError::LinkError(e.to_string()))?; - file.write_all(&content) - .await - .map_err(|e| VerificationError::LinkError(e.to_string()))?; - Ok(()) - } - - async fn save_points(url_g1: Url, url_g2: Url) -> Result { - Self::save_point(url_g1, Self::G1POINT.to_string()).await?; - Self::save_point(url_g2, Self::G2POINT.to_string()).await?; - - Ok(".".to_string()) - } - - pub(crate) async fn new( - cfg: VerifierConfig, - signing_client: Arc, - ) -> Result { - let srs_points_to_load = cfg.max_blob_size / Self::POINT_SIZE; - let path = Self::save_points(cfg.clone().g1_url, cfg.clone().g2_url).await?; - let kzg_handle = tokio::task::spawn_blocking(move || { - Kzg::setup( - &format!("{}/{}", path, Self::G1POINT), - "", - &format!("{}/{}", path, Self::G2POINT), - Self::SRSORDER, - srs_points_to_load, - "".to_string(), - ) - }); - - let kzg = kzg_handle - .await - .map_err(|e| VerificationError::Kzg(KzgError::Setup(e.to_string())))? - .map_err(KzgError::Internal)?; - - Ok(Self { - kzg, - cfg, - signing_client, - }) - } - - /// Return the commitment from a blob - fn commit(&self, blob: &[u8]) -> Result { - let blob = Blob::from_bytes_and_pad(blob); - self.kzg - .blob_to_kzg_commitment(&blob, PolynomialFormat::InEvaluationForm) - .map_err(|e| VerificationError::Kzg(KzgError::Internal(e))) - } - - /// Compare the given commitment with the commitment generated with the blob - pub fn verify_commitment( - &self, - expected_commitment: G1Commitment, - blob: &[u8], - ) -> Result<(), VerificationError> { - let actual_commitment = self.commit(blob)?; - let expected_commitment = G1Affine::new_unchecked( - Fq::from(num_bigint::BigUint::from_bytes_be(&expected_commitment.x)), - Fq::from(num_bigint::BigUint::from_bytes_be(&expected_commitment.y)), - ); - if !expected_commitment.is_on_curve() { - return Err(VerificationError::CommitmentNotOnCurve(expected_commitment)); - } - if !expected_commitment.is_in_correct_subgroup_assuming_on_curve() { - return Err(VerificationError::CommitmentNotOnCorrectSubgroup( - expected_commitment, - )); - } - if actual_commitment != expected_commitment { - return Err(VerificationError::DifferentCommitments { - expected: expected_commitment, - actual: actual_commitment, - }); - } - Ok(()) - } - - pub(crate) fn hash_encode_blob_header(&self, blob_header: &BlobHeader) -> Vec { - let mut blob_quorums = vec![]; - for quorum in &blob_header.blob_quorum_params { - let quorum = Token::Tuple(vec![ - Token::Uint(ethabi::Uint::from(quorum.quorum_number)), - Token::Uint(ethabi::Uint::from(quorum.adversary_threshold_percentage)), - Token::Uint(ethabi::Uint::from(quorum.confirmation_threshold_percentage)), - Token::Uint(ethabi::Uint::from(quorum.chunk_length)), - ]); - blob_quorums.push(quorum); - } - let blob_header = Token::Tuple(vec![ - Token::Tuple(vec![ - Token::Uint(ethabi::Uint::from_big_endian(&blob_header.commitment.x)), - Token::Uint(ethabi::Uint::from_big_endian(&blob_header.commitment.y)), - ]), - Token::Uint(ethabi::Uint::from(blob_header.data_length)), - Token::Array(blob_quorums), - ]); - - let encoded = encode(&[blob_header]); - web3::keccak256(&encoded).to_vec() - } - - pub(crate) fn process_inclusion_proof( - &self, - proof: &[u8], - leaf: [u8; 32], - index: u32, - ) -> Result, VerificationError> { - let mut index = index; - if proof.is_empty() || proof.len() % 32 != 0 { - return Err(VerificationError::WrongProof); - } - let mut computed_hash = leaf.to_vec(); - for chunk in proof.chunks(32) { - let mut buffer = [0u8; 64]; - if index % 2 == 0 { - buffer[..32].copy_from_slice(&computed_hash); - buffer[32..].copy_from_slice(chunk); - } else { - buffer[..32].copy_from_slice(chunk); - buffer[32..].copy_from_slice(&computed_hash); - } - computed_hash = web3::keccak256(&buffer).to_vec(); - index /= 2; - } - - Ok(computed_hash) - } - - /// Verifies the certificate's batch root - pub(crate) fn verify_merkle_proof(&self, cert: &BlobInfo) -> Result<(), VerificationError> { - let inclusion_proof = &cert.blob_verification_proof.inclusion_proof; - let root = &cert - .blob_verification_proof - .batch_medatada - .batch_header - .batch_root; - let blob_index = cert.blob_verification_proof.blob_index; - let blob_header = &cert.blob_header; - - let blob_header_hash = self.hash_encode_blob_header(blob_header); - let leaf_hash = web3::keccak256(&blob_header_hash); - - let generated_root = - self.process_inclusion_proof(inclusion_proof, leaf_hash, blob_index)?; - - if generated_root != *root { - return Err(VerificationError::DifferentRoots { - expected: hex::encode(root), - actual: hex::encode(&generated_root), - }); - } - Ok(()) - } - - fn hash_batch_metadata( - &self, - batch_header: &BatchHeader, - signatory_record_hash: &[u8], - confirmation_block_number: u32, - ) -> Vec { - let batch_header_token = Token::Tuple(vec![ - Token::FixedBytes(batch_header.batch_root.clone()), // Clone only where necessary - Token::Bytes(batch_header.quorum_numbers.clone()), - Token::Bytes(batch_header.quorum_signed_percentages.clone()), - Token::Uint(ethabi::Uint::from(batch_header.reference_block_number)), - ]); - - let encoded = encode(&[batch_header_token]); - let header_hash = web3::keccak256(&encoded).to_vec(); - - let hash_token = Token::Tuple(vec![ - Token::FixedBytes(header_hash.to_vec()), - Token::FixedBytes(signatory_record_hash.to_owned()), // Clone only if required - ]); - - let mut hash_encoded = encode(&[hash_token]); - - hash_encoded.append(&mut confirmation_block_number.to_be_bytes().to_vec()); - web3::keccak256(&hash_encoded).to_vec() - } - - /// Retrieves the block to make the request to the service manager - async fn get_context_block(&self) -> Result { - let latest = self - .signing_client - .as_ref() - .block_number() - .await - .map_err(ServiceManagerError::EnrichedClient)? - .as_u64(); - - let depth = self - .cfg - .settlement_layer_confirmation_depth - .saturating_sub(1); - let block_to_return = latest.saturating_sub(depth as u64); - Ok(block_to_return) - } - - async fn call_batch_id_to_metadata_hash( - &self, - blob_info: &BlobInfo, - ) -> Result, VerificationError> { - let context_block = self.get_context_block().await?; - - let func_selector = - ethabi::short_signature("batchIdToBatchMetadataHash", &[ParamType::Uint(32)]); - let mut data = func_selector.to_vec(); - let mut batch_id_vec = [0u8; 32]; - U256::from(blob_info.blob_verification_proof.batch_id).to_big_endian(&mut batch_id_vec); - data.append(batch_id_vec.to_vec().as_mut()); - - let call_request = CallRequest { - to: Some(self.cfg.svc_manager_addr), - data: Some(zksync_basic_types::web3::Bytes(data)), - ..Default::default() - }; - - let res = self - .signing_client - .as_ref() - .call_contract_function( - call_request, - Some(BlockId::Number(BlockNumber::Number(context_block.into()))), - ) - .await - .map_err(ServiceManagerError::EnrichedClient)?; - - Ok(res.0.to_vec()) - } - - /// Verifies the certificate batch hash - pub(crate) async fn verify_batch(&self, blob_info: &BlobInfo) -> Result<(), VerificationError> { - let expected_hash = self.call_batch_id_to_metadata_hash(blob_info).await?; - - if expected_hash == vec![0u8; 32] { - return Err(VerificationError::EmptyHash); - } - - let actual_hash = self.hash_batch_metadata( - &blob_info - .blob_verification_proof - .batch_medatada - .batch_header, - &blob_info - .blob_verification_proof - .batch_medatada - .signatory_record_hash, - blob_info - .blob_verification_proof - .batch_medatada - .confirmation_block_number, - ); - - if expected_hash != actual_hash { - return Err(VerificationError::DifferentHashes { - expected: hex::encode(&expected_hash), - actual: hex::encode(&actual_hash), - }); - } - Ok(()) - } - - fn decode_bytes(&self, encoded: Vec) -> Result, VerificationError> { - let output_type = [ParamType::Bytes]; - let tokens: Vec = ethabi::decode(&output_type, &encoded) - .map_err(|e| ServiceManagerError::Decoding(e.to_string()))?; - let token = tokens - .first() - .ok_or(ServiceManagerError::Decoding("No tokens found".to_string()))?; - match token { - Token::Bytes(data) => Ok(data.to_vec()), - _ => Err(VerificationError::from(ServiceManagerError::Decoding( - "Token type mismatch".to_string(), - ))), - } - } - - async fn get_quorum_adversary_threshold( - &self, - quorum_number: u32, - ) -> Result { - let func_selector = ethabi::short_signature("quorumAdversaryThresholdPercentages", &[]); - let data = func_selector.to_vec(); - - let call_request = CallRequest { - to: Some(self.cfg.svc_manager_addr), - data: Some(zksync_basic_types::web3::Bytes(data)), - ..Default::default() - }; - - let res = self - .signing_client - .as_ref() - .call_contract_function(call_request, None) - .await - .map_err(ServiceManagerError::EnrichedClient)?; - - let percentages = self.decode_bytes(res.0)?; - - if percentages.len() > quorum_number as usize { - return Ok(percentages[quorum_number as usize]); - } - Ok(0) - } - - async fn call_quorum_numbers_required(&self) -> Result, VerificationError> { - let func_selector = ethabi::short_signature("quorumNumbersRequired", &[]); - let data = func_selector.to_vec(); - let call_request = CallRequest { - to: Some(self.cfg.svc_manager_addr), - data: Some(zksync_basic_types::web3::Bytes(data)), - ..Default::default() - }; - - let res = self - .signing_client - .as_ref() - .call_contract_function(call_request, None) - .await - .map_err(ServiceManagerError::EnrichedClient)?; - - self.decode_bytes(res.0.to_vec()) - } - - /// Verifies that the certificate's blob quorum params are correct - pub async fn verify_security_params(&self, cert: &BlobInfo) -> Result<(), VerificationError> { - let blob_header = &cert.blob_header; - let batch_header = &cert.blob_verification_proof.batch_medatada.batch_header; - - let mut confirmed_quorums: HashMap = HashMap::new(); - for i in 0..blob_header.blob_quorum_params.len() { - if batch_header.quorum_numbers[i] as u32 - != blob_header.blob_quorum_params[i].quorum_number - { - return Err(VerificationError::WrongQuorumParams { - blob_quorum_params: blob_header.blob_quorum_params[i].clone(), - }); - } - if blob_header.blob_quorum_params[i].adversary_threshold_percentage - > blob_header.blob_quorum_params[i].confirmation_threshold_percentage - { - return Err(VerificationError::WrongQuorumParams { - blob_quorum_params: blob_header.blob_quorum_params[i].clone(), - }); - } - let quorum_adversary_threshold = self - .get_quorum_adversary_threshold(blob_header.blob_quorum_params[i].quorum_number) - .await?; - - if quorum_adversary_threshold > 0 - && blob_header.blob_quorum_params[i].adversary_threshold_percentage - < quorum_adversary_threshold as u32 - { - return Err(VerificationError::WrongQuorumParams { - blob_quorum_params: blob_header.blob_quorum_params[i].clone(), - }); - } - - if (batch_header.quorum_signed_percentages[i] as u32) - < blob_header.blob_quorum_params[i].confirmation_threshold_percentage - { - return Err(VerificationError::WrongQuorumParams { - blob_quorum_params: blob_header.blob_quorum_params[i].clone(), - }); - } - - confirmed_quorums.insert(blob_header.blob_quorum_params[i].quorum_number, true); - } - - let required_quorums = self.call_quorum_numbers_required().await?; - - for quorum in required_quorums { - if !confirmed_quorums.contains_key(&(quorum as u32)) { - return Err(VerificationError::QuorumNotConfirmed); - } - } - Ok(()) - } - - /// Verifies that the certificate is valid - pub async fn verify_inclusion_data_against_settlement_layer( - &self, - cert: &BlobInfo, - ) -> Result<(), VerificationError> { - self.verify_batch(cert).await?; - self.verify_merkle_proof(cert)?; - self.verify_security_params(cert).await?; - Ok(()) - } -} - #[cfg(test)] -mod test { +mod tests { use std::{collections::HashMap, str::FromStr, sync::Arc}; use url::Url; @@ -546,13 +11,12 @@ mod test { }; use zksync_web3_decl::client::{Client, DynClient, L1}; - use super::ServiceManagerError; use crate::eigen::{ blob_info::{ BatchHeader, BatchMetadata, BlobHeader, BlobInfo, BlobQuorumParam, BlobVerificationProof, G1Commitment, }, - verifier::{Verifier, VerifierClient, VerifierConfig}, + verifier::{ServiceManagerError, Verifier, VerifierClient, VerifierConfig}, }; fn get_verifier_config() -> VerifierConfig { @@ -1138,7 +602,7 @@ mod test { assert!(result.is_ok()); } - // #[ignore = "depends on external RPC"] + #[ignore = "depends on external RPC"] #[tokio::test] async fn test_verify_security_params() { let cfg = get_verifier_config(); @@ -1217,13 +681,14 @@ mod test { }, }; let result = verifier.verify_security_params(&cert).await; + println!("result {:?}", result); assert!(result.is_ok()); } /// Test security params verification with a mocked verifier. /// To test actual behaviour of the verifier, run the test above #[tokio::test] - async fn test_verify_security_params_mocked() { + async fn test_verify_securityyy_params_mocked() { let mut mock_replies = HashMap::new(); // First request From 4c381a84346f8ab88d3f01dc2848c7fb5f2b788d Mon Sep 17 00:00:00 2001 From: Stanislav Bezkorovainyi Date: Fri, 17 Jan 2025 18:31:53 +0100 Subject: [PATCH 48/97] fix: fix execute encoding for transactions (#3501) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ ## Why ❔ ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- core/genesis_export.bin | Bin 0 -> 895228 bytes .../src/i_executor/methods/execute_batches.rs | 15 ++++++++++----- core/node/eth_sender/src/eth_tx_aggregator.rs | 16 ++++++++++++---- core/node/eth_sender/src/tester.rs | 3 ++- core/node/eth_sender/src/tests.rs | 1 + 5 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 core/genesis_export.bin diff --git a/core/genesis_export.bin b/core/genesis_export.bin new file mode 100644 index 0000000000000000000000000000000000000000..a65382a51ebb02bfc37ef1c0c31258bd2815e8e9 GIT binary patch literal 895228 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 F{|{007Ht3k literal 0 HcmV?d00001 diff --git a/core/lib/l1_contract_interface/src/i_executor/methods/execute_batches.rs b/core/lib/l1_contract_interface/src/i_executor/methods/execute_batches.rs index 649a7ca2b419..5d5494ca62a8 100644 --- a/core/lib/l1_contract_interface/src/i_executor/methods/execute_batches.rs +++ b/core/lib/l1_contract_interface/src/i_executor/methods/execute_batches.rs @@ -1,11 +1,12 @@ use zksync_types::{ commitment::{L1BatchWithMetadata, PriorityOpsMerkleProof}, ethabi::{encode, Token}, + ProtocolVersionId, }; use crate::{ i_executor::structures::{StoredBatchInfo, SUPPORTED_ENCODING_VERSION}, - Tokenizable, Tokenize, + Tokenizable, }; /// Input required to encode `executeBatches` call. @@ -15,11 +16,15 @@ pub struct ExecuteBatches { pub priority_ops_proofs: Vec, } -impl Tokenize for &ExecuteBatches { - fn into_tokens(self) -> Vec { - let protocol_version = self.l1_batches[0].header.protocol_version.unwrap(); +impl ExecuteBatches { + // The encodings of `ExecuteBatches` operations are different depending on the protocol version + // of the underlying chain. + // However, we can send batches with older protocol versions just by changing the encoding. + // This makes the migration simpler. + pub fn encode_for_eth_tx(&self, chain_protocol_version: ProtocolVersionId) -> Vec { + let internal_protocol_version = self.l1_batches[0].header.protocol_version.unwrap(); - if protocol_version.is_pre_gateway() { + if internal_protocol_version.is_pre_gateway() && chain_protocol_version.is_pre_gateway() { vec![Token::Array( self.l1_batches .iter() diff --git a/core/node/eth_sender/src/eth_tx_aggregator.rs b/core/node/eth_sender/src/eth_tx_aggregator.rs index 8a829ed00faa..bc9d3266ec78 100644 --- a/core/node/eth_sender/src/eth_tx_aggregator.rs +++ b/core/node/eth_sender/src/eth_tx_aggregator.rs @@ -594,6 +594,7 @@ impl EthTxAggregator { stm_protocol_version_id, stm_validator_timelock_address, ), + chain_protocol_version_id, is_gateway, ) .await?; @@ -642,7 +643,11 @@ impl EthTxAggregator { .await; } - fn encode_aggregated_op(&self, op: &AggregatedOperation) -> TxData { + fn encode_aggregated_op( + &self, + op: &AggregatedOperation, + chain_protocol_version_id: ProtocolVersionId, + ) -> TxData { let mut args = vec![Token::Uint(self.rollup_chain_id.as_u64().into())]; let is_op_pre_gateway = op.protocol_version().is_pre_gateway(); @@ -686,8 +691,9 @@ impl EthTxAggregator { (calldata, None) } AggregatedOperation::Execute(op) => { - args.extend(op.into_tokens()); - let encoding_fn = if is_op_pre_gateway { + args.extend(op.encode_for_eth_tx(chain_protocol_version_id)); + let encoding_fn = if is_op_pre_gateway && chain_protocol_version_id.is_pre_gateway() + { &self.functions.post_shared_bridge_execute } else { &self.functions.post_gateway_execute @@ -743,6 +749,7 @@ impl EthTxAggregator { storage: &mut Connection<'_, Core>, aggregated_op: &AggregatedOperation, timelock_contract_address: Address, + chain_protocol_version_id: ProtocolVersionId, is_gateway: bool, ) -> Result { let mut transaction = storage.start_transaction().await.unwrap(); @@ -755,7 +762,8 @@ impl EthTxAggregator { (_, _) => None, }; let nonce = self.get_next_nonce(&mut transaction, sender_addr).await?; - let encoded_aggregated_op = self.encode_aggregated_op(aggregated_op); + let encoded_aggregated_op = + self.encode_aggregated_op(aggregated_op, chain_protocol_version_id); let l1_batch_number_range = aggregated_op.l1_batch_range(); let eth_tx_predicted_gas = match (op_type, is_gateway, self.aggregator.mode()) { diff --git a/core/node/eth_sender/src/tester.rs b/core/node/eth_sender/src/tester.rs index 943e808cfa6b..022e2bc87222 100644 --- a/core/node/eth_sender/src/tester.rs +++ b/core/node/eth_sender/src/tester.rs @@ -13,7 +13,7 @@ use zksync_object_store::MockObjectStore; use zksync_types::{ aggregated_operations::AggregatedActionType, block::L1BatchHeader, commitment::L1BatchCommitmentMode, eth_sender::EthTx, pubdata_da::PubdataSendingMode, - settlement::SettlementMode, Address, L1BatchNumber, ProtocolVersion, H256, + settlement::SettlementMode, Address, L1BatchNumber, ProtocolVersion, ProtocolVersionId, H256, }; use crate::{ @@ -525,6 +525,7 @@ impl EthSenderTester { &mut self.conn.connection().await.unwrap(), &aggregated_operation, Address::random(), + ProtocolVersionId::latest(), self.is_l2, ) .await diff --git a/core/node/eth_sender/src/tests.rs b/core/node/eth_sender/src/tests.rs index f104d222982a..8841f297cadc 100644 --- a/core/node/eth_sender/src/tests.rs +++ b/core/node/eth_sender/src/tests.rs @@ -236,6 +236,7 @@ async fn resend_each_block(commitment_mode: L1BatchCommitmentMode) -> anyhow::Re &mut tester.conn.connection().await.unwrap(), &get_dummy_operation(0), Address::random(), + ProtocolVersionId::latest(), false, ) .await?; From fe3c7b2583bc4f9277e186334e5822ddf95bdcd0 Mon Sep 17 00:00:00 2001 From: perekopskiy <53865202+perekopskiy@users.noreply.github.com> Date: Fri, 17 Jan 2025 19:32:12 +0200 Subject: [PATCH 49/97] feat: update l2 erc20 bridge address in updater as well (#3500) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ update l2 erc20 bridge address as well as l2 shared bridge ## Why ❔ ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. Co-authored-by: Jobe Smith <163019853+jobesmith-man@users.noreply.github.com> --- core/node/api_server/src/web3/state.rs | 3 ++- .../implementations/layers/web3_api/server/bridge_addresses.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/node/api_server/src/web3/state.rs b/core/node/api_server/src/web3/state.rs index 97ffd933c801..1319c866968c 100644 --- a/core/node/api_server/src/web3/state.rs +++ b/core/node/api_server/src/web3/state.rs @@ -241,8 +241,9 @@ impl BridgeAddressesHandle { self.0.write().await.l1_shared_default_bridge = Some(l1_shared_bridge); } - pub async fn update_l2_shared_bridge(&self, l2_shared_bridge: Address) { + pub async fn update_l2_bridges(&self, l2_shared_bridge: Address) { self.0.write().await.l2_shared_default_bridge = Some(l2_shared_bridge); + self.0.write().await.l2_erc20_default_bridge = Some(l2_shared_bridge); } pub async fn read(&self) -> api::BridgeAddresses { diff --git a/core/node/node_framework/src/implementations/layers/web3_api/server/bridge_addresses.rs b/core/node/node_framework/src/implementations/layers/web3_api/server/bridge_addresses.rs index a515e4cc1db9..b85d74699857 100644 --- a/core/node/node_framework/src/implementations/layers/web3_api/server/bridge_addresses.rs +++ b/core/node/node_framework/src/implementations/layers/web3_api/server/bridge_addresses.rs @@ -80,7 +80,7 @@ impl L1UpdaterInner { // - To not undo the previous change in case of a network error if info.should_use_l2_asset_router { self.bridge_address_updater - .update_l2_shared_bridge(L2_ASSET_ROUTER_ADDRESS) + .update_l2_bridges(L2_ASSET_ROUTER_ADDRESS) .await; } } From 2bded08b2e7e23eded774d744df558a6460e7b38 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 14:42:50 -0300 Subject: [PATCH 50/97] replace u64 constant with Duration --- core/node/da_clients/src/eigen/client.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/node/da_clients/src/eigen/client.rs b/core/node/da_clients/src/eigen/client.rs index d90f13c8d843..c72f910fb5e6 100644 --- a/core/node/da_clients/src/eigen/client.rs +++ b/core/node/da_clients/src/eigen/client.rs @@ -108,8 +108,8 @@ mod tests { } } - const STATUS_QUERY_TIMEOUT: u64 = 1800000; // 30 minutes - const STATUS_QUERY_INTERVAL: u64 = 5; // 5 ms + const STATUS_QUERY_INTERVAL: Duration = Duration::from_millis(5); + const MAX_RETRY_ATTEMPTS: usize = 1800000; // With this value we retry for a duration of 30 minutes async fn get_blob_info( client: &EigenClient, @@ -124,8 +124,8 @@ mod tests { }) .retry( &ConstantBuilder::default() - .with_delay(Duration::from_millis(STATUS_QUERY_INTERVAL)) - .with_max_times((STATUS_QUERY_TIMEOUT / STATUS_QUERY_INTERVAL) as usize), + .with_delay(STATUS_QUERY_INTERVAL) + .with_max_times(MAX_RETRY_ATTEMPTS), ) .when(|e| e.to_string().contains("Blob not found")) .await?; From 12d29c72f9b3b30bbc9d4760a42c9f897bb4f531 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 15:22:09 -0300 Subject: [PATCH 51/97] use `ethabi::encode()` --- core/node/da_clients/src/eigen/verifier/mod.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/node/da_clients/src/eigen/verifier/mod.rs b/core/node/da_clients/src/eigen/verifier/mod.rs index fdd42da38f64..f8eabe551bb4 100644 --- a/core/node/da_clients/src/eigen/verifier/mod.rs +++ b/core/node/da_clients/src/eigen/verifier/mod.rs @@ -348,12 +348,14 @@ impl Verifier { ) -> Result, VerificationError> { let context_block = self.get_context_block().await?; + let mut data = vec![]; let func_selector = ethabi::short_signature("batchIdToBatchMetadataHash", &[ParamType::Uint(32)]); - let mut data = func_selector.to_vec(); - let mut batch_id_vec = [0u8; 32]; - U256::from(blob_info.blob_verification_proof.batch_id).to_big_endian(&mut batch_id_vec); - data.append(batch_id_vec.to_vec().as_mut()); + data.extend_from_slice(&func_selector); + let batch_id_data = encode(&[Token::Uint(U256::from( + blob_info.blob_verification_proof.batch_id, + ))]); + data.extend_from_slice(&batch_id_data); let call_request = CallRequest { to: Some(self.cfg.svc_manager_addr), From 351d5a8ee7eee8fdb592fa314d427db25ac5be1b Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 15:39:25 -0300 Subject: [PATCH 52/97] refactor `decode_bytes` --- .../node/da_clients/src/eigen/verifier/mod.rs | 18 +- .../da_clients/src/eigen/verifier/tests.rs | 1404 ++++++++--------- 2 files changed, 700 insertions(+), 722 deletions(-) diff --git a/core/node/da_clients/src/eigen/verifier/mod.rs b/core/node/da_clients/src/eigen/verifier/mod.rs index f8eabe551bb4..555a27cfb24a 100644 --- a/core/node/da_clients/src/eigen/verifier/mod.rs +++ b/core/node/da_clients/src/eigen/verifier/mod.rs @@ -14,6 +14,7 @@ use zksync_types::{ use super::blob_info::{BatchHeader, BlobHeader, BlobInfo, BlobQuorumParam, G1Commitment}; +#[cfg(test)] mod tests; #[async_trait::async_trait] @@ -410,17 +411,14 @@ impl Verifier { fn decode_bytes(&self, encoded: Vec) -> Result, VerificationError> { let output_type = [ParamType::Bytes]; - let tokens: Vec = ethabi::decode(&output_type, &encoded) + let tokens = ethabi::decode(&output_type, &encoded) .map_err(|e| ServiceManagerError::Decoding(e.to_string()))?; - let token = tokens - .first() - .ok_or(ServiceManagerError::Decoding("No tokens found".to_string()))?; - match token { - Token::Bytes(data) => Ok(data.to_vec()), - _ => Err(VerificationError::from(ServiceManagerError::Decoding( - "Token type mismatch".to_string(), - ))), - } + + // Safe unwrap because decode guarantees type correctness and non-empty output + let token = tokens.into_iter().next().unwrap(); + + // Safe unwrap, as type is guaranteed + Ok(token.into_bytes().unwrap()) } async fn get_quorum_adversary_threshold( diff --git a/core/node/da_clients/src/eigen/verifier/tests.rs b/core/node/da_clients/src/eigen/verifier/tests.rs index 262ec3195d01..505756910143 100644 --- a/core/node/da_clients/src/eigen/verifier/tests.rs +++ b/core/node/da_clients/src/eigen/verifier/tests.rs @@ -1,26 +1,24 @@ -#[cfg(test)] -mod tests { - use std::{collections::HashMap, str::FromStr, sync::Arc}; +use std::{collections::HashMap, str::FromStr, sync::Arc}; - use url::Url; - use zksync_eth_client::{clients::PKSigningClient, EnrichedClientResult}; - use zksync_types::{ - url::SensitiveUrl, - web3::{BlockId, Bytes, CallRequest}, - Address, K256PrivateKey, SLChainId, H160, U64, - }; - use zksync_web3_decl::client::{Client, DynClient, L1}; +use url::Url; +use zksync_eth_client::{clients::PKSigningClient, EnrichedClientResult}; +use zksync_types::{ + url::SensitiveUrl, + web3::{BlockId, Bytes, CallRequest}, + Address, K256PrivateKey, SLChainId, H160, U64, +}; +use zksync_web3_decl::client::{Client, DynClient, L1}; - use crate::eigen::{ - blob_info::{ - BatchHeader, BatchMetadata, BlobHeader, BlobInfo, BlobQuorumParam, - BlobVerificationProof, G1Commitment, - }, - verifier::{ServiceManagerError, Verifier, VerifierClient, VerifierConfig}, - }; +use crate::eigen::{ + blob_info::{ + BatchHeader, BatchMetadata, BlobHeader, BlobInfo, BlobQuorumParam, BlobVerificationProof, + G1Commitment, + }, + verifier::{ServiceManagerError, Verifier, VerifierClient, VerifierConfig}, +}; - fn get_verifier_config() -> VerifierConfig { - VerifierConfig { +fn get_verifier_config() -> VerifierConfig { + VerifierConfig { rpc_url: "https://ethereum-holesky-rpc.publicnode.com".to_string(), svc_manager_addr: Address::from_str("0xD4A7E1Bd8015057293f0D0A557088c286942e84b").unwrap(), max_blob_size: 2 * 1024 * 1024, @@ -31,782 +29,764 @@ mod tests { .to_string(), chain_id: 17000, } - } +} - /// Mock struct for the Verifier - /// Used to avoid making actual calls to a remote disperser - /// and possible making the CI fail due to network issues. - /// To run tests with the actual verifier run: - /// `cargo test -p zksync_da_clients -- --ignored` - #[derive(Debug)] - pub struct MockVerifierClient { - replies: HashMap, - } +/// Mock struct for the Verifier +/// Used to avoid making actual calls to a remote disperser +/// and possible making the CI fail due to network issues. +/// To run tests with the actual verifier run: +/// `cargo test -p zksync_da_clients -- --ignored` +#[derive(Debug)] +pub struct MockVerifierClient { + replies: HashMap, +} - impl MockVerifierClient { - pub fn new(replies: HashMap) -> Self { - Self { replies } - } +impl MockVerifierClient { + pub fn new(replies: HashMap) -> Self { + Self { replies } } +} - #[async_trait::async_trait] - impl VerifierClient for MockVerifierClient { - async fn block_number(&self) -> EnrichedClientResult { - Ok(U64::from(42)) - } +#[async_trait::async_trait] +impl VerifierClient for MockVerifierClient { + async fn block_number(&self) -> EnrichedClientResult { + Ok(U64::from(42)) + } - async fn call_contract_function( - &self, - request: CallRequest, - _block: Option, - ) -> EnrichedClientResult { - let req = serde_json::to_string(&request).unwrap(); - Ok(self.replies.get(&req).unwrap().clone()) - } + async fn call_contract_function( + &self, + request: CallRequest, + _block: Option, + ) -> EnrichedClientResult { + let req = serde_json::to_string(&request).unwrap(); + Ok(self.replies.get(&req).unwrap().clone()) } +} - fn create_remote_signing_client(cfg: VerifierConfig) -> PKSigningClient { - let url = SensitiveUrl::from_str(&cfg.rpc_url).unwrap(); - let query_client: Client = Client::http(url).unwrap().build(); - let query_client = Box::new(query_client) as Box>; - PKSigningClient::new_raw( - K256PrivateKey::from_bytes( - zksync_types::H256::from_str(&cfg.private_key) - .map_err(|e| ServiceManagerError::Parsing(e.to_string())) - .unwrap(), - ) - .map_err(|e| ServiceManagerError::Parsing(e.to_string())) - .unwrap(), - cfg.svc_manager_addr, - Verifier::DEFAULT_PRIORITY_FEE_PER_GAS, - SLChainId(cfg.chain_id), - query_client, +fn create_remote_signing_client(cfg: VerifierConfig) -> PKSigningClient { + let url = SensitiveUrl::from_str(&cfg.rpc_url).unwrap(); + let query_client: Client = Client::http(url).unwrap().build(); + let query_client = Box::new(query_client) as Box>; + PKSigningClient::new_raw( + K256PrivateKey::from_bytes( + zksync_types::H256::from_str(&cfg.private_key) + .map_err(|e| ServiceManagerError::Parsing(e.to_string())) + .unwrap(), ) - } + .map_err(|e| ServiceManagerError::Parsing(e.to_string())) + .unwrap(), + cfg.svc_manager_addr, + Verifier::DEFAULT_PRIORITY_FEE_PER_GAS, + SLChainId(cfg.chain_id), + query_client, + ) +} - #[ignore = "depends on external RPC"] - #[tokio::test] - async fn test_verify_commitment() { - let cfg = get_verifier_config(); - let signing_client = create_remote_signing_client(cfg.clone()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); - let commitment = G1Commitment { - x: vec![ - 22, 11, 176, 29, 82, 48, 62, 49, 51, 119, 94, 17, 156, 142, 248, 96, 240, 183, 134, - 85, 152, 5, 74, 27, 175, 83, 162, 148, 17, 110, 201, 74, - ], - y: vec![ - 12, 132, 236, 56, 147, 6, 176, 135, 244, 166, 21, 18, 87, 76, 122, 3, 23, 22, 254, - 236, 148, 129, 110, 207, 131, 116, 58, 170, 4, 130, 191, 157, - ], - }; - let blob = vec![1u8; 100]; // Actual blob sent was this blob but kzg-padded, but Blob::from_bytes_and_pad padds it inside, so we don't need to pad it here. - let result = verifier.verify_commitment(commitment, &blob); - assert!(result.is_ok()); - } +#[ignore = "depends on external RPC"] +#[tokio::test] +async fn test_verify_commitment() { + let cfg = get_verifier_config(); + let signing_client = create_remote_signing_client(cfg.clone()); + let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let commitment = G1Commitment { + x: vec![ + 22, 11, 176, 29, 82, 48, 62, 49, 51, 119, 94, 17, 156, 142, 248, 96, 240, 183, 134, 85, + 152, 5, 74, 27, 175, 83, 162, 148, 17, 110, 201, 74, + ], + y: vec![ + 12, 132, 236, 56, 147, 6, 176, 135, 244, 166, 21, 18, 87, 76, 122, 3, 23, 22, 254, 236, + 148, 129, 110, 207, 131, 116, 58, 170, 4, 130, 191, 157, + ], + }; + let blob = vec![1u8; 100]; // Actual blob sent was this blob but kzg-padded, but Blob::from_bytes_and_pad padds it inside, so we don't need to pad it here. + let result = verifier.verify_commitment(commitment, &blob); + assert!(result.is_ok()); +} - /// Test the verification of the commitment with a mocked verifier. - /// To test actual behaviour of the verifier, run the test above - #[tokio::test] - async fn test_verify_commitment_mocked() { - let cfg = get_verifier_config(); - let signing_client = MockVerifierClient::new(HashMap::new()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); - let commitment = G1Commitment { - x: vec![ - 22, 11, 176, 29, 82, 48, 62, 49, 51, 119, 94, 17, 156, 142, 248, 96, 240, 183, 134, - 85, 152, 5, 74, 27, 175, 83, 162, 148, 17, 110, 201, 74, - ], - y: vec![ - 12, 132, 236, 56, 147, 6, 176, 135, 244, 166, 21, 18, 87, 76, 122, 3, 23, 22, 254, - 236, 148, 129, 110, 207, 131, 116, 58, 170, 4, 130, 191, 157, - ], - }; - let blob = vec![1u8; 100]; // Actual blob sent was this blob but kzg-padded, but Blob::from_bytes_and_pad padds it inside, so we don't need to pad it here. - let result = verifier.verify_commitment(commitment, &blob); - assert!(result.is_ok()); - } +/// Test the verification of the commitment with a mocked verifier. +/// To test actual behaviour of the verifier, run the test above +#[tokio::test] +async fn test_verify_commitment_mocked() { + let cfg = get_verifier_config(); + let signing_client = MockVerifierClient::new(HashMap::new()); + let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let commitment = G1Commitment { + x: vec![ + 22, 11, 176, 29, 82, 48, 62, 49, 51, 119, 94, 17, 156, 142, 248, 96, 240, 183, 134, 85, + 152, 5, 74, 27, 175, 83, 162, 148, 17, 110, 201, 74, + ], + y: vec![ + 12, 132, 236, 56, 147, 6, 176, 135, 244, 166, 21, 18, 87, 76, 122, 3, 23, 22, 254, 236, + 148, 129, 110, 207, 131, 116, 58, 170, 4, 130, 191, 157, + ], + }; + let blob = vec![1u8; 100]; // Actual blob sent was this blob but kzg-padded, but Blob::from_bytes_and_pad padds it inside, so we don't need to pad it here. + let result = verifier.verify_commitment(commitment, &blob); + assert!(result.is_ok()); +} - #[ignore = "depends on external RPC"] - #[tokio::test] - async fn test_verify_merkle_proof() { - let cfg = get_verifier_config(); - let signing_client = create_remote_signing_client(cfg.clone()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); - let cert = BlobInfo { - blob_header: BlobHeader { - commitment: G1Commitment { - x: vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - ], - y: vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - ], - }, - data_length: 4, - blob_quorum_params: vec![ - BlobQuorumParam { - quorum_number: 0, - adversary_threshold_percentage: 33, - confirmation_threshold_percentage: 55, - chunk_length: 1, - }, - BlobQuorumParam { - quorum_number: 1, - adversary_threshold_percentage: 33, - confirmation_threshold_percentage: 55, - chunk_length: 1, - }, +#[ignore = "depends on external RPC"] +#[tokio::test] +async fn test_verify_merkle_proof() { + let cfg = get_verifier_config(); + let signing_client = create_remote_signing_client(cfg.clone()); + let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let cert = BlobInfo { + blob_header: BlobHeader { + commitment: G1Commitment { + x: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, ], - }, - blob_verification_proof: BlobVerificationProof { - batch_id: 66507, - blob_index: 92, - batch_medatada: BatchMetadata { - batch_header: BatchHeader { - batch_root: vec![ - 179, 187, 53, 98, 192, 80, 151, 28, 125, 192, 115, 29, 129, 238, 216, - 8, 213, 210, 203, 143, 181, 19, 146, 113, 98, 131, 39, 238, 149, 248, - 211, 43, - ], - quorum_numbers: vec![0, 1], - quorum_signed_percentages: vec![100, 100], - reference_block_number: 2624794, - }, - signatory_record_hash: vec![ - 172, 32, 172, 142, 197, 52, 84, 143, 120, 26, 190, 9, 143, 217, 62, 19, 17, - 107, 105, 67, 203, 5, 172, 249, 6, 60, 105, 240, 134, 34, 66, 133, - ], - fee: vec![0], - confirmation_block_number: 2624876, - batch_header_hash: vec![ - 122, 115, 2, 85, 233, 75, 121, 85, 51, 81, 248, 170, 198, 252, 42, 16, 1, - 146, 96, 218, 159, 44, 41, 40, 94, 247, 147, 11, 255, 68, 40, 177, - ], - }, - inclusion_proof: vec![ - 203, 160, 237, 48, 117, 255, 75, 254, 117, 144, 164, 77, 29, 146, 36, 48, 190, - 140, 50, 100, 144, 237, 125, 125, 75, 54, 210, 247, 147, 23, 48, 189, 120, 4, - 125, 123, 195, 244, 207, 239, 145, 109, 0, 21, 11, 162, 109, 79, 192, 100, 138, - 157, 203, 22, 17, 114, 234, 72, 174, 231, 209, 133, 99, 118, 201, 160, 137, - 128, 112, 84, 34, 136, 174, 139, 96, 26, 246, 148, 134, 52, 200, 229, 160, 145, - 5, 120, 18, 187, 51, 11, 109, 91, 237, 171, 215, 207, 90, 95, 146, 54, 135, - 166, 66, 157, 255, 237, 69, 183, 141, 45, 162, 145, 71, 16, 87, 184, 120, 84, - 156, 220, 159, 4, 99, 48, 191, 203, 136, 112, 127, 226, 192, 184, 110, 6, 177, - 182, 109, 207, 197, 239, 161, 132, 17, 89, 56, 137, 205, 202, 101, 97, 60, 162, - 253, 23, 169, 75, 236, 211, 126, 121, 132, 191, 68, 167, 200, 16, 154, 149, - 202, 197, 7, 191, 26, 8, 67, 3, 37, 137, 16, 153, 30, 209, 238, 53, 233, 148, - 198, 253, 94, 216, 73, 25, 190, 205, 132, 208, 255, 219, 170, 98, 17, 160, 179, - 183, 200, 17, 99, 36, 130, 216, 223, 72, 222, 250, 73, 78, 79, 72, 253, 105, - 245, 84, 244, 196, + y: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, ], - quorum_indexes: vec![0, 1], }, - }; - let result = verifier.verify_merkle_proof(&cert); - assert!(result.is_ok()); - } - - /// Test the verificarion of a merkle proof with a mocked verifier. - /// To test actual behaviour of the verifier, run the test above - #[tokio::test] - async fn test_verify_merkle_proof_mocked() { - let cfg = get_verifier_config(); - let signing_client = MockVerifierClient::new(HashMap::new()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); - let cert = BlobInfo { - blob_header: BlobHeader { - commitment: G1Commitment { - x: vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - ], - y: vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - ], + data_length: 4, + blob_quorum_params: vec![ + BlobQuorumParam { + quorum_number: 0, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, }, - data_length: 4, - blob_quorum_params: vec![ - BlobQuorumParam { - quorum_number: 0, - adversary_threshold_percentage: 33, - confirmation_threshold_percentage: 55, - chunk_length: 1, - }, - BlobQuorumParam { - quorum_number: 1, - adversary_threshold_percentage: 33, - confirmation_threshold_percentage: 55, - chunk_length: 1, - }, - ], - }, - blob_verification_proof: BlobVerificationProof { - batch_id: 66507, - blob_index: 92, - batch_medatada: BatchMetadata { - batch_header: BatchHeader { - batch_root: vec![ - 179, 187, 53, 98, 192, 80, 151, 28, 125, 192, 115, 29, 129, 238, 216, - 8, 213, 210, 203, 143, 181, 19, 146, 113, 98, 131, 39, 238, 149, 248, - 211, 43, - ], - quorum_numbers: vec![0, 1], - quorum_signed_percentages: vec![100, 100], - reference_block_number: 2624794, - }, - signatory_record_hash: vec![ - 172, 32, 172, 142, 197, 52, 84, 143, 120, 26, 190, 9, 143, 217, 62, 19, 17, - 107, 105, 67, 203, 5, 172, 249, 6, 60, 105, 240, 134, 34, 66, 133, - ], - fee: vec![0], - confirmation_block_number: 2624876, - batch_header_hash: vec![ - 122, 115, 2, 85, 233, 75, 121, 85, 51, 81, 248, 170, 198, 252, 42, 16, 1, - 146, 96, 218, 159, 44, 41, 40, 94, 247, 147, 11, 255, 68, 40, 177, + BlobQuorumParam { + quorum_number: 1, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, + }, + ], + }, + blob_verification_proof: BlobVerificationProof { + batch_id: 66507, + blob_index: 92, + batch_medatada: BatchMetadata { + batch_header: BatchHeader { + batch_root: vec![ + 179, 187, 53, 98, 192, 80, 151, 28, 125, 192, 115, 29, 129, 238, 216, 8, + 213, 210, 203, 143, 181, 19, 146, 113, 98, 131, 39, 238, 149, 248, 211, 43, ], + quorum_numbers: vec![0, 1], + quorum_signed_percentages: vec![100, 100], + reference_block_number: 2624794, }, - inclusion_proof: vec![ - 203, 160, 237, 48, 117, 255, 75, 254, 117, 144, 164, 77, 29, 146, 36, 48, 190, - 140, 50, 100, 144, 237, 125, 125, 75, 54, 210, 247, 147, 23, 48, 189, 120, 4, - 125, 123, 195, 244, 207, 239, 145, 109, 0, 21, 11, 162, 109, 79, 192, 100, 138, - 157, 203, 22, 17, 114, 234, 72, 174, 231, 209, 133, 99, 118, 201, 160, 137, - 128, 112, 84, 34, 136, 174, 139, 96, 26, 246, 148, 134, 52, 200, 229, 160, 145, - 5, 120, 18, 187, 51, 11, 109, 91, 237, 171, 215, 207, 90, 95, 146, 54, 135, - 166, 66, 157, 255, 237, 69, 183, 141, 45, 162, 145, 71, 16, 87, 184, 120, 84, - 156, 220, 159, 4, 99, 48, 191, 203, 136, 112, 127, 226, 192, 184, 110, 6, 177, - 182, 109, 207, 197, 239, 161, 132, 17, 89, 56, 137, 205, 202, 101, 97, 60, 162, - 253, 23, 169, 75, 236, 211, 126, 121, 132, 191, 68, 167, 200, 16, 154, 149, - 202, 197, 7, 191, 26, 8, 67, 3, 37, 137, 16, 153, 30, 209, 238, 53, 233, 148, - 198, 253, 94, 216, 73, 25, 190, 205, 132, 208, 255, 219, 170, 98, 17, 160, 179, - 183, 200, 17, 99, 36, 130, 216, 223, 72, 222, 250, 73, 78, 79, 72, 253, 105, - 245, 84, 244, 196, + signatory_record_hash: vec![ + 172, 32, 172, 142, 197, 52, 84, 143, 120, 26, 190, 9, 143, 217, 62, 19, 17, + 107, 105, 67, 203, 5, 172, 249, 6, 60, 105, 240, 134, 34, 66, 133, + ], + fee: vec![0], + confirmation_block_number: 2624876, + batch_header_hash: vec![ + 122, 115, 2, 85, 233, 75, 121, 85, 51, 81, 248, 170, 198, 252, 42, 16, 1, 146, + 96, 218, 159, 44, 41, 40, 94, 247, 147, 11, 255, 68, 40, 177, ], - quorum_indexes: vec![0, 1], }, - }; - let result = verifier.verify_merkle_proof(&cert); - assert!(result.is_ok()); - } + inclusion_proof: vec![ + 203, 160, 237, 48, 117, 255, 75, 254, 117, 144, 164, 77, 29, 146, 36, 48, 190, 140, + 50, 100, 144, 237, 125, 125, 75, 54, 210, 247, 147, 23, 48, 189, 120, 4, 125, 123, + 195, 244, 207, 239, 145, 109, 0, 21, 11, 162, 109, 79, 192, 100, 138, 157, 203, 22, + 17, 114, 234, 72, 174, 231, 209, 133, 99, 118, 201, 160, 137, 128, 112, 84, 34, + 136, 174, 139, 96, 26, 246, 148, 134, 52, 200, 229, 160, 145, 5, 120, 18, 187, 51, + 11, 109, 91, 237, 171, 215, 207, 90, 95, 146, 54, 135, 166, 66, 157, 255, 237, 69, + 183, 141, 45, 162, 145, 71, 16, 87, 184, 120, 84, 156, 220, 159, 4, 99, 48, 191, + 203, 136, 112, 127, 226, 192, 184, 110, 6, 177, 182, 109, 207, 197, 239, 161, 132, + 17, 89, 56, 137, 205, 202, 101, 97, 60, 162, 253, 23, 169, 75, 236, 211, 126, 121, + 132, 191, 68, 167, 200, 16, 154, 149, 202, 197, 7, 191, 26, 8, 67, 3, 37, 137, 16, + 153, 30, 209, 238, 53, 233, 148, 198, 253, 94, 216, 73, 25, 190, 205, 132, 208, + 255, 219, 170, 98, 17, 160, 179, 183, 200, 17, 99, 36, 130, 216, 223, 72, 222, 250, + 73, 78, 79, 72, 253, 105, 245, 84, 244, 196, + ], + quorum_indexes: vec![0, 1], + }, + }; + let result = verifier.verify_merkle_proof(&cert); + assert!(result.is_ok()); +} - #[ignore = "depends on external RPC"] - #[tokio::test] - async fn test_hash_blob_header() { - let cfg = get_verifier_config(); - let signing_client = create_remote_signing_client(cfg.clone()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); - let blob_header = BlobHeader { +/// Test the verificarion of a merkle proof with a mocked verifier. +/// To test actual behaviour of the verifier, run the test above +#[tokio::test] +async fn test_verify_merkle_proof_mocked() { + let cfg = get_verifier_config(); + let signing_client = MockVerifierClient::new(HashMap::new()); + let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let cert = BlobInfo { + blob_header: BlobHeader { commitment: G1Commitment { x: vec![ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, ], y: vec![ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, ], }, - data_length: 2, + data_length: 4, blob_quorum_params: vec![ BlobQuorumParam { - quorum_number: 2, - adversary_threshold_percentage: 4, - confirmation_threshold_percentage: 5, - chunk_length: 6, + quorum_number: 0, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, }, BlobQuorumParam { - quorum_number: 2, - adversary_threshold_percentage: 4, - confirmation_threshold_percentage: 5, - chunk_length: 6, + quorum_number: 1, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, }, ], - }; - let result = verifier.hash_encode_blob_header(&blob_header); - let expected = "ba4675a31c9bf6b2f7abfdcedd34b74645cb7332b35db39bff00ae8516a67393"; - assert_eq!(result, hex::decode(expected).unwrap()); - } + }, + blob_verification_proof: BlobVerificationProof { + batch_id: 66507, + blob_index: 92, + batch_medatada: BatchMetadata { + batch_header: BatchHeader { + batch_root: vec![ + 179, 187, 53, 98, 192, 80, 151, 28, 125, 192, 115, 29, 129, 238, 216, 8, + 213, 210, 203, 143, 181, 19, 146, 113, 98, 131, 39, 238, 149, 248, 211, 43, + ], + quorum_numbers: vec![0, 1], + quorum_signed_percentages: vec![100, 100], + reference_block_number: 2624794, + }, + signatory_record_hash: vec![ + 172, 32, 172, 142, 197, 52, 84, 143, 120, 26, 190, 9, 143, 217, 62, 19, 17, + 107, 105, 67, 203, 5, 172, 249, 6, 60, 105, 240, 134, 34, 66, 133, + ], + fee: vec![0], + confirmation_block_number: 2624876, + batch_header_hash: vec![ + 122, 115, 2, 85, 233, 75, 121, 85, 51, 81, 248, 170, 198, 252, 42, 16, 1, 146, + 96, 218, 159, 44, 41, 40, 94, 247, 147, 11, 255, 68, 40, 177, + ], + }, + inclusion_proof: vec![ + 203, 160, 237, 48, 117, 255, 75, 254, 117, 144, 164, 77, 29, 146, 36, 48, 190, 140, + 50, 100, 144, 237, 125, 125, 75, 54, 210, 247, 147, 23, 48, 189, 120, 4, 125, 123, + 195, 244, 207, 239, 145, 109, 0, 21, 11, 162, 109, 79, 192, 100, 138, 157, 203, 22, + 17, 114, 234, 72, 174, 231, 209, 133, 99, 118, 201, 160, 137, 128, 112, 84, 34, + 136, 174, 139, 96, 26, 246, 148, 134, 52, 200, 229, 160, 145, 5, 120, 18, 187, 51, + 11, 109, 91, 237, 171, 215, 207, 90, 95, 146, 54, 135, 166, 66, 157, 255, 237, 69, + 183, 141, 45, 162, 145, 71, 16, 87, 184, 120, 84, 156, 220, 159, 4, 99, 48, 191, + 203, 136, 112, 127, 226, 192, 184, 110, 6, 177, 182, 109, 207, 197, 239, 161, 132, + 17, 89, 56, 137, 205, 202, 101, 97, 60, 162, 253, 23, 169, 75, 236, 211, 126, 121, + 132, 191, 68, 167, 200, 16, 154, 149, 202, 197, 7, 191, 26, 8, 67, 3, 37, 137, 16, + 153, 30, 209, 238, 53, 233, 148, 198, 253, 94, 216, 73, 25, 190, 205, 132, 208, + 255, 219, 170, 98, 17, 160, 179, 183, 200, 17, 99, 36, 130, 216, 223, 72, 222, 250, + 73, 78, 79, 72, 253, 105, 245, 84, 244, 196, + ], + quorum_indexes: vec![0, 1], + }, + }; + let result = verifier.verify_merkle_proof(&cert); + assert!(result.is_ok()); +} + +#[ignore = "depends on external RPC"] +#[tokio::test] +async fn test_hash_blob_header() { + let cfg = get_verifier_config(); + let signing_client = create_remote_signing_client(cfg.clone()); + let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let blob_header = BlobHeader { + commitment: G1Commitment { + x: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + y: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + }, + data_length: 2, + blob_quorum_params: vec![ + BlobQuorumParam { + quorum_number: 2, + adversary_threshold_percentage: 4, + confirmation_threshold_percentage: 5, + chunk_length: 6, + }, + BlobQuorumParam { + quorum_number: 2, + adversary_threshold_percentage: 4, + confirmation_threshold_percentage: 5, + chunk_length: 6, + }, + ], + }; + let result = verifier.hash_encode_blob_header(&blob_header); + let expected = "ba4675a31c9bf6b2f7abfdcedd34b74645cb7332b35db39bff00ae8516a67393"; + assert_eq!(result, hex::decode(expected).unwrap()); +} + +/// Test hashing of a blob header with a mocked verifier. +/// To test actual behaviour of the verifier, run the test above +#[tokio::test] +async fn test_hash_blob_header_mocked() { + let cfg = get_verifier_config(); + let signing_client = MockVerifierClient::new(HashMap::new()); + let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let blob_header = BlobHeader { + commitment: G1Commitment { + x: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + y: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ], + }, + data_length: 2, + blob_quorum_params: vec![ + BlobQuorumParam { + quorum_number: 2, + adversary_threshold_percentage: 4, + confirmation_threshold_percentage: 5, + chunk_length: 6, + }, + BlobQuorumParam { + quorum_number: 2, + adversary_threshold_percentage: 4, + confirmation_threshold_percentage: 5, + chunk_length: 6, + }, + ], + }; + let result = verifier.hash_encode_blob_header(&blob_header); + let expected = "ba4675a31c9bf6b2f7abfdcedd34b74645cb7332b35db39bff00ae8516a67393"; + assert_eq!(result, hex::decode(expected).unwrap()); +} + +#[ignore = "depends on external RPC"] +#[tokio::test] +async fn test_inclusion_proof() { + let cfg = get_verifier_config(); + let signing_client = create_remote_signing_client(cfg.clone()); + let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let proof = hex::decode("c455c1ea0e725d7ea3e5f29e9f48be8fc2787bb0a914d5a86710ba302c166ac4f626d76f67f1055bb960a514fb8923af2078fd84085d712655b58a19612e8cd15c3e4ac1cef57acde3438dbcf63f47c9fefe1221344c4d5c1a4943dd0d1803091ca81a270909dc0e146841441c9bd0e08e69ce6168181a3e4060ffacf3627480bec6abdd8d7bb92b49d33f180c42f49e041752aaded9c403db3a17b85e48a11e9ea9a08763f7f383dab6d25236f1b77c12b4c49c5cdbcbea32554a604e3f1d2f466851cb43fe73617b3d01e665e4c019bf930f92dea7394c25ed6a1e200d051fb0c30a2193c459f1cfef00bf1ba6656510d16725a4d1dc031cb759dbc90bab427b0f60ddc6764681924dda848824605a4f08b7f526fe6bd4572458c94e83fbf2150f2eeb28d3011ec921996dc3e69efa52d5fcf3182b20b56b5857a926aa66605808079b4d52c0c0cfe06923fa92e65eeca2c3e6126108e8c1babf5ac522f4d7").unwrap(); + let leaf: [u8; 32] = + hex::decode("f6106e6ae4631e68abe0fa898cedbe97dbae6c7efb1b088c5aa2e8b91190ff96") + .unwrap() + .try_into() + .unwrap(); + let expected_root = + hex::decode("7390b8023db8248123dcaeca57fa6c9340bef639e204f2278fc7ec3d46ad071b").unwrap(); + + let actual_root = verifier.process_inclusion_proof(&proof, leaf, 580).unwrap(); + + assert_eq!(actual_root, expected_root); +} + +/// Test proof inclusion with a mocked verifier. +/// To test actual behaviour of the verifier, run the test above +#[tokio::test] +async fn test_inclusion_proof_mocked() { + let cfg = get_verifier_config(); + let signing_client = MockVerifierClient::new(HashMap::new()); + let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let proof = hex::decode("c455c1ea0e725d7ea3e5f29e9f48be8fc2787bb0a914d5a86710ba302c166ac4f626d76f67f1055bb960a514fb8923af2078fd84085d712655b58a19612e8cd15c3e4ac1cef57acde3438dbcf63f47c9fefe1221344c4d5c1a4943dd0d1803091ca81a270909dc0e146841441c9bd0e08e69ce6168181a3e4060ffacf3627480bec6abdd8d7bb92b49d33f180c42f49e041752aaded9c403db3a17b85e48a11e9ea9a08763f7f383dab6d25236f1b77c12b4c49c5cdbcbea32554a604e3f1d2f466851cb43fe73617b3d01e665e4c019bf930f92dea7394c25ed6a1e200d051fb0c30a2193c459f1cfef00bf1ba6656510d16725a4d1dc031cb759dbc90bab427b0f60ddc6764681924dda848824605a4f08b7f526fe6bd4572458c94e83fbf2150f2eeb28d3011ec921996dc3e69efa52d5fcf3182b20b56b5857a926aa66605808079b4d52c0c0cfe06923fa92e65eeca2c3e6126108e8c1babf5ac522f4d7").unwrap(); + let leaf: [u8; 32] = + hex::decode("f6106e6ae4631e68abe0fa898cedbe97dbae6c7efb1b088c5aa2e8b91190ff96") + .unwrap() + .try_into() + .unwrap(); + let expected_root = + hex::decode("7390b8023db8248123dcaeca57fa6c9340bef639e204f2278fc7ec3d46ad071b").unwrap(); - /// Test hashing of a blob header with a mocked verifier. - /// To test actual behaviour of the verifier, run the test above - #[tokio::test] - async fn test_hash_blob_header_mocked() { - let cfg = get_verifier_config(); - let signing_client = MockVerifierClient::new(HashMap::new()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); - let blob_header = BlobHeader { + let actual_root = verifier.process_inclusion_proof(&proof, leaf, 580).unwrap(); + + assert_eq!(actual_root, expected_root); +} + +#[ignore = "depends on external RPC"] +#[tokio::test] +async fn test_verify_batch() { + let cfg = get_verifier_config(); + let signing_client = create_remote_signing_client(cfg.clone()); + let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let cert = BlobInfo { + blob_header: BlobHeader { commitment: G1Commitment { x: vec![ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, ], y: vec![ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, ], }, - data_length: 2, + data_length: 4, blob_quorum_params: vec![ BlobQuorumParam { - quorum_number: 2, - adversary_threshold_percentage: 4, - confirmation_threshold_percentage: 5, - chunk_length: 6, + quorum_number: 0, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, }, BlobQuorumParam { - quorum_number: 2, - adversary_threshold_percentage: 4, - confirmation_threshold_percentage: 5, - chunk_length: 6, + quorum_number: 1, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, }, ], - }; - let result = verifier.hash_encode_blob_header(&blob_header); - let expected = "ba4675a31c9bf6b2f7abfdcedd34b74645cb7332b35db39bff00ae8516a67393"; - assert_eq!(result, hex::decode(expected).unwrap()); - } - - #[ignore = "depends on external RPC"] - #[tokio::test] - async fn test_inclusion_proof() { - let cfg = get_verifier_config(); - let signing_client = create_remote_signing_client(cfg.clone()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); - let proof = hex::decode("c455c1ea0e725d7ea3e5f29e9f48be8fc2787bb0a914d5a86710ba302c166ac4f626d76f67f1055bb960a514fb8923af2078fd84085d712655b58a19612e8cd15c3e4ac1cef57acde3438dbcf63f47c9fefe1221344c4d5c1a4943dd0d1803091ca81a270909dc0e146841441c9bd0e08e69ce6168181a3e4060ffacf3627480bec6abdd8d7bb92b49d33f180c42f49e041752aaded9c403db3a17b85e48a11e9ea9a08763f7f383dab6d25236f1b77c12b4c49c5cdbcbea32554a604e3f1d2f466851cb43fe73617b3d01e665e4c019bf930f92dea7394c25ed6a1e200d051fb0c30a2193c459f1cfef00bf1ba6656510d16725a4d1dc031cb759dbc90bab427b0f60ddc6764681924dda848824605a4f08b7f526fe6bd4572458c94e83fbf2150f2eeb28d3011ec921996dc3e69efa52d5fcf3182b20b56b5857a926aa66605808079b4d52c0c0cfe06923fa92e65eeca2c3e6126108e8c1babf5ac522f4d7").unwrap(); - let leaf: [u8; 32] = - hex::decode("f6106e6ae4631e68abe0fa898cedbe97dbae6c7efb1b088c5aa2e8b91190ff96") - .unwrap() - .try_into() - .unwrap(); - let expected_root = - hex::decode("7390b8023db8248123dcaeca57fa6c9340bef639e204f2278fc7ec3d46ad071b") - .unwrap(); - - let actual_root = verifier.process_inclusion_proof(&proof, leaf, 580).unwrap(); - - assert_eq!(actual_root, expected_root); - } - - /// Test proof inclusion with a mocked verifier. - /// To test actual behaviour of the verifier, run the test above - #[tokio::test] - async fn test_inclusion_proof_mocked() { - let cfg = get_verifier_config(); - let signing_client = MockVerifierClient::new(HashMap::new()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); - let proof = hex::decode("c455c1ea0e725d7ea3e5f29e9f48be8fc2787bb0a914d5a86710ba302c166ac4f626d76f67f1055bb960a514fb8923af2078fd84085d712655b58a19612e8cd15c3e4ac1cef57acde3438dbcf63f47c9fefe1221344c4d5c1a4943dd0d1803091ca81a270909dc0e146841441c9bd0e08e69ce6168181a3e4060ffacf3627480bec6abdd8d7bb92b49d33f180c42f49e041752aaded9c403db3a17b85e48a11e9ea9a08763f7f383dab6d25236f1b77c12b4c49c5cdbcbea32554a604e3f1d2f466851cb43fe73617b3d01e665e4c019bf930f92dea7394c25ed6a1e200d051fb0c30a2193c459f1cfef00bf1ba6656510d16725a4d1dc031cb759dbc90bab427b0f60ddc6764681924dda848824605a4f08b7f526fe6bd4572458c94e83fbf2150f2eeb28d3011ec921996dc3e69efa52d5fcf3182b20b56b5857a926aa66605808079b4d52c0c0cfe06923fa92e65eeca2c3e6126108e8c1babf5ac522f4d7").unwrap(); - let leaf: [u8; 32] = - hex::decode("f6106e6ae4631e68abe0fa898cedbe97dbae6c7efb1b088c5aa2e8b91190ff96") - .unwrap() - .try_into() - .unwrap(); - let expected_root = - hex::decode("7390b8023db8248123dcaeca57fa6c9340bef639e204f2278fc7ec3d46ad071b") - .unwrap(); - - let actual_root = verifier.process_inclusion_proof(&proof, leaf, 580).unwrap(); - - assert_eq!(actual_root, expected_root); - } - - #[ignore = "depends on external RPC"] - #[tokio::test] - async fn test_verify_batch() { - let cfg = get_verifier_config(); - let signing_client = create_remote_signing_client(cfg.clone()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); - let cert = BlobInfo { - blob_header: BlobHeader { - commitment: G1Commitment { - x: vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - ], - y: vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, + }, + blob_verification_proof: BlobVerificationProof { + batch_id: 66507, + blob_index: 92, + batch_medatada: BatchMetadata { + batch_header: BatchHeader { + batch_root: vec![ + 179, 187, 53, 98, 192, 80, 151, 28, 125, 192, 115, 29, 129, 238, 216, 8, + 213, 210, 203, 143, 181, 19, 146, 113, 98, 131, 39, 238, 149, 248, 211, 43, ], + quorum_numbers: vec![0, 1], + quorum_signed_percentages: vec![100, 100], + reference_block_number: 2624794, }, - data_length: 4, - blob_quorum_params: vec![ - BlobQuorumParam { - quorum_number: 0, - adversary_threshold_percentage: 33, - confirmation_threshold_percentage: 55, - chunk_length: 1, - }, - BlobQuorumParam { - quorum_number: 1, - adversary_threshold_percentage: 33, - confirmation_threshold_percentage: 55, - chunk_length: 1, - }, + signatory_record_hash: vec![ + 172, 32, 172, 142, 197, 52, 84, 143, 120, 26, 190, 9, 143, 217, 62, 19, 17, + 107, 105, 67, 203, 5, 172, 249, 6, 60, 105, 240, 134, 34, 66, 133, ], - }, - blob_verification_proof: BlobVerificationProof { - batch_id: 66507, - blob_index: 92, - batch_medatada: BatchMetadata { - batch_header: BatchHeader { - batch_root: vec![ - 179, 187, 53, 98, 192, 80, 151, 28, 125, 192, 115, 29, 129, 238, 216, - 8, 213, 210, 203, 143, 181, 19, 146, 113, 98, 131, 39, 238, 149, 248, - 211, 43, - ], - quorum_numbers: vec![0, 1], - quorum_signed_percentages: vec![100, 100], - reference_block_number: 2624794, - }, - signatory_record_hash: vec![ - 172, 32, 172, 142, 197, 52, 84, 143, 120, 26, 190, 9, 143, 217, 62, 19, 17, - 107, 105, 67, 203, 5, 172, 249, 6, 60, 105, 240, 134, 34, 66, 133, - ], - fee: vec![0], - confirmation_block_number: 2624876, - batch_header_hash: vec![ - 122, 115, 2, 85, 233, 75, 121, 85, 51, 81, 248, 170, 198, 252, 42, 16, 1, - 146, 96, 218, 159, 44, 41, 40, 94, 247, 147, 11, 255, 68, 40, 177, - ], - }, - inclusion_proof: vec![ - 203, 160, 237, 48, 117, 255, 75, 254, 117, 144, 164, 77, 29, 146, 36, 48, 190, - 140, 50, 100, 144, 237, 125, 125, 75, 54, 210, 247, 147, 23, 48, 189, 120, 4, - 125, 123, 195, 244, 207, 239, 145, 109, 0, 21, 11, 162, 109, 79, 192, 100, 138, - 157, 203, 22, 17, 114, 234, 72, 174, 231, 209, 133, 99, 118, 201, 160, 137, - 128, 112, 84, 34, 136, 174, 139, 96, 26, 246, 148, 134, 52, 200, 229, 160, 145, - 5, 120, 18, 187, 51, 11, 109, 91, 237, 171, 215, 207, 90, 95, 146, 54, 135, - 166, 66, 157, 255, 237, 69, 183, 141, 45, 162, 145, 71, 16, 87, 184, 120, 84, - 156, 220, 159, 4, 99, 48, 191, 203, 136, 112, 127, 226, 192, 184, 110, 6, 177, - 182, 109, 207, 197, 239, 161, 132, 17, 89, 56, 137, 205, 202, 101, 97, 60, 162, - 253, 23, 169, 75, 236, 211, 126, 121, 132, 191, 68, 167, 200, 16, 154, 149, - 202, 197, 7, 191, 26, 8, 67, 3, 37, 137, 16, 153, 30, 209, 238, 53, 233, 148, - 198, 253, 94, 216, 73, 25, 190, 205, 132, 208, 255, 219, 170, 98, 17, 160, 179, - 183, 200, 17, 99, 36, 130, 216, 223, 72, 222, 250, 73, 78, 79, 72, 253, 105, - 245, 84, 244, 196, + fee: vec![0], + confirmation_block_number: 2624876, + batch_header_hash: vec![ + 122, 115, 2, 85, 233, 75, 121, 85, 51, 81, 248, 170, 198, 252, 42, 16, 1, 146, + 96, 218, 159, 44, 41, 40, 94, 247, 147, 11, 255, 68, 40, 177, ], - quorum_indexes: vec![0, 1], }, - }; - let result = verifier.verify_batch(&cert).await; - assert!(result.is_ok()); - } + inclusion_proof: vec![ + 203, 160, 237, 48, 117, 255, 75, 254, 117, 144, 164, 77, 29, 146, 36, 48, 190, 140, + 50, 100, 144, 237, 125, 125, 75, 54, 210, 247, 147, 23, 48, 189, 120, 4, 125, 123, + 195, 244, 207, 239, 145, 109, 0, 21, 11, 162, 109, 79, 192, 100, 138, 157, 203, 22, + 17, 114, 234, 72, 174, 231, 209, 133, 99, 118, 201, 160, 137, 128, 112, 84, 34, + 136, 174, 139, 96, 26, 246, 148, 134, 52, 200, 229, 160, 145, 5, 120, 18, 187, 51, + 11, 109, 91, 237, 171, 215, 207, 90, 95, 146, 54, 135, 166, 66, 157, 255, 237, 69, + 183, 141, 45, 162, 145, 71, 16, 87, 184, 120, 84, 156, 220, 159, 4, 99, 48, 191, + 203, 136, 112, 127, 226, 192, 184, 110, 6, 177, 182, 109, 207, 197, 239, 161, 132, + 17, 89, 56, 137, 205, 202, 101, 97, 60, 162, 253, 23, 169, 75, 236, 211, 126, 121, + 132, 191, 68, 167, 200, 16, 154, 149, 202, 197, 7, 191, 26, 8, 67, 3, 37, 137, 16, + 153, 30, 209, 238, 53, 233, 148, 198, 253, 94, 216, 73, 25, 190, 205, 132, 208, + 255, 219, 170, 98, 17, 160, 179, 183, 200, 17, 99, 36, 130, 216, 223, 72, 222, 250, + 73, 78, 79, 72, 253, 105, 245, 84, 244, 196, + ], + quorum_indexes: vec![0, 1], + }, + }; + let result = verifier.verify_batch(&cert).await; + assert!(result.is_ok()); +} - /// Test batch verification with a mocked verifier. - /// To test actual behaviour of the verifier, run the test above - #[tokio::test] - async fn test_verify_batch_mocked() { - let mut mock_replies = HashMap::new(); - let mock_req = CallRequest { - from: None, - to: Some(H160::from_str("0xd4a7e1bd8015057293f0d0a557088c286942e84b").unwrap()), - gas: None, - gas_price: None, - value: None, - data: Some(Bytes::from( - hex::decode( - "eccbbfc900000000000000000000000000000000000000000000000000000000000103cb", - ) - .unwrap(), - )), - transaction_type: None, - access_list: None, - max_fee_per_gas: None, - max_priority_fee_per_gas: None, - }; - let mock_req = serde_json::to_string(&mock_req).unwrap(); - let mock_res = Bytes::from( - hex::decode("60933e76989e57d6fd210ae2fc3086958d708660ee6927f91963047ab1a91ba8") +/// Test batch verification with a mocked verifier. +/// To test actual behaviour of the verifier, run the test above +#[tokio::test] +async fn test_verify_batch_mocked() { + let mut mock_replies = HashMap::new(); + let mock_req = CallRequest { + from: None, + to: Some(H160::from_str("0xd4a7e1bd8015057293f0d0a557088c286942e84b").unwrap()), + gas: None, + gas_price: None, + value: None, + data: Some(Bytes::from( + hex::decode("eccbbfc900000000000000000000000000000000000000000000000000000000000103cb") .unwrap(), - ); - mock_replies.insert(mock_req, mock_res); + )), + transaction_type: None, + access_list: None, + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + }; + let mock_req = serde_json::to_string(&mock_req).unwrap(); + let mock_res = Bytes::from( + hex::decode("60933e76989e57d6fd210ae2fc3086958d708660ee6927f91963047ab1a91ba8").unwrap(), + ); + mock_replies.insert(mock_req, mock_res); - let cfg = get_verifier_config(); - let signing_client = MockVerifierClient::new(mock_replies); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); - let cert = BlobInfo { - blob_header: BlobHeader { - commitment: G1Commitment { - x: vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - ], - y: vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - ], - }, - data_length: 4, - blob_quorum_params: vec![ - BlobQuorumParam { - quorum_number: 0, - adversary_threshold_percentage: 33, - confirmation_threshold_percentage: 55, - chunk_length: 1, - }, - BlobQuorumParam { - quorum_number: 1, - adversary_threshold_percentage: 33, - confirmation_threshold_percentage: 55, - chunk_length: 1, - }, + let cfg = get_verifier_config(); + let signing_client = MockVerifierClient::new(mock_replies); + let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let cert = BlobInfo { + blob_header: BlobHeader { + commitment: G1Commitment { + x: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + ], + y: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, ], }, - blob_verification_proof: BlobVerificationProof { - batch_id: 66507, - blob_index: 92, - batch_medatada: BatchMetadata { - batch_header: BatchHeader { - batch_root: vec![ - 179, 187, 53, 98, 192, 80, 151, 28, 125, 192, 115, 29, 129, 238, 216, - 8, 213, 210, 203, 143, 181, 19, 146, 113, 98, 131, 39, 238, 149, 248, - 211, 43, - ], - quorum_numbers: vec![0, 1], - quorum_signed_percentages: vec![100, 100], - reference_block_number: 2624794, - }, - signatory_record_hash: vec![ - 172, 32, 172, 142, 197, 52, 84, 143, 120, 26, 190, 9, 143, 217, 62, 19, 17, - 107, 105, 67, 203, 5, 172, 249, 6, 60, 105, 240, 134, 34, 66, 133, - ], - fee: vec![0], - confirmation_block_number: 2624876, - batch_header_hash: vec![ - 122, 115, 2, 85, 233, 75, 121, 85, 51, 81, 248, 170, 198, 252, 42, 16, 1, - 146, 96, 218, 159, 44, 41, 40, 94, 247, 147, 11, 255, 68, 40, 177, + data_length: 4, + blob_quorum_params: vec![ + BlobQuorumParam { + quorum_number: 0, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, + }, + BlobQuorumParam { + quorum_number: 1, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, + }, + ], + }, + blob_verification_proof: BlobVerificationProof { + batch_id: 66507, + blob_index: 92, + batch_medatada: BatchMetadata { + batch_header: BatchHeader { + batch_root: vec![ + 179, 187, 53, 98, 192, 80, 151, 28, 125, 192, 115, 29, 129, 238, 216, 8, + 213, 210, 203, 143, 181, 19, 146, 113, 98, 131, 39, 238, 149, 248, 211, 43, ], + quorum_numbers: vec![0, 1], + quorum_signed_percentages: vec![100, 100], + reference_block_number: 2624794, }, - inclusion_proof: vec![ - 203, 160, 237, 48, 117, 255, 75, 254, 117, 144, 164, 77, 29, 146, 36, 48, 190, - 140, 50, 100, 144, 237, 125, 125, 75, 54, 210, 247, 147, 23, 48, 189, 120, 4, - 125, 123, 195, 244, 207, 239, 145, 109, 0, 21, 11, 162, 109, 79, 192, 100, 138, - 157, 203, 22, 17, 114, 234, 72, 174, 231, 209, 133, 99, 118, 201, 160, 137, - 128, 112, 84, 34, 136, 174, 139, 96, 26, 246, 148, 134, 52, 200, 229, 160, 145, - 5, 120, 18, 187, 51, 11, 109, 91, 237, 171, 215, 207, 90, 95, 146, 54, 135, - 166, 66, 157, 255, 237, 69, 183, 141, 45, 162, 145, 71, 16, 87, 184, 120, 84, - 156, 220, 159, 4, 99, 48, 191, 203, 136, 112, 127, 226, 192, 184, 110, 6, 177, - 182, 109, 207, 197, 239, 161, 132, 17, 89, 56, 137, 205, 202, 101, 97, 60, 162, - 253, 23, 169, 75, 236, 211, 126, 121, 132, 191, 68, 167, 200, 16, 154, 149, - 202, 197, 7, 191, 26, 8, 67, 3, 37, 137, 16, 153, 30, 209, 238, 53, 233, 148, - 198, 253, 94, 216, 73, 25, 190, 205, 132, 208, 255, 219, 170, 98, 17, 160, 179, - 183, 200, 17, 99, 36, 130, 216, 223, 72, 222, 250, 73, 78, 79, 72, 253, 105, - 245, 84, 244, 196, + signatory_record_hash: vec![ + 172, 32, 172, 142, 197, 52, 84, 143, 120, 26, 190, 9, 143, 217, 62, 19, 17, + 107, 105, 67, 203, 5, 172, 249, 6, 60, 105, 240, 134, 34, 66, 133, + ], + fee: vec![0], + confirmation_block_number: 2624876, + batch_header_hash: vec![ + 122, 115, 2, 85, 233, 75, 121, 85, 51, 81, 248, 170, 198, 252, 42, 16, 1, 146, + 96, 218, 159, 44, 41, 40, 94, 247, 147, 11, 255, 68, 40, 177, ], - quorum_indexes: vec![0, 1], }, - }; - let result = verifier.verify_batch(&cert).await; - assert!(result.is_ok()); - } + inclusion_proof: vec![ + 203, 160, 237, 48, 117, 255, 75, 254, 117, 144, 164, 77, 29, 146, 36, 48, 190, 140, + 50, 100, 144, 237, 125, 125, 75, 54, 210, 247, 147, 23, 48, 189, 120, 4, 125, 123, + 195, 244, 207, 239, 145, 109, 0, 21, 11, 162, 109, 79, 192, 100, 138, 157, 203, 22, + 17, 114, 234, 72, 174, 231, 209, 133, 99, 118, 201, 160, 137, 128, 112, 84, 34, + 136, 174, 139, 96, 26, 246, 148, 134, 52, 200, 229, 160, 145, 5, 120, 18, 187, 51, + 11, 109, 91, 237, 171, 215, 207, 90, 95, 146, 54, 135, 166, 66, 157, 255, 237, 69, + 183, 141, 45, 162, 145, 71, 16, 87, 184, 120, 84, 156, 220, 159, 4, 99, 48, 191, + 203, 136, 112, 127, 226, 192, 184, 110, 6, 177, 182, 109, 207, 197, 239, 161, 132, + 17, 89, 56, 137, 205, 202, 101, 97, 60, 162, 253, 23, 169, 75, 236, 211, 126, 121, + 132, 191, 68, 167, 200, 16, 154, 149, 202, 197, 7, 191, 26, 8, 67, 3, 37, 137, 16, + 153, 30, 209, 238, 53, 233, 148, 198, 253, 94, 216, 73, 25, 190, 205, 132, 208, + 255, 219, 170, 98, 17, 160, 179, 183, 200, 17, 99, 36, 130, 216, 223, 72, 222, 250, + 73, 78, 79, 72, 253, 105, 245, 84, 244, 196, + ], + quorum_indexes: vec![0, 1], + }, + }; + let result = verifier.verify_batch(&cert).await; + assert!(result.is_ok()); +} - #[ignore = "depends on external RPC"] - #[tokio::test] - async fn test_verify_security_params() { - let cfg = get_verifier_config(); - let signing_client = create_remote_signing_client(cfg.clone()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); - let cert = BlobInfo { - blob_header: BlobHeader { - commitment: G1Commitment { - x: vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - ], - y: vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - ], - }, - data_length: 4, - blob_quorum_params: vec![ - BlobQuorumParam { - quorum_number: 0, - adversary_threshold_percentage: 33, - confirmation_threshold_percentage: 55, - chunk_length: 1, - }, - BlobQuorumParam { - quorum_number: 1, - adversary_threshold_percentage: 33, - confirmation_threshold_percentage: 55, - chunk_length: 1, - }, +#[ignore = "depends on external RPC"] +#[tokio::test] +async fn test_verify_security_params() { + let cfg = get_verifier_config(); + let signing_client = create_remote_signing_client(cfg.clone()); + let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let cert = BlobInfo { + blob_header: BlobHeader { + commitment: G1Commitment { + x: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + ], + y: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, ], }, - blob_verification_proof: BlobVerificationProof { - batch_id: 66507, - blob_index: 92, - batch_medatada: BatchMetadata { - batch_header: BatchHeader { - batch_root: vec![ - 179, 187, 53, 98, 192, 80, 151, 28, 125, 192, 115, 29, 129, 238, 216, - 8, 213, 210, 203, 143, 181, 19, 146, 113, 98, 131, 39, 238, 149, 248, - 211, 43, - ], - quorum_numbers: vec![0, 1], - quorum_signed_percentages: vec![100, 100], - reference_block_number: 2624794, - }, - signatory_record_hash: vec![ - 172, 32, 172, 142, 197, 52, 84, 143, 120, 26, 190, 9, 143, 217, 62, 19, 17, - 107, 105, 67, 203, 5, 172, 249, 6, 60, 105, 240, 134, 34, 66, 133, - ], - fee: vec![0], - confirmation_block_number: 2624876, - batch_header_hash: vec![ - 122, 115, 2, 85, 233, 75, 121, 85, 51, 81, 248, 170, 198, 252, 42, 16, 1, - 146, 96, 218, 159, 44, 41, 40, 94, 247, 147, 11, 255, 68, 40, 177, + data_length: 4, + blob_quorum_params: vec![ + BlobQuorumParam { + quorum_number: 0, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, + }, + BlobQuorumParam { + quorum_number: 1, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, + }, + ], + }, + blob_verification_proof: BlobVerificationProof { + batch_id: 66507, + blob_index: 92, + batch_medatada: BatchMetadata { + batch_header: BatchHeader { + batch_root: vec![ + 179, 187, 53, 98, 192, 80, 151, 28, 125, 192, 115, 29, 129, 238, 216, 8, + 213, 210, 203, 143, 181, 19, 146, 113, 98, 131, 39, 238, 149, 248, 211, 43, ], + quorum_numbers: vec![0, 1], + quorum_signed_percentages: vec![100, 100], + reference_block_number: 2624794, }, - inclusion_proof: vec![ - 203, 160, 237, 48, 117, 255, 75, 254, 117, 144, 164, 77, 29, 146, 36, 48, 190, - 140, 50, 100, 144, 237, 125, 125, 75, 54, 210, 247, 147, 23, 48, 189, 120, 4, - 125, 123, 195, 244, 207, 239, 145, 109, 0, 21, 11, 162, 109, 79, 192, 100, 138, - 157, 203, 22, 17, 114, 234, 72, 174, 231, 209, 133, 99, 118, 201, 160, 137, - 128, 112, 84, 34, 136, 174, 139, 96, 26, 246, 148, 134, 52, 200, 229, 160, 145, - 5, 120, 18, 187, 51, 11, 109, 91, 237, 171, 215, 207, 90, 95, 146, 54, 135, - 166, 66, 157, 255, 237, 69, 183, 141, 45, 162, 145, 71, 16, 87, 184, 120, 84, - 156, 220, 159, 4, 99, 48, 191, 203, 136, 112, 127, 226, 192, 184, 110, 6, 177, - 182, 109, 207, 197, 239, 161, 132, 17, 89, 56, 137, 205, 202, 101, 97, 60, 162, - 253, 23, 169, 75, 236, 211, 126, 121, 132, 191, 68, 167, 200, 16, 154, 149, - 202, 197, 7, 191, 26, 8, 67, 3, 37, 137, 16, 153, 30, 209, 238, 53, 233, 148, - 198, 253, 94, 216, 73, 25, 190, 205, 132, 208, 255, 219, 170, 98, 17, 160, 179, - 183, 200, 17, 99, 36, 130, 216, 223, 72, 222, 250, 73, 78, 79, 72, 253, 105, - 245, 84, 244, 196, + signatory_record_hash: vec![ + 172, 32, 172, 142, 197, 52, 84, 143, 120, 26, 190, 9, 143, 217, 62, 19, 17, + 107, 105, 67, 203, 5, 172, 249, 6, 60, 105, 240, 134, 34, 66, 133, + ], + fee: vec![0], + confirmation_block_number: 2624876, + batch_header_hash: vec![ + 122, 115, 2, 85, 233, 75, 121, 85, 51, 81, 248, 170, 198, 252, 42, 16, 1, 146, + 96, 218, 159, 44, 41, 40, 94, 247, 147, 11, 255, 68, 40, 177, ], - quorum_indexes: vec![0, 1], }, - }; - let result = verifier.verify_security_params(&cert).await; - println!("result {:?}", result); - assert!(result.is_ok()); - } + inclusion_proof: vec![ + 203, 160, 237, 48, 117, 255, 75, 254, 117, 144, 164, 77, 29, 146, 36, 48, 190, 140, + 50, 100, 144, 237, 125, 125, 75, 54, 210, 247, 147, 23, 48, 189, 120, 4, 125, 123, + 195, 244, 207, 239, 145, 109, 0, 21, 11, 162, 109, 79, 192, 100, 138, 157, 203, 22, + 17, 114, 234, 72, 174, 231, 209, 133, 99, 118, 201, 160, 137, 128, 112, 84, 34, + 136, 174, 139, 96, 26, 246, 148, 134, 52, 200, 229, 160, 145, 5, 120, 18, 187, 51, + 11, 109, 91, 237, 171, 215, 207, 90, 95, 146, 54, 135, 166, 66, 157, 255, 237, 69, + 183, 141, 45, 162, 145, 71, 16, 87, 184, 120, 84, 156, 220, 159, 4, 99, 48, 191, + 203, 136, 112, 127, 226, 192, 184, 110, 6, 177, 182, 109, 207, 197, 239, 161, 132, + 17, 89, 56, 137, 205, 202, 101, 97, 60, 162, 253, 23, 169, 75, 236, 211, 126, 121, + 132, 191, 68, 167, 200, 16, 154, 149, 202, 197, 7, 191, 26, 8, 67, 3, 37, 137, 16, + 153, 30, 209, 238, 53, 233, 148, 198, 253, 94, 216, 73, 25, 190, 205, 132, 208, + 255, 219, 170, 98, 17, 160, 179, 183, 200, 17, 99, 36, 130, 216, 223, 72, 222, 250, + 73, 78, 79, 72, 253, 105, 245, 84, 244, 196, + ], + quorum_indexes: vec![0, 1], + }, + }; + let result = verifier.verify_security_params(&cert).await; + println!("result {:?}", result); + assert!(result.is_ok()); +} - /// Test security params verification with a mocked verifier. - /// To test actual behaviour of the verifier, run the test above - #[tokio::test] - async fn test_verify_securityyy_params_mocked() { - let mut mock_replies = HashMap::new(); +/// Test security params verification with a mocked verifier. +/// To test actual behaviour of the verifier, run the test above +#[tokio::test] +async fn test_verify_securityyy_params_mocked() { + let mut mock_replies = HashMap::new(); - // First request - let mock_req = CallRequest { - from: None, - to: Some(H160::from_str("0xd4a7e1bd8015057293f0d0a557088c286942e84b").unwrap()), - gas: None, - gas_price: None, - value: None, - data: Some(Bytes::from(hex::decode("8687feae").unwrap())), - transaction_type: None, - access_list: None, - max_fee_per_gas: None, - max_priority_fee_per_gas: None, - }; - let mock_req = serde_json::to_string(&mock_req).unwrap(); - let mock_res = Bytes::from( + // First request + let mock_req = CallRequest { + from: None, + to: Some(H160::from_str("0xd4a7e1bd8015057293f0d0a557088c286942e84b").unwrap()), + gas: None, + gas_price: None, + value: None, + data: Some(Bytes::from(hex::decode("8687feae").unwrap())), + transaction_type: None, + access_list: None, + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + }; + let mock_req = serde_json::to_string(&mock_req).unwrap(); + let mock_res = Bytes::from( hex::decode("000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020001000000000000000000000000000000000000000000000000000000000000") .unwrap(), ); - mock_replies.insert(mock_req, mock_res); + mock_replies.insert(mock_req, mock_res); - // Second request - let mock_req = CallRequest { - from: None, - to: Some(H160::from_str("0xd4a7e1bd8015057293f0d0a557088c286942e84b").unwrap()), - gas: None, - gas_price: None, - value: None, - data: Some(Bytes::from(hex::decode("e15234ff").unwrap())), - transaction_type: None, - access_list: None, - max_fee_per_gas: None, - max_priority_fee_per_gas: None, - }; - let mock_req = serde_json::to_string(&mock_req).unwrap(); - let mock_res = Bytes::from( + // Second request + let mock_req = CallRequest { + from: None, + to: Some(H160::from_str("0xd4a7e1bd8015057293f0d0a557088c286942e84b").unwrap()), + gas: None, + gas_price: None, + value: None, + data: Some(Bytes::from(hex::decode("e15234ff").unwrap())), + transaction_type: None, + access_list: None, + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + }; + let mock_req = serde_json::to_string(&mock_req).unwrap(); + let mock_res = Bytes::from( hex::decode("000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020001000000000000000000000000000000000000000000000000000000000000") .unwrap(), ); - mock_replies.insert(mock_req, mock_res); + mock_replies.insert(mock_req, mock_res); - let cfg = get_verifier_config(); - let signing_client = MockVerifierClient::new(mock_replies); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); - let cert = BlobInfo { - blob_header: BlobHeader { - commitment: G1Commitment { - x: vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - ], - y: vec![ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - ], - }, - data_length: 4, - blob_quorum_params: vec![ - BlobQuorumParam { - quorum_number: 0, - adversary_threshold_percentage: 33, - confirmation_threshold_percentage: 55, - chunk_length: 1, - }, - BlobQuorumParam { - quorum_number: 1, - adversary_threshold_percentage: 33, - confirmation_threshold_percentage: 55, - chunk_length: 1, - }, + let cfg = get_verifier_config(); + let signing_client = MockVerifierClient::new(mock_replies); + let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let cert = BlobInfo { + blob_header: BlobHeader { + commitment: G1Commitment { + x: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + ], + y: vec![ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, ], }, - blob_verification_proof: BlobVerificationProof { - batch_id: 66507, - blob_index: 92, - batch_medatada: BatchMetadata { - batch_header: BatchHeader { - batch_root: vec![ - 179, 187, 53, 98, 192, 80, 151, 28, 125, 192, 115, 29, 129, 238, 216, - 8, 213, 210, 203, 143, 181, 19, 146, 113, 98, 131, 39, 238, 149, 248, - 211, 43, - ], - quorum_numbers: vec![0, 1], - quorum_signed_percentages: vec![100, 100], - reference_block_number: 2624794, - }, - signatory_record_hash: vec![ - 172, 32, 172, 142, 197, 52, 84, 143, 120, 26, 190, 9, 143, 217, 62, 19, 17, - 107, 105, 67, 203, 5, 172, 249, 6, 60, 105, 240, 134, 34, 66, 133, - ], - fee: vec![0], - confirmation_block_number: 2624876, - batch_header_hash: vec![ - 122, 115, 2, 85, 233, 75, 121, 85, 51, 81, 248, 170, 198, 252, 42, 16, 1, - 146, 96, 218, 159, 44, 41, 40, 94, 247, 147, 11, 255, 68, 40, 177, + data_length: 4, + blob_quorum_params: vec![ + BlobQuorumParam { + quorum_number: 0, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, + }, + BlobQuorumParam { + quorum_number: 1, + adversary_threshold_percentage: 33, + confirmation_threshold_percentage: 55, + chunk_length: 1, + }, + ], + }, + blob_verification_proof: BlobVerificationProof { + batch_id: 66507, + blob_index: 92, + batch_medatada: BatchMetadata { + batch_header: BatchHeader { + batch_root: vec![ + 179, 187, 53, 98, 192, 80, 151, 28, 125, 192, 115, 29, 129, 238, 216, 8, + 213, 210, 203, 143, 181, 19, 146, 113, 98, 131, 39, 238, 149, 248, 211, 43, ], + quorum_numbers: vec![0, 1], + quorum_signed_percentages: vec![100, 100], + reference_block_number: 2624794, }, - inclusion_proof: vec![ - 203, 160, 237, 48, 117, 255, 75, 254, 117, 144, 164, 77, 29, 146, 36, 48, 190, - 140, 50, 100, 144, 237, 125, 125, 75, 54, 210, 247, 147, 23, 48, 189, 120, 4, - 125, 123, 195, 244, 207, 239, 145, 109, 0, 21, 11, 162, 109, 79, 192, 100, 138, - 157, 203, 22, 17, 114, 234, 72, 174, 231, 209, 133, 99, 118, 201, 160, 137, - 128, 112, 84, 34, 136, 174, 139, 96, 26, 246, 148, 134, 52, 200, 229, 160, 145, - 5, 120, 18, 187, 51, 11, 109, 91, 237, 171, 215, 207, 90, 95, 146, 54, 135, - 166, 66, 157, 255, 237, 69, 183, 141, 45, 162, 145, 71, 16, 87, 184, 120, 84, - 156, 220, 159, 4, 99, 48, 191, 203, 136, 112, 127, 226, 192, 184, 110, 6, 177, - 182, 109, 207, 197, 239, 161, 132, 17, 89, 56, 137, 205, 202, 101, 97, 60, 162, - 253, 23, 169, 75, 236, 211, 126, 121, 132, 191, 68, 167, 200, 16, 154, 149, - 202, 197, 7, 191, 26, 8, 67, 3, 37, 137, 16, 153, 30, 209, 238, 53, 233, 148, - 198, 253, 94, 216, 73, 25, 190, 205, 132, 208, 255, 219, 170, 98, 17, 160, 179, - 183, 200, 17, 99, 36, 130, 216, 223, 72, 222, 250, 73, 78, 79, 72, 253, 105, - 245, 84, 244, 196, + signatory_record_hash: vec![ + 172, 32, 172, 142, 197, 52, 84, 143, 120, 26, 190, 9, 143, 217, 62, 19, 17, + 107, 105, 67, 203, 5, 172, 249, 6, 60, 105, 240, 134, 34, 66, 133, + ], + fee: vec![0], + confirmation_block_number: 2624876, + batch_header_hash: vec![ + 122, 115, 2, 85, 233, 75, 121, 85, 51, 81, 248, 170, 198, 252, 42, 16, 1, 146, + 96, 218, 159, 44, 41, 40, 94, 247, 147, 11, 255, 68, 40, 177, ], - quorum_indexes: vec![0, 1], }, - }; - let result = verifier.verify_security_params(&cert).await; - assert!(result.is_ok()); - } + inclusion_proof: vec![ + 203, 160, 237, 48, 117, 255, 75, 254, 117, 144, 164, 77, 29, 146, 36, 48, 190, 140, + 50, 100, 144, 237, 125, 125, 75, 54, 210, 247, 147, 23, 48, 189, 120, 4, 125, 123, + 195, 244, 207, 239, 145, 109, 0, 21, 11, 162, 109, 79, 192, 100, 138, 157, 203, 22, + 17, 114, 234, 72, 174, 231, 209, 133, 99, 118, 201, 160, 137, 128, 112, 84, 34, + 136, 174, 139, 96, 26, 246, 148, 134, 52, 200, 229, 160, 145, 5, 120, 18, 187, 51, + 11, 109, 91, 237, 171, 215, 207, 90, 95, 146, 54, 135, 166, 66, 157, 255, 237, 69, + 183, 141, 45, 162, 145, 71, 16, 87, 184, 120, 84, 156, 220, 159, 4, 99, 48, 191, + 203, 136, 112, 127, 226, 192, 184, 110, 6, 177, 182, 109, 207, 197, 239, 161, 132, + 17, 89, 56, 137, 205, 202, 101, 97, 60, 162, 253, 23, 169, 75, 236, 211, 126, 121, + 132, 191, 68, 167, 200, 16, 154, 149, 202, 197, 7, 191, 26, 8, 67, 3, 37, 137, 16, + 153, 30, 209, 238, 53, 233, 148, 198, 253, 94, 216, 73, 25, 190, 205, 132, 208, + 255, 219, 170, 98, 17, 160, 179, 183, 200, 17, 99, 36, 130, 216, 223, 72, 222, 250, + 73, 78, 79, 72, 253, 105, 245, 84, 244, 196, + ], + quorum_indexes: vec![0, 1], + }, + }; + let result = verifier.verify_security_params(&cert).await; + assert!(result.is_ok()); } From d312b513e55f23caf25a97913f0d41ac23ca108b Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 15:50:56 -0300 Subject: [PATCH 53/97] wrap `G1Affine` inside of `Box` / fix clippy --- core/node/da_clients/src/eigen/verifier/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/node/da_clients/src/eigen/verifier/mod.rs b/core/node/da_clients/src/eigen/verifier/mod.rs index 555a27cfb24a..c7367beb0475 100644 --- a/core/node/da_clients/src/eigen/verifier/mod.rs +++ b/core/node/da_clients/src/eigen/verifier/mod.rs @@ -74,8 +74,8 @@ pub enum VerificationError { WrongProof, #[error("Different commitments: expected {expected:?}, got {actual:?}")] DifferentCommitments { - expected: G1Affine, - actual: G1Affine, + expected: Box, + actual: Box, }, #[error("Different roots: expected {expected:?}, got {actual:?}")] DifferentRoots { expected: String, actual: String }, @@ -214,8 +214,8 @@ impl Verifier { } if actual_commitment != expected_commitment { return Err(VerificationError::DifferentCommitments { - expected: expected_commitment, - actual: actual_commitment, + expected: Box::new(expected_commitment), + actual: Box::new(actual_commitment), }); } Ok(()) From 57f608ec0386e50e4851586fa3cc83f15c35f997 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 17:04:37 -0300 Subject: [PATCH 54/97] use `SensitiveUrl` for `eigenda_eth_rpc` --- core/bin/zksync_server/src/node_builder.rs | 2 +- core/lib/config/src/configs/da_client/eigen.rs | 8 +++++--- core/lib/env_config/src/da_client.rs | 5 ++++- core/lib/protobuf_config/src/da_client.rs | 10 ++++++++-- core/node/da_clients/src/eigen/sdk.rs | 4 ++-- core/node/da_clients/src/eigen/verifier/mod.rs | 3 ++- core/node/da_clients/src/eigen/verifier/tests.rs | 4 ++-- 7 files changed, 24 insertions(+), 12 deletions(-) diff --git a/core/bin/zksync_server/src/node_builder.rs b/core/bin/zksync_server/src/node_builder.rs index 8350d2f0b422..cfafbbcd3ed4 100644 --- a/core/bin/zksync_server/src/node_builder.rs +++ b/core/bin/zksync_server/src/node_builder.rs @@ -550,7 +550,7 @@ impl MainNodeBuilder { (DAClientConfig::Eigen(mut config), DataAvailabilitySecrets::Eigen(secret)) => { if config.eigenda_eth_rpc.is_none() { - config.eigenda_eth_rpc = Some(l1_secrets.l1_rpc_url.expose_str().to_string()); + config.eigenda_eth_rpc = Some(l1_secrets.l1_rpc_url); } self.node.add_layer(EigenWiringLayer::new(config, secret)); } diff --git a/core/lib/config/src/configs/da_client/eigen.rs b/core/lib/config/src/configs/da_client/eigen.rs index cf92e7e969bf..15a17615b600 100644 --- a/core/lib/config/src/configs/da_client/eigen.rs +++ b/core/lib/config/src/configs/da_client/eigen.rs @@ -1,5 +1,7 @@ +use std::str::FromStr; + use serde::Deserialize; -use zksync_basic_types::{secrets::PrivateKey, Address, H160}; +use zksync_basic_types::{secrets::PrivateKey, url::SensitiveUrl, Address, H160}; /// Default address of the EigenDA service manager contract deployed on Holesky. const DEFAULT_EIGENDA_SVC_MANAGER_ADDRESS: H160 = H160([ @@ -16,7 +18,7 @@ pub struct EigenConfig { /// a value less or equal to 0 means that the disperser will not wait for finalization pub settlement_layer_confirmation_depth: u32, /// URL of the Ethereum RPC server - pub eigenda_eth_rpc: Option, + pub eigenda_eth_rpc: Option, //Option, /// Address of the service manager contract pub eigenda_svc_manager_address: Address, /// Wait for the blob to be finalized before returning the response @@ -36,7 +38,7 @@ impl Default for EigenConfig { Self { disperser_rpc: "https://disperser-holesky.eigenda.xyz:443".to_string(), settlement_layer_confirmation_depth: 0, - eigenda_eth_rpc: Some("https://ethereum-holesky-rpc.publicnode.com".to_string()), + eigenda_eth_rpc: Some(SensitiveUrl::from_str("https://ethereum-holesky-rpc.publicnode.com").unwrap()), // Save to unwrap, never fails eigenda_svc_manager_address: DEFAULT_EIGENDA_SVC_MANAGER_ADDRESS, wait_for_finalization: false, authenticated: false, diff --git a/core/lib/env_config/src/da_client.rs b/core/lib/env_config/src/da_client.rs index 51fdd06877e9..6d026abae217 100644 --- a/core/lib/env_config/src/da_client.rs +++ b/core/lib/env_config/src/da_client.rs @@ -90,6 +90,9 @@ impl FromEnv for DataAvailabilitySecrets { #[cfg(test)] mod tests { + use std::str::FromStr; + + use zksync_basic_types::url::SensitiveUrl; use zksync_config::{ configs::{ da_client::{ @@ -266,7 +269,7 @@ mod tests { DAClientConfig::Eigen(EigenConfig { disperser_rpc: "http://localhost:8080".to_string(), settlement_layer_confirmation_depth: 0, - eigenda_eth_rpc: Some("http://localhost:8545".to_string()), + eigenda_eth_rpc: Some(SensitiveUrl::from_str("http://localhost:8545").unwrap()), eigenda_svc_manager_address: "0x0000000000000000000000000000000000000123" .parse() .unwrap(), diff --git a/core/lib/protobuf_config/src/da_client.rs b/core/lib/protobuf_config/src/da_client.rs index a236718d3138..823c899b6307 100644 --- a/core/lib/protobuf_config/src/da_client.rs +++ b/core/lib/protobuf_config/src/da_client.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use anyhow::Context; use zksync_config::configs::{ self, @@ -9,6 +11,7 @@ use zksync_config::configs::{ }, }; use zksync_protobuf::{required, ProtoRepr}; +use zksync_types::url::SensitiveUrl; use crate::{ parse_h160, @@ -66,7 +69,9 @@ impl ProtoRepr for proto::DataAvailabilityClient { &conf.settlement_layer_confirmation_depth, ) .context("settlement_layer_confirmation_depth")?, - eigenda_eth_rpc: required(&conf.eigenda_eth_rpc).ok().cloned(), + eigenda_eth_rpc: Some(SensitiveUrl::from_str( + required(&conf.eigenda_eth_rpc).context("eigenda_eth_rpc")?, + )?), eigenda_svc_manager_address: required(&conf.eigenda_svc_manager_address) .and_then(|x| parse_h160(x)) .context("eigenda_svc_manager_address")?, @@ -118,7 +123,8 @@ impl ProtoRepr for proto::DataAvailabilityClient { settlement_layer_confirmation_depth: Some( config.settlement_layer_confirmation_depth, ), - eigenda_eth_rpc: config.eigenda_eth_rpc.clone(), + // eigenda_eth_rpc: config.eigenda_eth_rpc.into(), + eigenda_eth_rpc: Some(format!("{:?}", config.eigenda_eth_rpc)), eigenda_svc_manager_address: Some(format!( "{:?}", config.eigenda_svc_manager_address diff --git a/core/node/da_clients/src/eigen/sdk.rs b/core/node/da_clients/src/eigen/sdk.rs index 157607a27ea4..71c7fbeabbd9 100644 --- a/core/node/da_clients/src/eigen/sdk.rs +++ b/core/node/da_clients/src/eigen/sdk.rs @@ -12,7 +12,7 @@ use url::Url; use zksync_config::EigenConfig; use zksync_da_client::types::DAError; use zksync_eth_client::clients::PKSigningClient; -use zksync_types::{url::SensitiveUrl, K256PrivateKey, SLChainId}; +use zksync_types::{K256PrivateKey, SLChainId}; use zksync_web3_decl::client::{Client, DynClient, L1}; use super::{ @@ -69,7 +69,7 @@ impl RawEigenClient { chain_id: config.chain_id, }; - let url = SensitiveUrl::from_str(&verifier_config.rpc_url)?; + let url = verifier_config.rpc_url.clone(); let query_client: Client = Client::http(url)?.build(); let query_client = Box::new(query_client) as Box>; let signing_client = PKSigningClient::new_raw( diff --git a/core/node/da_clients/src/eigen/verifier/mod.rs b/core/node/da_clients/src/eigen/verifier/mod.rs index c7367beb0475..66067b0075b4 100644 --- a/core/node/da_clients/src/eigen/verifier/mod.rs +++ b/core/node/da_clients/src/eigen/verifier/mod.rs @@ -8,6 +8,7 @@ use url::Url; use zksync_basic_types::web3::CallRequest; use zksync_eth_client::{clients::PKSigningClient, EnrichedClientError, EnrichedClientResult}; use zksync_types::{ + url::SensitiveUrl, web3::{self, BlockId, BlockNumber}, Address, U256, U64, }; @@ -98,7 +99,7 @@ pub enum VerificationError { /// Configuration for the verifier used for authenticated dispersals #[derive(Debug, Clone)] pub struct VerifierConfig { - pub rpc_url: String, + pub rpc_url: SensitiveUrl, pub svc_manager_addr: Address, pub max_blob_size: u32, pub g1_url: Url, diff --git a/core/node/da_clients/src/eigen/verifier/tests.rs b/core/node/da_clients/src/eigen/verifier/tests.rs index 505756910143..d53bb669a8b5 100644 --- a/core/node/da_clients/src/eigen/verifier/tests.rs +++ b/core/node/da_clients/src/eigen/verifier/tests.rs @@ -19,7 +19,7 @@ use crate::eigen::{ fn get_verifier_config() -> VerifierConfig { VerifierConfig { - rpc_url: "https://ethereum-holesky-rpc.publicnode.com".to_string(), + rpc_url: SensitiveUrl::from_str("https://ethereum-holesky-rpc.publicnode.com").unwrap(), svc_manager_addr: Address::from_str("0xD4A7E1Bd8015057293f0D0A557088c286942e84b").unwrap(), max_blob_size: 2 * 1024 * 1024, g1_url: Url::parse("https://github.com/Layr-Labs/eigenda-proxy/raw/2fd70b99ef5bf137d7bbca3461cf9e1f2c899451/resources/g1.point").unwrap(), @@ -64,7 +64,7 @@ impl VerifierClient for MockVerifierClient { } fn create_remote_signing_client(cfg: VerifierConfig) -> PKSigningClient { - let url = SensitiveUrl::from_str(&cfg.rpc_url).unwrap(); + let url = cfg.rpc_url; let query_client: Client = Client::http(url).unwrap().build(); let query_client = Box::new(query_client) as Box>; PKSigningClient::new_raw( From 183281973b5d81610a0b349b8f21a44a81e65b9a Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 17:16:16 -0300 Subject: [PATCH 55/97] refactor `save_point` fn --- core/node/da_clients/src/eigen/verifier/mod.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/core/node/da_clients/src/eigen/verifier/mod.rs b/core/node/da_clients/src/eigen/verifier/mod.rs index 66067b0075b4..c8be274c1f38 100644 --- a/core/node/da_clients/src/eigen/verifier/mod.rs +++ b/core/node/da_clients/src/eigen/verifier/mod.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, path::Path, sync::Arc}; +use std::{collections::HashMap, sync::Arc}; use ark_bn254::{Fq, G1Affine}; use ethabi::{encode, ParamType, Token}; @@ -126,7 +126,7 @@ impl Verifier { pub const G2POINT: &'static str = "g2.point.powerOf2"; pub const POINT_SIZE: u32 = 32; - async fn save_point(url: Url, point: String) -> Result<(), VerificationError> { + async fn save_point(url: Url, point: &str) -> Result<(), VerificationError> { let response = reqwest::get(url) .await .map_err(|e| VerificationError::LinkError(e.to_string()))?; @@ -135,9 +135,8 @@ impl Verifier { "Failed to get point".to_string(), )); } - let path = format!("./{}", point); - let path = Path::new(&path); - let mut file = File::create(path) + + let mut file = File::create(point) .await .map_err(|e| VerificationError::LinkError(e.to_string()))?; let content = response @@ -151,8 +150,8 @@ impl Verifier { } async fn save_points(url_g1: Url, url_g2: Url) -> Result { - Self::save_point(url_g1, Self::G1POINT.to_string()).await?; - Self::save_point(url_g2, Self::G2POINT.to_string()).await?; + Self::save_point(url_g1, Self::G1POINT).await?; + Self::save_point(url_g2, Self::G2POINT).await?; Ok(".".to_string()) } From 3a97228adb0ff87323f94cf8bf3a003c70d0d71a Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 17:37:32 -0300 Subject: [PATCH 56/97] remove retriable error wrapping --- core/node/da_clients/src/eigen/client.rs | 7 ++----- core/node/da_clients/src/eigen/sdk.rs | 15 +++------------ 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/core/node/da_clients/src/eigen/client.rs b/core/node/da_clients/src/eigen/client.rs index c72f910fb5e6..e9e49ee49a77 100644 --- a/core/node/da_clients/src/eigen/client.rs +++ b/core/node/da_clients/src/eigen/client.rs @@ -90,16 +90,13 @@ mod tests { use backon::{ConstantBuilder, Retryable}; use serial_test::file_serial; use zksync_config::{configs::da_client::eigen::EigenSecrets, EigenConfig}; - use zksync_da_client::{ - types::{DAError, DispatchResponse}, - DataAvailabilityClient, - }; + use zksync_da_client::{types::DispatchResponse, DataAvailabilityClient}; use zksync_types::secrets::PrivateKey; use crate::eigen::{blob_info::BlobInfo, EigenClient, GetBlobData}; impl EigenClient { - async fn get_blob_data(&self, blob_id: BlobInfo) -> anyhow::Result, DAError> { + async fn get_blob_data(&self, blob_id: BlobInfo) -> anyhow::Result> { self.client.get_blob_data(blob_id).await } diff --git a/core/node/da_clients/src/eigen/sdk.rs b/core/node/da_clients/src/eigen/sdk.rs index 71c7fbeabbd9..955c68ea215e 100644 --- a/core/node/da_clients/src/eigen/sdk.rs +++ b/core/node/da_clients/src/eigen/sdk.rs @@ -10,7 +10,6 @@ use tonic::{ }; use url::Url; use zksync_config::EigenConfig; -use zksync_da_client::types::DAError; use zksync_eth_client::clients::PKSigningClient; use zksync_types::{K256PrivateKey, SLChainId}; use zksync_web3_decl::client::{Client, DynClient, L1}; @@ -333,9 +332,8 @@ impl RawEigenClient { } } - pub async fn get_blob_data(&self, blob_info: BlobInfo) -> Result, DAError> { + pub async fn get_blob_data(&self, blob_info: BlobInfo) -> anyhow::Result> { use anyhow::anyhow; - use zksync_da_client::types::DAError; let blob_index = blob_info.blob_verification_proof.blob_index; let batch_header_hash = blob_info @@ -349,18 +347,11 @@ impl RawEigenClient { batch_header_hash, blob_index, }) - .await - .map_err(|e| DAError { - error: anyhow!(e), - is_retriable: true, - })? + .await? .into_inner(); if get_response.data.is_empty() { - return Err(DAError { - error: anyhow!("Failed to get blob data"), - is_retriable: false, - }); + return Err(anyhow!("Failed to get blob data")); } let data = remove_empty_byte_from_padded_bytes(&get_response.data); From 5c4309d17a698f70561327c48d8db9f93e1531ff Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 18:34:25 -0300 Subject: [PATCH 57/97] replace `PKSigningClient` for simpler L1 Client --- core/node/da_clients/src/eigen/sdk.rs | 15 +--- .../node/da_clients/src/eigen/verifier/mod.rs | 10 +-- .../da_clients/src/eigen/verifier/tests.rs | 74 ++++++++----------- 3 files changed, 33 insertions(+), 66 deletions(-) diff --git a/core/node/da_clients/src/eigen/sdk.rs b/core/node/da_clients/src/eigen/sdk.rs index 955c68ea215e..51c8685f0bcb 100644 --- a/core/node/da_clients/src/eigen/sdk.rs +++ b/core/node/da_clients/src/eigen/sdk.rs @@ -10,8 +10,6 @@ use tonic::{ }; use url::Url; use zksync_config::EigenConfig; -use zksync_eth_client::clients::PKSigningClient; -use zksync_types::{K256PrivateKey, SLChainId}; use zksync_web3_decl::client::{Client, DynClient, L1}; use super::{ @@ -64,24 +62,13 @@ impl RawEigenClient { g1_url: Url::parse(&config.g1_url)?, g2_url: Url::parse(&config.g2_url)?, settlement_layer_confirmation_depth: config.settlement_layer_confirmation_depth, - private_key: hex::encode(private_key.secret_bytes()), - chain_id: config.chain_id, }; let url = verifier_config.rpc_url.clone(); let query_client: Client = Client::http(url)?.build(); let query_client = Box::new(query_client) as Box>; - let signing_client = PKSigningClient::new_raw( - K256PrivateKey::from_bytes(zksync_types::H256::from_str( - &verifier_config.private_key, - )?)?, - verifier_config.svc_manager_addr, - Verifier::DEFAULT_PRIORITY_FEE_PER_GAS, - SLChainId(verifier_config.chain_id), - query_client, - ); - let verifier = Verifier::new(verifier_config, Arc::new(signing_client)) + let verifier = Verifier::new(verifier_config, Arc::new(query_client)) .await .context("Failed to create verifier")?; Ok(RawEigenClient { diff --git a/core/node/da_clients/src/eigen/verifier/mod.rs b/core/node/da_clients/src/eigen/verifier/mod.rs index c8be274c1f38..79f388414e2b 100644 --- a/core/node/da_clients/src/eigen/verifier/mod.rs +++ b/core/node/da_clients/src/eigen/verifier/mod.rs @@ -6,12 +6,13 @@ use rust_kzg_bn254::{blob::Blob, kzg::Kzg, polynomial::PolynomialFormat}; use tokio::{fs::File, io::AsyncWriteExt}; use url::Url; use zksync_basic_types::web3::CallRequest; -use zksync_eth_client::{clients::PKSigningClient, EnrichedClientError, EnrichedClientResult}; +use zksync_eth_client::{EnrichedClientError, EnrichedClientResult, EthInterface}; use zksync_types::{ url::SensitiveUrl, web3::{self, BlockId, BlockNumber}, Address, U256, U64, }; +use zksync_web3_decl::client::{DynClient, L1}; use super::blob_info::{BatchHeader, BlobHeader, BlobInfo, BlobQuorumParam, G1Commitment}; @@ -32,7 +33,7 @@ pub trait VerifierClient: Sync + Send + std::fmt::Debug { } #[async_trait::async_trait] -impl VerifierClient for PKSigningClient { +impl VerifierClient for Box> { async fn block_number(&self) -> EnrichedClientResult { self.as_ref().block_number().await } @@ -60,9 +61,6 @@ pub enum ServiceManagerError { EnrichedClient(#[from] EnrichedClientError), #[error("Decoding error: {0}")] Decoding(String), - #[cfg(test)] - #[error("Parsing error: {0}")] - Parsing(String), } #[derive(Debug, thiserror::Error)] @@ -105,8 +103,6 @@ pub struct VerifierConfig { pub g1_url: Url, pub g2_url: Url, pub settlement_layer_confirmation_depth: u32, - pub private_key: String, - pub chain_id: u64, } /// Verifier used to verify the integrity of the blob info diff --git a/core/node/da_clients/src/eigen/verifier/tests.rs b/core/node/da_clients/src/eigen/verifier/tests.rs index d53bb669a8b5..3946809eb915 100644 --- a/core/node/da_clients/src/eigen/verifier/tests.rs +++ b/core/node/da_clients/src/eigen/verifier/tests.rs @@ -1,11 +1,11 @@ use std::{collections::HashMap, str::FromStr, sync::Arc}; use url::Url; -use zksync_eth_client::{clients::PKSigningClient, EnrichedClientResult}; +use zksync_eth_client::EnrichedClientResult; use zksync_types::{ url::SensitiveUrl, web3::{BlockId, Bytes, CallRequest}, - Address, K256PrivateKey, SLChainId, H160, U64, + Address, H160, U64, }; use zksync_web3_decl::client::{Client, DynClient, L1}; @@ -14,7 +14,7 @@ use crate::eigen::{ BatchHeader, BatchMetadata, BlobHeader, BlobInfo, BlobQuorumParam, BlobVerificationProof, G1Commitment, }, - verifier::{ServiceManagerError, Verifier, VerifierClient, VerifierConfig}, + verifier::{Verifier, VerifierClient, VerifierConfig}, }; fn get_verifier_config() -> VerifierConfig { @@ -25,9 +25,6 @@ fn get_verifier_config() -> VerifierConfig { g1_url: Url::parse("https://github.com/Layr-Labs/eigenda-proxy/raw/2fd70b99ef5bf137d7bbca3461cf9e1f2c899451/resources/g1.point").unwrap(), g2_url: Url::parse("https://github.com/Layr-Labs/eigenda-proxy/raw/2fd70b99ef5bf137d7bbca3461cf9e1f2c899451/resources/g2.point.powerOf2").unwrap(), settlement_layer_confirmation_depth: 0, - private_key: "0xd08aa7ae1bb5ddd46c3c2d8cdb5894ab9f54dec467233686ca42629e826ac4c6" - .to_string(), - chain_id: 17000, } } @@ -63,31 +60,18 @@ impl VerifierClient for MockVerifierClient { } } -fn create_remote_signing_client(cfg: VerifierConfig) -> PKSigningClient { +fn create_remote_query_client(cfg: VerifierConfig) -> Box> { let url = cfg.rpc_url; let query_client: Client = Client::http(url).unwrap().build(); - let query_client = Box::new(query_client) as Box>; - PKSigningClient::new_raw( - K256PrivateKey::from_bytes( - zksync_types::H256::from_str(&cfg.private_key) - .map_err(|e| ServiceManagerError::Parsing(e.to_string())) - .unwrap(), - ) - .map_err(|e| ServiceManagerError::Parsing(e.to_string())) - .unwrap(), - cfg.svc_manager_addr, - Verifier::DEFAULT_PRIORITY_FEE_PER_GAS, - SLChainId(cfg.chain_id), - query_client, - ) + Box::new(query_client) as Box> } #[ignore = "depends on external RPC"] #[tokio::test] async fn test_verify_commitment() { let cfg = get_verifier_config(); - let signing_client = create_remote_signing_client(cfg.clone()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let query_client = create_remote_query_client(cfg.clone()); + let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let commitment = G1Commitment { x: vec![ 22, 11, 176, 29, 82, 48, 62, 49, 51, 119, 94, 17, 156, 142, 248, 96, 240, 183, 134, 85, @@ -108,8 +92,8 @@ async fn test_verify_commitment() { #[tokio::test] async fn test_verify_commitment_mocked() { let cfg = get_verifier_config(); - let signing_client = MockVerifierClient::new(HashMap::new()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let query_client = MockVerifierClient::new(HashMap::new()); + let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let commitment = G1Commitment { x: vec![ 22, 11, 176, 29, 82, 48, 62, 49, 51, 119, 94, 17, 156, 142, 248, 96, 240, 183, 134, 85, @@ -129,8 +113,8 @@ async fn test_verify_commitment_mocked() { #[tokio::test] async fn test_verify_merkle_proof() { let cfg = get_verifier_config(); - let signing_client = create_remote_signing_client(cfg.clone()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let query_client = create_remote_query_client(cfg.clone()); + let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let cert = BlobInfo { blob_header: BlobHeader { commitment: G1Commitment { @@ -210,8 +194,8 @@ async fn test_verify_merkle_proof() { #[tokio::test] async fn test_verify_merkle_proof_mocked() { let cfg = get_verifier_config(); - let signing_client = MockVerifierClient::new(HashMap::new()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let query_client = MockVerifierClient::new(HashMap::new()); + let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let cert = BlobInfo { blob_header: BlobHeader { commitment: G1Commitment { @@ -290,8 +274,8 @@ async fn test_verify_merkle_proof_mocked() { #[tokio::test] async fn test_hash_blob_header() { let cfg = get_verifier_config(); - let signing_client = create_remote_signing_client(cfg.clone()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let query_client = create_remote_query_client(cfg.clone()); + let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let blob_header = BlobHeader { commitment: G1Commitment { x: vec![ @@ -329,8 +313,8 @@ async fn test_hash_blob_header() { #[tokio::test] async fn test_hash_blob_header_mocked() { let cfg = get_verifier_config(); - let signing_client = MockVerifierClient::new(HashMap::new()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let query_client = MockVerifierClient::new(HashMap::new()); + let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let blob_header = BlobHeader { commitment: G1Commitment { x: vec![ @@ -367,8 +351,8 @@ async fn test_hash_blob_header_mocked() { #[tokio::test] async fn test_inclusion_proof() { let cfg = get_verifier_config(); - let signing_client = create_remote_signing_client(cfg.clone()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let query_client = create_remote_query_client(cfg.clone()); + let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let proof = hex::decode("c455c1ea0e725d7ea3e5f29e9f48be8fc2787bb0a914d5a86710ba302c166ac4f626d76f67f1055bb960a514fb8923af2078fd84085d712655b58a19612e8cd15c3e4ac1cef57acde3438dbcf63f47c9fefe1221344c4d5c1a4943dd0d1803091ca81a270909dc0e146841441c9bd0e08e69ce6168181a3e4060ffacf3627480bec6abdd8d7bb92b49d33f180c42f49e041752aaded9c403db3a17b85e48a11e9ea9a08763f7f383dab6d25236f1b77c12b4c49c5cdbcbea32554a604e3f1d2f466851cb43fe73617b3d01e665e4c019bf930f92dea7394c25ed6a1e200d051fb0c30a2193c459f1cfef00bf1ba6656510d16725a4d1dc031cb759dbc90bab427b0f60ddc6764681924dda848824605a4f08b7f526fe6bd4572458c94e83fbf2150f2eeb28d3011ec921996dc3e69efa52d5fcf3182b20b56b5857a926aa66605808079b4d52c0c0cfe06923fa92e65eeca2c3e6126108e8c1babf5ac522f4d7").unwrap(); let leaf: [u8; 32] = hex::decode("f6106e6ae4631e68abe0fa898cedbe97dbae6c7efb1b088c5aa2e8b91190ff96") @@ -388,8 +372,8 @@ async fn test_inclusion_proof() { #[tokio::test] async fn test_inclusion_proof_mocked() { let cfg = get_verifier_config(); - let signing_client = MockVerifierClient::new(HashMap::new()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let query_client = MockVerifierClient::new(HashMap::new()); + let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let proof = hex::decode("c455c1ea0e725d7ea3e5f29e9f48be8fc2787bb0a914d5a86710ba302c166ac4f626d76f67f1055bb960a514fb8923af2078fd84085d712655b58a19612e8cd15c3e4ac1cef57acde3438dbcf63f47c9fefe1221344c4d5c1a4943dd0d1803091ca81a270909dc0e146841441c9bd0e08e69ce6168181a3e4060ffacf3627480bec6abdd8d7bb92b49d33f180c42f49e041752aaded9c403db3a17b85e48a11e9ea9a08763f7f383dab6d25236f1b77c12b4c49c5cdbcbea32554a604e3f1d2f466851cb43fe73617b3d01e665e4c019bf930f92dea7394c25ed6a1e200d051fb0c30a2193c459f1cfef00bf1ba6656510d16725a4d1dc031cb759dbc90bab427b0f60ddc6764681924dda848824605a4f08b7f526fe6bd4572458c94e83fbf2150f2eeb28d3011ec921996dc3e69efa52d5fcf3182b20b56b5857a926aa66605808079b4d52c0c0cfe06923fa92e65eeca2c3e6126108e8c1babf5ac522f4d7").unwrap(); let leaf: [u8; 32] = hex::decode("f6106e6ae4631e68abe0fa898cedbe97dbae6c7efb1b088c5aa2e8b91190ff96") @@ -408,8 +392,8 @@ async fn test_inclusion_proof_mocked() { #[tokio::test] async fn test_verify_batch() { let cfg = get_verifier_config(); - let signing_client = create_remote_signing_client(cfg.clone()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let query_client = create_remote_query_client(cfg.clone()); + let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let cert = BlobInfo { blob_header: BlobHeader { commitment: G1Commitment { @@ -511,8 +495,8 @@ async fn test_verify_batch_mocked() { mock_replies.insert(mock_req, mock_res); let cfg = get_verifier_config(); - let signing_client = MockVerifierClient::new(mock_replies); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let query_client = MockVerifierClient::new(mock_replies); + let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let cert = BlobInfo { blob_header: BlobHeader { commitment: G1Commitment { @@ -591,8 +575,8 @@ async fn test_verify_batch_mocked() { #[tokio::test] async fn test_verify_security_params() { let cfg = get_verifier_config(); - let signing_client = create_remote_signing_client(cfg.clone()); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let query_client = create_remote_query_client(cfg.clone()); + let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let cert = BlobInfo { blob_header: BlobHeader { commitment: G1Commitment { @@ -715,8 +699,8 @@ async fn test_verify_securityyy_params_mocked() { mock_replies.insert(mock_req, mock_res); let cfg = get_verifier_config(); - let signing_client = MockVerifierClient::new(mock_replies); - let verifier = Verifier::new(cfg, Arc::new(signing_client)).await.unwrap(); + let query_client = MockVerifierClient::new(mock_replies); + let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let cert = BlobInfo { blob_header: BlobHeader { commitment: G1Commitment { From a66e9eb1eb6b5c81ebab1db9f0bf9cb82d7f4241 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 17 Jan 2025 18:41:16 -0300 Subject: [PATCH 58/97] remove chain id from `EigenConfig` --- core/lib/config/src/configs/da_client/eigen.rs | 3 --- core/lib/env_config/src/da_client.rs | 2 -- core/lib/protobuf_config/src/da_client.rs | 2 -- core/lib/protobuf_config/src/proto/config/da_client.proto | 1 - 4 files changed, 8 deletions(-) diff --git a/core/lib/config/src/configs/da_client/eigen.rs b/core/lib/config/src/configs/da_client/eigen.rs index 15a17615b600..f2d5f30e0e55 100644 --- a/core/lib/config/src/configs/da_client/eigen.rs +++ b/core/lib/config/src/configs/da_client/eigen.rs @@ -29,8 +29,6 @@ pub struct EigenConfig { pub g1_url: String, /// Url to the file containing the G2 point used for KZG pub g2_url: String, - /// Chain ID of the Ethereum network - pub chain_id: u64, } impl Default for EigenConfig { @@ -44,7 +42,6 @@ impl Default for EigenConfig { authenticated: false, g1_url: "https://github.com/Layr-Labs/eigenda-proxy/raw/2fd70b99ef5bf137d7bbca3461cf9e1f2c899451/resources/g1.point".to_string(), g2_url: "https://github.com/Layr-Labs/eigenda-proxy/raw/2fd70b99ef5bf137d7bbca3461cf9e1f2c899451/resources/g2.point.powerOf2".to_string(), - chain_id: 19000, } } } diff --git a/core/lib/env_config/src/da_client.rs b/core/lib/env_config/src/da_client.rs index 6d026abae217..515cc9665809 100644 --- a/core/lib/env_config/src/da_client.rs +++ b/core/lib/env_config/src/da_client.rs @@ -259,7 +259,6 @@ mod tests { DA_AUTHENTICATED=false DA_G1_URL="resources1" DA_G2_URL="resources2" - DA_CHAIN_ID=1 "#; lock.set_env(config); @@ -277,7 +276,6 @@ mod tests { authenticated: false, g1_url: "resources1".to_string(), g2_url: "resources2".to_string(), - chain_id: 1 }) ); } diff --git a/core/lib/protobuf_config/src/da_client.rs b/core/lib/protobuf_config/src/da_client.rs index 823c899b6307..9c988a53dac3 100644 --- a/core/lib/protobuf_config/src/da_client.rs +++ b/core/lib/protobuf_config/src/da_client.rs @@ -80,7 +80,6 @@ impl ProtoRepr for proto::DataAvailabilityClient { authenticated: *required(&conf.authenticated).context("authenticated")?, g1_url: required(&conf.g1_url).context("g1_url")?.clone(), g2_url: required(&conf.g2_url).context("g2_url")?.clone(), - chain_id: *required(&conf.chain_id).context("chain_id")?, }), proto::data_availability_client::Config::ObjectStore(conf) => { ObjectStore(object_store_proto::ObjectStore::read(conf)?) @@ -133,7 +132,6 @@ impl ProtoRepr for proto::DataAvailabilityClient { authenticated: Some(config.authenticated), g1_url: Some(config.g1_url.clone()), g2_url: Some(config.g2_url.clone()), - chain_id: Some(config.chain_id), }), ObjectStore(config) => proto::data_availability_client::Config::ObjectStore( object_store_proto::ObjectStore::build(config), diff --git a/core/lib/protobuf_config/src/proto/config/da_client.proto b/core/lib/protobuf_config/src/proto/config/da_client.proto index cf4318c520d7..75f2e2410d0a 100644 --- a/core/lib/protobuf_config/src/proto/config/da_client.proto +++ b/core/lib/protobuf_config/src/proto/config/da_client.proto @@ -45,7 +45,6 @@ message EigenConfig { optional bool authenticated = 8; optional string g1_url = 9; optional string g2_url = 10; - optional uint64 chain_id = 11; reserved 1,2; reserved "rpc_node_url","inclusion_polling_interval_ms"; } From d9e911cfa6a1d4fdd49266c29918a61423210727 Mon Sep 17 00:00:00 2001 From: Anton Baliasnikov Date: Mon, 20 Jan 2025 09:40:32 +0000 Subject: [PATCH 59/97] chore: fix zkstack completion for zsh (#3495) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Fixes `zkstack` completion for `zsh` shell. ## Why ❔ For `zsh` shell default completion will fail with the following error: `command not found: compdef` because autocompletion is not activated by default. This PR adds the following to activate it on `zsh` in addition to `source` of the autocompletion script: ``` autoload -Uz compinit compinit ``` *[Related thread](https://unix.stackexchange.com/questions/339954/zsh-command-not-found-compinstall-compinit-compdef) ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- zkstack_cli/crates/zkstack/build.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/zkstack_cli/crates/zkstack/build.rs b/zkstack_cli/crates/zkstack/build.rs index e52e952bf730..d2d478f80227 100644 --- a/zkstack_cli/crates/zkstack/build.rs +++ b/zkstack_cli/crates/zkstack/build.rs @@ -112,15 +112,22 @@ impl ShellAutocomplete for clap_complete::Shell { .context(format!("could not read .{}rc", shell))?; if !shell_rc_content.contains("# zkstack completion") { - std::fs::write( - shell_rc, + let completion_snippet = if shell == "zsh" { + format!( + "{}\n# zkstack completion\nautoload -Uz compinit\ncompinit\nsource \"{}\"\n", + shell_rc_content, + completion_file.to_str().unwrap() + ) + } else { format!( "{}\n# zkstack completion\nsource \"{}\"\n", shell_rc_content, completion_file.to_str().unwrap() - ), - ) - .context(format!("could not write .{}rc", shell))?; + ) + }; + + std::fs::write(shell_rc, completion_snippet) + .context(format!("could not write .{}rc", shell))?; } } else { println!( From 6a0c1a6650d8c65de77a63a92273e7499717b1af Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Mon, 20 Jan 2025 21:09:11 +1100 Subject: [PATCH 60/97] test: make fee tests robust (#3504) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ A bunch of small improvements to make fee tests robust: * Upgrade ethers * Catch retryable networking issues in `ethers.FetchRequest` * Destroy providers on context teardown to avoid econnreset * Retry fee-related issues that can arise when transaction is rejected by mempool ## Why ❔ Fee tests are disruptively flaky right now ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- core/tests/recovery-test/package.json | 2 +- core/tests/revert-test/package.json | 2 +- core/tests/ts-integration/package.json | 2 +- .../tests/ts-integration/src/context-owner.ts | 18 +++++- core/tests/ts-integration/src/helpers.ts | 35 +++++++--- .../ts-integration/src/retry-provider.ts | 64 +++++++++++++------ core/tests/ts-integration/tests/fees.test.ts | 6 +- core/tests/upgrade-test/package.json | 2 +- yarn.lock | 45 +++++++------ 9 files changed, 124 insertions(+), 52 deletions(-) diff --git a/core/tests/recovery-test/package.json b/core/tests/recovery-test/package.json index 8b2ea7f054c0..28c469fc725d 100644 --- a/core/tests/recovery-test/package.json +++ b/core/tests/recovery-test/package.json @@ -23,7 +23,7 @@ "@types/node": "^18.19.15", "@types/node-fetch": "^2.5.7", "chai": "^4.3.4", - "ethers": "^6.7.1", + "ethers": "^6.13.5", "mocha": "^9.0.2", "mocha-steps": "^1.3.0", "node-fetch": "^2.6.1", diff --git a/core/tests/revert-test/package.json b/core/tests/revert-test/package.json index c3be63dff631..7dc2566eb0ef 100644 --- a/core/tests/revert-test/package.json +++ b/core/tests/revert-test/package.json @@ -24,7 +24,7 @@ "@types/node-fetch": "^2.5.7", "chai": "^4.3.4", "ethereumjs-abi": "^0.6.8", - "ethers": "^6.7.1", + "ethers": "^6.13.5", "mocha": "^9.0.2", "mocha-steps": "^1.3.0", "node-fetch": "^2.6.1", diff --git a/core/tests/ts-integration/package.json b/core/tests/ts-integration/package.json index 3362b9d6a89e..11d9c2d2e4ed 100644 --- a/core/tests/ts-integration/package.json +++ b/core/tests/ts-integration/package.json @@ -24,7 +24,7 @@ "chalk": "^4.0.0", "elliptic": "^6.5.5", "ethereumjs-abi": "^0.6.8", - "ethers": "^6.7.1", + "ethers": "^6.13.5", "hardhat": "=2.22.2", "jest": "^29.0.3", "jest-environment-node": "^29.0.3", diff --git a/core/tests/ts-integration/src/context-owner.ts b/core/tests/ts-integration/src/context-owner.ts index 57ca54da7b27..72d977cda6db 100644 --- a/core/tests/ts-integration/src/context-owner.ts +++ b/core/tests/ts-integration/src/context-owner.ts @@ -612,9 +612,25 @@ export class TestContextOwner { await this.waitForVmPlayground(); this.reporter.finishAction(); } - this.reporter.startAction(`Tearing down the context`); + this.reporter.startAction(`Collecting funds`); await this.collectFunds(); this.reporter.finishAction(); + this.reporter.startAction(`Destroying providers`); + // Destroy providers so that they drop potentially active connections to the node. Not doing so might cause + // unexpected network errors to propagate during node termination. + try { + this.l1Provider.destroy(); + } catch (err: any) { + // Catch any request cancellation errors that propagate here after destroying L1 provider + console.log(`Caught error while destroying L1 provider: ${err}`); + } + try { + this.l2Provider.destroy(); + } catch (err: any) { + // Catch any request cancellation errors that propagate here after destroying L2 provider + console.log(`Caught error while destroying L2 provider: ${err}`); + } + this.reporter.finishAction(); } catch (error: any) { // Report the issue to the console and mark the last action as failed. this.reporter.error(`An error occurred: ${error.message || error}`); diff --git a/core/tests/ts-integration/src/helpers.ts b/core/tests/ts-integration/src/helpers.ts index 88819c669655..758884e2be99 100644 --- a/core/tests/ts-integration/src/helpers.ts +++ b/core/tests/ts-integration/src/helpers.ts @@ -3,7 +3,6 @@ import * as zksync from 'zksync-ethers'; import * as ethers from 'ethers'; import * as hre from 'hardhat'; import { ZkSyncArtifact } from '@matterlabs/hardhat-zksync-solc/dist/src/types'; -import { TransactionReceipt } from 'ethers'; export const SYSTEM_CONTEXT_ADDRESS = '0x000000000000000000000000000000000000800b'; @@ -78,8 +77,8 @@ export async function anyTransaction(wallet: zksync.Wallet): Promise { const MAX_ATTEMPTS = 3; - let txResponse = null; - let txReceipt: TransactionReceipt | null = null; + let txResponse: ethers.TransactionResponse | null = null; + let txReceipt: ethers.TransactionReceipt | null = null; let nonce = Number(await wallet.getNonce()); for (let i = 0; i < MAX_ATTEMPTS; i++) { // Send a dummy transaction and wait for it to execute. We override `maxFeePerGas` as the default ethers behavior @@ -87,11 +86,31 @@ export async function waitForNewL1Batch(wallet: zksync.Wallet): Promise { + // Unlike `waitForTransaction` below, these errors are not wrapped as `EthersError` for some reason + if (e.message.match(/Not enough gas/)) { + console.log( + `Transaction did not have enough gas, likely gas price went up (attempt ${i + 1}/${MAX_ATTEMPTS})` + ); + return null; + } else if (e.message.match(/max fee per gas less than block base fee/)) { + console.log( + `Transaction's max fee per gas was lower than block base fee, likely gas price went up (attempt ${i + 1}/${MAX_ATTEMPTS})` + ); + return null; + } else { + return Promise.reject(e); + } + }); + if (!txResponse) { + continue; + } } else { console.log('Gas price has not gone up, waiting longer'); } diff --git a/core/tests/ts-integration/src/retry-provider.ts b/core/tests/ts-integration/src/retry-provider.ts index 4c89e0407b9e..9cf63da0638c 100644 --- a/core/tests/ts-integration/src/retry-provider.ts +++ b/core/tests/ts-integration/src/retry-provider.ts @@ -4,6 +4,22 @@ import { Reporter } from './reporter'; import { AugmentedTransactionResponse } from './transaction-response'; import { L1Provider, RetryableL1Wallet } from './l1-provider'; +// Error markers observed on stage so far. +const IGNORED_ERRORS = [ + 'timeout', + 'etimedout', + 'econnrefused', + 'econnreset', + 'bad gateway', + 'service temporarily unavailable', + 'nonetwork' +]; + +function isIgnored(err: any): boolean { + const errString: string = err.toString().toLowerCase(); + return IGNORED_ERRORS.some((sampleErr) => errString.indexOf(sampleErr) !== -1); +} + /** * RetryProvider retries every RPC request if it detects a timeout-related issue on the server side. */ @@ -11,17 +27,39 @@ export class RetryProvider extends zksync.Provider { private readonly reporter: Reporter; private readonly knownTransactionHashes: Set = new Set(); - constructor(_url?: string | { url: string; timeout: number }, network?: ethers.Networkish, reporter?: Reporter) { - let url; + constructor(_url: string | { url: string; timeout: number }, network?: ethers.Networkish, reporter?: Reporter) { + let fetchRequest: ethers.FetchRequest; if (typeof _url === 'object') { - const fetchRequest: ethers.FetchRequest = new ethers.FetchRequest(_url.url); + fetchRequest = new ethers.FetchRequest(_url.url); fetchRequest.timeout = _url.timeout; - url = fetchRequest; } else { - url = _url; + fetchRequest = new ethers.FetchRequest(_url); } + let defaultGetUrlFunc = ethers.FetchRequest.createGetUrlFunc(); + fetchRequest.getUrlFunc = async (req: ethers.FetchRequest, signal?: ethers.FetchCancelSignal) => { + // Retry network requests that failed because of temporary issues (such as timeout, econnreset). + for (let retry = 0; retry < 50; retry++) { + try { + const result = await defaultGetUrlFunc(req, signal); + // If we obtained result not from the first attempt, print a warning. + if (retry != 0) { + this.reporter?.debug(`RPC request ${req} took ${retry} retries to succeed`); + } + return result; + } catch (err: any) { + if (isIgnored(err)) { + // Error is related to timeouts. Sleep a bit and try again. + await zksync.utils.sleep(this.pollingInterval); + continue; + } + // Re-throw any non-timeout-related error. + throw err; + } + } + return Promise.reject(new Error(`Retried too many times, giving up on request=${req}`)); + }; - super(url, network); + super(fetchRequest, network); this.reporter = reporter ?? new Reporter(); } @@ -35,19 +73,7 @@ export class RetryProvider extends zksync.Provider { } return result; } catch (err: any) { - // Error markers observed on stage so far. - const ignoredErrors = [ - 'timeout', - 'etimedout', - 'econnrefused', - 'econnreset', - 'bad gateway', - 'service temporarily unavailable', - 'nonetwork' - ]; - const errString: string = err.toString().toLowerCase(); - const found = ignoredErrors.some((sampleErr) => errString.indexOf(sampleErr) !== -1); - if (found) { + if (isIgnored(err)) { // Error is related to timeouts. Sleep a bit and try again. await zksync.utils.sleep(this.pollingInterval); continue; diff --git a/core/tests/ts-integration/tests/fees.test.ts b/core/tests/ts-integration/tests/fees.test.ts index a111f5804852..f598cf49ce02 100644 --- a/core/tests/ts-integration/tests/fees.test.ts +++ b/core/tests/ts-integration/tests/fees.test.ts @@ -410,9 +410,13 @@ testFees('Test fees', function () { }); afterAll(async () => { - await mainNodeSpawner.killAndSpawnMainNode(); // Returning the pubdata price to the default one // Spawning with no options restores defaults. + await mainNodeSpawner.killAndSpawnMainNode(); + + // Wait for current batch to close so gas price returns to normal. + await waitForNewL1Batch(alice); + await testMaster.deinitialize(); __ZKSYNC_TEST_CONTEXT_OWNER__.setL2NodePid(mainNodeSpawner.mainNode!.proc.pid!); }); diff --git a/core/tests/upgrade-test/package.json b/core/tests/upgrade-test/package.json index 5bb23c36d3b8..57aefb7b7caa 100644 --- a/core/tests/upgrade-test/package.json +++ b/core/tests/upgrade-test/package.json @@ -23,7 +23,7 @@ "@types/node-fetch": "^2.5.7", "chai": "^4.3.4", "chai-as-promised": "^7.1.1", - "ethers": "^6.7.1", + "ethers": "^6.13.5", "mocha": "^9.0.2", "mocha-steps": "^1.3.0", "node-fetch": "^2.6.1", diff --git a/yarn.lock b/yarn.lock index 732577daeb68..eabdce369080 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2943,10 +2943,12 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.6.tgz#df929d1bb2eee5afdda598a41930fe50b43eaa6a" integrity sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ== -"@types/node@18.15.13": - version "18.15.13" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" - integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== +"@types/node@22.7.5": + version "22.7.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.5.tgz#cfde981727a7ab3611a481510b473ae54442b92b" + integrity sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ== + dependencies: + undici-types "~6.19.2" "@types/node@^10.0.3": version "10.17.60" @@ -5487,18 +5489,18 @@ ethers@^5.0.2, ethers@^5.7.0, ethers@^5.7.2, ethers@~5.7.0, ethers@~5.7.2: "@ethersproject/web" "5.7.1" "@ethersproject/wordlists" "5.7.0" -ethers@^6.7.1: - version "6.12.1" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.12.1.tgz#517ff6d66d4fd5433e38e903051da3e57c87ff37" - integrity sha512-j6wcVoZf06nqEcBbDWkKg8Fp895SS96dSnTCjiXT+8vt2o02raTn4Lo9ERUuIVU5bAjoPYeA+7ytQFexFmLuVw== +ethers@^6.13.5: + version "6.13.5" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.5.tgz#8c1d6ac988ac08abc3c1d8fabbd4b8b602851ac4" + integrity sha512-+knKNieu5EKRThQJWwqaJ10a6HE9sSehGeqWN65//wE7j47ZpFhKAnHB/JJFibwwg61I/koxaPsXbXpD/skNOQ== dependencies: "@adraffy/ens-normalize" "1.10.1" "@noble/curves" "1.2.0" "@noble/hashes" "1.3.2" - "@types/node" "18.15.13" + "@types/node" "22.7.5" aes-js "4.0.0-beta.5" - tslib "2.4.0" - ws "8.5.0" + tslib "2.7.0" + ws "8.17.1" ethers@~5.5.0: version "5.5.4" @@ -10791,10 +10793,10 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== +tslib@2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" @@ -11007,6 +11009,11 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +undici-types@~6.19.2: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + undici@^5.14.0: version "5.28.4" resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" @@ -11287,10 +11294,10 @@ ws@7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== -ws@8.5.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" - integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== +ws@8.17.1: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" + integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== ws@^7.4.6: version "7.5.9" From 731b8240abd4c0cfa42f2ce89c23f8ebf67e1bf2 Mon Sep 17 00:00:00 2001 From: Joonatan Saarhelo Date: Mon, 20 Jan 2025 11:06:34 +0000 Subject: [PATCH 61/97] feat(vm): Implement call tracing for fast VM (#2905) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ - Implements call tracing for the fast VM. - Enables call tracing for the batch VM executor (i.e., one used in the state keeper) if it is requested. Compares produced traces in the shadow VM mode. ## Why ❔ Call tracing is the last large missing part of the fast VM. --------- Co-authored-by: Alex Ostrovski --- core/lib/multivm/Cargo.toml | 2 +- .../multivm/src/tracers/call_tracer/mod.rs | 3 + .../src/tracers/call_tracer/vm_latest/mod.rs | 7 +- core/lib/multivm/src/versions/shadow/mod.rs | 2 +- core/lib/multivm/src/versions/shadow/tests.rs | 88 +++++- .../src/versions/testonly/call_tracer.rs | 292 ++++++++++++++++++ core/lib/multivm/src/versions/testonly/mod.rs | 13 +- .../src/versions/testonly/tester/mod.rs | 5 + core/lib/multivm/src/versions/vm_fast/mod.rs | 2 +- .../src/versions/vm_fast/tests/call_tracer.rs | 31 ++ .../multivm/src/versions/vm_fast/tests/mod.rs | 19 +- .../src/versions/vm_fast/tracers/calls.rs | 155 ++++++++++ .../versions/vm_fast/tracers/evm_deploy.rs | 4 +- .../src/versions/vm_fast/tracers/mod.rs | 6 +- .../versions/vm_fast/tracers/validation.rs | 8 +- .../lib/multivm/src/versions/vm_fast/utils.rs | 7 +- .../versions/vm_latest/tests/call_tracer.rs | 61 ++-- .../src/versions/vm_latest/tests/mod.rs | 16 +- core/lib/vm_executor/src/batch/factory.rs | 54 +++- core/lib/vm_interface/src/utils/shadow.rs | 72 ++++- .../api_server/src/web3/namespaces/debug.rs | 1 + .../state_keeper/src/executor/tests/mod.rs | 49 ++- .../state_keeper/src/executor/tests/tester.rs | 62 +--- core/node/state_keeper/src/testonly/mod.rs | 88 ++---- 24 files changed, 857 insertions(+), 190 deletions(-) create mode 100644 core/lib/multivm/src/versions/testonly/call_tracer.rs create mode 100644 core/lib/multivm/src/versions/vm_fast/tests/call_tracer.rs create mode 100644 core/lib/multivm/src/versions/vm_fast/tracers/calls.rs diff --git a/core/lib/multivm/Cargo.toml b/core/lib/multivm/Cargo.toml index 107a168e305a..325a6f3a99e3 100644 --- a/core/lib/multivm/Cargo.toml +++ b/core/lib/multivm/Cargo.toml @@ -40,5 +40,5 @@ assert_matches.workspace = true pretty_assertions.workspace = true rand.workspace = true test-casing.workspace = true -zksync_test_contracts.workspace = true zksync_eth_signer.workspace = true +zksync_test_contracts.workspace = true diff --git a/core/lib/multivm/src/tracers/call_tracer/mod.rs b/core/lib/multivm/src/tracers/call_tracer/mod.rs index 44f274876032..20beb10642d8 100644 --- a/core/lib/multivm/src/tracers/call_tracer/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/mod.rs @@ -17,6 +17,8 @@ pub mod vm_virtual_blocks; #[derive(Debug, Clone)] pub struct CallTracer { stack: Vec, + finished_calls: Vec, + result: Arc>>, max_stack_depth: usize, @@ -41,6 +43,7 @@ impl CallTracer { pub fn new(result: Arc>>) -> Self { Self { stack: vec![], + finished_calls: vec![], result, max_stack_depth: 0, max_near_calls: 0, diff --git a/core/lib/multivm/src/tracers/call_tracer/vm_latest/mod.rs b/core/lib/multivm/src/tracers/call_tracer/vm_latest/mod.rs index ed18a3eca47d..afa02d24f200 100644 --- a/core/lib/multivm/src/tracers/call_tracer/vm_latest/mod.rs +++ b/core/lib/multivm/src/tracers/call_tracer/vm_latest/mod.rs @@ -67,7 +67,9 @@ impl VmTracer for CallTracer { _bootloader_state: &BootloaderState, _stop_reason: VmExecutionStopReason, ) { - self.store_result() + let result = std::mem::take(&mut self.finished_calls); + let cell = self.result.as_ref(); + cell.set(result).unwrap(); } } @@ -191,7 +193,6 @@ impl CallTracer { .farcall .parent_gas .saturating_sub(state.vm_local_state.callstack.current.ergs_remaining as u64); - self.save_output_latest(state, memory, ret_opcode, &mut current_call.farcall); // If there is a parent call, push the current call to it @@ -199,7 +200,7 @@ impl CallTracer { if let Some(parent_call) = self.stack.last_mut() { parent_call.farcall.calls.push(current_call.farcall); } else { - self.push_call_and_update_stats(current_call.farcall, current_call.near_calls_after); + self.finished_calls.push(current_call.farcall); } } } diff --git a/core/lib/multivm/src/versions/shadow/mod.rs b/core/lib/multivm/src/versions/shadow/mod.rs index 1ad5bdba5a7b..ee1eb02b0dd5 100644 --- a/core/lib/multivm/src/versions/shadow/mod.rs +++ b/core/lib/multivm/src/versions/shadow/mod.rs @@ -25,7 +25,7 @@ use crate::{ mod tests; type ReferenceVm = vm_latest::Vm, HistoryEnabled>; -type ShadowedFastVm = crate::vm_instance::ShadowedFastVm; +type ShadowedFastVm = crate::vm_instance::ShadowedFastVm; fn hash_block(block_env: L2BlockEnv, tx_hashes: &[H256]) -> H256 { let mut hasher = L2BlockHasher::new( diff --git a/core/lib/multivm/src/versions/shadow/tests.rs b/core/lib/multivm/src/versions/shadow/tests.rs index 2d3dd5d3ae30..dc7417ad1259 100644 --- a/core/lib/multivm/src/versions/shadow/tests.rs +++ b/core/lib/multivm/src/versions/shadow/tests.rs @@ -1,20 +1,30 @@ //! Unit tests from the `testonly` test suite. -use std::{collections::HashSet, rc::Rc}; +use std::{collections::HashSet, fmt, rc::Rc}; use zksync_types::{writes::StateDiffRecord, StorageKey, Transaction, H256, U256}; +use zksync_vm2::interface::Tracer; +use zksync_vm_interface::{ + utils::{CheckDivergence, DivergenceErrors}, + Call, +}; use super::ShadowedFastVm; use crate::{ interface::{ pubdata::{PubdataBuilder, PubdataInput}, + storage::InMemoryStorage, utils::{ShadowMut, ShadowRef}, CurrentExecutionState, L2BlockEnv, VmExecutionResultAndLogs, }, - versions::testonly::TestedVm, + versions::testonly::{TestedVm, TestedVmWithCallTracer}, + vm_fast, }; -impl TestedVm for ShadowedFastVm { +impl TestedVm for ShadowedFastVm +where + Tr: Tracer + Default + fmt::Debug + 'static, +{ type StateDump = (); fn dump_state(&self) -> Self::StateDump { @@ -135,6 +145,44 @@ impl TestedVm for ShadowedFastVm { } } +#[derive(Debug)] +struct ExecutionResultAndTraces { + result: VmExecutionResultAndLogs, + traces: Vec, +} + +impl From<(VmExecutionResultAndLogs, Vec)> for ExecutionResultAndTraces { + fn from((result, traces): (VmExecutionResultAndLogs, Vec)) -> Self { + Self { result, traces } + } +} + +impl From for (VmExecutionResultAndLogs, Vec) { + fn from(value: ExecutionResultAndTraces) -> Self { + (value.result, value.traces) + } +} + +impl CheckDivergence for ExecutionResultAndTraces { + fn check_divergence(&self, other: &Self) -> DivergenceErrors { + let mut errors = self.result.check_divergence(&other.result); + errors.extend(self.traces.check_divergence(&other.traces)); + errors + } +} + +impl TestedVmWithCallTracer for ShadowedFastVm { + fn inspect_with_call_tracer(&mut self) -> (VmExecutionResultAndLogs, Vec) { + self.get_custom_mut("inspect_with_call_tracer", |r| { + ExecutionResultAndTraces::from(match r { + ShadowMut::Main(vm) => vm.inspect_with_call_tracer(), + ShadowMut::Shadow(vm) => vm.inspect_with_call_tracer(), + }) + }) + .into() + } +} + mod block_tip { use crate::versions::testonly::block_tip::*; @@ -167,6 +215,40 @@ mod bytecode_publishing { } } +mod call_tracer { + use crate::versions::testonly::call_tracer::*; + + #[test] + fn basic_behavior() { + test_basic_behavior::>(); + } + + #[test] + fn transfer() { + test_transfer::>(); + } + + #[test] + fn reverted_tx() { + test_reverted_tx::>(); + } + + #[test] + fn reverted_deployment() { + test_reverted_deployment_tx::>(); + } + + #[test] + fn out_of_gas() { + test_out_of_gas::>(); + } + + #[test] + fn recursive_tx() { + test_recursive_tx::>(); + } +} + mod circuits { use crate::versions::testonly::circuits::*; diff --git a/core/lib/multivm/src/versions/testonly/call_tracer.rs b/core/lib/multivm/src/versions/testonly/call_tracer.rs new file mode 100644 index 000000000000..0e86cebec46e --- /dev/null +++ b/core/lib/multivm/src/versions/testonly/call_tracer.rs @@ -0,0 +1,292 @@ +//! Call tracer tests. These tests are special in the sense that it's too unreliable to keep fixtures +//! (since they can be invalidated by unrelated changes in system contracts, e.g. by changing consumed gas costs). + +use assert_matches::assert_matches; +use ethabi::Token; +use zksync_system_constants::MSG_VALUE_SIMULATOR_ADDRESS; +use zksync_test_contracts::{Account, LoadnextContractExecutionParams, TestContract, TxType}; +use zksync_types::{ + fee::Fee, utils::deployed_address_create, zk_evm_types::FarCallOpcode, Address, Execute, +}; + +use super::{ContractToDeploy, TestedVmWithCallTracer, VmTester, VmTesterBuilder}; +use crate::{ + interface::{Call, CallType, ExecutionResult, TxExecutionMode}, + vm_latest::constants::BATCH_COMPUTATIONAL_GAS_LIMIT, +}; + +fn check_call(call: &Call) { + assert!(call.gas_used < call.gas); + assert!(call.gas_used > call.calls.iter().map(|call| call.gas_used).sum::()); + + for subcall in &call.calls { + if subcall.r#type != CallType::Call(FarCallOpcode::Mimic) { + pretty_assertions::assert_eq!(call.to, subcall.from); + } + check_call(subcall); + } +} + +fn extract_single_call(calls: &[Call], filter: impl Fn(&Call) -> bool) -> &Call { + fn walk<'a>( + matching_call: &mut Option<&'a Call>, + calls: &'a [Call], + filter: &impl Fn(&Call) -> bool, + ) { + for call in calls { + if filter(call) { + if let Some(prev_call) = matching_call { + panic!("Multiple call match filter: {prev_call:?}, {call:?}"); + } + *matching_call = Some(call); + } + walk(matching_call, &call.calls, filter); + } + } + + let mut matching_call = None; + walk(&mut matching_call, calls, &filter); + matching_call.expect("no calls match the filter") +} + +pub(crate) fn test_basic_behavior() { + let bytecode = TestContract::counter().bytecode.to_vec(); + let address = Address::repeat_byte(0xA5); + let mut vm: VmTester = VmTesterBuilder::new() + .with_empty_in_memory_storage() + .with_rich_accounts(1) + .with_bootloader_gas_limit(BATCH_COMPUTATIONAL_GAS_LIMIT) + .with_execution_mode(TxExecutionMode::VerifyExecute) + .with_custom_contracts(vec![ContractToDeploy::new(bytecode, address)]) + .build(); + + let calldata = "7cf5dab00000000000000000000000000000000000000000000000000000000000000006"; + let calldata = hex::decode(calldata).unwrap(); + + let account = &mut vm.rich_accounts[0]; + let tx = account.get_l2_tx_for_execute( + Execute { + contract_address: Some(address), + calldata: calldata.clone(), + value: 0.into(), + factory_deps: vec![], + }, + None, + ); + + vm.vm.push_transaction(tx); + let (res, call_traces) = vm.vm.inspect_with_call_tracer(); + assert!(!res.result.is_failed(), "{:#?}", res.result); + + for call in &call_traces { + check_call(call); + assert_eq!(call.error, None); + assert_eq!(call.revert_reason, None); + } + + let call_to_contract = extract_single_call(&call_traces, |call| call.to == address); + assert_eq!(call_to_contract.from, account.address); + assert_eq!(call_to_contract.input, calldata); +} + +pub(crate) fn test_transfer() { + let mut vm: VmTester = VmTesterBuilder::new() + .with_empty_in_memory_storage() + .with_rich_accounts(1) + .with_bootloader_gas_limit(BATCH_COMPUTATIONAL_GAS_LIMIT) + .with_execution_mode(TxExecutionMode::VerifyExecute) + .build(); + + let recipient = Address::repeat_byte(0x23); + let value = 1_000_000_000.into(); + let account = &mut vm.rich_accounts[0]; + let transfer = account.get_l2_tx_for_execute( + Execute { + contract_address: Some(recipient), + calldata: vec![], + value, + factory_deps: vec![], + }, + None, + ); + + vm.vm.push_transaction(transfer); + let (res, call_traces) = vm.vm.inspect_with_call_tracer(); + assert!(!res.result.is_failed(), "{:#?}", res.result); + + for call in &call_traces { + check_call(call); + assert_eq!(call.error, None); + assert_eq!(call.revert_reason, None); + } + + let transfer_call = extract_single_call(&call_traces, |call| call.to == recipient); + assert_eq!(transfer_call.from, account.address); + assert_eq!(transfer_call.value, value); +} + +pub(crate) fn test_reverted_tx() { + let counter_address = Address::repeat_byte(0x23); + let mut vm: VmTester = VmTesterBuilder::new() + .with_empty_in_memory_storage() + .with_rich_accounts(1) + .with_bootloader_gas_limit(BATCH_COMPUTATIONAL_GAS_LIMIT) + .with_execution_mode(TxExecutionMode::VerifyExecute) + .with_custom_contracts(vec![ContractToDeploy::new( + TestContract::counter().bytecode.to_vec(), + counter_address, + )]) + .build(); + + let account = &mut vm.rich_accounts[0]; + let calldata = TestContract::counter() + .function("incrementWithRevert") + .encode_input(&[Token::Uint(1.into()), Token::Bool(true)]) + .unwrap(); + let reverted_tx = account.get_l2_tx_for_execute( + Execute { + contract_address: Some(counter_address), + calldata, + value: 0.into(), + factory_deps: vec![], + }, + None, + ); + + vm.vm.push_transaction(reverted_tx); + let (res, call_traces) = vm.vm.inspect_with_call_tracer(); + assert_matches!(&res.result, ExecutionResult::Revert { .. }); + + let call_to_contract = extract_single_call(&call_traces, |call| call.to == counter_address); + assert_eq!( + call_to_contract.revert_reason.as_ref().unwrap(), + "This method always reverts" + ); +} + +pub(crate) fn test_out_of_gas() { + let contract_address = Address::repeat_byte(0x23); + let mut vm: VmTester = VmTesterBuilder::new() + .with_empty_in_memory_storage() + .with_rich_accounts(1) + .with_execution_mode(TxExecutionMode::VerifyExecute) + .with_custom_contracts(vec![ContractToDeploy::new( + TestContract::expensive().bytecode.to_vec(), + contract_address, + )]) + .build(); + + let account = &mut vm.rich_accounts[0]; + let execute = Execute { + contract_address: Some(contract_address), + calldata: TestContract::expensive() + .function("expensive") + .encode_input(&[Token::Uint(1_000.into())]) + .unwrap(), + value: 0.into(), + factory_deps: vec![], + }; + let out_of_gas_tx = account.get_l2_tx_for_execute( + execute, + Some(Fee { + gas_limit: 500_000.into(), // insufficient gas + ..Account::default_fee() + }), + ); + + vm.vm.push_transaction(out_of_gas_tx); + let (res, call_traces) = vm.vm.inspect_with_call_tracer(); + assert_matches!(&res.result, ExecutionResult::Revert { .. }); + + let out_of_gas_call = extract_single_call(&call_traces, |call| { + call.from == account.address && call.to == contract_address + }); + assert_eq!(out_of_gas_call.error.as_ref().unwrap(), "Panic"); + assert_eq!(out_of_gas_call.gas_used, out_of_gas_call.gas); + + let parent_call = + extract_single_call(&call_traces, |call| call.calls.contains(out_of_gas_call)); + assert_eq!( + parent_call.revert_reason.as_ref().unwrap(), + "Unknown revert reason" + ); +} + +pub(crate) fn test_reverted_deployment_tx() { + let mut vm: VmTester = VmTesterBuilder::new() + .with_empty_in_memory_storage() + .with_rich_accounts(1) + .with_execution_mode(TxExecutionMode::VerifyExecute) + .build(); + + let account = &mut vm.rich_accounts[0]; + let deploy_tx = account.get_deploy_tx(TestContract::failed_call().bytecode, None, TxType::L2); + + vm.vm.push_transaction(deploy_tx.tx); + let (res, call_traces) = vm.vm.inspect_with_call_tracer(); + assert_matches!(&res.result, ExecutionResult::Success { .. }); + + let constructor_call = extract_single_call(&call_traces, |call| { + call.r#type == CallType::Create && call.from == account.address + }); + assert_eq!(constructor_call.input, [] as [u8; 0]); + assert_eq!(constructor_call.error, None); + assert_eq!(constructor_call.revert_reason, None); + let deploy_address = deployed_address_create(account.address, 0.into()); + assert_eq!(constructor_call.to, deploy_address); + + assert_eq!(constructor_call.calls.len(), 1, "{constructor_call:#?}"); + let inner_call = &constructor_call.calls[0]; + assert_eq!(inner_call.from, deploy_address); + assert_eq!(inner_call.to, MSG_VALUE_SIMULATOR_ADDRESS); + inner_call.revert_reason.as_ref().unwrap(); +} + +pub(crate) fn test_recursive_tx() { + let contract_address = Address::repeat_byte(0x42); + let mut vm: VmTester = VmTesterBuilder::new() + .with_empty_in_memory_storage() + .with_rich_accounts(1) + .with_bootloader_gas_limit(BATCH_COMPUTATIONAL_GAS_LIMIT) + .with_execution_mode(TxExecutionMode::VerifyExecute) + .with_custom_contracts(vec![ContractToDeploy::new( + TestContract::load_test().bytecode.to_vec(), + contract_address, + )]) + .build(); + + let account = &mut vm.rich_accounts[0]; + let calldata = LoadnextContractExecutionParams { + recursive_calls: 20, + ..LoadnextContractExecutionParams::empty() + } + .to_bytes(); + let recursive_tx = account.get_l2_tx_for_execute( + Execute { + contract_address: Some(contract_address), + calldata: calldata.clone(), + value: 0.into(), + factory_deps: vec![], + }, + None, + ); + + vm.vm.push_transaction(recursive_tx); + let (res, call_traces) = vm.vm.inspect_with_call_tracer(); + assert!(!res.result.is_failed(), "{:#?}", res.result); + + let mut call_to_contract = extract_single_call(&call_traces, |call| { + call.to == contract_address && call.input == calldata + }); + let mut depth = 0; + while let Some(child_call) = call_to_contract.calls.first() { + assert_eq!(call_to_contract.calls.len(), 1, "{call_to_contract:#?}"); + assert_eq!(child_call.from, contract_address); + assert_eq!(child_call.to, contract_address); + assert_ne!(child_call.input, call_to_contract.input); + + depth += 1; + call_to_contract = child_call; + } + assert_eq!(depth, 20); +} diff --git a/core/lib/multivm/src/versions/testonly/mod.rs b/core/lib/multivm/src/versions/testonly/mod.rs index 8f24c849272d..c1a603bfeefc 100644 --- a/core/lib/multivm/src/versions/testonly/mod.rs +++ b/core/lib/multivm/src/versions/testonly/mod.rs @@ -20,15 +20,17 @@ use zksync_types::{ get_is_account_key, h256_to_u256, u256_to_h256, utils::storage_key_for_eth_balance, Address, L1BatchNumber, L2BlockNumber, L2ChainId, ProtocolVersionId, U256, }; -use zksync_vm_interface::{ - pubdata::PubdataBuilder, L1BatchEnv, L2BlockEnv, SystemEnv, TxExecutionMode, -}; pub(super) use self::tester::{ - validation_params, TestedVm, TestedVmForValidation, VmTester, VmTesterBuilder, + validation_params, TestedVm, TestedVmForValidation, TestedVmWithCallTracer, VmTester, + VmTesterBuilder, }; use crate::{ - interface::storage::InMemoryStorage, pubdata_builders::FullPubdataBuilder, + interface::{ + pubdata::PubdataBuilder, storage::InMemoryStorage, L1BatchEnv, L2BlockEnv, SystemEnv, + TxExecutionMode, + }, + pubdata_builders::FullPubdataBuilder, vm_latest::constants::BATCH_COMPUTATIONAL_GAS_LIMIT, }; @@ -36,6 +38,7 @@ pub(super) mod account_validation_rules; pub(super) mod block_tip; pub(super) mod bootloader; pub(super) mod bytecode_publishing; +pub(super) mod call_tracer; pub(super) mod circuits; pub(super) mod code_oracle; pub(super) mod default_aa; diff --git a/core/lib/multivm/src/versions/testonly/tester/mod.rs b/core/lib/multivm/src/versions/testonly/tester/mod.rs index c29f2dbbf8f3..a25909525f11 100644 --- a/core/lib/multivm/src/versions/testonly/tester/mod.rs +++ b/core/lib/multivm/src/versions/testonly/tester/mod.rs @@ -8,6 +8,7 @@ use zksync_types::{ writes::StateDiffRecord, Address, L1BatchNumber, StorageKey, Transaction, H256, U256, }; +use zksync_vm_interface::Call; pub(crate) use self::transaction_test_info::{ExpectedError, TransactionTestInfo, TxModifier}; use super::get_empty_storage; @@ -252,3 +253,7 @@ pub(crate) fn validation_params(tx: &L2Tx, system: &SystemEnv) -> ValidationPara timestamp_asserter_params: None, } } + +pub(crate) trait TestedVmWithCallTracer: TestedVm { + fn inspect_with_call_tracer(&mut self) -> (VmExecutionResultAndLogs, Vec); +} diff --git a/core/lib/multivm/src/versions/vm_fast/mod.rs b/core/lib/multivm/src/versions/vm_fast/mod.rs index 58e7ad4b006c..291961d3312a 100644 --- a/core/lib/multivm/src/versions/vm_fast/mod.rs +++ b/core/lib/multivm/src/versions/vm_fast/mod.rs @@ -2,7 +2,7 @@ pub use zksync_vm2::interface; pub(crate) use self::version::FastVmVersion; pub use self::{ - tracers::{FullValidationTracer, ValidationTracer}, + tracers::{CallTracer, FullValidationTracer, ValidationTracer}, vm::Vm, }; diff --git a/core/lib/multivm/src/versions/vm_fast/tests/call_tracer.rs b/core/lib/multivm/src/versions/vm_fast/tests/call_tracer.rs new file mode 100644 index 000000000000..9a990bb8d200 --- /dev/null +++ b/core/lib/multivm/src/versions/vm_fast/tests/call_tracer.rs @@ -0,0 +1,31 @@ +use crate::versions::{testonly::call_tracer, vm_fast::Vm}; + +#[test] +fn basic_behavior() { + call_tracer::test_basic_behavior::>(); +} + +#[test] +fn transfer() { + call_tracer::test_transfer::>(); +} + +#[test] +fn reverted_tx() { + call_tracer::test_reverted_tx::>(); +} + +#[test] +fn reverted_deployment() { + call_tracer::test_reverted_deployment_tx::>(); +} + +#[test] +fn out_of_gas() { + call_tracer::test_out_of_gas::>(); +} + +#[test] +fn recursive_tx() { + call_tracer::test_recursive_tx::>(); +} diff --git a/core/lib/multivm/src/versions/vm_fast/tests/mod.rs b/core/lib/multivm/src/versions/vm_fast/tests/mod.rs index e148444922ba..89edd85b86f1 100644 --- a/core/lib/multivm/src/versions/vm_fast/tests/mod.rs +++ b/core/lib/multivm/src/versions/vm_fast/tests/mod.rs @@ -8,21 +8,24 @@ use zksync_vm_interface::{ pubdata::{PubdataBuilder, PubdataInput}, storage::ReadStorage, tracer::ViolatedValidationRule, - CurrentExecutionState, InspectExecutionMode, L2BlockEnv, VmExecutionMode, + Call, CurrentExecutionState, InspectExecutionMode, L2BlockEnv, VmExecutionMode, VmExecutionResultAndLogs, VmInterface, }; use super::{FullValidationTracer, ValidationTracer, Vm}; use crate::{ interface::storage::{ImmutableStorageView, InMemoryStorage}, - versions::testonly::{validation_params, TestedVm, TestedVmForValidation}, - vm_fast::tracers::WithBuiltinTracers, + versions::testonly::{ + validation_params, TestedVm, TestedVmForValidation, TestedVmWithCallTracer, + }, + vm_fast::{tracers::WithBuiltinTracers, CallTracer}, }; mod account_validation_rules; mod block_tip; mod bootloader; mod bytecode_publishing; +mod call_tracer; mod circuits; mod code_oracle; mod default_aa; @@ -180,7 +183,7 @@ where } } -impl TestedVmForValidation for Vm, (), FullValidationTracer> { +impl TestedVmForValidation for TestedFastVm<(), FullValidationTracer> { fn run_validation(&mut self, tx: L2Tx, timestamp: u64) -> Option { let validation_params = validation_params(&tx, &self.system_env); self.push_transaction(tx.into()); @@ -189,3 +192,11 @@ impl TestedVmForValidation for Vm, (), Ful tracer.1.validation_error() } } + +impl TestedVmWithCallTracer for TestedFastVm { + fn inspect_with_call_tracer(&mut self) -> (VmExecutionResultAndLogs, Vec) { + let mut tracer = (CallTracer::default(), ()); + let result = self.inspect(&mut tracer, InspectExecutionMode::OneTx); + (result, tracer.0.into_result()) + } +} diff --git a/core/lib/multivm/src/versions/vm_fast/tracers/calls.rs b/core/lib/multivm/src/versions/vm_fast/tracers/calls.rs new file mode 100644 index 000000000000..727e33ebb8d2 --- /dev/null +++ b/core/lib/multivm/src/versions/vm_fast/tracers/calls.rs @@ -0,0 +1,155 @@ +use zksync_system_constants::CONTRACT_DEPLOYER_ADDRESS; +use zksync_types::{zk_evm_types::FarCallOpcode, U256}; +use zksync_vm2::{ + interface::{ + CallframeInterface, CallingMode, Opcode, OpcodeType, ReturnType, ShouldStop, + StateInterface, Tracer, + }, + FatPointer, +}; + +use crate::{ + interface::{Call, CallType, VmRevertReason}, + vm_fast::utils::read_raw_fat_pointer, +}; + +/// Call tracer for the fast VM. +#[derive(Debug, Clone, Default)] +pub struct CallTracer { + stack: Vec, + finished_calls: Vec, + current_stack_depth: usize, + // TODO: report as metrics + max_stack_depth: usize, + max_near_calls: usize, +} + +#[derive(Debug, Clone)] +struct FarcallAndNearCallCount { + farcall: Call, + near_calls_after: usize, +} + +impl CallTracer { + /// Converts this tracer into the captured calls. + pub fn into_result(self) -> Vec { + self.finished_calls + } +} + +impl Tracer for CallTracer { + fn after_instruction( + &mut self, + state: &mut S, + ) -> ShouldStop { + match OP::VALUE { + Opcode::FarCall(ty) => { + self.current_stack_depth += 1; + self.max_stack_depth = self.max_stack_depth.max(self.current_stack_depth); + + let current_gas = state.current_frame().gas() as u64; + let from = state.current_frame().caller(); + let to = state.current_frame().address(); + let input = read_raw_fat_pointer(state, state.read_register(1).0); + let value = U256::from(state.current_frame().context_u128()); + let ty = match ty { + CallingMode::Normal => CallType::Call(FarCallOpcode::Normal), + CallingMode::Delegate => CallType::Call(FarCallOpcode::Delegate), + CallingMode::Mimic => { + let prev_this_address = state.callframe(1).address(); + if prev_this_address == CONTRACT_DEPLOYER_ADDRESS { + // EraVM contract creation is encoded as a mimic call from `ContractDeployer` to the created contract. + CallType::Create + } else { + CallType::Call(FarCallOpcode::Mimic) + } + } + }; + + self.stack.push(FarcallAndNearCallCount { + farcall: Call { + r#type: ty, + from, + to, + // The previous frame always exists directly after a far call + parent_gas: current_gas + state.callframe(1).gas() as u64, + gas: current_gas, + input, + value, + ..Default::default() + }, + near_calls_after: 0, + }); + } + Opcode::NearCall => { + self.current_stack_depth += 1; + self.max_stack_depth = self.max_stack_depth.max(self.current_stack_depth); + + if let Some(frame) = self.stack.last_mut() { + frame.near_calls_after += 1; + self.max_near_calls = self.max_near_calls.max(frame.near_calls_after); + } + } + Opcode::Ret(variant) => { + self.current_stack_depth -= 1; + + let Some(mut current_call) = self.stack.pop() else { + return ShouldStop::Continue; + }; + + if current_call.near_calls_after == 0 { + // Might overflow due to stipend + current_call.farcall.gas_used = current_call + .farcall + .parent_gas + .saturating_sub(state.current_frame().gas() as u64); + + let (maybe_output_ptr, is_pointer) = state.read_register(1); + let output = if is_pointer { + let output_ptr = FatPointer::from(maybe_output_ptr); + if output_ptr.length == 0 && output_ptr.offset == 0 { + // Trivial pointer, which is formally cannot be dereferenced. This only matters + // when extracting the revert reason; the legacy VM treats the trivial pointer specially. + None + } else { + Some(read_raw_fat_pointer(state, maybe_output_ptr)) + } + } else { + None + }; + + match variant { + ReturnType::Normal => { + current_call.farcall.output = output.unwrap_or_default(); + } + ReturnType::Revert => { + current_call.farcall.revert_reason = + Some(if let Some(output) = &output { + VmRevertReason::from(output.as_slice()).to_string() + } else { + "Unknown revert reason".to_owned() + }); + } + ReturnType::Panic => { + current_call.farcall.error = Some("Panic".to_string()); + } + } + + // If there is a parent call, push the current call to it + // Otherwise, put the current call back on the stack, because it's the top level call + if let Some(parent_call) = self.stack.last_mut() { + parent_call.farcall.calls.push(current_call.farcall); + } else { + self.finished_calls.push(current_call.farcall); + } + } else { + current_call.near_calls_after -= 1; + self.stack.push(current_call); + } + } + _ => {} + } + + ShouldStop::Continue + } +} diff --git a/core/lib/multivm/src/versions/vm_fast/tracers/evm_deploy.rs b/core/lib/multivm/src/versions/vm_fast/tracers/evm_deploy.rs index 1202b5b94dd2..deb06366eb36 100644 --- a/core/lib/multivm/src/versions/vm_fast/tracers/evm_deploy.rs +++ b/core/lib/multivm/src/versions/vm_fast/tracers/evm_deploy.rs @@ -8,7 +8,7 @@ use zksync_vm2::interface::{ CallframeInterface, CallingMode, GlobalStateInterface, Opcode, OpcodeType, ShouldStop, Tracer, }; -use crate::vm_fast::utils::read_fat_pointer; +use crate::vm_fast::utils::read_raw_fat_pointer; /// Container for dynamic bytecodes added by [`EvmDeployTracer`]. #[derive(Debug, Clone, Default)] @@ -54,7 +54,7 @@ impl EvmDeployTracer { return; } - let data = read_fat_pointer(state, state.read_register(1).0); + let data = read_raw_fat_pointer(state, state.read_register(1).0); if data.len() < 4 { return; } diff --git a/core/lib/multivm/src/versions/vm_fast/tracers/mod.rs b/core/lib/multivm/src/versions/vm_fast/tracers/mod.rs index 3d9602536743..db527bdbaceb 100644 --- a/core/lib/multivm/src/versions/vm_fast/tracers/mod.rs +++ b/core/lib/multivm/src/versions/vm_fast/tracers/mod.rs @@ -3,10 +3,14 @@ use zksync_vm2::interface::{CycleStats, GlobalStateInterface, OpcodeType, ShouldStop, Tracer}; pub(super) use self::evm_deploy::DynamicBytecodes; -pub use self::validation::{FullValidationTracer, ValidationTracer}; +pub use self::{ + calls::CallTracer, + validation::{FullValidationTracer, ValidationTracer}, +}; use self::{circuits::CircuitsTracer, evm_deploy::EvmDeployTracer}; use crate::interface::CircuitStatistic; +mod calls; mod circuits; mod evm_deploy; mod validation; diff --git a/core/lib/multivm/src/versions/vm_fast/tracers/validation.rs b/core/lib/multivm/src/versions/vm_fast/tracers/validation.rs index 52b0a4747b7d..6833051c5407 100644 --- a/core/lib/multivm/src/versions/vm_fast/tracers/validation.rs +++ b/core/lib/multivm/src/versions/vm_fast/tracers/validation.rs @@ -14,7 +14,7 @@ use zksync_vm_interface::tracer::{ TimestampAsserterParams, ValidationParams, ValidationTraces, ViolatedValidationRule, }; -use crate::{tracers::TIMESTAMP_ASSERTER_FUNCTION_SELECTOR, vm_fast::utils::read_fat_pointer}; +use crate::{tracers::TIMESTAMP_ASSERTER_FUNCTION_SELECTOR, vm_fast::utils::read_raw_fat_pointer}; /// [`Tracer`] used for account validation per [EIP-4337] and [EIP-7562]. /// @@ -157,7 +157,7 @@ impl Tracer for FullValidationTracer { // Intercept calls to keccak, whitelist storage slots corresponding to the hash let code_address = state.current_frame().code_address(); if code_address == KECCAK256_PRECOMPILE_ADDRESS { - let calldata = read_fat_pointer(state, state.read_register(1).0); + let calldata = read_raw_fat_pointer(state, state.read_register(1).0); if calldata.len() != 64 { return ShouldStop::Continue; } @@ -185,7 +185,7 @@ impl Tracer for FullValidationTracer { if let Some(ref params) = self.timestamp_asserter_params { if code_address == params.address { - let calldata = read_fat_pointer(state, state.read_register(1).0); + let calldata = read_raw_fat_pointer(state, state.read_register(1).0); if calldata.len() == 68 && calldata[..4] == TIMESTAMP_ASSERTER_FUNCTION_SELECTOR { @@ -215,7 +215,7 @@ impl Tracer for FullValidationTracer { } Ret(kind) => { if self.add_return_value_to_allowed_slots && kind == Normal { - let return_value = read_fat_pointer(state, state.read_register(1).0); + let return_value = read_raw_fat_pointer(state, state.read_register(1).0); self.slots_obtained_via_keccak .insert(return_value.as_slice().into()); } diff --git a/core/lib/multivm/src/versions/vm_fast/utils.rs b/core/lib/multivm/src/versions/vm_fast/utils.rs index 20a6545d3385..8b3f57be988d 100644 --- a/core/lib/multivm/src/versions/vm_fast/utils.rs +++ b/core/lib/multivm/src/versions/vm_fast/utils.rs @@ -1,8 +1,11 @@ use zksync_types::U256; use zksync_vm2::{interface::StateInterface, FatPointer}; -pub(super) fn read_fat_pointer(state: &S, raw: U256) -> Vec { - let pointer = FatPointer::from(raw); +pub(super) fn read_raw_fat_pointer(state: &S, raw: U256) -> Vec { + read_fat_pointer(state, FatPointer::from(raw)) +} + +pub(super) fn read_fat_pointer(state: &S, pointer: FatPointer) -> Vec { let length = pointer.length - pointer.offset; let start = pointer.start + pointer.offset; let mut result = vec![0; length as usize]; diff --git a/core/lib/multivm/src/versions/vm_latest/tests/call_tracer.rs b/core/lib/multivm/src/versions/vm_latest/tests/call_tracer.rs index c8f623478569..451647545671 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/call_tracer.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/call_tracer.rs @@ -1,15 +1,14 @@ use std::sync::Arc; use once_cell::sync::OnceCell; -use zksync_test_contracts::TestContract; use zksync_types::{Address, Execute}; use super::TestedLatestVm; use crate::{ interface::{InspectExecutionMode, TxExecutionMode, VmInterface}, tracers::CallTracer, - versions::testonly::{read_max_depth_contract, ContractToDeploy, VmTesterBuilder}, - vm_latest::{constants::BATCH_COMPUTATIONAL_GAS_LIMIT, ToTracerPointer}, + versions::testonly::{call_tracer, read_max_depth_contract, ContractToDeploy, VmTesterBuilder}, + vm_latest::{constants::BATCH_COMPUTATIONAL_GAS_LIMIT, ToTracerPointer, Vm}, }; // This test is ultra slow, so it's ignored by default. @@ -48,43 +47,31 @@ fn test_max_depth() { } #[test] -fn test_basic_behavior() { - let contract = TestContract::counter().bytecode.to_vec(); - let address = Address::repeat_byte(1); - let mut vm = VmTesterBuilder::new() - .with_empty_in_memory_storage() - .with_rich_accounts(1) - .with_bootloader_gas_limit(BATCH_COMPUTATIONAL_GAS_LIMIT) - .with_execution_mode(TxExecutionMode::VerifyExecute) - .with_custom_contracts(vec![ContractToDeploy::account(contract, address)]) - .build::(); +fn basic_behavior() { + call_tracer::test_basic_behavior::>(); +} - let increment_by_6_calldata = - "7cf5dab00000000000000000000000000000000000000000000000000000000000000006"; +#[test] +fn transfer() { + call_tracer::test_transfer::>(); +} - let account = &mut vm.rich_accounts[0]; - let tx = account.get_l2_tx_for_execute( - Execute { - contract_address: Some(address), - calldata: hex::decode(increment_by_6_calldata).unwrap(), - value: Default::default(), - factory_deps: vec![], - }, - None, - ); +#[test] +fn reverted_tx() { + call_tracer::test_reverted_tx::>(); +} - let result = Arc::new(OnceCell::new()); - let call_tracer = CallTracer::new(result.clone()).into_tracer_pointer(); - vm.vm.push_transaction(tx); - let res = vm - .vm - .inspect(&mut call_tracer.into(), InspectExecutionMode::OneTx); +#[test] +fn reverted_deployment() { + call_tracer::test_reverted_deployment_tx::>(); +} - let call_tracer_result = result.get().unwrap(); +#[test] +fn out_of_gas() { + call_tracer::test_out_of_gas::>(); +} - assert_eq!(call_tracer_result.len(), 1); - // Expect that there are a plenty of subcalls underneath. - let subcall = &call_tracer_result[0].calls; - assert!(subcall.len() > 10); - assert!(!res.result.is_failed()); +#[test] +fn recursive_tx() { + call_tracer::test_recursive_tx::>(); } diff --git a/core/lib/multivm/src/versions/vm_latest/tests/mod.rs b/core/lib/multivm/src/versions/vm_latest/tests/mod.rs index 10750f6ffaaf..260b1a4eeef3 100644 --- a/core/lib/multivm/src/versions/vm_latest/tests/mod.rs +++ b/core/lib/multivm/src/versions/vm_latest/tests/mod.rs @@ -4,6 +4,7 @@ use std::{ sync::Arc, }; +use once_cell::sync::OnceCell; use zk_evm_1_5_0::{ aux_structures::{MemoryPage, Timestamp}, vm_state::VmLocalState, @@ -13,7 +14,7 @@ use zksync_types::{ bytecode::BytecodeHash, l2::L2Tx, vm::VmVersion, writes::StateDiffRecord, StorageKey, StorageValue, Transaction, H256, U256, }; -use zksync_vm_interface::VmInterface; +use zksync_vm_interface::{Call, InspectExecutionMode, VmInterface}; use super::{HistoryEnabled, ToTracerPointer, Vm}; use crate::{ @@ -23,10 +24,11 @@ use crate::{ tracer::ViolatedValidationRule, CurrentExecutionState, L2BlockEnv, VmExecutionMode, VmExecutionResultAndLogs, }, - tracers::ValidationTracer, + tracers::{CallTracer, ValidationTracer}, utils::bytecode::bytes_to_be_words, versions::testonly::{ filter_out_base_system_contracts, validation_params, TestedVm, TestedVmForValidation, + TestedVmWithCallTracer, }, vm_latest::{ constants::BOOTLOADER_HEAP_PAGE, @@ -338,3 +340,13 @@ impl Vm (VmExecutionResultAndLogs, Vec) { + let result = Arc::new(OnceCell::new()); + let call_tracer = CallTracer::new(result.clone()).into_tracer_pointer(); + let res = self.inspect(&mut call_tracer.into(), InspectExecutionMode::OneTx); + let traces = result.get().unwrap().clone(); + (res, traces) + } +} diff --git a/core/lib/vm_executor/src/batch/factory.rs b/core/lib/vm_executor/src/batch/factory.rs index 59b610426669..5e375d1f3062 100644 --- a/core/lib/vm_executor/src/batch/factory.rs +++ b/core/lib/vm_executor/src/batch/factory.rs @@ -8,8 +8,8 @@ use zksync_multivm::{ executor::{BatchExecutor, BatchExecutorFactory}, pubdata::PubdataBuilder, storage::{ReadStorage, StoragePtr, StorageView, StorageViewStats}, - utils::DivergenceHandler, - BatchTransactionExecutionResult, BytecodeCompressionError, CompressedBytecodeInfo, + utils::{DivergenceHandler, ShadowMut}, + BatchTransactionExecutionResult, BytecodeCompressionError, Call, CompressedBytecodeInfo, ExecutionResult, FinishedL1Batch, Halt, L1BatchEnv, L2BlockEnv, SystemEnv, VmFactory, VmInterface, VmInterfaceHistoryEnabled, }, @@ -28,6 +28,23 @@ use super::{ }; use crate::shared::{InteractionType, Sealed, STORAGE_METRICS}; +#[doc(hidden)] +pub trait CallTracingTracer: vm_fast::interface::Tracer + Default { + fn into_traces(self) -> Vec; +} + +impl CallTracingTracer for () { + fn into_traces(self) -> Vec { + vec![] + } +} + +impl CallTracingTracer for vm_fast::CallTracer { + fn into_traces(self) -> Vec { + self.into_result() + } +} + /// Encapsulates a tracer used during batch processing. Currently supported tracers are `()` (no-op) and [`TraceCalls`]. /// /// All members of this trait are implementation details. @@ -37,7 +54,7 @@ pub trait BatchTracer: fmt::Debug + 'static + Send + Sealed { const TRACE_CALLS: bool; /// Tracer for the fast VM. #[doc(hidden)] - type Fast: vm_fast::interface::Tracer + Default; + type Fast: CallTracingTracer; } impl Sealed for () {} @@ -56,7 +73,7 @@ impl Sealed for TraceCalls {} impl BatchTracer for TraceCalls { const TRACE_CALLS: bool = true; - type Fast = (); // TODO: change once call tracing is implemented in fast VM + type Fast = vm_fast::CallTracer; } /// The default implementation of [`BatchExecutorFactory`]. @@ -213,13 +230,14 @@ impl BatchVm { tx: Transaction, with_compression: bool, ) -> BatchTransactionExecutionResult { - let call_tracer_result = Arc::new(OnceCell::default()); + let legacy_tracer_result = Arc::new(OnceCell::default()); let legacy_tracer = if Tr::TRACE_CALLS { - vec![CallTracer::new(call_tracer_result.clone()).into_tracer_pointer()] + vec![CallTracer::new(legacy_tracer_result.clone()).into_tracer_pointer()] } else { vec![] }; let mut legacy_tracer = legacy_tracer.into(); + let mut fast_traces = vec![]; let (compression_result, tx_result) = match self { Self::Legacy(vm) => vm.inspect_transaction_with_bytecode_compression( @@ -228,16 +246,34 @@ impl BatchVm { with_compression, ), Self::Fast(vm) => { - let mut tracer = (legacy_tracer.into(), Default::default()); - vm.inspect_transaction_with_bytecode_compression(&mut tracer, tx, with_compression) + let mut tracer = (legacy_tracer.into(), (Tr::Fast::default(), ())); + let res = vm.inspect_transaction_with_bytecode_compression( + &mut tracer, + tx, + with_compression, + ); + let (_, (call_tracer, _)) = tracer; + fast_traces = call_tracer.into_traces(); + res } }; let compressed_bytecodes = compression_result.map(Cow::into_owned); - let call_traces = Arc::try_unwrap(call_tracer_result) + let legacy_traces = Arc::try_unwrap(legacy_tracer_result) .expect("failed extracting call traces") .take() .unwrap_or_default(); + let call_traces = match self { + Self::Legacy(_) => legacy_traces, + Self::Fast(FastVmInstance::Fast(_)) => fast_traces, + Self::Fast(FastVmInstance::Shadowed(vm)) => { + vm.get_custom_mut("call_traces", |r| match r { + ShadowMut::Main(_) => legacy_traces.as_slice(), + ShadowMut::Shadow(_) => fast_traces.as_slice(), + }); + fast_traces + } + }; BatchTransactionExecutionResult { tx_result: Box::new(tx_result), diff --git a/core/lib/vm_interface/src/utils/shadow.rs b/core/lib/vm_interface/src/utils/shadow.rs index d6a6d16c77a0..e4a7aa51f78c 100644 --- a/core/lib/vm_interface/src/utils/shadow.rs +++ b/core/lib/vm_interface/src/utils/shadow.rs @@ -7,16 +7,19 @@ use std::{ sync::Arc, }; -use zksync_types::{StorageKey, StorageLog, StorageLogWithPreviousValue, Transaction}; +use zksync_types::{ + Address, StorageKey, StorageLog, StorageLogWithPreviousValue, Transaction, U256, +}; use super::dump::{DumpingVm, VmDump}; use crate::{ pubdata::PubdataBuilder, storage::{ReadStorage, StoragePtr, StorageView}, tracer::{ValidationError, ValidationTraces}, - BytecodeCompressionResult, CurrentExecutionState, FinishedL1Batch, InspectExecutionMode, - L1BatchEnv, L2BlockEnv, PushTransactionResult, SystemEnv, VmExecutionResultAndLogs, VmFactory, - VmInterface, VmInterfaceHistoryEnabled, VmTrackingContracts, + BytecodeCompressionResult, Call, CallType, CurrentExecutionState, FinishedL1Batch, + InspectExecutionMode, L1BatchEnv, L2BlockEnv, PushTransactionResult, SystemEnv, + VmExecutionResultAndLogs, VmFactory, VmInterface, VmInterfaceHistoryEnabled, + VmTrackingContracts, }; /// Handler for VM divergences. @@ -233,6 +236,64 @@ impl CheckDivergence for Result { } } +/// `PartialEq` for `Call` doesn't compare gas-related fields. Here, we do compare them. +#[derive(Debug, PartialEq)] +struct StrictCall<'a> { + r#type: CallType, + from: Address, + to: Address, + // `gas` / `parent_gas` differ between fast VM and legacy VM during validation + gas_used: u64, + value: U256, + input: &'a [u8], + output: &'a [u8], + error: Option<&'a str>, + revert_reason: Option<&'a str>, +} + +impl<'a> StrictCall<'a> { + fn flatten(calls: &'a [Call]) -> Vec { + let mut flattened = Vec::new(); + Self::flatten_inner(&mut flattened, calls); + flattened + } + + fn flatten_inner(flattened: &mut Vec, calls: &'a [Call]) { + // Depth-first, parents-before-children traversal. + for call in calls { + flattened.push(Self { + r#type: call.r#type, + from: call.from, + to: call.to, + gas_used: call.gas_used, + value: call.value, + input: &call.input, + output: &call.output, + error: call.error.as_deref(), + revert_reason: call.revert_reason.as_deref(), + }); + Self::flatten_inner(flattened, &call.calls); + } + } +} + +impl CheckDivergence for [Call] { + fn check_divergence(&self, other: &Self) -> DivergenceErrors { + let this = StrictCall::flatten(self); + let other = StrictCall::flatten(other); + let mut errors = DivergenceErrors::new(); + + errors.check_match("call_traces", &this, &other); + errors + } +} + +impl CheckDivergence for &T { + fn check_divergence(&self, other: &Self) -> DivergenceErrors { + (**self).check_divergence(*other) + } +} + /// Shadowed VM that executes 2 VMs for each operation and compares their outputs. /// /// If a divergence is detected, the VM state is dumped using [a pluggable handler](Self::set_dump_handler()), @@ -526,7 +587,8 @@ impl DivergenceErrors { } } - fn extend(&mut self, from: Self) { + /// Extends this instance from another set of errors. + pub fn extend(&mut self, from: Self) { self.divergences.extend(from.divergences); } diff --git a/core/node/api_server/src/web3/namespaces/debug.rs b/core/node/api_server/src/web3/namespaces/debug.rs index 98af15032008..180de6b273e5 100644 --- a/core/node/api_server/src/web3/namespaces/debug.rs +++ b/core/node/api_server/src/web3/namespaces/debug.rs @@ -54,6 +54,7 @@ impl DebugNamespace { } } } + pub(crate) fn map_default_call( call: Call, only_top_call: bool, diff --git a/core/node/state_keeper/src/executor/tests/mod.rs b/core/node/state_keeper/src/executor/tests/mod.rs index 219cacc60c85..8d735e9ed920 100644 --- a/core/node/state_keeper/src/executor/tests/mod.rs +++ b/core/node/state_keeper/src/executor/tests/mod.rs @@ -1,18 +1,20 @@ -// FIXME: move storage-agnostic tests to VM executor crate - use assert_matches::assert_matches; use rand::{thread_rng, Rng}; use test_casing::{test_casing, Product}; use zksync_contracts::l2_message_root; use zksync_dal::{ConnectionPool, Core}; -use zksync_multivm::interface::{BatchTransactionExecutionResult, ExecutionResult, Halt}; +use zksync_multivm::interface::{ + BatchTransactionExecutionResult, Call, CallType, ExecutionResult, Halt, +}; use zksync_test_contracts::{Account, TestContract}; use zksync_types::{ - get_nonce_key, utils::storage_key_for_eth_balance, vm::FastVmMode, web3, Execute, PriorityOpId, - H256, L2_MESSAGE_ROOT_ADDRESS, U256, + get_nonce_key, + utils::{deployed_address_create, storage_key_for_eth_balance}, + vm::FastVmMode, + web3, Execute, PriorityOpId, H256, L2_MESSAGE_ROOT_ADDRESS, U256, }; -use self::tester::{AccountExt, StorageSnapshot, TestConfig, Tester}; +use self::tester::{AccountExt, StorageSnapshot, TestConfig, Tester, TRANSFER_VALUE}; mod read_storage_factory; mod tester; @@ -854,9 +856,9 @@ async fn execute_tx_with_large_packable_bytecode(vm_mode: FastVmMode) { executor.finish_batch().await.unwrap(); } -#[test_casing(2, [FastVmMode::Old, FastVmMode::Shadow])] // new VM doesn't support call tracing yet +#[test_casing(3, FAST_VM_MODES)] #[tokio::test] -async fn execute_tx_with_call_traces(vm_mode: FastVmMode) { +async fn execute_txs_with_call_traces(vm_mode: FastVmMode) { let connection_pool = ConnectionPool::::constrained_test_pool(1).await; let mut alice = Account::random(); let mut tester = Tester::with_config( @@ -876,4 +878,35 @@ async fn execute_tx_with_call_traces(vm_mode: FastVmMode) { assert_matches!(res.tx_result.result, ExecutionResult::Success { .. }); assert!(!res.call_traces.is_empty()); + + find_first_call(&res.call_traces, &|call| { + call.from == alice.address && call.value == TRANSFER_VALUE.into() + }) + .expect("no transfer call"); + + let deploy_tx = alice.deploy_loadnext_tx().tx; + let res = executor.execute_tx(deploy_tx).await.unwrap(); + assert_matches!(res.tx_result.result, ExecutionResult::Success { .. }); + assert!(!res.call_traces.is_empty()); + + let create_call = find_first_call(&res.call_traces, &|call| { + call.from == alice.address && call.r#type == CallType::Create + }) + .expect("no create call"); + + let expected_address = deployed_address_create(alice.address, 0.into()); + assert_eq!(create_call.to, expected_address); + assert!(!create_call.input.is_empty()); +} + +fn find_first_call<'a>(calls: &'a [Call], predicate: &impl Fn(&Call) -> bool) -> Option<&'a Call> { + for call in calls { + if predicate(call) { + return Some(call); + } + if let Some(call) = find_first_call(&call.calls, predicate) { + return Some(call); + } + } + None } diff --git a/core/node/state_keeper/src/executor/tests/tester.rs b/core/node/state_keeper/src/executor/tests/tester.rs index 8b6df7f04840..6c5015fbca46 100644 --- a/core/node/state_keeper/src/executor/tests/tester.rs +++ b/core/node/state_keeper/src/executor/tests/tester.rs @@ -1,7 +1,7 @@ //! Testing harness for the batch executor. //! Contains helper functionality to initialize test context and perform tests without too much boilerplate. -use std::{collections::HashMap, fmt::Debug, str::FromStr, sync::Arc}; +use std::{collections::HashMap, fmt::Debug, sync::Arc}; use assert_matches::assert_matches; use tempfile::TempDir; @@ -42,14 +42,12 @@ use zksync_vm_executor::batch::{MainBatchExecutorFactory, TraceCalls}; use super::{read_storage_factory::RocksdbStorageFactory, StorageType}; use crate::{ - testonly::{self, BASE_SYSTEM_CONTRACTS}, + testonly::{self, apply_genesis_logs, BASE_SYSTEM_CONTRACTS}, tests::{default_l1_batch_env, default_system_env}, AsyncRocksdbCache, }; -fn get_da_contract_address() -> Address { - Address::from_str("7726827caac94a7f9e1b160f7ea819f172f7b6f9").unwrap() -} +pub(super) const TRANSFER_VALUE: u64 = 123_456_789; /// Representation of configuration parameters used by the state keeper. /// Has sensible defaults for most tests, each of which can be overridden. @@ -294,7 +292,7 @@ impl Tester { .await .unwrap(); - // Also setting up the da for tests + // Also setting up the DA for tests Self::setup_da(&mut storage).await; } } @@ -334,37 +332,28 @@ impl Tester { } } - pub async fn setup_contract<'a>( - con: &mut Connection<'a, Core>, - address: Address, - code: Vec, - ) { + async fn setup_contract(conn: &mut Connection<'_, Core>, address: Address, code: Vec) { let hash: H256 = BytecodeHash::for_bytecode(&code).value(); let known_code_key = get_known_code_key(&hash); let code_key = get_code_key(&address); - let logs = vec![ - StorageLog::new_write_log(known_code_key, H256::from_low_u64_be(1u64)), + let logs = [ + StorageLog::new_write_log(known_code_key, H256::from_low_u64_be(1)), StorageLog::new_write_log(code_key, hash), ]; + apply_genesis_logs(conn, &logs).await; - for log in logs { - apply_genesis_log(con, log).await; - } - - let mut factory_deps = HashMap::new(); - factory_deps.insert(hash, code); - - con.factory_deps_dal() + let factory_deps = HashMap::from([(hash, code)]); + conn.factory_deps_dal() .insert_factory_deps(L2BlockNumber(0), &factory_deps) .await .unwrap(); } - async fn setup_da<'a>(con: &mut Connection<'a, Core>) { + async fn setup_da(conn: &mut Connection<'_, Core>) { Self::setup_contract( - con, - get_da_contract_address(), + conn, + Address::repeat_byte(0x23), l2_rollup_da_validator_bytecode(), ) .await; @@ -442,6 +431,7 @@ impl AccountExt for Account { TxType::L2, ) } + fn l1_execute(&mut self, serial_id: PriorityOpId) -> Transaction { self.get_l1_tx(Execute::transfer(Address::random(), 0.into()), serial_id.0) } @@ -505,7 +495,7 @@ impl AccountExt for Account { /// Automatically increments nonce of the account. fn execute_with_gas_limit(&mut self, gas_limit: u32) -> Transaction { self.get_l2_tx_for_execute( - Execute::transfer(Address::random(), 0.into()), + Execute::transfer(Address::random(), TRANSFER_VALUE.into()), Some(testonly::fee(gas_limit)), ) } @@ -766,25 +756,3 @@ impl StorageSnapshot { snapshot } } - -async fn apply_genesis_log<'a>(storage: &mut Connection<'a, Core>, log: StorageLog) { - storage - .storage_logs_dal() - .append_storage_logs(L2BlockNumber(0), &[log]) - .await - .unwrap(); - - if storage - .storage_logs_dedup_dal() - .filter_written_slots(&[log.key.hashed_key()]) - .await - .unwrap() - .is_empty() - { - storage - .storage_logs_dedup_dal() - .insert_initial_writes(L1BatchNumber(0), &[log.key.hashed_key()]) - .await - .unwrap(); - } -} diff --git a/core/node/state_keeper/src/testonly/mod.rs b/core/node/state_keeper/src/testonly/mod.rs index c0f3707f9455..0484fe3198fd 100644 --- a/core/node/state_keeper/src/testonly/mod.rs +++ b/core/node/state_keeper/src/testonly/mod.rs @@ -1,7 +1,7 @@ //! Test utilities that can be used for testing sequencer that may //! be useful outside of this crate. -use std::collections::HashMap; +use std::collections::HashSet; use async_trait::async_trait; use once_cell::sync::Lazy; @@ -15,10 +15,10 @@ use zksync_multivm::interface::{ }; use zksync_state::OwnedStorage; use zksync_types::{ - bytecode::BytecodeHash, commitment::PubdataParams, fee::Fee, get_code_key, get_known_code_key, - u256_to_h256, utils::storage_key_for_standard_token_balance, AccountTreeId, Address, - L1BatchNumber, L2BlockNumber, StorageLog, Transaction, H256, L2_BASE_TOKEN_ADDRESS, - SYSTEM_CONTEXT_MINIMAL_BASE_FEE, U256, + commitment::PubdataParams, fee::Fee, u256_to_h256, + utils::storage_key_for_standard_token_balance, AccountTreeId, Address, L1BatchNumber, + L2BlockNumber, StorageLog, Transaction, L2_BASE_TOKEN_ADDRESS, SYSTEM_CONTEXT_MINIMAL_BASE_FEE, + U256, }; pub mod test_batch_executor; @@ -76,25 +76,28 @@ impl BatchExecutor for MockBatchExecutor { } } -async fn apply_genesis_log<'a>(storage: &mut Connection<'a, Core>, log: StorageLog) { +pub(crate) async fn apply_genesis_logs(storage: &mut Connection<'_, Core>, logs: &[StorageLog]) { storage .storage_logs_dal() - .append_storage_logs(L2BlockNumber(0), &[log]) + .append_storage_logs(L2BlockNumber(0), logs) .await .unwrap(); - if storage + + let all_hashed_keys: Vec<_> = logs.iter().map(|log| log.key.hashed_key()).collect(); + let repeated_writes = storage .storage_logs_dedup_dal() - .filter_written_slots(&[log.key.hashed_key()]) + .filter_written_slots(&all_hashed_keys) .await - .unwrap() - .is_empty() - { - storage - .storage_logs_dedup_dal() - .insert_initial_writes(L1BatchNumber(0), &[log.key.hashed_key()]) - .await - .unwrap(); - } + .unwrap(); + let initial_writes: Vec<_> = HashSet::from_iter(all_hashed_keys) + .difference(&repeated_writes) + .copied() + .collect(); + storage + .storage_logs_dedup_dal() + .insert_initial_writes(L1BatchNumber(0), &initial_writes) + .await + .unwrap(); } /// Adds funds for specified account list. @@ -103,43 +106,18 @@ pub async fn fund(pool: &ConnectionPool, addresses: &[Address]) { let mut storage = pool.connection().await.unwrap(); let eth_amount = U256::from(10u32).pow(U256::from(32)); //10^32 wei - - for address in addresses { - let key = storage_key_for_standard_token_balance( - AccountTreeId::new(L2_BASE_TOKEN_ADDRESS), - address, - ); - let value = u256_to_h256(eth_amount); - let storage_log = StorageLog::new_write_log(key, value); - - apply_genesis_log(&mut storage, storage_log).await; - } -} - -pub async fn setup_contract(pool: &ConnectionPool, address: Address, code: Vec) { - let mut storage = pool.connection().await.unwrap(); - - let hash: H256 = BytecodeHash::for_bytecode(&code).value(); - let known_code_key = get_known_code_key(&hash); - let code_key = get_code_key(&address); - - let logs = vec![ - StorageLog::new_write_log(known_code_key, H256::from_low_u64_be(1u64)), - StorageLog::new_write_log(code_key, hash), - ]; - - for log in logs { - apply_genesis_log(&mut storage, log).await; - } - - let mut factory_deps = HashMap::new(); - factory_deps.insert(hash, code); - - storage - .factory_deps_dal() - .insert_factory_deps(L2BlockNumber(0), &factory_deps) - .await - .unwrap(); + let storage_logs: Vec<_> = addresses + .iter() + .map(|address| { + let key = storage_key_for_standard_token_balance( + AccountTreeId::new(L2_BASE_TOKEN_ADDRESS), + address, + ); + StorageLog::new_write_log(key, u256_to_h256(eth_amount)) + }) + .collect(); + + apply_genesis_logs(&mut storage, &storage_logs).await; } pub(crate) const DEFAULT_GAS_PER_PUBDATA: u32 = 10000; From 67663bffaad4712a782f21279c07feeefb68e828 Mon Sep 17 00:00:00 2001 From: Artem Makhortov <13339874+artmakh@users.noreply.github.com> Date: Mon, 20 Jan 2025 19:25:58 +0700 Subject: [PATCH 62/97] fix(ci): Update github-hosted runner label (#3506) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Update github-hosted runner label ## Why ❔ Github update runner OS version ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- .github/workflows/protobuf.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/protobuf.yaml b/.github/workflows/protobuf.yaml index ba667430dd04..62748c79251a 100644 --- a/.github/workflows/protobuf.yaml +++ b/.github/workflows/protobuf.yaml @@ -31,7 +31,7 @@ env: jobs: compatibility: - runs-on: [ubuntu-22.04-github-hosted-16core] + runs-on: [ubuntu-24.04-github-hosted-16core] steps: - uses: mozilla-actions/sccache-action@89e9040de88b577a072e3760aaf59f585da083af # v0.0.5 From 995e583aa9b4ef6e0d8697fbb040e4b991a4248d Mon Sep 17 00:00:00 2001 From: Joonatan Saarhelo Date: Mon, 20 Jan 2025 16:56:09 +0000 Subject: [PATCH 63/97] fix: copy special case to fast VM call tracer (#3509) --- core/lib/multivm/src/versions/vm_fast/tracers/calls.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/lib/multivm/src/versions/vm_fast/tracers/calls.rs b/core/lib/multivm/src/versions/vm_fast/tracers/calls.rs index 727e33ebb8d2..2413e5916a25 100644 --- a/core/lib/multivm/src/versions/vm_fast/tracers/calls.rs +++ b/core/lib/multivm/src/versions/vm_fast/tracers/calls.rs @@ -50,7 +50,11 @@ impl Tracer for CallTracer { let current_gas = state.current_frame().gas() as u64; let from = state.current_frame().caller(); let to = state.current_frame().address(); - let input = read_raw_fat_pointer(state, state.read_register(1).0); + let input = if current_gas == 0 { + vec![] + } else { + read_raw_fat_pointer(state, state.read_register(1).0) + }; let value = U256::from(state.current_frame().context_u128()); let ty = match ty { CallingMode::Normal => CallType::Call(FarCallOpcode::Normal), From b7618e1ca78f92dacce617d7f56d49779d8123d9 Mon Sep 17 00:00:00 2001 From: Tahsin Tunan Date: Mon, 20 Jan 2025 23:37:24 +0600 Subject: [PATCH 64/97] chore: Localize feature import to crate for circuit prover (#3502) ### Description This PR resolves the build failure in [`zksync_prover_job_processor`](https://github.com/matter-labs/zksync-era/tree/main/prover/crates/lib/prover_job_processor) caused by the following error: ``` error[E0432]: unresolved import tokio::task::JoinHandle --> crates/lib/prover_job_processor/src/job_runner.rs:1:5 | 1 | use tokio::task::JoinHandle; | ^^^^^^^^^^^^^^^^^^^^^^^ no JoinHandle in task ``` The error occurred because `JoinHandle` was gated behind the `rt` feature in Tokio, which was disabled. Enabling the feature resolves the import and fixes the build. --- prover/Cargo.toml | 6 +++--- prover/crates/lib/circuit_prover_service/Cargo.toml | 6 ++---- prover/crates/lib/prover_job_processor/Cargo.toml | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/prover/Cargo.toml b/prover/Cargo.toml index aed8cbfdc723..c1d006f2a759 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -71,13 +71,13 @@ vise = "0.2.0" circuit_definitions = "=0.150.19" circuit_sequencer_api = "=0.150.19" zkevm_test_harness = "=0.150.19" -proof-compression-gpu = { package = "proof-compression", version = "=0.152.10"} -fflonk-gpu = { package = "fflonk-cuda", version = "=0.152.10"} +proof-compression-gpu = { package = "proof-compression", version = "=0.152.10" } +fflonk-gpu = { package = "fflonk-cuda", version = "=0.152.10" } fflonk = "=0.30.12" franklin-crypto = "=0.30.12" # GPU proving dependencies -wrapper_prover = { package = "zksync-wrapper-prover", version = "=0.152.10"} +wrapper_prover = { package = "zksync-wrapper-prover", version = "=0.152.10" } shivini = "=0.152.10" boojum-cuda = "=0.152.10" diff --git a/prover/crates/lib/circuit_prover_service/Cargo.toml b/prover/crates/lib/circuit_prover_service/Cargo.toml index ca7d1ede02f1..3b152528ab68 100644 --- a/prover/crates/lib/circuit_prover_service/Cargo.toml +++ b/prover/crates/lib/circuit_prover_service/Cargo.toml @@ -13,7 +13,7 @@ categories.workspace = true [dependencies] zksync_prover_job_processor.workspace = true zksync_prover_fri_types.workspace = true -zksync_prover_keystore.workspace = true +zksync_prover_keystore = { workspace = true, features = ["gpu-light"] } zksync_prover_dal.workspace = true zksync_types.workspace = true zksync_object_store.workspace = true @@ -24,8 +24,6 @@ tokio = { workspace = true, features = ["macros", "time"] } tokio-util.workspace = true tracing.workspace = true -shivini = { workspace = true, features = [ - "circuit_definitions", -] } +shivini = { workspace = true, features = ["circuit_definitions"] } zkevm_test_harness.workspace = true vise.workspace = true diff --git a/prover/crates/lib/prover_job_processor/Cargo.toml b/prover/crates/lib/prover_job_processor/Cargo.toml index 5197b33b1f95..fea4c6195fd7 100644 --- a/prover/crates/lib/prover_job_processor/Cargo.toml +++ b/prover/crates/lib/prover_job_processor/Cargo.toml @@ -14,7 +14,7 @@ categories.workspace = true async-trait.workspace = true anyhow.workspace = true futures.workspace = true -tokio.workspace = true +tokio = { workspace = true, features = ["rt"] } tokio-stream.workspace = true tokio-util.workspace = true tracing.workspace = true From c89d8c2c259f58d35b55cb1bfe7248f73791683e Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Mon, 20 Jan 2025 16:52:20 -0300 Subject: [PATCH 65/97] use temp dir for kzg points --- Cargo.lock | 1 + .../lib/config/src/configs/da_client/eigen.rs | 5 +- core/lib/env_config/src/da_client.rs | 2 + core/lib/protobuf_config/src/da_client.rs | 3 +- .../src/proto/config/da_client.proto | 1 + core/node/da_clients/Cargo.toml | 1 + core/node/da_clients/src/eigen/sdk.rs | 1 + .../node/da_clients/src/eigen/verifier/mod.rs | 66 +++++++++++++------ .../da_clients/src/eigen/verifier/tests.rs | 1 + 9 files changed, 59 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5bc26bf5e260..16d0bc9a007c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11822,6 +11822,7 @@ dependencies = [ "sha3 0.10.8", "subxt-metadata", "subxt-signer", + "tempfile", "thiserror", "tiny-keccak 2.0.2", "tokio", diff --git a/core/lib/config/src/configs/da_client/eigen.rs b/core/lib/config/src/configs/da_client/eigen.rs index f2d5f30e0e55..2283700ec3d3 100644 --- a/core/lib/config/src/configs/da_client/eigen.rs +++ b/core/lib/config/src/configs/da_client/eigen.rs @@ -18,13 +18,15 @@ pub struct EigenConfig { /// a value less or equal to 0 means that the disperser will not wait for finalization pub settlement_layer_confirmation_depth: u32, /// URL of the Ethereum RPC server - pub eigenda_eth_rpc: Option, //Option, + pub eigenda_eth_rpc: Option, /// Address of the service manager contract pub eigenda_svc_manager_address: Address, /// Wait for the blob to be finalized before returning the response pub wait_for_finalization: bool, /// Authenticated dispersal pub authenticated: bool, + /// Optional path to downloaded points directory + pub points_dir: Option, /// Url to the file containing the G1 point used for KZG pub g1_url: String, /// Url to the file containing the G2 point used for KZG @@ -40,6 +42,7 @@ impl Default for EigenConfig { eigenda_svc_manager_address: DEFAULT_EIGENDA_SVC_MANAGER_ADDRESS, wait_for_finalization: false, authenticated: false, + points_dir: None, g1_url: "https://github.com/Layr-Labs/eigenda-proxy/raw/2fd70b99ef5bf137d7bbca3461cf9e1f2c899451/resources/g1.point".to_string(), g2_url: "https://github.com/Layr-Labs/eigenda-proxy/raw/2fd70b99ef5bf137d7bbca3461cf9e1f2c899451/resources/g2.point.powerOf2".to_string(), } diff --git a/core/lib/env_config/src/da_client.rs b/core/lib/env_config/src/da_client.rs index 515cc9665809..4469dd1afe4f 100644 --- a/core/lib/env_config/src/da_client.rs +++ b/core/lib/env_config/src/da_client.rs @@ -257,6 +257,7 @@ mod tests { DA_EIGENDA_SVC_MANAGER_ADDRESS="0x0000000000000000000000000000000000000123" DA_WAIT_FOR_FINALIZATION=true DA_AUTHENTICATED=false + DA_POINTS_DIR="resources/" DA_G1_URL="resources1" DA_G2_URL="resources2" "#; @@ -274,6 +275,7 @@ mod tests { .unwrap(), wait_for_finalization: true, authenticated: false, + points_dir: Some("resources/".to_string()), g1_url: "resources1".to_string(), g2_url: "resources2".to_string(), }) diff --git a/core/lib/protobuf_config/src/da_client.rs b/core/lib/protobuf_config/src/da_client.rs index 9c988a53dac3..17dab3d07239 100644 --- a/core/lib/protobuf_config/src/da_client.rs +++ b/core/lib/protobuf_config/src/da_client.rs @@ -78,6 +78,7 @@ impl ProtoRepr for proto::DataAvailabilityClient { wait_for_finalization: *required(&conf.wait_for_finalization) .context("wait_for_finalization")?, authenticated: *required(&conf.authenticated).context("authenticated")?, + points_dir: Some(required(&conf.points_dir).context("points_dir")?.clone()), g1_url: required(&conf.g1_url).context("g1_url")?.clone(), g2_url: required(&conf.g2_url).context("g2_url")?.clone(), }), @@ -122,7 +123,6 @@ impl ProtoRepr for proto::DataAvailabilityClient { settlement_layer_confirmation_depth: Some( config.settlement_layer_confirmation_depth, ), - // eigenda_eth_rpc: config.eigenda_eth_rpc.into(), eigenda_eth_rpc: Some(format!("{:?}", config.eigenda_eth_rpc)), eigenda_svc_manager_address: Some(format!( "{:?}", @@ -132,6 +132,7 @@ impl ProtoRepr for proto::DataAvailabilityClient { authenticated: Some(config.authenticated), g1_url: Some(config.g1_url.clone()), g2_url: Some(config.g2_url.clone()), + points_dir: Some(format!("{:?}", config.points_dir)), }), ObjectStore(config) => proto::data_availability_client::Config::ObjectStore( object_store_proto::ObjectStore::build(config), diff --git a/core/lib/protobuf_config/src/proto/config/da_client.proto b/core/lib/protobuf_config/src/proto/config/da_client.proto index 75f2e2410d0a..21ac2fb5e116 100644 --- a/core/lib/protobuf_config/src/proto/config/da_client.proto +++ b/core/lib/protobuf_config/src/proto/config/da_client.proto @@ -45,6 +45,7 @@ message EigenConfig { optional bool authenticated = 8; optional string g1_url = 9; optional string g2_url = 10; + optional string points_dir = 11; reserved 1,2; reserved "rpc_node_url","inclusion_polling_interval_ms"; } diff --git a/core/node/da_clients/Cargo.toml b/core/node/da_clients/Cargo.toml index a0bc18567196..5a7930e433e8 100644 --- a/core/node/da_clients/Cargo.toml +++ b/core/node/da_clients/Cargo.toml @@ -66,6 +66,7 @@ zksync_web3_decl.workspace = true zksync_eth_client.workspace = true url.workspace = true thiserror.workspace = true +tempfile.workspace = true [dev-dependencies] serial_test.workspace = true diff --git a/core/node/da_clients/src/eigen/sdk.rs b/core/node/da_clients/src/eigen/sdk.rs index 51c8685f0bcb..fed9e3f63d82 100644 --- a/core/node/da_clients/src/eigen/sdk.rs +++ b/core/node/da_clients/src/eigen/sdk.rs @@ -61,6 +61,7 @@ impl RawEigenClient { max_blob_size: Self::BLOB_SIZE_LIMIT as u32, g1_url: Url::parse(&config.g1_url)?, g2_url: Url::parse(&config.g2_url)?, + points_dir: config.points_dir.clone(), settlement_layer_confirmation_depth: config.settlement_layer_confirmation_depth, }; diff --git a/core/node/da_clients/src/eigen/verifier/mod.rs b/core/node/da_clients/src/eigen/verifier/mod.rs index 79f388414e2b..15b3378498ae 100644 --- a/core/node/da_clients/src/eigen/verifier/mod.rs +++ b/core/node/da_clients/src/eigen/verifier/mod.rs @@ -1,9 +1,9 @@ -use std::{collections::HashMap, sync::Arc}; +use std::{collections::HashMap, io::Write, sync::Arc}; use ark_bn254::{Fq, G1Affine}; use ethabi::{encode, ParamType, Token}; use rust_kzg_bn254::{blob::Blob, kzg::Kzg, polynomial::PolynomialFormat}; -use tokio::{fs::File, io::AsyncWriteExt}; +use tempfile::NamedTempFile; use url::Url; use zksync_basic_types::web3::CallRequest; use zksync_eth_client::{EnrichedClientError, EnrichedClientResult, EthInterface}; @@ -100,11 +100,27 @@ pub struct VerifierConfig { pub rpc_url: SensitiveUrl, pub svc_manager_addr: Address, pub max_blob_size: u32, + pub points_dir: Option, pub g1_url: Url, pub g2_url: Url, pub settlement_layer_confirmation_depth: u32, } +#[derive(Debug)] +enum PointFile { + Temp(NamedTempFile), + Path(String), +} + +impl PointFile { + fn path(&self) -> &str { + match self { + PointFile::Temp(file) => file.path().to_str().unwrap(), + PointFile::Path(path) => path, + } + } +} + /// Verifier used to verify the integrity of the blob info /// Kzg is used for commitment verification /// EigenDA service manager is used to connect to the service manager contract @@ -122,34 +138,43 @@ impl Verifier { pub const G2POINT: &'static str = "g2.point.powerOf2"; pub const POINT_SIZE: u32 = 32; - async fn save_point(url: Url, point: &str) -> Result<(), VerificationError> { + async fn download_temp_point(url: Url) -> Result { let response = reqwest::get(url) .await - .map_err(|e| VerificationError::LinkError(e.to_string()))?; + .map_err(|e| VerificationError::LinkError(e.to_string())) + .unwrap(); if !response.status().is_success() { return Err(VerificationError::LinkError( "Failed to get point".to_string(), )); } - let mut file = File::create(point) - .await - .map_err(|e| VerificationError::LinkError(e.to_string()))?; + let mut file = NamedTempFile::new().unwrap(); let content = response .bytes() .await - .map_err(|e| VerificationError::LinkError(e.to_string()))?; + .map_err(|e| VerificationError::LinkError(e.to_string())) + .unwrap(); file.write_all(&content) - .await - .map_err(|e| VerificationError::LinkError(e.to_string()))?; - Ok(()) + .map_err(|e| VerificationError::LinkError(e.to_string())) + .unwrap(); + Ok(file) } - async fn save_points(url_g1: Url, url_g2: Url) -> Result { - Self::save_point(url_g1, Self::G1POINT).await?; - Self::save_point(url_g2, Self::G2POINT).await?; - - Ok(".".to_string()) + async fn get_points(cfg: &VerifierConfig) -> Result<(PointFile, PointFile), VerificationError> { + match &cfg.points_dir { + Some(path) => Ok(( + PointFile::Path(format!("{}/{}", path, Self::G1POINT)), + PointFile::Path(format!("{}/{}", path, Self::G2POINT)), + )), + None => { + tracing::info!("Points for KZG setup not found, downloading temporary points"); + Ok(( + PointFile::Temp(Self::download_temp_point(cfg.g1_url.clone()).await?), + PointFile::Temp(Self::download_temp_point(cfg.g2_url.clone()).await?), + )) + } + } } pub(crate) async fn new( @@ -157,18 +182,19 @@ impl Verifier { signing_client: Arc, ) -> Result { let srs_points_to_load = cfg.max_blob_size / Self::POINT_SIZE; - let path = Self::save_points(cfg.clone().g1_url, cfg.clone().g2_url).await?; + let (g1_point_file, g2_point_file) = Self::get_points(&cfg).await?; + let g1_point_file_path = g1_point_file.path().to_string(); + let g2_point_file_path = g2_point_file.path().to_string(); let kzg_handle = tokio::task::spawn_blocking(move || { Kzg::setup( - &format!("{}/{}", path, Self::G1POINT), + &g1_point_file_path, //&format!("{}/{}", path, Self::G1POINT), "", - &format!("{}/{}", path, Self::G2POINT), + &g2_point_file_path, //&format!("{}/{}", path, Self::G2POINT), Self::SRSORDER, srs_points_to_load, "".to_string(), ) }); - let kzg = kzg_handle .await .map_err(|e| VerificationError::Kzg(KzgError::Setup(e.to_string())))? diff --git a/core/node/da_clients/src/eigen/verifier/tests.rs b/core/node/da_clients/src/eigen/verifier/tests.rs index 3946809eb915..f020388cfe26 100644 --- a/core/node/da_clients/src/eigen/verifier/tests.rs +++ b/core/node/da_clients/src/eigen/verifier/tests.rs @@ -24,6 +24,7 @@ fn get_verifier_config() -> VerifierConfig { max_blob_size: 2 * 1024 * 1024, g1_url: Url::parse("https://github.com/Layr-Labs/eigenda-proxy/raw/2fd70b99ef5bf137d7bbca3461cf9e1f2c899451/resources/g1.point").unwrap(), g2_url: Url::parse("https://github.com/Layr-Labs/eigenda-proxy/raw/2fd70b99ef5bf137d7bbca3461cf9e1f2c899451/resources/g2.point.powerOf2").unwrap(), + points_dir: None, settlement_layer_confirmation_depth: 0, } } From 2b896dc3bcb802b628da0539b0ae6cd5efdde036 Mon Sep 17 00:00:00 2001 From: perekopskiy <53865202+perekopskiy@users.noreply.github.com> Date: Mon, 20 Jan 2025 22:05:17 +0200 Subject: [PATCH 66/97] fix(docker): include l1 zkout in images (#3510) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ include l1 zkout in images ## Why ❔ it's required for genesis ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- docker/external-node/Dockerfile | 1 + docker/server-v2/Dockerfile | 1 + 2 files changed, 2 insertions(+) diff --git a/docker/external-node/Dockerfile b/docker/external-node/Dockerfile index 3c968b896e43..31fab51719a5 100644 --- a/docker/external-node/Dockerfile +++ b/docker/external-node/Dockerfile @@ -25,6 +25,7 @@ COPY --from=builder /usr/local/cargo/bin/sqlx /usr/bin COPY --from=builder /usr/src/zksync/docker/external-node/entrypoint.sh /usr/bin COPY contracts/system-contracts/zkout/ /contracts/system-contracts/zkout/ COPY contracts/l1-contracts/out/ /contracts/l1-contracts/out/ +COPY contracts/l1-contracts/zkout/ /contracts/l1-contracts/zkout/ COPY contracts/l2-contracts/zkout/ /contracts/l2-contracts/zkout/ COPY etc/tokens/ /etc/tokens/ COPY etc/ERC20/ /etc/ERC20/ diff --git a/docker/server-v2/Dockerfile b/docker/server-v2/Dockerfile index 5855bab8d7ab..869f6bdb463f 100644 --- a/docker/server-v2/Dockerfile +++ b/docker/server-v2/Dockerfile @@ -33,6 +33,7 @@ COPY --from=builder /usr/src/zksync/core/target/release/block_reverter /usr/bin COPY --from=builder /usr/src/zksync/core/target/release/merkle_tree_consistency_checker /usr/bin COPY contracts/system-contracts/zkout/ /contracts/system-contracts/zkout/ COPY contracts/l1-contracts/out/ /contracts/l1-contracts/out/ +COPY contracts/l1-contracts/zkout/ /contracts/l1-contracts/zkout/ COPY contracts/l2-contracts/zkout/ /contracts/l2-contracts/zkout/ COPY etc/tokens/ /etc/tokens/ COPY etc/ERC20/ /etc/ERC20/ From c140ba8f57caabf9c9bdd4bd8c9743a9ccf668be Mon Sep 17 00:00:00 2001 From: Stanislav Bezkorovainyi Date: Tue, 21 Jan 2025 08:12:09 +0100 Subject: [PATCH 67/97] fix(gateway): erc20 workaround for gateway upgrade (#3511) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ ## Why ❔ ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- core/node/eth_watch/src/client.rs | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/core/node/eth_watch/src/client.rs b/core/node/eth_watch/src/client.rs index 03b22df55995..1a48898c131d 100644 --- a/core/node/eth_watch/src/client.rs +++ b/core/node/eth_watch/src/client.rs @@ -15,7 +15,7 @@ use zksync_types::{ abi::ZkChainSpecificUpgradeData, api::{ChainAggProof, Log}, ethabi::{self, decode, encode, Contract, ParamType}, - web3::{keccak256, BlockId, BlockNumber, CallRequest, Filter, FilterBuilder}, + web3::{keccak256, BlockId, BlockNumber, Filter, FilterBuilder}, Address, L1BatchNumber, L2ChainId, SLChainId, H256, L2_NATIVE_TOKEN_VAULT_ADDRESS, SHARED_BRIDGE_ETHER_TOKEN_ADDRESS, U256, U64, }; @@ -511,28 +511,9 @@ where if base_token_l1_address == SHARED_BRIDGE_ETHER_TOKEN_ADDRESS { (String::from("Ether"), String::from("ETH")) } else { - // TODO(EVM-934): support non-standard tokens. - let selectors: [[u8; 4]; 2] = [ - ethabi::short_signature("name", &[]), - ethabi::short_signature("symbol", &[]), - ]; - let types: [ParamType; 2] = [ParamType::String, ParamType::String]; - - let mut decoded_result = vec![]; - for (selector, param_type) in selectors.into_iter().zip(types.into_iter()) { - let request = CallRequest { - to: Some(base_token_l1_address), - data: Some(selector.into()), - ..Default::default() - }; - let result = self.client.call_contract_function(request, None).await?; - // Base tokens are expected to support erc20 metadata - let mut token = ethabi::decode(&[param_type], &result.0) - .expect("base token does not support erc20 metadata"); - decoded_result.push(token.pop().unwrap()); - } - - (decoded_result[0].to_string(), decoded_result[1].to_string()) + // Due to an issue in the upgrade process, the automatically + // deployed wrapped base tokens will contain generic names + (String::from("Base Token"), String::from("BT")) }; let base_token_asset_id = encode_ntv_asset_id( From 86db9ff6aed4017b0914ff6795295c45246282bf Mon Sep 17 00:00:00 2001 From: perekopskiy <53865202+perekopskiy@users.noreply.github.com> Date: Tue, 21 Jan 2025 14:34:40 +0200 Subject: [PATCH 68/97] fix(api): use scaled version of batch fee input in `get_batch_fee_input_impl` (#3507) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ - Use scaled version of batch fee input in get_batch_fee_input_impl - Improve adjust_pubdata_price_for_tx so it assumes optimistic scenario ## Why ❔ Scaled fee input should be used for every user-facing API method ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- core/lib/multivm/src/utils/mod.rs | 14 +++++++++----- core/node/api_server/src/web3/namespaces/zks.rs | 4 +--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/core/lib/multivm/src/utils/mod.rs b/core/lib/multivm/src/utils/mod.rs index 7bb907efc9b8..825874747682 100644 --- a/core/lib/multivm/src/utils/mod.rs +++ b/core/lib/multivm/src/utils/mod.rs @@ -113,13 +113,13 @@ pub fn adjust_pubdata_price_for_tx( ) -> BatchFeeInput { // If no max base fee was provided, we just use the maximal one for convenience. let max_base_fee = max_base_fee.unwrap_or(U256::MAX); - let desired_gas_per_pubdata = + let bounded_tx_gas_per_pubdata_limit = tx_gas_per_pubdata_limit.min(get_max_gas_per_pubdata_byte(vm_version).into()); let (current_base_fee, current_gas_per_pubdata) = derive_base_fee_and_gas_per_pubdata(batch_fee_input, vm_version); - if U256::from(current_gas_per_pubdata) <= desired_gas_per_pubdata + if U256::from(current_gas_per_pubdata) <= bounded_tx_gas_per_pubdata_limit && U256::from(current_base_fee) <= max_base_fee { // gas per pubdata is already smaller than or equal to `tx_gas_per_pubdata_limit`. @@ -138,8 +138,9 @@ pub fn adjust_pubdata_price_for_tx( // `gasPerPubdata = ceil(17 * l1gasprice / fair_l2_gas_price)` // `gasPerPubdata <= 17 * l1gasprice / fair_l2_gas_price + 1` // `fair_l2_gas_price(gasPerPubdata - 1) / 17 <= l1gasprice` - let new_l1_gas_price = - fair_l2_gas_price * (desired_gas_per_pubdata - U256::from(1u32)) / U256::from(17); + let new_l1_gas_price = fair_l2_gas_price + * bounded_tx_gas_per_pubdata_limit.saturating_sub(U256::from(1u32)) + / U256::from(17); BatchFeeInput::L1Pegged(L1PeggedBatchFeeModelInput { l1_gas_price: new_l1_gas_price.as_u64(), @@ -154,11 +155,14 @@ pub fn adjust_pubdata_price_for_tx( current_l2_fair_gas_price }; + // We want to adjust gas per pubdata to be min(bounded_tx_gas_per_pubdata_limit, current_gas_per_pubdata). + let desired_gas_per_pubdata = + bounded_tx_gas_per_pubdata_limit.min(U256::from(current_gas_per_pubdata)); // `gasPerPubdata = ceil(fair_pubdata_price / fair_l2_gas_price)` // `gasPerPubdata <= fair_pubdata_price / fair_l2_gas_price + 1` // `fair_l2_gas_price(gasPerPubdata - 1) <= fair_pubdata_price` let new_fair_pubdata_price = - fair_l2_gas_price * (desired_gas_per_pubdata - U256::from(1u32)); + fair_l2_gas_price * desired_gas_per_pubdata.saturating_sub(U256::from(1u32)); BatchFeeInput::PubdataIndependent(PubdataIndependentBatchFeeModelInput { fair_pubdata_price: new_fair_pubdata_price.as_u64(), diff --git a/core/node/api_server/src/web3/namespaces/zks.rs b/core/node/api_server/src/web3/namespaces/zks.rs index b272f7c443e9..9f7c5662a631 100644 --- a/core/node/api_server/src/web3/namespaces/zks.rs +++ b/core/node/api_server/src/web3/namespaces/zks.rs @@ -677,9 +677,7 @@ impl ZksNamespace { Ok(self .state .tx_sender - .0 - .batch_fee_input_provider - .get_batch_fee_input() + .scaled_batch_fee_input() .await? .into_pubdata_independent()) } From 28803673fd7438bf2d0e6cefcc0552360727eb05 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Tue, 21 Jan 2025 10:09:17 -0300 Subject: [PATCH 69/97] refactor `VerifierClient` trait --- .../node/da_clients/src/eigen/verifier/mod.rs | 217 +++++++++--------- .../da_clients/src/eigen/verifier/tests.rs | 68 +++++- 2 files changed, 164 insertions(+), 121 deletions(-) diff --git a/core/node/da_clients/src/eigen/verifier/mod.rs b/core/node/da_clients/src/eigen/verifier/mod.rs index 15b3378498ae..1c8d464d8844 100644 --- a/core/node/da_clients/src/eigen/verifier/mod.rs +++ b/core/node/da_clients/src/eigen/verifier/mod.rs @@ -6,12 +6,8 @@ use rust_kzg_bn254::{blob::Blob, kzg::Kzg, polynomial::PolynomialFormat}; use tempfile::NamedTempFile; use url::Url; use zksync_basic_types::web3::CallRequest; -use zksync_eth_client::{EnrichedClientError, EnrichedClientResult, EthInterface}; -use zksync_types::{ - url::SensitiveUrl, - web3::{self, BlockId, BlockNumber}, - Address, U256, U64, -}; +use zksync_eth_client::{EnrichedClientError, EthInterface}; +use zksync_types::{url::SensitiveUrl, web3, Address, U256}; use zksync_web3_decl::client::{DynClient, L1}; use super::blob_info::{BatchHeader, BlobHeader, BlobInfo, BlobQuorumParam, G1Commitment}; @@ -19,31 +15,116 @@ use super::blob_info::{BatchHeader, BlobHeader, BlobInfo, BlobQuorumParam, G1Com #[cfg(test)] mod tests; +fn decode_bytes(encoded: Vec) -> Result, VerificationError> { + let output_type = [ParamType::Bytes]; + let tokens = ethabi::decode(&output_type, &encoded) + .map_err(|e| ServiceManagerError::Decoding(e.to_string()))?; + + // Safe unwrap because decode guarantees type correctness and non-empty output + let token = tokens.into_iter().next().unwrap(); + + // Safe unwrap, as type is guaranteed + Ok(token.into_bytes().unwrap()) +} + #[async_trait::async_trait] pub trait VerifierClient: Sync + Send + std::fmt::Debug { - /// Returns the current block number. - async fn block_number(&self) -> EnrichedClientResult; + async fn batch_id_to_batch_metadata_hash( + &self, + blob_info: &BlobInfo, + svc_manager_addr: Address, + ) -> Result, VerificationError>; + + async fn quorum_adversary_threshold_percentages( + &self, + quorum_number: u32, + svc_manager_addr: Address, + ) -> Result; - /// Invokes a function on a contract specified by `contract_address` / `contract_abi` using `eth_call`. - async fn call_contract_function( + async fn quorum_numbers_required( &self, - request: web3::CallRequest, - block: Option, - ) -> EnrichedClientResult; + svc_manager_addr: Address, + ) -> Result, VerificationError>; } #[async_trait::async_trait] impl VerifierClient for Box> { - async fn block_number(&self) -> EnrichedClientResult { - self.as_ref().block_number().await + async fn batch_id_to_batch_metadata_hash( + &self, + blob_info: &BlobInfo, + svc_manager_addr: Address, + ) -> Result, VerificationError> { + let mut data = vec![]; + let func_selector = + ethabi::short_signature("batchIdToBatchMetadataHash", &[ParamType::Uint(32)]); + data.extend_from_slice(&func_selector); + let batch_id_data = encode(&[Token::Uint(U256::from( + blob_info.blob_verification_proof.batch_id, + ))]); + data.extend_from_slice(&batch_id_data); + + let call_request = CallRequest { + to: Some(svc_manager_addr), + data: Some(zksync_basic_types::web3::Bytes(data)), + ..Default::default() + }; + + let res = self + .as_ref() + .call_contract_function(call_request, None) + .await + .map_err(ServiceManagerError::EnrichedClient)?; + + Ok(res.0.to_vec()) } - async fn call_contract_function( + async fn quorum_adversary_threshold_percentages( &self, - request: web3::CallRequest, - block: Option, - ) -> EnrichedClientResult { - self.as_ref().call_contract_function(request, block).await + quorum_number: u32, + svc_manager_addr: Address, + ) -> Result { + let func_selector = ethabi::short_signature("quorumAdversaryThresholdPercentages", &[]); + let data = func_selector.to_vec(); + + let call_request = CallRequest { + to: Some(svc_manager_addr), + data: Some(zksync_basic_types::web3::Bytes(data)), + ..Default::default() + }; + + let res = self + .as_ref() + .call_contract_function(call_request, None) + .await + .map_err(ServiceManagerError::EnrichedClient)?; + + let percentages = decode_bytes(res.0)?; + + if percentages.len() > quorum_number as usize { + return Ok(percentages[quorum_number as usize]); + } + Ok(0) + } + + async fn quorum_numbers_required( + &self, + svc_manager_addr: Address, + ) -> Result, VerificationError> { + let func_selector = ethabi::short_signature("quorumNumbersRequired", &[]); + let data = func_selector.to_vec(); + let call_request = CallRequest { + to: Some(svc_manager_addr), + data: Some(zksync_basic_types::web3::Bytes(data)), + ..Default::default() + }; + + let res = self + .as_ref() + .call_contract_function(call_request, None) + .await + .map_err(ServiceManagerError::EnrichedClient)?; + + decode_bytes(res.0.to_vec()) } } @@ -347,56 +428,14 @@ impl Verifier { web3::keccak256(&hash_encoded).to_vec() } - /// Retrieves the block to make the request to the service manager - async fn get_context_block(&self) -> Result { - let latest = self - .signing_client - .as_ref() - .block_number() - .await - .map_err(ServiceManagerError::EnrichedClient)? - .as_u64(); - - let depth = self - .cfg - .settlement_layer_confirmation_depth - .saturating_sub(1); - let block_to_return = latest.saturating_sub(depth as u64); - Ok(block_to_return) - } - async fn call_batch_id_to_metadata_hash( &self, blob_info: &BlobInfo, ) -> Result, VerificationError> { - let context_block = self.get_context_block().await?; - - let mut data = vec![]; - let func_selector = - ethabi::short_signature("batchIdToBatchMetadataHash", &[ParamType::Uint(32)]); - data.extend_from_slice(&func_selector); - let batch_id_data = encode(&[Token::Uint(U256::from( - blob_info.blob_verification_proof.batch_id, - ))]); - data.extend_from_slice(&batch_id_data); - - let call_request = CallRequest { - to: Some(self.cfg.svc_manager_addr), - data: Some(zksync_basic_types::web3::Bytes(data)), - ..Default::default() - }; - - let res = self - .signing_client + self.signing_client .as_ref() - .call_contract_function( - call_request, - Some(BlockId::Number(BlockNumber::Number(context_block.into()))), - ) + .batch_id_to_batch_metadata_hash(blob_info, self.cfg.svc_manager_addr) .await - .map_err(ServiceManagerError::EnrichedClient)?; - - Ok(res.0.to_vec()) } /// Verifies the certificate batch hash @@ -431,63 +470,21 @@ impl Verifier { Ok(()) } - fn decode_bytes(&self, encoded: Vec) -> Result, VerificationError> { - let output_type = [ParamType::Bytes]; - let tokens = ethabi::decode(&output_type, &encoded) - .map_err(|e| ServiceManagerError::Decoding(e.to_string()))?; - - // Safe unwrap because decode guarantees type correctness and non-empty output - let token = tokens.into_iter().next().unwrap(); - - // Safe unwrap, as type is guaranteed - Ok(token.into_bytes().unwrap()) - } - async fn get_quorum_adversary_threshold( &self, quorum_number: u32, ) -> Result { - let func_selector = ethabi::short_signature("quorumAdversaryThresholdPercentages", &[]); - let data = func_selector.to_vec(); - - let call_request = CallRequest { - to: Some(self.cfg.svc_manager_addr), - data: Some(zksync_basic_types::web3::Bytes(data)), - ..Default::default() - }; - - let res = self - .signing_client + self.signing_client .as_ref() - .call_contract_function(call_request, None) + .quorum_adversary_threshold_percentages(quorum_number, self.cfg.svc_manager_addr) .await - .map_err(ServiceManagerError::EnrichedClient)?; - - let percentages = self.decode_bytes(res.0)?; - - if percentages.len() > quorum_number as usize { - return Ok(percentages[quorum_number as usize]); - } - Ok(0) } async fn call_quorum_numbers_required(&self) -> Result, VerificationError> { - let func_selector = ethabi::short_signature("quorumNumbersRequired", &[]); - let data = func_selector.to_vec(); - let call_request = CallRequest { - to: Some(self.cfg.svc_manager_addr), - data: Some(zksync_basic_types::web3::Bytes(data)), - ..Default::default() - }; - - let res = self - .signing_client + self.signing_client .as_ref() - .call_contract_function(call_request, None) + .quorum_numbers_required(self.cfg.svc_manager_addr) .await - .map_err(ServiceManagerError::EnrichedClient)?; - - self.decode_bytes(res.0.to_vec()) } /// Verifies that the certificate's blob quorum params are correct diff --git a/core/node/da_clients/src/eigen/verifier/tests.rs b/core/node/da_clients/src/eigen/verifier/tests.rs index f020388cfe26..4f1e720fabbc 100644 --- a/core/node/da_clients/src/eigen/verifier/tests.rs +++ b/core/node/da_clients/src/eigen/verifier/tests.rs @@ -1,14 +1,15 @@ use std::{collections::HashMap, str::FromStr, sync::Arc}; +use ethabi::{ParamType, Token}; use url::Url; -use zksync_eth_client::EnrichedClientResult; use zksync_types::{ url::SensitiveUrl, - web3::{BlockId, Bytes, CallRequest}, - Address, H160, U64, + web3::{Bytes, CallRequest}, + Address, H160, U256, }; use zksync_web3_decl::client::{Client, DynClient, L1}; +use super::VerificationError; use crate::eigen::{ blob_info::{ BatchHeader, BatchMetadata, BlobHeader, BlobInfo, BlobQuorumParam, BlobVerificationProof, @@ -47,17 +48,62 @@ impl MockVerifierClient { #[async_trait::async_trait] impl VerifierClient for MockVerifierClient { - async fn block_number(&self) -> EnrichedClientResult { - Ok(U64::from(42)) + async fn batch_id_to_batch_metadata_hash( + &self, + blob_info: &BlobInfo, + svc_manager_addr: Address, + ) -> Result, VerificationError> { + let mut data = vec![]; + let func_selector = + ethabi::short_signature("batchIdToBatchMetadataHash", &[ParamType::Uint(32)]); + data.extend_from_slice(&func_selector); + let batch_id_data = ethabi::encode(&[Token::Uint(U256::from( + blob_info.blob_verification_proof.batch_id, + ))]); + data.extend_from_slice(&batch_id_data); + + let call_request = CallRequest { + to: Some(svc_manager_addr), + data: Some(zksync_basic_types::web3::Bytes(data)), + ..Default::default() + }; + + let req = serde_json::to_string(&call_request).unwrap(); + Ok(self.replies.get(&req).unwrap().clone().0) } - async fn call_contract_function( + async fn quorum_adversary_threshold_percentages( &self, - request: CallRequest, - _block: Option, - ) -> EnrichedClientResult { - let req = serde_json::to_string(&request).unwrap(); - Ok(self.replies.get(&req).unwrap().clone()) + _quorum_number: u32, + svc_manager_addr: Address, + ) -> Result { + let func_selector = ethabi::short_signature("quorumAdversaryThresholdPercentages", &[]); + let data = func_selector.to_vec(); + + let call_request = CallRequest { + to: Some(svc_manager_addr), + data: Some(zksync_basic_types::web3::Bytes(data)), + ..Default::default() + }; + + let req = serde_json::to_string(&call_request).unwrap(); + Ok(self.replies.get(&req).unwrap().clone().0[0]) + } + + async fn quorum_numbers_required( + &self, + svc_manager_addr: Address, + ) -> Result, VerificationError> { + let func_selector = ethabi::short_signature("quorumNumbersRequired", &[]); + let data = func_selector.to_vec(); + let call_request = CallRequest { + to: Some(svc_manager_addr), + data: Some(zksync_basic_types::web3::Bytes(data)), + ..Default::default() + }; + + let req = serde_json::to_string(&call_request).unwrap(); + Ok(self.replies.get(&req).unwrap().clone().0) } } From 52de75845caf4e3ce981c2d761bdeffbc4ba1af4 Mon Sep 17 00:00:00 2001 From: zksync-era-bot <147085853+zksync-era-bot@users.noreply.github.com> Date: Tue, 21 Jan 2025 17:48:49 +0400 Subject: [PATCH 70/97] chore(main): release core 26.1.0 (#3499) :robot: I have created a release *beep* *boop* --- ## [26.1.0](https://github.com/matter-labs/zksync-era/compare/core-v26.0.0...core-v26.1.0) (2025-01-21) ### Features * update l2 erc20 bridge address in updater as well ([#3500](https://github.com/matter-labs/zksync-era/issues/3500)) ([fe3c7b2](https://github.com/matter-labs/zksync-era/commit/fe3c7b2583bc4f9277e186334e5822ddf95bdcd0)) * **vm:** Implement call tracing for fast VM ([#2905](https://github.com/matter-labs/zksync-era/issues/2905)) ([731b824](https://github.com/matter-labs/zksync-era/commit/731b8240abd4c0cfa42f2ce89c23f8ebf67e1bf2)) ### Bug Fixes * copy special case to fast VM call tracer ([#3509](https://github.com/matter-labs/zksync-era/issues/3509)) ([995e583](https://github.com/matter-labs/zksync-era/commit/995e583aa9b4ef6e0d8697fbb040e4b991a4248d)) * fix execute encoding for transactions ([#3501](https://github.com/matter-labs/zksync-era/issues/3501)) ([4c381a8](https://github.com/matter-labs/zksync-era/commit/4c381a84346f8ab88d3f01dc2848c7fb5f2b788d)) * **gateway:** erc20 workaround for gateway upgrade ([#3511](https://github.com/matter-labs/zksync-era/issues/3511)) ([c140ba8](https://github.com/matter-labs/zksync-era/commit/c140ba8f57caabf9c9bdd4bd8c9743a9ccf668be)) ### Performance Improvements * optimize get_unsealed_l1_batch_inner ([#3491](https://github.com/matter-labs/zksync-era/issues/3491)) ([9b121c9](https://github.com/matter-labs/zksync-era/commit/9b121c96bbb2e53be74aa81e0ca250ce9251f8db)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). --------- Co-authored-by: zksync-era-bot --- .github/release-please/manifest.json | 2 +- core/CHANGELOG.md | 20 ++++++++++++++++++++ core/Cargo.lock | 2 +- core/bin/external_node/Cargo.toml | 2 +- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/.github/release-please/manifest.json b/.github/release-please/manifest.json index 782c7a30086c..0ba598bb0516 100644 --- a/.github/release-please/manifest.json +++ b/.github/release-please/manifest.json @@ -1,5 +1,5 @@ { - "core": "26.0.0", + "core": "26.1.0", "prover": "17.1.1", "zkstack_cli": "0.1.2" } diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index d0491b179035..256c83058ee2 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## [26.1.0](https://github.com/matter-labs/zksync-era/compare/core-v26.0.0...core-v26.1.0) (2025-01-21) + + +### Features + +* update l2 erc20 bridge address in updater as well ([#3500](https://github.com/matter-labs/zksync-era/issues/3500)) ([fe3c7b2](https://github.com/matter-labs/zksync-era/commit/fe3c7b2583bc4f9277e186334e5822ddf95bdcd0)) +* **vm:** Implement call tracing for fast VM ([#2905](https://github.com/matter-labs/zksync-era/issues/2905)) ([731b824](https://github.com/matter-labs/zksync-era/commit/731b8240abd4c0cfa42f2ce89c23f8ebf67e1bf2)) + + +### Bug Fixes + +* copy special case to fast VM call tracer ([#3509](https://github.com/matter-labs/zksync-era/issues/3509)) ([995e583](https://github.com/matter-labs/zksync-era/commit/995e583aa9b4ef6e0d8697fbb040e4b991a4248d)) +* fix execute encoding for transactions ([#3501](https://github.com/matter-labs/zksync-era/issues/3501)) ([4c381a8](https://github.com/matter-labs/zksync-era/commit/4c381a84346f8ab88d3f01dc2848c7fb5f2b788d)) +* **gateway:** erc20 workaround for gateway upgrade ([#3511](https://github.com/matter-labs/zksync-era/issues/3511)) ([c140ba8](https://github.com/matter-labs/zksync-era/commit/c140ba8f57caabf9c9bdd4bd8c9743a9ccf668be)) + + +### Performance Improvements + +* optimize get_unsealed_l1_batch_inner ([#3491](https://github.com/matter-labs/zksync-era/issues/3491)) ([9b121c9](https://github.com/matter-labs/zksync-era/commit/9b121c96bbb2e53be74aa81e0ca250ce9251f8db)) + ## [26.0.0](https://github.com/matter-labs/zksync-era/compare/core-v25.4.0...core-v26.0.0) (2025-01-17) diff --git a/core/Cargo.lock b/core/Cargo.lock index 0c7b38d2d163..d064b2a3d7bc 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -11994,7 +11994,7 @@ dependencies = [ [[package]] name = "zksync_external_node" -version = "26.0.0" +version = "26.1.0" dependencies = [ "anyhow", "assert_matches", diff --git a/core/bin/external_node/Cargo.toml b/core/bin/external_node/Cargo.toml index c8b63a12f70f..f9e494a62780 100644 --- a/core/bin/external_node/Cargo.toml +++ b/core/bin/external_node/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "zksync_external_node" description = "Non-validator ZKsync node" -version = "26.0.0" # x-release-please-version +version = "26.1.0" # x-release-please-version edition.workspace = true authors.workspace = true homepage.workspace = true From 6fcecb9daebfe1e06a29951613b54a3f22e6087c Mon Sep 17 00:00:00 2001 From: Yury Akudovich Date: Tue, 21 Jan 2025 14:49:43 +0100 Subject: [PATCH 71/97] ci: Remove prover-gpu-fri and witness-vector-generator from build (#3513) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Remove prover-gpu-fri and witness-vector-generator from build. ## Why ❔ To cleanup unneeded builds. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. ref ZKD-2084 --- ...r.yml => build-circuit-prover-gpu-gar.yml} | 28 +------------ .github/workflows/build-docker-from-tag.yml | 4 +- .../build-proof-fri-gpu-compressor-gar.yml | 4 +- .github/workflows/build-prover-template.yml | 41 ------------------- .github/workflows/release-test-stage.yml | 4 +- 5 files changed, 7 insertions(+), 74 deletions(-) rename .github/workflows/{build-prover-fri-gpu-gar-and-circuit-prover-gpu-gar.yml => build-circuit-prover-gpu-gar.yml} (67%) diff --git a/.github/workflows/build-prover-fri-gpu-gar-and-circuit-prover-gpu-gar.yml b/.github/workflows/build-circuit-prover-gpu-gar.yml similarity index 67% rename from .github/workflows/build-prover-fri-gpu-gar-and-circuit-prover-gpu-gar.yml rename to .github/workflows/build-circuit-prover-gpu-gar.yml index 30990889caf6..a8e86d545c9f 100644 --- a/.github/workflows/build-prover-fri-gpu-gar-and-circuit-prover-gpu-gar.yml +++ b/.github/workflows/build-circuit-prover-gpu-gar.yml @@ -27,7 +27,7 @@ jobs: - name: Download Setup data run: | - gsutil -m rsync -r gs://matterlabs-setup-data-us/${{ inputs.setup_keys_id }} docker/prover-gpu-fri-gar + gsutil -m rsync -r gs://matterlabs-setup-data-us/${{ inputs.setup_keys_id }} docker/circuit-prover-gpu-gar - name: Login to us-central1 GAR run: | @@ -47,32 +47,6 @@ jobs: run: | gcloud auth print-access-token --lifetime=7200 --impersonate-service-account=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com | docker login -u oauth2accesstoken --password-stdin https://europe-docker.pkg.dev - - name: Build and push prover-gpu-fri-gar - uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 - with: - context: docker/prover-gpu-fri-gar - build-args: | - PROVER_IMAGE=${{ inputs.protocol_version }}-${{ inputs.image_tag_suffix }} - push: true - tags: | - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/prover-fri-gpu-gar:2.0-${{ inputs.protocol_version }}-${{ inputs.image_tag_suffix }} - - - name: Build and push prover-gpu-fri-gar to Asia GAR - run: | - docker buildx imagetools create \ - --tag asia-docker.pkg.dev/matterlabs-infra/matterlabs-docker/prover-fri-gpu-gar:2.0-${{ inputs.protocol_version }}-${{ inputs.image_tag_suffix }} \ - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/prover-fri-gpu-gar:2.0-${{ inputs.protocol_version }}-${{ inputs.image_tag_suffix }} - - - name: Build and push prover-gpu-fri-gar to Europe GAR - run: | - docker buildx imagetools create \ - --tag europe-docker.pkg.dev/matterlabs-infra/matterlabs-docker/prover-fri-gpu-gar:2.0-${{ inputs.protocol_version }}-${{ inputs.image_tag_suffix }} \ - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/prover-fri-gpu-gar:2.0-${{ inputs.protocol_version }}-${{ inputs.image_tag_suffix }} - - - name: Move Setup data from prover-gpu-fri-gar to circuit-prover-gpu-gar - run: | - mv -v docker/prover-gpu-fri-gar/*.bin docker/circuit-prover-gpu-gar/ - - name: Build and push circuit-prover-gpu-gar uses: docker/build-push-action@5cd11c3a4ced054e52742c5fd54dca954e0edd85 # v6.7.0 with: diff --git a/.github/workflows/build-docker-from-tag.yml b/.github/workflows/build-docker-from-tag.yml index 34f4b55903d2..3d637a224a99 100644 --- a/.github/workflows/build-docker-from-tag.yml +++ b/.github/workflows/build-docker-from-tag.yml @@ -110,10 +110,10 @@ jobs: DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} - build-gar-prover-fri-gpu-and-circuit-prover-gpu-gar: + build-circuit-prover-gpu-gar: name: Build GAR prover FRI GPU needs: [setup, build-push-prover-images] - uses: ./.github/workflows/build-prover-fri-gpu-gar-and-circuit-prover-gpu-gar.yml + uses: ./.github/workflows/build-circuit-prover-gpu-gar.yml if: contains(github.ref_name, 'prover') with: setup_keys_id: ${{ needs.setup.outputs.prover_fri_gpu_key_id }} diff --git a/.github/workflows/build-proof-fri-gpu-compressor-gar.yml b/.github/workflows/build-proof-fri-gpu-compressor-gar.yml index e11a49640369..05255eeb2c8d 100644 --- a/.github/workflows/build-proof-fri-gpu-compressor-gar.yml +++ b/.github/workflows/build-proof-fri-gpu-compressor-gar.yml @@ -58,13 +58,13 @@ jobs: tags: | us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/proof-fri-gpu-compressor-gar:2.0-${{ inputs.protocol_version }}-${{ inputs.image_tag_suffix }} - - name: Build and push prover-gpu-fri-gar to Asia GAR + - name: Build and push proof-fri-gpu-compressor-gar to Asia GAR run: | docker buildx imagetools create \ --tag asia-docker.pkg.dev/matterlabs-infra/matterlabs-docker/proof-fri-gpu-compressor-gar:2.0-${{ inputs.protocol_version }}-${{ inputs.image_tag_suffix }} \ us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/proof-fri-gpu-compressor-gar:2.0-${{ inputs.protocol_version }}-${{ inputs.image_tag_suffix }} - - name: Build and push prover-gpu-fri-gar to Europe GAR + - name: Build and push proof-fri-gpu-compressor-gar to Europe GAR run: | docker buildx imagetools create \ --tag europe-docker.pkg.dev/matterlabs-infra/matterlabs-docker/proof-fri-gpu-compressor-gar:2.0-${{ inputs.protocol_version }}-${{ inputs.image_tag_suffix }} \ diff --git a/.github/workflows/build-prover-template.yml b/.github/workflows/build-prover-template.yml index 7d2920a2af4e..dc097b240b70 100644 --- a/.github/workflows/build-prover-template.yml +++ b/.github/workflows/build-prover-template.yml @@ -98,8 +98,6 @@ jobs: matrix: components: - witness-generator - - prover-gpu-fri - - witness-vector-generator - prover-fri-gateway - prover-job-monitor - proof-fri-gpu-compressor @@ -201,42 +199,3 @@ jobs: docker push us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.components }}:latest docker push ghcr.io/${{ github.repository_owner }}/${{ matrix.components }}:latest docker push matterlabs/${{ matrix.components }}:latest - - copy-images: - name: Copy images between docker registries - needs: [build-images, get-protocol-version] - env: - PROTOCOL_VERSION: ${{ needs.get-protocol-version.outputs.protocol_version }} - runs-on: matterlabs-ci-runner - if: ${{ inputs.action == 'push' }} - strategy: - matrix: - component: - - witness-vector-generator - steps: - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 - - - name: Login to us-central1 GAR - run: | - gcloud auth print-access-token --lifetime=7200 --impersonate-service-account=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com | docker login -u oauth2accesstoken --password-stdin https://us-docker.pkg.dev - - - name: Login and push to Asia GAR - run: | - gcloud auth print-access-token --lifetime=7200 --impersonate-service-account=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com | docker login -u oauth2accesstoken --password-stdin https://asia-docker.pkg.dev - docker buildx imagetools create \ - --tag asia-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ inputs.image_tag_suffix }} \ - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ inputs.image_tag_suffix }} - docker buildx imagetools create \ - --tag asia-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ inputs.image_tag_suffix }} \ - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ inputs.image_tag_suffix }} - - - name: Login and push to Europe GAR - run: | - gcloud auth print-access-token --lifetime=7200 --impersonate-service-account=gha-ci-runners@matterlabs-infra.iam.gserviceaccount.com | docker login -u oauth2accesstoken --password-stdin https://europe-docker.pkg.dev - docker buildx imagetools create \ - --tag europe-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ inputs.image_tag_suffix }} \ - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ env.PROTOCOL_VERSION }}-${{ inputs.image_tag_suffix }} - docker buildx imagetools create \ - --tag europe-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ inputs.image_tag_suffix }} \ - us-docker.pkg.dev/matterlabs-infra/matterlabs-docker/${{ matrix.component }}:2.0-${{ inputs.image_tag_suffix }} diff --git a/.github/workflows/release-test-stage.yml b/.github/workflows/release-test-stage.yml index afd33979234d..cff6c5ee0b3f 100644 --- a/.github/workflows/release-test-stage.yml +++ b/.github/workflows/release-test-stage.yml @@ -121,10 +121,10 @@ jobs: DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} - build-gar-prover-fri-gpu-and-circuit-prover-gpu-gar: + build-circuit-prover-gpu-gar: name: Build GAR prover FRI GPU needs: [setup, build-push-prover-images] - uses: ./.github/workflows/build-prover-fri-gpu-gar-and-circuit-prover-gpu-gar.yml + uses: ./.github/workflows/build-circuit-prover-gpu-gar.yml if: needs.changed_files.outputs.prover == 'true' || needs.changed_files.outputs.all == 'true' with: setup_keys_id: ${{ needs.setup.outputs.prover_fri_gpu_key_id }} From 5a7e533473adeeb68215a270788ee788b0f687c1 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Tue, 21 Jan 2025 11:15:13 -0300 Subject: [PATCH 72/97] remove `VerifierConfig` --- core/node/da_clients/src/eigen/sdk.rs | 34 ++++------- .../node/da_clients/src/eigen/verifier/mod.rs | 60 ++++++++----------- .../da_clients/src/eigen/verifier/tests.rs | 56 +++++++---------- 3 files changed, 57 insertions(+), 93 deletions(-) diff --git a/core/node/da_clients/src/eigen/sdk.rs b/core/node/da_clients/src/eigen/sdk.rs index fed9e3f63d82..e0fc13c288e2 100644 --- a/core/node/da_clients/src/eigen/sdk.rs +++ b/core/node/da_clients/src/eigen/sdk.rs @@ -8,15 +8,11 @@ use tonic::{ transport::{Channel, ClientTlsConfig, Endpoint}, Streaming, }; -use url::Url; use zksync_config::EigenConfig; use zksync_web3_decl::client::{Client, DynClient, L1}; use super::{ - blob_info::BlobInfo, - disperser::BlobInfo as DisperserBlobInfo, - verifier::{Verifier, VerifierConfig}, - GetBlobData, + blob_info::BlobInfo, disperser::BlobInfo as DisperserBlobInfo, verifier::Verifier, GetBlobData, }; use crate::eigen::{ blob_info, @@ -45,37 +41,27 @@ impl RawEigenClient { pub async fn new( private_key: SecretKey, - config: EigenConfig, + cfg: EigenConfig, get_blob_data: Arc, ) -> anyhow::Result { let endpoint = - Endpoint::from_str(config.disperser_rpc.as_str())?.tls_config(ClientTlsConfig::new())?; + Endpoint::from_str(cfg.disperser_rpc.as_str())?.tls_config(ClientTlsConfig::new())?; let client = DisperserClient::connect(endpoint).await?; - let verifier_config = VerifierConfig { - rpc_url: config - .eigenda_eth_rpc - .clone() - .ok_or(anyhow::anyhow!("EigenDA ETH RPC not set"))?, - svc_manager_addr: config.eigenda_svc_manager_address, - max_blob_size: Self::BLOB_SIZE_LIMIT as u32, - g1_url: Url::parse(&config.g1_url)?, - g2_url: Url::parse(&config.g2_url)?, - points_dir: config.points_dir.clone(), - settlement_layer_confirmation_depth: config.settlement_layer_confirmation_depth, - }; - - let url = verifier_config.rpc_url.clone(); - let query_client: Client = Client::http(url)?.build(); + let rpc_url = cfg + .eigenda_eth_rpc + .clone() + .ok_or(anyhow::anyhow!("EigenDA ETH RPC not set"))?; + let query_client: Client = Client::http(rpc_url)?.build(); let query_client = Box::new(query_client) as Box>; - let verifier = Verifier::new(verifier_config, Arc::new(query_client)) + let verifier = Verifier::new(cfg.clone(), Arc::new(query_client)) .await .context("Failed to create verifier")?; Ok(RawEigenClient { client, private_key, - config, + config: cfg, verifier, get_blob_data, }) diff --git a/core/node/da_clients/src/eigen/verifier/mod.rs b/core/node/da_clients/src/eigen/verifier/mod.rs index 1c8d464d8844..f21c3874e544 100644 --- a/core/node/da_clients/src/eigen/verifier/mod.rs +++ b/core/node/da_clients/src/eigen/verifier/mod.rs @@ -4,13 +4,16 @@ use ark_bn254::{Fq, G1Affine}; use ethabi::{encode, ParamType, Token}; use rust_kzg_bn254::{blob::Blob, kzg::Kzg, polynomial::PolynomialFormat}; use tempfile::NamedTempFile; -use url::Url; use zksync_basic_types::web3::CallRequest; +use zksync_config::EigenConfig; use zksync_eth_client::{EnrichedClientError, EthInterface}; -use zksync_types::{url::SensitiveUrl, web3, Address, U256}; +use zksync_types::{web3, Address, U256}; use zksync_web3_decl::client::{DynClient, L1}; -use super::blob_info::{BatchHeader, BlobHeader, BlobInfo, BlobQuorumParam, G1Commitment}; +use super::{ + blob_info::{BatchHeader, BlobHeader, BlobInfo, BlobQuorumParam, G1Commitment}, + sdk::RawEigenClient, +}; #[cfg(test)] mod tests; @@ -175,18 +178,6 @@ pub enum VerificationError { LinkError(String), } -/// Configuration for the verifier used for authenticated dispersals -#[derive(Debug, Clone)] -pub struct VerifierConfig { - pub rpc_url: SensitiveUrl, - pub svc_manager_addr: Address, - pub max_blob_size: u32, - pub points_dir: Option, - pub g1_url: Url, - pub g2_url: Url, - pub settlement_layer_confirmation_depth: u32, -} - #[derive(Debug)] enum PointFile { Temp(NamedTempFile), @@ -208,8 +199,8 @@ impl PointFile { #[derive(Debug)] pub struct Verifier { kzg: Kzg, - cfg: VerifierConfig, - signing_client: Arc, + cfg: EigenConfig, + client: Arc, } impl Verifier { @@ -219,7 +210,7 @@ impl Verifier { pub const G2POINT: &'static str = "g2.point.powerOf2"; pub const POINT_SIZE: u32 = 32; - async fn download_temp_point(url: Url) -> Result { + async fn download_temp_point(url: &String) -> Result { let response = reqwest::get(url) .await .map_err(|e| VerificationError::LinkError(e.to_string())) @@ -242,7 +233,7 @@ impl Verifier { Ok(file) } - async fn get_points(cfg: &VerifierConfig) -> Result<(PointFile, PointFile), VerificationError> { + async fn get_points(cfg: &EigenConfig) -> Result<(PointFile, PointFile), VerificationError> { match &cfg.points_dir { Some(path) => Ok(( PointFile::Path(format!("{}/{}", path, Self::G1POINT)), @@ -251,18 +242,18 @@ impl Verifier { None => { tracing::info!("Points for KZG setup not found, downloading temporary points"); Ok(( - PointFile::Temp(Self::download_temp_point(cfg.g1_url.clone()).await?), - PointFile::Temp(Self::download_temp_point(cfg.g2_url.clone()).await?), + PointFile::Temp(Self::download_temp_point(&cfg.g1_url).await?), + PointFile::Temp(Self::download_temp_point(&cfg.g2_url).await?), )) } } } pub(crate) async fn new( - cfg: VerifierConfig, - signing_client: Arc, + cfg: EigenConfig, + client: Arc, ) -> Result { - let srs_points_to_load = cfg.max_blob_size / Self::POINT_SIZE; + let srs_points_to_load = RawEigenClient::blob_size_limit() as u32 / Self::POINT_SIZE; // TODO: MAKE BLOB_SIZE_LIMIT part of Self? let (g1_point_file, g2_point_file) = Self::get_points(&cfg).await?; let g1_point_file_path = g1_point_file.path().to_string(); let g2_point_file_path = g2_point_file.path().to_string(); @@ -281,11 +272,7 @@ impl Verifier { .map_err(|e| VerificationError::Kzg(KzgError::Setup(e.to_string())))? .map_err(KzgError::Internal)?; - Ok(Self { - kzg, - cfg, - signing_client, - }) + Ok(Self { kzg, cfg, client }) } /// Return the commitment from a blob @@ -432,9 +419,9 @@ impl Verifier { &self, blob_info: &BlobInfo, ) -> Result, VerificationError> { - self.signing_client + self.client .as_ref() - .batch_id_to_batch_metadata_hash(blob_info, self.cfg.svc_manager_addr) + .batch_id_to_batch_metadata_hash(blob_info, self.cfg.eigenda_svc_manager_address) .await } @@ -474,16 +461,19 @@ impl Verifier { &self, quorum_number: u32, ) -> Result { - self.signing_client + self.client .as_ref() - .quorum_adversary_threshold_percentages(quorum_number, self.cfg.svc_manager_addr) + .quorum_adversary_threshold_percentages( + quorum_number, + self.cfg.eigenda_svc_manager_address, + ) .await } async fn call_quorum_numbers_required(&self) -> Result, VerificationError> { - self.signing_client + self.client .as_ref() - .quorum_numbers_required(self.cfg.svc_manager_addr) + .quorum_numbers_required(self.cfg.eigenda_svc_manager_address) .await } diff --git a/core/node/da_clients/src/eigen/verifier/tests.rs b/core/node/da_clients/src/eigen/verifier/tests.rs index 4f1e720fabbc..e2d67fd45827 100644 --- a/core/node/da_clients/src/eigen/verifier/tests.rs +++ b/core/node/da_clients/src/eigen/verifier/tests.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, str::FromStr, sync::Arc}; use ethabi::{ParamType, Token}; -use url::Url; +use zksync_config::EigenConfig; use zksync_types::{ url::SensitiveUrl, web3::{Bytes, CallRequest}, @@ -15,21 +15,9 @@ use crate::eigen::{ BatchHeader, BatchMetadata, BlobHeader, BlobInfo, BlobQuorumParam, BlobVerificationProof, G1Commitment, }, - verifier::{Verifier, VerifierClient, VerifierConfig}, + verifier::{Verifier, VerifierClient}, }; -fn get_verifier_config() -> VerifierConfig { - VerifierConfig { - rpc_url: SensitiveUrl::from_str("https://ethereum-holesky-rpc.publicnode.com").unwrap(), - svc_manager_addr: Address::from_str("0xD4A7E1Bd8015057293f0D0A557088c286942e84b").unwrap(), - max_blob_size: 2 * 1024 * 1024, - g1_url: Url::parse("https://github.com/Layr-Labs/eigenda-proxy/raw/2fd70b99ef5bf137d7bbca3461cf9e1f2c899451/resources/g1.point").unwrap(), - g2_url: Url::parse("https://github.com/Layr-Labs/eigenda-proxy/raw/2fd70b99ef5bf137d7bbca3461cf9e1f2c899451/resources/g2.point.powerOf2").unwrap(), - points_dir: None, - settlement_layer_confirmation_depth: 0, - } -} - /// Mock struct for the Verifier /// Used to avoid making actual calls to a remote disperser /// and possible making the CI fail due to network issues. @@ -107,8 +95,8 @@ impl VerifierClient for MockVerifierClient { } } -fn create_remote_query_client(cfg: VerifierConfig) -> Box> { - let url = cfg.rpc_url; +fn create_remote_query_client() -> Box> { + let url = SensitiveUrl::from_str("https://ethereum-holesky-rpc.publicnode.com").unwrap(); let query_client: Client = Client::http(url).unwrap().build(); Box::new(query_client) as Box> } @@ -116,8 +104,8 @@ fn create_remote_query_client(cfg: VerifierConfig) -> Box> { #[ignore = "depends on external RPC"] #[tokio::test] async fn test_verify_commitment() { - let cfg = get_verifier_config(); - let query_client = create_remote_query_client(cfg.clone()); + let cfg = EigenConfig::default(); + let query_client = create_remote_query_client(); let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let commitment = G1Commitment { x: vec![ @@ -138,7 +126,7 @@ async fn test_verify_commitment() { /// To test actual behaviour of the verifier, run the test above #[tokio::test] async fn test_verify_commitment_mocked() { - let cfg = get_verifier_config(); + let cfg = EigenConfig::default(); let query_client = MockVerifierClient::new(HashMap::new()); let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let commitment = G1Commitment { @@ -159,8 +147,8 @@ async fn test_verify_commitment_mocked() { #[ignore = "depends on external RPC"] #[tokio::test] async fn test_verify_merkle_proof() { - let cfg = get_verifier_config(); - let query_client = create_remote_query_client(cfg.clone()); + let cfg = EigenConfig::default(); + let query_client = create_remote_query_client(); let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let cert = BlobInfo { blob_header: BlobHeader { @@ -240,7 +228,7 @@ async fn test_verify_merkle_proof() { /// To test actual behaviour of the verifier, run the test above #[tokio::test] async fn test_verify_merkle_proof_mocked() { - let cfg = get_verifier_config(); + let cfg = EigenConfig::default(); let query_client = MockVerifierClient::new(HashMap::new()); let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let cert = BlobInfo { @@ -320,8 +308,8 @@ async fn test_verify_merkle_proof_mocked() { #[ignore = "depends on external RPC"] #[tokio::test] async fn test_hash_blob_header() { - let cfg = get_verifier_config(); - let query_client = create_remote_query_client(cfg.clone()); + let cfg = EigenConfig::default(); + let query_client = create_remote_query_client(); let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let blob_header = BlobHeader { commitment: G1Commitment { @@ -359,7 +347,7 @@ async fn test_hash_blob_header() { /// To test actual behaviour of the verifier, run the test above #[tokio::test] async fn test_hash_blob_header_mocked() { - let cfg = get_verifier_config(); + let cfg = EigenConfig::default(); let query_client = MockVerifierClient::new(HashMap::new()); let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let blob_header = BlobHeader { @@ -397,8 +385,8 @@ async fn test_hash_blob_header_mocked() { #[ignore = "depends on external RPC"] #[tokio::test] async fn test_inclusion_proof() { - let cfg = get_verifier_config(); - let query_client = create_remote_query_client(cfg.clone()); + let cfg = EigenConfig::default(); + let query_client = create_remote_query_client(); let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let proof = hex::decode("c455c1ea0e725d7ea3e5f29e9f48be8fc2787bb0a914d5a86710ba302c166ac4f626d76f67f1055bb960a514fb8923af2078fd84085d712655b58a19612e8cd15c3e4ac1cef57acde3438dbcf63f47c9fefe1221344c4d5c1a4943dd0d1803091ca81a270909dc0e146841441c9bd0e08e69ce6168181a3e4060ffacf3627480bec6abdd8d7bb92b49d33f180c42f49e041752aaded9c403db3a17b85e48a11e9ea9a08763f7f383dab6d25236f1b77c12b4c49c5cdbcbea32554a604e3f1d2f466851cb43fe73617b3d01e665e4c019bf930f92dea7394c25ed6a1e200d051fb0c30a2193c459f1cfef00bf1ba6656510d16725a4d1dc031cb759dbc90bab427b0f60ddc6764681924dda848824605a4f08b7f526fe6bd4572458c94e83fbf2150f2eeb28d3011ec921996dc3e69efa52d5fcf3182b20b56b5857a926aa66605808079b4d52c0c0cfe06923fa92e65eeca2c3e6126108e8c1babf5ac522f4d7").unwrap(); let leaf: [u8; 32] = @@ -418,7 +406,7 @@ async fn test_inclusion_proof() { /// To test actual behaviour of the verifier, run the test above #[tokio::test] async fn test_inclusion_proof_mocked() { - let cfg = get_verifier_config(); + let cfg = EigenConfig::default(); let query_client = MockVerifierClient::new(HashMap::new()); let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let proof = hex::decode("c455c1ea0e725d7ea3e5f29e9f48be8fc2787bb0a914d5a86710ba302c166ac4f626d76f67f1055bb960a514fb8923af2078fd84085d712655b58a19612e8cd15c3e4ac1cef57acde3438dbcf63f47c9fefe1221344c4d5c1a4943dd0d1803091ca81a270909dc0e146841441c9bd0e08e69ce6168181a3e4060ffacf3627480bec6abdd8d7bb92b49d33f180c42f49e041752aaded9c403db3a17b85e48a11e9ea9a08763f7f383dab6d25236f1b77c12b4c49c5cdbcbea32554a604e3f1d2f466851cb43fe73617b3d01e665e4c019bf930f92dea7394c25ed6a1e200d051fb0c30a2193c459f1cfef00bf1ba6656510d16725a4d1dc031cb759dbc90bab427b0f60ddc6764681924dda848824605a4f08b7f526fe6bd4572458c94e83fbf2150f2eeb28d3011ec921996dc3e69efa52d5fcf3182b20b56b5857a926aa66605808079b4d52c0c0cfe06923fa92e65eeca2c3e6126108e8c1babf5ac522f4d7").unwrap(); @@ -438,8 +426,8 @@ async fn test_inclusion_proof_mocked() { #[ignore = "depends on external RPC"] #[tokio::test] async fn test_verify_batch() { - let cfg = get_verifier_config(); - let query_client = create_remote_query_client(cfg.clone()); + let cfg = EigenConfig::default(); + let query_client = create_remote_query_client(); let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let cert = BlobInfo { blob_header: BlobHeader { @@ -541,7 +529,7 @@ async fn test_verify_batch_mocked() { ); mock_replies.insert(mock_req, mock_res); - let cfg = get_verifier_config(); + let cfg = EigenConfig::default(); let query_client = MockVerifierClient::new(mock_replies); let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let cert = BlobInfo { @@ -621,8 +609,8 @@ async fn test_verify_batch_mocked() { #[ignore = "depends on external RPC"] #[tokio::test] async fn test_verify_security_params() { - let cfg = get_verifier_config(); - let query_client = create_remote_query_client(cfg.clone()); + let cfg = EigenConfig::default(); + let query_client = create_remote_query_client(); let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let cert = BlobInfo { blob_header: BlobHeader { @@ -745,7 +733,7 @@ async fn test_verify_securityyy_params_mocked() { ); mock_replies.insert(mock_req, mock_res); - let cfg = get_verifier_config(); + let cfg = EigenConfig::default(); let query_client = MockVerifierClient::new(mock_replies); let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); let cert = BlobInfo { From 87d5e0d504d9b829fd675760ab8a5c53ca0daad9 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Tue, 21 Jan 2025 12:48:44 -0300 Subject: [PATCH 73/97] fix eigen config not initializing correctly --- core/lib/protobuf_config/src/da_client.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/lib/protobuf_config/src/da_client.rs b/core/lib/protobuf_config/src/da_client.rs index 17dab3d07239..9ba38336da52 100644 --- a/core/lib/protobuf_config/src/da_client.rs +++ b/core/lib/protobuf_config/src/da_client.rs @@ -78,7 +78,7 @@ impl ProtoRepr for proto::DataAvailabilityClient { wait_for_finalization: *required(&conf.wait_for_finalization) .context("wait_for_finalization")?, authenticated: *required(&conf.authenticated).context("authenticated")?, - points_dir: Some(required(&conf.points_dir).context("points_dir")?.clone()), + points_dir: conf.points_dir.clone(), g1_url: required(&conf.g1_url).context("g1_url")?.clone(), g2_url: required(&conf.g2_url).context("g2_url")?.clone(), }), @@ -123,7 +123,10 @@ impl ProtoRepr for proto::DataAvailabilityClient { settlement_layer_confirmation_depth: Some( config.settlement_layer_confirmation_depth, ), - eigenda_eth_rpc: Some(format!("{:?}", config.eigenda_eth_rpc)), + eigenda_eth_rpc: config + .eigenda_eth_rpc + .as_ref() + .map(|a| a.expose_str().to_string()), eigenda_svc_manager_address: Some(format!( "{:?}", config.eigenda_svc_manager_address @@ -132,7 +135,7 @@ impl ProtoRepr for proto::DataAvailabilityClient { authenticated: Some(config.authenticated), g1_url: Some(config.g1_url.clone()), g2_url: Some(config.g2_url.clone()), - points_dir: Some(format!("{:?}", config.points_dir)), + points_dir: config.points_dir.as_ref().map(|a| a.to_string()), }), ObjectStore(config) => proto::data_availability_client::Config::ObjectStore( object_store_proto::ObjectStore::build(config), From 0d40f170d91c9f59de11eaa6e40efab6f7a7d89d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Fran=C3=A7a?= Date: Wed, 22 Jan 2025 06:13:50 +0000 Subject: [PATCH 74/97] build(consensus): Bump consensus version (#3512) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Bumps version of consensus dependencies to 0.8. New version includes a needed fix for the consensus algorithm and also removes attesters field from Genesis spec (as it was already deprecated before). Tests that use attesters were commented out instead of fixed since most likely the attester feature will be deprecated when we remove L1 batches (with ZK OS). But keeping it for now (not sure if we don't want to reuse the code for something else). --- core/Cargo.lock | 40 +-- core/Cargo.toml | 20 +- core/lib/dal/src/consensus_dal/mod.rs | 1 - core/lib/dal/src/consensus_dal/tests.rs | 269 +++++++++--------- core/node/consensus/src/config.rs | 20 -- core/node/consensus/src/en.rs | 2 +- core/node/consensus/src/mn.rs | 2 +- core/node/consensus/src/registry/mod.rs | 10 +- core/node/consensus/src/registry/tests.rs | 9 +- core/node/consensus/src/storage/connection.rs | 1 - core/node/consensus/src/storage/testonly.rs | 6 +- core/node/consensus/src/tests/attestation.rs | 2 +- core/node/consensus/src/tests/mod.rs | 5 +- prover/Cargo.lock | 28 +- zkstack_cli/Cargo.lock | 24 +- zkstack_cli/Cargo.toml | 10 +- 16 files changed, 213 insertions(+), 236 deletions(-) diff --git a/core/Cargo.lock b/core/Cargo.lock index d064b2a3d7bc..ad7915ca2376 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -11427,9 +11427,9 @@ dependencies = [ [[package]] name = "zksync_concurrency" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8312ab73d3caa55775bd531795b507fa8f76bd9dabfaeb0954fe43e8fc1323b" +checksum = "cec98400a9e8ba02bfd029eacfe7d6fb7b85b8ef00de59d6bb119d29cc9f7442" dependencies = [ "anyhow", "once_cell", @@ -11463,9 +11463,9 @@ dependencies = [ [[package]] name = "zksync_consensus_bft" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb6b0944322f30f88cd7fb22f7875435b394a135fc1b479719a18c42d9fb724d" +checksum = "0fa086aeb444d3d0122014fca06959e5c1be507d63596022bd28b8cdcc5cc687" dependencies = [ "anyhow", "async-trait", @@ -11485,9 +11485,9 @@ dependencies = [ [[package]] name = "zksync_consensus_crypto" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b539960de98df3c3bd27d2d9b97de862027686bbb3bdfc5aaad5b74bb929a1" +checksum = "c04840825dfbe3b9f708d245c87618d5dcf28f29d7b58922971351068a0b8231" dependencies = [ "anyhow", "blst", @@ -11506,9 +11506,9 @@ dependencies = [ [[package]] name = "zksync_consensus_executor" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a75d86368579d5aa59b1baebbdc1aebca7c9234f3e3cba734db7e9bbc4880b0" +checksum = "d6d369ec72851aecdfb24c99ecb50b7c177f0ce7068bb84a17a294a26fb92fab" dependencies = [ "anyhow", "async-trait", @@ -11528,9 +11528,9 @@ dependencies = [ [[package]] name = "zksync_consensus_network" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30f73993b7d677dfd4e4f2598dd20906e6a5f3a2168c6cab3a599c056dc5e39a" +checksum = "6a74ed5a9a48d403b48c7ed0dea8cf2cd239e407227657aac27d75d00c3e4bcc" dependencies = [ "anyhow", "async-trait", @@ -11565,9 +11565,9 @@ dependencies = [ [[package]] name = "zksync_consensus_roles" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c49949546895a10431b9daec6ec4208ef0917ace006446d304b51f5b234ba462" +checksum = "05498eab1de26869028b5822cfa4490cac625508d427d59668dc73e8162de65f" dependencies = [ "anyhow", "bit-vec", @@ -11587,9 +11587,9 @@ dependencies = [ [[package]] name = "zksync_consensus_storage" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb0d6a54e7d8d2adeee4ba38662161e9309180ad497299092e5641db9fb1c1e" +checksum = "b20eb99fdd0e171a370214d2b7c99b5d4e8c11b9828a6b5705423bf653849a70" dependencies = [ "anyhow", "async-trait", @@ -11607,9 +11607,9 @@ dependencies = [ [[package]] name = "zksync_consensus_utils" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723e2a4b056cc5af192a83163c89a6951ee75c098cc5c4a4cdc435f4232d88bd" +checksum = "f2f9fa69ef68e6a1955a1d7b33077103fb6d106b560fec0d599c6de268f5be03" dependencies = [ "anyhow", "rand 0.8.5", @@ -12671,9 +12671,9 @@ dependencies = [ [[package]] name = "zksync_protobuf" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8986ad796f8e00d8999fee72effba1a21bce40f5f877d681ac9cd89a94834d8" +checksum = "d9032e12528c2466293b206d6edb53b7e900e4a4cc4573e4d075ac2dc00e1b55" dependencies = [ "anyhow", "bit-vec", @@ -12692,9 +12692,9 @@ dependencies = [ [[package]] name = "zksync_protobuf_build" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d870b31995e3acb8e47afeb68ebeeffcf6121e70020e65b3d5d31692115d236" +checksum = "7c644fc8ef3c4d343ea42cebd5551e3562933f15dd9b0e68a52c2657603eb0f5" dependencies = [ "anyhow", "heck 0.5.0", diff --git a/core/Cargo.toml b/core/Cargo.toml index 1c071c2839fa..7ed6f2d6b601 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -245,16 +245,16 @@ fflonk = "=0.30.12" zksync_vm2 = { git = "https://github.com/matter-labs/vm2.git", rev = "457d8a7eea9093af9440662e33e598c13ba41633" } # Consensus dependencies. -zksync_concurrency = "=0.7.0" -zksync_consensus_bft = "=0.7.0" -zksync_consensus_crypto = "=0.7.0" -zksync_consensus_executor = "=0.7.0" -zksync_consensus_network = "=0.7.0" -zksync_consensus_roles = "=0.7.0" -zksync_consensus_storage = "=0.7.0" -zksync_consensus_utils = "=0.7.0" -zksync_protobuf = "=0.7.0" -zksync_protobuf_build = "=0.7.0" +zksync_concurrency = "=0.8.0" +zksync_consensus_bft = "=0.8.0" +zksync_consensus_crypto = "=0.8.0" +zksync_consensus_executor = "=0.8.0" +zksync_consensus_network = "=0.8.0" +zksync_consensus_roles = "=0.8.0" +zksync_consensus_storage = "=0.8.0" +zksync_consensus_utils = "=0.8.0" +zksync_protobuf = "=0.8.0" +zksync_protobuf_build = "=0.8.0" # "Local" dependencies zksync_multivm = { version = "0.1.0", path = "lib/multivm" } diff --git a/core/lib/dal/src/consensus_dal/mod.rs b/core/lib/dal/src/consensus_dal/mod.rs index 7f3bcd1166ad..e6058e86b47d 100644 --- a/core/lib/dal/src/consensus_dal/mod.rs +++ b/core/lib/dal/src/consensus_dal/mod.rs @@ -234,7 +234,6 @@ impl ConsensusDal<'_, '_> { protocol_version: old.genesis.protocol_version, validators: old.genesis.validators.clone(), - attesters: old.genesis.attesters.clone(), leader_selection: old.genesis.leader_selection.clone(), } .with_hash(), diff --git a/core/lib/dal/src/consensus_dal/tests.rs b/core/lib/dal/src/consensus_dal/tests.rs index 694abc8508b6..4b4c5f7b3821 100644 --- a/core/lib/dal/src/consensus_dal/tests.rs +++ b/core/lib/dal/src/consensus_dal/tests.rs @@ -1,17 +1,9 @@ use rand::Rng as _; -use zksync_consensus_roles::{attester, validator}; +use zksync_consensus_roles::validator; use zksync_consensus_storage::ReplicaState; -use zksync_types::{ - block::L1BatchTreeData, - commitment::{L1BatchCommitmentArtifacts, L1BatchCommitmentHash}, - ProtocolVersion, -}; use super::*; -use crate::{ - tests::{create_l1_batch_header, create_l2_block_header}, - ConnectionPool, Core, CoreDal, -}; +use crate::{ConnectionPool, Core, CoreDal}; #[tokio::test] async fn replica_state_read_write() { @@ -52,138 +44,141 @@ async fn replica_state_read_write() { } } -#[tokio::test] -async fn test_batch_certificate() { - let rng = &mut rand::thread_rng(); - let setup = validator::testonly::Setup::new(rng, 3); - let pool = ConnectionPool::::test_pool().await; - let mut conn = pool.connection().await.unwrap(); - let cfg = GlobalConfig { - genesis: setup.genesis.clone(), - registry_address: Some(rng.gen()), - seed_peers: [].into(), - }; - conn.consensus_dal() - .try_update_global_config(&cfg) - .await - .unwrap(); +// NOTE: This test is disabled since we are going to remove L1 batches. Most likely +// we will remove all the attester related code as well, but keeping this until +// we are sure. +// #[tokio::test] +// async fn test_batch_certificate() { +// let rng = &mut rand::thread_rng(); +// let setup = validator::testonly::Setup::new(rng, 3); +// let pool = ConnectionPool::::test_pool().await; +// let mut conn = pool.connection().await.unwrap(); +// let cfg = GlobalConfig { +// genesis: setup.genesis.clone(), +// registry_address: Some(rng.gen()), +// seed_peers: [].into(), +// }; +// conn.consensus_dal() +// .try_update_global_config(&cfg) +// .await +// .unwrap(); - let make_cert = |number: attester::BatchNumber, hash: attester::BatchHash| { - let m = attester::Batch { - genesis: setup.genesis.hash(), - hash, - number, - }; - let mut sigs = attester::MultiSig::default(); - for k in &setup.attester_keys { - sigs.add(k.public(), k.sign_msg(m.clone()).sig); - } - attester::BatchQC { - message: m, - signatures: sigs, - } - }; +// let make_cert = |number: attester::BatchNumber, hash: attester::BatchHash| { +// let m = attester::Batch { +// genesis: setup.genesis.hash(), +// hash, +// number, +// }; +// let mut sigs = attester::MultiSig::default(); +// for k in &setup.attester_keys { +// sigs.add(k.public(), k.sign_msg(m.clone()).sig); +// } +// attester::BatchQC { +// message: m, +// signatures: sigs, +// } +// }; - // Required for inserting l2 blocks - conn.protocol_versions_dal() - .save_protocol_version_with_tx(&ProtocolVersion::default()) - .await - .unwrap(); +// // Required for inserting l2 blocks +// conn.protocol_versions_dal() +// .save_protocol_version_with_tx(&ProtocolVersion::default()) +// .await +// .unwrap(); - // Insert some mock L2 blocks and L1 batches - let mut block_number = 0; - let mut batch_number = 0; - for _ in 0..3 { - for _ in 0..3 { - block_number += 1; - let l2_block = create_l2_block_header(block_number); - conn.blocks_dal().insert_l2_block(&l2_block).await.unwrap(); - } - batch_number += 1; - let l1_batch = create_l1_batch_header(batch_number); - conn.blocks_dal() - .insert_mock_l1_batch(&l1_batch) - .await - .unwrap(); - conn.blocks_dal() - .save_l1_batch_tree_data( - l1_batch.number, - &L1BatchTreeData { - hash: rng.gen(), - rollup_last_leaf_index: rng.gen(), - }, - ) - .await - .unwrap(); - conn.blocks_dal() - .save_l1_batch_commitment_artifacts( - l1_batch.number, - &L1BatchCommitmentArtifacts { - commitment_hash: L1BatchCommitmentHash { - pass_through_data: rng.gen(), - aux_output: rng.gen(), - meta_parameters: rng.gen(), - commitment: rng.gen(), - }, - l2_l1_merkle_root: rng.gen(), - compressed_state_diffs: None, - compressed_initial_writes: None, - compressed_repeated_writes: None, - zkporter_is_available: false, - aux_commitments: None, - aggregation_root: rng.gen(), - local_root: rng.gen(), - state_diff_hash: rng.gen(), - }, - ) - .await - .unwrap(); - conn.blocks_dal() - .mark_l2_blocks_as_executed_in_l1_batch(l1_batch.number) - .await - .unwrap(); - } +// // Insert some mock L2 blocks and L1 batches +// let mut block_number = 0; +// let mut batch_number = 0; +// for _ in 0..3 { +// for _ in 0..3 { +// block_number += 1; +// let l2_block = create_l2_block_header(block_number); +// conn.blocks_dal().insert_l2_block(&l2_block).await.unwrap(); +// } +// batch_number += 1; +// let l1_batch = create_l1_batch_header(batch_number); +// conn.blocks_dal() +// .insert_mock_l1_batch(&l1_batch) +// .await +// .unwrap(); +// conn.blocks_dal() +// .save_l1_batch_tree_data( +// l1_batch.number, +// &L1BatchTreeData { +// hash: rng.gen(), +// rollup_last_leaf_index: rng.gen(), +// }, +// ) +// .await +// .unwrap(); +// conn.blocks_dal() +// .save_l1_batch_commitment_artifacts( +// l1_batch.number, +// &L1BatchCommitmentArtifacts { +// commitment_hash: L1BatchCommitmentHash { +// pass_through_data: rng.gen(), +// aux_output: rng.gen(), +// meta_parameters: rng.gen(), +// commitment: rng.gen(), +// }, +// l2_l1_merkle_root: rng.gen(), +// compressed_state_diffs: None, +// compressed_initial_writes: None, +// compressed_repeated_writes: None, +// zkporter_is_available: false, +// aux_commitments: None, +// aggregation_root: rng.gen(), +// local_root: rng.gen(), +// state_diff_hash: rng.gen(), +// }, +// ) +// .await +// .unwrap(); +// conn.blocks_dal() +// .mark_l2_blocks_as_executed_in_l1_batch(l1_batch.number) +// .await +// .unwrap(); +// } - let n = attester::BatchNumber(batch_number.into()); +// let n = attester::BatchNumber(batch_number.into()); - // Insert a batch certificate for the last L1 batch. - let hash = batch_hash(&conn.consensus_dal().batch_info(n).await.unwrap().unwrap()); - let want = make_cert(n, hash); - conn.consensus_dal() - .upsert_attester_committee(n, setup.genesis.attesters.as_ref().unwrap()) - .await - .unwrap(); - conn.consensus_dal() - .insert_batch_certificate(&want) - .await - .unwrap(); +// // Insert a batch certificate for the last L1 batch. +// let hash = batch_hash(&conn.consensus_dal().batch_info(n).await.unwrap().unwrap()); +// let want = make_cert(n, hash); +// conn.consensus_dal() +// .upsert_attester_committee(n, setup.genesis.attesters.as_ref().unwrap()) +// .await +// .unwrap(); +// conn.consensus_dal() +// .insert_batch_certificate(&want) +// .await +// .unwrap(); - // Reinserting a cert should fail. - assert!(conn - .consensus_dal() - .insert_batch_certificate(&make_cert(n, hash)) - .await - .is_err()); +// // Reinserting a cert should fail. +// assert!(conn +// .consensus_dal() +// .insert_batch_certificate(&make_cert(n, hash)) +// .await +// .is_err()); - // Retrieve the latest certificate. - let got_n = conn - .consensus_dal() - .last_batch_certificate_number() - .await - .unwrap() - .unwrap(); - let got = conn - .consensus_dal() - .batch_certificate(got_n) - .await - .unwrap() - .unwrap(); - assert_eq!(got, want); +// // Retrieve the latest certificate. +// let got_n = conn +// .consensus_dal() +// .last_batch_certificate_number() +// .await +// .unwrap() +// .unwrap(); +// let got = conn +// .consensus_dal() +// .batch_certificate(got_n) +// .await +// .unwrap() +// .unwrap(); +// assert_eq!(got, want); - // Try insert batch certificate for non-existing batch - assert!(conn - .consensus_dal() - .insert_batch_certificate(&make_cert(n.next(), rng.gen())) - .await - .is_err()); -} +// // Try insert batch certificate for non-existing batch +// assert!(conn +// .consensus_dal() +// .insert_batch_certificate(&make_cert(n.next(), rng.gen())) +// .await +// .is_err()); +// } diff --git a/core/node/consensus/src/config.rs b/core/node/consensus/src/config.rs index 2cb6045151bd..733500998a30 100644 --- a/core/node/consensus/src/config.rs +++ b/core/node/consensus/src/config.rs @@ -42,7 +42,6 @@ pub(super) struct GenesisSpec { pub(super) chain_id: validator::ChainId, pub(super) protocol_version: validator::ProtocolVersion, pub(super) validators: validator::Committee, - pub(super) attesters: Option, pub(super) leader_selection: validator::LeaderSelectionMode, pub(super) registry_address: Option, pub(super) seed_peers: BTreeMap, @@ -54,7 +53,6 @@ impl GenesisSpec { chain_id: cfg.genesis.chain_id, protocol_version: cfg.genesis.protocol_version, validators: cfg.genesis.validators.clone(), - attesters: cfg.genesis.attesters.clone(), leader_selection: cfg.genesis.leader_selection.clone(), registry_address: cfg.registry_address, seed_peers: cfg.seed_peers.clone(), @@ -75,19 +73,6 @@ impl GenesisSpec { .collect::>() .context("validators")?; - let attesters: Vec<_> = x - .attesters - .iter() - .enumerate() - .map(|(i, v)| { - Ok(attester::WeightedAttester { - key: Text::new(&v.key.0).decode().context("key").context(i)?, - weight: v.weight, - }) - }) - .collect::>() - .context("attesters")?; - Ok(Self { chain_id: validator::ChainId(x.chain_id.as_u64()), protocol_version: validator::ProtocolVersion(x.protocol_version.0), @@ -95,11 +80,6 @@ impl GenesisSpec { Text::new(&x.leader.0).decode().context("leader")?, ), validators: validator::Committee::new(validators).context("validators")?, - attesters: if attesters.is_empty() { - None - } else { - Some(attester::Committee::new(attesters).context("attesters")?) - }, registry_address: x.registry_address, seed_peers: x .seed_peers diff --git a/core/node/consensus/src/en.rs b/core/node/consensus/src/en.rs index 2bddc3280362..ffb580ce1284 100644 --- a/core/node/consensus/src/en.rs +++ b/core/node/consensus/src/en.rs @@ -208,7 +208,7 @@ impl EN { attestation: Arc, ) -> ctx::Result<()> { const POLL_INTERVAL: time::Duration = time::Duration::seconds(5); - let registry = registry::Registry::new(cfg.genesis.clone(), self.pool.clone()).await; + let registry = registry::Registry::new(self.pool.clone()).await; let mut next = attester::BatchNumber(0); loop { let status = loop { diff --git a/core/node/consensus/src/mn.rs b/core/node/consensus/src/mn.rs index a392acfbe5f0..028a635398c6 100644 --- a/core/node/consensus/src/mn.rs +++ b/core/node/consensus/src/mn.rs @@ -118,7 +118,7 @@ async fn run_attestation_controller( attestation: Arc, ) -> ctx::Result<()> { const POLL_INTERVAL: time::Duration = time::Duration::seconds(5); - let registry = registry::Registry::new(cfg.genesis, pool.clone()).await; + let registry = registry::Registry::new(pool.clone()).await; let registry_addr = cfg.registry_address.map(registry::Address::new); let mut next = attester::BatchNumber(0); loop { diff --git a/core/node/consensus/src/registry/mod.rs b/core/node/consensus/src/registry/mod.rs index 74da41309573..235389acda0e 100644 --- a/core/node/consensus/src/registry/mod.rs +++ b/core/node/consensus/src/registry/mod.rs @@ -1,7 +1,7 @@ use anyhow::Context as _; use zksync_concurrency::{ctx, error::Wrap as _}; use zksync_consensus_crypto::ByteFmt; -use zksync_consensus_roles::{attester, validator}; +use zksync_consensus_roles::attester; use crate::{storage::ConnectionPool, vm::VM}; @@ -30,22 +30,20 @@ pub type Address = crate::abi::Address; #[derive(Debug)] pub(crate) struct Registry { contract: abi::ConsensusRegistry, - genesis: validator::Genesis, vm: VM, } impl Registry { - pub async fn new(genesis: validator::Genesis, pool: ConnectionPool) -> Self { + pub async fn new(pool: ConnectionPool) -> Self { Self { contract: abi::ConsensusRegistry::load(), - genesis, vm: VM::new(pool).await, } } /// Attester committee for the given batch. /// It reads committee from the contract. - /// Falls back to committee specified in the genesis. + /// Falls back to empty committee. pub async fn attester_committee_for( &self, ctx: &ctx::Ctx, @@ -57,7 +55,7 @@ impl Registry { return Ok(None); }; let Some(address) = address else { - return Ok(self.genesis.attesters.clone()); + return Ok(None); }; let raw = self .vm diff --git a/core/node/consensus/src/registry/tests.rs b/core/node/consensus/src/registry/tests.rs index 15329077a651..736b6c9b3ac7 100644 --- a/core/node/consensus/src/registry/tests.rs +++ b/core/node/consensus/src/registry/tests.rs @@ -1,6 +1,6 @@ use rand::Rng as _; use zksync_concurrency::{ctx, scope, time}; -use zksync_consensus_roles::{attester, validator::testonly::Setup}; +use zksync_consensus_roles::attester; use zksync_test_contracts::Account; use zksync_types::ProtocolVersionId; @@ -26,21 +26,20 @@ async fn test_attester_committee() { zksync_concurrency::testonly::abort_on_panic(); let ctx = &ctx::test_root(&ctx::RealClock); let rng = &mut ctx.rng(); - let setup = Setup::new(rng, 10); let account = &mut Account::random(); let to_fund = &[account.address]; scope::run!(ctx, |ctx, s| async { let pool = ConnectionPool::test(false, ProtocolVersionId::latest()).await; - let registry = Registry::new(setup.genesis.clone(), pool.clone()).await; + let registry = Registry::new(pool.clone()).await; // If the registry contract address is not specified, - // then the committee from genesis should be returned. + // then an empty committee should be returned. let got = registry .attester_committee_for(ctx, None, attester::BatchNumber(10)) .await .unwrap(); - assert_eq!(setup.genesis.attesters, got); + assert!(got.is_none()); let (mut node, runner) = crate::testonly::StateKeeper::new(ctx, pool.clone()).await?; s.spawn_bg(runner.run_real(ctx, to_fund)); diff --git a/core/node/consensus/src/storage/connection.rs b/core/node/consensus/src/storage/connection.rs index 6ec5794e968d..08fa7996bdc1 100644 --- a/core/node/consensus/src/storage/connection.rs +++ b/core/node/consensus/src/storage/connection.rs @@ -277,7 +277,6 @@ impl<'a> Connection<'a> { first_block: txn.next_block(ctx).await.context("next_block()")?, protocol_version: spec.protocol_version, validators: spec.validators.clone(), - attesters: spec.attesters.clone(), leader_selection: spec.leader_selection.clone(), } .with_hash(), diff --git a/core/node/consensus/src/storage/testonly.rs b/core/node/consensus/src/storage/testonly.rs index 295ae4fc1790..858bca542c33 100644 --- a/core/node/consensus/src/storage/testonly.rs +++ b/core/node/consensus/src/storage/testonly.rs @@ -16,6 +16,7 @@ use crate::registry; impl Connection<'_> { /// Wrapper for `consensus_dal().batch_of_block()`. + #[allow(dead_code)] pub async fn batch_of_block( &mut self, ctx: &ctx::Ctx, @@ -27,6 +28,7 @@ impl Connection<'_> { } /// Wrapper for `consensus_dal().last_batch_certificate_number()`. + #[allow(dead_code)] pub async fn last_batch_certificate_number( &mut self, ctx: &ctx::Ctx, @@ -37,6 +39,7 @@ impl Connection<'_> { } /// Wrapper for `consensus_dal().batch_certificate()`. + #[allow(dead_code)] pub async fn batch_certificate( &mut self, ctx: &ctx::Ctx, @@ -187,6 +190,7 @@ impl ConnectionPool { Ok(blocks) } + #[allow(dead_code)] pub async fn wait_for_batch_certificates_and_verify( &self, ctx: &ctx::Ctx, @@ -217,7 +221,7 @@ impl ConnectionPool { .await .wrap("batch_of_block()")? .context("batch of first_block is missing")?; - let registry = registry::Registry::new(cfg.genesis.clone(), self.clone()).await; + let registry = registry::Registry::new(self.clone()).await; for i in first.0..want_last.0 { let i = attester::BatchNumber(i); let cert = conn diff --git a/core/node/consensus/src/tests/attestation.rs b/core/node/consensus/src/tests/attestation.rs index 6f24fbe65b4c..8e20474f453c 100644 --- a/core/node/consensus/src/tests/attestation.rs +++ b/core/node/consensus/src/tests/attestation.rs @@ -151,7 +151,7 @@ async fn test_multiple_attesters(version: ProtocolVersionId) { tracing::info!("deploy registry with 1 attester"); let attesters: Vec<_> = setup.genesis.attesters.as_ref().unwrap().iter().collect(); - let registry = Registry::new(setup.genesis.clone(), validator_pool.clone()).await; + let registry = Registry::new(validator_pool.clone()).await; let (registry_addr, tx) = registry.deploy(account); cfgs[0] .config diff --git a/core/node/consensus/src/tests/mod.rs b/core/node/consensus/src/tests/mod.rs index c7697ba8480e..c685fdd3223d 100644 --- a/core/node/consensus/src/tests/mod.rs +++ b/core/node/consensus/src/tests/mod.rs @@ -22,7 +22,10 @@ use crate::{ testonly, }; -mod attestation; +// NOTE: These tests are disabled since we are going to remove L1 batches. Most likely +// we will remove all the attester related code as well, but keeping this until +// we are sure. +//mod attestation; const VERSIONS: [ProtocolVersionId; 2] = [ProtocolVersionId::latest(), ProtocolVersionId::next()]; const FROM_SNAPSHOT: [bool; 2] = [true, false]; diff --git a/prover/Cargo.lock b/prover/Cargo.lock index 761c9e398cb4..3d43239a15c6 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -8157,9 +8157,9 @@ dependencies = [ [[package]] name = "zksync_concurrency" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8312ab73d3caa55775bd531795b507fa8f76bd9dabfaeb0954fe43e8fc1323b" +checksum = "cec98400a9e8ba02bfd029eacfe7d6fb7b85b8ef00de59d6bb119d29cc9f7442" dependencies = [ "anyhow", "once_cell", @@ -8192,9 +8192,9 @@ dependencies = [ [[package]] name = "zksync_consensus_crypto" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b539960de98df3c3bd27d2d9b97de862027686bbb3bdfc5aaad5b74bb929a1" +checksum = "c04840825dfbe3b9f708d245c87618d5dcf28f29d7b58922971351068a0b8231" dependencies = [ "anyhow", "blst", @@ -8213,9 +8213,9 @@ dependencies = [ [[package]] name = "zksync_consensus_roles" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c49949546895a10431b9daec6ec4208ef0917ace006446d304b51f5b234ba462" +checksum = "05498eab1de26869028b5822cfa4490cac625508d427d59668dc73e8162de65f" dependencies = [ "anyhow", "bit-vec 0.6.3", @@ -8235,9 +8235,9 @@ dependencies = [ [[package]] name = "zksync_consensus_storage" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb0d6a54e7d8d2adeee4ba38662161e9309180ad497299092e5641db9fb1c1e" +checksum = "b20eb99fdd0e171a370214d2b7c99b5d4e8c11b9828a6b5705423bf653849a70" dependencies = [ "anyhow", "async-trait", @@ -8255,9 +8255,9 @@ dependencies = [ [[package]] name = "zksync_consensus_utils" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723e2a4b056cc5af192a83163c89a6951ee75c098cc5c4a4cdc435f4232d88bd" +checksum = "f2f9fa69ef68e6a1955a1d7b33077103fb6d106b560fec0d599c6de268f5be03" dependencies = [ "anyhow", "rand 0.8.5", @@ -8582,9 +8582,9 @@ dependencies = [ [[package]] name = "zksync_protobuf" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8986ad796f8e00d8999fee72effba1a21bce40f5f877d681ac9cd89a94834d8" +checksum = "d9032e12528c2466293b206d6edb53b7e900e4a4cc4573e4d075ac2dc00e1b55" dependencies = [ "anyhow", "bit-vec 0.6.3", @@ -8603,9 +8603,9 @@ dependencies = [ [[package]] name = "zksync_protobuf_build" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d870b31995e3acb8e47afeb68ebeeffcf6121e70020e65b3d5d31692115d236" +checksum = "7c644fc8ef3c4d343ea42cebd5551e3562933f15dd9b0e68a52c2657603eb0f5" dependencies = [ "anyhow", "heck 0.5.0", diff --git a/zkstack_cli/Cargo.lock b/zkstack_cli/Cargo.lock index cfc16535db50..b21bb44b589a 100644 --- a/zkstack_cli/Cargo.lock +++ b/zkstack_cli/Cargo.lock @@ -7186,9 +7186,9 @@ dependencies = [ [[package]] name = "zksync_concurrency" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8312ab73d3caa55775bd531795b507fa8f76bd9dabfaeb0954fe43e8fc1323b" +checksum = "cec98400a9e8ba02bfd029eacfe7d6fb7b85b8ef00de59d6bb119d29cc9f7442" dependencies = [ "anyhow", "once_cell", @@ -7219,9 +7219,9 @@ dependencies = [ [[package]] name = "zksync_consensus_crypto" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b539960de98df3c3bd27d2d9b97de862027686bbb3bdfc5aaad5b74bb929a1" +checksum = "c04840825dfbe3b9f708d245c87618d5dcf28f29d7b58922971351068a0b8231" dependencies = [ "anyhow", "blst", @@ -7240,9 +7240,9 @@ dependencies = [ [[package]] name = "zksync_consensus_roles" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c49949546895a10431b9daec6ec4208ef0917ace006446d304b51f5b234ba462" +checksum = "05498eab1de26869028b5822cfa4490cac625508d427d59668dc73e8162de65f" dependencies = [ "anyhow", "bit-vec", @@ -7262,9 +7262,9 @@ dependencies = [ [[package]] name = "zksync_consensus_utils" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723e2a4b056cc5af192a83163c89a6951ee75c098cc5c4a4cdc435f4232d88bd" +checksum = "f2f9fa69ef68e6a1955a1d7b33077103fb6d106b560fec0d599c6de268f5be03" dependencies = [ "anyhow", "rand", @@ -7340,9 +7340,9 @@ dependencies = [ [[package]] name = "zksync_protobuf" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8986ad796f8e00d8999fee72effba1a21bce40f5f877d681ac9cd89a94834d8" +checksum = "d9032e12528c2466293b206d6edb53b7e900e4a4cc4573e4d075ac2dc00e1b55" dependencies = [ "anyhow", "bit-vec", @@ -7361,9 +7361,9 @@ dependencies = [ [[package]] name = "zksync_protobuf_build" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d870b31995e3acb8e47afeb68ebeeffcf6121e70020e65b3d5d31692115d236" +checksum = "7c644fc8ef3c4d343ea42cebd5551e3562933f15dd9b0e68a52c2657603eb0f5" dependencies = [ "anyhow", "heck", diff --git a/zkstack_cli/Cargo.toml b/zkstack_cli/Cargo.toml index f3e0d8a9c9d5..79a87b4e1ae2 100644 --- a/zkstack_cli/Cargo.toml +++ b/zkstack_cli/Cargo.toml @@ -35,11 +35,11 @@ zksync_types = { path = "../core/lib/types" } zksync_web3_decl = { path = "../core/lib/web3_decl" } zksync_eth_client = { path = "../core/lib/eth_client" } zksync_contracts = { path = "../core/lib/contracts" } -zksync_consensus_roles = "=0.7.0" -zksync_consensus_crypto = "=0.7.0" -zksync_consensus_utils = "=0.7.0" -zksync_protobuf = "=0.7.0" -zksync_protobuf_build = "=0.7.0" +zksync_consensus_roles = "=0.8.0" +zksync_consensus_crypto = "=0.8.0" +zksync_consensus_utils = "=0.8.0" +zksync_protobuf = "=0.8.0" +zksync_protobuf_build = "=0.8.0" # External dependencies anyhow = "1.0.82" From c3cb48cd2bc22c3ef430b55e3fb3d05555370088 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Wed, 22 Jan 2025 12:06:07 -0300 Subject: [PATCH 75/97] fix/refactor mock tests --- .../node/da_clients/src/eigen/verifier/mod.rs | 1 - .../da_clients/src/eigen/verifier/tests.rs | 61 +++++++------------ 2 files changed, 23 insertions(+), 39 deletions(-) diff --git a/core/node/da_clients/src/eigen/verifier/mod.rs b/core/node/da_clients/src/eigen/verifier/mod.rs index f21c3874e544..9bfadd69ffba 100644 --- a/core/node/da_clients/src/eigen/verifier/mod.rs +++ b/core/node/da_clients/src/eigen/verifier/mod.rs @@ -120,7 +120,6 @@ impl VerifierClient for Box> { data: Some(zksync_basic_types::web3::Bytes(data)), ..Default::default() }; - let res = self .as_ref() .call_contract_function(call_request, None) diff --git a/core/node/da_clients/src/eigen/verifier/tests.rs b/core/node/da_clients/src/eigen/verifier/tests.rs index e2d67fd45827..d26c57b653e1 100644 --- a/core/node/da_clients/src/eigen/verifier/tests.rs +++ b/core/node/da_clients/src/eigen/verifier/tests.rs @@ -15,7 +15,7 @@ use crate::eigen::{ BatchHeader, BatchMetadata, BlobHeader, BlobInfo, BlobQuorumParam, BlobVerificationProof, G1Commitment, }, - verifier::{Verifier, VerifierClient}, + verifier::{decode_bytes, Verifier, VerifierClient}, }; /// Mock struct for the Verifier @@ -62,7 +62,7 @@ impl VerifierClient for MockVerifierClient { async fn quorum_adversary_threshold_percentages( &self, - _quorum_number: u32, + quorum_number: u32, svc_manager_addr: Address, ) -> Result { let func_selector = ethabi::short_signature("quorumAdversaryThresholdPercentages", &[]); @@ -75,7 +75,13 @@ impl VerifierClient for MockVerifierClient { }; let req = serde_json::to_string(&call_request).unwrap(); - Ok(self.replies.get(&req).unwrap().clone().0[0]) + let res = self.replies.get(&req).unwrap().clone(); + let percentages = decode_bytes(res.0)?; + + if percentages.len() > quorum_number as usize { + return Ok(percentages[quorum_number as usize]); + } + Ok(0) } async fn quorum_numbers_required( @@ -91,7 +97,8 @@ impl VerifierClient for MockVerifierClient { }; let req = serde_json::to_string(&call_request).unwrap(); - Ok(self.replies.get(&req).unwrap().clone().0) + let res = self.replies.get(&req).unwrap().clone(); + decode_bytes(res.0.to_vec()) } } @@ -509,19 +516,12 @@ async fn test_verify_batch() { async fn test_verify_batch_mocked() { let mut mock_replies = HashMap::new(); let mock_req = CallRequest { - from: None, to: Some(H160::from_str("0xd4a7e1bd8015057293f0d0a557088c286942e84b").unwrap()), - gas: None, - gas_price: None, - value: None, data: Some(Bytes::from( hex::decode("eccbbfc900000000000000000000000000000000000000000000000000000000000103cb") .unwrap(), )), - transaction_type: None, - access_list: None, - max_fee_per_gas: None, - max_priority_fee_per_gas: None, + ..Default::default() }; let mock_req = serde_json::to_string(&mock_req).unwrap(); let mock_res = Bytes::from( @@ -683,59 +683,44 @@ async fn test_verify_security_params() { }, }; let result = verifier.verify_security_params(&cert).await; - println!("result {:?}", result); assert!(result.is_ok()); } /// Test security params verification with a mocked verifier. /// To test actual behaviour of the verifier, run the test above #[tokio::test] -async fn test_verify_securityyy_params_mocked() { +async fn test_verify_securityt_params_mocked() { let mut mock_replies = HashMap::new(); // First request let mock_req = CallRequest { - from: None, to: Some(H160::from_str("0xd4a7e1bd8015057293f0d0a557088c286942e84b").unwrap()), - gas: None, - gas_price: None, - value: None, data: Some(Bytes::from(hex::decode("8687feae").unwrap())), - transaction_type: None, - access_list: None, - max_fee_per_gas: None, - max_priority_fee_per_gas: None, + ..Default::default() }; let mock_req = serde_json::to_string(&mock_req).unwrap(); let mock_res = Bytes::from( - hex::decode("000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020001000000000000000000000000000000000000000000000000000000000000") - .unwrap(), - ); + hex::decode("000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000032121210000000000000000000000000000000000000000000000000000000000") + .unwrap(), + ); mock_replies.insert(mock_req, mock_res); // Second request let mock_req = CallRequest { - from: None, to: Some(H160::from_str("0xd4a7e1bd8015057293f0d0a557088c286942e84b").unwrap()), - gas: None, - gas_price: None, - value: None, data: Some(Bytes::from(hex::decode("e15234ff").unwrap())), - transaction_type: None, - access_list: None, - max_fee_per_gas: None, - max_priority_fee_per_gas: None, + ..Default::default() }; let mock_req = serde_json::to_string(&mock_req).unwrap(); let mock_res = Bytes::from( - hex::decode("000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020001000000000000000000000000000000000000000000000000000000000000") - .unwrap(), - ); + hex::decode("000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020001000000000000000000000000000000000000000000000000000000000000") + .unwrap(), + ); mock_replies.insert(mock_req, mock_res); let cfg = EigenConfig::default(); - let query_client = MockVerifierClient::new(mock_replies); - let verifier = Verifier::new(cfg, Arc::new(query_client)).await.unwrap(); + let client = MockVerifierClient::new(mock_replies); + let verifier = Verifier::new(cfg, Arc::new(client)).await.unwrap(); let cert = BlobInfo { blob_header: BlobHeader { commitment: G1Commitment { From e25fe6609390968d211179967793253dd7d5ad87 Mon Sep 17 00:00:00 2001 From: Anton Baliasnikov Date: Wed, 22 Jan 2025 15:33:47 +0000 Subject: [PATCH 76/97] ci: add unified release please and crates.io publishing (#3508) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ CI: * [x] Add unified release-please workflow with automated crates.io publishing * [x] Add separate workflow to publish crates manually if required Rust: * [x] `release-please` is now updating workspace version in Cargo.toml and all crates are re-using the workspace version ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- .github/release-please/config.json | 21 +-- .github/workflows/publish-crates.yml | 42 +++++ .../workflows/release-please-cargo-lock.yml | 55 ------ .github/workflows/release-please.yml | 45 ++--- core/Cargo.lock | 156 +++++++++--------- core/Cargo.toml | 132 +++++++-------- core/bin/external_node/Cargo.toml | 2 +- .../Cargo.toml | 2 +- core/bin/selector_generator/Cargo.toml | 2 +- core/bin/snapshots_creator/Cargo.toml | 2 +- .../bin/system-constants-generator/Cargo.toml | 2 +- core/bin/verified_sources_fetcher/Cargo.toml | 2 +- core/bin/zksync_server/Cargo.toml | 2 +- core/bin/zksync_tee_prover/Cargo.toml | 2 +- core/tests/loadnext/Cargo.toml | 2 +- core/tests/vm-benchmark/Cargo.toml | 2 +- etc/nix/tee_prover.nix | 2 +- etc/nix/zksync.nix | 2 +- prover/Cargo.lock | 80 ++++----- prover/Cargo.toml | 53 +++--- prover/rust-toolchain | 3 +- zkstack_cli/Cargo.lock | 36 ++-- zkstack_cli/Cargo.toml | 26 +-- 23 files changed, 332 insertions(+), 341 deletions(-) create mode 100644 .github/workflows/publish-crates.yml delete mode 100644 .github/workflows/release-please-cargo-lock.yml diff --git a/.github/release-please/config.json b/.github/release-please/config.json index 358e249a18bd..28c3583af29e 100644 --- a/.github/release-please/config.json +++ b/.github/release-please/config.json @@ -5,26 +5,27 @@ "bump-minor-pre-major": true, "bump-patch-for-minor-pre-major": true, "include-component-in-tag": true, + "release-type": "simple", "packages": { "core": { - "release-type": "simple", - "component": "core", + "component": "core" + }, + "prover": { + "component": "prover", "extra-files": [ { "type": "generic", - "path": "bin/external_node/Cargo.toml" + "path": "Cargo.toml" } ] }, - "prover": { - "release-type": "simple", - "component": "prover" - }, "zkstack_cli": { - "release-type": "simple", "component": "zkstack_cli", - "plugins": [ - "cargo-workspace" + "extra-files": [ + { + "type": "generic", + "path": "Cargo.toml" + } ] } } diff --git a/.github/workflows/publish-crates.yml b/.github/workflows/publish-crates.yml new file mode 100644 index 000000000000..3c62fbae4f1a --- /dev/null +++ b/.github/workflows/publish-crates.yml @@ -0,0 +1,42 @@ +name: Publish crates + +on: + workflow_dispatch: + inputs: + compontent: + description: 'Component to release. Possible values are: core, prover or zkstack_cli.' + required: true + default: 'zkstack_cli' + run-build: + type: boolean + description: 'Build the workspace before release.' + required: false + default: true + run-tests: + type: boolean + description: 'Run tests before release.' + required: false + default: false + org-owner: + type: string + description: 'Organization to add as owner of the crates.' + required: false + default: 'github:matter-labs:crates-io' + + +jobs: + + publish-crates: + name: Publish to crates.io + runs-on: ubuntu-latest + steps: + - name: Publish crates + uses: matter-labs/zksync-ci-common/.github/actions/publish-crates@v1 + with: + slack_webhook: ${{ secrets.SLACK_WEBHOOK_RELEASES }} # Slack webhook for notifications + cargo_registry_token: ${{ secrets.CRATES_IO_TOKEN }} # Crates.io token for publishing + workspace_path: ${{ inputs.component }} + org_owner: ${{ inputs.org-owner }} + run_build: ${{ inputs.run-build }} + run_tests: ${{ inputs.run-tests }} + gh_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-please-cargo-lock.yml b/.github/workflows/release-please-cargo-lock.yml deleted file mode 100644 index 3b6e615e2960..000000000000 --- a/.github/workflows/release-please-cargo-lock.yml +++ /dev/null @@ -1,55 +0,0 @@ -on: - push: - branches: - - release-please--branches--main--components--core - -name: release-please-update-cargo-lock -jobs: - update_cargo_lock: - # TODO: After migraton switch to CI - runs-on: [matterlabs-default-infra-runners] - - steps: - - uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4 - with: - submodules: "recursive" - persist-credentials: false - - - name: Check last commit - id: condition - run: | - COMMIT=$(git log -1 --pretty=%B) - if [[ "$COMMIT" == "Update Cargo.lock" ]]; then - echo "Cargo.lock is already updated" - echo "::set-output name=skip_steps::true" - else - echo "Cargo.lock should be updated" - echo "::set-output name=skip_steps::false" - fi - - - name: Setup environment - if: steps.condition.outputs.skip_steps != 'true' - run: | - echo ZKSYNC_HOME=$(pwd) >> $GITHUB_ENV - echo $(pwd)/bin >> $GITHUB_PATH - echo IN_DOCKER=1 >> .env - - - name: Start services - if: steps.condition.outputs.skip_steps != 'true' - run: docker compose up -d zk - - - name: Cargo check - if: steps.condition.outputs.skip_steps != 'true' - run: ci_run cargo check --manifest-path core/Cargo.toml - - - name: Push changes - if: steps.condition.outputs.skip_steps != 'true' - env: - GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} - run: | - git config --global user.email "zksync-era-bot@users.noreply.github.com" - git config --global user.name "zksync-era-bot" - git remote set-url origin 'https://${{ secrets.RELEASE_TOKEN }}@github.com/matter-labs/zksync-era.git' - git add core/Cargo.lock - git commit -m "Update Cargo.lock" - git push diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 4a8f527f45c6..c1eabbb4c45d 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -1,29 +1,32 @@ +name: Release-please + +# Give permissions to the release-please to open, update PRs +# and commit to PRs the repository to update Cargo.lock +permissions: + contents: write + pull-requests: write + id-token: write + attestations: write + +# Run the workflow on push to the main branch or manually on: push: branches: - main workflow_dispatch: -permissions: - contents: write - pull-requests: write - -name: release-please jobs: - release-please: - runs-on: ubuntu-latest - steps: - - name: Run release-please - id: release - uses: google-github-actions/release-please-action@e4dc86ba9405554aeba3c6bb2d169500e7d3b4ee # v4.1.1 - with: - token: ${{ secrets.RELEASE_TOKEN }} - config-file: .github/release-please/config.json - manifest-file: .github/release-please/manifest.json - - name: Send Release Info - if: ${{ steps.release.outputs.releases_created == 'true' }} - uses: matter-labs/format-release-please-for-slack-action@69e6fe9e4ec531b7b5fb0d826f73c190db83cf42 # v2.1.0 - with: - release-please-output: ${{ toJSON(steps.release.outputs) }} - slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_RELEASES }} + # Prepare the release PR with changelog updates and create github releases + release-please: + uses: matter-labs/zksync-ci-common/.github/workflows/release-please.yaml@v1 + secrets: + slack_webhook: ${{ secrets.SLACK_WEBHOOK_RELEASES }} # Slack webhook for notifications + gh_token: ${{ secrets.GITHUB_TOKEN }} # GitHub token for release-please + with: + config: '.github/release-please/config.json' # Path to the configuration file + manifest: '.github/release-please/manifest.json' # Path to the manifest file + update-cargo-lock: true # Update Cargo.lock file in the release PR + publish-to-crates-io: true # Enable publishing to crates.io + upgrade-dependencies: true # Upgrade cross-workspace dependencies + version-suffix: 'non-semver-compat' # Version suffix for the crates.io release diff --git a/core/Cargo.lock b/core/Cargo.lock index ad7915ca2376..3f9e1ee93561 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -1233,7 +1233,7 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] name = "block_reverter" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "clap 4.5.23", @@ -2233,7 +2233,7 @@ dependencies = [ [[package]] name = "custom_genesis_export" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "bincode", @@ -3485,7 +3485,7 @@ dependencies = [ [[package]] name = "genesis_generator" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "clap 4.5.23", @@ -5156,7 +5156,7 @@ checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "loadnext" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", @@ -5343,7 +5343,7 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "merkle_tree_consistency_checker" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "clap 4.5.23", @@ -7984,7 +7984,7 @@ dependencies = [ [[package]] name = "selector_generator" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "clap 4.5.23", @@ -8644,7 +8644,7 @@ dependencies = [ [[package]] name = "snapshots_creator" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "futures 0.3.31", @@ -9393,7 +9393,7 @@ dependencies = [ [[package]] name = "system-constants-generator" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "codegen", "once_cell", @@ -10373,7 +10373,7 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "verified_sources_fetcher" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "serde_json", @@ -10430,7 +10430,7 @@ dependencies = [ [[package]] name = "vm-benchmark" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "assert_matches", "criterion", @@ -11293,7 +11293,7 @@ dependencies = [ [[package]] name = "zksync_base_token_adjuster" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", @@ -11315,7 +11315,7 @@ dependencies = [ [[package]] name = "zksync_basic_types" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "bincode", @@ -11360,7 +11360,7 @@ dependencies = [ [[package]] name = "zksync_block_reverter" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -11384,7 +11384,7 @@ dependencies = [ [[package]] name = "zksync_circuit_breaker" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", @@ -11398,7 +11398,7 @@ dependencies = [ [[package]] name = "zksync_commitment_generator" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "circuit_encodings", @@ -11446,7 +11446,7 @@ dependencies = [ [[package]] name = "zksync_config" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "rand 0.8.5", @@ -11619,7 +11619,7 @@ dependencies = [ [[package]] name = "zksync_consistency_checker" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -11644,7 +11644,7 @@ dependencies = [ [[package]] name = "zksync_contract_verification_server" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "axum 0.7.9", @@ -11663,7 +11663,7 @@ dependencies = [ [[package]] name = "zksync_contract_verifier" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "clap 4.5.23", @@ -11680,7 +11680,7 @@ dependencies = [ [[package]] name = "zksync_contract_verifier_lib" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -11712,7 +11712,7 @@ dependencies = [ [[package]] name = "zksync_contracts" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "bincode", "envy", @@ -11726,7 +11726,7 @@ dependencies = [ [[package]] name = "zksync_core_leftovers" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "ctrlc", @@ -11740,7 +11740,7 @@ dependencies = [ [[package]] name = "zksync_crypto_primitives" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "blake2 0.10.6", @@ -11768,7 +11768,7 @@ dependencies = [ [[package]] name = "zksync_da_client" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", @@ -11777,7 +11777,7 @@ dependencies = [ [[package]] name = "zksync_da_clients" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", @@ -11821,7 +11821,7 @@ dependencies = [ [[package]] name = "zksync_da_dispatcher" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "chrono", @@ -11838,7 +11838,7 @@ dependencies = [ [[package]] name = "zksync_dal" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "bigdecimal", @@ -11874,7 +11874,7 @@ dependencies = [ [[package]] name = "zksync_db_connection" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -11892,7 +11892,7 @@ dependencies = [ [[package]] name = "zksync_env_config" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "envy", @@ -11904,7 +11904,7 @@ dependencies = [ [[package]] name = "zksync_eth_client" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "assert_matches", "async-trait", @@ -11926,7 +11926,7 @@ dependencies = [ [[package]] name = "zksync_eth_sender" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -11956,7 +11956,7 @@ dependencies = [ [[package]] name = "zksync_eth_signer" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "async-trait", "rlp", @@ -11967,7 +11967,7 @@ dependencies = [ [[package]] name = "zksync_eth_watch" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-recursion", @@ -11994,7 +11994,7 @@ dependencies = [ [[package]] name = "zksync_external_node" -version = "26.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -12048,7 +12048,7 @@ dependencies = [ [[package]] name = "zksync_external_price_api" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", @@ -12069,7 +12069,7 @@ dependencies = [ [[package]] name = "zksync_external_proof_integration_api" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", @@ -12115,7 +12115,7 @@ dependencies = [ [[package]] name = "zksync_health_check" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "assert_matches", "async-trait", @@ -12130,7 +12130,7 @@ dependencies = [ [[package]] name = "zksync_house_keeper" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", @@ -12162,7 +12162,7 @@ dependencies = [ [[package]] name = "zksync_l1_contract_interface" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "circuit_definitions", @@ -12183,7 +12183,7 @@ dependencies = [ [[package]] name = "zksync_logs_bloom_backfill" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "tokio", @@ -12195,7 +12195,7 @@ dependencies = [ [[package]] name = "zksync_mempool" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "tracing", "zksync_types", @@ -12203,7 +12203,7 @@ dependencies = [ [[package]] name = "zksync_merkle_tree" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -12232,7 +12232,7 @@ dependencies = [ [[package]] name = "zksync_metadata_calculator" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -12266,7 +12266,7 @@ dependencies = [ [[package]] name = "zksync_mini_merkle_tree" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "criterion", "once_cell", @@ -12276,7 +12276,7 @@ dependencies = [ [[package]] name = "zksync_multivm" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -12308,7 +12308,7 @@ dependencies = [ [[package]] name = "zksync_node_api_server" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -12362,7 +12362,7 @@ dependencies = [ [[package]] name = "zksync_node_consensus" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", @@ -12406,7 +12406,7 @@ dependencies = [ [[package]] name = "zksync_node_db_pruner" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -12428,7 +12428,7 @@ dependencies = [ [[package]] name = "zksync_node_fee_model" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", @@ -12447,7 +12447,7 @@ dependencies = [ [[package]] name = "zksync_node_framework" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -12510,7 +12510,7 @@ dependencies = [ [[package]] name = "zksync_node_framework_derive" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "proc-macro2 1.0.92", "quote 1.0.37", @@ -12519,7 +12519,7 @@ dependencies = [ [[package]] name = "zksync_node_genesis" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "bincode", @@ -12540,7 +12540,7 @@ dependencies = [ [[package]] name = "zksync_node_storage_init" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", @@ -12562,7 +12562,7 @@ dependencies = [ [[package]] name = "zksync_node_sync" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -12597,7 +12597,7 @@ dependencies = [ [[package]] name = "zksync_node_test_utils" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "zksync_contracts", "zksync_dal", @@ -12609,7 +12609,7 @@ dependencies = [ [[package]] name = "zksync_object_store" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -12647,7 +12647,7 @@ dependencies = [ [[package]] name = "zksync_proof_data_handler" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "axum 0.7.9", @@ -12709,7 +12709,7 @@ dependencies = [ [[package]] name = "zksync_protobuf_config" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "hex", @@ -12729,7 +12729,7 @@ dependencies = [ [[package]] name = "zksync_prover_interface" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "bincode", "chrono", @@ -12748,7 +12748,7 @@ dependencies = [ [[package]] name = "zksync_queued_job_processor" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", @@ -12760,7 +12760,7 @@ dependencies = [ [[package]] name = "zksync_reorg_detector" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -12781,7 +12781,7 @@ dependencies = [ [[package]] name = "zksync_server" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "clap 4.5.23", @@ -12811,7 +12811,7 @@ dependencies = [ [[package]] name = "zksync_shared_metrics" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "rustc_version 0.4.1", "serde", @@ -12823,7 +12823,7 @@ dependencies = [ [[package]] name = "zksync_snapshots_applier" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -12862,7 +12862,7 @@ dependencies = [ [[package]] name = "zksync_state" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -12887,7 +12887,7 @@ dependencies = [ [[package]] name = "zksync_state_keeper" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -12927,7 +12927,7 @@ dependencies = [ [[package]] name = "zksync_storage" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "num_cpus", "once_cell", @@ -12940,7 +12940,7 @@ dependencies = [ [[package]] name = "zksync_system_constants" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "once_cell", "zksync_basic_types", @@ -12948,7 +12948,7 @@ dependencies = [ [[package]] name = "zksync_tee_prover" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", @@ -12973,7 +12973,7 @@ dependencies = [ [[package]] name = "zksync_tee_verifier" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "bincode", @@ -12991,7 +12991,7 @@ dependencies = [ [[package]] name = "zksync_test_contracts" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "ethabi", "foundry-compilers", @@ -13007,7 +13007,7 @@ dependencies = [ [[package]] name = "zksync_types" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -13042,7 +13042,7 @@ dependencies = [ [[package]] name = "zksync_utils" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -13058,7 +13058,7 @@ dependencies = [ [[package]] name = "zksync_vlog" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "chrono", @@ -13103,7 +13103,7 @@ dependencies = [ [[package]] name = "zksync_vm_executor" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -13121,7 +13121,7 @@ dependencies = [ [[package]] name = "zksync_vm_interface" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -13139,7 +13139,7 @@ dependencies = [ [[package]] name = "zksync_vm_runner" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", @@ -13173,7 +13173,7 @@ dependencies = [ [[package]] name = "zksync_web3_decl" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "assert_matches", diff --git a/core/Cargo.toml b/core/Cargo.toml index 7ed6f2d6b601..3b6ba37c28a6 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -91,7 +91,7 @@ inherits = "release" debug = true [workspace.package] -version = "0.1.0" +version = "26.1.0-non-semver-compat" edition = "2021" authors = ["The Matter Labs Team "] homepage = "https://zksync.io/" @@ -257,70 +257,70 @@ zksync_protobuf = "=0.8.0" zksync_protobuf_build = "=0.8.0" # "Local" dependencies -zksync_multivm = { version = "0.1.0", path = "lib/multivm" } -zksync_vlog = { version = "0.1.0", path = "lib/vlog" } -zksync_vm_interface = { version = "0.1.0", path = "lib/vm_interface" } -zksync_vm_executor = { version = "0.1.0", path = "lib/vm_executor" } -zksync_basic_types = { version = "0.1.0", path = "lib/basic_types" } -zksync_circuit_breaker = { version = "0.1.0", path = "lib/circuit_breaker" } -zksync_config = { version = "0.1.0", path = "lib/config" } -zksync_contract_verifier_lib = { version = "0.1.0", path = "lib/contract_verifier" } -zksync_contracts = { version = "0.1.0", path = "lib/contracts" } -zksync_core_leftovers = { version = "0.1.0", path = "lib/zksync_core_leftovers" } -zksync_dal = { version = "0.1.0", path = "lib/dal" } -zksync_db_connection = { version = "0.1.0", path = "lib/db_connection" } -zksync_env_config = { version = "0.1.0", path = "lib/env_config" } -zksync_eth_client = { version = "0.1.0", path = "lib/eth_client" } -zksync_da_client = { version = "0.1.0", path = "lib/da_client" } -zksync_eth_signer = { version = "0.1.0", path = "lib/eth_signer" } -zksync_health_check = { version = "0.1.0", path = "lib/health_check" } -zksync_l1_contract_interface = { version = "0.1.0", path = "lib/l1_contract_interface" } -zksync_mempool = { version = "0.1.0", path = "lib/mempool" } -zksync_merkle_tree = { version = "0.1.0", path = "lib/merkle_tree" } -zksync_bin_metadata = { version = "0.1.0", path = "lib/bin_metadata" } -zksync_mini_merkle_tree = { version = "0.1.0", path = "lib/mini_merkle_tree" } -zksync_object_store = { version = "0.1.0", path = "lib/object_store" } -zksync_protobuf_config = { version = "0.1.0", path = "lib/protobuf_config" } -zksync_prover_interface = { version = "0.1.0", path = "lib/prover_interface" } -zksync_queued_job_processor = { version = "0.1.0", path = "lib/queued_job_processor" } -zksync_snapshots_applier = { version = "0.1.0", path = "lib/snapshots_applier" } -zksync_state = { version = "0.1.0", path = "lib/state" } -zksync_storage = { version = "0.1.0", path = "lib/storage" } -zksync_system_constants = { version = "0.1.0", path = "lib/constants" } -zksync_tee_verifier = { version = "0.1.0", path = "lib/tee_verifier" } -zksync_test_contracts = { version = "0.1.0", path = "lib/test_contracts" } -zksync_types = { version = "0.1.0", path = "lib/types" } -zksync_utils = { version = "0.1.0", path = "lib/utils" } -zksync_web3_decl = { version = "0.1.0", path = "lib/web3_decl" } -zksync_crypto_primitives = { version = "0.1.0", path = "lib/crypto_primitives" } -zksync_external_price_api = { version = "0.1.0", path = "lib/external_price_api" } +zksync_multivm = { version = "=26.1.0-non-semver-compat", path = "lib/multivm" } +zksync_vlog = { version = "=26.1.0-non-semver-compat", path = "lib/vlog" } +zksync_vm_interface = { version = "=26.1.0-non-semver-compat", path = "lib/vm_interface" } +zksync_vm_executor = { version = "=26.1.0-non-semver-compat", path = "lib/vm_executor" } +zksync_basic_types = { version = "=26.1.0-non-semver-compat", path = "lib/basic_types" } +zksync_circuit_breaker = { version = "=26.1.0-non-semver-compat", path = "lib/circuit_breaker" } +zksync_config = { version = "=26.1.0-non-semver-compat", path = "lib/config" } +zksync_contract_verifier_lib = { version = "=26.1.0-non-semver-compat", path = "lib/contract_verifier" } +zksync_contracts = { version = "=26.1.0-non-semver-compat", path = "lib/contracts" } +zksync_core_leftovers = { version = "=26.1.0-non-semver-compat", path = "lib/zksync_core_leftovers" } +zksync_dal = { version = "=26.1.0-non-semver-compat", path = "lib/dal" } +zksync_db_connection = { version = "=26.1.0-non-semver-compat", path = "lib/db_connection" } +zksync_env_config = { version = "=26.1.0-non-semver-compat", path = "lib/env_config" } +zksync_eth_client = { version = "=26.1.0-non-semver-compat", path = "lib/eth_client" } +zksync_da_client = { version = "=26.1.0-non-semver-compat", path = "lib/da_client" } +zksync_eth_signer = { version = "=26.1.0-non-semver-compat", path = "lib/eth_signer" } +zksync_health_check = { version = "=26.1.0-non-semver-compat", path = "lib/health_check" } +zksync_l1_contract_interface = { version = "=26.1.0-non-semver-compat", path = "lib/l1_contract_interface" } +zksync_mempool = { version = "=26.1.0-non-semver-compat", path = "lib/mempool" } +zksync_merkle_tree = { version = "=26.1.0-non-semver-compat", path = "lib/merkle_tree" } +zksync_bin_metadata = { version = "=26.1.0-non-semver-compat", path = "lib/bin_metadata" } +zksync_mini_merkle_tree = { version = "=26.1.0-non-semver-compat", path = "lib/mini_merkle_tree" } +zksync_object_store = { version = "=26.1.0-non-semver-compat", path = "lib/object_store" } +zksync_protobuf_config = { version = "=26.1.0-non-semver-compat", path = "lib/protobuf_config" } +zksync_prover_interface = { version = "=26.1.0-non-semver-compat", path = "lib/prover_interface" } +zksync_queued_job_processor = { version = "=26.1.0-non-semver-compat", path = "lib/queued_job_processor" } +zksync_snapshots_applier = { version = "=26.1.0-non-semver-compat", path = "lib/snapshots_applier" } +zksync_state = { version = "=26.1.0-non-semver-compat", path = "lib/state" } +zksync_storage = { version = "=26.1.0-non-semver-compat", path = "lib/storage" } +zksync_system_constants = { version = "=26.1.0-non-semver-compat", path = "lib/constants" } +zksync_tee_verifier = { version = "=26.1.0-non-semver-compat", path = "lib/tee_verifier" } +zksync_test_contracts = { version = "=26.1.0-non-semver-compat", path = "lib/test_contracts" } +zksync_types = { version = "=26.1.0-non-semver-compat", path = "lib/types" } +zksync_utils = { version = "=26.1.0-non-semver-compat", path = "lib/utils" } +zksync_web3_decl = { version = "=26.1.0-non-semver-compat", path = "lib/web3_decl" } +zksync_crypto_primitives = { version = "=26.1.0-non-semver-compat", path = "lib/crypto_primitives" } +zksync_external_price_api = { version = "=26.1.0-non-semver-compat", path = "lib/external_price_api" } # Framework and components -zksync_node_framework = { version = "0.1.0", path = "node/node_framework" } -zksync_node_framework_derive = { version = "0.1.0", path = "lib/node_framework_derive" } -zksync_eth_watch = { version = "0.1.0", path = "node/eth_watch" } -zksync_shared_metrics = { version = "0.1.0", path = "node/shared_metrics" } -zksync_proof_data_handler = { version = "0.1.0", path = "node/proof_data_handler" } -zksync_block_reverter = { version = "0.1.0", path = "node/block_reverter" } -zksync_commitment_generator = { version = "0.1.0", path = "node/commitment_generator" } -zksync_house_keeper = { version = "0.1.0", path = "node/house_keeper" } -zksync_node_genesis = { version = "0.1.0", path = "node/genesis" } -zksync_da_dispatcher = { version = "0.1.0", path = "node/da_dispatcher" } -zksync_da_clients = { version = "0.1.0", path = "node/da_clients" } -zksync_eth_sender = { version = "0.1.0", path = "node/eth_sender" } -zksync_node_db_pruner = { version = "0.1.0", path = "node/db_pruner" } -zksync_node_fee_model = { version = "0.1.0", path = "node/fee_model" } -zksync_vm_runner = { version = "0.1.0", path = "node/vm_runner" } -zksync_external_proof_integration_api = { version = "0.1.0", path = "node/external_proof_integration_api" } -zksync_node_test_utils = { version = "0.1.0", path = "node/test_utils" } -zksync_state_keeper = { version = "0.1.0", path = "node/state_keeper" } -zksync_reorg_detector = { version = "0.1.0", path = "node/reorg_detector" } -zksync_consistency_checker = { version = "0.1.0", path = "node/consistency_checker" } -zksync_metadata_calculator = { version = "0.1.0", path = "node/metadata_calculator" } -zksync_node_sync = { version = "0.1.0", path = "node/node_sync" } -zksync_node_storage_init = { version = "0.1.0", path = "node/node_storage_init" } -zksync_node_consensus = { version = "0.1.0", path = "node/consensus" } -zksync_contract_verification_server = { version = "0.1.0", path = "node/contract_verification_server" } -zksync_node_api_server = { version = "0.1.0", path = "node/api_server" } -zksync_base_token_adjuster = { version = "0.1.0", path = "node/base_token_adjuster" } -zksync_logs_bloom_backfill = { version = "0.1.0", path = "node/logs_bloom_backfill" } +zksync_node_framework = { version = "=26.1.0-non-semver-compat", path = "node/node_framework" } +zksync_node_framework_derive = { version = "=26.1.0-non-semver-compat", path = "lib/node_framework_derive" } +zksync_eth_watch = { version = "=26.1.0-non-semver-compat", path = "node/eth_watch" } +zksync_shared_metrics = { version = "=26.1.0-non-semver-compat", path = "node/shared_metrics" } +zksync_proof_data_handler = { version = "=26.1.0-non-semver-compat", path = "node/proof_data_handler" } +zksync_block_reverter = { version = "=26.1.0-non-semver-compat", path = "node/block_reverter" } +zksync_commitment_generator = { version = "=26.1.0-non-semver-compat", path = "node/commitment_generator" } +zksync_house_keeper = { version = "=26.1.0-non-semver-compat", path = "node/house_keeper" } +zksync_node_genesis = { version = "=26.1.0-non-semver-compat", path = "node/genesis" } +zksync_da_dispatcher = { version = "=26.1.0-non-semver-compat", path = "node/da_dispatcher" } +zksync_da_clients = { version = "=26.1.0-non-semver-compat", path = "node/da_clients" } +zksync_eth_sender = { version = "=26.1.0-non-semver-compat", path = "node/eth_sender" } +zksync_node_db_pruner = { version = "=26.1.0-non-semver-compat", path = "node/db_pruner" } +zksync_node_fee_model = { version = "=26.1.0-non-semver-compat", path = "node/fee_model" } +zksync_vm_runner = { version = "=26.1.0-non-semver-compat", path = "node/vm_runner" } +zksync_external_proof_integration_api = { version = "=26.1.0-non-semver-compat", path = "node/external_proof_integration_api" } +zksync_node_test_utils = { version = "=26.1.0-non-semver-compat", path = "node/test_utils" } +zksync_state_keeper = { version = "=26.1.0-non-semver-compat", path = "node/state_keeper" } +zksync_reorg_detector = { version = "=26.1.0-non-semver-compat", path = "node/reorg_detector" } +zksync_consistency_checker = { version = "=26.1.0-non-semver-compat", path = "node/consistency_checker" } +zksync_metadata_calculator = { version = "=26.1.0-non-semver-compat", path = "node/metadata_calculator" } +zksync_node_sync = { version = "=26.1.0-non-semver-compat", path = "node/node_sync" } +zksync_node_storage_init = { version = "=26.1.0-non-semver-compat", path = "node/node_storage_init" } +zksync_node_consensus = { version = "=26.1.0-non-semver-compat", path = "node/consensus" } +zksync_contract_verification_server = { version = "=26.1.0-non-semver-compat", path = "node/contract_verification_server" } +zksync_node_api_server = { version = "=26.1.0-non-semver-compat", path = "node/api_server" } +zksync_base_token_adjuster = { version = "=26.1.0-non-semver-compat", path = "node/base_token_adjuster" } +zksync_logs_bloom_backfill = { version = "=26.1.0-non-semver-compat", path = "node/logs_bloom_backfill" } diff --git a/core/bin/external_node/Cargo.toml b/core/bin/external_node/Cargo.toml index f9e494a62780..cb75a20c3ed7 100644 --- a/core/bin/external_node/Cargo.toml +++ b/core/bin/external_node/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "zksync_external_node" description = "Non-validator ZKsync node" -version = "26.1.0" # x-release-please-version +version.workspace = true edition.workspace = true authors.workspace = true homepage.workspace = true diff --git a/core/bin/merkle_tree_consistency_checker/Cargo.toml b/core/bin/merkle_tree_consistency_checker/Cargo.toml index eb7dcd81a0dc..f915f321f139 100644 --- a/core/bin/merkle_tree_consistency_checker/Cargo.toml +++ b/core/bin/merkle_tree_consistency_checker/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "merkle_tree_consistency_checker" description = "Tool to verify consistency of ZKsync Merkle Tree" -version = "0.1.0" +version.workspace = true edition.workspace = true authors.workspace = true homepage.workspace = true diff --git a/core/bin/selector_generator/Cargo.toml b/core/bin/selector_generator/Cargo.toml index b3425c11b4ec..28b3983605b3 100644 --- a/core/bin/selector_generator/Cargo.toml +++ b/core/bin/selector_generator/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "selector_generator" -version = "0.1.0" +version.workspace = true edition.workspace = true authors.workspace = true homepage.workspace = true diff --git a/core/bin/snapshots_creator/Cargo.toml b/core/bin/snapshots_creator/Cargo.toml index 5a36c646e88e..aa2dde097240 100644 --- a/core/bin/snapshots_creator/Cargo.toml +++ b/core/bin/snapshots_creator/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "snapshots_creator" description = "Tool to create ZKsync state snapshots" -version = "0.1.0" +version.workspace = true edition.workspace = true authors.workspace = true homepage.workspace = true diff --git a/core/bin/system-constants-generator/Cargo.toml b/core/bin/system-constants-generator/Cargo.toml index 7177d29ca743..d3b600ba258f 100644 --- a/core/bin/system-constants-generator/Cargo.toml +++ b/core/bin/system-constants-generator/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "system-constants-generator" description = "Tool for generating JSON files with the system constants for L1/L2 contracts" -version = "0.1.0" +version.workspace = true edition.workspace = true authors.workspace = true homepage.workspace = true diff --git a/core/bin/verified_sources_fetcher/Cargo.toml b/core/bin/verified_sources_fetcher/Cargo.toml index 5fa90590ed5f..b143cafdbaee 100644 --- a/core/bin/verified_sources_fetcher/Cargo.toml +++ b/core/bin/verified_sources_fetcher/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "verified_sources_fetcher" description = "Tool to fetch verified contract sources" -version = "0.1.0" +version.workspace = true edition.workspace = true authors.workspace = true homepage.workspace = true diff --git a/core/bin/zksync_server/Cargo.toml b/core/bin/zksync_server/Cargo.toml index 4cf028be8210..e5eeeb0c79a9 100644 --- a/core/bin/zksync_server/Cargo.toml +++ b/core/bin/zksync_server/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "zksync_server" description = "ZKsync validator/sequencer node" -version = "0.1.0" +version.workspace = true edition.workspace = true authors.workspace = true homepage.workspace = true diff --git a/core/bin/zksync_tee_prover/Cargo.toml b/core/bin/zksync_tee_prover/Cargo.toml index b853da348ee0..303ad30cf2ac 100644 --- a/core/bin/zksync_tee_prover/Cargo.toml +++ b/core/bin/zksync_tee_prover/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "zksync_tee_prover" description = "ZKsync TEE prover" -version = "0.1.0" +version.workspace = true edition.workspace = true authors.workspace = true homepage.workspace = true diff --git a/core/tests/loadnext/Cargo.toml b/core/tests/loadnext/Cargo.toml index 91f987035acf..32957f0372ac 100644 --- a/core/tests/loadnext/Cargo.toml +++ b/core/tests/loadnext/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "loadnext" -version = "0.1.0" +version.workspace = true edition.workspace = true authors.workspace = true homepage.workspace = true diff --git a/core/tests/vm-benchmark/Cargo.toml b/core/tests/vm-benchmark/Cargo.toml index eb4a5a239252..f7c1bf880bad 100644 --- a/core/tests/vm-benchmark/Cargo.toml +++ b/core/tests/vm-benchmark/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vm-benchmark" -version = "0.1.0" +version.workspace = true edition.workspace = true license.workspace = true publish = false diff --git a/etc/nix/tee_prover.nix b/etc/nix/tee_prover.nix index 55545d1bb8e4..5811297ce854 100644 --- a/etc/nix/tee_prover.nix +++ b/etc/nix/tee_prover.nix @@ -7,7 +7,7 @@ let in craneLib.buildPackage (commonArgs // { inherit pname; - version = (builtins.fromTOML (builtins.readFile ../../core/bin/zksync_tee_prover/Cargo.toml)).package.version; + version = (builtins.fromTOML (builtins.readFile ../../core/Cargo.toml)).workspace.package.version; inherit cargoExtraArgs; cargoArtifacts = craneLib.buildDepsOnly (commonArgs // { diff --git a/etc/nix/zksync.nix b/etc/nix/zksync.nix index 1ecac58b5d91..16d452c01bfd 100644 --- a/etc/nix/zksync.nix +++ b/etc/nix/zksync.nix @@ -3,7 +3,7 @@ }: craneLib.buildPackage (commonArgs // { pname = "zksync"; - version = (builtins.fromTOML (builtins.readFile ../../core/bin/zksync_tee_prover/Cargo.toml)).package.version; + version = (builtins.fromTOML (builtins.readFile ../../core/Cargo.toml)).workspace.package.version; cargoExtraArgs = "--all"; cargoArtifacts = craneLib.buildDepsOnly (commonArgs // { diff --git a/prover/Cargo.lock b/prover/Cargo.lock index 3d43239a15c6..a9a8d7e71c54 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -4705,7 +4705,7 @@ dependencies = [ [[package]] name = "prover_cli" -version = "0.1.0" +version = "17.1.1" dependencies = [ "anyhow", "assert_cmd", @@ -4738,7 +4738,7 @@ dependencies = [ [[package]] name = "prover_version" -version = "0.1.0" +version = "17.1.1" dependencies = [ "zksync_prover_fri_types", ] @@ -8063,7 +8063,7 @@ dependencies = [ [[package]] name = "zksync_basic_types" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "chrono", @@ -8107,7 +8107,7 @@ dependencies = [ [[package]] name = "zksync_circuit_prover" -version = "0.1.0" +version = "17.1.1" dependencies = [ "anyhow", "async-trait", @@ -8137,7 +8137,7 @@ dependencies = [ [[package]] name = "zksync_circuit_prover_service" -version = "0.1.0" +version = "17.1.1" dependencies = [ "anyhow", "async-trait", @@ -8176,7 +8176,7 @@ dependencies = [ [[package]] name = "zksync_config" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "rand 0.8.5", @@ -8267,7 +8267,7 @@ dependencies = [ [[package]] name = "zksync_contracts" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "envy", "hex", @@ -8280,7 +8280,7 @@ dependencies = [ [[package]] name = "zksync_core_leftovers" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "ctrlc", @@ -8294,7 +8294,7 @@ dependencies = [ [[package]] name = "zksync_crypto_primitives" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "blake2 0.10.6", @@ -8322,7 +8322,7 @@ dependencies = [ [[package]] name = "zksync_dal" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "bigdecimal", @@ -8357,7 +8357,7 @@ dependencies = [ [[package]] name = "zksync_db_connection" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "rand 0.8.5", @@ -8373,7 +8373,7 @@ dependencies = [ [[package]] name = "zksync_env_config" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "envy", @@ -8384,7 +8384,7 @@ dependencies = [ [[package]] name = "zksync_eth_client" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "async-trait", "jsonrpsee", @@ -8401,7 +8401,7 @@ dependencies = [ [[package]] name = "zksync_eth_signer" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "async-trait", "rlp", @@ -8457,7 +8457,7 @@ dependencies = [ [[package]] name = "zksync_l1_contract_interface" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "circuit_definitions", @@ -8474,7 +8474,7 @@ dependencies = [ [[package]] name = "zksync_mini_merkle_tree" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "once_cell", "zksync_basic_types", @@ -8483,7 +8483,7 @@ dependencies = [ [[package]] name = "zksync_multivm" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "circuit_sequencer_api", @@ -8509,7 +8509,7 @@ dependencies = [ [[package]] name = "zksync_object_store" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", @@ -8545,7 +8545,7 @@ dependencies = [ [[package]] name = "zksync_proof_fri_compressor" -version = "0.1.0" +version = "17.1.1" dependencies = [ "anyhow", "async-trait", @@ -8620,7 +8620,7 @@ dependencies = [ [[package]] name = "zksync_protobuf_config" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "hex", @@ -8640,7 +8640,7 @@ dependencies = [ [[package]] name = "zksync_prover_autoscaler" -version = "0.1.0" +version = "17.1.1" dependencies = [ "anyhow", "async-trait", @@ -8677,7 +8677,7 @@ dependencies = [ [[package]] name = "zksync_prover_dal" -version = "0.1.0" +version = "17.1.1" dependencies = [ "sqlx", "strum", @@ -8687,7 +8687,7 @@ dependencies = [ [[package]] name = "zksync_prover_fri" -version = "0.1.0" +version = "17.1.1" dependencies = [ "anyhow", "async-trait", @@ -8721,7 +8721,7 @@ dependencies = [ [[package]] name = "zksync_prover_fri_gateway" -version = "0.1.0" +version = "17.1.1" dependencies = [ "anyhow", "async-trait", @@ -8747,7 +8747,7 @@ dependencies = [ [[package]] name = "zksync_prover_fri_types" -version = "0.1.0" +version = "17.1.1" dependencies = [ "circuit_definitions", "serde", @@ -8757,7 +8757,7 @@ dependencies = [ [[package]] name = "zksync_prover_fri_utils" -version = "0.1.0" +version = "17.1.1" dependencies = [ "anyhow", "regex", @@ -8775,7 +8775,7 @@ dependencies = [ [[package]] name = "zksync_prover_interface" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "chrono", "circuit_definitions", @@ -8791,7 +8791,7 @@ dependencies = [ [[package]] name = "zksync_prover_job_monitor" -version = "0.1.0" +version = "17.1.1" dependencies = [ "anyhow", "async-trait", @@ -8813,7 +8813,7 @@ dependencies = [ [[package]] name = "zksync_prover_job_processor" -version = "0.1.0" +version = "17.1.1" dependencies = [ "anyhow", "async-trait", @@ -8828,7 +8828,7 @@ dependencies = [ [[package]] name = "zksync_prover_keystore" -version = "0.1.0" +version = "17.1.1" dependencies = [ "anyhow", "bincode", @@ -8855,7 +8855,7 @@ dependencies = [ [[package]] name = "zksync_queued_job_processor" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", @@ -8884,7 +8884,7 @@ dependencies = [ [[package]] name = "zksync_system_constants" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "once_cell", "zksync_basic_types", @@ -8892,7 +8892,7 @@ dependencies = [ [[package]] name = "zksync_types" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", @@ -8924,7 +8924,7 @@ dependencies = [ [[package]] name = "zksync_utils" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "futures 0.3.31", @@ -8939,7 +8939,7 @@ dependencies = [ [[package]] name = "zksync_vk_setup_data_generator_server_fri" -version = "0.1.0" +version = "17.1.1" dependencies = [ "anyhow", "bincode", @@ -8963,7 +8963,7 @@ dependencies = [ [[package]] name = "zksync_vlog" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "chrono", @@ -9008,7 +9008,7 @@ dependencies = [ [[package]] name = "zksync_vm_interface" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", @@ -9024,7 +9024,7 @@ dependencies = [ [[package]] name = "zksync_web3_decl" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", @@ -9045,7 +9045,7 @@ dependencies = [ [[package]] name = "zksync_witness_generator" -version = "0.1.0" +version = "17.1.1" dependencies = [ "anyhow", "async-trait", @@ -9083,7 +9083,7 @@ dependencies = [ [[package]] name = "zksync_witness_vector_generator" -version = "0.1.0" +version = "17.1.1" dependencies = [ "anyhow", "async-trait", diff --git a/prover/Cargo.toml b/prover/Cargo.toml index c1d006f2a759..6c249b7094ae 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -4,7 +4,7 @@ members = ["crates/bin/*", "crates/lib/*"] resolver = "2" [workspace.package] -version = "0.1.0" +version = "17.1.1" # x-release-please-version edition = "2021" authors = ["The Matter Labs Team "] homepage = "https://zksync.io/" @@ -82,34 +82,33 @@ shivini = "=0.152.10" boojum-cuda = "=0.152.10" # Core workspace dependencies -zksync_multivm = { path = "../core/lib/multivm", version = "0.1.0" } -zksync_vlog = { path = "../core/lib/vlog" } -zksync_basic_types = { path = "../core/lib/basic_types" } -zksync_config = { path = "../core/lib/config" } -zksync_dal = { path = "../core/lib/dal" } -zksync_db_connection = { path = "../core/lib/db_connection" } -zksync_env_config = { path = "../core/lib/env_config" } -zksync_object_store = { path = "../core/lib/object_store" } -zksync_prover_interface = { path = "../core/lib/prover_interface" } -zksync_queued_job_processor = { path = "../core/lib/queued_job_processor" } -zksync_system_constants = { path = "../core/lib/constants" } -zksync_types = { path = "../core/lib/types" } -zksync_utils = { path = "../core/lib/utils" } -zksync_eth_client = { path = "../core/lib/eth_client" } -zksync_contracts = { path = "../core/lib/contracts" } -zksync_core_leftovers = { path = "../core/lib/zksync_core_leftovers" } -zksync_periodic_job = { path = "../core/lib/periodic_job" } -zksync_protobuf_config = { path = "../core/lib/protobuf_config" } +zksync_multivm = { version = "=26.1.0-non-semver-compat", path = "../core/lib/multivm" } +zksync_vlog = { version = "=26.1.0-non-semver-compat", path = "../core/lib/vlog" } +zksync_basic_types = { version = "=26.1.0-non-semver-compat", path = "../core/lib/basic_types" } +zksync_config = { version = "=26.1.0-non-semver-compat", path = "../core/lib/config" } +zksync_dal = { version = "=26.1.0-non-semver-compat", path = "../core/lib/dal" } +zksync_db_connection = { version = "=26.1.0-non-semver-compat", path = "../core/lib/db_connection" } +zksync_env_config = { version = "=26.1.0-non-semver-compat", path = "../core/lib/env_config" } +zksync_object_store = { version = "=26.1.0-non-semver-compat", path = "../core/lib/object_store" } +zksync_prover_interface = { version = "=26.1.0-non-semver-compat", path = "../core/lib/prover_interface" } +zksync_queued_job_processor = { version = "=26.1.0-non-semver-compat", path = "../core/lib/queued_job_processor" } +zksync_system_constants = { version = "=26.1.0-non-semver-compat", path = "../core/lib/constants" } +zksync_types = { version = "=26.1.0-non-semver-compat", path = "../core/lib/types" } +zksync_utils = { version = "=26.1.0-non-semver-compat", path = "../core/lib/utils" } +zksync_eth_client = { version = "=26.1.0-non-semver-compat", path = "../core/lib/eth_client" } +zksync_contracts = { version = "=26.1.0-non-semver-compat", path = "../core/lib/contracts" } +zksync_core_leftovers = { version = "=26.1.0-non-semver-compat", path = "../core/lib/zksync_core_leftovers" } +zksync_protobuf_config = { version = "=26.1.0-non-semver-compat", path = "../core/lib/protobuf_config" } # Prover workspace dependencies -zksync_prover_dal = { path = "crates/lib/prover_dal" } -zksync_prover_fri_types = { path = "crates/lib/prover_fri_types" } -zksync_prover_fri_utils = { path = "crates/lib/prover_fri_utils" } -zksync_prover_keystore = { path = "crates/lib/keystore" } -zksync_vk_setup_data_generator_server_fri = { path = "crates/bin/vk_setup_data_generator_server_fri" } -zksync_prover_job_processor = { path = "crates/lib/prover_job_processor" } -zksync_circuit_prover_service = { path = "crates/lib/circuit_prover_service" } -zksync_prover_job_monitor = { path = "crates/bin/prover_job_monitor" } +zksync_prover_dal = { version = "17.1.1", path = "crates/lib/prover_dal" } +zksync_prover_fri_types = { version = "17.1.1", path = "crates/lib/prover_fri_types" } +zksync_prover_fri_utils = { version = "17.1.1", path = "crates/lib/prover_fri_utils" } +zksync_prover_keystore = { version = "17.1.1", path = "crates/lib/keystore" } +zksync_vk_setup_data_generator_server_fri = { version = "17.1.1", path = "crates/bin/vk_setup_data_generator_server_fri" } +zksync_prover_job_processor = { version = "17.1.1", path = "crates/lib/prover_job_processor" } +zksync_circuit_prover_service = { version = "17.1.1", path = "crates/lib/circuit_prover_service" } +zksync_prover_job_monitor = { version = "17.1.1", path = "crates/bin/prover_job_monitor" } # for `perf` profiling [profile.perf] diff --git a/prover/rust-toolchain b/prover/rust-toolchain index 03c040b91f1f..bc5d1d6bbd8e 100644 --- a/prover/rust-toolchain +++ b/prover/rust-toolchain @@ -1 +1,2 @@ -nightly-2024-08-01 +[toolchain] +channel = "nightly-2024-08-01" diff --git a/zkstack_cli/Cargo.lock b/zkstack_cli/Cargo.lock index b21bb44b589a..bbdb50159076 100644 --- a/zkstack_cli/Cargo.lock +++ b/zkstack_cli/Cargo.lock @@ -7045,7 +7045,7 @@ dependencies = [ [[package]] name = "zkstack" -version = "0.1.0" +version = "0.1.2" dependencies = [ "anyhow", "chrono", @@ -7093,7 +7093,7 @@ dependencies = [ [[package]] name = "zkstack_cli_common" -version = "0.1.0" +version = "0.1.2" dependencies = [ "anyhow", "async-trait", @@ -7122,7 +7122,7 @@ dependencies = [ [[package]] name = "zkstack_cli_config" -version = "0.1.0" +version = "0.1.2" dependencies = [ "anyhow", "clap", @@ -7146,14 +7146,14 @@ dependencies = [ [[package]] name = "zkstack_cli_git_version_macro" -version = "0.1.0" +version = "0.1.2" dependencies = [ "chrono", ] [[package]] name = "zkstack_cli_types" -version = "0.1.0" +version = "0.1.2" dependencies = [ "clap", "ethers", @@ -7165,7 +7165,7 @@ dependencies = [ [[package]] name = "zksync_basic_types" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "chrono", @@ -7205,7 +7205,7 @@ dependencies = [ [[package]] name = "zksync_config" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "rand", @@ -7274,7 +7274,7 @@ dependencies = [ [[package]] name = "zksync_contracts" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "envy", "hex", @@ -7287,7 +7287,7 @@ dependencies = [ [[package]] name = "zksync_crypto_primitives" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "blake2", @@ -7303,7 +7303,7 @@ dependencies = [ [[package]] name = "zksync_eth_client" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "async-trait", "jsonrpsee", @@ -7320,7 +7320,7 @@ dependencies = [ [[package]] name = "zksync_eth_signer" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "async-trait", "rlp", @@ -7331,7 +7331,7 @@ dependencies = [ [[package]] name = "zksync_mini_merkle_tree" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "once_cell", "zksync_basic_types", @@ -7378,7 +7378,7 @@ dependencies = [ [[package]] name = "zksync_protobuf_config" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "hex", @@ -7398,7 +7398,7 @@ dependencies = [ [[package]] name = "zksync_system_constants" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "once_cell", "zksync_basic_types", @@ -7406,7 +7406,7 @@ dependencies = [ [[package]] name = "zksync_types" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", @@ -7438,7 +7438,7 @@ dependencies = [ [[package]] name = "zksync_utils" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "futures", @@ -7453,7 +7453,7 @@ dependencies = [ [[package]] name = "zksync_vlog" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "chrono", @@ -7478,7 +7478,7 @@ dependencies = [ [[package]] name = "zksync_web3_decl" -version = "0.1.0" +version = "26.1.0-non-semver-compat" dependencies = [ "anyhow", "async-trait", diff --git a/zkstack_cli/Cargo.toml b/zkstack_cli/Cargo.toml index 79a87b4e1ae2..0d726ccc3600 100644 --- a/zkstack_cli/Cargo.toml +++ b/zkstack_cli/Cargo.toml @@ -9,7 +9,7 @@ members = [ resolver = "2" [workspace.package] -version = "0.1.0" +version = "0.1.2" # x-release-please-version edition = "2021" homepage = "https://zksync.io/" license = "MIT OR Apache-2.0" @@ -21,20 +21,20 @@ keywords = ["zk", "cryptography", "blockchain", "ZKStack", "ZKsync"] [workspace.dependencies] # Local dependencies -zkstack_cli_common = { path = "crates/common" } -zkstack_cli_config = { path = "crates/config" } -zkstack_cli_types = { path = "crates/types" } -zkstack_cli_git_version_macro = { path = "crates/git_version_macro" } +zkstack_cli_common = { version = "0.1.2", path = "crates/common" } +zkstack_cli_config = { version = "0.1.2", path = "crates/config" } +zkstack_cli_types = { version = "0.1.2", path = "crates/types" } +zkstack_cli_git_version_macro = { version = "0.1.2", path = "crates/git_version_macro" } # ZkSync deps -zksync_config = { path = "../core/lib/config" } -zksync_protobuf_config = { path = "../core/lib/protobuf_config" } -zksync_basic_types = { path = "../core/lib/basic_types" } -zksync_system_constants = { path = "../core/lib/constants" } -zksync_types = { path = "../core/lib/types" } -zksync_web3_decl = { path = "../core/lib/web3_decl" } -zksync_eth_client = { path = "../core/lib/eth_client" } -zksync_contracts = { path = "../core/lib/contracts" } +zksync_config = { version = "=26.1.0-non-semver-compat", path = "../core/lib/config" } +zksync_protobuf_config = { version = "=26.1.0-non-semver-compat", path = "../core/lib/protobuf_config" } +zksync_basic_types = { version = "=26.1.0-non-semver-compat", path = "../core/lib/basic_types" } +zksync_system_constants = { version = "=26.1.0-non-semver-compat", path = "../core/lib/constants" } +zksync_types = { version = "=26.1.0-non-semver-compat", path = "../core/lib/types" } +zksync_web3_decl = { version = "=26.1.0-non-semver-compat", path = "../core/lib/web3_decl" } +zksync_eth_client = { version = "=26.1.0-non-semver-compat", path = "../core/lib/eth_client" } +zksync_contracts = { version = "=26.1.0-non-semver-compat", path = "../core/lib/contracts" } zksync_consensus_roles = "=0.8.0" zksync_consensus_crypto = "=0.8.0" zksync_consensus_utils = "=0.8.0" From 516e5210ed70b25a15a68a58c8065331aab542e0 Mon Sep 17 00:00:00 2001 From: Artem Fomiuk <88630083+Artemka374@users.noreply.github.com> Date: Wed, 22 Jan 2025 18:05:11 +0200 Subject: [PATCH 77/97] fix: JSON proof serialization (#3514) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Add `#[serde(untagged)]` for L1BatchProofForL1 to ensure backwards compatibility. ## Why ❔ ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- core/lib/prover_interface/src/outputs.rs | 1 + core/lib/prover_interface/tests/job_serialization.rs | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/lib/prover_interface/src/outputs.rs b/core/lib/prover_interface/src/outputs.rs index b536c39778a5..1c3a613628d3 100644 --- a/core/lib/prover_interface/src/outputs.rs +++ b/core/lib/prover_interface/src/outputs.rs @@ -13,6 +13,7 @@ use zksync_types::{protocol_version::ProtocolSemanticVersion, tee_types::TeeType /// A "final" ZK proof that can be sent to the L1 contract. #[derive(Clone, Serialize, Deserialize)] +#[serde(untagged)] #[allow(clippy::large_enum_variant)] pub enum L1BatchProofForL1 { Fflonk(FflonkL1BatchProofForL1), diff --git a/core/lib/prover_interface/tests/job_serialization.rs b/core/lib/prover_interface/tests/job_serialization.rs index cc78a9acf262..f28a8d94bf0c 100644 --- a/core/lib/prover_interface/tests/job_serialization.rs +++ b/core/lib/prover_interface/tests/job_serialization.rs @@ -102,8 +102,7 @@ fn test_proof_request_serialization() { let encoded_obj = serde_json::to_string(&proof).unwrap(); let encoded_json = r#"{ "Proof": { - "Plonk": { - "aggregation_result_coords": [ + "aggregation_result_coords": [ [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], @@ -159,7 +158,6 @@ fn test_proof_request_serialization() { }, "protocol_version": "0.25.10" } - } }"#; let decoded_obj: SubmitProofRequest = serde_json::from_str(&encoded_obj).unwrap(); let decoded_json: SubmitProofRequest = serde_json::from_str(encoded_json).unwrap(); From de8ad1a1ca738fec2e7386b69493f96c85115ba8 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Wed, 22 Jan 2025 14:17:30 -0300 Subject: [PATCH 78/97] remove unwraps --- core/node/da_clients/src/eigen/verifier/mod.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/core/node/da_clients/src/eigen/verifier/mod.rs b/core/node/da_clients/src/eigen/verifier/mod.rs index 9bfadd69ffba..8f96bd2c4c9f 100644 --- a/core/node/da_clients/src/eigen/verifier/mod.rs +++ b/core/node/da_clients/src/eigen/verifier/mod.rs @@ -186,7 +186,7 @@ enum PointFile { impl PointFile { fn path(&self) -> &str { match self { - PointFile::Temp(file) => file.path().to_str().unwrap(), + PointFile::Temp(file) => file.path().to_str().unwrap_or_default(), // Safe unwrap because NamedTempFile guarantees a valid path PointFile::Path(path) => path, } } @@ -212,23 +212,21 @@ impl Verifier { async fn download_temp_point(url: &String) -> Result { let response = reqwest::get(url) .await - .map_err(|e| VerificationError::LinkError(e.to_string())) - .unwrap(); + .map_err(|e| VerificationError::LinkError(e.to_string()))?; if !response.status().is_success() { return Err(VerificationError::LinkError( "Failed to get point".to_string(), )); } - let mut file = NamedTempFile::new().unwrap(); + let mut file = + NamedTempFile::new().map_err(|e| VerificationError::LinkError(e.to_string()))?; let content = response .bytes() .await - .map_err(|e| VerificationError::LinkError(e.to_string())) - .unwrap(); + .map_err(|e| VerificationError::LinkError(e.to_string()))?; file.write_all(&content) - .map_err(|e| VerificationError::LinkError(e.to_string())) - .unwrap(); + .map_err(|e| VerificationError::LinkError(e.to_string()))?; Ok(file) } From c476920e47068bdd33ee17f007d83f04f2053712 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Wed, 22 Jan 2025 15:29:12 -0300 Subject: [PATCH 79/97] address comments/suggestions --- .../lib/config/src/configs/da_client/eigen.rs | 2 +- .../node/da_clients/src/eigen/verifier/mod.rs | 46 +++++++++++-------- .../da_clients/src/eigen/verifier/tests.rs | 8 ++-- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/core/lib/config/src/configs/da_client/eigen.rs b/core/lib/config/src/configs/da_client/eigen.rs index 2283700ec3d3..3525eef22ea2 100644 --- a/core/lib/config/src/configs/da_client/eigen.rs +++ b/core/lib/config/src/configs/da_client/eigen.rs @@ -38,7 +38,7 @@ impl Default for EigenConfig { Self { disperser_rpc: "https://disperser-holesky.eigenda.xyz:443".to_string(), settlement_layer_confirmation_depth: 0, - eigenda_eth_rpc: Some(SensitiveUrl::from_str("https://ethereum-holesky-rpc.publicnode.com").unwrap()), // Save to unwrap, never fails + eigenda_eth_rpc: Some(SensitiveUrl::from_str("https://ethereum-holesky-rpc.publicnode.com").unwrap()), // Safe to unwrap, never fails eigenda_svc_manager_address: DEFAULT_EIGENDA_SVC_MANAGER_ADDRESS, wait_for_finalization: false, authenticated: false, diff --git a/core/node/da_clients/src/eigen/verifier/mod.rs b/core/node/da_clients/src/eigen/verifier/mod.rs index 8f96bd2c4c9f..152665e08920 100644 --- a/core/node/da_clients/src/eigen/verifier/mod.rs +++ b/core/node/da_clients/src/eigen/verifier/mod.rs @@ -32,19 +32,25 @@ fn decode_bytes(encoded: Vec) -> Result, VerificationError> { #[async_trait::async_trait] pub trait VerifierClient: Sync + Send + std::fmt::Debug { + /// Request to the EigenDA service manager contract + /// the batch metadata hash for a given batch id async fn batch_id_to_batch_metadata_hash( &self, - blob_info: &BlobInfo, + batch_id: u32, svc_manager_addr: Address, ) -> Result, VerificationError>; + /// Request to the EigenDA service manager contract + /// the quorum adversary threshold percentages for a given quorum number async fn quorum_adversary_threshold_percentages( &self, quorum_number: u32, svc_manager_addr: Address, ) -> Result; - async fn quorum_numbers_required( + /// Request to the EigenDA service manager contract + /// the set of quorum numbers that are required + async fn required_quorum_numbers( &self, svc_manager_addr: Address, ) -> Result, VerificationError>; @@ -54,16 +60,14 @@ pub trait VerifierClient: Sync + Send + std::fmt::Debug { impl VerifierClient for Box> { async fn batch_id_to_batch_metadata_hash( &self, - blob_info: &BlobInfo, + batch_id: u32, svc_manager_addr: Address, ) -> Result, VerificationError> { let mut data = vec![]; let func_selector = ethabi::short_signature("batchIdToBatchMetadataHash", &[ParamType::Uint(32)]); data.extend_from_slice(&func_selector); - let batch_id_data = encode(&[Token::Uint(U256::from( - blob_info.blob_verification_proof.batch_id, - ))]); + let batch_id_data = encode(&[Token::Uint(U256::from(batch_id))]); data.extend_from_slice(&batch_id_data); let call_request = CallRequest { @@ -109,7 +113,7 @@ impl VerifierClient for Box> { Ok(0) } - async fn quorum_numbers_required( + async fn required_quorum_numbers( &self, svc_manager_addr: Address, ) -> Result, VerificationError> { @@ -174,7 +178,7 @@ pub enum VerificationError { #[error("Commitment not on correct subgroup: {0}")] CommitmentNotOnCorrectSubgroup(G1Affine), #[error("Link Error: {0}")] - LinkError(String), + PointDownloadError(String), } #[derive(Debug)] @@ -212,21 +216,22 @@ impl Verifier { async fn download_temp_point(url: &String) -> Result { let response = reqwest::get(url) .await - .map_err(|e| VerificationError::LinkError(e.to_string()))?; + .map_err(|e| VerificationError::PointDownloadError(e.to_string()))?; if !response.status().is_success() { - return Err(VerificationError::LinkError( - "Failed to get point".to_string(), - )); + return Err(VerificationError::PointDownloadError(format!( + "Failed to download point from source {}", + url + ))); } - let mut file = - NamedTempFile::new().map_err(|e| VerificationError::LinkError(e.to_string()))?; + let mut file = NamedTempFile::new() + .map_err(|e| VerificationError::PointDownloadError(e.to_string()))?; let content = response .bytes() .await - .map_err(|e| VerificationError::LinkError(e.to_string()))?; + .map_err(|e| VerificationError::PointDownloadError(e.to_string()))?; file.write_all(&content) - .map_err(|e| VerificationError::LinkError(e.to_string()))?; + .map_err(|e| VerificationError::PointDownloadError(e.to_string()))?; Ok(file) } @@ -237,7 +242,7 @@ impl Verifier { PointFile::Path(format!("{}/{}", path, Self::G2POINT)), )), None => { - tracing::info!("Points for KZG setup not found, downloading temporary points"); + tracing::info!("Points for KZG setup not found, downloading points to a temp file"); Ok(( PointFile::Temp(Self::download_temp_point(&cfg.g1_url).await?), PointFile::Temp(Self::download_temp_point(&cfg.g2_url).await?), @@ -418,7 +423,10 @@ impl Verifier { ) -> Result, VerificationError> { self.client .as_ref() - .batch_id_to_batch_metadata_hash(blob_info, self.cfg.eigenda_svc_manager_address) + .batch_id_to_batch_metadata_hash( + blob_info.blob_verification_proof.batch_id, + self.cfg.eigenda_svc_manager_address, + ) .await } @@ -470,7 +478,7 @@ impl Verifier { async fn call_quorum_numbers_required(&self) -> Result, VerificationError> { self.client .as_ref() - .quorum_numbers_required(self.cfg.eigenda_svc_manager_address) + .required_quorum_numbers(self.cfg.eigenda_svc_manager_address) .await } diff --git a/core/node/da_clients/src/eigen/verifier/tests.rs b/core/node/da_clients/src/eigen/verifier/tests.rs index d26c57b653e1..2e874ad7b30c 100644 --- a/core/node/da_clients/src/eigen/verifier/tests.rs +++ b/core/node/da_clients/src/eigen/verifier/tests.rs @@ -38,16 +38,14 @@ impl MockVerifierClient { impl VerifierClient for MockVerifierClient { async fn batch_id_to_batch_metadata_hash( &self, - blob_info: &BlobInfo, + batch_id: u32, svc_manager_addr: Address, ) -> Result, VerificationError> { let mut data = vec![]; let func_selector = ethabi::short_signature("batchIdToBatchMetadataHash", &[ParamType::Uint(32)]); data.extend_from_slice(&func_selector); - let batch_id_data = ethabi::encode(&[Token::Uint(U256::from( - blob_info.blob_verification_proof.batch_id, - ))]); + let batch_id_data = ethabi::encode(&[Token::Uint(U256::from(batch_id))]); data.extend_from_slice(&batch_id_data); let call_request = CallRequest { @@ -84,7 +82,7 @@ impl VerifierClient for MockVerifierClient { Ok(0) } - async fn quorum_numbers_required( + async fn required_quorum_numbers( &self, svc_manager_addr: Address, ) -> Result, VerificationError> { From 4fd5944e9ea34f5662947429e9280eeeba60faf8 Mon Sep 17 00:00:00 2001 From: perekopskiy <53865202+perekopskiy@users.noreply.github.com> Date: Wed, 22 Jan 2025 20:29:38 +0200 Subject: [PATCH 80/97] ci: explicit nonces for upgrade test (#3517) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Explicit nonces for upgrade test ## Why ❔ Test is flaky, sometimes incorrect nonce is used ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- core/tests/upgrade-test/tests/upgrade.test.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/core/tests/upgrade-test/tests/upgrade.test.ts b/core/tests/upgrade-test/tests/upgrade.test.ts index 5a1902ec8671..b593e5ad6677 100644 --- a/core/tests/upgrade-test/tests/upgrade.test.ts +++ b/core/tests/upgrade-test/tests/upgrade.test.ts @@ -268,9 +268,10 @@ describe('Upgrade test', function () { bootloaderHash = ethers.hexlify(zksync.utils.hashBytecode(bootloaderCode)); defaultAccountHash = ethers.hexlify(zksync.utils.hashBytecode(defaultAACode)); - await publishBytecode(tester.ethWallet, bytecodeSupplier, bootloaderCode); - await publishBytecode(tester.ethWallet, bytecodeSupplier, defaultAACode); - await publishBytecode(tester.ethWallet, bytecodeSupplier, forceDeployBytecode); + let nonce = await tester.ethWallet.getNonce(); + nonce += await publishBytecode(tester.ethWallet, bytecodeSupplier, bootloaderCode, nonce); + nonce += await publishBytecode(tester.ethWallet, bytecodeSupplier, defaultAACode, nonce); + await publishBytecode(tester.ethWallet, bytecodeSupplier, forceDeployBytecode, nonce); }); step('Schedule governance call', async () => { @@ -523,7 +524,12 @@ function readCode(newPath: string, legacyPath: string): string { } } -async function publishBytecode(wallet: ethers.Wallet, bytecodeSupplierAddr: string, bytecode: string) { +async function publishBytecode( + wallet: ethers.Wallet, + bytecodeSupplierAddr: string, + bytecode: string, + nonce: number +): Promise { const hash = zksync.utils.hashBytecode(bytecode); const abi = [ 'function publishBytecode(bytes calldata _bytecode) public', @@ -533,8 +539,11 @@ async function publishBytecode(wallet: ethers.Wallet, bytecodeSupplierAddr: stri const contract = new ethers.Contract(bytecodeSupplierAddr, abi, wallet); const block = await contract.publishingBlock(hash); if (block == BigInt(0)) { - await (await contract.publishBytecode(bytecode)).wait(); + const tx = await contract.publishBytecode(bytecode, { nonce }); + await tx.wait(); + return 1; } + return 0; } async function checkedRandomTransfer(sender: zksync.Wallet, amount: bigint): Promise { From d64acd2a1224f6182cd50d29e644255e0eeec142 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Wed, 22 Jan 2025 17:35:55 -0300 Subject: [PATCH 81/97] improve Error handling --- core/node/da_clients/src/eigen/errors.rs | 111 ++++++++++++++++++ core/node/da_clients/src/eigen/mod.rs | 1 + core/node/da_clients/src/eigen/sdk.rs | 36 +++--- .../node/da_clients/src/eigen/verifier/mod.rs | 52 +------- 4 files changed, 136 insertions(+), 64 deletions(-) create mode 100644 core/node/da_clients/src/eigen/errors.rs diff --git a/core/node/da_clients/src/eigen/errors.rs b/core/node/da_clients/src/eigen/errors.rs new file mode 100644 index 000000000000..0b661da60c68 --- /dev/null +++ b/core/node/da_clients/src/eigen/errors.rs @@ -0,0 +1,111 @@ +use ark_bn254::G1Affine; +use tonic::{transport::Error as TonicError, Status}; +use zksync_eth_client::EnrichedClientError; + +use super::blob_info::BlobQuorumParam; + +/// Errors returned by this crate +#[derive(Debug, thiserror::Error)] +pub enum EigenClientError { + #[error(transparent)] + EthClient(#[from] EthClientError), + #[error(transparent)] + Verification(#[from] VerificationError), + #[error(transparent)] + Communication(#[from] CommunicationError), + #[error(transparent)] + BlobStatus(#[from] BlobStatusError), + #[error(transparent)] + Conversion(#[from] ConversionError), + #[error(transparent)] + Config(#[from] ConfigError), +} + +#[derive(Debug, thiserror::Error)] +pub enum ConfigError { + #[error(transparent)] + Secp(#[from] secp256k1::Error), + #[error(transparent)] + Tonic(#[from] TonicError), +} + +#[derive(Debug, thiserror::Error)] +pub enum CommunicationError { + #[error(transparent)] + Secp(#[from] secp256k1::Error), + #[error(transparent)] + Hex(#[from] hex::FromHexError), + #[error(transparent)] + GetBlobData(#[from] Box), +} + +#[derive(Debug, thiserror::Error)] +pub enum BlobStatusError { + #[error(transparent)] + Prost(#[from] prost::DecodeError), + #[error(transparent)] + Status(#[from] Status), +} + +/// Errors specific to conversion +#[derive(Debug, thiserror::Error)] +pub enum ConversionError {} + +/// Errors for the EthClient +#[derive(Debug, thiserror::Error)] +pub enum EthClientError { + #[error(transparent)] + HTTPClient(#[from] reqwest::Error), + #[error(transparent)] + SerdeJSON(#[from] serde_json::Error), + #[error("RPC: {0}")] + Rpc(String), +} + +#[derive(Debug, thiserror::Error)] +pub enum KzgError { + #[error("Kzg setup error: {0}")] + Setup(String), + #[error(transparent)] + Internal(#[from] rust_kzg_bn254::errors::KzgError), +} + +#[derive(Debug, thiserror::Error)] +pub enum ServiceManagerError { + #[error(transparent)] + EnrichedClient(#[from] EnrichedClientError), + #[error("Decoding error: {0}")] + Decoding(String), +} + +/// Errors for the Verifier +#[derive(Debug, thiserror::Error)] +pub enum VerificationError { + #[error(transparent)] + ServiceManager(#[from] ServiceManagerError), + #[error(transparent)] + Kzg(#[from] KzgError), + #[error("Wrong proof")] + WrongProof, + #[error("Different commitments: expected {expected:?}, got {actual:?}")] + DifferentCommitments { + expected: Box, + actual: Box, + }, + #[error("Different roots: expected {expected:?}, got {actual:?}")] + DifferentRoots { expected: String, actual: String }, + #[error("Empty hashes")] + EmptyHash, + #[error("Different hashes: expected {expected:?}, got {actual:?}")] + DifferentHashes { expected: String, actual: String }, + #[error("Wrong quorum params: {blob_quorum_params:?}")] + WrongQuorumParams { blob_quorum_params: BlobQuorumParam }, + #[error("Quorum not confirmed")] + QuorumNotConfirmed, + #[error("Commitment not on curve: {0}")] + CommitmentNotOnCurve(G1Affine), + #[error("Commitment not on correct subgroup: {0}")] + CommitmentNotOnCorrectSubgroup(G1Affine), + #[error("Link Error: {0}")] + PointDownloadError(String), +} diff --git a/core/node/da_clients/src/eigen/mod.rs b/core/node/da_clients/src/eigen/mod.rs index 98cefc67b799..ff1d732604b6 100644 --- a/core/node/da_clients/src/eigen/mod.rs +++ b/core/node/da_clients/src/eigen/mod.rs @@ -1,5 +1,6 @@ mod blob_info; mod client; +mod errors; mod sdk; mod verifier; diff --git a/core/node/da_clients/src/eigen/sdk.rs b/core/node/da_clients/src/eigen/sdk.rs index e0fc13c288e2..bc6cd12027de 100644 --- a/core/node/da_clients/src/eigen/sdk.rs +++ b/core/node/da_clients/src/eigen/sdk.rs @@ -12,7 +12,11 @@ use zksync_config::EigenConfig; use zksync_web3_decl::client::{Client, DynClient, L1}; use super::{ - blob_info::BlobInfo, disperser::BlobInfo as DisperserBlobInfo, verifier::Verifier, GetBlobData, + blob_info::BlobInfo, + disperser::BlobInfo as DisperserBlobInfo, + errors::{ConfigError, EigenClientError, EthClientError, VerificationError}, + verifier::Verifier, + GetBlobData, }; use crate::eigen::{ blob_info, @@ -22,7 +26,6 @@ use crate::eigen::{ disperser_client::DisperserClient, AuthenticatedReply, BlobAuthHeader, }, - verifier::VerificationError, }; #[derive(Debug)] @@ -43,21 +46,24 @@ impl RawEigenClient { private_key: SecretKey, cfg: EigenConfig, get_blob_data: Arc, - ) -> anyhow::Result { - let endpoint = - Endpoint::from_str(cfg.disperser_rpc.as_str())?.tls_config(ClientTlsConfig::new())?; - let client = DisperserClient::connect(endpoint).await?; - - let rpc_url = cfg - .eigenda_eth_rpc - .clone() - .ok_or(anyhow::anyhow!("EigenDA ETH RPC not set"))?; - let query_client: Client = Client::http(rpc_url)?.build(); + ) -> Result { + let endpoint = Endpoint::from_str(cfg.disperser_rpc.as_str()) + .map_err(ConfigError::Tonic)? + .tls_config(ClientTlsConfig::new()) + .map_err(ConfigError::Tonic)?; + let client = DisperserClient::connect(endpoint) + .await + .map_err(ConfigError::Tonic)?; + + let rpc_url = cfg.eigenda_eth_rpc.clone().unwrap(); + // TODO: remove unwrap + // .ok_or(anyhow::anyhow!("EigenDA ETH RPC not set"))?; + let query_client: Client = Client::http(rpc_url) + .map_err(|e| EthClientError::Rpc(e.to_string()))? + .build(); let query_client = Box::new(query_client) as Box>; - let verifier = Verifier::new(cfg.clone(), Arc::new(query_client)) - .await - .context("Failed to create verifier")?; + let verifier = Verifier::new(cfg.clone(), Arc::new(query_client)).await?; Ok(RawEigenClient { client, private_key, diff --git a/core/node/da_clients/src/eigen/verifier/mod.rs b/core/node/da_clients/src/eigen/verifier/mod.rs index 152665e08920..334a4713419e 100644 --- a/core/node/da_clients/src/eigen/verifier/mod.rs +++ b/core/node/da_clients/src/eigen/verifier/mod.rs @@ -6,12 +6,13 @@ use rust_kzg_bn254::{blob::Blob, kzg::Kzg, polynomial::PolynomialFormat}; use tempfile::NamedTempFile; use zksync_basic_types::web3::CallRequest; use zksync_config::EigenConfig; -use zksync_eth_client::{EnrichedClientError, EthInterface}; +use zksync_eth_client::EthInterface; use zksync_types::{web3, Address, U256}; use zksync_web3_decl::client::{DynClient, L1}; use super::{ - blob_info::{BatchHeader, BlobHeader, BlobInfo, BlobQuorumParam, G1Commitment}, + blob_info::{BatchHeader, BlobHeader, BlobInfo, G1Commitment}, + errors::{KzgError, ServiceManagerError, VerificationError}, sdk::RawEigenClient, }; @@ -134,53 +135,6 @@ impl VerifierClient for Box> { } } -#[derive(Debug, thiserror::Error)] -pub enum KzgError { - #[error("Kzg setup error: {0}")] - Setup(String), - #[error(transparent)] - Internal(#[from] rust_kzg_bn254::errors::KzgError), -} - -#[derive(Debug, thiserror::Error)] -pub enum ServiceManagerError { - #[error(transparent)] - EnrichedClient(#[from] EnrichedClientError), - #[error("Decoding error: {0}")] - Decoding(String), -} - -#[derive(Debug, thiserror::Error)] -pub enum VerificationError { - #[error(transparent)] - ServiceManager(#[from] ServiceManagerError), - #[error(transparent)] - Kzg(#[from] KzgError), - #[error("Wrong proof")] - WrongProof, - #[error("Different commitments: expected {expected:?}, got {actual:?}")] - DifferentCommitments { - expected: Box, - actual: Box, - }, - #[error("Different roots: expected {expected:?}, got {actual:?}")] - DifferentRoots { expected: String, actual: String }, - #[error("Empty hash")] - EmptyHash, - #[error("Different hashes: expected {expected:?}, got {actual:?}")] - DifferentHashes { expected: String, actual: String }, - #[error("Wrong quorum params: {blob_quorum_params:?}")] - WrongQuorumParams { blob_quorum_params: BlobQuorumParam }, - #[error("Quorum not confirmed")] - QuorumNotConfirmed, - #[error("Commitment not on curve: {0}")] - CommitmentNotOnCurve(G1Affine), - #[error("Commitment not on correct subgroup: {0}")] - CommitmentNotOnCorrectSubgroup(G1Affine), - #[error("Link Error: {0}")] - PointDownloadError(String), -} - #[derive(Debug)] enum PointFile { Temp(NamedTempFile), From 01a3d7a734a2352072dd6d902e1f49b3ab31addc Mon Sep 17 00:00:00 2001 From: Daniyar Itegulov Date: Thu, 23 Jan 2025 19:45:11 +1100 Subject: [PATCH 82/97] ci: fix nonce reuse (#3520) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ In a very unlikely scenario timeouted tx can still succeed in the short window while we are resubmitting it with updated gas. This PR handles this gracefully + another case when nonce might have been used asynchronously by someone else. ## Why ❔ Good ol' flakiness ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- core/tests/ts-integration/src/helpers.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/core/tests/ts-integration/src/helpers.ts b/core/tests/ts-integration/src/helpers.ts index 758884e2be99..58b698681388 100644 --- a/core/tests/ts-integration/src/helpers.ts +++ b/core/tests/ts-integration/src/helpers.ts @@ -104,6 +104,22 @@ export async function waitForNewL1Batch(wallet: zksync.Wallet): Promisee.message.match(/nonce too low/)) { + if (!txResponse) { + // Our transaction was never accepted to the mempool with this nonce so it must have been used by another transaction. + return wallet.getNonce().then((newNonce) => { + console.log( + `Transaction's nonce is too low, updating from ${nonce} to ${newNonce} (attempt ${i + 1}/${MAX_ATTEMPTS})` + ); + nonce = newNonce; + return null; + }); + } else { + console.log( + `Transaction's nonce is too low, likely previous attempt succeeded, waiting longer (attempt ${i + 1}/${MAX_ATTEMPTS})` + ); + return txResponse; + } } else { return Promise.reject(e); } From c916797d49d636c9e642264786d4124ebd338ec3 Mon Sep 17 00:00:00 2001 From: Alex Ostrovski Date: Thu, 23 Jan 2025 11:00:26 +0200 Subject: [PATCH 83/97] fix(en): Fix race condition in EN storage initialization (#3515) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Reworks `NodeStorageInitializer::is_chain_tip_correct()` so that it performs the minimum amount of work possible, i.e. detects whether the latest L1 batch / L2 block diverge or not. ## Why ❔ EN storage initialization is prone to a data race: the "is storage initialized" check calls `NodeStorageInitializer::is_chain_tip_correct()`, which internally performs the entire iteration of the reorg detector (in particular, binary search for the first diverged block). This can lead to a data race with block revert logic, which may be executed concurrently. This data race was observed on the revert integration tests. ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- .../src/external_node/revert.rs | 6 ++ core/node/node_storage_init/src/lib.rs | 5 +- core/node/node_storage_init/src/traits.rs | 3 + core/node/reorg_detector/src/lib.rs | 86 ++++++++++++++----- core/node/reorg_detector/src/tests.rs | 10 +++ 5 files changed, 86 insertions(+), 24 deletions(-) diff --git a/core/node/node_storage_init/src/external_node/revert.rs b/core/node/node_storage_init/src/external_node/revert.rs index 86d137c6b660..db06a4492bb7 100644 --- a/core/node/node_storage_init/src/external_node/revert.rs +++ b/core/node/node_storage_init/src/external_node/revert.rs @@ -34,6 +34,12 @@ impl RevertStorage for ExternalNodeReverter { Ok(()) } + async fn is_reorg_needed(&self, stop_receiver: watch::Receiver) -> anyhow::Result { + ReorgDetector::new(self.client.clone(), self.pool.clone()) + .check_reorg_presence(stop_receiver) + .await + } + async fn last_correct_batch_for_reorg( &self, stop_receiver: watch::Receiver, diff --git a/core/node/node_storage_init/src/lib.rs b/core/node/node_storage_init/src/lib.rs index 10b0131908ca..a8b72b769a18 100644 --- a/core/node/node_storage_init/src/lib.rs +++ b/core/node/node_storage_init/src/lib.rs @@ -182,10 +182,7 @@ impl NodeStorageInitializer { ) -> anyhow::Result { // May be `true` if stop signal is received, but the node will shut down without launching any tasks anyway. let initialized = if let Some(reverter) = &self.strategy.block_reverter { - reverter - .last_correct_batch_for_reorg(stop_receiver) - .await? - .is_none() + !reverter.is_reorg_needed(stop_receiver).await? } else { true }; diff --git a/core/node/node_storage_init/src/traits.rs b/core/node/node_storage_init/src/traits.rs index 3b6467764d97..d28b0226d845 100644 --- a/core/node/node_storage_init/src/traits.rs +++ b/core/node/node_storage_init/src/traits.rs @@ -18,6 +18,9 @@ pub trait InitializeStorage: fmt::Debug + Send + Sync + 'static { /// This trait assumes that for any invalid state there exists a batch number to which the storage can be rolled back. #[async_trait::async_trait] pub trait RevertStorage: fmt::Debug + Send + Sync + 'static { + /// Checks whether a reorg is needed for the storage. + async fn is_reorg_needed(&self, stop_receiver: watch::Receiver) -> anyhow::Result; + /// Checks if the storage is invalid state and has to be rolled back. async fn last_correct_batch_for_reorg( &self, diff --git a/core/node/reorg_detector/src/lib.rs b/core/node/reorg_detector/src/lib.rs index d1954ca4b74b..ec5b505d7803 100644 --- a/core/node/reorg_detector/src/lib.rs +++ b/core/node/reorg_detector/src/lib.rs @@ -266,26 +266,32 @@ impl ReorgDetector { &self.health_check } - async fn check_consistency(&mut self) -> Result<(), Error> { + async fn find_last_diverged_batch(&mut self) -> Result, HashMatchError> { let mut storage = self.pool.connection().await?; - let Some(local_l1_batch) = storage + // Create a readonly transaction to get a consistent view of the storage. + let mut storage_tx = storage + .transaction_builder()? + .set_readonly() + .build() + .await?; + let Some(local_l1_batch) = storage_tx .blocks_dal() .get_last_l1_batch_number_with_tree_data() .await? else { - return Ok(()); + return Ok(None); }; - let Some(local_l2_block) = storage.blocks_dal().get_sealed_l2_block_number().await? else { - return Ok(()); + let Some(local_l2_block) = storage_tx.blocks_dal().get_sealed_l2_block_number().await? + else { + return Ok(None); }; + drop(storage_tx); drop(storage); let remote_l1_batch = self.client.sealed_l1_batch_number().await?; let remote_l2_block = self.client.sealed_l2_block_number().await?; - let checked_l1_batch = local_l1_batch.min(remote_l1_batch); let checked_l2_block = local_l2_block.min(remote_l2_block); - let root_hashes_match = self.root_hashes_match(checked_l1_batch).await?; let l2_block_hashes_match = self.l2_block_hashes_match(checked_l2_block).await?; @@ -295,13 +301,21 @@ impl ReorgDetector { // In other cases either there is only a height mismatch which means that one of // the nodes needs to do catching up; however, it is not certain that there is actually // a re-org taking place. - if root_hashes_match && l2_block_hashes_match { + Ok(if root_hashes_match && l2_block_hashes_match { self.event_handler .update_correct_block(checked_l2_block, checked_l1_batch); + None + } else { + let diverged_l1_batch = checked_l1_batch + (root_hashes_match as u32); + self.event_handler.report_divergence(diverged_l1_batch); + Some(diverged_l1_batch) + }) + } + + async fn check_consistency(&mut self) -> Result<(), Error> { + let Some(diverged_l1_batch) = self.find_last_diverged_batch().await? else { return Ok(()); - } - let diverged_l1_batch = checked_l1_batch + (root_hashes_match as u32); - self.event_handler.report_divergence(diverged_l1_batch); + }; // Check that the first L1 batch matches, to make sure that // we are actually tracking the same chain as the main node. @@ -455,15 +469,7 @@ impl ReorgDetector { ) -> Result<(), Error> { while !*stop_receiver.borrow_and_update() { let sleep_interval = match self.check_consistency().await { - Err(Error::HashMatch(HashMatchError::MissingData(MissingData::RootHash))) => { - tracing::debug!("Last L1 batch on the main node doesn't have a state root hash; waiting until it is computed"); - self.sleep_interval / 10 - } - Err(err) if err.is_retriable() => { - tracing::warn!("Following transient error occurred: {err}"); - tracing::info!("Trying again after a delay"); - self.sleep_interval - } + Err(Error::HashMatch(err)) => self.handle_hash_err(err)?, Err(err) => return Err(err), Ok(()) if stop_after_success => return Ok(()), Ok(()) => self.sleep_interval, @@ -480,6 +486,46 @@ impl ReorgDetector { } Ok(()) } + + /// Returns the sleep interval if the error is transient. + fn handle_hash_err(&self, err: HashMatchError) -> Result { + match err { + HashMatchError::MissingData(MissingData::RootHash) => { + tracing::debug!("Last L1 batch on the main node doesn't have a state root hash; waiting until it is computed"); + Ok(self.sleep_interval / 10) + } + err if err.is_retriable() => { + tracing::warn!("Following transient error occurred: {err}"); + tracing::info!("Trying again after a delay"); + Ok(self.sleep_interval) + } + err => Err(err), + } + } + + /// Checks whether a reorg is present. Unlike [`Self::run_once()`], this method doesn't pinpoint the first diverged L1 batch; + /// it just checks whether diverged batches / blocks exist in general. + /// + /// Internally retries transient errors. Returns `Ok(false)` if a stop signal is received. + pub async fn check_reorg_presence( + &mut self, + mut stop_receiver: watch::Receiver, + ) -> anyhow::Result { + while !*stop_receiver.borrow_and_update() { + let sleep_interval = match self.find_last_diverged_batch().await { + Err(err) => self.handle_hash_err(err)?, + Ok(maybe_diverged_batch) => return Ok(maybe_diverged_batch.is_some()), + }; + + if tokio::time::timeout(sleep_interval, stop_receiver.changed()) + .await + .is_ok() + { + break; + } + } + Ok(false) + } } /// Fallible and async predicate for binary search. diff --git a/core/node/reorg_detector/src/tests.rs b/core/node/reorg_detector/src/tests.rs index 5465cf8662d6..64e9c224d224 100644 --- a/core/node/reorg_detector/src/tests.rs +++ b/core/node/reorg_detector/src/tests.rs @@ -312,12 +312,19 @@ async fn reorg_is_detected_on_batch_hash_mismatch() { store_l2_block(&mut storage, 2, l2_block_hash).await; detector.check_consistency().await.unwrap(); + let (_stop_sender, stop_receiver) = watch::channel(false); + assert!(!detector + .check_reorg_presence(stop_receiver.clone()) + .await + .unwrap()); + seal_l1_batch(&mut storage, 2, H256::repeat_byte(0xff)).await; // ^ Hash of L1 batch #2 differs from that on the main node. assert_matches!( detector.check_consistency().await, Err(Error::ReorgDetected(L1BatchNumber(1))) ); + assert!(detector.check_reorg_presence(stop_receiver).await.unwrap()); } #[tokio::test] @@ -621,6 +628,9 @@ async fn reorg_is_detected_based_on_l2_block_hashes(last_correct_l1_batch: u32) detector.check_consistency().await, Err(Error::ReorgDetected(L1BatchNumber(num))) if num == last_correct_l1_batch ); + + let (_stop_sender, stop_receiver) = watch::channel(false); + assert!(detector.check_reorg_presence(stop_receiver).await.unwrap()); } #[derive(Debug)] From 4073f81e2e732c02c578cc538c43c988201da61b Mon Sep 17 00:00:00 2001 From: Anton Baliasnikov Date: Thu, 23 Jan 2025 11:17:33 +0000 Subject: [PATCH 84/97] ci: fix typo in param and return to release token (#3522) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ * [x] Fixes the typo in `component` argument * [x] Returns `RELEASE_TOKEN` to run required CI in release PR * [x] Update runner for publishing crates ## Checklist - [x] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [x] Tests for the changes have been added / updated. - [x] Documentation comments have been added / updated. - [x] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --- .github/workflows/publish-crates.yml | 4 ++-- .github/workflows/release-please.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish-crates.yml b/.github/workflows/publish-crates.yml index 3c62fbae4f1a..ebfb96a544c8 100644 --- a/.github/workflows/publish-crates.yml +++ b/.github/workflows/publish-crates.yml @@ -3,7 +3,7 @@ name: Publish crates on: workflow_dispatch: inputs: - compontent: + component: description: 'Component to release. Possible values are: core, prover or zkstack_cli.' required: true default: 'zkstack_cli' @@ -28,7 +28,7 @@ jobs: publish-crates: name: Publish to crates.io - runs-on: ubuntu-latest + runs-on: matterlabs-ci-runner-high-performance steps: - name: Publish crates uses: matter-labs/zksync-ci-common/.github/actions/publish-crates@v1 diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index c1eabbb4c45d..6a3935e9da84 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -22,7 +22,7 @@ jobs: uses: matter-labs/zksync-ci-common/.github/workflows/release-please.yaml@v1 secrets: slack_webhook: ${{ secrets.SLACK_WEBHOOK_RELEASES }} # Slack webhook for notifications - gh_token: ${{ secrets.GITHUB_TOKEN }} # GitHub token for release-please + gh_token: ${{ secrets.RELEASE_TOKEN }} # GitHub token for release-please with: config: '.github/release-please/config.json' # Path to the configuration file manifest: '.github/release-please/manifest.json' # Path to the manifest file From 3e931be6bddaacbd7d029c537db03a3c191fdc21 Mon Sep 17 00:00:00 2001 From: Artem Fomiuk <88630083+Artemka374@users.noreply.github.com> Date: Thu, 23 Jan 2025 17:19:12 +0200 Subject: [PATCH 85/97] feat: Compressor optimizations (#3476) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What ❔ Integrate optimizations for compressor's work. Before: PLONK: ~180s FFLONK: ~248s Now Both: ~70s ## Why ❔ ## Checklist - [ ] PR title corresponds to the body of PR (we generate changelog entries from PRs). - [ ] Tests for the changes have been added / updated. - [ ] Documentation comments have been added / updated. - [ ] Code has been formatted via `zkstack dev fmt` and `zkstack dev lint`. --------- Co-authored-by: afo Co-authored-by: zksync-admin-bot2 <91326834+zksync-admin-bot2@users.noreply.github.com> --- .dockerignore | 1 + .../build-proof-fri-gpu-compressor-gar.yml | 2 +- .gitignore | 2 + core/Cargo.lock | 97 +- core/Cargo.toml | 15 +- core/lib/prover_interface/Cargo.toml | 1 + core/lib/prover_interface/src/outputs.rs | 25 +- .../proof-fri-gpu-compressor-gar/Dockerfile | 7 +- etc/env/base/contracts.toml | 4 +- etc/env/base/fri_proof_compressor.toml | 4 +- etc/env/file_based/general.yaml | 4 +- prover/Cargo.lock | 149 +- prover/Cargo.toml | 21 +- .../proof_fri_compressor/src/compressor.rs | 212 +- .../bin/proof_fri_compressor/src/main.rs | 19 +- .../Cargo.toml | 2 +- .../src/main.rs | 159 +- .../src/utils.rs | 141 - prover/crates/lib/keystore/Cargo.toml | 3 +- prover/crates/lib/keystore/src/compressor.rs | 221 ++ prover/crates/lib/keystore/src/keystore.rs | 98 +- prover/crates/lib/keystore/src/lib.rs | 3 + .../lib/keystore/src/setup_data_generator.rs | 110 +- prover/crates/lib/prover_fri_types/src/lib.rs | 12 - .../keys/finalization_hints_compression_1.bin | Bin 120 -> 373 bytes .../keys/finalization_hints_compression_2.bin | Bin 120 -> 367 bytes .../keys/finalization_hints_compression_3.bin | Bin 120 -> 366 bytes .../keys/finalization_hints_compression_4.bin | Bin 2024 -> 18934 bytes ...nalization_hints_compression_wrapper_1.bin | 35 + ...nalization_hints_compression_wrapper_5.bin | Bin 2024 -> 19057 bytes .../keys/verification_compression_1_key.json | 476 ++- .../keys/verification_compression_2_key.json | 498 ++- .../keys/verification_compression_3_key.json | 498 ++- .../keys/verification_compression_4_key.json | 3422 ++++++++--------- ...erification_compression_wrapper_1_key.json | 260 ++ ...erification_compression_wrapper_5_key.json | 424 +- .../data/keys/verification_scheduler_key.json | 490 ++- prover/setup-data-gpu-keys.json | 6 +- .../crates/zkstack/completion/_zkstack.zsh | 8 +- .../crates/zkstack/completion/zkstack.fish | 12 +- .../crates/zkstack/completion/zkstack.sh | 24 +- .../commands/prover/args/compressor_keys.rs | 53 +- .../zkstack/src/commands/prover/args/init.rs | 18 +- .../src/commands/prover/compressor_keys.rs | 81 +- .../zkstack/src/commands/prover/init.rs | 47 +- 45 files changed, 3791 insertions(+), 3873 deletions(-) delete mode 100644 prover/crates/bin/vk_setup_data_generator_server_fri/src/utils.rs create mode 100644 prover/crates/lib/keystore/src/compressor.rs create mode 100644 prover/data/keys/finalization_hints_compression_wrapper_1.bin create mode 100644 prover/data/keys/verification_compression_wrapper_1_key.json diff --git a/.dockerignore b/.dockerignore index 9be9fd580858..528c0ed9793a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -25,6 +25,7 @@ keys/setup !contracts/ !setup_2\^26.key !setup_2\^24.key +!setup_compact.key # It's required to remove .git from contracts, # otherwise yarn tries to use .git parent directory that # doesn't exist. diff --git a/.github/workflows/build-proof-fri-gpu-compressor-gar.yml b/.github/workflows/build-proof-fri-gpu-compressor-gar.yml index 05255eeb2c8d..aeaaf5ff190d 100644 --- a/.github/workflows/build-proof-fri-gpu-compressor-gar.yml +++ b/.github/workflows/build-proof-fri-gpu-compressor-gar.yml @@ -28,7 +28,7 @@ jobs: - name: Download FFLONK key and setup data run: | gsutil -m rsync -r gs://matterlabs-setup-data-us/${{ inputs.setup_keys_id }} docker/proof-fri-gpu-compressor-gar - gsutil -m cp -r gs://matterlabs-setup-keys-us/setup-keys/setup_fflonk_compact.key docker/proof-fri-gpu-compressor-gar + gsutil -m cp -r gs://matterlabs-setup-keys-us/setup-keys/setup_compact.key docker/proof-fri-gpu-compressor-gar - name: Login to us-central1 GAR run: | diff --git a/.gitignore b/.gitignore index d92880f5ae48..716024531d4a 100644 --- a/.gitignore +++ b/.gitignore @@ -105,6 +105,8 @@ hyperchain-*.yml # Prover keys that should not be commited prover/crates/bin/vk_setup_data_generator_server_fri/data/setup_* prover/data/keys/setup_* +prover/data/keys/fflonk_setup_snark_data.bin +prover/data/keys/plonk_setup_snark_data.bin # ZK Stack CLI chains/era/configs/* diff --git a/core/Cargo.lock b/core/Cargo.lock index 3f9e1ee93561..1b251bec741d 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -1289,9 +1289,9 @@ dependencies = [ [[package]] name = "boojum" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14bd053feb7173130679a2119e105b5e78af7eb6b0e752de6793e4ee63d8e899" +checksum = "d689807d79092f8f7cfcb72a2313a43da77d56314e41324810566f385875c185" dependencies = [ "arrayvec 0.7.6", "bincode", @@ -1690,9 +1690,9 @@ dependencies = [ [[package]] name = "circuit_definitions" -version = "0.150.19" +version = "0.150.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10ebc81d5c2f6ee8de436c242f6466fb315fe25afcbc81aa1c47dfca39a55403" +checksum = "1f04f9c7c6b39255199aaba49802c5f40f95bcff24f5a456446a912d254f4bb1" dependencies = [ "circuit_encodings", "crossbeam", @@ -1704,26 +1704,26 @@ dependencies = [ [[package]] name = "circuit_encodings" -version = "0.150.19" +version = "0.150.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33375d2448a78c1aed9b8755f7939a6b6f19e2fa80f44f4930a5b4c2bb7cbb44" +checksum = "fc3399f1981164c3c687ea15b1eedd35a16f28069c845a24530de21f996f3fdd" dependencies = [ "derivative", "serde", - "zk_evm 0.150.19", + "zk_evm 0.150.20", "zkevm_circuits", ] [[package]] name = "circuit_sequencer_api" -version = "0.150.19" +version = "0.150.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2fec5c28e5a9f085279e70e13b2eebb63a95ee0bfb99d58095ac01c1c7b256" +checksum = "b5583037ec61607ac481b0c887b7fb4f860e65c92c6f3f7be74f6bab7c40c3ce" dependencies = [ "derivative", "rayon", "serde", - "zk_evm 0.150.19", + "zk_evm 0.150.20", "zksync_bellman", ] @@ -2983,9 +2983,9 @@ dependencies = [ [[package]] name = "fflonk" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e63d70f1cbf9e572ccaf22ca1dfce4b93ff48b9a5e8dd70de50d87edb960d173" +checksum = "b36c5fa909ab71b7eb4b8f7fd092f72ed83b93f2615e42f245ca808d8f308917" dependencies = [ "bincode", "byteorder", @@ -3272,9 +3272,9 @@ dependencies = [ [[package]] name = "franklin-crypto" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d7b8e5864df7f3747e5e64a5b87b4a57aa2a4a20c55c9e96a3a305a8143c45" +checksum = "8309d8fc22fc389d831390473b0ee9fe94e85f19a8b9229b9aec8aa73f5bcee3" dependencies = [ "arr_macro", "bit-vec", @@ -7258,9 +7258,9 @@ dependencies = [ [[package]] name = "rescue_poseidon" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c250446885c257bee70bc0f2600229ce72f03073b87fb8f5dd278dba16b11f30" +checksum = "5e631fd184b6d2f2c04f9dc75405289d99fd0d6612d8dfbb478c01bfbab648fb" dependencies = [ "addchain", "arrayvec 0.7.6", @@ -8665,9 +8665,9 @@ dependencies = [ [[package]] name = "snark_wrapper" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f361c2c47b71ee43f62954ce69f7730e14acb7fb3b0f2c697da02f97327c569" +checksum = "eddb498315057210abd25e2fbe2ea30ab69a07ca0c166406a3e7c056ec8fbbfd" dependencies = [ "derivative", "rand 0.4.6", @@ -11160,9 +11160,9 @@ dependencies = [ [[package]] name = "zk_evm" -version = "0.150.19" +version = "0.150.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84ee848aa90ae045457795b1c0afeb388fbd9fa1e57aa0e8791b28f405e7cc2c" +checksum = "f11d0310228af78e804e5e7deccd1ad6797fce1c44c3b8016722ab78dc183c4a" dependencies = [ "anyhow", "lazy_static", @@ -11170,7 +11170,7 @@ dependencies = [ "serde", "serde_json", "static_assertions", - "zk_evm_abstractions 0.150.19", + "zk_evm_abstractions 0.150.20", ] [[package]] @@ -11201,22 +11201,22 @@ dependencies = [ [[package]] name = "zk_evm_abstractions" -version = "0.150.19" +version = "0.150.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f08feaa3e3d99e1e57234fe6ba2aa062609492c6499b2344121c4a699292ab7" +checksum = "d7616edbdeeeb214211e9bdc4346b6a62c6c6118c3d2b83b7db24c01f65f6e25" dependencies = [ "anyhow", "num_enum 0.6.1", "serde", "static_assertions", - "zkevm_opcode_defs 0.150.19", + "zkevm_opcode_defs 0.150.20", ] [[package]] name = "zkevm_circuits" -version = "0.150.19" +version = "0.150.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760cfbbce18f42bbecd2565de9bf658234cac2431cce9b0c1df08e9df645d467" +checksum = "6f36004572f5086c513715e11f38230e2538c159d4f5d90dc518833c6fc78293" dependencies = [ "arrayvec 0.7.6", "boojum", @@ -11228,7 +11228,7 @@ dependencies = [ "seq-macro", "serde", "smallvec", - "zkevm_opcode_defs 0.150.19", + "zkevm_opcode_defs 0.150.20", "zksync_cs_derive", ] @@ -11276,9 +11276,9 @@ dependencies = [ [[package]] name = "zkevm_opcode_defs" -version = "0.150.19" +version = "0.150.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2bd8ef52c8f9911dd034b91d29f087ab52f80a80f9d996deb881abbb953793" +checksum = "ce6b4a47c0e7f95b51d29ca336821321cec4bbba0acdd412c3a209270a0d37fe" dependencies = [ "bitflags 2.6.0", "blake2 0.10.6", @@ -11337,9 +11337,9 @@ dependencies = [ [[package]] name = "zksync_bellman" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d06d424f7e3862d7a6715179bafffbe7a5dce17129f95ac4124502ab9f1edfb8" +checksum = "78fc3c598daf718b6fc791bfbb01c4634199e479ea9b2c82d06cd108b967d441" dependencies = [ "arrayvec 0.7.6", "bit-vec", @@ -11411,7 +11411,7 @@ dependencies = [ "tokio", "tracing", "vise", - "zk_evm 0.150.19", + "zk_evm 0.150.20", "zksync_contracts", "zksync_dal", "zksync_eth_client", @@ -11756,9 +11756,9 @@ dependencies = [ [[package]] name = "zksync_cs_derive" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23237b019a469bfa59c11108beff84a63a43f52fa3afbf1b461527031fc47644" +checksum = "97ab7469afcd9e1cb220fe17b3c9f2abe031648b94add97da37065c58be08554" dependencies = [ "proc-macro-error", "proc-macro2 1.0.92", @@ -12087,9 +12087,9 @@ dependencies = [ [[package]] name = "zksync_ff" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5aa518ed0ea7ef737d50de02025f5a593dbb11104b3c1bf5a00f39581b47dc" +checksum = "6583c2db6dc787600879d27ec98d2eb628a757ee41831e54f8be1dae4acc599f" dependencies = [ "byteorder", "hex", @@ -12100,9 +12100,9 @@ dependencies = [ [[package]] name = "zksync_ff_derive" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b43100a1278e2f64820368db8751c2441860ea74ab5749074cf8f864647af" +checksum = "8f62e93dde881d8dd44d1864c7682394dde6d18e582fc5af78768221a1766fdf" dependencies = [ "num-bigint 0.4.6", "num-integer", @@ -12145,9 +12145,9 @@ dependencies = [ [[package]] name = "zksync_kzg" -version = "0.150.19" +version = "0.150.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9da880b8282a97d9dfd6ac9f0189d310c0602059a8de20aa66a883979d6adba" +checksum = "174f82592590901cbcf2b298059c89f817b404299ffbd050a3915ea72357f545" dependencies = [ "boojum", "derivative", @@ -12295,7 +12295,7 @@ dependencies = [ "zk_evm 0.133.0", "zk_evm 0.140.0", "zk_evm 0.141.0", - "zk_evm 0.150.19", + "zk_evm 0.150.20", "zksync_contracts", "zksync_eth_signer", "zksync_mini_merkle_tree", @@ -12335,7 +12335,7 @@ dependencies = [ "tower-http 0.5.2", "tracing", "vise", - "zk_evm 0.150.19", + "zk_evm 0.150.20", "zksync_config", "zksync_consensus_roles", "zksync_contracts", @@ -12634,9 +12634,9 @@ dependencies = [ [[package]] name = "zksync_pairing" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f0d96f3e386f3b4c76a614d73b71714d6712e917d462bf8053b8af352da0b3" +checksum = "baafdd03ca7a48dc9b6808be3630f2d8a003aa425d71946e9158d8c0aeb1cc79" dependencies = [ "byteorder", "cfg-if", @@ -12741,6 +12741,7 @@ dependencies = [ "serde_with", "strum", "tokio", + "zksync_bellman", "zksync_object_store", "zksync_types", "zksync_vm_interface", @@ -12845,9 +12846,9 @@ dependencies = [ [[package]] name = "zksync_solidity_vk_codegen" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb10f377dcc24fe2268cc5f530c16af1c879a791570d8fe64064b58ba143c7cc" +checksum = "bb05a12f5552d7947427f755e29f548ce94733851f1fa16edaf8b75c28033e73" dependencies = [ "ethereum-types", "franklin-crypto", @@ -13088,8 +13089,8 @@ source = "git+https://github.com/matter-labs/vm2.git?rev=457d8a7eea9093af9440662 dependencies = [ "enum_dispatch", "primitive-types", - "zk_evm_abstractions 0.150.19", - "zkevm_opcode_defs 0.150.19", + "zk_evm_abstractions 0.150.20", + "zkevm_opcode_defs 0.150.20", "zksync_vm2_interface", ] diff --git a/core/Cargo.toml b/core/Cargo.toml index 3b6ba37c28a6..80e9ac035283 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -228,18 +228,19 @@ tokio-stream = "0.1.16" # We *always* pin the latest version of protocol to disallow accidental changes in the execution logic. # However, for the historical version of protocol crates, we have lax requirements. Otherwise, # Bumping a crypto dependency like `boojum` would require us to republish all the historical packages. -circuit_encodings = "=0.150.19" -circuit_sequencer_api = "=0.150.19" -circuit_definitions = "=0.150.19" -crypto_codegen = { package = "zksync_solidity_vk_codegen",version = "=0.30.12" } -kzg = { package = "zksync_kzg", version = "=0.150.19" } +circuit_encodings = "=0.150.20" +circuit_sequencer_api = "=0.150.20" +circuit_definitions = "=0.150.20" +crypto_codegen = { package = "zksync_solidity_vk_codegen",version = "=0.30.13" } +kzg = { package = "zksync_kzg", version = "=0.150.20" } zk_evm = { version = "=0.133.0" } zk_evm_1_3_1 = { package = "zk_evm", version = "0.131.0-rc.2" } zk_evm_1_3_3 = { package = "zk_evm", version = "0.133" } zk_evm_1_4_0 = { package = "zk_evm", version = "0.140" } zk_evm_1_4_1 = { package = "zk_evm", version = "0.141" } -zk_evm_1_5_0 = { package = "zk_evm", version = "=0.150.19" } -fflonk = "=0.30.12" +zk_evm_1_5_0 = { package = "zk_evm", version = "=0.150.20" } +fflonk = "=0.30.13" +bellman = {package = "zksync_bellman", version = "=0.30.13"} # New VM; pinned to a specific commit because of instability zksync_vm2 = { git = "https://github.com/matter-labs/vm2.git", rev = "457d8a7eea9093af9440662e33e598c13ba41633" } diff --git a/core/lib/prover_interface/Cargo.toml b/core/lib/prover_interface/Cargo.toml index 533e3bc1296a..dfa860c5aa6b 100644 --- a/core/lib/prover_interface/Cargo.toml +++ b/core/lib/prover_interface/Cargo.toml @@ -18,6 +18,7 @@ zksync_types.workspace = true # We can use the newest api to send proofs to L1. circuit_definitions.workspace = true fflonk.workspace = true +bellman.workspace = true circuit_sequencer_api.workspace = true serde.workspace = true diff --git a/core/lib/prover_interface/src/outputs.rs b/core/lib/prover_interface/src/outputs.rs index 1c3a613628d3..3fc037d0c040 100644 --- a/core/lib/prover_interface/src/outputs.rs +++ b/core/lib/prover_interface/src/outputs.rs @@ -1,10 +1,12 @@ use core::fmt; +use bellman::plonk::better_better_cs::proof::Proof as PlonkProof; use circuit_definitions::{ boojum::pairing::bn256::Bn256, - circuit_definitions::aux_layer::ZkSyncSnarkWrapperCircuitNoLookupCustomGate, + circuit_definitions::aux_layer::{ + ZkSyncSnarkWrapperCircuit, ZkSyncSnarkWrapperCircuitNoLookupCustomGate, + }, }; -use circuit_sequencer_api::proof::FinalProof; use fflonk::FflonkProof; use serde::{Deserialize, Serialize}; use serde_with::{hex::Hex, serde_as}; @@ -43,10 +45,18 @@ pub struct FflonkL1BatchProofForL1 { pub protocol_version: ProtocolSemanticVersion, } +// Implementation created to allow conversion from FflonkL1BatchProofForL1(which is old L1BatchProofForL1) +// to L1BatchProofForL1 to avoid compatibility problems with serialization/deserialization +impl From for L1BatchProofForL1 { + fn from(proof: FflonkL1BatchProofForL1) -> Self { + L1BatchProofForL1::Fflonk(proof) + } +} + #[derive(Clone, Serialize, Deserialize)] pub struct PlonkL1BatchProofForL1 { pub aggregation_result_coords: [[u8; 32]; 4], - pub scheduler_proof: FinalProof, + pub scheduler_proof: PlonkProof, pub protocol_version: ProtocolSemanticVersion, } @@ -125,11 +135,12 @@ impl StoredObject for L1BatchProofForL1 { } fn deserialize(bytes: Vec) -> Result { - zksync_object_store::bincode::deserialize::(&bytes).or_else(|_| { - zksync_object_store::bincode::deserialize::(&bytes) + match zksync_object_store::bincode::deserialize::(&bytes) { + Ok(proof) => Ok(proof.into()), + Err(_) => zksync_object_store::bincode::deserialize::(&bytes) .map(Into::into) - .map_err(Into::into) - }) + .map_err(Into::into), + } } } diff --git a/docker/proof-fri-gpu-compressor-gar/Dockerfile b/docker/proof-fri-gpu-compressor-gar/Dockerfile index 42127ea6126c..d74440bd009b 100644 --- a/docker/proof-fri-gpu-compressor-gar/Dockerfile +++ b/docker/proof-fri-gpu-compressor-gar/Dockerfile @@ -4,18 +4,15 @@ FROM nvidia/cuda:12.4.0-runtime-ubuntu22.04 as app # HACK copying to root is the only way to make Docker layer caching work for these files for some reason COPY *.bin / -COPY ./setup_fflonk_compact.key /setup_fflonk_compact.key +COPY ./setup_compact.key /setup_compact.key RUN apt-get update && apt-get install -y curl libpq5 ca-certificates && rm -rf /var/lib/apt/lists/* - # copy finalization hints required for assembly generation -COPY --from=proof_fri_gpu /setup_2\^24.key /setup_2\^24.key COPY --from=proof_fri_gpu /prover/data/keys/ /prover/data/keys/ COPY --from=proof_fri_gpu /usr/bin/zksync_proof_fri_compressor /usr/bin/ -ENV CRS_FILE=/setup_2\^24.key -ENV COMPACT_CRS_FILE=/setup_fflonk_compact.key +ENV COMPACT_CRS_FILE=/setup_compact.key ENTRYPOINT ["zksync_proof_fri_compressor"] diff --git a/etc/env/base/contracts.toml b/etc/env/base/contracts.toml index 1cb22440e33c..d3eaabf92bd8 100644 --- a/etc/env/base/contracts.toml +++ b/etc/env/base/contracts.toml @@ -39,7 +39,7 @@ BLOB_VERSIONED_HASH_RETRIEVER_ADDR = "0x0000000000000000000000000000000000000000 GENESIS_ROOT = "0x09e68951458b18c24ae5f4100160b53c4888c9b3c3c1859cc674bc02236675ad" GENESIS_BATCH_COMMITMENT = "0x7238eab6a0e9f5bb84421feae6b6b9ae80816d490c875d29ff3ded375a3e078f" -GENESIS_ROLLUP_LEAF_INDEX = "64" +GENESIS_ROLLUP_LEAF_INDEX = "64" # Ecosystem-wide params L1_ROLLUP_DA_VALIDATOR = "0x0000000000000000000000000000000000000000" @@ -67,7 +67,7 @@ L1_NATIVE_TOKEN_VAULT_IMPL_ADDR ="0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" L1_NATIVE_TOKEN_VAULT_PROXY_ADDR ="0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" L2_NATIVE_TOKEN_VAULT_IMPL_ADDR = "0x0000000000000000000000000000000000010004" L2_NATIVE_TOKEN_VAULT_PROXY_ADDR = "0x0000000000000000000000000000000000010004" -L2_SHARED_BRIDGE_IMPL_ADDR = "0x0000000000000000000000000000000000010003" +L2_SHARED_BRIDGE_IMPL_ADDR = "0x0000000000000000000000000000000000010003" L2_SHARED_BRIDGE_ADDR = "0x0000000000000000000000000000000000010003" L2_ERC20_BRIDGE_ADDR = "0x0000000000000000000000000000000000010003" CTM_DEPLOYMENT_TRACKER_IMPL_ADDR ="0xFC073319977e314F251EAE6ae6bE76B0B3BAeeCF" diff --git a/etc/env/base/fri_proof_compressor.toml b/etc/env/base/fri_proof_compressor.toml index a8825ca98613..c8855d6b2a48 100644 --- a/etc/env/base/fri_proof_compressor.toml +++ b/etc/env/base/fri_proof_compressor.toml @@ -5,8 +5,8 @@ prometheus_pushgateway_url = "http://127.0.0.1:9091" prometheus_push_interval_ms = 100 generation_timeout_in_secs = 3600 max_attempts = 5 -universal_setup_path = "../keys/setup/setup_2^24.key" -universal_setup_download_url = "https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2^24.key" +universal_setup_path = "../keys/setup/setup_compact.key" +universal_setup_download_url = "https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_compact.key" verify_wrapper_proof = true universal_fflonk_setup_path = "../keys/setup/setup_fflonk_compact.key" universal_fflonk_setup_download_url = "https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_fflonk_compact.key" diff --git a/etc/env/file_based/general.yaml b/etc/env/file_based/general.yaml index f15b63a757f7..ece5a1156c5f 100644 --- a/etc/env/file_based/general.yaml +++ b/etc/env/file_based/general.yaml @@ -182,8 +182,8 @@ proof_compressor: prometheus_push_interval_ms: 100 generation_timeout_in_secs: 3600 max_attempts: 5 - universal_setup_path: keys/setup/setup_2^24.key - universal_setup_download_url: https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_2^24.key + universal_setup_path: keys/setup/setup_compact.key + universal_setup_download_url: https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_compact.key verify_wrapper_proof: true universal_fflonk_setup_path: keys/setup/setup_fflonk_compact.key universal_fflonk_setup_download_url: https://storage.googleapis.com/matterlabs-setup-keys-us/setup-keys/setup_fflonk_compact.key diff --git a/prover/Cargo.lock b/prover/Cargo.lock index a9a8d7e71c54..19223f360857 100644 --- a/prover/Cargo.lock +++ b/prover/Cargo.lock @@ -625,9 +625,9 @@ dependencies = [ [[package]] name = "boojum" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14bd053feb7173130679a2119e105b5e78af7eb6b0e752de6793e4ee63d8e899" +checksum = "d689807d79092f8f7cfcb72a2313a43da77d56314e41324810566f385875c185" dependencies = [ "arrayvec 0.7.6", "bincode", @@ -657,9 +657,9 @@ dependencies = [ [[package]] name = "boojum-cuda" -version = "0.152.10" +version = "0.152.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd43bc7fc457920cb3b823e4f95ffbbf180b2c48b8d643125cd121325cdd8db" +checksum = "896aeb550e4b92e6c96858c1d0aa8413c00f97fb91f321a2bf3ed912942870f8" dependencies = [ "boojum", "cmake", @@ -805,9 +805,9 @@ dependencies = [ [[package]] name = "circuit_definitions" -version = "0.150.19" +version = "0.150.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10ebc81d5c2f6ee8de436c242f6466fb315fe25afcbc81aa1c47dfca39a55403" +checksum = "1f04f9c7c6b39255199aaba49802c5f40f95bcff24f5a456446a912d254f4bb1" dependencies = [ "circuit_encodings", "crossbeam", @@ -819,26 +819,26 @@ dependencies = [ [[package]] name = "circuit_encodings" -version = "0.150.19" +version = "0.150.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33375d2448a78c1aed9b8755f7939a6b6f19e2fa80f44f4930a5b4c2bb7cbb44" +checksum = "fc3399f1981164c3c687ea15b1eedd35a16f28069c845a24530de21f996f3fdd" dependencies = [ "derivative", "serde", - "zk_evm 0.150.19", + "zk_evm 0.150.20", "zkevm_circuits", ] [[package]] name = "circuit_sequencer_api" -version = "0.150.19" +version = "0.150.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2fec5c28e5a9f085279e70e13b2eebb63a95ee0bfb99d58095ac01c1c7b256" +checksum = "b5583037ec61607ac481b0c887b7fb4f860e65c92c6f3f7be74f6bab7c40c3ce" dependencies = [ "derivative", "rayon", "serde", - "zk_evm 0.150.19", + "zk_evm 0.150.20", "zksync_bellman", ] @@ -1706,9 +1706,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "era_cudart" -version = "0.152.10" +version = "0.152.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f881cf689ba889bb0fa04c0e71aba701acd7fafd3fa545e3f2782f2a8c0ba0" +checksum = "6ff6fc4fba6bf756cdebd6750161d280af2859d217dad89bfb2823ac760bf0e8" dependencies = [ "bitflags 2.6.0", "era_cudart_sys", @@ -1717,11 +1717,11 @@ dependencies = [ [[package]] name = "era_cudart_sys" -version = "0.152.10" +version = "0.152.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f29cbd9e6d97fc1f05b484f960e921fe69548b4773a361b2e403e4cb9d6d575" +checksum = "7e0daeb39d2111a868a50e0bd7d90fa355f93022038088c0dd865bbdda1113ef" dependencies = [ - "serde_json", + "regex-lite", ] [[package]] @@ -1838,9 +1838,9 @@ dependencies = [ [[package]] name = "fflonk" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e63d70f1cbf9e572ccaf22ca1dfce4b93ff48b9a5e8dd70de50d87edb960d173" +checksum = "b36c5fa909ab71b7eb4b8f7fd092f72ed83b93f2615e42f245ca808d8f308917" dependencies = [ "bincode", "byteorder", @@ -1855,9 +1855,9 @@ dependencies = [ [[package]] name = "fflonk-cuda" -version = "0.152.10" +version = "0.152.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b008e6158c95747b3b135adbd7f6d563c406849a10c00abfef109b4d0442a589" +checksum = "890b635123fe176814ddbda1fbe006c55ca02375e5dde83539018f283219a8ba" dependencies = [ "bincode", "byteorder", @@ -1975,9 +1975,9 @@ dependencies = [ [[package]] name = "franklin-crypto" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d7b8e5864df7f3747e5e64a5b87b4a57aa2a4a20c55c9e96a3a305a8143c45" +checksum = "8309d8fc22fc389d831390473b0ee9fe94e85f19a8b9229b9aec8aa73f5bcee3" dependencies = [ "arr_macro", "bit-vec 0.6.3", @@ -4550,9 +4550,9 @@ dependencies = [ [[package]] name = "proof-compression" -version = "0.152.10" +version = "0.152.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40624617ed1535349cf31671a7091703d3a31e64d6a7760a5e952c68ee1f4f0e" +checksum = "de4c8014afcd29bfe93ac3bd1ea0d9f2da06fa9895337bead3f3d0d904080e36" dependencies = [ "bincode", "byteorder", @@ -4562,6 +4562,7 @@ dependencies = [ "serde", "serde_json", "shivini", + "zksync-gpu-prover", ] [[package]] @@ -4945,6 +4946,12 @@ dependencies = [ "regex-syntax 0.8.5", ] +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + [[package]] name = "regex-syntax" version = "0.6.29" @@ -5070,9 +5077,9 @@ dependencies = [ [[package]] name = "rescue_poseidon" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c250446885c257bee70bc0f2600229ce72f03073b87fb8f5dd278dba16b11f30" +checksum = "5e631fd184b6d2f2c04f9dc75405289d99fd0d6612d8dfbb478c01bfbab648fb" dependencies = [ "addchain", "arrayvec 0.7.6", @@ -5860,9 +5867,9 @@ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] name = "shivini" -version = "0.152.10" +version = "0.152.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c336213e4ec7651d2984e892326d09c195ee166493c192b0d8aad36288e5c5f" +checksum = "8937f1fe25a1ea33a40bdf560847b934fe68322c40cd54dd77ab433128022cce" dependencies = [ "bincode", "boojum", @@ -5951,9 +5958,9 @@ dependencies = [ [[package]] name = "snark_wrapper" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f361c2c47b71ee43f62954ce69f7730e14acb7fb3b0f2c697da02f97327c569" +checksum = "eddb498315057210abd25e2fbe2ea30ab69a07ca0c166406a3e7c056ec8fbbfd" dependencies = [ "derivative", "rand 0.4.6", @@ -7839,9 +7846,9 @@ dependencies = [ [[package]] name = "zk_evm" -version = "0.150.19" +version = "0.150.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84ee848aa90ae045457795b1c0afeb388fbd9fa1e57aa0e8791b28f405e7cc2c" +checksum = "f11d0310228af78e804e5e7deccd1ad6797fce1c44c3b8016722ab78dc183c4a" dependencies = [ "anyhow", "lazy_static", @@ -7849,7 +7856,7 @@ dependencies = [ "serde", "serde_json", "static_assertions", - "zk_evm_abstractions 0.150.19", + "zk_evm_abstractions 0.150.20", ] [[package]] @@ -7880,22 +7887,22 @@ dependencies = [ [[package]] name = "zk_evm_abstractions" -version = "0.150.19" +version = "0.150.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f08feaa3e3d99e1e57234fe6ba2aa062609492c6499b2344121c4a699292ab7" +checksum = "d7616edbdeeeb214211e9bdc4346b6a62c6c6118c3d2b83b7db24c01f65f6e25" dependencies = [ "anyhow", "num_enum 0.6.1", "serde", "static_assertions", - "zkevm_opcode_defs 0.150.19", + "zkevm_opcode_defs 0.150.20", ] [[package]] name = "zkevm-assembly" -version = "0.150.19" +version = "0.150.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd4bc83f3a711d820829dccce24fa59ab4c588c2745203ec6a6ad8c871362b7" +checksum = "c2dc9539ce7f550231934e6b1faae23387fd132f1ac053b8e674d30968158bff" dependencies = [ "env_logger 0.9.3", "hex", @@ -7908,14 +7915,14 @@ dependencies = [ "smallvec", "structopt", "thiserror 1.0.69", - "zkevm_opcode_defs 0.150.19", + "zkevm_opcode_defs 0.150.20", ] [[package]] name = "zkevm_circuits" -version = "0.150.19" +version = "0.150.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760cfbbce18f42bbecd2565de9bf658234cac2431cce9b0c1df08e9df645d467" +checksum = "6f36004572f5086c513715e11f38230e2538c159d4f5d90dc518833c6fc78293" dependencies = [ "arrayvec 0.7.6", "boojum", @@ -7927,7 +7934,7 @@ dependencies = [ "seq-macro", "serde", "smallvec", - "zkevm_opcode_defs 0.150.19", + "zkevm_opcode_defs 0.150.20", "zksync_cs_derive", ] @@ -7975,9 +7982,9 @@ dependencies = [ [[package]] name = "zkevm_opcode_defs" -version = "0.150.19" +version = "0.150.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2bd8ef52c8f9911dd034b91d29f087ab52f80a80f9d996deb881abbb953793" +checksum = "ce6b4a47c0e7f95b51d29ca336821321cec4bbba0acdd412c3a209270a0d37fe" dependencies = [ "bitflags 2.6.0", "blake2 0.10.6", @@ -7992,9 +7999,9 @@ dependencies = [ [[package]] name = "zkevm_test_harness" -version = "0.150.19" +version = "0.150.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24b28c5b855e4e28d85455b48f346e9d46a00c6af84d5bbc38e5b5f7410b5cb" +checksum = "36ed8dd80455d90a51a6618a5bc07685beaad582cabca71ccef25866cd73993b" dependencies = [ "bincode", "circuit_definitions", @@ -8020,9 +8027,9 @@ dependencies = [ [[package]] name = "zksync-gpu-ffi" -version = "0.152.10" +version = "0.152.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f6a84e4e361977a2dc5dbe783e3856e40b1050dc1b9bb3e9833a5e59c20697" +checksum = "6c3fbd4c8df140131d28b05581b19418bc5e561beb21dec6f24ca2f34343399c" dependencies = [ "cmake", "crossbeam", @@ -8035,9 +8042,9 @@ dependencies = [ [[package]] name = "zksync-gpu-prover" -version = "0.152.10" +version = "0.152.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ba58bbaf4920c635553d3dfb7796636223f55e75ae6512eb9c98f48f0a03215" +checksum = "0256175ceb3ea675d4c0ebcd690fdd45138bab1c5bc298b2e0db320e5abc0bdb" dependencies = [ "bit-vec 0.6.3", "cfg-if", @@ -8052,9 +8059,9 @@ dependencies = [ [[package]] name = "zksync-wrapper-prover" -version = "0.152.10" +version = "0.152.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4bcf41946f95a1e64ce99cde1d54966a04c5ef2c89d9a87f0fa61e39987510b" +checksum = "390d8f99cf47fade7f2fe38925f9787b3d27641a878887ae980e4ab5f6731ac0" dependencies = [ "circuit_definitions", "zkevm_test_harness", @@ -8084,9 +8091,9 @@ dependencies = [ [[package]] name = "zksync_bellman" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d06d424f7e3862d7a6715179bafffbe7a5dce17129f95ac4124502ab9f1edfb8" +checksum = "78fc3c598daf718b6fc791bfbb01c4634199e479ea9b2c82d06cd108b967d441" dependencies = [ "arrayvec 0.7.6", "bit-vec 0.6.3", @@ -8310,9 +8317,9 @@ dependencies = [ [[package]] name = "zksync_cs_derive" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23237b019a469bfa59c11108beff84a63a43f52fa3afbf1b461527031fc47644" +checksum = "97ab7469afcd9e1cb220fe17b3c9f2abe031648b94add97da37065c58be08554" dependencies = [ "proc-macro-error", "proc-macro2 1.0.92", @@ -8412,9 +8419,9 @@ dependencies = [ [[package]] name = "zksync_ff" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5aa518ed0ea7ef737d50de02025f5a593dbb11104b3c1bf5a00f39581b47dc" +checksum = "6583c2db6dc787600879d27ec98d2eb628a757ee41831e54f8be1dae4acc599f" dependencies = [ "byteorder", "hex", @@ -8425,9 +8432,9 @@ dependencies = [ [[package]] name = "zksync_ff_derive" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b43100a1278e2f64820368db8751c2441860ea74ab5749074cf8f864647af" +checksum = "8f62e93dde881d8dd44d1864c7682394dde6d18e582fc5af78768221a1766fdf" dependencies = [ "num-bigint 0.4.6", "num-integer", @@ -8440,9 +8447,9 @@ dependencies = [ [[package]] name = "zksync_kzg" -version = "0.150.19" +version = "0.150.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9da880b8282a97d9dfd6ac9f0189d310c0602059a8de20aa66a883979d6adba" +checksum = "174f82592590901cbcf2b298059c89f817b404299ffbd050a3915ea72357f545" dependencies = [ "boojum", "derivative", @@ -8498,7 +8505,7 @@ dependencies = [ "zk_evm 0.133.0", "zk_evm 0.140.0", "zk_evm 0.141.0", - "zk_evm 0.150.19", + "zk_evm 0.150.20", "zksync_contracts", "zksync_mini_merkle_tree", "zksync_system_constants", @@ -8532,9 +8539,9 @@ dependencies = [ [[package]] name = "zksync_pairing" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f0d96f3e386f3b4c76a614d73b71714d6712e917d462bf8053b8af352da0b3" +checksum = "baafdd03ca7a48dc9b6808be3630f2d8a003aa425d71946e9158d8c0aeb1cc79" dependencies = [ "byteorder", "cfg-if", @@ -8784,6 +8791,7 @@ dependencies = [ "serde", "serde_with", "strum", + "zksync_bellman", "zksync_object_store", "zksync_types", "zksync_vm_interface", @@ -8841,6 +8849,7 @@ dependencies = [ "hex", "md5", "once_cell", + "proof-compression", "serde", "serde_json", "sha3 0.10.8", @@ -8867,9 +8876,9 @@ dependencies = [ [[package]] name = "zksync_solidity_vk_codegen" -version = "0.30.12" +version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb10f377dcc24fe2268cc5f530c16af1c879a791570d8fe64064b58ba143c7cc" +checksum = "bb05a12f5552d7947427f755e29f548ce94733851f1fa16edaf8b75c28033e73" dependencies = [ "ethereum-types", "franklin-crypto", @@ -8993,8 +9002,8 @@ source = "git+https://github.com/matter-labs/vm2.git?rev=457d8a7eea9093af9440662 dependencies = [ "enum_dispatch", "primitive-types", - "zk_evm_abstractions 0.150.19", - "zkevm_opcode_defs 0.150.19", + "zk_evm_abstractions 0.150.20", + "zkevm_opcode_defs 0.150.20", "zksync_vm2_interface", ] diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 6c249b7094ae..194f6b90a2e7 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -67,19 +67,18 @@ tracing-test = "0.2.5" url = "2.5.2" vise = "0.2.0" -# Proving dependencies -circuit_definitions = "=0.150.19" -circuit_sequencer_api = "=0.150.19" -zkevm_test_harness = "=0.150.19" -proof-compression-gpu = { package = "proof-compression", version = "=0.152.10" } -fflonk-gpu = { package = "fflonk-cuda", version = "=0.152.10" } -fflonk = "=0.30.12" -franklin-crypto = "=0.30.12" +circuit_definitions = "=0.150.20" +circuit_sequencer_api = "=0.150.20" +zkevm_test_harness = "=0.150.20" +fflonk = "=0.30.13" +franklin-crypto = "=0.30.13" # GPU proving dependencies -wrapper_prover = { package = "zksync-wrapper-prover", version = "=0.152.10" } -shivini = "=0.152.10" -boojum-cuda = "=0.152.10" +proof-compression-gpu = { package = "proof-compression", version = "=0.152.11"} +fflonk-gpu = { package = "fflonk-cuda", version = "=0.152.11"} +wrapper_prover = { package = "zksync-wrapper-prover", version = "=0.152.11"} +shivini = "=0.152.11" +boojum-cuda = "=0.152.11" # Core workspace dependencies zksync_multivm = { version = "=26.1.0-non-semver-compat", path = "../core/lib/multivm" } diff --git a/prover/crates/bin/proof_fri_compressor/src/compressor.rs b/prover/crates/bin/proof_fri_compressor/src/compressor.rs index 3671fa183b5d..581e1fed8a48 100644 --- a/prover/crates/bin/proof_fri_compressor/src/compressor.rs +++ b/prover/crates/bin/proof_fri_compressor/src/compressor.rs @@ -2,27 +2,14 @@ use std::{sync::Arc, time::Instant}; use anyhow::Context as _; use async_trait::async_trait; -use circuit_sequencer_api::proof::FinalProof; -use fflonk_gpu::{FflonkSnarkVerifierCircuit, FflonkSnarkVerifierCircuitProof}; +use proof_compression_gpu::{run_proof_chain, SnarkWrapper, SnarkWrapperProof}; use tokio::task::JoinHandle; -use wrapper_prover::{GPUWrapperConfigs, WrapperProver}; -use zkevm_test_harness::proof_wrapper_utils::{get_trusted_setup, DEFAULT_WRAPPER_CONFIG}; use zksync_object_store::ObjectStore; use zksync_prover_dal::{ConnectionPool, Prover, ProverDal}; use zksync_prover_fri_types::{ circuit_definitions::{ boojum::field::goldilocks::GoldilocksField, - circuit_definitions::{ - aux_layer::{ - wrapper::ZkSyncCompressionWrapper, ZkSyncCompressionForWrapperCircuit, - ZkSyncCompressionLayerCircuit, ZkSyncCompressionProof, - ZkSyncCompressionProofForWrapper, ZkSyncCompressionVerificationKeyForWrapper, - }, - recursion_layer::{ - ZkSyncRecursionLayerProof, ZkSyncRecursionLayerStorageType, - ZkSyncRecursionVerificationKey, - }, - }, + circuit_definitions::recursion_layer::ZkSyncRecursionLayerProof, zkevm_circuits::scheduler::block_header::BlockAuxilaryOutputWitness, }, get_current_pod_name, AuxOutputWitnessWrapper, FriProofWrapper, @@ -39,23 +26,16 @@ use crate::metrics::METRICS; pub struct ProofCompressor { blob_store: Arc, pool: ConnectionPool, - compression_mode: u8, max_attempts: u32, protocol_version: ProtocolSemanticVersion, keystore: Keystore, is_fflonk: bool, } -pub enum Proof { - Plonk(Box), - Fflonk(FflonkSnarkVerifierCircuitProof), -} - impl ProofCompressor { pub fn new( blob_store: Arc, pool: ConnectionPool, - compression_mode: u8, max_attempts: u32, protocol_version: ProtocolSemanticVersion, keystore: Keystore, @@ -64,7 +44,6 @@ impl ProofCompressor { Self { blob_store, pool, - compression_mode, max_attempts, protocol_version, keystore, @@ -85,151 +64,6 @@ impl ProofCompressor { } array } - - #[tracing::instrument(skip(proof, _compression_mode))] - pub fn generate_plonk_proof( - proof: ZkSyncRecursionLayerProof, - _compression_mode: u8, - keystore: Keystore, - ) -> anyhow::Result { - let scheduler_vk = keystore - .load_recursive_layer_verification_key( - ZkSyncRecursionLayerStorageType::SchedulerCircuit as u8, - ) - .context("get_recursiver_layer_vk_for_circuit_type()")?; - - let wrapper_proof = { - let crs = get_trusted_setup(); - let wrapper_config = DEFAULT_WRAPPER_CONFIG; - let mut prover = WrapperProver::::new(&crs, wrapper_config).unwrap(); - - prover - .generate_setup_data(scheduler_vk.into_inner()) - .unwrap(); - prover.generate_proofs(proof.into_inner()).unwrap(); - - prover.get_wrapper_proof().unwrap() - }; - - // (Re)serialization should always succeed. - let serialized = bincode::serialize(&wrapper_proof) - .expect("Failed to serialize proof with ZkSyncSnarkWrapperCircuit"); - - // For sending to L1, we can use the `FinalProof` type, that has a generic circuit inside, that is not used for serialization. - // So `FinalProof` and `Proof>>` are compatible on serialization bytecode level. - let final_proof: FinalProof = - bincode::deserialize(&serialized).expect("Failed to deserialize final proof"); - Ok(final_proof) - } - - #[tracing::instrument(skip(proof, compression_mode, keystore))] - pub fn generate_fflonk_proof( - proof: ZkSyncRecursionLayerProof, - compression_mode: u8, - keystore: Keystore, - ) -> anyhow::Result { - let scheduler_vk = keystore - .load_recursive_layer_verification_key( - ZkSyncRecursionLayerStorageType::SchedulerCircuit as u8, - ) - .context("get_recursiver_layer_vk_for_circuit_type()")?; - - // compress proof step by step: 1 -> 2 -> 3 -> 4 -> 5(wrapper) - let (compression_wrapper_proof, compression_wrapper_vk) = Self::compress_proof( - &keystore, - proof.into_inner(), - scheduler_vk.into_inner(), - compression_mode, - )?; - - // construct fflonk snark verifier circuit - let wrapper_function = - ZkSyncCompressionWrapper::from_numeric_circuit_type(compression_mode); - let fixed_parameters = compression_wrapper_vk.fixed_parameters.clone(); - let circuit = FflonkSnarkVerifierCircuit { - witness: Some(compression_wrapper_proof), - vk: compression_wrapper_vk, - fixed_parameters, - transcript_params: (), - wrapper_function, - }; - - tracing::info!("Proving FFLONK snark verifier"); - - let setup = keystore.load_fflonk_snark_verifier_setup_data()?; - - tracing::info!("Loaded setup data for FFLONK verification"); - - let proof = fflonk_gpu::gpu_prove_fflonk_snark_verifier_circuit_with_precomputation( - &circuit, - &setup, - &setup.get_verification_key(), - ); - tracing::info!("Finished proof generation"); - Ok(proof) - } - - pub fn compress_proof( - keystore: &Keystore, - proof: ZkSyncCompressionProof, - vk: ZkSyncRecursionVerificationKey, - compression_steps: u8, - ) -> anyhow::Result<( - ZkSyncCompressionProofForWrapper, - ZkSyncCompressionVerificationKeyForWrapper, - )> { - let worker = franklin_crypto::boojum::worker::Worker::new(); - let mut compression_circuit = - ZkSyncCompressionLayerCircuit::from_witness_and_vk(Some(proof), vk.clone(), 1); - let mut compression_wrapper_circuit = None; - - for step_idx in 1..compression_steps { - tracing::info!("Proving compression {:?}", step_idx); - let setup_data = keystore.load_compression_setup_data(step_idx)?; - let (proof, vk) = - proof_compression_gpu::prove_compression_layer_circuit_with_precomputations( - compression_circuit.clone(), - &setup_data.setup, - setup_data.finalization_hint, - setup_data.vk, - &worker, - ); - tracing::info!("Proof for compression {:?} is generated!", step_idx); - - if step_idx + 1 == compression_steps { - compression_wrapper_circuit = - Some(ZkSyncCompressionForWrapperCircuit::from_witness_and_vk( - Some(proof), - vk, - compression_steps, - )); - } else { - compression_circuit = ZkSyncCompressionLayerCircuit::from_witness_and_vk( - Some(proof), - vk, - step_idx + 1, - ); - } - } - - // last wrapping step - tracing::info!("Proving compression {} for wrapper", compression_steps); - - let setup_data = keystore.load_compression_wrapper_setup_data(compression_steps)?; - let (proof, vk) = - proof_compression_gpu::prove_compression_wrapper_circuit_with_precomputations( - compression_wrapper_circuit.unwrap(), - &setup_data.setup, - setup_data.finalization_hint, - setup_data.vk, - &worker, - ); - tracing::info!( - "Proof for compression wrapper {} is generated!", - compression_steps - ); - Ok((proof, vk)) - } } #[async_trait] @@ -237,7 +71,7 @@ impl JobProcessor for ProofCompressor { type Job = ZkSyncRecursionLayerProof; type JobId = L1BatchNumber; - type JobArtifacts = Proof; + type JobArtifacts = SnarkWrapperProof; const SERVICE_NAME: &'static str = "ProofCompressor"; @@ -292,23 +126,19 @@ impl JobProcessor for ProofCompressor { job: ZkSyncRecursionLayerProof, _started_at: Instant, ) -> JoinHandle> { - let compression_mode = self.compression_mode; let keystore = self.keystore.clone(); - let is_fflonk = self.is_fflonk; + let snark_wrapper_mode = if self.is_fflonk { + SnarkWrapper::FFfonk + } else { + SnarkWrapper::Plonk + }; + tokio::task::spawn_blocking(move || { - if !is_fflonk { - Ok(Proof::Plonk(Box::new(Self::generate_plonk_proof( - job, - compression_mode, - keystore, - )?))) - } else { - Ok(Proof::Fflonk(Self::generate_fflonk_proof( - job, - compression_mode, - keystore, - )?)) - } + Ok(run_proof_chain( + snark_wrapper_mode, + &keystore, + job.into_inner(), + )) }) } @@ -333,16 +163,18 @@ impl JobProcessor for ProofCompressor { Self::aux_output_witness_to_array(aux_output_witness_wrapper.0); let l1_batch_proof = match artifacts { - Proof::Plonk(proof) => L1BatchProofForL1::Plonk(PlonkL1BatchProofForL1 { - aggregation_result_coords, - scheduler_proof: *proof, - protocol_version: self.protocol_version, - }), - Proof::Fflonk(proof) => L1BatchProofForL1::Fflonk(FflonkL1BatchProofForL1 { + SnarkWrapperProof::Plonk(proof) => L1BatchProofForL1::Plonk(PlonkL1BatchProofForL1 { aggregation_result_coords, scheduler_proof: proof, protocol_version: self.protocol_version, }), + SnarkWrapperProof::FFfonk(proof) => { + L1BatchProofForL1::Fflonk(FflonkL1BatchProofForL1 { + aggregation_result_coords, + scheduler_proof: proof, + protocol_version: self.protocol_version, + }) + } }; let blob_save_started_at = Instant::now(); diff --git a/prover/crates/bin/proof_fri_compressor/src/main.rs b/prover/crates/bin/proof_fri_compressor/src/main.rs index dae03ab41465..bb46c1e7cb75 100644 --- a/prover/crates/bin/proof_fri_compressor/src/main.rs +++ b/prover/crates/bin/proof_fri_compressor/src/main.rs @@ -1,7 +1,7 @@ #![allow(incomplete_features)] // We have to use generic const exprs. #![feature(generic_const_exprs)] -use std::{env, time::Duration}; +use std::time::Duration; use anyhow::Context as _; use clap::Parser; @@ -96,7 +96,6 @@ async fn main() -> anyhow::Result<()> { let proof_compressor = ProofCompressor::new( blob_store, pool, - config.compression_mode, config.max_attempts, protocol_version, keystore, @@ -114,7 +113,7 @@ async fn main() -> anyhow::Result<()> { }) .expect("Error setting Ctrl+C handler"); // Setting handler should always succeed. - setup_crs_keys(&config, is_fflonk); + setup_crs_keys(&config); tracing::info!("Starting proof compressor"); @@ -139,20 +138,10 @@ async fn main() -> anyhow::Result<()> { Ok(()) } -fn setup_crs_keys(config: &FriProofCompressorConfig, is_fflonk: bool) { - if is_fflonk { - download_initial_setup_keys_if_not_present( - &config.universal_fflonk_setup_path, - &config.universal_fflonk_setup_download_url, - ); - - env::set_var("COMPACT_CRS_FILE", &config.universal_fflonk_setup_path); - return; - } - +fn setup_crs_keys(config: &FriProofCompressorConfig) { download_initial_setup_keys_if_not_present( &config.universal_setup_path, &config.universal_setup_download_url, ); - env::set_var("CRS_FILE", &config.universal_setup_path); + std::env::set_var("COMPACT_CRS_FILE", &config.universal_setup_path); } diff --git a/prover/crates/bin/vk_setup_data_generator_server_fri/Cargo.toml b/prover/crates/bin/vk_setup_data_generator_server_fri/Cargo.toml index f385c33dd6ad..895d43ae42b7 100644 --- a/prover/crates/bin/vk_setup_data_generator_server_fri/Cargo.toml +++ b/prover/crates/bin/vk_setup_data_generator_server_fri/Cargo.toml @@ -40,4 +40,4 @@ proptest.workspace = true [features] default = [] -gpu = ["zksync_prover_keystore/gpu", "proof-compression-gpu", "shivini"] +gpu = ["zksync_prover_keystore/gpu", "proof-compression-gpu/allocator", "shivini"] diff --git a/prover/crates/bin/vk_setup_data_generator_server_fri/src/main.rs b/prover/crates/bin/vk_setup_data_generator_server_fri/src/main.rs index edd88846d1bc..3e5370b2c888 100644 --- a/prover/crates/bin/vk_setup_data_generator_server_fri/src/main.rs +++ b/prover/crates/bin/vk_setup_data_generator_server_fri/src/main.rs @@ -1,6 +1,5 @@ -#![feature(allocator_api)] -#![allow(dead_code)] // todo: remove after setup is generated -#![allow(unused_imports)] // todo: remove after setup is generated +#![feature(allocator_api, generic_const_exprs)] +#![allow(incomplete_features)] //! Tool to generate different types of keys used by the proving system. //! @@ -12,60 +11,41 @@ use clap::{Parser, Subcommand}; use commitment_generator::read_and_update_contract_toml; use indicatif::{ProgressBar, ProgressStyle}; #[cfg(feature = "gpu")] -use shivini::ProverContext; +use proof_compression_gpu::{ + precompute_proof_chain_with_fflonk, precompute_proof_chain_with_plonk, BlobStorageExt, +}; use tracing::level_filters::LevelFilter; use zkevm_test_harness::{ - boojum::worker::Worker, compute_setups::{ basic_vk_count, generate_base_layer_vks, generate_recursive_layer_vks, recursive_layer_vk_count, }, - data_source::{in_memory_data_source::InMemoryDataSource, SetupDataSource}, - proof_wrapper_utils::{ - check_trusted_setup_file_existace, get_wrapper_setup_and_vk_from_scheduler_vk, - WrapperConfig, - }, -}; -use zksync_prover_fri_types::{ - circuit_definitions::circuit_definitions::recursion_layer::ZkSyncRecursionLayerStorageType, - ProverServiceDataKey, + data_source::in_memory_data_source::InMemoryDataSource, }; -#[cfg(feature = "gpu")] -use zksync_prover_keystore::setup_data_generator::get_fflonk_snark_verifier_setup_and_vk; +use zksync_prover_fri_types::ProverServiceDataKey; use zksync_prover_keystore::{ keystore::Keystore, setup_data_generator::{CPUSetupDataGenerator, GPUSetupDataGenerator, SetupDataGenerator}, }; -#[cfg(feature = "gpu")] -use crate::utils::{ - generate_compression_for_wrapper_vks, generate_compression_vks, - get_plonk_wrapper_setup_and_vk_from_scheduler_vk, -}; - mod commitment_generator; -mod utils; mod vk_commitment_helper; #[cfg(test)] mod tests; + /// Generates new verification keys, and stores them in `keystore`. /// Jobs describe how many generators can run in parallel (each one is around 30 GB). /// If quiet is true, it doesn't display any progress bar. fn generate_vks(keystore: &Keystore, jobs: usize, quiet: bool) -> anyhow::Result<()> { - // Start by checking the trusted setup existence. - // This is used at the last step, but we want to fail early if user didn't configure everything - // correctly. - check_trusted_setup_file_existace(); - let progress_bar = if quiet { None } else { let count = basic_vk_count() + recursive_layer_vk_count() + 2; let progress_bar = ProgressBar::new(count as u64); progress_bar.set_style(ProgressStyle::default_bar() - .template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos:>7}/{len:7} ({eta})") - .progress_chars("#>-")); + .template("{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {pos:>7}/{len:7} ({eta})") + .progress_chars("#>-")); Some(progress_bar) }; @@ -89,66 +69,7 @@ fn generate_vks(keystore: &Keystore, jobs: usize, quiet: bool) -> anyhow::Result }) .map_err(|err| anyhow::anyhow!("Failed generating recursive vk's: {err}"))?; - #[cfg(feature = "gpu")] - { - let config = WrapperConfig::new(5); - let worker = Worker::new(); - - tracing::info!("Creating prover context"); - - let _context = ProverContext::create().context("failed initializing gpu prover context")?; - tracing::info!("Generating verification keys for compression layers."); - generate_compression_vks(config, &mut in_memory_source, &worker); - - tracing::info!("Generating verification keys for compression for wrapper."); - - generate_compression_for_wrapper_vks(config, &mut in_memory_source, &worker); - - tracing::info!("Saving keys & hints"); - } - - keystore.save_keys_from_data_source(&in_memory_source)?; - - // Generate snark VK - let scheduler_vk = in_memory_source - .get_recursion_layer_vk(ZkSyncRecursionLayerStorageType::SchedulerCircuit as u8) - .map_err(|err| anyhow::anyhow!("Failed to get scheduler vk: {err}"))?; - - tracing::info!("Generating PLONK verification keys for snark wrapper."); - - let (_, plonk_vk) = - get_wrapper_setup_and_vk_from_scheduler_vk(scheduler_vk.clone(), WrapperConfig::new(1)); - - keystore - .save_snark_verification_key(plonk_vk) - .context("save_plonk_snark_vk")?; - - if let Some(p) = pb.lock().unwrap().as_ref() { - p.inc(1) - } - - tracing::info!("PLONK vk is generated"); - - #[cfg(feature = "gpu")] - { - tracing::info!("Generating FFLONK verification keys for snark wrapper."); - - let (_, fflonk_vk) = get_fflonk_snark_verifier_setup_and_vk(&mut in_memory_source); - - keystore - .save_fflonk_snark_verification_key(fflonk_vk) - .context("save_fflonk_snark_vk")?; - - if let Some(p) = pb.lock().unwrap().as_ref() { - p.inc(1) - } - - tracing::info!("FFLONK vk is generated"); - } - - // Let's also update the commitments file. - let commitments = keystore.generate_commitments()?; - keystore.save_commitments(&commitments) + keystore.save_keys_from_data_source(&in_memory_source) } #[derive(Debug, Parser)] @@ -171,9 +92,6 @@ enum CircuitSelector { Recursive, /// Select circuits from basic group. Basic, - Compression, - CompressionWrapper, - Snark, } #[derive(Debug, Parser)] @@ -215,6 +133,10 @@ enum Command { #[arg(long)] quiet: bool, }, + #[command(name = "generate-compressor-data")] + GenerateCompressorPrecomputations, + #[command(name = "generate-crs")] + GenerateCompactCrs, /// Generates setup keys (used by the CPU prover). #[command(name = "generate-sk")] GenerateSetupKeys { @@ -280,17 +202,6 @@ fn generate_setup_keys( .numeric_circuit .expect("--numeric-circuit must be provided"), ), - CircuitSelector::Compression => ProverServiceDataKey::new_compression( - options - .numeric_circuit - .expect("--numeric-circuit must be provided"), - ), - CircuitSelector::CompressionWrapper => ProverServiceDataKey::new_compression_wrapper( - options - .numeric_circuit - .expect("--numeric-circuit must be provided"), - ), - CircuitSelector::Snark => ProverServiceDataKey::snark(), }; let digest = generator @@ -325,7 +236,6 @@ fn main() -> anyhow::Result<()> { read_and_update_contract_toml(&keystore, dryrun) } - Command::GenerateSetupKeys { options } => { let generator = CPUSetupDataGenerator { keystore: keystore_from_optional_path( @@ -344,5 +254,44 @@ fn main() -> anyhow::Result<()> { }; generate_setup_keys(&generator, &options) } + Command::GenerateCompressorPrecomputations => { + #[cfg(not(feature = "gpu"))] + { + anyhow::bail!("Must compile with --gpu feature to use this option.") + } + #[cfg(feature = "gpu")] + { + let keystore = Keystore::locate(); + precompute_proof_chain_with_plonk(&keystore); + precompute_proof_chain_with_fflonk(&keystore); + + let commitments = keystore.generate_commitments()?; + keystore.save_commitments(&commitments) + } + } + Command::GenerateCompactCrs => { + #[cfg(not(feature = "gpu"))] + { + anyhow::bail!("Must compile with --gpu feature to use this option.") + } + #[cfg(feature = "gpu")] + { + let keystore = Keystore::locate(); + + if std::env::var("COMPACT_CRS_FILE").is_err() { + return Err(anyhow::anyhow!("COMPACT_CRS_FILE env variable is not set")); + } + + if std::env::var("IGNITION_TRANSCRIPT_PATH").is_err() { + return Err(anyhow::anyhow!( + "IGNITION_TRANSCRIPT_PATH env variable is not set" + )); + } + + Ok(proof_compression_gpu::create_compact_raw_crs( + keystore.write_compact_raw_crs(), + )) + } + } } } diff --git a/prover/crates/bin/vk_setup_data_generator_server_fri/src/utils.rs b/prover/crates/bin/vk_setup_data_generator_server_fri/src/utils.rs deleted file mode 100644 index 85ce0b5be0b6..000000000000 --- a/prover/crates/bin/vk_setup_data_generator_server_fri/src/utils.rs +++ /dev/null @@ -1,141 +0,0 @@ -use circuit_definitions::{ - boojum::worker::Worker, - circuit_definitions::{ - aux_layer::{ - ZkSyncCompressionForWrapperCircuit, ZkSyncCompressionLayerCircuit, - ZkSyncCompressionLayerStorage, ZkSyncSnarkWrapperSetup, ZkSyncSnarkWrapperVK, - }, - recursion_layer::{ZkSyncRecursionLayerStorageType, ZkSyncRecursionLayerVerificationKey}, - }, -}; -#[cfg(feature = "gpu")] -use shivini::cs::gpu_setup_and_vk_from_base_setup_vk_params_and_hints; -use zkevm_test_harness::{ - data_source::{BlockDataSource, SetupDataSource}, - proof_wrapper_utils::{ - check_trusted_setup_file_existace, get_vk_for_previous_circuit, - get_wrapper_setup_and_vk_from_compression_vk, WrapperConfig, - }, - prover_utils::light::{ - create_light_compression_for_wrapper_setup_data, create_light_compression_layer_setup_data, - }, -}; - -#[cfg(feature = "gpu")] -pub(crate) fn generate_compression_vks( - config: WrapperConfig, - source: &mut DS, - worker: &Worker, -) { - for circuit_type in config.get_compression_types() { - let vk = get_vk_for_previous_circuit(source, circuit_type).unwrap_or_else(|_| { - panic!( - "VK of previous circuit should be present. Current circuit type: {}", - circuit_type - ) - }); - - let compression_circuit = - ZkSyncCompressionLayerCircuit::from_witness_and_vk(None, vk, circuit_type); - let proof_config = compression_circuit.proof_config_for_compression_step(); - - let (setup_base, vk_geometry, vars_hint, witness_hint, finalization_hint) = - create_light_compression_layer_setup_data( - compression_circuit, - worker, - proof_config.fri_lde_factor, - proof_config.merkle_tree_cap_size, - ); - - let (_, vk) = gpu_setup_and_vk_from_base_setup_vk_params_and_hints( - setup_base, - vk_geometry, - vars_hint.clone(), - witness_hint, - worker, - ) - .expect("failed creating GPU compression layer setup data"); - - source - .set_compression_vk(ZkSyncCompressionLayerStorage::from_inner( - circuit_type, - vk.clone(), - )) - .unwrap(); - source - .set_compression_hint(ZkSyncCompressionLayerStorage::from_inner( - circuit_type, - finalization_hint.clone(), - )) - .unwrap(); - } -} - -#[cfg(feature = "gpu")] -pub(crate) fn generate_compression_for_wrapper_vks( - config: WrapperConfig, - source: &mut DS, - worker: &Worker, -) { - let compression_for_wrapper_type = config.get_compression_for_wrapper_type(); - let vk = get_vk_for_previous_circuit(source, compression_for_wrapper_type).unwrap(); - - let circuit = ZkSyncCompressionForWrapperCircuit::from_witness_and_vk( - None, - vk, - compression_for_wrapper_type, - ); - - let proof_config = circuit.proof_config_for_compression_step(); - - let (setup_base, vk_geometry, vars_hint, witness_hint, finalization_hint) = - create_light_compression_for_wrapper_setup_data( - circuit, - worker, - proof_config.fri_lde_factor, - proof_config.merkle_tree_cap_size, - ); - - let (_, vk) = gpu_setup_and_vk_from_base_setup_vk_params_and_hints( - setup_base, - vk_geometry, - vars_hint.clone(), - witness_hint, - worker, - ) - .expect("failed creating GPU compression for wrapper layer setup data"); - - source - .set_compression_for_wrapper_vk(ZkSyncCompressionLayerStorage::from_inner( - compression_for_wrapper_type, - vk.clone(), - )) - .unwrap(); - source - .set_compression_for_wrapper_hint(ZkSyncCompressionLayerStorage::from_inner( - compression_for_wrapper_type, - finalization_hint.clone(), - )) - .unwrap(); -} - -/// Computes wrapper vk from scheduler vk -/// We store all vks in the RAM -pub fn get_plonk_wrapper_setup_and_vk_from_scheduler_vk( - source: &mut DS, - vk: ZkSyncRecursionLayerVerificationKey, - config: WrapperConfig, -) -> (ZkSyncSnarkWrapperSetup, ZkSyncSnarkWrapperVK) { - // Check trusted setup file for later - check_trusted_setup_file_existace(); - - // Check circuit type correctness - assert_eq!( - vk.numeric_circuit_type(), - ZkSyncRecursionLayerStorageType::SchedulerCircuit as u8 - ); - - let wrapper_type = config.get_wrapper_type(); - let wrapper_vk = source.get_compression_for_wrapper_vk(wrapper_type).unwrap(); - get_wrapper_setup_and_vk_from_compression_vk(wrapper_vk, config) -} diff --git a/prover/crates/lib/keystore/Cargo.toml b/prover/crates/lib/keystore/Cargo.toml index a247a24bdd8b..2c21067f2428 100644 --- a/prover/crates/lib/keystore/Cargo.toml +++ b/prover/crates/lib/keystore/Cargo.toml @@ -17,6 +17,7 @@ zkevm_test_harness.workspace = true circuit_definitions = { workspace = true, features = ["log_tracing"] } shivini = { workspace = true, optional = true } fflonk-gpu = { workspace = true, optional = true } +proof-compression-gpu = { workspace = true, optional = true } fflonk.workspace = true boojum-cuda = { workspace = true, optional = true } @@ -37,4 +38,4 @@ futures = { workspace = true, features = ["compat"] } default = [] # feature to not compile era-bellman-cuda, but to be able to use GPU features gpu-light = ["dep:shivini", "dep:boojum-cuda"] -gpu = ["dep:shivini", "dep:fflonk-gpu", "dep:boojum-cuda"] +gpu = ["dep:shivini", "dep:fflonk-gpu", "dep:boojum-cuda", "dep:proof-compression-gpu"] diff --git a/prover/crates/lib/keystore/src/compressor.rs b/prover/crates/lib/keystore/src/compressor.rs new file mode 100644 index 000000000000..ea4518238fd6 --- /dev/null +++ b/prover/crates/lib/keystore/src/compressor.rs @@ -0,0 +1,221 @@ +use std::{ + fs::File, + io::{Read, Write}, +}; + +use circuit_definitions::circuit_definitions::recursion_layer::ZkSyncRecursionLayerStorageType; +use zksync_prover_fri_types::ProverServiceDataKey; + +use crate::keystore::{Keystore, ProverServiceDataType}; + +const COMPACT_CRS_ENV_VAR: &str = "COMPACT_CRS_FILE"; + +impl proof_compression_gpu::BlobStorage for Keystore { + fn read_scheduler_vk(&self) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::new_recursive( + ZkSyncRecursionLayerStorageType::SchedulerCircuit as u8, + ), + ProverServiceDataType::VerificationKey, + ); + + Box::new(File::open(filepath).unwrap()) + } + + fn read_compression_layer_finalization_hint(&self, circuit_id: u8) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::new_compression(circuit_id), + ProverServiceDataType::FinalizationHints, + ); + + Box::new(File::open(filepath).unwrap()) + } + + fn read_compression_layer_vk(&self, circuit_id: u8) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::new_compression(circuit_id), + ProverServiceDataType::VerificationKey, + ); + + Box::new(File::open(filepath).unwrap()) + } + + fn read_compression_layer_precomputation(&self, circuit_id: u8) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::new_compression(circuit_id), + ProverServiceDataType::SetupData, + ); + + Box::new(File::open(filepath).unwrap()) + } + + fn read_compression_wrapper_finalization_hint(&self, circuit_id: u8) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::new_compression_wrapper(circuit_id), + ProverServiceDataType::FinalizationHints, + ); + + Box::new(File::open(filepath).unwrap()) + } + + fn read_compression_wrapper_vk(&self, circuit_id: u8) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::new_compression_wrapper(circuit_id), + ProverServiceDataType::VerificationKey, + ); + + Box::new(File::open(filepath).unwrap()) + } + + fn read_compression_wrapper_precomputation( + &self, + circuit_id: u8, + ) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::new_compression_wrapper(circuit_id), + ProverServiceDataType::SetupData, + ); + + Box::new(File::open(filepath).unwrap()) + } + + fn read_fflonk_vk(&self) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::snark(), + ProverServiceDataType::FflonkSnarkVerificationKey, + ); + + Box::new(File::open(filepath).unwrap()) + } + + fn read_fflonk_precomputation(&self) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::snark(), + ProverServiceDataType::FflonkSetupData, + ); + + Box::new(File::open(filepath).unwrap()) + } + + fn read_plonk_vk(&self) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::snark(), + ProverServiceDataType::SnarkVerificationKey, + ); + + Box::new(File::open(filepath).unwrap()) + } + + fn read_plonk_precomputation(&self) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::snark(), + ProverServiceDataType::PlonkSetupData, + ); + + Box::new(File::open(filepath).unwrap()) + } + + fn read_compact_raw_crs(&self) -> Box { + let filepath = + std::env::var(COMPACT_CRS_ENV_VAR).expect("No compact CRS file path provided"); + Box::new(File::open(filepath).unwrap()) + } +} + +impl proof_compression_gpu::BlobStorageExt for Keystore { + fn write_compression_layer_finalization_hint(&self, circuit_id: u8) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::new_compression(circuit_id), + ProverServiceDataType::FinalizationHints, + ); + + Box::new(File::create(filepath).unwrap()) + } + + fn write_compression_layer_vk(&self, circuit_id: u8) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::new_compression(circuit_id), + ProverServiceDataType::VerificationKey, + ); + + Box::new(File::create(filepath).unwrap()) + } + + fn write_compression_layer_precomputation(&self, circuit_id: u8) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::new_compression(circuit_id), + ProverServiceDataType::SetupData, + ); + + Box::new(File::create(filepath).unwrap()) + } + + fn write_compression_wrapper_finalization_hint(&self, circuit_id: u8) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::new_compression_wrapper(circuit_id), + ProverServiceDataType::FinalizationHints, + ); + + Box::new(File::create(filepath).unwrap()) + } + + fn write_compression_wrapper_vk(&self, circuit_id: u8) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::new_compression_wrapper(circuit_id), + ProverServiceDataType::VerificationKey, + ); + + Box::new(File::create(filepath).unwrap()) + } + + fn write_compression_wrapper_precomputation(&self, circuit_id: u8) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::new_compression_wrapper(circuit_id), + ProverServiceDataType::SetupData, + ); + + Box::new(File::create(filepath).unwrap()) + } + + fn write_fflonk_vk(&self) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::snark(), + ProverServiceDataType::FflonkSnarkVerificationKey, + ); + + Box::new(File::create(filepath).unwrap()) + } + + fn write_fflonk_precomputation(&self) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::snark(), + ProverServiceDataType::FflonkSetupData, + ); + + Box::new(File::create(filepath).unwrap()) + } + + fn write_plonk_vk(&self) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::snark(), + ProverServiceDataType::SnarkVerificationKey, + ); + + Box::new(File::create(filepath).unwrap()) + } + + fn write_plonk_precomputation(&self) -> Box { + let filepath = self.get_file_path( + ProverServiceDataKey::snark(), + ProverServiceDataType::PlonkSetupData, + ); + + Box::new(File::create(filepath).unwrap()) + } + + fn write_compact_raw_crs(&self) -> Box { + let filepath = + std::env::var(COMPACT_CRS_ENV_VAR).expect("No compact CRS file path provided"); + Box::new(File::create(filepath).unwrap()) + } +} diff --git a/prover/crates/lib/keystore/src/keystore.rs b/prover/crates/lib/keystore/src/keystore.rs index c4ffb32ce73c..eec7b4c8344e 100644 --- a/prover/crates/lib/keystore/src/keystore.rs +++ b/prover/crates/lib/keystore/src/keystore.rs @@ -41,6 +41,8 @@ pub enum ProverServiceDataType { VerificationKey, SetupData, FinalizationHints, + PlonkSetupData, + FflonkSetupData, SnarkVerificationKey, FflonkSnarkVerificationKey, } @@ -114,7 +116,7 @@ impl Keystore { &self.basedir } - fn get_file_path( + pub(crate) fn get_file_path( &self, key: ProverServiceDataKey, service_data_type: ProverServiceDataType, @@ -130,6 +132,12 @@ impl Keystore { ProverServiceDataType::FinalizationHints => self .basedir .join(format!("finalization_hints_{}.bin", name)), + ProverServiceDataType::PlonkSetupData => { + self.basedir.join(format!("plonk_setup_{}_data.bin", name)) + } + ProverServiceDataType::FflonkSetupData => { + self.basedir.join(format!("fflonk_setup_{}_data.bin", name)) + } ProverServiceDataType::SnarkVerificationKey => { self.basedir.join(format!("verification_{}_key.json", name)) } @@ -187,6 +195,14 @@ impl Keystore { &self, circuit_type: u8, ) -> anyhow::Result { + if circuit_type == ZkSyncRecursionLayerStorageType::SchedulerCircuit as u8 { + let vk = Self::load_json_from_file(self.get_file_path( + ProverServiceDataKey::new_recursive(circuit_type), + ProverServiceDataType::VerificationKey, + ))?; + return Ok(ZkSyncRecursionLayerVerificationKey::SchedulerCircuit(vk)); + } + Self::load_json_from_file(self.get_file_path( ProverServiceDataKey::new_recursive(circuit_type), ProverServiceDataType::VerificationKey, @@ -197,20 +213,43 @@ impl Keystore { &self, circuit_type: u8, ) -> anyhow::Result { - Self::load_json_from_file(self.get_file_path( + let key = Self::load_json_from_file(self.get_file_path( ProverServiceDataKey::new_compression(circuit_type), ProverServiceDataType::VerificationKey, - )) + ))?; + + match circuit_type { + 1 => Ok(ZkSyncCompressionLayerVerificationKey::CompressionMode1Circuit(key)), + 2 => Ok(ZkSyncCompressionLayerVerificationKey::CompressionMode2Circuit(key)), + 3 => Ok(ZkSyncCompressionLayerVerificationKey::CompressionMode3Circuit(key)), + 4 => Ok(ZkSyncCompressionLayerVerificationKey::CompressionMode4Circuit(key)), + _ => Err(anyhow::anyhow!( + "Invalid compression circuit type: {}", + circuit_type + )), + } } pub fn load_compression_for_wrapper_vk( &self, circuit_type: u8, ) -> anyhow::Result { - Self::load_json_from_file(self.get_file_path( + let key = Self::load_json_from_file(self.get_file_path( ProverServiceDataKey::new_compression_wrapper(circuit_type), ProverServiceDataType::VerificationKey, - )) + ))?; + + match circuit_type { + 1 => Ok(ZkSyncCompressionForWrapperVerificationKey::CompressionMode1Circuit(key)), + 2 => Ok(ZkSyncCompressionForWrapperVerificationKey::CompressionMode2Circuit(key)), + 3 => Ok(ZkSyncCompressionForWrapperVerificationKey::CompressionMode3Circuit(key)), + 4 => Ok(ZkSyncCompressionForWrapperVerificationKey::CompressionMode4Circuit(key)), + 5 => Ok(ZkSyncCompressionForWrapperVerificationKey::CompressionMode5Circuit(key)), + _ => Err(anyhow::anyhow!( + "Invalid compression circuit type: {}", + circuit_type + )), + } } pub fn save_base_layer_verification_key( @@ -233,6 +272,12 @@ impl Keystore { ProverServiceDataKey::new_recursive(vk.numeric_circuit_type()), ProverServiceDataType::VerificationKey, ); + + if let ZkSyncRecursionLayerVerificationKey::SchedulerCircuit(key) = vk { + tracing::info!("saving recursive layer verification key to: {:?}", filepath); + return Self::save_json_pretty(filepath, &key); + } + tracing::info!("saving recursive layer verification key to: {:?}", filepath); Self::save_json_pretty(filepath, &vk) } @@ -249,7 +294,7 @@ impl Keystore { "saving compression layer verification key to: {:?}", filepath ); - Self::save_json_pretty(filepath, &vk) + Self::save_json_pretty(filepath, &vk.into_inner()) } pub fn save_compression_for_wrapper_vk( @@ -264,7 +309,7 @@ impl Keystore { "saving compression wrapper verification key to: {:?}", filepath ); - Self::save_json_pretty(filepath, &vk) + Self::save_json_pretty(filepath, &vk.into_inner()) } /// @@ -645,45 +690,6 @@ impl Keystore { ) .context("save_finalization_hints()")?; - // Compression - // todo: don't use hardcoded values - for circuit in 1..5 { - let vk = source - .get_compression_vk(circuit as u8) - .map_err(|err| anyhow::anyhow!("No vk exist for circuit type: {circuit}: {err}"))?; - - self.save_compression_vk(vk) - .context("save_compression_vk()")?; - - let hint = source.get_compression_hint(circuit as u8).map_err(|err| { - anyhow::anyhow!("No finalization hint exist for circuit type: {circuit}: {err}") - })?; - - self.save_finalization_hints( - ProverServiceDataKey::new_compression(circuit as u8), - &hint.into_inner(), - ) - .context("save_finalization_hints()")?; - } - - // Compression wrapper - let vk = source - .get_compression_for_wrapper_vk(5) - .map_err(|err| anyhow::anyhow!("No vk exist for circuit type: 5: {err}"))?; - - self.save_compression_for_wrapper_vk(vk) - .context("save_compression_wrapper_vk()")?; - - let hint = source.get_compression_for_wrapper_hint(5).map_err(|err| { - anyhow::anyhow!("No finalization hint exist for circuit type: 5: {err}") - })?; - - self.save_finalization_hints( - ProverServiceDataKey::new_compression_wrapper(5), - &hint.into_inner(), - ) - .context("save_finalization_hints()")?; - Ok(()) } diff --git a/prover/crates/lib/keystore/src/lib.rs b/prover/crates/lib/keystore/src/lib.rs index e5f00fd307ba..bc8bbdcd992c 100644 --- a/prover/crates/lib/keystore/src/lib.rs +++ b/prover/crates/lib/keystore/src/lib.rs @@ -28,6 +28,9 @@ pub mod keystore; pub mod setup_data_generator; pub mod utils; +#[cfg(feature = "gpu")] +pub mod compressor; + #[derive(Debug, Serialize, Deserialize)] #[serde( bound = "F: serde::Serialize + serde::de::DeserializeOwned, P: serde::Serialize + serde::de::DeserializeOwned" diff --git a/prover/crates/lib/keystore/src/setup_data_generator.rs b/prover/crates/lib/keystore/src/setup_data_generator.rs index 162d94ced6dd..ae926d17ca3f 100644 --- a/prover/crates/lib/keystore/src/setup_data_generator.rs +++ b/prover/crates/lib/keystore/src/setup_data_generator.rs @@ -7,21 +7,9 @@ use anyhow::Context as _; #[cfg(any(feature = "gpu", feature = "gpu-light"))] use boojum_cuda::poseidon2::GLHasher; #[cfg(any(feature = "gpu", feature = "gpu-light"))] -use circuit_definitions::circuit_definitions::aux_layer::{ - wrapper::ZkSyncCompressionWrapper, CompressionProofsTreeHasherForWrapper, -}; -#[cfg(feature = "gpu")] -use fflonk_gpu::{ - FflonkDeviceSetup, FflonkSnarkVerifierCircuit, FflonkSnarkVerifierCircuitDeviceSetup, - FflonkSnarkVerifierCircuitVK, -}; -#[cfg(any(feature = "gpu", feature = "gpu-light"))] use shivini::cs::gpu_setup_and_vk_from_base_setup_vk_params_and_hints; #[cfg(any(feature = "gpu", feature = "gpu-light"))] -use zkevm_test_harness::{ - compute_setups::light::generate_light_circuit_setup_data, - data_source::in_memory_data_source::InMemoryDataSource, -}; +use zkevm_test_harness::compute_setups::light::generate_light_circuit_setup_data; use zkevm_test_harness::{ compute_setups::{generate_circuit_setup_data, CircuitSetupData}, data_source::SetupDataSource, @@ -55,10 +43,10 @@ pub fn generate_setup_data_common( .into_inner(), ), ProvingStage::Compression => { - unreachable!("Compression stage should not be generated with CPU.") + unreachable!("Compression stage setup data should be generated with a generate-compressor-data command") } ProvingStage::CompressionWrapper => { - unreachable!("CompressionWrapper stage should not be generated with CPU.") + unreachable!("CompressionWrapper stage setup data should be generated with a generate-compressor-data command") } _ => ( Some(keystore.load_finalization_hints(circuit)?), @@ -108,23 +96,9 @@ pub trait SetupDataGenerator { } if circuit == ProverServiceDataKey::snark() { - #[cfg(not(feature = "gpu"))] - { - anyhow::bail!("Must compile with --gpu feature to use this option."); - } - #[cfg(feature = "gpu")] - { - let mut data_source = self.keystore().load_keys_to_data_source()?; - let (setup, _) = get_fflonk_snark_verifier_setup_and_vk(&mut data_source); - if !dry_run { - self.keystore() - .save_fflonk_snark_setup_data(setup) - .context("save_setup_data()")?; - } - return Ok(String::from( - "FFLONK is serialized differently, skipping hashing.", - )); - } + unreachable!( + "Snark setup data should be generated with generate-compressor-data command" + ) } let serialized = self.generate_setup_data(circuit)?; @@ -146,7 +120,7 @@ pub trait SetupDataGenerator { dry_run: bool, recompute_if_missing: bool, ) -> anyhow::Result> { - Ok(ProverServiceDataKey::all() + Ok(ProverServiceDataKey::all_boojum() .iter() .map(|circuit| { tracing::info!("Generating setup data for {:?}", circuit.name()); @@ -206,44 +180,10 @@ impl SetupDataGenerator for GPUSetupDataGenerator { let worker = Worker::new(); match circuit.stage { - ProvingStage::CompressionWrapper => { - let (gpu_setup_data, verification_key) = - gpu_setup_and_vk_from_base_setup_vk_params_and_hints::< - CompressionProofsTreeHasherForWrapper, - _, - >( - circuit_setup_data.setup_base, - circuit_setup_data.vk_geometry, - circuit_setup_data.vars_hint.clone(), - circuit_setup_data.wits_hint, - &worker, - ) - .context("failed creating GPU base layer setup data")?; - - let gpu_prover_setup_data = GpuProverSetupData { - setup: gpu_setup_data, - vk: verification_key.clone(), - finalization_hint: circuit_setup_data.finalization_hint, - }; - - let serialized_vk = get_vk_by_circuit(self.keystore.clone(), circuit)?; - - assert_eq!( - bincode::serialize(&verification_key) - .expect("Failed serializing setup data"), - serialized_vk, - "Verification key mismatch for circuit: {:?}", - circuit.name() - ); - - // Serialization should always succeed. - Ok(bincode::serialize(&gpu_prover_setup_data) - .expect("Failed serializing setup data")) - } - ProvingStage::Snark => { - unreachable!( - "We cannot serialize Fflonk data with bincode, it is done separately" - ) + ProvingStage::CompressionWrapper + | ProvingStage::Snark + | ProvingStage::Compression => { + unreachable!("Setup data for compression, compression-wrapper and snark stages should be generated with generate-compressor-data command") } _ => { let (gpu_setup_data, verification_key) = @@ -320,31 +260,3 @@ fn get_vk_by_circuit(keystore: Keystore, circuit: ProverServiceDataKey) -> anyho } } } - -#[cfg(feature = "gpu")] -pub fn get_fflonk_snark_verifier_setup_and_vk( - data_source: &mut InMemoryDataSource, -) -> ( - FflonkSnarkVerifierCircuitDeviceSetup, - FflonkSnarkVerifierCircuitVK, -) { - let vk = data_source - .get_compression_for_wrapper_vk(5) - .unwrap() - .into_inner(); - let fixed_parameters = vk.fixed_parameters.clone(); - // todo: do not hardcode this value - let wrapper_function = ZkSyncCompressionWrapper::from_numeric_circuit_type(5); - - let circuit = FflonkSnarkVerifierCircuit { - witness: None, - vk, - fixed_parameters, - transcript_params: (), - wrapper_function, - }; - let setup = FflonkDeviceSetup::<_, _, _>::create_setup_on_device(&circuit).unwrap(); - let snark_vk = setup.get_verification_key(); - - (setup, snark_vk) -} diff --git a/prover/crates/lib/prover_fri_types/src/lib.rs b/prover/crates/lib/prover_fri_types/src/lib.rs index 9d7c32c21d73..5b7a54a31052 100644 --- a/prover/crates/lib/prover_fri_types/src/lib.rs +++ b/prover/crates/lib/prover_fri_types/src/lib.rs @@ -294,12 +294,6 @@ impl ProverServiceDataKey { for numeric_circuit in ZkSyncRecursionLayerStorageType::as_iter_u8() { results.push(ProverServiceDataKey::new_recursive(numeric_circuit)) } - - for numeric_circuit in 1..MAX_COMPRESSION_CIRCUITS { - results.push(ProverServiceDataKey::new_compression(numeric_circuit)); - } - results.push(ProverServiceDataKey::new_compression_wrapper(5)); - results } @@ -311,12 +305,6 @@ impl ProverServiceDataKey { } } - pub fn all() -> Vec { - let mut keys = Self::all_boojum(); - keys.push(Self::snark()); - keys - } - pub fn is_base_layer(&self) -> bool { self.stage == ProvingStage::BasicCircuits } diff --git a/prover/data/keys/finalization_hints_compression_1.bin b/prover/data/keys/finalization_hints_compression_1.bin index 8f5cadc55c5763890b983eb7c20f96124a014c28..d71029042a994cd20811d5c2b29f799c5a275379 100644 GIT binary patch literal 373 zcmbVI(F(#a41C{LggyIU+pVC#A|uqTYN4iOYY`Ox-L944gG2BoB-~wcmq!IqkM4#i zL&}=F3dRv$427xQ!y%#Yt^i__M}X~rO;&fQcC)_josCyoIb0~@Jf2n169$KB7?LH< z8@9{(>=?tSdW}|7!erYDnK2)a*7Rt|$1gz^|BYZw7F#7!2@Jr>2(ZfFKN-urK6cFE GtLF=Aiccy4 literal 120 tcmZQ%fB+6C%?zbs)Z}m&ixJFVfpMX9&I$+vBFn%4<1?Yj!_0-r0|4JI1yTS2 diff --git a/prover/data/keys/finalization_hints_compression_2.bin b/prover/data/keys/finalization_hints_compression_2.bin index c6a4253d81e61be5664c6c38d3d1269e0503fd8b..5dc0776779f414ecd38a95d3a32e39acec3caf6b 100644 GIT binary patch literal 367 zcmbV|%?`pK5QOi2icQZRMEt3~ifJ}TQAuziEyP6QyUUL;J;=q&1~N13d^{U~cC>dq zYZ693gx8kvs>x670gh3H&jetLd<9tkcgX2J&OYT^X-&9MWpS~Pb$Ah8P3SF(VThI3 zdeh~2E{uK@QlU{4rH<`aS#sfIv_wrV{J{9O-|jdsoeEE(opII-c}x14nt_)G=Z1vc FyxxU$P7?qC literal 120 pcmZQ%fB+6C%?PDo)FxIaO94W&z$BowuM~s<(+A@-p~*9&@d1K!0*C+r diff --git a/prover/data/keys/finalization_hints_compression_3.bin b/prover/data/keys/finalization_hints_compression_3.bin index ef3f24e18c23dcaff87a7e9715352a2bc3c516c0..f8148a2cb2ee7c6e3bad411a366a757c4f9eee18 100644 GIT binary patch literal 366 zcmbVHK?=e!5WM#lOV1vfwpi*{q%1Lwx)3*^O%N3SZkmV}ErOR}hnZn#?gcl|cj|C8!0<0P8Uxwz>u&gc~Pc`aI A(f|Me literal 120 ncmZQ%fB+6C%?_nu)H)bL0K#B_@u74eKZL=E#%Ds~Go$eVd5i*C diff --git a/prover/data/keys/finalization_hints_compression_4.bin b/prover/data/keys/finalization_hints_compression_4.bin index ca6c08bab1f180057124b5423fface7b6a9ed134..d15a487c3572bdf212cf3d101211548e328cd1a8 100644 GIT binary patch literal 18934 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 A4*&oF literal 2024 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 o-TjBp|MvqDYuU-rA`W9@<#ly>QOnfSlu~9B zLpn+!)@$-Uws6KvVOC}tqMBA{sMNOS#P@d2-aqd1eD3#m&$-Xby_ZayKa>|*G$EAv zUd+OUi)U?jyR2irR$H)967SLoMcxS)lT#deDIs2x}m+NAHB+wTBGUHdxrf&-xf{`ZaZOc*;TT zP^wNlvHruH+VZe(9hM-cIX+a~n$y${@8F*CjlIvm26Olt9n`i{qp)dMQUv7=qY=Iy z*Gpw=uO-H(!VbTw6q0L(d^RSqD`4(S4ZO-YKWbdFcNW$=-V`P2-(?R+L>LIJmIYYC zM_q0hM$%_#uv>O|MnihrJGiLs-)n_mR}#-NBpg>UcwR}&SE8h88L4VC0ri|?_hx(! zj*DSQP3XO#AS$sQN`bcdtW%$e{ZTn%867&x+>d&88r@15mr)H*r&*ug7x?};+*%~1 z^WHYvz~aeF<}w=dDXcfErRkwuI|%n1Xq3CE?KXzTq~AN<(kZ+G%cHJvQbV)9g44E~ zpES6iM$B)7QQ6Glj!RpRtA0o}94v9vhBabx!%Q?UAA_IL=130VEMk91-4aB~Imzpg z@A)OogDs<0!G-+mA}=HU30S?V)vD&wM=mVWVcMz8oOlTzX=Yk3@yzyyB`acz6I>-s z*wM#rl`>n47|(krX}i*z+?B|;P^J!T?~R&-b?=UE8d3@=gePs-59)X9i-2#7g{Jpg z7jJ`0`)E!{y0H`RH>dMbU!MOE35%C^Us>8+9R#;~_sDP7m=Wu3_JUL~YFa;yeCOZ4 z+6B*^EQ3SE`OFL@mm{#KBe7J6n@BuQJj+6lPMs@3Zr#$be5l1=3ahFPrc*Y*Du8JL zlPS6W!Rqh@PeHVP{-_LAYxc8$q;-b_ceC>3#d^~-@Uo;H^R=<-Q(#Wgg}JS=iuLei zy@Qr5O(%%&+bQlv#>A~0DsryRK=X42@)+Fj)b;V_t`uFkE3oq)r&J#Celt=Hg(J36-2`hvfLoiZH;PQrooKV-$LY=49e61v2DGt(Vm z9r^)-49dg+Y?N3!!t9RKg4aA*GwduZT@K^F>l}$5`EzFxxX>1J6+ZXo&m, - #[clap(long)] - pub fflonk_path: Option, - #[clap(long, default_value = "plonk")] - pub compressor_type: CompressorType, -} - -#[derive(Debug, Clone, ValueEnum, EnumIter, strum::Display, PartialEq, Eq, Default)] -pub enum CompressorType { - Fflonk, - #[default] - Plonk, - All, + pub path: Option, } impl CompressorKeysArgs { - pub fn fill_values_with_prompt( - self, - default_plonk_path: &str, - default_fflonk_path: &str, - ) -> CompressorKeysArgs { - let plonk_path = if self.compressor_type != CompressorType::Fflonk { - Some(self.plonk_path.unwrap_or_else(|| { - Prompt::new(MSG_SETUP_COMPRESSOR_KEY_PATH_PROMPT) - .default(default_plonk_path) - .ask() - })) - } else { - None - }; - - let fflonk_path = if self.compressor_type != CompressorType::Plonk { - Some(self.fflonk_path.unwrap_or_else(|| { - Prompt::new(MSG_SETUP_COMPRESSOR_KEY_PATH_PROMPT) - .default(default_fflonk_path) - .ask() - })) - } else { - None - }; + pub fn fill_values_with_prompt(self, default_path: &str) -> CompressorKeysArgs { + let path = self.path.unwrap_or_else(|| { + Prompt::new(MSG_SETUP_COMPRESSOR_KEY_PATH_PROMPT) + .default(default_path) + .ask() + }); - CompressorKeysArgs { - plonk_path, - fflonk_path, - ..self - } + CompressorKeysArgs { path: Some(path) } } } diff --git a/zkstack_cli/crates/zkstack/src/commands/prover/args/init.rs b/zkstack_cli/crates/zkstack/src/commands/prover/args/init.rs index afe57a8f5eb4..4956a23ac987 100644 --- a/zkstack_cli/crates/zkstack/src/commands/prover/args/init.rs +++ b/zkstack_cli/crates/zkstack/src/commands/prover/args/init.rs @@ -202,16 +202,13 @@ impl ProverInitArgs { pub(crate) fn fill_values_with_prompt( &self, shell: &Shell, - default_plonk_key_path: &str, - default_fflonk_key_path: &str, + default_compressor_key_path: &str, chain_config: &ChainConfig, ) -> anyhow::Result { let proof_store = self.fill_proof_storage_values_with_prompt(shell)?; let public_store = self.fill_public_storage_values_with_prompt(shell)?; - let compressor_key_args = self.fill_setup_compressor_key_values_with_prompt( - default_plonk_key_path, - default_fflonk_key_path, - ); + let compressor_key_args = + self.fill_setup_compressor_key_values_with_prompt(default_compressor_key_path); let bellman_cuda_config = self.fill_bellman_cuda_values_with_prompt(); let cloud_type = self.get_cloud_type_with_prompt(); let database_config = self.fill_database_values_with_prompt(chain_config); @@ -358,14 +355,11 @@ impl ProverInitArgs { fn fill_setup_compressor_key_values_with_prompt( &self, - default_plonk_path: &str, - default_fflonk_path: &str, + default_path: &str, ) -> Option { if self.dev { return Some(CompressorKeysArgs { - plonk_path: Some(default_plonk_path.to_string()), - fflonk_path: Some(default_fflonk_path.to_string()), - ..self.compressor_keys_args.clone() + path: Some(default_path.to_string()), }); } @@ -379,7 +373,7 @@ impl ProverInitArgs { Some( self.compressor_keys_args .clone() - .fill_values_with_prompt(default_plonk_path, default_fflonk_path), + .fill_values_with_prompt(default_path), ) } else { None diff --git a/zkstack_cli/crates/zkstack/src/commands/prover/compressor_keys.rs b/zkstack_cli/crates/zkstack/src/commands/prover/compressor_keys.rs index 19ba634a4075..88eec0688da7 100644 --- a/zkstack_cli/crates/zkstack/src/commands/prover/compressor_keys.rs +++ b/zkstack_cli/crates/zkstack/src/commands/prover/compressor_keys.rs @@ -3,7 +3,7 @@ use xshell::Shell; use zkstack_cli_common::{logger, spinner::Spinner}; use zkstack_cli_config::{get_link_to_prover, EcosystemConfig, GeneralConfig}; -use super::args::compressor_keys::{CompressorKeysArgs, CompressorType}; +use super::args::compressor_keys::CompressorKeysArgs; use crate::messages::{ MSG_CHAIN_NOT_FOUND_ERR, MSG_DOWNLOADING_SETUP_COMPRESSOR_KEY_SPINNER, MSG_PROOF_COMPRESSOR_CONFIG_NOT_FOUND_ERR, MSG_SETUP_KEY_PATH_ERROR, @@ -16,39 +16,12 @@ pub(crate) async fn run(shell: &Shell, args: CompressorKeysArgs) -> anyhow::Resu .context(MSG_CHAIN_NOT_FOUND_ERR)?; let mut general_config = chain_config.get_general_config()?; - let default_plonk_path = get_default_plonk_compressor_keys_path(&ecosystem_config)?; - let default_fflonk_path = get_default_fflonk_compressor_keys_path(&ecosystem_config)?; - let args = args.fill_values_with_prompt(&default_plonk_path, &default_fflonk_path); - - match args.compressor_type { - CompressorType::Fflonk => { - let path = args.clone().fflonk_path.context(MSG_SETUP_KEY_PATH_ERROR)?; - - download_compressor_key(shell, &mut general_config, CompressorType::Fflonk, &path)?; - } - CompressorType::Plonk => { - let path = args.plonk_path.context(MSG_SETUP_KEY_PATH_ERROR)?; - - download_compressor_key(shell, &mut general_config, CompressorType::Plonk, &path)?; - } - CompressorType::All => { - let plonk_path = args.clone().plonk_path.context(MSG_SETUP_KEY_PATH_ERROR)?; - let fflonk_path = args.clone().fflonk_path.context(MSG_SETUP_KEY_PATH_ERROR)?; - - download_compressor_key( - shell, - &mut general_config, - CompressorType::Fflonk, - &fflonk_path, - )?; - download_compressor_key( - shell, - &mut general_config, - CompressorType::Plonk, - &plonk_path, - )?; - } - } + let default_path = get_default_compressor_keys_path(&ecosystem_config)?; + let args = args.fill_values_with_prompt(&default_path); + + let path = args.path.context(MSG_SETUP_KEY_PATH_ERROR)?; + + download_compressor_key(shell, &mut general_config, &path)?; chain_config.save_general_config(&general_config)?; @@ -58,7 +31,6 @@ pub(crate) async fn run(shell: &Shell, args: CompressorKeysArgs) -> anyhow::Resu pub(crate) fn download_compressor_key( shell: &Shell, general_config: &mut GeneralConfig, - r#type: CompressorType, path: &str, ) -> anyhow::Result<()> { let spinner = Spinner::new(MSG_DOWNLOADING_SETUP_COMPRESSOR_KEY_SPINNER); @@ -68,50 +40,35 @@ pub(crate) fn download_compressor_key( .expect(MSG_PROOF_COMPRESSOR_CONFIG_NOT_FOUND_ERR) .clone(); - let url = match r#type { - CompressorType::Fflonk => { - compressor_config.universal_fflonk_setup_path = path.to_string(); - general_config.proof_compressor_config = Some(compressor_config.clone()); - compressor_config.universal_fflonk_setup_download_url - } - CompressorType::Plonk => { - compressor_config.universal_setup_path = path.to_string(); - general_config.proof_compressor_config = Some(compressor_config.clone()); - compressor_config.universal_setup_download_url - } - _ => unreachable!("Invalid compressor type"), - }; + compressor_config.universal_setup_path = path.to_string(); + general_config.proof_compressor_config = Some(compressor_config.clone()); let path = std::path::Path::new(path); - logger::info(format!("Downloading setup key by URL: {}", url)); + logger::info(format!( + "Downloading setup key by URL: {}", + compressor_config.universal_setup_download_url + )); let client = reqwest::blocking::Client::builder() .timeout(std::time::Duration::from_secs(600)) .build()?; - let response = client.get(url).send()?.bytes()?; + let response = client + .get(compressor_config.universal_setup_download_url) + .send()? + .bytes()?; shell.write_file(path, &response)?; spinner.finish(); Ok(()) } -pub fn get_default_plonk_compressor_keys_path( - ecosystem_config: &EcosystemConfig, -) -> anyhow::Result { - let link_to_prover = get_link_to_prover(ecosystem_config); - let path = link_to_prover.join("keys/setup/setup_2^24.key"); - let string = path.to_str().unwrap(); - - Ok(String::from(string)) -} - -pub fn get_default_fflonk_compressor_keys_path( +pub fn get_default_compressor_keys_path( ecosystem_config: &EcosystemConfig, ) -> anyhow::Result { let link_to_prover = get_link_to_prover(ecosystem_config); - let path = link_to_prover.join("keys/setup/setup_fflonk_compact.key"); + let path = link_to_prover.join("keys/setup/setup_compact.key"); let string = path.to_str().unwrap(); Ok(String::from(string)) diff --git a/zkstack_cli/crates/zkstack/src/commands/prover/init.rs b/zkstack_cli/crates/zkstack/src/commands/prover/init.rs index b318f4924ec3..51034e02a213 100644 --- a/zkstack_cli/crates/zkstack/src/commands/prover/init.rs +++ b/zkstack_cli/crates/zkstack/src/commands/prover/init.rs @@ -24,10 +24,7 @@ use super::{ }; use crate::{ commands::prover::{ - args::{compressor_keys::CompressorType, init::ProofStorageFileBacked}, - compressor_keys::{ - get_default_fflonk_compressor_keys_path, get_default_plonk_compressor_keys_path, - }, + args::init::ProofStorageFileBacked, compressor_keys::get_default_compressor_keys_path, }, consts::{PROVER_MIGRATIONS, PROVER_STORE_MAX_RETRIES}, messages::{ @@ -41,18 +38,12 @@ use crate::{ pub(crate) async fn run(args: ProverInitArgs, shell: &Shell) -> anyhow::Result<()> { let ecosystem_config = EcosystemConfig::from_file(shell)?; - let default_plonk_key_path = get_default_plonk_compressor_keys_path(&ecosystem_config)?; - let default_fflonk_key_path = get_default_fflonk_compressor_keys_path(&ecosystem_config)?; + let default_compressor_key_path = get_default_compressor_keys_path(&ecosystem_config)?; let chain_config = ecosystem_config .load_current_chain() .context(MSG_CHAIN_NOT_FOUND_ERR)?; - let args = args.fill_values_with_prompt( - shell, - &default_plonk_key_path, - &default_fflonk_key_path, - &chain_config, - )?; + let args = args.fill_values_with_prompt(shell, &default_compressor_key_path, &chain_config)?; if chain_config.get_general_config().is_err() || chain_config.get_secrets_config().is_err() { copy_configs(shell, &ecosystem_config.link_to_code, &chain_config.configs)?; @@ -66,35 +57,9 @@ pub(crate) async fn run(args: ProverInitArgs, shell: &Shell) -> anyhow::Result<( let public_object_store_config = get_object_store_config(shell, args.public_store)?; if let Some(args) = args.compressor_key_args { - match args.compressor_type { - CompressorType::Fflonk => { - let path = args.clone().fflonk_path.context(MSG_SETUP_KEY_PATH_ERROR)?; - - download_compressor_key(shell, &mut general_config, CompressorType::Fflonk, &path)?; - } - CompressorType::Plonk => { - let path = args.plonk_path.context(MSG_SETUP_KEY_PATH_ERROR)?; - - download_compressor_key(shell, &mut general_config, CompressorType::Plonk, &path)?; - } - CompressorType::All => { - let fflonk_path = args.clone().fflonk_path.context(MSG_SETUP_KEY_PATH_ERROR)?; - let plonk_path = args.clone().plonk_path.context(MSG_SETUP_KEY_PATH_ERROR)?; - - download_compressor_key( - shell, - &mut general_config, - CompressorType::Fflonk, - &fflonk_path, - )?; - download_compressor_key( - shell, - &mut general_config, - CompressorType::Plonk, - &plonk_path, - )?; - } - } + let path = args.path.context(MSG_SETUP_KEY_PATH_ERROR)?; + + download_compressor_key(shell, &mut general_config, &path)?; } if let Some(args) = args.setup_keys { From 0b1397eab266602cfc20356f536267a193f6fc80 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Thu, 23 Jan 2025 14:42:12 -0300 Subject: [PATCH 86/97] create temp file inside closure --- .../node/da_clients/src/eigen/verifier/mod.rs | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/core/node/da_clients/src/eigen/verifier/mod.rs b/core/node/da_clients/src/eigen/verifier/mod.rs index 334a4713419e..cb6ea07ff5fe 100644 --- a/core/node/da_clients/src/eigen/verifier/mod.rs +++ b/core/node/da_clients/src/eigen/verifier/mod.rs @@ -171,6 +171,7 @@ impl Verifier { let response = reqwest::get(url) .await .map_err(|e| VerificationError::PointDownloadError(e.to_string()))?; + if !response.status().is_success() { return Err(VerificationError::PointDownloadError(format!( "Failed to download point from source {}", @@ -178,15 +179,28 @@ impl Verifier { ))); } - let mut file = NamedTempFile::new() - .map_err(|e| VerificationError::PointDownloadError(e.to_string()))?; let content = response .bytes() .await .map_err(|e| VerificationError::PointDownloadError(e.to_string()))?; - file.write_all(&content) - .map_err(|e| VerificationError::PointDownloadError(e.to_string()))?; - Ok(file) + + // Tempfile writting uses `std::fs`, so we need to spawn a blocking task + let temp_file = tokio::task::spawn_blocking(move || { + let mut file = NamedTempFile::new() + .map_err(|e| VerificationError::PointDownloadError(e.to_string()))?; + + file.write_all(&content) + .map_err(|e| VerificationError::PointDownloadError(e.to_string()))?; + + file.flush() + .map_err(|e| VerificationError::PointDownloadError(e.to_string()))?; + + Ok::(file) + }) + .await + .map_err(|e| VerificationError::PointDownloadError(e.to_string()))??; + + Ok::(temp_file) } async fn get_points(cfg: &EigenConfig) -> Result<(PointFile, PointFile), VerificationError> { From bfcd2069a4035fa78bf1c509d89a1f005d0d42dd Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 24 Jan 2025 17:20:24 -0300 Subject: [PATCH 87/97] add EigenDA to validium init cfg --- zkstack_cli/crates/config/src/chain.rs | 4 +++- .../crates/zkstack/src/commands/chain/args/init/da_configs.rs | 3 +++ .../crates/zkstack/src/commands/chain/args/init/mod.rs | 1 + zkstack_cli/crates/zkstack/src/commands/chain/init/configs.rs | 1 + zkstack_cli/crates/zkstack/src/commands/chain/init/mod.rs | 3 +++ 5 files changed, 11 insertions(+), 1 deletion(-) diff --git a/zkstack_cli/crates/config/src/chain.rs b/zkstack_cli/crates/config/src/chain.rs index 19f275d78f80..ec46fd3f5c30 100644 --- a/zkstack_cli/crates/config/src/chain.rs +++ b/zkstack_cli/crates/config/src/chain.rs @@ -9,7 +9,7 @@ use zkstack_cli_types::{BaseToken, L1BatchCommitmentMode, L1Network, ProverMode, use zksync_basic_types::L2ChainId; use zksync_config::{ configs::{gateway::GatewayChainConfig, GatewayConfig}, - DAClientConfig::{Avail, NoDA}, + DAClientConfig::{Avail, Eigen, NoDA}, }; use crate::{ @@ -75,6 +75,7 @@ pub enum DAValidatorType { Rollup = 0, NoDA = 1, Avail = 2, + EigenDA = 3, } impl Serialize for ChainConfig { @@ -121,6 +122,7 @@ impl ChainConfig { (L1BatchCommitmentMode::Rollup, _) => Ok(DAValidatorType::Rollup), (L1BatchCommitmentMode::Validium, None | Some(NoDA)) => Ok(DAValidatorType::NoDA), (L1BatchCommitmentMode::Validium, Some(Avail(_))) => Ok(DAValidatorType::Avail), + (L1BatchCommitmentMode::Validium, Some(Eigen(_))) => Ok(DAValidatorType::EigenDA), _ => anyhow::bail!("DAValidatorType is not supported"), } } diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/args/init/da_configs.rs b/zkstack_cli/crates/zkstack/src/commands/chain/args/init/da_configs.rs index 4d0e97e7ef05..7a6466b802a7 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/args/init/da_configs.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/args/init/da_configs.rs @@ -31,6 +31,7 @@ pub struct ValidiumTypeArgs { pub enum ValidiumTypeInternal { NoDA, Avail, + EigenDA, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, EnumIter, Display, ValueEnum)] @@ -43,6 +44,7 @@ pub enum AvailClientTypeInternal { pub enum ValidiumType { NoDA, Avail((AvailConfig, AvailSecrets)), + EigenDA, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, EnumIter, Display, ValueEnum)] @@ -54,6 +56,7 @@ pub enum AvailFinalityState { impl ValidiumType { pub fn read() -> Self { match PromptSelect::new(MSG_VALIDIUM_TYPE_PROMPT, ValidiumTypeInternal::iter()).ask() { + ValidiumTypeInternal::EigenDA => ValidiumType::EigenDA, ValidiumTypeInternal::NoDA => ValidiumType::NoDA, ValidiumTypeInternal::Avail => { let avail_client_type = PromptSelect::new( diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/args/init/mod.rs b/zkstack_cli/crates/zkstack/src/commands/chain/args/init/mod.rs index 2d8539620ef0..770ac50996a2 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/args/init/mod.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/args/init/mod.rs @@ -95,6 +95,7 @@ impl InitArgs { Some(da_configs::ValidiumTypeInternal::Avail) => panic!( "Avail is not supported via CLI args, use interactive mode" // TODO: Add support for configuration via CLI args ), + Some(da_configs::ValidiumTypeInternal::EigenDA) => Some(ValidiumType::EigenDA), }, _ => None, }; diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/init/configs.rs b/zkstack_cli/crates/zkstack/src/commands/chain/init/configs.rs index 4db4c9927de1..eff256284d13 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/init/configs.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/init/configs.rs @@ -97,6 +97,7 @@ pub async fn init_configs( general_config.da_client_config = Some(avail_config.into()); secrets.data_availability = Some(DataAvailabilitySecrets::Avail(avail_secrets)); } + ValidiumType::EigenDA => {} // This is left blank to be able to define the config by file instead of giving it via CLI } } diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/init/mod.rs b/zkstack_cli/crates/zkstack/src/commands/chain/init/mod.rs index f115048d1181..d4e5fbab85c9 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/init/mod.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/init/mod.rs @@ -256,6 +256,9 @@ pub(crate) fn get_l1_da_validator(chain_config: &ChainConfig) -> anyhow::Result< match da_client_config { DAClientConfig::Avail(_) => contracts_config.l1.avail_l1_da_validator_addr, DAClientConfig::NoDA => contracts_config.l1.no_da_validium_l1_validator_addr, + DAClientConfig::Eigen(_) => { + contracts_config.l1.no_da_validium_l1_validator_addr + } // TODO: change to EigenDA for M1 implementation _ => anyhow::bail!("DA client config is not supported"), } } else { From 278975cf0445e24170e9e0832aa8b344de57f176 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 24 Jan 2025 19:33:47 -0300 Subject: [PATCH 88/97] update contracts submodule --- contracts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts b/contracts index 16dedf6d7769..6210c7f1cfc1 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 16dedf6d77695ce00f81fce35a3066381b97fca1 +Subproject commit 6210c7f1cfc1d3dd5229c60cf985c11810e6f3ca From 56cda84d30f9aeb2ceba49855ad1e1d53551cb4f Mon Sep 17 00:00:00 2001 From: Gianbelinche <39842759+gianbelinche@users.noreply.github.com> Date: Mon, 27 Jan 2025 15:20:51 -0300 Subject: [PATCH 89/97] Remove unnecessary clone --- core/node/da_clients/src/eigen/sdk.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/node/da_clients/src/eigen/sdk.rs b/core/node/da_clients/src/eigen/sdk.rs index bc6cd12027de..41842581052c 100644 --- a/core/node/da_clients/src/eigen/sdk.rs +++ b/core/node/da_clients/src/eigen/sdk.rs @@ -287,7 +287,7 @@ impl RawEigenClient { let resp = self .client .clone() - .get_blob_status(polling_request.clone()) + .get_blob_status(polling_request) .await? .into_inner(); From cfba485ba28776f2c412d5aa0390a73420785c00 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Mon, 27 Jan 2025 15:42:28 -0300 Subject: [PATCH 90/97] add eigenda validator --- contracts | 2 +- zkstack_cli/crates/config/src/contracts.rs | 8 ++++++++ .../src/forge_interface/deploy_ecosystem/input.rs | 3 +++ .../src/forge_interface/deploy_ecosystem/output.rs | 1 + zkstack_cli/crates/types/src/l1_network.rs | 11 +++++++++++ .../crates/zkstack/src/commands/chain/init/mod.rs | 4 +--- 6 files changed, 25 insertions(+), 4 deletions(-) diff --git a/contracts b/contracts index 6210c7f1cfc1..3e2b628be66d 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 6210c7f1cfc1d3dd5229c60cf985c11810e6f3ca +Subproject commit 3e2b628be66d2cb200d542dd71d6f19788ad8037 diff --git a/zkstack_cli/crates/config/src/contracts.rs b/zkstack_cli/crates/config/src/contracts.rs index eb88cf3af854..f8b7b212ea4a 100644 --- a/zkstack_cli/crates/config/src/contracts.rs +++ b/zkstack_cli/crates/config/src/contracts.rs @@ -114,6 +114,11 @@ impl ContractsConfig { .deployed_addresses .avail_l1_da_validator_addr, ); + self.l1.eigenda_l1_validator_addr = Some( + deploy_l1_output + .deployed_addresses + .eigenda_l1_validator_addr, + ); self.l1.chain_admin_addr = deploy_l1_output.deployed_addresses.chain_admin; } @@ -252,6 +257,9 @@ pub struct L1Contracts { // `Option` to be able to parse configs from pre-gateway protocol version. #[serde(skip_serializing_if = "Option::is_none")] pub no_da_validium_l1_validator_addr: Option
, + // `Option` to be able to parse configs from previous protocol version + #[serde(skip_serializing_if = "Option::is_none")] + pub eigenda_l1_validator_addr: Option
, // `Option` to be able to parse configs from pre-gateway protocol version. #[serde(skip_serializing_if = "Option::is_none")] pub transaction_filterer_addr: Option
, diff --git a/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/input.rs b/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/input.rs index 47fe66143250..5cd67198aa70 100644 --- a/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/input.rs +++ b/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/input.rs @@ -168,6 +168,7 @@ impl DeployL1Config { validator_timelock_execution_delay: initial_deployment_config .validator_timelock_execution_delay, avail_l1_da_validator_addr: l1_network.avail_l1_da_validator_addr(), + eigenda_l1_validator_addr: l1_network.eigenda_l1_validator_addr(), }, tokens: TokensDeployL1Config { token_weth_address: initial_deployment_config.token_weth_address, @@ -204,6 +205,8 @@ pub struct ContractsDeployL1Config { pub evm_emulator_hash: Option, #[serde(skip_serializing_if = "Option::is_none")] pub avail_l1_da_validator_addr: Option
, + #[serde(skip_serializing_if = "Option::is_none")] + pub eigenda_l1_validator_addr: Option
, } #[derive(Debug, Deserialize, Serialize, Clone)] diff --git a/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/output.rs b/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/output.rs index a0bca69cafd5..9294cba6ef8c 100644 --- a/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/output.rs +++ b/zkstack_cli/crates/config/src/forge_interface/deploy_ecosystem/output.rs @@ -36,6 +36,7 @@ pub struct DeployL1DeployedAddressesOutput { pub rollup_l1_da_validator_addr: Address, pub no_da_validium_l1_validator_addr: Address, pub avail_l1_da_validator_addr: Address, + pub eigenda_l1_validator_addr: Address, pub l1_rollup_da_manager: Address, pub native_token_vault_addr: Address, } diff --git a/zkstack_cli/crates/types/src/l1_network.rs b/zkstack_cli/crates/types/src/l1_network.rs index 609af7ef3e7c..0578f685b6ec 100644 --- a/zkstack_cli/crates/types/src/l1_network.rs +++ b/zkstack_cli/crates/types/src/l1_network.rs @@ -48,4 +48,15 @@ impl L1Network { L1Network::Mainnet => None, // TODO: add mainnet address after it is known } } + + pub fn eigenda_l1_validator_addr(&self) -> Option
{ + match self { + L1Network::Localhost => None, + L1Network::Sepolia | L1Network::Holesky => { + None + //TODO: add real address + } + L1Network::Mainnet => None, // TODO: add mainnet address after it is known + } + } } diff --git a/zkstack_cli/crates/zkstack/src/commands/chain/init/mod.rs b/zkstack_cli/crates/zkstack/src/commands/chain/init/mod.rs index d4e5fbab85c9..938c24a8ac1b 100644 --- a/zkstack_cli/crates/zkstack/src/commands/chain/init/mod.rs +++ b/zkstack_cli/crates/zkstack/src/commands/chain/init/mod.rs @@ -256,9 +256,7 @@ pub(crate) fn get_l1_da_validator(chain_config: &ChainConfig) -> anyhow::Result< match da_client_config { DAClientConfig::Avail(_) => contracts_config.l1.avail_l1_da_validator_addr, DAClientConfig::NoDA => contracts_config.l1.no_da_validium_l1_validator_addr, - DAClientConfig::Eigen(_) => { - contracts_config.l1.no_da_validium_l1_validator_addr - } // TODO: change to EigenDA for M1 implementation + DAClientConfig::Eigen(_) => contracts_config.l1.eigenda_l1_validator_addr, _ => anyhow::bail!("DA client config is not supported"), } } else { From b0b080d2c0831bbadcf7b9a52055d090b264b1f1 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Mon, 27 Jan 2025 16:56:51 -0300 Subject: [PATCH 91/97] address comments --- core/node/da_clients/src/eigen/sdk.rs | 7 ++--- .../node/da_clients/src/eigen/verifier/mod.rs | 26 +++++++++++++++---- .../da_clients/src/eigen/verifier/tests.rs | 3 ++- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/core/node/da_clients/src/eigen/sdk.rs b/core/node/da_clients/src/eigen/sdk.rs index 41842581052c..1bf47cb147df 100644 --- a/core/node/da_clients/src/eigen/sdk.rs +++ b/core/node/da_clients/src/eigen/sdk.rs @@ -55,9 +55,10 @@ impl RawEigenClient { .await .map_err(ConfigError::Tonic)?; - let rpc_url = cfg.eigenda_eth_rpc.clone().unwrap(); - // TODO: remove unwrap - // .ok_or(anyhow::anyhow!("EigenDA ETH RPC not set"))?; + let rpc_url = cfg + .eigenda_eth_rpc + .clone() + .ok_or(EthClientError::Rpc("EigenDA ETH RPC not set".to_string()))?; let query_client: Client = Client::http(rpc_url) .map_err(|e| EthClientError::Rpc(e.to_string()))? .build(); diff --git a/core/node/da_clients/src/eigen/verifier/mod.rs b/core/node/da_clients/src/eigen/verifier/mod.rs index cb6ea07ff5fe..e97dc4dd5999 100644 --- a/core/node/da_clients/src/eigen/verifier/mod.rs +++ b/core/node/da_clients/src/eigen/verifier/mod.rs @@ -7,7 +7,7 @@ use tempfile::NamedTempFile; use zksync_basic_types::web3::CallRequest; use zksync_config::EigenConfig; use zksync_eth_client::EthInterface; -use zksync_types::{web3, Address, U256}; +use zksync_types::{web3, Address, U256, U64}; use zksync_web3_decl::client::{DynClient, L1}; use super::{ @@ -39,6 +39,7 @@ pub trait VerifierClient: Sync + Send + std::fmt::Debug { &self, batch_id: u32, svc_manager_addr: Address, + settlement_layer_confirmation_depth: Option, ) -> Result, VerificationError>; /// Request to the EigenDA service manager contract @@ -63,6 +64,7 @@ impl VerifierClient for Box> { &self, batch_id: u32, svc_manager_addr: Address, + settlement_layer_confirmation_depth: Option, ) -> Result, VerificationError> { let mut data = vec![]; let func_selector = @@ -77,9 +79,22 @@ impl VerifierClient for Box> { ..Default::default() }; + let block_id = match settlement_layer_confirmation_depth { + Some(depth) => { + let depth = depth.saturating_sub(U64::one()); + let mut current_block = self + .block_number() + .await + .map_err(ServiceManagerError::EnrichedClient)?; + current_block = current_block.saturating_sub(depth); + Some(current_block.into()) + } + None => None, + }; + let res = self .as_ref() - .call_contract_function(call_request, None) + .call_contract_function(call_request, block_id) .await .map_err(ServiceManagerError::EnrichedClient)?; @@ -223,15 +238,15 @@ impl Verifier { cfg: EigenConfig, client: Arc, ) -> Result { - let srs_points_to_load = RawEigenClient::blob_size_limit() as u32 / Self::POINT_SIZE; // TODO: MAKE BLOB_SIZE_LIMIT part of Self? + let srs_points_to_load = RawEigenClient::blob_size_limit() as u32 / Self::POINT_SIZE; let (g1_point_file, g2_point_file) = Self::get_points(&cfg).await?; let g1_point_file_path = g1_point_file.path().to_string(); let g2_point_file_path = g2_point_file.path().to_string(); let kzg_handle = tokio::task::spawn_blocking(move || { Kzg::setup( - &g1_point_file_path, //&format!("{}/{}", path, Self::G1POINT), + &g1_point_file_path, "", - &g2_point_file_path, //&format!("{}/{}", path, Self::G2POINT), + &g2_point_file_path, Self::SRSORDER, srs_points_to_load, "".to_string(), @@ -394,6 +409,7 @@ impl Verifier { .batch_id_to_batch_metadata_hash( blob_info.blob_verification_proof.batch_id, self.cfg.eigenda_svc_manager_address, + Some(U64::from(self.cfg.settlement_layer_confirmation_depth)), ) .await } diff --git a/core/node/da_clients/src/eigen/verifier/tests.rs b/core/node/da_clients/src/eigen/verifier/tests.rs index 2e874ad7b30c..034723fcc37b 100644 --- a/core/node/da_clients/src/eigen/verifier/tests.rs +++ b/core/node/da_clients/src/eigen/verifier/tests.rs @@ -5,7 +5,7 @@ use zksync_config::EigenConfig; use zksync_types::{ url::SensitiveUrl, web3::{Bytes, CallRequest}, - Address, H160, U256, + Address, H160, U256, U64, }; use zksync_web3_decl::client::{Client, DynClient, L1}; @@ -40,6 +40,7 @@ impl VerifierClient for MockVerifierClient { &self, batch_id: u32, svc_manager_addr: Address, + _settlement_layer_confirmation_depth: Option, ) -> Result, VerificationError> { let mut data = vec![]; let func_selector = From efdf1b404f3352a34417bc652e41e0b9cbad92b7 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Tue, 28 Jan 2025 09:56:45 -0300 Subject: [PATCH 92/97] address comments --- core/node/da_clients/src/eigen/errors.rs | 2 +- core/node/da_clients/src/eigen/sdk.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/node/da_clients/src/eigen/errors.rs b/core/node/da_clients/src/eigen/errors.rs index 0b661da60c68..b2eba8ee843e 100644 --- a/core/node/da_clients/src/eigen/errors.rs +++ b/core/node/da_clients/src/eigen/errors.rs @@ -106,6 +106,6 @@ pub enum VerificationError { CommitmentNotOnCurve(G1Affine), #[error("Commitment not on correct subgroup: {0}")] CommitmentNotOnCorrectSubgroup(G1Affine), - #[error("Link Error: {0}")] + #[error("Point download error: {0}")] PointDownloadError(String), } diff --git a/core/node/da_clients/src/eigen/sdk.rs b/core/node/da_clients/src/eigen/sdk.rs index 1bf47cb147df..c22f86de6a57 100644 --- a/core/node/da_clients/src/eigen/sdk.rs +++ b/core/node/da_clients/src/eigen/sdk.rs @@ -366,6 +366,7 @@ fn remove_empty_byte_from_padded_bytes(data: &[u8]) -> Vec { let parse_size = DATA_CHUNK_SIZE; let chunk_count = data.len().div_ceil(parse_size); + // Safe subtraction, as we know chunk_count is always less than the length of the data let mut valid_data = Vec::with_capacity(data.len() - chunk_count); for chunk in data.chunks(parse_size) { From 2587277550a12f8fa35352a8afd1755bd3fbdf8f Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Tue, 28 Jan 2025 11:52:13 -0300 Subject: [PATCH 93/97] update yarn.lock --- yarn.lock | 203 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 160 insertions(+), 43 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5df8cb570e0f..eabdce369080 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1737,15 +1737,23 @@ sinon-chai "^3.7.0" ts-morph "^22.0.0" -"@matterlabs/hardhat-zksync-node@^0.0.1-beta.7": - version "0.0.1" - resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-node/-/hardhat-zksync-node-0.0.1.tgz#d44bda3c0069b149e2a67c9697eb81166b169ea6" - integrity sha512-rMabl+I813lzXINqTq5OvujQ30wsfO9mTLMPDXuYzEEhEzvnXlaVxuqynKBXrgXAxjmr+G79rqvcWgeKygtwBA== +"@matterlabs/hardhat-zksync-node@^1.2.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-node/-/hardhat-zksync-node-1.2.1.tgz#786d51b28ad3aa5b8b973831e016151326d844e4" + integrity sha512-BZDJyEB9iu54D6sOKTGeJrN5TRFLrg6k9E1x3lEwpOfewPwg1eTfb9e/LKGSCePbSremZIHzK3eDRr80hVdDjA== dependencies: - "@matterlabs/hardhat-zksync-solc" "^1.0.5" - axios "^1.4.0" - chalk "4.1.2" - fs-extra "^11.1.1" + "@matterlabs/hardhat-zksync-solc" "^1.2.5" + axios "^1.7.2" + chai "^4.3.4" + chalk "^4.1.2" + debug "^4.3.5" + fs-extra "^11.2.0" + proxyquire "^2.1.3" + semver "^7.6.2" + sinon "^18.0.0" + sinon-chai "^3.7.0" + source-map-support "^0.5.21" + undici "^6.18.2" "@matterlabs/hardhat-zksync-solc@0.4.2": version "0.4.2" @@ -1771,16 +1779,7 @@ proper-lockfile "^4.1.2" semver "^7.5.1" -"@matterlabs/hardhat-zksync-solc@^0.3.15": - version "0.3.17" - resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-solc/-/hardhat-zksync-solc-0.3.17.tgz#72f199544dc89b268d7bfc06d022a311042752fd" - integrity sha512-aZgQ0yfXW5xPkfuEH1d44ncWV4T2LzKZd0VVPo4PL5cUrYs2/II1FaEDp5zsf3FxOR1xT3mBsjuSrtJkk4AL8Q== - dependencies: - "@nomiclabs/hardhat-docker" "^2.0.0" - chalk "4.1.2" - dockerode "^3.3.4" - -"@matterlabs/hardhat-zksync-solc@^1.0.5", "@matterlabs/hardhat-zksync-solc@^1.1.4": +"@matterlabs/hardhat-zksync-solc@=1.1.4", "@matterlabs/hardhat-zksync-solc@^1.0.5": version "1.1.4" resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-solc/-/hardhat-zksync-solc-1.1.4.tgz#04a2fad6fb6b6944c64ad969080ee65b9af3f617" integrity sha512-4/usbogh9neewR2/v8Dn2OzqVblZMUuT/iH2MyPZgPRZYQlL4SlZtMvokU9UQjZT6iSoaKCbbdWESHDHSzfUjA== @@ -1797,6 +1796,15 @@ sinon-chai "^3.7.0" undici "^5.14.0" +"@matterlabs/hardhat-zksync-solc@^0.3.15": + version "0.3.17" + resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-solc/-/hardhat-zksync-solc-0.3.17.tgz#72f199544dc89b268d7bfc06d022a311042752fd" + integrity sha512-aZgQ0yfXW5xPkfuEH1d44ncWV4T2LzKZd0VVPo4PL5cUrYs2/II1FaEDp5zsf3FxOR1xT3mBsjuSrtJkk4AL8Q== + dependencies: + "@nomiclabs/hardhat-docker" "^2.0.0" + chalk "4.1.2" + dockerode "^3.3.4" + "@matterlabs/hardhat-zksync-solc@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-solc/-/hardhat-zksync-solc-1.2.0.tgz#c1ccd1eca0381840196f220b339da08320ad9583" @@ -1814,7 +1822,7 @@ sinon-chai "^3.7.0" undici "^6.18.2" -"@matterlabs/hardhat-zksync-solc@^1.2.4": +"@matterlabs/hardhat-zksync-solc@^1.2.4", "@matterlabs/hardhat-zksync-solc@^1.2.5": version "1.2.5" resolved "https://registry.yarnpkg.com/@matterlabs/hardhat-zksync-solc/-/hardhat-zksync-solc-1.2.5.tgz#fbeeabc3fea0dd232fa3c8cb31bd93c103eba11a" integrity sha512-iZyznWl1Hoe/Z46hnUe1s2drBZBjJOS/eN+Ql2lIBX9B6NevBl9DYzkKzH5HEIMCLGnX9sWpRAJqUQJWy9UB6w== @@ -1882,6 +1890,11 @@ resolved "https://registry.yarnpkg.com/@matterlabs/prettier-config/-/prettier-config-1.0.3.tgz#3e2eb559c0112bbe9671895f935700dad2a15d38" integrity sha512-JW7nHREPqEtjBWz3EfxLarkmJBD8vi7Kx/1AQ6eBZnz12eHc1VkOyrc6mpR5ogTf0dOUNXFAfZut+cDe2dn4kQ== +"@matterlabs/zksync-contracts@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@matterlabs/zksync-contracts/-/zksync-contracts-0.6.1.tgz#39f061959d5890fd0043a2f1ae710f764b172230" + integrity sha512-+hucLw4DhGmTmQlXOTEtpboYCaOm/X2VJcWmnW4abNcOgQXEHX+mTxQrxEfPjIZT0ZE6z5FTUrOK9+RgUZwBMQ== + "@metamask/eth-sig-util@^4.0.0": version "4.0.1" resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" @@ -2305,11 +2318,21 @@ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.5.tgz#572b5da102fc9be1d73f34968e0ca56765969812" integrity sha512-f7L1//4sLlflAN7fVzJLoRedrf5Na3Oal5PZfIq55NFcVZ90EpV1q5xOvL4lFvg3MNICSDr2hH0JUBxwlxcoPg== +"@openzeppelin/contracts-upgradeable@4.9.5": + version "4.9.5" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.9.5.tgz#572b5da102fc9be1d73f34968e0ca56765969812" + integrity sha512-f7L1//4sLlflAN7fVzJLoRedrf5Na3Oal5PZfIq55NFcVZ90EpV1q5xOvL4lFvg3MNICSDr2hH0JUBxwlxcoPg== + "@openzeppelin/contracts-v4@npm:@openzeppelin/contracts@4.9.5": version "4.9.5" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.5.tgz#1eed23d4844c861a1835b5d33507c1017fa98de8" integrity sha512-ZK+W5mVhRppff9BE6YdR8CC52C8zAvsVAiWhEtQ5+oNxFE6h1WdeWo+FJSF8KKvtxxVYZ7MTP/5KoVpAU3aSWg== +"@openzeppelin/contracts@4.9.5": + version "4.9.5" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.5.tgz#1eed23d4844c861a1835b5d33507c1017fa98de8" + integrity sha512-ZK+W5mVhRppff9BE6YdR8CC52C8zAvsVAiWhEtQ5+oNxFE6h1WdeWo+FJSF8KKvtxxVYZ7MTP/5KoVpAU3aSWg== + "@openzeppelin/contracts@^4.8.0": version "4.9.6" resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.6.tgz#2a880a24eb19b4f8b25adc2a5095f2aa27f39677" @@ -2920,10 +2943,12 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.6.tgz#df929d1bb2eee5afdda598a41930fe50b43eaa6a" integrity sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ== -"@types/node@18.15.13": - version "18.15.13" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" - integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== +"@types/node@22.7.5": + version "22.7.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.5.tgz#cfde981727a7ab3611a481510b473ae54442b92b" + integrity sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ== + dependencies: + undici-types "~6.19.2" "@types/node@^10.0.3": version "10.17.60" @@ -5464,18 +5489,18 @@ ethers@^5.0.2, ethers@^5.7.0, ethers@^5.7.2, ethers@~5.7.0, ethers@~5.7.2: "@ethersproject/web" "5.7.1" "@ethersproject/wordlists" "5.7.0" -ethers@^6.7.1: - version "6.12.1" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.12.1.tgz#517ff6d66d4fd5433e38e903051da3e57c87ff37" - integrity sha512-j6wcVoZf06nqEcBbDWkKg8Fp895SS96dSnTCjiXT+8vt2o02raTn4Lo9ERUuIVU5bAjoPYeA+7ytQFexFmLuVw== +ethers@^6.13.5: + version "6.13.5" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.5.tgz#8c1d6ac988ac08abc3c1d8fabbd4b8b602851ac4" + integrity sha512-+knKNieu5EKRThQJWwqaJ10a6HE9sSehGeqWN65//wE7j47ZpFhKAnHB/JJFibwwg61I/koxaPsXbXpD/skNOQ== dependencies: "@adraffy/ens-normalize" "1.10.1" "@noble/curves" "1.2.0" "@noble/hashes" "1.3.2" - "@types/node" "18.15.13" + "@types/node" "22.7.5" aes-js "4.0.0-beta.5" - tslib "2.4.0" - ws "8.5.0" + tslib "2.7.0" + ws "8.17.1" ethers@~5.5.0: version "5.5.4" @@ -5750,6 +5775,14 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" +fill-keys@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fill-keys/-/fill-keys-1.0.2.tgz#9a8fa36f4e8ad634e3bf6b4f3c8882551452eb20" + integrity sha512-tcgI872xXjwFF4xgQmLxi76GnwJG3g/3isB1l4/G5Z4zrbddGpBjqZCO9oEAcB5wX0Hj/5iQB3toxfO7in1hHA== + dependencies: + is-object "~1.0.1" + merge-descriptors "~1.0.0" + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -6827,6 +6860,13 @@ is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.13.1: dependencies: hasown "^2.0.0" +is-core-module@^2.16.0: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + is-data-view@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" @@ -6895,6 +6935,11 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-object@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" + integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== + is-path-inside@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" @@ -8123,6 +8168,11 @@ memorystream@^0.3.1: resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== +merge-descriptors@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -8380,6 +8430,11 @@ mocha@^9.0.2: yargs-parser "20.2.4" yargs-unparser "2.0.0" +module-not-found-error@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/module-not-found-error/-/module-not-found-error-1.0.1.tgz#cf8b4ff4f29640674d6cdd02b0e3bc523c2bbdc0" + integrity sha512-pEk4ECWQXV6z2zjhRZUongnLJNUeGQJ3w6OQ5ctGwD+i5o93qjRQUk2Rt6VdNeu3sEP0AB4LcfvdebpxBRVr4g== + moo@^0.5.0: version "0.5.2" resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c" @@ -9220,6 +9275,15 @@ proxy-from-env@^1.1.0: resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== +proxyquire@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/proxyquire/-/proxyquire-2.1.3.tgz#2049a7eefa10a9a953346a18e54aab2b4268df39" + integrity sha512-BQWfCqYM+QINd+yawJz23tbBM40VIGXOdDw3X344KcclI/gtBbdWF6SlQ4nK/bYhF9d27KYug9WzljHC6B9Ysg== + dependencies: + fill-keys "^1.0.2" + module-not-found-error "^1.0.1" + resolve "^1.11.1" + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -9547,6 +9611,15 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.20.0, resolve@^1.22 path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.11.1: + version "1.22.10" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== + dependencies: + is-core-module "^2.16.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + responselike@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626" @@ -10068,7 +10141,7 @@ source-map-support@0.5.13: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@^0.5.13: +source-map-support@^0.5.13, source-map-support@^0.5.21: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -10196,7 +10269,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -10213,6 +10286,15 @@ string-width@^2.1.0, string-width@^2.1.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -10279,7 +10361,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -10300,6 +10382,13 @@ strip-ansi@^5.1.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -10400,7 +10489,7 @@ synckit@^0.8.6: version "0.1.0" dependencies: "@matterlabs/hardhat-zksync-deploy" "^0.7.0" - "@matterlabs/hardhat-zksync-solc" "^1.1.4" + "@matterlabs/hardhat-zksync-solc" "=1.1.4" "@matterlabs/hardhat-zksync-verify" "^1.4.3" commander "^9.4.1" eslint "^8.51.0" @@ -10582,6 +10671,11 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +toml@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" + integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== + tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -10699,10 +10793,10 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== +tslib@2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" @@ -10915,6 +11009,11 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +undici-types@~6.19.2: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + undici@^5.14.0: version "5.28.4" resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" @@ -11150,7 +11249,16 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -11186,10 +11294,10 @@ ws@7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== -ws@8.5.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" - integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== +ws@8.17.1: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" + integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== ws@^7.4.6: version "7.5.9" @@ -11231,6 +11339,11 @@ yaml@^2.4.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.2.tgz#7a2b30f2243a5fc299e1f14ca58d475ed4bc5362" integrity sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA== +yaml@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.7.0.tgz#aef9bb617a64c937a9a748803786ad8d3ffe1e98" + integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA== + yargs-parser@20.2.4: version "20.2.4" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" @@ -11315,3 +11428,7 @@ zksync-ethers@^6.9.0: version "6.9.0" resolved "https://registry.yarnpkg.com/zksync-ethers/-/zksync-ethers-6.9.0.tgz#efaff1d59e2cff837eeda84c4ba59fdca4972a91" integrity sha512-2CppwvLHtz689L7E9EhevbFtsqVukKC/lVicwdeUS2yqV46ET4iBR11rYdEfGW2oEo1h6yJuuwIBDFm2SybkIA== + +"zksync-ethers@https://github.com/zksync-sdk/zksync-ethers#sb-use-new-encoding-in-sdk": + version "6.12.1" + resolved "https://github.com/zksync-sdk/zksync-ethers#bc6e3ab201f743fcbb53e0216f3de421bb3a617f" From 28de691a9f0b7f546ed6c3cfdc861e9a04681ad8 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Tue, 28 Jan 2025 15:21:01 -0300 Subject: [PATCH 94/97] change `path()` fn to return `&std::path::Path` --- .../node/da_clients/src/eigen/verifier/mod.rs | 50 ++++++++++++------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/core/node/da_clients/src/eigen/verifier/mod.rs b/core/node/da_clients/src/eigen/verifier/mod.rs index e97dc4dd5999..e186de56bcad 100644 --- a/core/node/da_clients/src/eigen/verifier/mod.rs +++ b/core/node/da_clients/src/eigen/verifier/mod.rs @@ -1,9 +1,10 @@ -use std::{collections::HashMap, io::Write, sync::Arc}; +use std::{collections::HashMap, io::Write, path::Path, sync::Arc}; use ark_bn254::{Fq, G1Affine}; use ethabi::{encode, ParamType, Token}; use rust_kzg_bn254::{blob::Blob, kzg::Kzg, polynomial::PolynomialFormat}; use tempfile::NamedTempFile; +use tokio::task::JoinHandle; use zksync_basic_types::web3::CallRequest; use zksync_config::EigenConfig; use zksync_eth_client::EthInterface; @@ -157,10 +158,10 @@ enum PointFile { } impl PointFile { - fn path(&self) -> &str { + fn path(&self) -> &Path { match self { - PointFile::Temp(file) => file.path().to_str().unwrap_or_default(), // Safe unwrap because NamedTempFile guarantees a valid path - PointFile::Path(path) => path, + PointFile::Temp(file) => file.path(), + PointFile::Path(path) => Path::new(path), } } } @@ -240,22 +241,35 @@ impl Verifier { ) -> Result { let srs_points_to_load = RawEigenClient::blob_size_limit() as u32 / Self::POINT_SIZE; let (g1_point_file, g2_point_file) = Self::get_points(&cfg).await?; - let g1_point_file_path = g1_point_file.path().to_string(); - let g2_point_file_path = g2_point_file.path().to_string(); - let kzg_handle = tokio::task::spawn_blocking(move || { - Kzg::setup( - &g1_point_file_path, - "", - &g2_point_file_path, - Self::SRSORDER, - srs_points_to_load, - "".to_string(), - ) - }); + let kzg_handle: JoinHandle> = + tokio::task::spawn_blocking(move || { + let g1_point_file_path = + g1_point_file + .path() + .to_str() + .ok_or(KzgError::Setup(format!( + "Could not format point path into a valid string" + )))?; + let g2_point_file_path = + g2_point_file + .path() + .to_str() + .ok_or(KzgError::Setup(format!( + "Could not format point path into a valid string" + )))?; + Kzg::setup( + g1_point_file_path, + "", + g2_point_file_path, + Self::SRSORDER, + srs_points_to_load, + "".to_string(), + ) + .map_err(KzgError::Internal) + }); let kzg = kzg_handle .await - .map_err(|e| VerificationError::Kzg(KzgError::Setup(e.to_string())))? - .map_err(KzgError::Internal)?; + .map_err(|e| VerificationError::Kzg(KzgError::Setup(e.to_string())))??; Ok(Self { kzg, cfg, client }) } From 1870c2a71aa846edca80b46716b02e2bb3af7b69 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Tue, 28 Jan 2025 16:24:25 -0300 Subject: [PATCH 95/97] replace `Path(String)` for `Path(PathBuf)` --- .../node/da_clients/src/eigen/verifier/mod.rs | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/core/node/da_clients/src/eigen/verifier/mod.rs b/core/node/da_clients/src/eigen/verifier/mod.rs index e186de56bcad..295b474bda3d 100644 --- a/core/node/da_clients/src/eigen/verifier/mod.rs +++ b/core/node/da_clients/src/eigen/verifier/mod.rs @@ -1,4 +1,9 @@ -use std::{collections::HashMap, io::Write, path::Path, sync::Arc}; +use std::{ + collections::HashMap, + io::Write, + path::{Path, PathBuf}, + sync::Arc, +}; use ark_bn254::{Fq, G1Affine}; use ethabi::{encode, ParamType, Token}; @@ -154,14 +159,14 @@ impl VerifierClient for Box> { #[derive(Debug)] enum PointFile { Temp(NamedTempFile), - Path(String), + Path(PathBuf), } impl PointFile { fn path(&self) -> &Path { match self { PointFile::Temp(file) => file.path(), - PointFile::Path(path) => Path::new(path), + PointFile::Path(path) => path.as_path(), } } } @@ -222,8 +227,8 @@ impl Verifier { async fn get_points(cfg: &EigenConfig) -> Result<(PointFile, PointFile), VerificationError> { match &cfg.points_dir { Some(path) => Ok(( - PointFile::Path(format!("{}/{}", path, Self::G1POINT)), - PointFile::Path(format!("{}/{}", path, Self::G2POINT)), + PointFile::Path(PathBuf::from(format!("{}/{}", path, Self::G1POINT))), + PointFile::Path(PathBuf::from(format!("{}/{}", path, Self::G2POINT))), )), None => { tracing::info!("Points for KZG setup not found, downloading points to a temp file"); @@ -243,20 +248,12 @@ impl Verifier { let (g1_point_file, g2_point_file) = Self::get_points(&cfg).await?; let kzg_handle: JoinHandle> = tokio::task::spawn_blocking(move || { - let g1_point_file_path = - g1_point_file - .path() - .to_str() - .ok_or(KzgError::Setup(format!( - "Could not format point path into a valid string" - )))?; - let g2_point_file_path = - g2_point_file - .path() - .to_str() - .ok_or(KzgError::Setup(format!( - "Could not format point path into a valid string" - )))?; + let g1_point_file_path = g1_point_file.path().to_str().ok_or(KzgError::Setup( + "Could not format point path into a valid string".to_string(), + ))?; + let g2_point_file_path = g2_point_file.path().to_str().ok_or(KzgError::Setup( + "Could not format point path into a valid string".to_string(), + ))?; Kzg::setup( g1_point_file_path, "", From b33700b49c0185bd4408f67d924a7d79356fb927 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Tue, 28 Jan 2025 17:16:01 -0300 Subject: [PATCH 96/97] run linter --- zkstack_cli/crates/zkstack/completion/_zkstack.zsh | 4 ++-- zkstack_cli/crates/zkstack/completion/zkstack.fish | 6 ++++-- zkstack_cli/crates/zkstack/completion/zkstack.sh | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/zkstack_cli/crates/zkstack/completion/_zkstack.zsh b/zkstack_cli/crates/zkstack/completion/_zkstack.zsh index c7357d661e2e..7d9b405680e5 100644 --- a/zkstack_cli/crates/zkstack/completion/_zkstack.zsh +++ b/zkstack_cli/crates/zkstack/completion/_zkstack.zsh @@ -134,7 +134,7 @@ _arguments "${_arguments_options[@]}" : \ '-o+[Enable Grafana]' \ '--observability=[Enable Grafana]' \ '--update-submodules=[]:UPDATE_SUBMODULES:(true false)' \ -'--validium-type=[Type of the Validium network]:VALIDIUM_TYPE:(no-da avail)' \ +'--validium-type=[Type of the Validium network]:VALIDIUM_TYPE:(no-da avail eigen-da)' \ '--support-l2-legacy-shared-bridge-test=[]' \ '--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ @@ -295,7 +295,7 @@ _arguments "${_arguments_options[@]}" : \ '--deploy-paymaster=[]' \ '--l1-rpc-url=[L1 RPC URL]:L1_RPC_URL:_default' \ '--update-submodules=[]:UPDATE_SUBMODULES:(true false)' \ -'--validium-type=[Type of the Validium network]:VALIDIUM_TYPE:(no-da avail)' \ +'--validium-type=[Type of the Validium network]:VALIDIUM_TYPE:(no-da avail eigen-da)' \ '--chain=[Chain to use]:CHAIN:_default' \ '--resume[]' \ '--zksync[]' \ diff --git a/zkstack_cli/crates/zkstack/completion/zkstack.fish b/zkstack_cli/crates/zkstack/completion/zkstack.fish index b724ddc6c723..a4cfb31a62c4 100644 --- a/zkstack_cli/crates/zkstack/completion/zkstack.fish +++ b/zkstack_cli/crates/zkstack/completion/zkstack.fish @@ -141,7 +141,8 @@ false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l update-submodules -r -f -a "true\t'' false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l validium-type -d 'Type of the Validium network' -r -f -a "no-da\t'' -avail\t''" +avail\t'' +eigen-da\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l support-l2-legacy-shared-bridge-test -r -f -a "true\t'' false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand ecosystem; and __fish_seen_subcommand_from init" -l chain -d 'Chain to use' -r @@ -247,7 +248,8 @@ complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_s complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l update-submodules -r -f -a "true\t'' false\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l validium-type -d 'Type of the Validium network' -r -f -a "no-da\t'' -avail\t''" +avail\t'' +eigen-da\t''" complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l chain -d 'Chain to use' -r complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l resume complete -c zkstack -n "__fish_zkstack_using_subcommand chain; and __fish_seen_subcommand_from init" -l zksync diff --git a/zkstack_cli/crates/zkstack/completion/zkstack.sh b/zkstack_cli/crates/zkstack/completion/zkstack.sh index a6b4498d53fb..4fa85e45f3d8 100644 --- a/zkstack_cli/crates/zkstack/completion/zkstack.sh +++ b/zkstack_cli/crates/zkstack/completion/zkstack.sh @@ -2063,7 +2063,7 @@ _zkstack() { return 0 ;; --validium-type) - COMPREPLY=($(compgen -W "no-da avail" -- "${cur}")) + COMPREPLY=($(compgen -W "no-da avail eigen-da" -- "${cur}")) return 0 ;; --chain) @@ -5195,7 +5195,7 @@ _zkstack() { return 0 ;; --validium-type) - COMPREPLY=($(compgen -W "no-da avail" -- "${cur}")) + COMPREPLY=($(compgen -W "no-da avail eigen-da" -- "${cur}")) return 0 ;; --support-l2-legacy-shared-bridge-test) From 2080f16376d3fb26ab464b6535eb54ec1e9c2dbc Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Tue, 28 Jan 2025 17:26:32 -0300 Subject: [PATCH 97/97] update genesis.yaml hashes --- etc/env/file_based/genesis.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etc/env/file_based/genesis.yaml b/etc/env/file_based/genesis.yaml index 0cd9959baeaf..480f798ce4df 100644 --- a/etc/env/file_based/genesis.yaml +++ b/etc/env/file_based/genesis.yaml @@ -1,6 +1,6 @@ -genesis_root: 0xd8c9be7efb705e7dcf529c14fce7048ea99dea9eab6a6b4e5f8de1ebf4f2ebf2 +genesis_root: 0x7bdb3d822ad837a3611c436d3be457363a08d06d83b74469831482353a7d8277 genesis_rollup_leaf_index: 68 -genesis_batch_commitment: 0xf6e873e8894b90f157511a133d941fb6f0892f83147e3d0d2cafa71af8c838e5 +genesis_batch_commitment: 0x81f5e324a4019e4161fb9dc5058a588aa364a551fdd5c0e8788521e64e7ad596 genesis_protocol_version: 26 default_aa_hash: 0x010004dbf8be36c421254d005352f8245146906919be0099e8a50d0e78df85e0 bootloader_hash: 0x0100088580465d88420e6369230ee94a32ff356dbcdd407a4be49fc8009b2a81

@_eYCJiP&vIJ-8-fUnGwwtZ$Ep>(k^VK4kwU1dOq|;{7HJm#OZ#)lC(&UPpTE$44fF;3Gmta#yj?%vrJwJX zC($vVPhY@) zIK{C^{>wb)D(?!wYk%)9ZRe}-T$=4?nf<(?_*p*o{jg8c{SBk(EB1GL{#fY5+a9b3$7#K) zhJQ%Y(S?rB@}ZoQr(V|{*fJF!M_$Kq6n}@iSNqXm2mb!Hr_XKnT%yz}DKGZM3X zKHsi(IumGb*x*n57YoD>TlOnJ9L5Lya6GB!zYHH~Kf}+npM!=sF@ANup6}@K%ozU$ znhE&t`7MJVwS&_;b-xS1+QcV=g)-xnPOww?&zthbe?fjPUL##L`MK3JAK+8gFTf}8 zPW4UuDkt%J+9x3V!XK{^-h*?%*f+qw75kNO{I2Kz0{qIkSx5zrGkWJD&G?1?#P}6| zg*J*bez(N&n_ExgT{fWcx&MSva97h zuX!J@?E4_UBX-WWEc-MV&0^WtY4Ck4U2cZX8TR-7x|TB}$3)(x#&weA&9Z9)d7<}Z z&=l~K7SCC>KQ3>Uy*j9WdgTr7hn*n4*y#Y_y?avqCZI+2MBs^bVgzpKBY972Z#A`j zKBpLgC$}Hgx7t@yKYje{UxwrBzEkMd{V>maICWey?+~6mt`KgIi~UWa1FhHbJzUYF zat{f*lFrG~^>vQNxPJk;UcoWdL4EJVLAoF58J#Zc^f5`hPvm*O5FIdo^)A(PRW$pf zd2L?=@{PaK+`C%%YuSBr?x%Ddatr-nM?vfN!VbNk$ls@4rumWBBkMe}dne{|8`RUC zaehTT=1cdZnE$Ox&4_;At@*Bmj>%5IV(|F~vU@Zi#?L{xccDn7H^jFcM=qjj>9xeN+;8oM{U@EZi|7=s1Mi_4WE-)S=zC zgzZSe!=R3rXuM#Z?qhtuJas;}r-1o{@Z|Y~aBDmgFA3`_`3`uAiwmy~j`djUPLak(Dh6Y}vi#Y<>Bk$6djZWHM|0#C+M zI-Qtv=8oetUP@yB_iQzGk?=W2BXktl|EK7@5xqBso4Ik0Z-KLzh(|e6cOZ&b1tn_}9*8R3{&vN^j z;>QR&=(y-{#5;&CC4Nl%n&bz;8H3>O!~ckD2%P;#YKQZ^t7-nrzmjUVqnPT6z!UAm z2;8#Uy`cu}^L}om{#@H1ZqPpa9rI|v)}Z})j=yd{kK4oQoYZ-iz}{QKpc?F`>*S^nT`>6*rIg5`9a?#w*YU`!5@oD?~@r^)L^dYs}062C(b zLH9e_pkJ=HeAe`WV@%SIpDUBRcVC9CWIcG_2JcDXd_4{thMe;X_6_xUuZIx#!#;?2 zjp8^QZOgI#Kpb%p?RtB4zLQ^D{W#M5OgSC@a|%*!zbRKJQ#q(5e!qs&=bC0(6yM*o z+ef)G9h_yydpl_VQh8&DJ0ji}jC-)i`KWgu$LaHkJ0Si^Xq9m~%4dC8T*d7mpXHFI zFO)lj>bd&<%M8VnsC*vt_NI^5z^2fTiFgk;~ZBE^e531;fd(jc{`t1m3>Nd zPx~s%4@cvD%fHk>&W7-0xe&r_Icv*lTka6NKSD=VeQzA_h#jEM!L$Hvd=EbPJkrVQ zRo>A>JaqZ%Q}|WZOWQBNo1D)I_*LSK%fFb4&m88nCIU~!XIS6Dhr}Dh`Whd_)Oe%F z^{~A;_J{i(n(kn?y%2agq2q={O^>-)ypixA@kYewqwgVE{)wMUyphHei8n^*%%;Qn z>OXTn3;KlO>C68nCCMhzLQE{Bxib- zhw$X}4&fGk@jg@k@<)R9Z8+o%#{GyM-*>I?k^CVGpZLRteNXsr!IScfZ10od1Nl`X zJ|cY>oI@?iIaJ^CS>8R|a+IIS*`qGSysyQUY6a!7ox|_rDl8Dc;draaN2TuMe6`>X zDz8?zYQI9ghx0c}`I6r%{hIRc*7@Zi{{>wBM!ffl{T`V?^POL<|Lt=D$IX6cu>W6FJ$>7>{i(w9 zLyF#qfWfdmfd3uZKh05EVQ%z*y}@uy_8hIhAj}0W`m6650sq@_lI2$fo+w`;a7%uO z{tDrW-eb5%=stv?t2 zCGr&OCg+R$p4DJoLU{7Jgm4>AM$Z=NcZRd^1Sb<-S3%d^7>JnA-aCQ(C3SxFD{U{ zb02IMXCvfMJiX3JpKGNzm^8}`j#opjoPkszN1$JE#s^7#&nuKKqQ8+wy}swGRfSbl z4ElrCCsIEscMi&JM!7Q^m%|q*7g4>w7ohx_#^w9A0S`E9*!Log2X_F&?~&6`vVKqC zmjVZ=W&fxy{jRP~)wb4Z*@aV}`A;1yr+_#c@WBS~V_RN2uw~PZEt_{;RN1~|*VZjt z>)c-r}@>bfr=kPHrE0)yr_eaNu#>b{cr>1LT)3E~ei5RkS|Mil?^+hssZj3^= zR!8^MhW+Vrf2ua^kJYa8udYrH?YY3$g+y&fYs~&{Anl>7(t!Ug39(sqqOknxo@3Fp z{dc~1AQP^hTAs1%#L`bK4}S6sTb-MJbmoO`e)Vts+27xnKmJYi=iC44$kU$ts{i^* z;T5laH{wzmza!Ri}R*j_cb$LPmG)k)hN(S($(cNR!=>wA%ssi{(b!d8g^6=)`#8k8yI^LM|+c%y~ z0zvx|)x-O%VDHIG$0iPh_z&QdwW-?V!P@ZV!(-L`qeG_8tE-jDS$KDe01l(vNky$v^p|5zCWcZmocMSTVFK>U)KP>z5)E)6!^u}DSx6i z=}#QEn)wI}blv3m7@ymx0R;0)ssEy{Ka73uIsH~gOJ6>=@36n+(t&f%U$@>Ls*d>w zrfSsHIa@BTP&wBuX37&)*PVA>zduo(tnRnQPW4Bw%Vq%0R{k*fDrt(f={=jO`}R>& ziNBO#N%r?IKHdQSP&WJ$)d22quT4*rcq7fYZM}c>zVV^GfpWx%TR!%=XD1FkJ@egB zP`&<*_X0yM^M~b}9GDzGI67P#9uOKEm>eCd!IG7fQeg1)o z-ILYfnm>IA%QWqej80CGl)9v{{gPdmZ{50m%Z^B`@XyVa9hF_%wp@1cj!VMK$X^t$ z$@*pD|A$Zav+5S^{b2U`{dl22Iv&c}q_(CH?HHe~?gN`{8{I!zUv{!~?SawB+VJHE zYm<9w)nU7haWHokio?LSj!*8Gtd31Vn~aW+DFysrR2vzetPM=oh=(LMY~}W|zV|?w zn$=&MqottB_2OPSHZtyyusIhguy38er#h7`LMjszwJ}!USa?{0hWbnsE#JQ4Rp#%c z=VT2!b&N#YM53L7pmFAJYPvef3>Fm>nKeS-H7H#QI-31@ak7S^tUgZ2gS-AgL{EFF* zpG{6++7Iv@<63|ul}W%)nDyx0$Ho4{$VmSm*f)KFg`|3ToKDF9(FB~-z0&q;YLWd1 z_Fdrbu1#;5+&q&y95r53h&fPFKwe{8Rn8&6KwMh=V(pA9r6W3haqJyS#gsODe(?Xg5JrFPbZ`AsJf~qQCL^Qz z4LxmJ_agLF8`}+&XxsX27j(`gPhOWVN!;By7qVcIwcxFB@==Ex(9h7`Z2F=e_gBXbLvyhI!-iU<|7&XpBdPj7FW36-X;c2VR*^i5 zJ)^r>=@`A}QwA1&dJk;S2$J_@=qBoIYEO1}V|rpVimti=PKB;i9gH%rz3$pf1XZUF zCDt>UB)?ehm<;PV_JfyS@WCJ7cISa7danJ(52}TQ{qNuXweEAixNP9&pIo{95C0tW zZIgH*+Jr$B*%e760bJ~hoz;D#P{YP=infvV_rE>-t}n-gK|;Z)>cQHMLzlpWs7(>4 z7;O)q>mc>W|DoH;MVBVjR%&#L2bKt4XfO?&U%lj*jQY~Ciy@uiJCkd2V03Ky*&B4FD7;>JG-%8M!@luT;(vjRp2`j% zsp?dwcvRJF_g_skP+&p)D+q+dClSE+R}aCl9iJSXK8*bW__Ux16pg~8`KFZs4nY$- z!WSW;CTczkk12lLKmeZ(V@{@RdIoR`L`4Dm?~kv1*iXm~vO@l$_&WyaPmuc_z;CFWLoiTH{!c>UWx|Cn7Tn%3i=kY*xnXj`&3S~%hFNdLU?5bZmm zcr5Ft5C7-RScaWAQa)y9#nMO059zq}$T;lLh;6DxO(14s)q=#4O7Lgwk0a%~Ogda& zgCaJ2r+A(ywA>U-dBPtbsg6#Dlo4>QR6zS-aFIzTULf=o693AMk0n-#WqvJ|s!J%R zE-RMt(`^0x#7~~sZsG%>qK{$fqwQbnh-Fxbn6k29lpOn~zxNZegRF_k+Bv%GkSPOb z_8J6$H=)Z@#C-Q$PL6RIC(nLRIKai zYqxd1E><7rw_Rx4(2_^(yl7gR>9E|j{D}e^{QV!_y7l7BB1fJ#xCs=A$j=lR;A8>H zCI_~v^cEpeEWsgP*+7Ia!+vwvD(!7y2eC#ThPAcGg$`YNU8Vti@aj60gpK2`+I89f z_rd9G1IWxN(DURlUp*Q`9S zVzvY$C9^4JkOUC<16B)%Z(+Z}HjBA7T)@EVEgPvH`d6dVQ?-3{Yn@_t1*3p)JTPa$ ztZTpGIZq4PN&4f$2hY7CDc|IM=kZ^=#r&Q0yzNEXe=WWoLFyq>KDIsy{Qa^QZolfX zO}jQ#c5J$2*JWEOFUFtSwwc;E(fBHQ|6TeI(WU9jClJ1xp7d931c!~)5J=b@?D=0a wn#~_0Pk+_-rZ=qV{L-x-eDKh@tKReG{xe^@aqC-t_g&Y2<+bJ_4&C{`K;XgIk}>-%nKP`ipx*o z9_8IT)4sY(sg`3(-AMJ7o1uQ-d7huYji0BvCd1Py_1mhIN-~}`N)_q3lk(MDQ17mk zkAAC94s7zBE_$9`r~0UH=6uw{pPQa0{CB0tsGL$8HmI)wpQC(rkxJ7O#@mgbYw0G% z^;+(bal7|{bOm~@{LFc3C9XLxpHYLEixst))%_a4y;7T13txxSI=Z@dIJ!Q@znt!6 z{AkCSLcc&)Kh=Wg_d6}lrFp@j?Wpulri#P!Gw8I)yr~xF zj~Q-QWqZQ*&?nm_V2d(FSZu+Ynl6ZbC&U#qj~r92+xzBk^U;`$Nz`z&}C_4^|5={Vf| zSQY%Bn_3&GUn72^yV{4*bP0bfI;i=tgYXg&QV#fBfkOJ;Ig9viG3NdR^KY*kp$oXk zX;HTVKBVD!XAJzB0|t>R2p-{cKMt;doGd;|%gG>SoB2AG(r}ie#nT!-#qcH6A?8P= z%iRe%Av_<}<)*pZ;u+;0O-nhneIxbzc$(T<0Cvb2r!9g5@CoOnBo?FM|eovP>nWJ)TC@1ozayi1Mx^sg>=hT#|+sPef`o5Ft zmqm#N>0i#}@~gc;(p%~wJx>>-KGDr@P^2$V{9T~9YP?g!RqiP3LFK+n>kGyAj;r!i z&vD0C58kKeEA38^ygCZ~c{9O1jn_Tm{2k$UZ*fQu_}-wj=Xu;O1vuJOk4U?X=$}J$ z_Dy~7W@^6?a?5mGsmDF7`>$LccjFTfC#t9UEk*t4Q_w7+aS#4guF#bez_O&joSddpj_KYQ&?%?sg#O3ql-nzG z%H6E#G!~~5;6$fyT6F3S=yZ#rQ!mkh=tBMQb^le4#~acK{RACSKGSgpN+$9b9t&Un zm7$aHlY0X5Nb>V3JulhZAkDubd>fTo(rEa|lYXe&-b(aYf*zdU`P2Q%bHCav^o#jo z$r(Yvgr8P7FukEo#2#MkYCea^GM_KH&zirt$ozTtN&i4koZ_lc$)^wOn=MIpBDFgvPbx9788h>w^X!mjjze{ZSV$B0QKsOq|f^kvN?i zZxA}U9}+sD-%O{n`ashO{P$+ybqt-{qgo%_uIcn(oKAofoi4QLWa>A(o7yLRAarWD zMd;Lc8~0ziuVuK<36)eM(Z&6Y`ylx%?q|^-;@dAMbvKQBwX4UC=eOed)0&^VF}}5c z8@(~+G2EFp?dDu+Ck3<1^mCx6QQ!HXzE5TNzU4JS_fKwvy#c%h?0qA(t5RvI-|`%R zpE{LlVK_WG>X-ZFz@C$T1t&rKPvAW9mrx$#AU|pf%ps2;91})NITHqXvb060p=&= zUL)}AHBvrXa^6Sf)ei0l+11r3jw(^V-MP`ofzT1)mW)r;aQ{NOrW~FZNYn|MryNi) zA87yMYtSs9d($CfZ$M|jok7PJ{*!We!g!iKE_j@&GI01!)Q&S<27bMUA1?zxDsZ(T z-cR^vEwO%oUHGMGLiiV>6gztmeh=n9HK^z17y91J(Px$yTUH?~A9PZfyFmI)>*dr7o?SjpojXfD-d=`} zw}pIsBaN#@J~n!d^g7M6TB-H=l#{I2PnX_45$bIomyt*4-xJi&c1vC@y{}e-E@aRB zP;VDdujx&~@9q<+b?g^?9vlU`)&h@P+7bUGwO6;Z8Ww`_Xg>!0LgftI$PU~crQ4vB zNVgWm1}9LioIPy*sfJINslP?TkC%bJTHx+CK$lAS73fRVIgk8Dm+%KhLU}m+5k9&& zYOoJJCq3xFzpmqxf8%}={}ymB`G+R@L2&nE>^YH9p8reiIeAL*{9Bku#>0L_m{0mP z+Ap-G%jT0fFB5?$=X=8X(-^=(>(D%iTVZ15=4moPm>2T zemm0#&^6kR=keg)HCk0aN|Ei25eze1RW#p$T z=1G@apzXm@-7he9=0%7x4PVFoh>#Ee56Tx>RTVxA;mLd$!fihMw<`EsslSo-YsAla z&4|CRBrbzji$BO~`UXvxUx+@<{#f*l>O#BwpjQ!>l754}RjeJT zA1bqk>SF}wYWOD6Td2=^E2HxrnLNd5jbG8@SWy|jdm_tu&&F?SyAi~5vux)n-A=pJ zPC%DBjpt{&on=-#sv3THCPW{VzC`Gi*7QnidSQJC^jeH`0p`6$`S|a};DaZGPu%Z; z4=U+p$-{?wH<@2UxGfLguY!M`dResyzar1T{(*Z3=vN?mq%u=PmqCZ^oAH-aJ|*|c zX_a_<#`=!bJ06tlrE>1SYyCy-Me`O|&m#J^F4A&GdI-8nxl|5UlB*uI7}i#)iy-%~ z#aJIwGv&<8d+HLIPm#a$YWOpR$A=uUoTaX0InnDHNQYqssK7rhvIb3t#BQPVLfuGc(H7)A1bls5Wby~;` z(Y5JEqOZ{AAvs#t(mfRR^&c}C+n3EZtIn&tKb)ZDXd>B z{{N_gU;Mo}<5x|ZADSIs?MGe;@ebxex&KM+f{Nw*G~c~f;q{Tj?17I)$vXW%JGBd7mBH3zV^SOejD@`@(j>lK(75Z%TGfytPhu*2=pQ0 zC%)i#!~HR;7Fttf`9kI`geT8i2)Fn``JgL}cO}|c%Y3cJse0M(ZR6+E9nn*PygTbf z-sO4P=&y{?%T9shKCPf_!SAF8X};9*S}k`3wT-8C zJy;Wdtp`^qA2tjA&63Z@{F_it?R^a5--PgFehc9izq#Vygm4=_=e;9(utxl9h0OnH z_HWX`x{UE}pfQTXU*g}apdX|^+@DmDXBmGeugT*N;WmAL%J>(IzV6QmKaESr#b%u% zGt0QvtY^vopPBkmVIvIbVH4jDIs>UO(2e}CKc;Isw*P0zlbX)(i&&2_fAT&7=&@GS zb6ES2iv8AZAQt`B3y6l%jZ?C-)~isP83``Ew0ssQ^Tv_FRZ3bj(~os);X z6MsX-uj5j%<*fX&C0`Z)#TjHg(B_(sQ9R`5U`#|$)vWb!OUhTn>=#SB?GD>->L%uy z=7Zz#>|aUz>yfF9*gMt2{cl(8m=WK&ML z_V`Hd`XX{`jnAX?uu6XTeCLPOF)HDor=PFsjOd!^jB#QPK+hHv$9i6s_Ss*ayZ!YV zs^FjR_?S;HI=IuKAK`DpUSxk?>qnj6P>qf+{(WjjTr2)PHdKH%M5g0xZ`Q2Wdi7k< zs}i?0e@pb9TBP}u;*Vjjw_NO0svvj(Cp=bsuG&TaHWwKWC377tj2>?AD;g(&t&^qG7ll#mJ@4z!6P{>Q2RRXNqp27Ss$?E$gI~b zIf*#CkE&_A*6s{Y{HEHoYMRF1hWmZcBiNV1dLqcDI#+_Ol!q4kaKhL>Jii>zm-sE} zF~B-{Thvc*U4nKfESuAD5^g~D;#bRd4MFbO3h+dg;{zZKA1?N3dS zJtBG;f6A;onD`abI;;4T_?7Z*vy49%pP}hX`VI5ZFr@Fzx`opFe>)ss_;w~A=BeM> z|84pM&9}?6+ys6?TJj;tscE~weBFS#bI49~=<=;-2hWqzZ{z8Suhon^V@Bq8mU+fT42<%N zi*Y4=hRWK0{5sFb>jE?M@dD);!+4GJT|s`1!d20S{9OB8@QYNnn|OrQ1)#^hbr3a~zq#c>Q&9Qr-~Bod%KLD`^`Ku)J-r6=4mox2K)xDr zZMZJ#WTwdOQr@1tceDPVm%OpJ9`=X%#Nl;M5?7JaH0POdFZGWHd1fv*wHW->7pxBe zF#>mPC-$DAQhC`Q0QE2Xx%2V;-0t&?TqEH~^~>z%ZV28RN#57(`e1j`4|@Ngd%ngS ztpDd^-?rZ8-QZ{PVs~A=Pr=puw#`0nyw?Oh6YlTM>HXhXy&pV#sPg`9Sto8r1i*SL z4?T4&{CoI~C(zFZANnxzo~BzZ?>W!7eMa7s@qu2(PZxa^;h!@5_>=RVL4Jt&ERye3 zj>GFiE^=QqZuc1n6Sef`W!HnaUlDks9*n?kJ!sR(*0Yo!>5JqC?E0Dxn0elZKVyB4 z_wz*PHrMzjnT{f-3G{0Q?U;|Ge*wLp^?aPz$uQrRIA0a$t=C17k7Iq)bW4zr(|V~) zJ}$`5qTW75>k}MjyN?K+ymN`qB6%{K52W9rJx%U+2)Fsf*jtlVX`;O1qP?vxoc{~f zOT6={^kbguC8VKZ`Pmo6+Y8nK8q4@O^IZopeps+yDz!=SJhNJtCO#H_jp%^5eaV*s zK0lY@M!LW8BFH;_BM;af#Lt`uT>6u;dW7XN0#BUZ2;7=q$pc2)6eDErJIV37Pb+6?t_ZKs|0pE#rL`?GELcX+4kQAAbKU)YI*} zqJ;Gi?`^Vf_H5H%weh%DpkK-H;C$s`1J~*CZJ|KL(Y8%?$$b25!Ia|(T+#x)f4?}pI58FsgdHpMnr;RxyY5j}ymJz#c zw>RJQuX)a+S8e@E`X9=r-G9j&gzz}Mly?EoN2s^#`Ix8P^DZR(nEiOZp4P)fPE9;u z>`*u2!(l+X9lh?2`V^-j&o6ceKcELlzZ~y0`xfZFa=s^cFIfEG^lbC|-qYHSUBq+> z>1feu3f+qN`8Cdi=|8UWK5qQ|tx&HKAu#@i}UD7Q2TRXeT!dYeLhmZM*P~J z!(@wmH1unK&g?hrs3~uXRm$}~{5f1VwTK^=I)nJEN&9iU!JqW`awJAYuODH#ROw?P zkFaMfr}6dJmczPVDx=q9oi$Qk`aM&wGqS#H){{*BXa(C#_^H?jL+!UBf5ZJ~_EhJh zUESvxe?jyB-V1DDe|9;tP~2~4Hu{A4h2sN~ABX(M#pvfgnYxM-uz{#JF2I!-}7?1!m%THJ*MR~c&`$Au8-loZsk#2Q=ogC4|N{+dnxicx8p9O z{LIC;p04#w^S+butCiw>u1DjJh z-wfU#tD^tCHl6SHIFC4Uo!9K2!|_(k$CST}h{Vgvxuk~uDJ#)4ieE#&H_@I%;I{n9 zdx1gyGW$~kxE&uuJeWS={uI3rbET8#{V6N)eg@Gm*q3Se-&s!julb`;B#;;PPL#h3 z_n(lwn_84M`c&^vLOYn3MJA7}de7DSjMhi@Z#Msc=Z*Xtrl*%T{0@By*;Hm9h0&j8 zpUfJqPdCp}4vpTB{UI~=-&%SCW`Xn1V&5VB&yb#o=skPh=DVLp^lmtB_PmS#AHpsF zUm1O`_kn|QvVKSN-G}#RU&?tfHG?0B@R%p>5|k_8#^||fdU#WG-L($y>HdM};j?Jp zE9p0S4|OfhUv!Z3A^qGba_wz#DBs(u@`}!PT*`0qo{e)G8&C#uA@L~LZ92!#xv16Y zhQuNNry5=!yASfd9@ab~<$~vw4~{$sV#$M0-Xijx_*SVNEI)0e=d2gfbZ^H?;BH0s zf7a(k#`lXi%{IgR1ieSmm5ECqAPeFKW~EUt(1?SH$igCc?GNbnj>z7dc);Y7Oy1JOO=1_q^CrwX>X7yW%z3pp$^C5k-i@6* z5uDFU{O0iayue$_`MgUPiQIa{`1oeqA1`KKgZ?nz&gHz|r8{+huB*cDERS>N7jM@u z$#}6oKzegV9G8ZEWPQ!=+bcIX4_W)Uv`;Yt_jWHVy*Mw?*SffzzV!A8J-xl?R{`9J z2!-Md`S@0o=8?n!a@lYI0{yW$^HAP&m(t7_6_TBLFv^+na zqH_aee*s;U!~1x={joTF-sf2_{fx{P@QXdbj|};9xTpM#dk@+}9>smXvs;0^|RU%J{vPh<<2(cd~qYt(D<5 z>lezCMC8+ZW##_WjDNL-|JoRS@(0Af{Y!ctu_Jc;BYtLRXOjKQ5N`SH?q5~GFMe`Z zzefD?*7G#J$UL}z9itP-G*3H1`|G3lAE`5bS|^F%_kO3&_x|!$ zGxrm9#?NtUXlK1w)EPhJ=c4ob@;c+Eb(RQz_jsN0TYBF8A94I{HT^^R(X|o%RHJ_I zuAufJ^)3D84Os9R@y}a7X#dRH7N^U+@lre+o!8IR8NVgB?&EdFZ}E+{z0UYkar$^y z))_yoS4Qdo$vWew{6!T1r(*aif0aSJPj;U3%I( z=jbGOM&fIg-6H3jX0!aBnuGHz9|zog2O#|J74PR*ay{69<5vh6@~BI&4oCTp;9RGb zX`k9tY5o2Vy`RkZGWz}6%yRkOKxVmoe*oX@L4MgmK3~2|kX|Owon=xE?{mG6x+D1q zY7g=A4F2>sSK&{zfOUtu(ckN@k^Ud46a8`S%(F><_d`Ty%!b}4)cjjKAIkl2dcO2j zW&IF=C(3UKZ@yIU5@*zuhoSXKtV1+?F}c2X`8?w=jX%iq*ml4d`sh3l=}&r(nAQnM zpAz4SK7EGi^|P|{;<#pM2v4S02)Fd9-XB(eK5}TkFQoU(^;wJVKezbZ?5kSs#Pv;= z=$ob`_2Ks>Y}bnN`_lii^i9{o>Kkeg`UZa%J*wpcZ9cL6k=_`e%pboU$5H%VKwehz zjWhVqqxV4yt+}%NNB+m!2t1MhB5;fUs^XWpJ#4>5{Pui!cyEKgbBI4#2d>7S#X9ll zwQ>Gz8hdv56ZsvU@8+Kiznc5T@5b-xHFDx-18=$9x(8jNa6`|k`EHB<-2W~8G5PMG zz4d}0|JJ70Nw30RZGPn(>sOOU@-GX4ykOWh_OBX#@-G(n5ihT3_Ecaf$F6AiM-&$tf#e0s8H$Jc4 zOXuki*yFPG{F#;82c2VjtE!w?bo18K8Nbzk_y5K52Yk^`CqA(AM}HS@53S5ePXv5s zDpU}Z~ z*Py>}yi7muXY`HT$nt)a=JSo+FyC`?^!xw%9D^V(%j)<4vuEn>wwvg!RyMC|}LLr+m)Z zh(DQV&m(ZlUzqni+xCb1yMX$9mCZ*r@~LevtbChCat`_%`&l!6Z2VTf$D?(Ndg8bA zhDYx`%m=@%SIJ)_`7!yx9{h?ugBtF_kSk0KWF?l{r<7e_$jWB z$hZ5y>Wts+{}&b*|KHaczh!6KuhtpAO`m_LGk%IQBlGLMtj_pt{qbT8KlW2o{&mj$ zZ|lFYI^!pM5T(y(3_s;(`Fr>7AJg;l>%Q{+jtD%_-;Ka6f7iXc3Vz9t&3#|XeCNTA zoBkw5r%nnOR1b*tK^{lA58GZBr1@Ms_kF!p@zZ?H1OE&^?@!}?p?UuY-vN!~xm%DY zDYAXB*3-JPd0zigvs+JV`4_Mm;pxusIvvGf6d$(y#KeQ<9J}-yjxQHzUCqRUsuSaZ z{F-&08T6?&UqSjJDsKhc7cky_y)X6yay!V$VA2qHwFYVXzq2*#J z^1qBX_>NHexu-=g^!Wi=E`oJDz3v>+*`5al{+{pqZjpWHGuEBUtaB`VeHr@KsGpV| zO4d(H-#TL+7F0j!b-0l3L!>t%c4eXIUb5^$Il6ziPISMw4Ba1nR_G43Tw{LAjyp|0 zr@fx7PLrM2-zgG(hJ6MD*r0&-m0R(@ z1Ak=c&*j>R_duPCkhitqble)1qwk9Hb+NwUSQWmo{Wmc@kl*2J>-YR-`(9l9{HONEX8T@rD|Gh> z&`I8lZvS|>^>*oB1fFQGBXG-~0Xg_RDqNG_i>AC7{JPUNZoI$Ccs@PO38kPi`-)Z_}C98HkSb-di6b!~Z?_SIPG}ym4xu?s*@=G6FC> zqj;UNW`4`jdw~JJ0UhE4ioOP|bE|!GZgob^t@bl&iu~@?*}PuA%G3H)>l=(dgP;tQ*)PLGg4IIr^i}8`7@5yc(%(O6yWflp=kK7OZCEQVLhkUr z8=f!6m-i5Zd76yP6VCgPa#}t>bUkltWZrO2P)+A31n;x+d!NDlp%MNrDHad)`@Ms) zbAoc_d@;Os&*y-&eKr_ZoNq0@^bXN@B64KQnect6f0Fq=gx89n=@)6g3VwZlrNVck z*Aaish&$-}c{n3OpF0lgqUQrFNc_-{hrXlv9VGeS@3)GaDRl<(i_i5N?RqHBc)i0! zFaF-JsmFMolQ{RhPhs~O2*z@)-*Z*YHkKDm4Dns?{Z+_W^gA>u`QE*18h4XkxPpDKc$rVil-&^0^YP-p5KhN#M3o!Gh$7#-KRjJr_#DniwFPHoS zs%@y{d$eu0S?v#U``G{&C+)X)G|Gu zOq$QrU-n{=XS@eZ{0ByYDu6w~_YVeS9bW5imC5Fb?s=_GgZHSj=KbmP6v0V#ZEhlf5MUdn=BMhJVR)!@8Q}$skV`z&|>IQGllH7z@F@%K90zs_~lh8N|N}&S#jd zovos$(Q(_}s&)FisO$KAJ#By5Vd;z1@1y8fCI3VE38ev=_eAKJtS`&^A%utMD*A~0 z4AEChjr=%t9u|x|{(8WMuh;Wo{Ff2tW-H|hDZvqzU!7s)}OWNF=ICx%yGF?uSJ#ek|%Ig2Lwl}$DdVXFc_Kx_7xXe-cKEAd%ur+N)|AWs&&+B$FX`l4m zvL6c{)~@Gs>~FhwWCz6_Js-CN#{)ZneO}Njh`%6L=sf$IVkgF^ z9)H)_=zYt`?XvNAI{S6`c;*yrE$4hep34naJdxrk}r?E zpK)6B{jp#3<*ycBri{MV-vzFy@A>-x;@=y+ul+6U-!K2mYI;8_`aGYJeZxa3iqoK% zvvI$D(D?1CORDI5`3{=+_0{w~^`HFx>Uy92MDpVyV<*Volbw4Z`l-)D@@}&7=dcUu zJyZ^U4(cGD(|M;f=be`So$<>MVvs$}*1T`}JofM4JF-87{m?`=azyBP`$3IAi@cb` zL+fQ-P}33bvC4kzY?{V_dUM@B&EE^5_8icqynVOld4cS@nq_?QE=`|{<-2a}hxPgX z!Mt7KWG+Pf^1H%^?MKbLr!o|uVP082+Fz&rKR?5Ki|72^qu{x6dSxFx&iCbdh+vVg z$@FIP9RDHzcq8!>@{&A%%d-Oxo@Ho%Jn}u6UW6jJVtz_IugCO!`?|bJovHPY^yens zpVg@W$#bR$D9_o}WB3jH$o!TydC6_kKlxr>R+)YIs>FGtkgs@LZEd=Jg#+5aXKl}~ z%x77Z=6n`LqWLVdM!)a8W|sT(&HK*sT^fpW%ll``c;(}4%-gdQpJAG`{!?;(eLG}} zvIy3NAAchrvdh_AgjX*#?qna{lqmVZI(V3apl z?Zo!s!H?iPV%tCIaZp^q`hITbTeSUyp${gG{EEefKH87SF6{f})LxR@*G!L}*NGng z`Rvjo*q5m7Nc+di(C2#Lm-c|4UdI+2q%#OE=1j`ae{+Z*}9bFo`D#-DpP;(4T;HzoJb z+;VPiz2CE|ZGSPkVd;g}LqDLu_>=E`SnvC`e_-Bx!gja)y_Q}$&34!5(b>K4i~Q~Y zBA8~Z&&c%?*1jv3DV8E*%j?xJ&iLI!!9NJx!|P76E|ZU~*HArv?%hoC+_9_jKAi|W zaeXWTxAM?R=l$KUIUEP9z&^DC{0{faqDRmV^Sy|IylztGCp6~F5#a2D>F|kzAybPvu}^MjL+FNdXD87c+16k@%?(3r&-2% zE8t^Nzh%E($93iSbBg1@2t1iT!}>OV+H}FRv4873103(~@qMt)XZCmS`E2RcX`kUf>Tw<>eFm*ZrcTNJ zvJS}6-RKXrsK%4g`@AsA#J5V{ug>J99ppbyAB=dehHoOjnkJO*v0W=q{ek@?zUgQe z`ltNL_}vp(wsRfN(fOo+zh~Lb3p9Ufx~Gtj)%~sScsA^+`NAEjv;%<8hO69{{L8<=H=LpdTogR=(oz5h7YOeu8{I z81Dm%--u`db{bqR&$GNAi1y#MqCOm2hreIkd4uS+j^B;T1C~))?gb3pVEQ#LDVDrK;uIc!2MU=n^B&}#c>zeO^O$()Vd7T zgwPv~6Og}PKZ5-HDJp-Q$Dsm$9?Nj5uj^gfpGN^O1ov5!}zp`w&^IyN{!v z)7oxP7YSbDdk`J(!ElH^tFeAbe%J#V-?J2Nbv&%+OVbhbwBoIljzM!GHwqtSm>;?}XgUS! zp;;3La9)HMfa!0=0bMVa{&}~;Z^mA`&PRHcE@P`IuU;|J<>UjyB!6;_svpH#+%zC>$thY4)Lq+=H)rw zV}ZTG#@Rj8u4({3fxaC&p6&b+nj$)@9DldG3%+NO7%kgalo;=>LzSUoeZzJ{X@d`b|`ZeNLslfhG zd`o^4$Mv1}Ykm##DNZf%E%500){DotT{mk!x!e+s;Rf9n$$C<1Yk$U7hx~Sm(8Qe}wTApda68>>}EqPrOIxaQ0!Y%>IzK)fvBi zUf18$8NU_JxSw8N{C^wAZ}xN69B13-MBY{>y!1YJbUxn_$4lyGUi@Ow+k1PR@!Rq5 zAJ!Sa-T!wkF#g*Y82`KC_`!$9j?U>jtKNI!`0CCt?_Oa1?^s~`S1mC99fChOUjjWn zU->PQpAmZkzhprCm9G29zQAtA?qPTMxw&@T5r=o(W!=9wejn7kd&cv7xV?Mh&$~Wg z-QQ>Ki4Zuyg#0OTwLmoX&*608t_S}fPw9LP=YbHU@pA;rC`)j}4HBPlzDN6I@N0P= zY&E()>o?wjV}*=h_IoR{VEW!O!{edEn=B9V7MY-G9oD z6fi$Z?=N6_;BH>)*WI17UB4#r;P~bursImTd?51_!jtDIgj;+-Y)J7auCgxjUT&{E z-_3X4UPbt+Jxy1$PI9{F`g~1SKF_8*q>IfzGR_b_U%I}p3SH+Z&x+$R%%bQM1VLKA zm^`vdao%tx*5is4H&cE==0WyLnSHq##p_aPO^y3B{!8|c$@}2lU9;wMy*_C3p&1XY zYen=U=}*?1-ZaxQoS%BnANgk-KeJvA=;_^ErCqKU;j1cqgKZYlj-SfYINfP9CG*1X zjo=&;evV=ItiTn}rW8OV!{k{b7P&wq+27%Xme@33;{i)*PxW8B5 zNAk8@?oU#>TqaNDV6M)keE~=hazCeXX@38Q%v=;d%JKdZN<45LF^zoVtyCXV#{I}D zAN@eNGkISG_!J{h-Ul$hG+xSQ;T|1c#_a(=>!stt^Y#4P$@0E&vyXa;-ghUw!MZj~ zD7BZv(Jy4bUdet0CXL^p4(u!O712w-M@@Vs_S$hcU%3+d^hy6p{-?XWivA7Z$@3k; zZT)NOZ<~(^-WTB~tG;(1;1N4cuhD^TX&;I2yaWBW@y%0zE2_T_pQHS&&zcjzsx`MyiR$gGI2n1+yQlF%o3nH)!^i z`KZF{b`1wvkNTd^`tQjlxA9duU!`>l?}Lks9l4wL8)g=X9X;MC`ckP!__d7UW5N1eoy+$_*heP!57B+rPv1uOS(RbC z(ET*JNBrey___L21DDULMFPK8zl{y7*VTs?$vSo86PXvGzFNGI)@i!0()d*R zol-9SMt*)uL<8@W?0Jrs%NqAd_UL_*sr&+sBiJ9A(&GsBr5+PH>3caJ z4RlY9YdUSva9*z_O7lJTbLEMD99ci?*~0Tkb|nWnq5A(I_0sx%e}HH{p`WE5-V?tK zsej{NIuI?wXZ(r`)}!H{bU!3~X4doEJ96>;kJ?YcbM059GE>yfV0^s}`yJNB_FW#A zbxw4sRs5M$7K@kHS?hVgWA>RVI*$s&!-*P|lv{P!bcXJuU&eCCegF zeQ;drnJ@wJ@cSpK z7ycjYYtO%ne)ArHeJ)^I)u7%dNasx;&LDei?Din+bHkK|=kc8Gr{&%~m2Tm4K~#D@ z`E@-%5&eyGD>#pUs6hM(Ka&?bpXL2Xt3aUw>$#lDw4pgSH^}~TR`!R%Bjs{E9}+*W z;W#RwU8SBY`y+9FiTIhhOSs*8EPS~*KJP)~Gq9g%)lwce`Ss$j49UJa_z6r0vyTYe zkYaxVbdY^P@Q+a+bdl%Aj~JAE3CgoS;r+SJxA8v8z^}mMvi~60?L=HmIQdIde+pQV0zdExv^WIT3#6AwF>ckuhF)!JVnyB*1^(>&D0 zp3`|w#Gl6Q{sqks+U-UI^|0sUmqhV@q|W$ly>H$lUa0;*Ed5W;CxDKYf8{+Sas`0A zS4!`PlANq|;^$Q$o}hDYSK*ATBGX;#vFzN=t5}6LL{EW0&~K~qf!|A+UUYL|&#N%+ z6`TE2vwOc7a=~_N)#dv763b3e#}#%E)1T&l__>IAajV}~G|w?lo%9^{$lI*CO7&)X z=??T~eQe6%-jsuXyz1pBcSiMc_!8CZRIbV!L7nGZI2HU(`th6#2mj{p zw>UTd_Zb(y{dZsg(SQDQ`uKO$$F_g&*jX36_NQOXyy{K&!Y`=sckIH!3Q(nvRc=4s zwRO5u{q&l)%J9!jAD=ujFgacUN$E&w*P+SSl*dZ%Iy62Gmgzq-R@yaL8rpH>iru?0 zlR=ruBb$qpMeAVzRcgl(e+UKqQLg3>PcSC}=Ptp~556249@(P{L~73XIs18)s!8Q% zJPX>VUe0I|AL%pwn;70RQk*sth;1UN^I948Fb!d_xuZ3(LSSEl&7jrE!1k z&~+?l5U}gVM@ROU84AW%rv6L6_2gbtc9z#Q(Q;Re>_6)FUp{d5`Rh0MyNVhBZ@)q zjo!_)&+%5oVT{&@|HgEW4~&l<9v&(U4G0eoj1TWBVNrws_7o=uP(mQ)VFU&0PLA#x z-R~b7+cRDqD*2N~Fin&G?&0wX(sGv-wqLgMiY;5V_wR_*3V&`c>?rKq+W)dkcU%@e zjQoPnJNLhaFP^MaIrFK9D}mt#KXr0=G}P5eg(r{f7@aKchZt`iJ}^A9?0D(mq2ck; z&=rSE<9kcRAsfdigue*GW8hmx$9IeuMF!JzuwG}UV=>?A=NjQ=;w4KRf;@A|F4<=c}L3he{8g}{X>0_yq~=&CU^ul{oDoRQn+r| zdK%uM{_`lPD)$HYj!`XONo6Xk&mTT^JgH9btof3>_Nhj#5MO&BL; zCOl4OiZ3*S1Zbl_wEtMP$bm!qH~M=@ll|j+CbokWCPB@zg`-+X(69Mug~P8Y&-+yFs!;9E z&@BYtx_%=#ljiVJcseikF_AGvIZ@mVtvIyN9~-66cDT6zN-k~4R1Us@kmGOHYYxhf zGXKJF7@ZhKK9K8NuMFA;J;I+#N zf23bnFO(@W@}pO7eE4T~J#y$&=fQ9PsF+#Y_u!syt~mQ2I|gof=Bn*~@-gtI&7zqo zDnlx=n@K$ZTZNjg_QS^>hp#uh`{?$V&53q4v_FaH)&1KZNSd|)MO42{!nwR+e0a|={37%Bnthc8 zc=yn5Q#A2<&3mp4<}#^3*uH2nyUe=peEu=}S(W=3|AzR<)9|++MT+2nziD)2vUG&J z$(}06DuL5$S_Y-pmycWu?TgrwT=PT2Ba<&WS67U}XKH7B1*%d5fGw{AZ>j=cQU#u^ z0>5fx-^l3oBNTQ_9vz!0-CGOWckJA9Wq<$9?OU(dQ3aU=hJWIh$)5!}e4=vtMHMG1 zs!vs{9R8}YDh4XZ9|ehm_#guKf#MMacBA9NlSh$HM@$EfK+z~X`rKd%;7~MSAb1Hf zQewuV@R(uO2m<(In3FNM=^DT(Q4y+xK;DyZMU8PrR&k=V!-1Fz|-H1pBfksEi*kI`8T4M(^$4P^k-H!arKl=%fgGXcIrL%R@AyWs?%0K+y{QSSt#*a#WRHhOf`Tua_{;tsjWBW@{ zCu)DOvi+@u`<`37eozUXe#@Fi8!8n5jLK9h(q0J&SaTJ4#mTKw_ghbQeEVP({}|ge zEB;|^z2cwFTtNMR(u-6R->jQ@qvu0rz4o_m+ zU4_5Hla=Gg!v`vlFTy|TllZTUSr2+dZbi&s2N3xItIp~d{vCXmnCs6441A{Lm-=D+ zF+4d@+CO71%eW;$FQEJm)vS1R?N|NQSplA;9~XZ8h1-&zn`htu$t&JsekWb;dg=C8 zBt6G7-#m}aPl9~E{I|DX`?5_tHx+hlx@_mm`U_X$=hm&JHg@v8c11<`M|NfMiZP^b zCdd8Wiy%QGB?RJ|gEi&X3|Hp=UHdAFI2SMe^JurM)GHThH3iFDi&dD5#Qi{Vz}5Xnr z&CQMYwe$NW=kC|qd#}CTd!Kz&M$unJ4d|y*!`Y6^q*8?kva6jpDhK&%vX|n2M|sYz zO0`|B)RA>mJEL;l^mJ5i9TN0|pv-0|tqMO-tJUpFb-YTc>!|)5*Q5Ln&i^CM??Xuz z?Wi9a#o-*c$2jC!LO<00UTKeCj=Dhg==w|X%yGY0C{>|)US9hDAwZUq#t?EZP&Cp! z*UPCbNGr8cdHmdPGphQ29@+@;u6iLgf!e%WJ~#sSm#Db!nbT^`W0tgs+STdQpW3vu4~& zc-(|ftV`h`;El>37W|6zb`JA8oJdJ`QGz0vy1x^I~0x<7hs)_pO? z%5>UC_~!8uJu9_T@Tgv{%ZVQ@P;Fd;#_Kp8hd~?ovEuMIbb5%(ox@cA3ZA#AKTvoK zc^iR;U-Eh^LyG5JcwF#Oc*3HOU+MfOt^B7XpL)c4_Ph82xE=a=0wvWt<}U@j5+4YD zRcAJLx+3}}z9v3pI(Hp{*O^PCbMTK^6)KOV^H8}(=WeD3e8zjIeVy=mM!S`2-K^JX zDUBDiQw1mYJUj+WT3v(nqj?uFf7N!Mk((kYcYq^H!6^!f~<9q{DdQyeDw zqr8?|)md1NJwBi9SN)JT`$3ijC+p6VoOhHvt8PNOFCaZAPiH5}Rq-IxfGQTbKc#NM zim5j6tscjIE`Jy>0METg4HF*P?$Y>Ns^>8$;{rgQN81N<|4V^Vny2D-{pH=2G6f~P@ulIHJV=KYk%{s+zcW&Pa_!S{kc)%ec4!?d2t!+K)f z7u@Nu`vIQ!`!(KG@c_@u;J?Jc<|3=3DBc^{>=wYyRmg#S>FiUjjKv&T^xc9Va z{JVEs^mkhn|E+UEf30&ue@HX^way9swY^2~-`1w-?>$lab8ln(pX1|yHj01uCT>so z!1(ZY8_grYe^Ky1T)Y+Ki&0)w15{1Zdmr+J{%+#=|FxNat1JB1HYf8h&hh+TYUaP- zR-wPv!`A$7j?TaBZkd1E-7U>pNxs zz^}~zW|{|9sFm_H9%#m{%)8a4e76_)dNZC&72|UOY?M2Qo;`Id%kS@Kdh3H`Wc;{- zk4eNWIT&fxO4~7Jc6zAK4wM9h6USj9uakJD%W6^btzf zZgKuZ`ftxAz`r%h7Nwyg!@q&|1U(4a(!^8JFwWHo)5kI`?8Mi>A+*TGmuNR{t>@D zD37P3pxma@wKd>3C>NEE160KgOQKg)yWkDwOh1U1NnCL_Gd9DIYs4IzF!vcEfFxwCvAI{qDiEl@pz5BeW~o(DP*w^5l-g9Fwf zO@9O6Q_wZynfOCMk?5yxJ`p-s3uL}9yEty3x^+6w^9c0nZ-eew-xegkaHjZ+?wJKO zSB5P^e%@9Zk6RFWbz4dp7x<9l$Zb9!n*KCB*PzR+621mt;hUr9L*cXEOg>bgmJagA zNR2VQEEGJtLuOqVpT4~ViaCYssD+m>KNo3Xl-QeOM^Su-;uU09k>3ts16!2rtJZ9X z`T{*OtDSy43GW1FSmgA{B;R0>BXdery2#x*G7sUJAxn=lFfTTBJO+2lyP2U~jg2U9DeQME+;t2UW;^Pzab3 zpT8J%KUS5fy*NL_hR95t(PtRs}=yCTu;YajeVcj_WN&x&RCwftVi8575nS%nJ&o_ z{I%RDen64^dc-vfEKmLs}s(Kc%Mv#^!kZWy>9ev zE+=|Dw@u(k+C5j5xL@dX?pI|_ofSnE)=b&8Mie~6fnF% zuj~F6TkzEU-~@VI!|^g+Sf02;pc`HfUe6x%PyQ!aX?-NuwH)a}n&M1|I5FAlpjoook4j#{{`jo@*^m>?H1Y32+He)FY+U3Z^P$( zyQ3e!&$m6ox4!%^cH?|YV(f9T+c06)YpPH9Y2n8- z-)cE^#-kz!JKA)<**_vXj^tXKmKVJ+eO9$@TDC&9lRehDOysQle3~EXwd?wjd%Qk^ zXNvpiby4d@&SePyWJgJyL)MG*IoET~A$TGBvFS_nQ$T<5`0~qDPR51v1A0FgX&Nut zooj)&^?LmA{&x}em~Z|4SsAaYva1hkdA}j-Ps4tFE!(f?Bz^yV5w=+>B=er@We zc{Y6J-%xp-@N4>q+COTJe+YSN`-kwrnEu5-Bz-LM;5^2I&(BqHTERn59*>8h+{W|y z&Be3(LV{29uj6q%UZisKjL%(AL!PLd*vtMoDYZI$u4mB-!K-^=jd=pys;>chVm(RI z)N-W{dVu9jM$RFW72EHN_KSXUFM>WQ!^j$zb1%hNpyUN;UP3;9F&|! z`eV^%ZLf>pj_j<+6;JG6WGvx&?*(|so!nPsU#xf-dW_^M4ld!m1O9T`^m&~%R6qA6 zsZV5=8ZW&V^MGCMUK}4UaWBEVVPDRY-Wl@cKiQ3>?|@|117u&)`K-WB8RqyZ*_llD zq+e(q-K}VnCI?JB66bMnZi)Iq`v%sLAi%y_1p8j|BjKYDyEh$b8u`B~cycf0aRq#3 z%O{b4L3y2giTw|)r?NsnzMrM(iST&=?+259CVa9zk%uLr$9n>ei&ZYGr z`rr{BL2=YC^^7N>@Ef&bURDFXj6dkV-t`fD zQ+$u)UW<+w2mZwG)#$&`c-@sz_-YaR8;i(pa^8}-UPnJ)6W1bn1Nl<;b1m-{X}T&p zt10eA=qRyt$*s_Ga3#mlj6c|}{h~Q74?VP4=f~Rv|M=BBKi@yq{&{cW{2KLVtKx9l z|5WLIN)X@A$Hn()KY``5>&N$H9{};VVb5OJe4qAZP7cN8$RDS8K8T&;aK!gV6VIpk z1KAC~{&>FZ^BuK3aGM>^*Kz{-CmPSsYWjDPPx{LEH6nL>|1GfN*e~$qGHvID;t;g1 ztS{Z`N!}nu{?dBGJy%b<1acPT_y!P@3N7r!RnS@i*qQ|DgR7m4ly)bpbiD9)KR^I2+=dvVRZ% zh4*PzV*Q37EbaG($j<~#qy36{_V;IL|2*(#mGxehQYDPz7Jw~b%N6}XJZIVd7|!$g z_oA2ELisJ9(e?@i1Dz`>dJy4U|6oDhqj7K3e))OCXDv9_dW8HmoSUa}1TFCAE0k9G zi}_qqwq;o6Q_%Zag`Liqsl2c=^IWc%r}GAmY8j^UyJ$D6^GBs#exKk$ZIkzdpce*F z9ZkaTnRUQZ=YI7(+nh{bXOS!>+!C z<6sg8!8wjQz~AmIv|b^<)n0!YZ=eTb=_}OUrVq2vW&HAX7y_$W%bmUKkNa^s_m66f zqrrF#v0Efw^2gEs^uApe?61{0b6@TMH@IKy@7s03q+Uh*;c%Rl#s&Eu$OFO%?7a5Z z82ZJ&J>)Z`uUiK`#uNL30p8f}Ui@gHd|-YHmB+4MsN7mVm;LUbT=lR$+5>)#_q%zW zg8GFz`&0COE#n7%B;BRqFW zx*b0E3c^#DQv&@_Ttei;7a5PehyZt}Pav-s@CeV82AuYHv!0@LzWGE4FwZNT2Pqz(bl%Nh4{QE@d@FW+TA$x@VbuOtMvrwl!gsRn zgwGY~VvOTYn6Co8Z}XMRHz<$CXHXucyDki0_5-7EnA5V`kHQ~lbU(1c^>b_O2Z}!v z@L@c@g7PT7l>53S%Q-_2eV_-}EhOIHC;4*EeLeM0`J$()>^Pyv>bRVVD@4!xw9eOV zE1sd_GH>Ad2l%n*QO0xh{7v%>$5j-?VFJ65&P{0fk04~x8lv>=WBRm zZO%_mG$_Yw%BKeD&%B>hQ%?vUwOqz}i2oemLHy_B_?r07L3u2nB*)jZ|LnKd_C;T! z|IBy~!K<@B)dqdWbb|OQ(FJ0~Y`+`3xEK12{6EN7@Dq{4X|ivfU8DD{Io?qGBWdUR z&p7@TM}NgRKv>6o$@ZB}6I?m((=GR!s4mxxOYYzJuC}KX-a6-TL0?NLgU^dV4$*n` zDt}(&OrV!-dgu6Y5MNA)g9Pm@J*7wwgHD1th(71GRLS`b?DxzO9r@?pe1C|~H+Ga^ zfRMjr?zOu}{A&N45i!yQj1iuH@ga?8<;0$gD?Tjc9f&c4Zx}t)+I^$(gT!y1b&wuH zvGJqLdl@UG+KjS8j7Utu}z{!@H@0s8`>`lu}amwPAY|J1-0zufPK_@CkChp8U$YT0L?V>x$R z3%^mjtWbsDgm_vjWc?B9m+i99eTbG_hS5QOh01Mt!}Jy^ucDi%UFLoW@X9=UbKhWd z=LtKH=lM6(Z)lv&p67={^IT8!1s-UBo8qP>4egq^kN3BlZ$u6itKLg6@8u}SU+g2- zl!qCue;SjA<{nQ>E?pjq97HYBM?;;3M7d((XM`Z=UE&{>FjB|4LPYXNUQ z_tthmk5ov${+%@+k^^acAJLb7|4g5JZ!YjLul~QK`~OG{dd7Gfk?-zDe@*lImCUay zE>Ci92&;viA>7}g3jV#U+D@hOfuVBu6V1gB+sQ+nPLlj{KPmWgKZS9X^|;r9ZVnr~ z>T#1Da|`Tpy&vIz+A7y}2l>fl-}v{Y9M<=tjL1GgmA)bSfb^1b_}rEI8LfAC|K#ZD zZ!vrXqJI*reE&7=Q^4k+G>LeA5>?|z4)I~Ghx+FJV;Ar1(*4o+W4?DOo>S&(A_qLA zX&v*LuX&#V>63JQ;(Mx(J9tTMxU~O^^xz%nZ#D91T#fWc{x*MS?@)QHJrgRo{0EotkOrUafl&K8;WsP?$S)1!jouF|H&G7I`Lb|-bUvh+ z{jvQXk^>)Ta(|1h`Tcd1`x~_SySvH#k-rmOzrSj7e*;#3?{9K{wqAT+ll!yf7NyHg z%2fs$Gk{0O(%dhQ)G zj*uR+^r-unR{8PPbH>hnXDYsct_JODvKPYZL+Nr8`I+Je;r@ur}uefAC&oej_NP|St4IU3?NPyl*jT#P;Sm2&zo-V zm>=CPe5XaXeKqLzE}>hJUliY8_+i~1_b~Ezu~crOyf2s4_cQ0^9Jk7^)$hTrmG|Os z4gkZ917rrIW__&em@yHMu|AUR`mV`+LT5?(f9({@jgJ-kC|{Yu>*ImB;dRsNB-m z4cl9`Kg#0;*~Q0A$sVEiR%m}MtL+lr7t!xsK9M+2jsq0kCmkDmUyeMk=2cE3&k`dsV0>3`ci>EG7>UFrINQIh`eI>z*G)8m2E^)mJs z%Q3dU_E7!Kg^BWu=xY?0CrY>g?~XsC=#IH@>CL`t|PTl0FIBgHv_Rr{4Z- z`Fu^2`?Kl)s^i?>M3eio_dzF{++PRahw0_ECiiFacYl-nv-yGIgXJdL4`#pQ=KA`< zh~&Idt+>-$Ysn9sL+ALg#I4<%@RajZa?ikEZnb<*KEGPNC!g6u-;?iHY3x6|FGTd9 z@555+fW8N-z{_oZU46xe6k;!37_Mvq46E- z@smEQWBjVemHiL@dltube{u09BggUnC(H2`m-kr`_7^+8A#$ABVSmxYN0d89^b^9H zO&1`add`n4eD|C8ee`>(^Nvr=bAPJtdH*6|-XD9K%=_a}`I{7%u6N#x5&1d-`#`TZ zi-CsDLmPYN2;mLj;`a{%y87(X1iydZB>dtXFTyYWqWhjIcOLQK!uK+t^`V^QZ{Zg; z@WJul5FgelziPtg_~!iKt6t5=`aSvi@^RteCgB_J2Om>uRbd&qD=l7?z`onuY&xQQL zJsfmzLMPtX!+Y6JMAOKJ#S8mJEq+3todtTr&x^D}J=}nTe5Wcur`xsZC;NqXztwKP zklPpSd=eD7=K?;)2>1)5G!EnE%iOc$b08Ko9Fx9dFPoTr{%r#o);$Vx5;=y<+1uMRBrhjG|ljR zKvnOZlivxyuIcl=Owadk2;ZUc+IBRLCsf|(eE$#kcR><<2|uc@Cn%5aFDSS1OZ!KJ zzY@P!m^E|-GUM}G?w6^5RY`s)g@&hewb3qg?H3k^9{XH|n_7CW>QqzQVWP@|;E=6}IPE z%5q*UzeVC&{{8NkOT4OZxx}wpTI9T1ev8DlS}v3P{AD`+m43fF2$Spypozaz-~CL} zKMF8D)d9Q!|J~px9+zGZ$K&-t1-ZAy{c2<#h~5eRKo+0hQU3QsH=qdbNgBSXdQbGQ zyeI14kHYWK`s;#D_&xwQoBBQJ;mAGhEdO{W(-{W@BI??)SSm3$;On9QNaYf6WdmS1Fv=Ifi0AP$_50!OmlU6$ zqyW)%pu>z}I7I?5KRB@S-$+E3@$@|SKHeS=!z8-ac*=M<&pE`m?b3NN{PB0gp;h(w zbFA-cyB!k$?Z!-LTo{hr>zmi#mjGnCm%0b{xo|)FdlX9F2b3xCcw98pc-Qv;F9u7H zJX4u-L@vaCF9{VGemCC37WjGj?vdud4Co&7fpDq9Q(53@{QKW)&oZ7+e=Cn0e8F&y zeBFRsLqIPCj`f-5d*WlrJuEtY0dGshpT{yG2Y6ZXY_wqf1HmJ4jJ zhxC!fr&uk%kGXrZmK${5iPp=L_u_qdIIiV(p6^LO1=(M4{{{7KhrFE7`Z!)L0-GA{ zN^J)jyJ*Aw?4la-Q3d)-MZSmHJtOmS{}uh0LwK;|os5U%H~27AFJA5?>u<8JnckTm zNk0o;8@q_o&@)T+8##>yB6=}&IcxL`x5s9Yl6&P_z+aCcMSMc-uQlyOt{rt8-S!9m zjqrqC;^Ry-9z}fNepuI2Sikj3iCu{@vU8E9X{Oq_k$ZSR-)}%uvZjf?Z-tz}94pLc zOlM5z)UUylqat{cmQ=3s+jXknK2;BDeSv$|MDJ4%5q%MS2SDz-H|sR)>k+x%`|BDX zi=uGM{r4D_=m%dOItg+s`u!x*6DtT0^gePxS2WFp_BHfFE94ZzVSNBN+4UknCGG0( z&ygPBewDL=%WLTYy4Ql?9wWI=?|aW{z6o+()1Q%FTCS@l_Zhm#vHXTr#r;73%KoSS zT_sSvgG?QqwjD0FMU5P>APL? zo!Bj^Ys&anpzAfbcJxL_3X!A7M-G1bf9d3DNheYJR&{++(}~zUs_UzUPJZ2TF>Ag< z#&#UObGMrO9p5g%&kE7=3m~r%JLtxDCGMa!=8@frr#i=>xP$wDmH1hoF#M3=_uNq7 zG_s$%Kdtqfmot9U>5+WptdMA*3JNd-7XooH++^ z3HTN6f2l2O7k>%n6phc5dJym@iN4k2_==3fy(hbc&kGIaworT}ui$?IZr6KczIihB zk;WJCbev_Uj<4kR>G;Y?H2$u?H1mbVqx9byednx^_)1ov8_MbL*yi+me&)SDwzq(8 z_>VVHf6f|xZfK1@H&iBn1%F}>rR%E>j*!9@>-st%5gyd9o4z9P6QUar`1a$sHT5sm z+ztBCcEoYEgFzUud-tIKKh<>AqSJRux=Vk*r{=wtMjotG5;v^Z599kYeZLe!AC}am z_#b|HW>WlF=dF}0_I&r(!uAUCQPPzA$qs4#6Ysz9fEwCQBtMq;(Co`^I?nyQD!sqL zLzxoEA@?iuJD1o4JD=sLGrx0*-H34=0UuL4;%E0hJs$D{As=g=&%O!%31lb!#uIa0+>Fl&%I^^e@A`p zmA|ud)bF!5dOq^rbUr2uh{&lk($}e~en+%E`MF{rp&yVFw&k1IJzuxoo4bzux zk6c%$T&b6y?RfF*aqchmd=uhee9i-QsXSj~o!gN3b%u96XnzNThhiJRKR9N1265&R z>}vM6{5U+v@!k7rzP;a1-~SJl$L=45%B^^vqJ8;Qkk2P5_FsDt7cY~Y^&O38V>jir z-A?kUOYCjghtm50u;_o+oG0P?n|mMd=d>?v2V`%ti69CahHtB>h}IVhI|uI_PJq?^8Pp43w>jhCFxJCr~TmG zj|pF8Jj82w9QnMqC%c87C5?S?j^p&cP5SlsOY%k^s?!ftER6O$OY&( zm38F1S6Jp!pCUZO?sNBU)9p|%;t$npx7hEC4HLf6YkZuD#>49--}w!#Z`}H?3YEv> zDWD%~T~uD<$#Fc!ON-+8UhfVgZ=(9j{T_`wWIx#BA$xFDs62i=f&F5Shuib)IX-9W zKav+u2fe5HI>2lEJcDw3oa+a??^yC-*L4uy z;f2$a{36lA2*|>IA$`W}kuB#!bbX{Ho(Z`O2y_mxll7jf_&c9Vzvs)d4A~P)(IfF) z)j3YhmuMfg&$V3a`<&=~|2&K<@-l$m>EupVMStA-yNd{VQ9k|g`KhIFTaK*b^MvI8 zDfj!JH^v9s^(6mgp7qMR9Pm2S{}x&V-Wj~)cy>={c-iLmSWEp_{3jSUvZlAQ2;wY6-58{n{m@V66WmQ13JMu z!oHsfowz^n*MsA1nc09&kDI-t4u6_TBW#s;{dVf=$NcM048%%!gM4~_U zhl>4)zCVWHA@#*RAH=;4gvU{hN8gWVkvO=Q9ad*yoc_M5_!IpHH2kcbUw4$8Pj|n7 z^dS1f)7gpntCn-Ae>_{nA5fP$lYsYbgFk&9s}=MCye!3DpM&uMKYTu^zhwAG`x#z# zwa^j9LHO0}`cKp2nKk|mBq_@K{@LI`ZR0d>&g<5y?S7m~)$mqQd4I2|Z~PaWY$tr) zVDd97H9lqie0&1$)SrQ`G7_(+eFDNS{#6L?gR*|uKj-)``;}4rp2q$8_?2_BpfZj# zx?ez=@eBWn@hkocZ4_zzZj9nLa~h3z>7Qx5eo^R(@G5kWVfw&03|;Yg0gX>zPJ(`- z^KFIH+YP)x@{}p=sV1 z^1R=jI4+rYP#!<7pxho8`^5y`Y+V-`Xb2nGLESZtJUlu zV84mztvIXGC7qs=bm6Hi?-v4~@YhQ*DFdDaTx1vh9Kft%W&gOhCzUPL0RDugQq5Ac%A3=aT>RYe*WHa~wjzoIl`}_NY z^7#IOa+_YHee(Q-j)QW$ymW@ ziv8aQS#%xdOKd}Yjz6Iz-~K;F=Z)w-9g;W3FMWjg3hDSb5(*kzhvim(KWcJ+^nOEl zesup%xry_$_Y>(pj+Fk6nw|-6cwRqF@2BB)v*-2jaqjPD>HR?-2%d>gu#Z*ucR(La zhkwlR_wy$AXUCg<(&YYZ`gkb4zoVx6#ef^8d-xq`{B_LNt={i{)*IKi@%Y{*&yVh_ z3FGr%ll#k9{cUM-e>Of39OwQBzvbhoZyr3({o#CO^88@ua(owl75r@S^YF*_n#dD& z6PNeD%dXe|eyZR99$o%k{d}LPr`z9c(gMH#16KNjCawFWI4F5I7__1|DU4tp7d?!4 z2l0W#j}ctpd^lsM3p-FDt|8^@KN1|ycORqqFI%2yw^LVrq4HS!FjQ{Y(e5v6z~_C< zQ2Tn}KVAbq`yCCyf1(EbM#pd6Poo$fR_CbBvn(5&?|GIJ7HB{84LrYr#Pyf?1?BPU z6O>!)Ph=Lh|J`5Jz-OcD^&x@}{AoK1ewz4e&BiC<{6`=kT)zKiS%44Q-;((S<&EO= z$r|`zlYB(YXNluC!e2VcHtjn=pH`3!2H<0)pf(X)Y zk=Cc2LNDAdR9=H#aI8uC@iHZn_k{=1m8=Kv+u)uQ&e!9hVaPczf8S7_|GF1(KkS1b zjKXm^Qa7wW5JwyaT!ddF-^s47d}oPasAT;O=$vSl^oY)~G*&Ox~?XKguc(=U||h3Lp?U!?Mo9iY#_v;b{%ze#m` zP5KM$hWPOY$ z9Mn%wp1S^XRU7}x@RQDK&^`j$+Z0zN{-%9L=#Ayi)^w@d^_Jc<_mpV8S9pNqzQMj= zy?T((D=$BFzIt%kJ2f5X{e0N}9?Lh;KVkV5&iDDNNc&*r<T$++uxr7y5tpyy;)-6*;fJ{FycAI4F;&v!L9fW1cVeHwLiW4$X5T({)ha zmVf*`j?j3Hra$y^)Na`rhmoWENglAq897#G`#(f#)UNLG?FOc%#;ood82 zYtnh0_^{`1=o}Mce`*={i|M_=a|Q#?&~l{4IfH@KAwAy+Udf*#JKyXVHanh5c4)Z2 z8u8)*#1Zbm{A!(}7%=C_NZvyJB>K^HoulyWiwrPCb`A#5{tiEpR+hh`4}Xg3T;`E$o;#u^q$qEPWqV0Y2_UFzarwch&v21K%|E2lub#^YT^k z{@U_W67?{jy9$-Z(?!tU(!=7f1?}hOuLbqv&xeG{t^U|E*iAtZ{DCh9?$&rP{z<{% zbJnyjCBk=GwnKe{>ysaWH0~4nC7$}6?7*+Io%C9uz8vBYo1Zuy6e^F$Q@~&LxaqtU z@r&ySzYx9cQEE9np6k}paKwHy_60IzKYEPDh2x=jAd&-FEa&jB&Nqu4Ty_4&LH$*s z^7wHE?d@?&`(Pe69?1U~3gN+SZ}>s$tAWcfvy}NM<1Z%Md!qDxECWB#>x}cce1C}A zn3H=?S}xP)+E8FQ|X@0nQ+9e9M-SMa_3B)@$}Gw*p}eb@oMm;+xt z2lUSI1<<8DpQWGA*3VPrN%N3>#3G$jD=&XRA|H_`?hBR2^HI>=<^z(eL(5A(e7l^+ zaVpAXyk%>A6el1GCb?cv@;-hGo|K=*lO7JOOJ2(j<-hM*l=odd*W-1+zvWhbDrc8E zA7s!9l~bnn>JGm5JJ%|9{$U6rq7$V)%K57Gom5|~Y}0;#dJpGskorZh&G?&A|1ObM(gOE%PLDtyOZtP9&U@KgDUARKzjr@K%R=(;i&8zq@H>;qcPqu` z@yG8;4_@WZm*2b2lyVxbd0!!Sy}(U>-+J&&3(hd&oQ!Y({q#cuN2mSw7T~Gjd?o() zJ%zzZ9}d4~fc)+B{=(q7CST6?gTlAa{=xD)wH(p%YB}!HaagWow^M&rb~fxVnm_XS zeGuG_sPB8y_tols29)zYNZ|o}p82pjSM9%NkXLV$~}P1_OrD=U!?e2 z8M8`=XAxU8wBC07;82tM%K<*gJrl3kbe#LEalhfQ#%FQCah~6GP43U8m)Yao-z(Gl zgT7$95Bl&%tmHb#qiFhiD}95NzR{#Xq551L_-hGLls=Un(62q5)c47le2feF6o1@q z@O5fMZUv>F+KApm?frUyH~2=>JF#{>rP>!#yTP}jKKZ+~;NmPm4ly;clyLdqzjRnF()tbjBMWz1F*`GP8ZxSzfqO!td-o zbESkL&PMrgjq*7~lT7&&^_B)DxeQ?MO)gc`^^5B-nIkY z2s-d4xLa>*+8Z0+K7lc955{2X>Sts!8&P{?Y<#B%U{#GIeV&ixCMV+RSLONr(=1|O z`Q_7NJI5yJLFAPrYd`HS()B~ z8d731Lm<$0W@7uq9&g{|&Z!Et>&yWx;*7UrY-*aM#Ched=UsWxx#w=(xGmHw__?9H zt$gL?jTdg(c3zMf`bA-pw7=MOF_}MqrkB<>_t_7nuixVfys?Qu*2Te^Ik0VFrm_cY zxOr^vSasd0(QEdNO^uFR1nXnhXl2BPF#+bSKyaJ#b0?;@O;yIHA3eX(!P%vlzas>`Eva#dw|R5M6i z_e$H}iBq?YhDCcqvJbavTZ(X^TbYU`lSBF=)Y39!Tzh0X`AX@8mQ$e*-GV`*PI2) zq&3_GOY-?17BZs9rYk$3wMNeJCMRY@ekOA6Vqex z96eZaK=6dOXCkc9Ceq169jHMc+jpna5B0dWGJX)6hy5ouR6|3(W^{ijRsHiKt-nv6 z_9(zWDxPPtYiuVg9i!Jh%D|$}?5d21k-R5GH&JiXyVAo;Gm~RsbX5&-I&j6R$|&QS zYp+Q~P-Xf+Y&(-l@`>e&$*`W|55M%R4?TYSNB2F|cg=r2T*)mMx_jrhdr$qBrCV-% z;?k{u@nO)nP2#yQ2t$u#7sZVP*IZYPI{6yqmyYiqpSX6M+{T%Mlhxe2xx97TmFHfvapRR+H(#`^1~97__KTMg zpY&z)bb9znRi;zLqpGC4A8N9O0`uc%ejp?|3BP=AiK(%fgXfNp!Y2pCqH4H2 zoNu7`BR#$s-zJ>6eXl#cilRDcRj|Jk%-q{Pv3GLMXxPZxQ%ScU(qO;+mi`~7m1l2W`SD^}1(Z>p zv?{z&o>Bpa22=l17dN$~QujTw^uMmDVZRq@z{u7p4^wmX@5jHbBQp@M;N+QVQ}IRj z-JcwstI_wjZ|}Y-0w3nN-DLCjt_SVBa9W%3u~J0+fJ%F3*r6OH4v&s@k`_ftQrpA!oG#=7IA&Jf+?@Ie58Hge~rydkM5~j>jbmR9|eTx zzPbgsZvC<|Pxj%&{W$yIpLcOwzRCO1V^`d4e#bp;f6>+}BC8Q3?ltuz>l4G@v_9ee zG}F#JXC~50mHOQaw!Y-T^;fPhZ(D!fl^1R-UxJ^TH=EWtS^0|IRQZAE)yzec2#?K7 zc`Md|L&rxEsMz42dANEkoxg&f;(xv5_K&={yZf*1-{stQ%i0egeBgV(ciQvc^ySk| Px$(Z05592sCwu-MEbQ9H literal 75424 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 CQ2T@c diff --git a/etc/multivm_bootloaders/vm_gateway/proved_batch.yul/proved_batch.yul.zbin b/etc/multivm_bootloaders/vm_gateway/proved_batch.yul/proved_batch.yul.zbin index 2506ce065d74fc6a610beac3843b6c147c63cf10..e877b81cc2fe5ddcb87caf5371f464af74bf0a30 100644 GIT binary patch literal 69792 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ξfs;`bHXnezE4&-45C&k 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 Y=@#v_0#YkJ%Jqu%1CU!$5+j{oa$=xgK zbeAB%iqR&AuBwguRPXXotXAF=A2iloVO^ihZF;LqsZbo`FV*t*%o%@Di)dcOp7Gr*bc|f~DFd56y9YXO1jzeR zWD|`xvnSoXGCMUM1y|JsXF^l1s*ExYTzw!FK;@akiS0}#@h|2(Cc}D8-2Z~}@A~;2 z??3ox-+`~+U(PKa`s>|a?LF%YD~4};`0{OUeGl;4M)6#<3j-?BYm#Pya?vlYDDNAG z7&dlWw2xYUf9D%-rgzy=e>3GnmFBQV?Fm*jX37}o$6XTd>jQynRr$4y+9_ZTRyN+zD7VLV)q5Mkh zUcLRYze?JcV7*cPHw)$d(&_Qt;}N~|s=et1zH4k(&{xtQSHAw)4=3fDyl{NsawgwA z*M9SpcbmT(e8%uI?4L9df7=nbSoZrHCnshrhe?|sXt2|yYT4DxkIAerp12U)8MZr_ zMhC|yX3slEH;9&3b8md@D5>K}-tx%bNq;U)%8caU%v|a(pIQp}G_iML^6CjP`(}?! zRa5WQ(zfk8wmf_D<{jI%Ub?+bXI3%v8^4DCG2qcN>Fz64o=Ihos+{hAP*ZgTSm1vP z95L}l1m*k7hhbPxPLIzXLA(MsH828Iqvg?j(@RheMiUz18{p9sF&{0D34Yx{P(B;_ zyiDKp49dw77A??!-!u3PKOsBF3h9UZ|CppdMec*3{Em#3jpid~Et{!!I35kRPjq#`xpBb07FtG;RM4wz>~olUyvbx;+gl;5ZQ z>boYPk4AJ;&1wQRlibbqr5gFg51N8-e+`IO?VaIuqSC3RVvQ&4@m=Nd=@2sl%9RG_ zKXh&~>clIAmO|oR>HfLICb7(~#!^)Y*5DNMjq(0E`#m-oU zm52!|tBjHp|M72rLUxcfHC;JN4;>O^P@4XS|68B@c6WLeMRn4uApR4s+#j9XKeewC zRr2&y{3gkQ)%sa;euj8RauV*-gL$e3N zM8aQ(X4CE4L;KU$SA}x%j1NcfXI&Eh)lgf4nUdNRQ%Hgm`2$)DyKkYtLN|+%;iICF5-cshOj;_h!>I&|@=OLI5gF?-vp;qN{9gMW9& St=n@~4!-x;8}^*&>Hq&8M!aAE literal 71904 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 _42u{WYDg z;vdjF#eP$MCCzVZP@j5BYQPsh4fR1XpN8d@K2hCB<$zDUrOu<|ueHBQ`)OUcFA*^% zUd43T4`28UO@G0|0)q$bUw}b0U$Eb|;O5@1sdlqJ5&XIk%~Te0DS4$pz80cHsWTJg zQ2h2#AKLRHIU5Y%DA$^wNBEgf`jCE1gWq7>L2L$p5yD0MhIwAc`fj0#t3BEmnbyDL zaYOps_>+B;VY!Vz%G;*FZ*+b^+~G*vL7^b@RUWpWOnPRa=BGBt7r%bC9ut4w!~+h+ z1Nd9LPV3PPqDO^)JD}N45L}8wNIx%dd^L)za+Yd6SrWaBa)M*!4V36F)qXL