diff --git a/.github/actions/docker-image/action.yml b/.github/actions/docker-image/action.yml new file mode 100644 index 0000000000..f0cc8ac781 --- /dev/null +++ b/.github/actions/docker-image/action.yml @@ -0,0 +1,61 @@ +name: Espresso Docker Image + +inputs: + images: + required: true + type: string + target: + required: true + type: string + platforms: + required: true + type: string + file: + required: true + type: string + +outputs: + digest: + value: "${{ steps.build.outputs.digest }}" + +runs: + using: composite + steps: + + - name: Generate docker metadata + uses: docker/metadata-action@v5 + id: metadata + with: + images: ${{ inputs.images }} + + # Build docker images and push to registry "by digest" later the images for + # ARM64 and AMD64 are merged and tagged as the final image. + # + # This step does not use `tags`. With tags (e.g. :main) push by digest fails. + - name: Build and push docker + uses: docker/build-push-action@v5 + id: build + with: + file: ${{ inputs.file }} + target: ${{ inputs.target }} + labels: ${{ steps.metadata.outputs.labels }} + platforms: ${{ inputs.platforms }} + cache-from: type=gha + cache-to: type=gha,mode=max + outputs: type=image,name=${{ inputs.images }},push-by-digest=true,name-canonical=true,push=true + + - name: Export digest + shell: bash + run: | + digest_dir="${{ runner.temp }}/${{ inputs.target }}-digests" + mkdir -p "${digest_dir}" + digest="${{ steps.build.outputs.digest }}" + touch "${digest_dir}/${digest#sha256:}" + + - name: Upload digest + uses: actions/upload-artifact@v3 + with: + name: "${{ inputs.target }}-digests" + path: "${{ runner.temp }}/${{ inputs.target }}-digests/*" + if-no-files-found: error + retention-days: 1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09065c28dd..94874f0c11 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,13 @@ on: - master - develop - integration + tags: + # YYYYMMDD + - "20[0-9][0-9][0-1][0-9][0-3][0-9]*" + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true jobs: test: diff --git a/.github/workflows/espresso-docker.yml b/.github/workflows/espresso-docker.yml new file mode 100644 index 0000000000..e7d01175d6 --- /dev/null +++ b/.github/workflows/espresso-docker.yml @@ -0,0 +1,162 @@ +# Build the nitro-node and nitro-node-dev images on ARM64 and AMD64 hosts. +# +# The reason for building the ARM image natively instead of with QEMU is that +# the QEMU build always failed after around 40 minutes. I'm currently not sure +# why it failed. I did also run into insufficient space issuse on public runners +# so it's possible this was always the culprit. +# +# After building, the images are merged together to make a multiplatform image. +# +# The latest wavm machine is also copied and exported as an artifact. In nitro +# this seems to be later used as machine for the non-dev nitro-node build. +# For more details on that see the Dockerfile and ./scripts/download.sh +name: Espresso Docker build CI +run-name: Docker build CI triggered from @${{ github.actor }} of ${{ github.head_ref }} + +on: + workflow_dispatch: + merge_group: + push: + branches: + - master + - develop + - integration + tags: + # YYYYMMDD + - "20[0-9][0-9][0-1][0-9][0-3][0-9]*" + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + docker_build: + strategy: + matrix: + platform: [ linux/amd64, linux/arm64 ] + include: + - platform: linux/amd64 + runs-on: X64 + - platform: linux/arm64 + runs-on: ARM64 + + runs-on: [ self-hosted, "${{ matrix.runs-on }}" ] + + steps: + # TODO We should be able to remove this but currently it's needed to avoid + # permission errors during checkout. + - name: Fix submodule permissions check + run: | + sudo chown -R runner:runner . + # Remove potentially leftover files from last run, it's unclear why + # this only works with `sudo` despite the chown command above. + sudo rm -rfv ./target + - name: Fix submodule permissions check + run: | + git config --global --add safe.directory '*' + + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Github Container Repo + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build nitro-node image + uses: ./.github/actions/docker-image + with: + file: Dockerfile.espresso + images: ghcr.io/espressosystems/nitro-espresso-integration/nitro-node + target: nitro-node + platforms: ${{ matrix.platform }} + + - name: Build nitro-node-dev image + uses: ./.github/actions/docker-image + id: nitro-node-dev + with: + file: Dockerfile.espresso + images: ghcr.io/espressosystems/nitro-espresso-integration/nitro-node-dev + target: nitro-node-dev + platforms: ${{ matrix.platform }} + + # The module root is not identical for the ARM build so we upload + # the binary only for AMD64. + + - name: Extract WAVM machine from container and print its root + if: matrix.platform == 'linux/amd64' + id: module-root + run: | + # Unfortunately, `docker cp` seems to always result in a "permission denied" + # We work around this by piping a tarball through stdout + digest="${{ steps.nitro-node-dev.outputs.digest }}" + image=ghcr.io/espressosystems/nitro-espresso-integration/nitro-node-dev@$digest + docker run --rm --entrypoint tar $image -cf - target/machines/latest | tar xf - + ls -lah target/machines/latest + module_root="$(cat "target/machines/latest/module-root.txt")" + echo "module-root=$module_root" >> $GITHUB_OUTPUT + echo -e "\x1b[1;34mWAVM module root:\x1b[0m $module_root" + + - name: Upload WAVM machine as artifact + uses: actions/upload-artifact@v3 + if: matrix.platform == 'linux/amd64' + with: + name: wavm-machine-${{ steps.module-root.outputs.module-root }} + path: target/machines/latest/* + if-no-files-found: error + + # Merge the AMD64 and ARM64 images into the final (multiplatform) image. + # + # For documentation refer to + # https://docs.docker.com/build/ci/github-actions/multi-platform/#distribute-build-across-multiple-runners + merge_into_multiplatform_images: + needs: + - docker_build + strategy: + matrix: + target: [ nitro-node, nitro-node-dev ] + include: + - target: nitro-node + image: ghcr.io/espressosystems/nitro-espresso-integration/nitro-node + - target: nitro-node-dev + image: ghcr.io/espressosystems/nitro-espresso-integration/nitro-node-dev + + runs-on: ubuntu-latest + steps: + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Github Container Repo + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Download digests + uses: actions/download-artifact@v3 + with: + name: "${{ matrix.target }}-digests" + path: "${{ runner.temp }}/digests" + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ matrix.image }} + + - name: Create manifest list and push + working-directory: "${{ runner.temp }}/digests" + run: | + docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ matrix.image }}@sha256:%s ' *) + - name: Inspect image + run: | + docker buildx imagetools inspect ${{ matrix.image }}:${{ steps.meta.outputs.version }} diff --git a/Dockerfile.espresso b/Dockerfile.espresso new file mode 100644 index 0000000000..30654fa54b --- /dev/null +++ b/Dockerfile.espresso @@ -0,0 +1,267 @@ +# This is based on the original `Dockerfile`. +# +# Changes: +# - The machine-versions target downloads the replay wasm for the espresso integration. +FROM debian:bullseye-slim as brotli-wasm-builder +WORKDIR /workspace +RUN apt-get update && \ + apt-get install -y cmake make git lbzip2 python3 xz-utils && \ + git clone https://github.com/emscripten-core/emsdk.git && \ + cd emsdk && \ + ./emsdk install 3.1.7 && \ + ./emsdk activate 3.1.7 +COPY scripts/build-brotli.sh scripts/ +COPY brotli brotli +RUN cd emsdk && . ./emsdk_env.sh && cd .. && ./scripts/build-brotli.sh -w -t /workspace/install/ + +FROM scratch as brotli-wasm-export +COPY --from=brotli-wasm-builder /workspace/install/ / + +FROM debian:bullseye-slim as brotli-library-builder +WORKDIR /workspace +COPY scripts/build-brotli.sh scripts/ +COPY brotli brotli +RUN apt-get update && \ + apt-get install -y cmake make gcc git && \ + ./scripts/build-brotli.sh -l -t /workspace/install/ + +FROM scratch as brotli-library-export +COPY --from=brotli-library-builder /workspace/install/ / + +FROM node:16-bullseye-slim as contracts-builder +RUN apt-get update && \ + apt-get install -y git python3 make g++ +WORKDIR /workspace +COPY contracts/package.json contracts/yarn.lock contracts/ +RUN cd contracts && yarn install --ignore-optional +COPY contracts contracts/ +COPY Makefile . +RUN NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-solidity + +FROM debian:bullseye-20211220 as wasm-base +WORKDIR /workspace +RUN apt-get update && apt-get install -y curl build-essential=12.9 + +FROM wasm-base as wasm-libs-builder + # clang / lld used by soft-float wasm +RUN apt-get install -y clang=1:11.0-51+nmu5 lld=1:11.0-51+nmu5 + # pinned rust 1.65.0 +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.68.2 --target x86_64-unknown-linux-gnu wasm32-unknown-unknown wasm32-wasi +COPY ./Makefile ./ +COPY arbitrator/arbutil arbitrator/arbutil +COPY arbitrator/wasm-libraries arbitrator/wasm-libraries +COPY --from=brotli-wasm-export / target/ +RUN . ~/.cargo/env && NITRO_BUILD_IGNORE_TIMESTAMPS=1 RUSTFLAGS='-C symbol-mangling-version=v0' make build-wasm-libs + +FROM scratch as wasm-libs-export +COPY --from=wasm-libs-builder /workspace/ / + +FROM wasm-base as wasm-bin-builder + # pinned go version +RUN curl -L https://golang.org/dl/go1.20.linux-`dpkg --print-architecture`.tar.gz | tar -C /usr/local -xzf - +COPY ./Makefile ./go.mod ./go.sum ./ +COPY ./arbcompress ./arbcompress +COPY ./arbos ./arbos +COPY ./arbstate ./arbstate +COPY ./arbutil ./arbutil +COPY ./gethhook ./gethhook +COPY ./blsSignatures ./blsSignatures +COPY ./cmd/chaininfo ./cmd/chaininfo +COPY ./cmd/replay ./cmd/replay +COPY ./das/dastree ./das/dastree +COPY ./precompiles ./precompiles +COPY ./statetransfer ./statetransfer +COPY ./util ./util +COPY ./wavmio ./wavmio +COPY ./zeroheavy ./zeroheavy +COPY ./contracts/src/precompiles/ ./contracts/src/precompiles/ +COPY ./contracts/package.json ./contracts/yarn.lock ./contracts/ +COPY ./solgen/gen.go ./solgen/ +COPY ./fastcache ./fastcache +COPY ./go-ethereum ./go-ethereum +COPY --from=brotli-wasm-export / target/ +COPY --from=contracts-builder workspace/contracts/build/contracts/src/precompiles/ contracts/build/contracts/src/precompiles/ +COPY --from=contracts-builder workspace/.make/ .make/ +RUN PATH="$PATH:/usr/local/go/bin" NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-wasm-bin + +FROM rust:1.68-slim-bullseye as prover-header-builder +WORKDIR /workspace +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y make && \ + cargo install --force cbindgen +COPY arbitrator/Cargo.* arbitrator/cbindgen.toml arbitrator/ +COPY ./Makefile ./ +COPY arbitrator/arbutil arbitrator/arbutil +COPY arbitrator/prover arbitrator/prover +COPY arbitrator/jit arbitrator/jit +RUN NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-prover-header + +FROM scratch as prover-header-export +COPY --from=prover-header-builder /workspace/target/ / + +FROM rust:1.68-slim-bullseye as prover-builder +WORKDIR /workspace +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y make wget gpg software-properties-common zlib1g-dev libstdc++-10-dev wabt +RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \ + add-apt-repository 'deb http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-12 main' && \ + apt-get update && \ + apt-get install -y llvm-12-dev libclang-common-12-dev +COPY arbitrator/Cargo.* arbitrator/ +COPY arbitrator/arbutil arbitrator/arbutil +COPY arbitrator/prover/Cargo.toml arbitrator/prover/ +COPY arbitrator/jit/Cargo.toml arbitrator/jit/ +RUN mkdir arbitrator/prover/src arbitrator/jit/src && \ + echo "fn test() {}" > arbitrator/jit/src/lib.rs && \ + echo "fn test() {}" > arbitrator/prover/src/lib.rs && \ + cargo build --manifest-path arbitrator/Cargo.toml --release --lib && \ + rm arbitrator/jit/src/lib.rs +COPY ./Makefile ./ +COPY arbitrator/prover arbitrator/prover +COPY arbitrator/jit arbitrator/jit +COPY --from=brotli-library-export / target/ +RUN touch -a -m arbitrator/prover/src/lib.rs +RUN NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-prover-lib +RUN NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-prover-bin +RUN NITRO_BUILD_IGNORE_TIMESTAMPS=1 make CARGOFLAGS="--features=llvm" build-jit + +FROM scratch as prover-export +COPY --from=prover-builder /workspace/target/ / + +FROM debian:bullseye-slim as module-root-calc +WORKDIR /workspace +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y wabt make +COPY --from=prover-export / target/ +COPY --from=wasm-bin-builder /workspace/target/ target/ +COPY --from=wasm-bin-builder /workspace/.make/ .make/ +COPY --from=wasm-libs-builder /workspace/target/ target/ +COPY --from=wasm-libs-builder /workspace/arbitrator/wasm-libraries/ arbitrator/wasm-libraries/ +COPY --from=wasm-libs-builder /workspace/.make/ .make/ +COPY ./Makefile ./ +COPY ./arbitrator ./arbitrator +COPY ./solgen ./solgen +COPY ./contracts ./contracts +RUN NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-replay-env + +FROM debian:bullseye-slim as machine-versions +RUN apt-get update && apt-get install -y unzip wget curl +WORKDIR /workspace/machines +# Download WAVM machines +COPY ./scripts/download-machine.sh . +RUN ./download-machine.sh consensus-v10 0x6b94a7fc388fd8ef3def759297828dc311761e88d8179c7ee8d3887dc554f3c3 +RUN ./download-machine.sh consensus-v10.1 0xda4e3ad5e7feacb817c21c8d0220da7650fe9051ece68a3f0b1c5d38bbb27b21 +RUN ./download-machine.sh consensus-v10.2 0x0754e09320c381566cc0449904c377a52bd34a6b9404432e80afd573b67f7b17 + +COPY ./scripts/download-machine-espresso.sh . +# TODO: download espresso wasm release once we have one +# RUN ./download-machine-espresso.sh 20231211 0x... + +FROM golang:1.20-bullseye as node-builder +WORKDIR /workspace +ARG version="" +ARG datetime="" +ARG modified="" +ENV NITRO_VERSION=$version +ENV NITRO_DATETIME=$datetime +ENV NITRO_MODIFIED=$modified +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y wabt +COPY go.mod go.sum ./ +COPY go-ethereum/go.mod go-ethereum/go.sum go-ethereum/ +COPY fastcache/go.mod fastcache/go.sum fastcache/ +RUN go mod download +COPY . ./ +COPY --from=contracts-builder workspace/contracts/build/ contracts/build/ +COPY --from=contracts-builder workspace/.make/ .make/ +COPY --from=prover-header-export / target/ +COPY --from=brotli-library-export / target/ +COPY --from=prover-export / target/ +RUN mkdir -p target/bin +COPY .nitro-tag.txt /nitro-tag.txt +RUN NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build + +FROM node-builder as fuzz-builder +RUN mkdir fuzzers/ +RUN ./scripts/fuzz.bash --build --binary-path /workspace/fuzzers/ + +FROM debian:bullseye-slim as nitro-fuzzer +COPY --from=fuzz-builder /workspace/fuzzers/*.fuzz /usr/local/bin/ +COPY ./scripts/fuzz.bash /usr/local/bin +RUN mkdir /fuzzcache +ENTRYPOINT [ "/usr/local/bin/fuzz.bash", "--binary-path", "/usr/local/bin/", "--fuzzcache-path", "/fuzzcache" ] + +FROM debian:bullseye-slim as nitro-node-slim +WORKDIR /home/user +COPY --from=node-builder /workspace/target/bin/nitro /usr/local/bin/ +COPY --from=node-builder /workspace/target/bin/relay /usr/local/bin/ +COPY --from=node-builder /workspace/target/bin/nitro-val /usr/local/bin/ +COPY --from=node-builder /workspace/target/bin/seq-coordinator-manager /usr/local/bin/ +COPY --from=machine-versions /workspace/machines /home/user/target/machines +USER root +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y \ + ca-certificates \ + wabt && \ + /usr/sbin/update-ca-certificates && \ + useradd -s /bin/bash user && \ + mkdir -p /home/user/l1keystore && \ + mkdir -p /home/user/.arbitrum/local/nitro && \ + chown -R user:user /home/user && \ + chmod -R 555 /home/user/target/machines && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /usr/share/doc/* /var/cache/ldconfig/aux-cache /usr/lib/python3.9/__pycache__/ /usr/lib/python3.9/*/__pycache__/ /var/log/* && \ + nitro --version + +USER user +WORKDIR /home/user/ +ENTRYPOINT [ "/usr/local/bin/nitro" ] + +FROM nitro-node-slim as nitro-node +USER root +COPY --from=prover-export /bin/jit /usr/local/bin/ +COPY --from=node-builder /workspace/target/bin/daserver /usr/local/bin/ +COPY --from=node-builder /workspace/target/bin/datool /usr/local/bin/ +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y \ + curl procps jq rsync \ + node-ws vim-tiny python3 \ + dnsutils && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /usr/share/doc/* /var/cache/ldconfig/aux-cache /usr/lib/python3.9/__pycache__/ /usr/lib/python3.9/*/__pycache__/ /var/log/* && \ + nitro --version + +USER user + +FROM nitro-node as nitro-node-dev +USER root +# Copy in latest WASM module root +RUN rm -f /home/user/target/machines/latest +COPY --from=prover-export /bin/jit /usr/local/bin/ +COPY --from=node-builder /workspace/target/bin/deploy /usr/local/bin/ +COPY --from=node-builder /workspace/target/bin/seq-coordinator-invalidate /usr/local/bin/ +COPY --from=module-root-calc /workspace/target/machines/latest/machine.wavm.br /home/user/target/machines/latest/ +COPY --from=module-root-calc /workspace/target/machines/latest/until-host-io-state.bin /home/user/target/machines/latest/ +COPY --from=module-root-calc /workspace/target/machines/latest/module-root.txt /home/user/target/machines/latest/ +COPY --from=module-root-calc /workspace/target/machines/latest/replay.wasm /home/user/target/machines/latest/ +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y \ + sudo && \ + chmod -R 555 /home/user/target/machines && \ + adduser user sudo && \ + echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /usr/share/doc/* /var/cache/ldconfig/aux-cache /usr/lib/python3.9/__pycache__/ /usr/lib/python3.9/*/__pycache__/ /var/log/* && \ + nitro --version + +USER user + +FROM nitro-node as nitro-node-default +# Just to ensure nitro-node-dist is default diff --git a/scripts/download-machine-espresso.sh b/scripts/download-machine-espresso.sh new file mode 100755 index 0000000000..c4c1feaa91 --- /dev/null +++ b/scripts/download-machine-espresso.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# Same as ./download-machine.sh but for the espresso integration. +# +# The url_base has been changed to point to the espresso integration repo such +# that it downloads the replay wasm binary for the integration instead. +# +# For this to work there needs to be a tagged github release that exported the +# wasm machine. Then, run +# +# ./download-machine-espresso.sh GIT_RELEASE_TAG WASM_MACHINE_ROOT +# ./download-machine-espresso.sh 20231211 0xb2ec17fe4ae788f2c81cd1d28242dfa47696598ea0f18cd78f64c7e2e8b75434 +set -euxo pipefail + +mkdir "$2" +ln -sfT "$2" latest +cd "$2" +echo "$2" > module-root.txt +url_base="https://github.com/EspressoSystems/nitro-espresso-integrations/releases/download/$1" +wget "$url_base/machine.wavm.br" + +status_code="$(curl -LI "$url_base/replay.wasm" -so /dev/null -w '%{http_code}')" +if [ "$status_code" -ne 404 ]; then + wget "$url_base/replay.wasm" +fi